feat(license): 租户自主管理最大设备数,ops 彻底移除 license 管理
license-service:
- LicenseAdminController: 新增 PATCH /api/license/admin/apps/{appKey},
租户可直接修改 maxDevices / isActive / remark
tenant-service:
- OpsController: 移除 GET /api/ops/apps/{appKey}/license 和
PUT /api/ops/apps/{appKey}/license/max-devices 两个端点,
同时移除 licenseServiceClient 字段注入
- LicenseServiceClient: 移除 updateMaxDevices() 和 getAppLicenseStatus()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
父节点
af922ae420
当前提交
8a3c41d5ff
@ -33,6 +33,18 @@ public class LicenseAdminController {
|
||||
return ResponseEntity.ok(ApiResponse.success(data));
|
||||
}
|
||||
|
||||
@PatchMapping("/apps/{appKey}")
|
||||
public ResponseEntity<ApiResponse<AppLicenseEntity>> updateAppLicense(
|
||||
@PathVariable String appKey,
|
||||
@RequestBody UpdateAppLicenseRequest req) {
|
||||
if (req.maxDevices() != null && req.maxDevices() < 1) {
|
||||
throw new com.xuqm.common.exception.BusinessException(400, "最大设备数必须大于0");
|
||||
}
|
||||
AppLicenseEntity updated = appLicenseService.update(
|
||||
appKey, null, req.maxDevices(), null, req.isActive(), req.remark());
|
||||
return ResponseEntity.ok(ApiResponse.success(updated));
|
||||
}
|
||||
|
||||
@DeleteMapping("/devices/{id}")
|
||||
public ResponseEntity<ApiResponse<Void>> revokeDevice(@PathVariable String id) {
|
||||
deviceService.revoke(id);
|
||||
@ -45,4 +57,10 @@ public class LicenseAdminController {
|
||||
return ResponseEntity.ok(ApiResponse.ok());
|
||||
}
|
||||
|
||||
public record UpdateAppLicenseRequest(
|
||||
Integer maxDevices,
|
||||
Boolean isActive,
|
||||
String remark
|
||||
) {}
|
||||
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ import com.xuqm.tenant.entity.RiskConfigEntity;
|
||||
import com.xuqm.tenant.entity.SensitiveWordEntity;
|
||||
import com.xuqm.tenant.service.OpsService;
|
||||
import com.xuqm.tenant.service.OpsPushDiagnosticsClient;
|
||||
import com.xuqm.tenant.service.LicenseServiceClient;
|
||||
import com.xuqm.tenant.service.RiskControlService;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@ -39,19 +38,16 @@ public class OpsController {
|
||||
private final FeatureServiceManager featureServiceManager;
|
||||
private final RiskControlService riskControlService;
|
||||
private final OpsPushDiagnosticsClient pushDiagnosticsClient;
|
||||
private final LicenseServiceClient licenseServiceClient;
|
||||
private final PrivateDeploymentProperties deploymentProperties;
|
||||
|
||||
public OpsController(OpsService opsService, FeatureServiceManager featureServiceManager,
|
||||
RiskControlService riskControlService,
|
||||
OpsPushDiagnosticsClient pushDiagnosticsClient,
|
||||
LicenseServiceClient licenseServiceClient,
|
||||
PrivateDeploymentProperties deploymentProperties) {
|
||||
this.opsService = opsService;
|
||||
this.featureServiceManager = featureServiceManager;
|
||||
this.riskControlService = riskControlService;
|
||||
this.pushDiagnosticsClient = pushDiagnosticsClient;
|
||||
this.licenseServiceClient = licenseServiceClient;
|
||||
this.deploymentProperties = deploymentProperties;
|
||||
}
|
||||
|
||||
@ -220,35 +216,6 @@ public class OpsController {
|
||||
body.getOrDefault("payload", "{}"))));
|
||||
}
|
||||
|
||||
/* ---------- License 管理 ---------- */
|
||||
@GetMapping("/api/ops/apps/{appKey}/license")
|
||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||
public ResponseEntity<ApiResponse<Map<String, Object>>> getAppLicense(@PathVariable String appKey) {
|
||||
return ResponseEntity.ok(ApiResponse.success(licenseServiceClient.getAppLicenseStatus(appKey)));
|
||||
}
|
||||
|
||||
@PutMapping("/api/ops/apps/{appKey}/license/max-devices")
|
||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||
public ResponseEntity<ApiResponse<Void>> updateMaxDevices(
|
||||
@PathVariable String appKey,
|
||||
@RequestBody Map<String, Object> body) {
|
||||
Object maxDevicesObj = body.get("maxDevices");
|
||||
if (maxDevicesObj == null) {
|
||||
throw new com.xuqm.common.exception.BusinessException(400, "最大设备数不能为空");
|
||||
}
|
||||
int maxDevices;
|
||||
if (maxDevicesObj instanceof Number n) {
|
||||
maxDevices = n.intValue();
|
||||
} else {
|
||||
maxDevices = Integer.parseInt(maxDevicesObj.toString());
|
||||
}
|
||||
if (maxDevices < 1) {
|
||||
throw new com.xuqm.common.exception.BusinessException(400, "最大设备数必须大于0");
|
||||
}
|
||||
licenseServiceClient.updateMaxDevices(appKey, maxDevices);
|
||||
return ResponseEntity.ok(ApiResponse.ok());
|
||||
}
|
||||
|
||||
/* ---------- 风控配置 ---------- */
|
||||
@GetMapping("/api/ops/risk/rules")
|
||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||
|
||||
@ -2,7 +2,6 @@ package com.xuqm.tenant.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xuqm.common.model.ApiResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -68,33 +67,6 @@ public class LicenseServiceClient {
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getAppLicenseStatus(String appKey) {
|
||||
try {
|
||||
ResponseEntity<String> response = callInternal(
|
||||
"/api/license/internal/apps/" + appKey + "/status", HttpMethod.GET, null);
|
||||
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
|
||||
JsonNode node = objectMapper.readTree(response.getBody());
|
||||
return objectMapper.convertValue(node.path("data"),
|
||||
new com.fasterxml.jackson.core.type.TypeReference<>() {});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
return Map.of("exists", false);
|
||||
}
|
||||
|
||||
public void updateMaxDevices(String appKey, int maxDevices) {
|
||||
try {
|
||||
Map<String, Object> body = Map.of(
|
||||
"id", appKey,
|
||||
"maxDevices", maxDevices
|
||||
);
|
||||
callInternal("/api/license/internal/apps", HttpMethod.POST, body);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseEntity<String> callInternal(String path, HttpMethod method, Object body) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("X-Internal-Token", internalToken);
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户