package com.xuqm.license.controller; import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import com.xuqm.common.exception.BusinessException; import com.xuqm.common.model.ApiResponse; import com.xuqm.common.security.LicenseFileCrypto; import com.xuqm.license.entity.AppLicenseEntity; import com.xuqm.license.service.AppLicenseService; import com.xuqm.license.service.DeviceService; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.LinkedHashMap; import java.util.Map; @RestController @RequestMapping("/api/license") public class LicensePublicController { private final DeviceService deviceService; private final AppLicenseService appLicenseService; public LicensePublicController(DeviceService deviceService, AppLicenseService appLicenseService) { this.deviceService = deviceService; this.appLicenseService = appLicenseService; } @PostMapping("/register") public ResponseEntity>> register(@Valid @RequestBody RegisterRequest req) { String resolvedAppKey = resolveAppKey(req.appKey(), req.licenseFile()); DeviceService.RegisterResult result = deviceService.register( resolvedAppKey, req.deviceId(), req.deviceName(), req.deviceModel(), req.deviceVendor(), req.osVersion(), req.userInfo()); Map data = new LinkedHashMap<>(); data.put("success", result.success()); data.put("token", result.token()); if (result.message() != null) { data.put("message", result.message()); } return ResponseEntity.ok(ApiResponse.success(data)); } @PostMapping("/verify") public ResponseEntity>> verify(@Valid @RequestBody VerifyRequest req) { String resolvedAppKey = resolveAppKey(req.appKey(), req.licenseFile()); DeviceService.VerifyResult result = deviceService.verify(resolvedAppKey, req.deviceId(), req.token(), req.userInfo()); Map data = new LinkedHashMap<>(); data.put("valid", result.valid()); if (result.error() != null) { data.put("error", result.error()); } return ResponseEntity.ok(ApiResponse.success(data)); } /** * Returns configured package names for the given appKey. * Used by the SDK (appKey-only flow) to validate the caller's package name client-side. */ @GetMapping("/app-info") public ResponseEntity>> appInfo(@RequestParam String appKey) { AppLicenseEntity license; try { license = appLicenseService.getByAppKey(appKey); } catch (BusinessException e) { throw new BusinessException(404, "App not found"); } Map data = new LinkedHashMap<>(); data.put("androidPackageName", license.getAndroidPackageName()); data.put("iosBundleId", license.getIosBundleId()); data.put("harmonyBundleName", license.getHarmonyBundleName()); return ResponseEntity.ok(ApiResponse.success(data)); } private static String resolveAppKey(String appKey, String licenseFile) { if (licenseFile != null && !licenseFile.isBlank()) { LicenseFileCrypto.LicensePayload payload = LicenseFileCrypto.decrypt(licenseFile); return payload.appKey(); } if (appKey == null || appKey.isBlank()) { throw new BusinessException(400, "appKey 或 licenseFile 必须提供其中一个"); } return appKey; } public record RegisterRequest( String appKey, @JsonProperty("packageName") @JsonAlias("package_name") String packageName, @JsonProperty("licenseFile") @JsonAlias("license_file") String licenseFile, @NotBlank @JsonProperty("deviceId") @JsonAlias("device_id") String deviceId, @JsonProperty("deviceName") @JsonAlias("device_name") String deviceName, @JsonProperty("deviceModel") @JsonAlias("device_model") String deviceModel, @JsonProperty("deviceVendor") @JsonAlias("device_vendor") String deviceVendor, @JsonProperty("osVersion") @JsonAlias("os_version") String osVersion, @JsonProperty("userInfo") @JsonAlias("user_info") JsonNode userInfo ) {} public record VerifyRequest( String appKey, @JsonProperty("packageName") @JsonAlias("package_name") String packageName, @JsonProperty("licenseFile") @JsonAlias("license_file") String licenseFile, @NotBlank @JsonProperty("deviceId") @JsonAlias("device_id") String deviceId, @NotBlank String token, @JsonProperty("userInfo") @JsonAlias("user_info") JsonNode userInfo ) {} }