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));
|
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}")
|
@DeleteMapping("/devices/{id}")
|
||||||
public ResponseEntity<ApiResponse<Void>> revokeDevice(@PathVariable String id) {
|
public ResponseEntity<ApiResponse<Void>> revokeDevice(@PathVariable String id) {
|
||||||
deviceService.revoke(id);
|
deviceService.revoke(id);
|
||||||
@ -45,4 +57,10 @@ public class LicenseAdminController {
|
|||||||
return ResponseEntity.ok(ApiResponse.ok());
|
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.entity.SensitiveWordEntity;
|
||||||
import com.xuqm.tenant.service.OpsService;
|
import com.xuqm.tenant.service.OpsService;
|
||||||
import com.xuqm.tenant.service.OpsPushDiagnosticsClient;
|
import com.xuqm.tenant.service.OpsPushDiagnosticsClient;
|
||||||
import com.xuqm.tenant.service.LicenseServiceClient;
|
|
||||||
import com.xuqm.tenant.service.RiskControlService;
|
import com.xuqm.tenant.service.RiskControlService;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -39,19 +38,16 @@ public class OpsController {
|
|||||||
private final FeatureServiceManager featureServiceManager;
|
private final FeatureServiceManager featureServiceManager;
|
||||||
private final RiskControlService riskControlService;
|
private final RiskControlService riskControlService;
|
||||||
private final OpsPushDiagnosticsClient pushDiagnosticsClient;
|
private final OpsPushDiagnosticsClient pushDiagnosticsClient;
|
||||||
private final LicenseServiceClient licenseServiceClient;
|
|
||||||
private final PrivateDeploymentProperties deploymentProperties;
|
private final PrivateDeploymentProperties deploymentProperties;
|
||||||
|
|
||||||
public OpsController(OpsService opsService, FeatureServiceManager featureServiceManager,
|
public OpsController(OpsService opsService, FeatureServiceManager featureServiceManager,
|
||||||
RiskControlService riskControlService,
|
RiskControlService riskControlService,
|
||||||
OpsPushDiagnosticsClient pushDiagnosticsClient,
|
OpsPushDiagnosticsClient pushDiagnosticsClient,
|
||||||
LicenseServiceClient licenseServiceClient,
|
|
||||||
PrivateDeploymentProperties deploymentProperties) {
|
PrivateDeploymentProperties deploymentProperties) {
|
||||||
this.opsService = opsService;
|
this.opsService = opsService;
|
||||||
this.featureServiceManager = featureServiceManager;
|
this.featureServiceManager = featureServiceManager;
|
||||||
this.riskControlService = riskControlService;
|
this.riskControlService = riskControlService;
|
||||||
this.pushDiagnosticsClient = pushDiagnosticsClient;
|
this.pushDiagnosticsClient = pushDiagnosticsClient;
|
||||||
this.licenseServiceClient = licenseServiceClient;
|
|
||||||
this.deploymentProperties = deploymentProperties;
|
this.deploymentProperties = deploymentProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,35 +216,6 @@ public class OpsController {
|
|||||||
body.getOrDefault("payload", "{}"))));
|
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")
|
@GetMapping("/api/ops/risk/rules")
|
||||||
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
@PreAuthorize("hasAuthority('ROLE_OPS')")
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package com.xuqm.tenant.service;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.xuqm.common.model.ApiResponse;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.*;
|
||||||
import org.springframework.stereotype.Service;
|
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) {
|
private ResponseEntity<String> callInternal(String path, HttpMethod method, Object body) {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.set("X-Internal-Token", internalToken);
|
headers.set("X-Internal-Token", internalToken);
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户