feat:补充接口注释
This commit is contained in:
parent
a00991f49d
commit
bf8ec7054c
@ -63,6 +63,8 @@ chmod +x /home/tms/scripts/tms.sh
|
||||
|
||||
Open:
|
||||
- Swagger UI: http://localhost:8080/swagger-ui.html
|
||||
- Internal API docs JSON: http://localhost:8080/v3/api-docs/internal-api
|
||||
- Runtime-generated Swagger/OpenAPI docs are the source of truth for `/api/**`.
|
||||
- Internal health API: `GET /api/v1/device/status`
|
||||
- Internal sign preview API: `POST /api/v1/sign/preview`
|
||||
- External sign API: `POST /openapi/v1/sign/signature`
|
||||
|
||||
16
pom.xml
16
pom.xml
@ -79,6 +79,22 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Source: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.83</version>
|
||||
<!-- <scope>compile</scope>-->
|
||||
</dependency>
|
||||
|
||||
<!-- Source: https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk18on -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>1.83</version>
|
||||
<!-- <scope>compile</scope>-->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -16,7 +16,7 @@ public class OpenApiConfig {
|
||||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("CISD TMS API")
|
||||
.description("CISD Terminal Management System API")
|
||||
.description("CISD TMS 内部业务接口文档")
|
||||
.version("v0.0.1")
|
||||
.license(new License().name("Internal Use")))
|
||||
.externalDocs(new ExternalDocumentation()
|
||||
|
||||
@ -5,6 +5,9 @@ import com.cisd.tms.modules.auth.dto.CurrentUserResponse;
|
||||
import com.cisd.tms.modules.auth.dto.LoginRequest;
|
||||
import com.cisd.tms.modules.auth.dto.LoginResponse;
|
||||
import com.cisd.tms.modules.auth.service.AuthService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -15,6 +18,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
@Tag(name = "认证鉴权", description = "内部用户登录与当前用户信息接口")
|
||||
public class AuthController {
|
||||
|
||||
private final AuthService authService;
|
||||
@ -24,12 +28,17 @@ public class AuthController {
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
@Operation(summary = "内部用户登录", description = "校验用户名和密码,返回访问令牌及过期时间。")
|
||||
public ApiResponse<LoginResponse> login(@Valid @RequestBody LoginRequest request) {
|
||||
return ApiResponse.success(authService.login(request));
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
public ApiResponse<CurrentUserResponse> me(@RequestHeader(value = "X-User", required = false) String user) {
|
||||
@Operation(summary = "查询当前用户", description = "根据请求头中的用户标识返回当前登录用户信息。")
|
||||
public ApiResponse<CurrentUserResponse> me(
|
||||
@Parameter(description = "当前用户标识,来自内部鉴权链路透传的 X-User 请求头")
|
||||
@RequestHeader(value = "X-User", required = false) String user
|
||||
) {
|
||||
return ApiResponse.success(authService.me(user));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
package com.cisd.tms.modules.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "当前登录用户信息")
|
||||
public class CurrentUserResponse {
|
||||
|
||||
@Schema(description = "用户名", example = "admin")
|
||||
private String username;
|
||||
@Schema(description = "展示名称", example = "系统管理员")
|
||||
private String displayName;
|
||||
@Schema(description = "角色标识", example = "ADMIN")
|
||||
private String role;
|
||||
|
||||
public String getUsername() {
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
package com.cisd.tms.modules.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "内部登录请求")
|
||||
public class LoginRequest {
|
||||
|
||||
@NotBlank(message = "username is required")
|
||||
@Schema(description = "登录用户名", example = "admin")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "password is required")
|
||||
@Schema(description = "登录密码", example = "P@ssw0rd")
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
package com.cisd.tms.modules.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "内部登录响应")
|
||||
public class LoginResponse {
|
||||
|
||||
@Schema(description = "登录用户名", example = "admin")
|
||||
private String username;
|
||||
@Schema(description = "访问令牌")
|
||||
private String token;
|
||||
@Schema(description = "令牌过期时间", example = "2026-03-11T18:00:00+08:00")
|
||||
private String expiresAt;
|
||||
|
||||
public String getUsername() {
|
||||
|
||||
@ -25,6 +25,9 @@ import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest;
|
||||
import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult;
|
||||
import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
@ -42,6 +45,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/device/crypto")
|
||||
@Tag(name = "密码卡调试", description = "密码卡设备调试、密钥操作和文件管理接口")
|
||||
public class CryptoCardController {
|
||||
|
||||
private final PcieCryptoService pcieCryptoService;
|
||||
@ -51,22 +55,29 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@GetMapping("/device-count")
|
||||
@Operation(summary = "查询密码卡数量", description = "返回当前环境可见的密码卡设备数量。")
|
||||
public ApiResponse<DeviceCountResult> getDeviceCount() {
|
||||
return ApiResponse.success(pcieCryptoService.getDeviceCount());
|
||||
}
|
||||
|
||||
@GetMapping("/device-conf")
|
||||
@Operation(summary = "查询密码卡配置", description = "返回密码卡底层驱动和配置快照。")
|
||||
public ApiResponse<DeviceConfResult> getDeviceConf() {
|
||||
return ApiResponse.success(pcieCryptoService.getDeviceConf());
|
||||
}
|
||||
|
||||
@GetMapping("/device-info")
|
||||
@Operation(summary = "查询密码卡信息", description = "返回密码卡设备详细信息。")
|
||||
public ApiResponse<DeviceInfoResult> getDeviceInfo() {
|
||||
return ApiResponse.success(pcieCryptoService.getDeviceInfo());
|
||||
}
|
||||
|
||||
@GetMapping("/random")
|
||||
public ApiResponse<Map<String, Object>> random(@RequestParam(defaultValue = "16") @Min(1) int length) {
|
||||
@Operation(summary = "生成随机数", description = "使用密码卡生成指定长度的随机数并以 Base64 返回。")
|
||||
public ApiResponse<Map<String, Object>> random(
|
||||
@Parameter(description = "随机数字节长度,默认 16")
|
||||
@RequestParam(defaultValue = "16") @Min(1) int length
|
||||
) {
|
||||
byte[] random = pcieCryptoService.generateRandom(length);
|
||||
return ApiResponse.success(Map.of(
|
||||
"length", random.length,
|
||||
@ -75,12 +86,14 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/self-test")
|
||||
@Operation(summary = "执行密码卡自检", description = "触发密码卡自检,自检通过时返回 passed=true。")
|
||||
public ApiResponse<Map<String, Object>> selfTest() {
|
||||
pcieCryptoService.deviceSelfTest();
|
||||
return ApiResponse.success(Map.of("passed", true));
|
||||
}
|
||||
|
||||
@PostMapping("/hmac")
|
||||
@Operation(summary = "计算 HMAC", description = "使用给定算法、密钥和数据在密码卡内完成 HMAC 计算。")
|
||||
public ApiResponse<Map<String, Object>> hmac(@Valid @RequestBody CryptoHmacInternalRequest request) {
|
||||
HmacRequest hmacRequest = new HmacRequest();
|
||||
hmacRequest.setAlgId(request.getAlgId());
|
||||
@ -95,6 +108,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/digest")
|
||||
@Operation(summary = "计算摘要", description = "使用密码卡对输入数据执行摘要运算。")
|
||||
public ApiResponse<Map<String, Object>> digest(@Valid @RequestBody CryptoDigestInternalRequest request) {
|
||||
DigestRequest digestRequest = new DigestRequest();
|
||||
digestRequest.setAlgId(request.getAlgId());
|
||||
@ -108,6 +122,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/encrypt/plain")
|
||||
@Operation(summary = "明文密钥加密", description = "使用明文对称密钥在密码卡内完成加密。")
|
||||
public ApiResponse<Map<String, Object>> encryptPlain(@Valid @RequestBody CryptoSymmetricInternalRequest request) {
|
||||
SymmetricCryptoRequest cryptoRequest = toSymmetricCryptoRequest(request);
|
||||
byte[] data = pcieCryptoService.encryptWithPlainKey(cryptoRequest).getData();
|
||||
@ -118,6 +133,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/decrypt/plain")
|
||||
@Operation(summary = "明文密钥解密", description = "使用明文对称密钥在密码卡内完成解密。")
|
||||
public ApiResponse<Map<String, Object>> decryptPlain(@Valid @RequestBody CryptoSymmetricInternalRequest request) {
|
||||
SymmetricCryptoRequest cryptoRequest = toSymmetricCryptoRequest(request);
|
||||
byte[] data = pcieCryptoService.decryptWithPlainKey(cryptoRequest).getData();
|
||||
@ -128,6 +144,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/encrypt/kek")
|
||||
@Operation(summary = "KEK 包裹密钥加密", description = "使用 KEK 包裹密钥在密码卡内完成加密。")
|
||||
public ApiResponse<Map<String, Object>> encryptKek(@Valid @RequestBody CryptoSymmetricKekInternalRequest request) {
|
||||
SymmetricKekCryptoRequest cryptoRequest = toSymmetricKekCryptoRequest(request);
|
||||
byte[] data = pcieCryptoService.encryptWithKekWrappedKey(cryptoRequest).getData();
|
||||
@ -138,6 +155,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/decrypt/kek")
|
||||
@Operation(summary = "KEK 包裹密钥解密", description = "使用 KEK 包裹密钥在密码卡内完成解密。")
|
||||
public ApiResponse<Map<String, Object>> decryptKek(@Valid @RequestBody CryptoSymmetricKekInternalRequest request) {
|
||||
SymmetricKekCryptoRequest cryptoRequest = toSymmetricKekCryptoRequest(request);
|
||||
byte[] data = pcieCryptoService.decryptWithKekWrappedKey(cryptoRequest).getData();
|
||||
@ -148,6 +166,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/mac/plain")
|
||||
@Operation(summary = "计算 MAC", description = "使用明文对称密钥在密码卡内完成 MAC 计算。")
|
||||
public ApiResponse<Map<String, Object>> macPlain(@Valid @RequestBody CryptoMacInternalRequest request) {
|
||||
MacCalcRequest macRequest = new MacCalcRequest();
|
||||
macRequest.setAlgId(request.getAlgId());
|
||||
@ -163,6 +182,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/file/create")
|
||||
@Operation(summary = "创建密码卡文件", description = "在密码卡内创建指定名称和容量的文件。")
|
||||
public ApiResponse<Void> createFile(@Valid @RequestBody CryptoFileCreateInternalRequest request) {
|
||||
FileCreateRequest fileCreateRequest = new FileCreateRequest();
|
||||
fileCreateRequest.setFileName(request.getFileName());
|
||||
@ -172,6 +192,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/file/write")
|
||||
@Operation(summary = "写入密码卡文件", description = "向密码卡文件的指定偏移位置写入 Base64 数据。")
|
||||
public ApiResponse<Void> writeFile(@Valid @RequestBody CryptoFileWriteInternalRequest request) {
|
||||
FileWriteRequest fileWriteRequest = new FileWriteRequest();
|
||||
fileWriteRequest.setFileName(request.getFileName());
|
||||
@ -182,6 +203,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/file/read")
|
||||
@Operation(summary = "读取密码卡文件", description = "从密码卡文件指定偏移位置读取指定长度的数据。")
|
||||
public ApiResponse<Map<String, Object>> readFile(@Valid @RequestBody CryptoFileReadInternalRequest request) {
|
||||
FileReadRequest fileReadRequest = new FileReadRequest();
|
||||
fileReadRequest.setFileName(request.getFileName());
|
||||
@ -195,12 +217,14 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/file/delete")
|
||||
@Operation(summary = "删除密码卡文件", description = "删除密码卡内指定文件。")
|
||||
public ApiResponse<Void> deleteFile(@Valid @RequestBody CryptoFileDeleteInternalRequest request) {
|
||||
pcieCryptoService.deleteFile(request.getFileName());
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/kek/generate")
|
||||
@Operation(summary = "生成 KEK", description = "在密码卡指定索引处生成 KEK 密钥。")
|
||||
public ApiResponse<Void> generateKek(@Valid @RequestBody CryptoGenerateKekInternalRequest request) {
|
||||
GenerateKekRequest generateKekRequest = new GenerateKekRequest();
|
||||
generateKekRequest.setKeyBits(request.getKeyBits());
|
||||
@ -210,11 +234,16 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@GetMapping("/kek/status")
|
||||
public ApiResponse<KeyStatusResult> kekStatus(@RequestParam @Min(0) int keyIndex) {
|
||||
@Operation(summary = "查询 KEK 状态", description = "返回指定 KEK 索引位置的密钥状态。")
|
||||
public ApiResponse<KeyStatusResult> kekStatus(
|
||||
@Parameter(description = "KEK 索引,从 0 开始")
|
||||
@RequestParam @Min(0) int keyIndex
|
||||
) {
|
||||
return ApiResponse.success(pcieCryptoService.statusKek(keyIndex));
|
||||
}
|
||||
|
||||
@GetMapping("/lmk/seed-mac")
|
||||
@Operation(summary = "导出 LMK Seed MAC", description = "导出当前 LMK Seed 的 MAC 值。")
|
||||
public ApiResponse<Map<String, Object>> exportLmkSeedMac() {
|
||||
byte[] mac = pcieCryptoService.exportLmkSeedMac();
|
||||
return ApiResponse.success(Map.of(
|
||||
@ -224,11 +253,13 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@GetMapping("/lmk/check")
|
||||
@Operation(summary = "校验 LMK", description = "检查密码卡中的 LMK 是否有效。")
|
||||
public ApiResponse<Boolean> checkLmk() {
|
||||
return ApiResponse.success(pcieCryptoService.checkLmk());
|
||||
}
|
||||
|
||||
@PostMapping("/std/keypair/rsa")
|
||||
@Operation(summary = "生成标准 RSA 密钥对", description = "在密码卡内生成标准 RSA 密钥对并返回公私钥。")
|
||||
public ApiResponse<Map<String, Object>> generateStandardRsaKeyPair(
|
||||
@Valid @RequestBody CryptoGenerateStandardRsaKeyPairInternalRequest request
|
||||
) {
|
||||
@ -246,6 +277,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/std/keypair/ecc")
|
||||
@Operation(summary = "生成标准 ECC 密钥对", description = "在密码卡内生成标准 ECC 密钥对并返回公私钥。")
|
||||
public ApiResponse<Map<String, Object>> generateStandardEccKeyPair(
|
||||
@Valid @RequestBody CryptoGenerateStandardEccKeyPairInternalRequest request
|
||||
) {
|
||||
@ -264,6 +296,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/envelope/exchange/rsa")
|
||||
@Operation(summary = "RSA 数字信封转换", description = "基于 RSA 公钥对输入数字信封执行转换。")
|
||||
public ApiResponse<Map<String, Object>> exchangeEnvelopeRsa(
|
||||
@Valid @RequestBody CryptoExchangeEnvelopeRsaInternalRequest request
|
||||
) {
|
||||
@ -280,6 +313,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/envelope/exchange/ecc")
|
||||
@Operation(summary = "ECC 数字信封转换", description = "基于 ECC 公钥对输入数字信封执行转换。")
|
||||
public ApiResponse<Map<String, Object>> exchangeEnvelopeEcc(
|
||||
@Valid @RequestBody CryptoExchangeEnvelopeEccInternalRequest request
|
||||
) {
|
||||
@ -297,6 +331,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/agreement/data-key/ecc")
|
||||
@Operation(summary = "生成 ECC 协商数据", description = "生成 ECC 协商过程中的响应方公钥和临时公钥数据。")
|
||||
public ApiResponse<Map<String, Object>> generateAgreementDataAndKeyEcc(
|
||||
@Valid @RequestBody CryptoGenerateAgreementDataAndKeyEccInternalRequest request
|
||||
) {
|
||||
@ -320,6 +355,7 @@ public class CryptoCardController {
|
||||
}
|
||||
|
||||
@PostMapping("/agreement/session-key/ecc")
|
||||
@Operation(summary = "生成 ECC 会话密钥", description = "根据协商双方参数生成 ECC 会话密钥及赞助方公钥数据。")
|
||||
public ApiResponse<Map<String, Object>> generateSessionKeyEcc(
|
||||
@Valid @RequestBody CryptoGenerateSessionKeyEccInternalRequest request
|
||||
) {
|
||||
|
||||
@ -4,6 +4,9 @@ import com.cisd.tms.common.api.ApiResponse;
|
||||
import com.cisd.tms.modules.device.dto.DeviceActionResponse;
|
||||
import com.cisd.tms.modules.device.dto.DeviceInfoResponse;
|
||||
import com.cisd.tms.modules.device.service.DeviceService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
@ -12,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/device")
|
||||
@Tag(name = "设备概览", description = "设备基础信息、状态和重启接口")
|
||||
public class DeviceController {
|
||||
|
||||
private final DeviceService deviceService;
|
||||
@ -21,17 +25,23 @@ public class DeviceController {
|
||||
}
|
||||
|
||||
@GetMapping("/info")
|
||||
@Operation(summary = "查询设备信息", description = "返回设备标识、版本和预置产品类型等基础信息。")
|
||||
public ApiResponse<DeviceInfoResponse> info() {
|
||||
return ApiResponse.success(deviceService.info());
|
||||
}
|
||||
|
||||
@GetMapping("/status")
|
||||
@Operation(summary = "查询设备状态", description = "返回当前设备运行状态与版本快照。")
|
||||
public ApiResponse<DeviceInfoResponse> status() {
|
||||
return ApiResponse.success(deviceService.info());
|
||||
}
|
||||
|
||||
@PostMapping("/restart")
|
||||
public ApiResponse<DeviceActionResponse> restart(@RequestHeader(value = "X-User", required = false) String operator) {
|
||||
@Operation(summary = "重启设备服务", description = "受理设备重启请求,并记录触发操作的内部用户。")
|
||||
public ApiResponse<DeviceActionResponse> restart(
|
||||
@Parameter(description = "触发重启的操作人,来自内部鉴权链路透传的 X-User 请求头")
|
||||
@RequestHeader(value = "X-User", required = false) String operator
|
||||
) {
|
||||
return ApiResponse.success(deviceService.restart(operator));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
package com.cisd.tms.modules.device.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "设备动作受理响应")
|
||||
public class DeviceActionResponse {
|
||||
|
||||
@Schema(description = "受理动作名称", example = "restart")
|
||||
private String action;
|
||||
@Schema(description = "是否已受理", example = "true")
|
||||
private boolean accepted;
|
||||
@Schema(description = "操作人", example = "admin")
|
||||
private String operator;
|
||||
@Schema(description = "受理时间", example = "2026-03-11T18:00:00+08:00")
|
||||
private String acceptedAt;
|
||||
|
||||
public String getAction() {
|
||||
|
||||
@ -1,12 +1,21 @@
|
||||
package com.cisd.tms.modules.device.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "设备信息响应")
|
||||
public class DeviceInfoResponse {
|
||||
|
||||
@Schema(description = "设备唯一标识", example = "TMS-DEVICE-01")
|
||||
private String deviceId;
|
||||
@Schema(description = "设备状态", example = "UP")
|
||||
private String status;
|
||||
@Schema(description = "设备版本", example = "1.0.0")
|
||||
private String version;
|
||||
@Schema(description = "预置产品类型", example = "STANDARD")
|
||||
private String presetProductType;
|
||||
@Schema(description = "预置版本", example = "6.6.4")
|
||||
private String presetVersion;
|
||||
@Schema(description = "预置来源", example = "application.yml")
|
||||
private String presetSource;
|
||||
|
||||
public String getDeviceId() {
|
||||
|
||||
@ -4,6 +4,9 @@ import com.cisd.tms.common.api.ApiResponse;
|
||||
import com.cisd.tms.modules.file.dto.FileDetailResponse;
|
||||
import com.cisd.tms.modules.file.dto.FileUploadResponse;
|
||||
import com.cisd.tms.modules.file.service.FileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -14,6 +17,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/files")
|
||||
@Tag(name = "文件管理", description = "初始化文件上传与文件元数据查询接口")
|
||||
public class FileController {
|
||||
|
||||
private final FileService fileService;
|
||||
@ -24,13 +28,21 @@ public class FileController {
|
||||
|
||||
// 内部上传入口,前端先拿到 fileId,再把 fileId 传给初始化相关接口。
|
||||
@PostMapping("/upload")
|
||||
public ApiResponse<FileUploadResponse> upload(@RequestParam("file") MultipartFile file) {
|
||||
@Operation(summary = "上传初始化文件", description = "上传许可证、配置包等初始化依赖文件,返回 fileId 供后续任务引用。")
|
||||
public ApiResponse<FileUploadResponse> upload(
|
||||
@Parameter(description = "待上传的初始化文件")
|
||||
@RequestParam("file") MultipartFile file
|
||||
) {
|
||||
return ApiResponse.success(fileService.upload(file));
|
||||
}
|
||||
|
||||
// 返回已存储的文件元数据,便于调用方确认 fileId 到实际文件的映射关系。
|
||||
@GetMapping("/{fileId}")
|
||||
public ApiResponse<FileDetailResponse> detail(@PathVariable String fileId) {
|
||||
@Operation(summary = "查询文件详情", description = "根据 fileId 返回已存储文件的元数据和当前状态。")
|
||||
public ApiResponse<FileDetailResponse> detail(
|
||||
@Parameter(description = "上传接口返回的文件唯一标识")
|
||||
@PathVariable String fileId
|
||||
) {
|
||||
return ApiResponse.success(fileService.getDetail(fileId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
package com.cisd.tms.modules.file.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "文件详情响应")
|
||||
public class FileDetailResponse {
|
||||
|
||||
@Schema(description = "文件唯一标识", example = "FILE-20260310-000001")
|
||||
private String fileId;
|
||||
@Schema(description = "原始文件名", example = "license.dat")
|
||||
private String originalFilename;
|
||||
@Schema(description = "文件实际存储路径", example = "/home/tms/uploads/FILE-20260310-000001/license.dat")
|
||||
private String storagePath;
|
||||
@Schema(description = "文件状态", example = "ACTIVE")
|
||||
private String status;
|
||||
@Schema(description = "当前文件大小,单位字节", example = "12")
|
||||
private long size;
|
||||
|
||||
public String getFileId() {
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
package com.cisd.tms.modules.file.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "文件上传响应")
|
||||
public class FileUploadResponse {
|
||||
|
||||
@Schema(description = "文件唯一标识", example = "FILE-20260310-000001")
|
||||
private String fileId;
|
||||
@Schema(description = "文件大小,单位字节", example = "12")
|
||||
private long size;
|
||||
@Schema(description = "文件实际存储路径", example = "/home/tms/uploads/FILE-20260310-000001/license.dat")
|
||||
private String storagePath;
|
||||
|
||||
public String getFileId() {
|
||||
|
||||
@ -10,6 +10,9 @@ import com.cisd.tms.modules.init.dto.InitTaskExecuteResponse;
|
||||
import com.cisd.tms.modules.init.dto.InitTaskStepLogResponse;
|
||||
import com.cisd.tms.modules.init.dto.InitTaskStepResponse;
|
||||
import com.cisd.tms.modules.init.service.InitService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@ -22,6 +25,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/init")
|
||||
@Tag(name = "CISD初始化任务", description = "初始化模板、预检、任务创建、执行与日志查询接口")
|
||||
public class InitController {
|
||||
|
||||
private final InitService initService;
|
||||
@ -35,7 +39,11 @@ public class InitController {
|
||||
* 前端据此识别后端预制版本,并渲染对应版本的初始化页面。
|
||||
*/
|
||||
@GetMapping("/template")
|
||||
public ApiResponse<InitPlanTemplateResponse> template(@RequestParam(required = false) String version) {
|
||||
@Operation(summary = "查询初始化模板", description = "返回初始化模板版本、预制产品类型和模板路径等元信息。")
|
||||
public ApiResponse<InitPlanTemplateResponse> template(
|
||||
@Parameter(description = "模板版本,不传时返回默认预制版本")
|
||||
@RequestParam(required = false) String version
|
||||
) {
|
||||
return ApiResponse.success(initService.loadTemplate(version));
|
||||
}
|
||||
|
||||
@ -43,6 +51,7 @@ public class InitController {
|
||||
* 初始化预检:仅做参数校验和步骤数量预估,不落库。
|
||||
*/
|
||||
@PostMapping("/preview")
|
||||
@Operation(summary = "预检初始化请求", description = "校验初始化参数并返回解析后的产品类型与预估步骤数量,不落库。")
|
||||
public ApiResponse<InitPreviewResponse> preview(@Valid @RequestBody InitPreviewRequest request) {
|
||||
return ApiResponse.success(initService.preview(request));
|
||||
}
|
||||
@ -51,17 +60,26 @@ public class InitController {
|
||||
* 创建初始化任务:写入任务主表和步骤表,状态初始化为 PENDING。
|
||||
*/
|
||||
@PostMapping("/tasks")
|
||||
@Operation(summary = "创建初始化任务", description = "根据初始化请求生成任务和步骤记录,任务初始状态为 PENDING。")
|
||||
public ApiResponse<InitCreateTaskResponse> createTask(@Valid @RequestBody InitPreviewRequest request) {
|
||||
return ApiResponse.success(initService.createTask(request));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}")
|
||||
public ApiResponse<InitTaskDetailResponse> taskDetail(@PathVariable String taskId) {
|
||||
@Operation(summary = "查询任务详情", description = "返回初始化任务的主状态、产品类型和原始计划快照。")
|
||||
public ApiResponse<InitTaskDetailResponse> taskDetail(
|
||||
@Parameter(description = "初始化任务唯一标识")
|
||||
@PathVariable String taskId
|
||||
) {
|
||||
return ApiResponse.success(initService.getTask(taskId));
|
||||
}
|
||||
|
||||
@GetMapping("/tasks/{taskId}/steps")
|
||||
public ApiResponse<List<InitTaskStepResponse>> taskSteps(@PathVariable String taskId) {
|
||||
@Operation(summary = "查询任务步骤", description = "返回初始化任务下全部步骤的执行状态和命令信息。")
|
||||
public ApiResponse<List<InitTaskStepResponse>> taskSteps(
|
||||
@Parameter(description = "初始化任务唯一标识")
|
||||
@PathVariable String taskId
|
||||
) {
|
||||
return ApiResponse.success(initService.getTaskSteps(taskId));
|
||||
}
|
||||
|
||||
@ -69,7 +87,13 @@ public class InitController {
|
||||
* 下载单步骤执行日志,用于页面定位失败原因。
|
||||
*/
|
||||
@GetMapping("/tasks/{taskId}/steps/{stepNo}/log")
|
||||
public ApiResponse<InitTaskStepLogResponse> taskStepLog(@PathVariable String taskId, @PathVariable int stepNo) {
|
||||
@Operation(summary = "查询步骤日志", description = "返回指定任务步骤的日志路径和日志内容,用于定位失败原因。")
|
||||
public ApiResponse<InitTaskStepLogResponse> taskStepLog(
|
||||
@Parameter(description = "初始化任务唯一标识")
|
||||
@PathVariable String taskId,
|
||||
@Parameter(description = "任务步骤序号,从 1 开始")
|
||||
@PathVariable int stepNo
|
||||
) {
|
||||
return ApiResponse.success(initService.getTaskStepLog(taskId, stepNo));
|
||||
}
|
||||
|
||||
@ -78,7 +102,11 @@ public class InitController {
|
||||
* 接口只负责启动后台执行并立即返回,前端需继续轮询任务详情、步骤状态和步骤日志。
|
||||
*/
|
||||
@PostMapping("/tasks/{taskId}/execute")
|
||||
public ApiResponse<InitTaskExecuteResponse> executeTask(@PathVariable String taskId) {
|
||||
@Operation(summary = "执行初始化任务", description = "异步受理初始化任务执行请求,立即返回当前任务快照,实际步骤在后台继续运行。")
|
||||
public ApiResponse<InitTaskExecuteResponse> executeTask(
|
||||
@Parameter(description = "初始化任务唯一标识")
|
||||
@PathVariable String taskId
|
||||
) {
|
||||
return ApiResponse.success(initService.executeTask(taskId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "初始化任务创建响应")
|
||||
public class InitCreateTaskResponse {
|
||||
|
||||
@Schema(description = "初始化任务唯一标识", example = "INIT-20260310-000001")
|
||||
private String taskId;
|
||||
@Schema(description = "任务状态", example = "PENDING")
|
||||
private String status;
|
||||
@Schema(description = "解析后的产品类型", example = "STANDARD")
|
||||
private String resolvedProductType;
|
||||
@Schema(description = "命中的预制版本", example = "6.6.4")
|
||||
private String presetVersion;
|
||||
|
||||
public String getTaskId() {
|
||||
|
||||
@ -1,12 +1,18 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "初始化模板元信息")
|
||||
public class InitPlanTemplateResponse {
|
||||
|
||||
@Schema(description = "预制产品类型", example = "STANDARD")
|
||||
private String presetProductType;
|
||||
@Schema(description = "预制模板版本", example = "6.6.4")
|
||||
private String presetVersion;
|
||||
@Schema(description = "支持的模板版本列表")
|
||||
private List<String> supportedVersions;
|
||||
@Schema(description = "模板文件路径", example = "classpath:initplan/standard-6.6.4.json")
|
||||
private String templatePath;
|
||||
|
||||
public String getPresetProductType() {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
@ -8,6 +9,7 @@ import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "初始化预检与任务创建请求")
|
||||
public class InitPreviewRequest {
|
||||
|
||||
private static final String IPV4_REGEX =
|
||||
@ -16,39 +18,49 @@ public class InitPreviewRequest {
|
||||
|
||||
@NotBlank(message = "orgCodeType is required")
|
||||
@Pattern(regexp = "BIC|CIPSID|LEI", message = "orgCodeType must be BIC/CIPSID/LEI")
|
||||
@Schema(description = "机构代码类型", example = "BIC")
|
||||
private String orgCodeType;
|
||||
|
||||
@NotBlank(message = "orgCode is required")
|
||||
@Size(max = 32, message = "orgCode length must be <= 32")
|
||||
@Schema(description = "机构代码", example = "BKCHCNBJXXX")
|
||||
private String orgCode;
|
||||
|
||||
@NotBlank(message = "orgNameCn is required")
|
||||
@Size(max = 128, message = "orgNameCn length must be <= 128")
|
||||
@Schema(description = "机构中文名称", example = "中国银行")
|
||||
private String orgNameCn;
|
||||
|
||||
@Size(max = 256, message = "orgNameEn length must be <= 256")
|
||||
@Schema(description = "机构英文名称", example = "Bank of China")
|
||||
private String orgNameEn;
|
||||
|
||||
@NotBlank(message = "deployMode is required")
|
||||
@Pattern(regexp = "SINGLE|DUAL|QUAD", message = "deployMode must be SINGLE/DUAL/QUAD")
|
||||
@Schema(description = "部署模式", example = "DUAL")
|
||||
private String deployMode;
|
||||
|
||||
@Valid
|
||||
@NotNull(message = "nodes is required")
|
||||
@Schema(description = "节点 IP 配置")
|
||||
private Nodes nodes;
|
||||
|
||||
@Valid
|
||||
@NotNull(message = "mq is required")
|
||||
@Schema(description = "消息中间件配置")
|
||||
private Mq mq;
|
||||
|
||||
@Valid
|
||||
@NotNull(message = "licenses is required")
|
||||
@Schema(description = "初始化许可证与配置包引用")
|
||||
private Licenses licenses;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "签名服务配置,直参版按需填写")
|
||||
private SignServer signServer;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "直连 TLQ 配置,直参版按需填写")
|
||||
private DirectTlqConfig directTlq;
|
||||
|
||||
public String getOrgCodeType() {
|
||||
@ -131,19 +143,24 @@ public class InitPreviewRequest {
|
||||
this.directTlq = directTlq;
|
||||
}
|
||||
|
||||
@Schema(description = "节点 IP 配置")
|
||||
public static class Nodes {
|
||||
|
||||
@NotBlank(message = "nodes.node01Ip is required")
|
||||
@Pattern(regexp = IPV4_REGEX, message = "nodes.node01Ip must be a valid IPv4")
|
||||
@Schema(description = "节点 1 IP", example = "192.168.1.11")
|
||||
private String node01Ip;
|
||||
|
||||
@Pattern(regexp = IPV4_REGEX, message = "nodes.node02Ip must be a valid IPv4")
|
||||
@Schema(description = "节点 2 IP", example = "192.168.1.12")
|
||||
private String node02Ip;
|
||||
|
||||
@Pattern(regexp = IPV4_REGEX, message = "nodes.node03Ip must be a valid IPv4")
|
||||
@Schema(description = "节点 3 IP", example = "192.168.1.13")
|
||||
private String node03Ip;
|
||||
|
||||
@Pattern(regexp = IPV4_REGEX, message = "nodes.node04Ip must be a valid IPv4")
|
||||
@Schema(description = "节点 4 IP", example = "192.168.1.14")
|
||||
private String node04Ip;
|
||||
|
||||
public String getNode01Ip() {
|
||||
@ -179,25 +196,31 @@ public class InitPreviewRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "消息中间件配置")
|
||||
public static class Mq {
|
||||
|
||||
@NotBlank(message = "mq.mqType is required")
|
||||
@Schema(description = "消息中间件类型", example = "RABBITMQ")
|
||||
private String mqType;
|
||||
|
||||
@NotBlank(message = "mq.channelUsername is required")
|
||||
@Size(max = 32, message = "mq.channelUsername length must be <= 32")
|
||||
@Schema(description = "通道用户名", example = "tms")
|
||||
private String channelUsername;
|
||||
|
||||
@NotBlank(message = "mq.channelPassword is required")
|
||||
@Size(max = 128, message = "mq.channelPassword length must be <= 128")
|
||||
@Schema(description = "通道密码")
|
||||
private String channelPassword;
|
||||
|
||||
@Size(max = 128, message = "mq.tlqLicenseFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "mq.tlqLicenseFileId must be a fileId")
|
||||
@Schema(description = "TLQ 许可证文件 fileId", example = "FILE-20260310-000001")
|
||||
private String tlqLicenseFileId;
|
||||
|
||||
@Size(max = 128, message = "mq.cfmqConfigFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "mq.cfmqConfigFileId must be a fileId")
|
||||
@Schema(description = "CFMQ 配置文件 fileId", example = "FILE-20260310-000002")
|
||||
private String cfmqConfigFileId;
|
||||
|
||||
public String getMqType() {
|
||||
@ -241,15 +264,18 @@ public class InitPreviewRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "许可证和配置包引用")
|
||||
public static class Licenses {
|
||||
|
||||
@NotBlank(message = "licenses.receiverLicenseFileId is required")
|
||||
@Size(max = 128, message = "licenses.receiverLicenseFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "licenses.receiverLicenseFileId must be a fileId")
|
||||
@Schema(description = "接收端许可证文件 fileId", example = "FILE-20260310-000003")
|
||||
private String receiverLicenseFileId;
|
||||
|
||||
@Size(max = 128, message = "licenses.cfgZipFileId length must be <= 128")
|
||||
@Pattern(regexp = FILE_ID_REGEX, message = "licenses.cfgZipFileId must be a fileId")
|
||||
@Schema(description = "标准版配置包 zip 文件 fileId", example = "FILE-20260310-000004")
|
||||
private String cfgZipFileId;
|
||||
|
||||
public String getReceiverLicenseFileId() {
|
||||
@ -269,16 +295,20 @@ public class InitPreviewRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "签名服务配置")
|
||||
public static class SignServer {
|
||||
|
||||
@Size(max = 64, message = "signServer.signHost length must be <= 64")
|
||||
@Schema(description = "签名服务地址", example = "10.0.0.20")
|
||||
private String signHost;
|
||||
|
||||
@NotNull(message = "signServer.signPort must not be null")
|
||||
@Max(value = 65535, message = "signServer.signPort must be <= 65535")
|
||||
@Schema(description = "签名服务端口", example = "8088")
|
||||
private Integer signPort;
|
||||
|
||||
@Pattern(regexp = "Infosec|CFCA", message = "signServer.signType must be Infosec/CFCA")
|
||||
@Schema(description = "签名服务厂商类型", example = "CFCA")
|
||||
private String signType;
|
||||
|
||||
@Size(max = 512, message = "signServer.signCertDn length must be <= 512")
|
||||
@ -339,16 +369,20 @@ public class InitPreviewRequest {
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "直连 TLQ 配置")
|
||||
public static class DirectTlqConfig {
|
||||
|
||||
@Pattern(regexp = "A|B", message = "directTlq.localNodeSeq must be A/B")
|
||||
@Schema(description = "本节点序号", example = "A")
|
||||
private String localNodeSeq;
|
||||
|
||||
@Size(max = 256, message = "directTlq.upstreamInstitutions length must be <= 256")
|
||||
@Schema(description = "上游机构列表", example = "BANKA,BANKB")
|
||||
private String upstreamInstitutions;
|
||||
|
||||
@Min(value = 1, message = "directTlq.upstreamCocCount must be >= 1")
|
||||
@Max(value = 2, message = "directTlq.upstreamCocCount must be <= 2")
|
||||
@Schema(description = "上游 COC 数量", example = "2")
|
||||
private Integer upstreamCocCount;
|
||||
|
||||
public String getLocalNodeSeq() {
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "初始化预检响应")
|
||||
public class InitPreviewResponse {
|
||||
|
||||
@Schema(description = "解析后的产品类型", example = "STANDARD")
|
||||
private String resolvedProductType;
|
||||
@Schema(description = "命中的预制版本", example = "6.6.4")
|
||||
private String presetVersion;
|
||||
@Schema(description = "预计执行步骤数", example = "18")
|
||||
private int stepCount;
|
||||
@Schema(description = "预检摘要说明")
|
||||
private String summary;
|
||||
|
||||
public String getResolvedProductType() {
|
||||
|
||||
@ -1,14 +1,25 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "初始化任务详情")
|
||||
public class InitTaskDetailResponse {
|
||||
|
||||
@Schema(description = "初始化任务唯一标识", example = "INIT-20260310-000001")
|
||||
private String taskId;
|
||||
@Schema(description = "任务状态", example = "RUNNING")
|
||||
private String status;
|
||||
@Schema(description = "解析后的产品类型", example = "STANDARD")
|
||||
private String resolvedProductType;
|
||||
@Schema(description = "命中的预制版本", example = "6.6.4")
|
||||
private String presetVersion;
|
||||
@Schema(description = "机构代码", example = "BKCHCNBJXXX")
|
||||
private String orgCode;
|
||||
@Schema(description = "消息中间件类型", example = "RABBITMQ")
|
||||
private String mqType;
|
||||
@Schema(description = "部署模式", example = "DUAL")
|
||||
private String deployMode;
|
||||
@Schema(description = "初始化计划原始 JSON 快照")
|
||||
private String initPlanJson;
|
||||
|
||||
public String getTaskId() {
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "初始化任务执行受理响应")
|
||||
public class InitTaskExecuteResponse {
|
||||
|
||||
@Schema(description = "初始化任务唯一标识", example = "INIT-20260310-000001")
|
||||
private String taskId;
|
||||
@Schema(description = "任务当前状态", example = "RUNNING")
|
||||
private String status;
|
||||
@Schema(description = "总步骤数", example = "18")
|
||||
private int totalSteps;
|
||||
@Schema(description = "已成功步骤数", example = "6")
|
||||
private int successSteps;
|
||||
|
||||
public String getTaskId() {
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "初始化步骤日志详情")
|
||||
public class InitTaskStepLogResponse {
|
||||
|
||||
@Schema(description = "初始化任务唯一标识", example = "INIT-20260310-000001")
|
||||
private String taskId;
|
||||
@Schema(description = "步骤序号", example = "3")
|
||||
private int stepNo;
|
||||
@Schema(description = "步骤编码", example = "DB_APPLY")
|
||||
private String stepCode;
|
||||
@Schema(description = "日志文件路径")
|
||||
private String logPath;
|
||||
@Schema(description = "日志内容")
|
||||
private String content;
|
||||
|
||||
public String getTaskId() {
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
package com.cisd.tms.modules.init.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "初始化任务步骤状态")
|
||||
public class InitTaskStepResponse {
|
||||
|
||||
@Schema(description = "步骤序号", example = "1")
|
||||
private int stepNo;
|
||||
@Schema(description = "步骤编码", example = "DB_APPLY")
|
||||
private String stepCode;
|
||||
@Schema(description = "步骤状态", example = "SUCCESS")
|
||||
private String status;
|
||||
@Schema(description = "步骤执行摘要")
|
||||
private String message;
|
||||
@Schema(description = "执行命令行")
|
||||
private String commandLine;
|
||||
@Schema(description = "步骤日志路径")
|
||||
private String logPath;
|
||||
@Schema(description = "命令退出码", example = "0")
|
||||
private Integer exitCode;
|
||||
|
||||
public int getStepNo() {
|
||||
|
||||
@ -11,6 +11,8 @@ import com.cisd.tms.modules.mk.enums.MasterKeyStatus;
|
||||
import com.cisd.tms.modules.mk.service.LmkService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -30,6 +32,7 @@ import java.util.List;
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/v1")
|
||||
@Tag(name = "主密钥管理", description = "LMK 生成、恢复、状态查询和销毁接口")
|
||||
public class LmkController {
|
||||
|
||||
@Resource
|
||||
@ -42,6 +45,7 @@ public class LmkController {
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/masterkey/generate")
|
||||
@Operation(summary = "生成主密钥", description = "生成新的主密钥并返回密钥信息。")
|
||||
public ApiResponse<LMK> generateMasterKey() {
|
||||
LMK lmk = lmkService.generateLMK();
|
||||
return ApiResponse.success(lmk);
|
||||
@ -53,6 +57,7 @@ public class LmkController {
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/masterkey/uKeySignAndKeyComponent")
|
||||
@Operation(summary = "UKey 签名并获取密钥分量", description = "根据 UKey 签名结果导出内部密钥公钥并返回主密钥分量。")
|
||||
public ApiResponse<UKeySignResult> signAndKeyComponent(@RequestBody UKeySignDTO uKeySignDTO) {
|
||||
String iPubKey = lmkService.exportIkPublicKeyHex();
|
||||
String payload;
|
||||
@ -68,6 +73,7 @@ public class LmkController {
|
||||
}
|
||||
|
||||
@GetMapping("/masterKeyStatus")
|
||||
@Operation(summary = "查询主密钥状态", description = "返回主密钥当前是否存在以及相关状态详情。")
|
||||
public ApiResponse<MasterKeyStatus.StatusDetail> getMasterKeyStatus() {
|
||||
return ApiResponse.success(lmkService.getMasterKeyStatus());
|
||||
}
|
||||
@ -80,6 +86,7 @@ public class LmkController {
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@PostMapping("/recoveryMasterKey")
|
||||
@Operation(summary = "恢复主密钥", description = "根据上传的分量或序列化载荷恢复主密钥,并在需要时重建内部密钥。")
|
||||
public ApiResponse<RecoveryResult> recoveryMasterKey(@RequestBody MasterKeyRestoreDTO masterKeyRestoreDTO)
|
||||
throws IOException, ClassNotFoundException {
|
||||
RecoveryResult result;
|
||||
@ -103,6 +110,7 @@ public class LmkController {
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/masterKeyRecovery")
|
||||
@Operation(summary = "分量合成恢复主密钥", description = "根据主密钥分量直接合成并恢复主密钥。")
|
||||
public ApiResponse<LMK> masterKeyRecovery(@RequestBody MasterKeyRestoreDTO masterKeyRestoreDTO) {
|
||||
String masterKey = lmkService.masterKeyCompose(masterKeyRestoreDTO.getComponents());
|
||||
LMK lmk = lmkService.recoveryLMK1(LMK.getInstance(masterKey, masterKeyRestoreDTO));
|
||||
@ -110,6 +118,7 @@ public class LmkController {
|
||||
}
|
||||
|
||||
@GetMapping("/destroyLMKAndIK")
|
||||
@Operation(summary = "销毁主密钥和内部密钥", description = "销毁当前主密钥以及相关内部密钥材料。")
|
||||
public ApiResponse<String> destroyLMKAndIK() {
|
||||
lmkService.destroyLMKAndIK();
|
||||
return ApiResponse.success("主密钥和内部密钥销毁成功!");
|
||||
|
||||
@ -6,6 +6,8 @@ import com.cisd.tms.modules.sign.dto.internal.InternalSignPreviewResponse;
|
||||
import com.cisd.tms.modules.sign.service.SignService;
|
||||
import com.cisd.tms.modules.sign.support.SignCommand;
|
||||
import com.cisd.tms.modules.sign.support.SignResult;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@ -14,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/sign")
|
||||
@Tag(name = "签名预览", description = "内部签名预览与摘要计算接口")
|
||||
public class SignController {
|
||||
|
||||
private final SignService signService;
|
||||
@ -23,6 +26,7 @@ public class SignController {
|
||||
}
|
||||
|
||||
@PostMapping("/preview")
|
||||
@Operation(summary = "预览签名摘要", description = "根据明文和算法生成摘要预览结果,不落库。")
|
||||
public ApiResponse<InternalSignPreviewResponse> preview(@Valid @RequestBody InternalSignPreviewRequest request) {
|
||||
SignCommand command = new SignCommand();
|
||||
command.setAlgorithm(request.getAlgorithm());
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
package com.cisd.tms.modules.sign.dto.internal;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Schema(description = "内部签名预览请求")
|
||||
public class InternalSignPreviewRequest {
|
||||
|
||||
@NotBlank(message = "plainText is required")
|
||||
@Schema(description = "待签名明文", example = "hello-tms")
|
||||
private String plainText;
|
||||
|
||||
@Schema(description = "签名算法,不传时使用默认算法", example = "SHA256withRSA")
|
||||
private String algorithm;
|
||||
|
||||
public String getPlainText() {
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
package com.cisd.tms.modules.sign.dto.internal;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "内部签名预览响应")
|
||||
public class InternalSignPreviewResponse {
|
||||
|
||||
@Schema(description = "实际使用的签名算法", example = "SHA256withRSA")
|
||||
private String algorithm;
|
||||
@Schema(description = "摘要十六进制串")
|
||||
private String digestHex;
|
||||
|
||||
public String getAlgorithm() {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.cisd.tms.modules.system.controller;
|
||||
|
||||
import com.cisd.tms.common.api.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import java.util.Map;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -8,9 +10,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/system")
|
||||
@Tag(name = "系统状态", description = "服务健康检查接口")
|
||||
public class HealthController {
|
||||
|
||||
@GetMapping("/health")
|
||||
@Operation(summary = "查询服务健康状态", description = "返回当前服务是否可用以及服务名称等基础健康信息。")
|
||||
public ApiResponse<Map<String, Object>> status() {
|
||||
return ApiResponse.success(Map.of(
|
||||
"status", "UP",
|
||||
|
||||
@ -1,12 +1,36 @@
|
||||
package com.cisd.tms;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
class TmsApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGenerateAnnotatedInternalApiDocs() throws Exception {
|
||||
mockMvc.perform(get("/v3/api-docs/internal-api"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"description\":\"CISD TMS 内部业务接口文档\"")))
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"/api/v1/init/template\"")))
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"summary\":\"查询初始化模板\"")))
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"/api/v1/files/upload\"")))
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"summary\":\"上传初始化文件\"")))
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"/api/v1/auth/login\"")))
|
||||
.andExpect(content().string(org.hamcrest.Matchers.containsString("\"summary\":\"内部用户登录\"")));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user