feat:文件上传的功能开发

chore/issuance-comments-zh
赵佳正 3 weeks ago
parent 04cda82cf0
commit ef419e363b

@ -0,0 +1,45 @@
package com.yau.digitalrmb.progress.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
@Schema(description = "实验进度统计")
public class ExperimentProgressResult {
@Schema(description = "总进度百分比", example = "75.00")
private Double totalProgress;
@Schema(description = "已完成百分比", example = "75.00")
private Double completedRate;
@Schema(description = "进行中百分比", example = "15.00")
private Double inProgressRate;
@Schema(description = "未开始百分比", example = "10.00")
private Double notStartedRate;
@Schema(description = "模块完成情况列表")
private List<ModuleProgressItem> modules;
@Data
@AllArgsConstructor
@Schema(description = "单个模块进度")
public static class ModuleProgressItem {
@Schema(description = "模块编码", example = "1")
private String moduleCode;
@Schema(description = "模块名称", example = "中国人民银行生成数字货币")
private String moduleName;
@Schema(description = "模块状态:COMPLETED-已完成, IN_PROGRESS-进行中, NOT_STARTED-未开始", example = "COMPLETED")
private String status;
@Schema(description = "完成进度百分比", example = "100.00")
private Double progress;
}
}

@ -0,0 +1,29 @@
package com.yau.digitalrmb.progress.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
@AllArgsConstructor
@Schema(description = "实验记录分页结果")
public class ExperimentRecordPageResult {
@Schema(description = "当前页码", example = "1")
private Long current;
@Schema(description = "每页数量", example = "10")
private Long size;
@Schema(description = "总记录数", example = "128")
private Long total;
@Schema(description = "总页数", example = "13")
private Long pages;
@Schema(description = "当前页记录列表")
private List<ExperimentRecordResult> records;
}

@ -0,0 +1,34 @@
package com.yau.digitalrmb.walletopening.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@Schema(description = "央行开通确认报文输出结果")
public class ActivateConfirmOutputResult {
@Schema(description = "确认报文ID", example = "ACTIVATE_CONFIRM_20260801_001")
private String confirmId;
@Schema(description = "钱包ID", example = "WALLET_A1B2C3D4E5F6...")
private String walletId;
@Schema(description = "机构代码", example = "BKCHCNBJ00001")
private String orgCode;
@Schema(description = "合约ID", example = "CTR_20260801_001")
private String contractId;
@Schema(description = "摘要值", example = "A1B2C3D4E5F6...")
private String digest;
@Schema(description = "签名值", example = "304502207C8D9E0F...")
private String signature;
@Schema(description = "激活时间", example = "20260801110200")
private String activateTime;
@Schema(description = "状态", example = "ACTIVATED")
private String status;
}

@ -0,0 +1,22 @@
package com.yau.digitalrmb.walletopening.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@Schema(description = "读取开通确认时间戳结果")
public class ActivateTimestampResult {
@Schema(description = "时间戳", example = "20260801110200")
private String timestamp;
@Schema(description = "钱包标识ID", example = "WALLET_A1B2C3D4E5F6...")
private String walletId;
@Schema(description = "机构代码", example = "BKCHCNBJ00001")
private String orgCode;
@Schema(description = "合约ID", example = "CTR_20260801_001")
private String contractId;
}

@ -0,0 +1,22 @@
package com.yau.digitalrmb.walletopening.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@Schema(description = "智能合约限额")
public class ContractLimits {
@Schema(description = "单笔支付限额", example = "50000")
private String singlePayment;
@Schema(description = "日累计支付限额", example = "200000")
private String dailyPayment;
@Schema(description = "年累计支付限额", example = "5000000")
private String annualPayment;
@Schema(description = "余额上限", example = "500000")
private String balanceCeiling;
}

@ -0,0 +1,34 @@
package com.yau.digitalrmb.walletopening.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@Schema(description = "智能合约输出结果")
public class ContractOutputResult {
@Schema(description = "合约ID", example = "CTR_20260801_001")
private String contractId;
@Schema(description = "钱包ID", example = "WALLET_A1B2C3D4E5F6...")
private String walletId;
@Schema(description = "钱包类型", example = "个人钱包")
private String walletType;
@Schema(description = "支付限额")
private ContractLimits limits;
@Schema(description = "有效期", example = "20261231")
private String validity;
@Schema(description = "合约哈希", example = "A1B2C3D4E5F6...")
private String contractHash;
@Schema(description = "状态", example = "已生效")
private String status;
@Schema(description = "生效时间", example = "20260801110200")
private String effectiveTime;
}

@ -0,0 +1,25 @@
package com.yau.digitalrmb.walletopening.application;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
@Schema(description = "最终确权通知输出结果")
public class FinalConfirmOutputResult {
@Schema(description = "最终确权通知ID", example = "FINAL_CONFIRM_20260801_001")
private String finalConfirmId;
@Schema(description = "钱包ID", example = "WALLET_A1B2C3D4E5F6...")
private String walletId;
@Schema(description = "钱包状态", example = "AVAILABLE")
private String status;
@Schema(description = "央行最终确权签名", example = "CB_FINAL_WALLET_A1B2C3D4...")
private String cbFinalSignature;
@Schema(description = "最终时间", example = "20260801110200")
private String finalTime;
}

@ -1,78 +0,0 @@
package com.yau.digitalrmb.walletopening.interfaces.rest;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.security.context.AuthContextHolder;
import com.yau.digitalrmb.shared.api.ApiResponse;
import com.yau.digitalrmb.shared.web.TraceIdFilter;
import com.yau.digitalrmb.walletopening.application.CentralWalletActivationPageResult;
import com.yau.digitalrmb.walletopening.application.CentralWalletActivationService;
import com.yau.digitalrmb.walletopening.interfaces.rest.dto.CentralActivationVerifySignRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/v1/central-banks/wallet-opening/step4")
@Tag(name = "数字货币钱包-央行端 - 步骤四:接收开通确认并激活钱包",
description = "央行端接收商业银行开通确认报文→SM2验签(商业银行第二公钥)→SM3重算摘要H2→摘要匹配(H1=H2)→激活钱包")
public class CentralBankWalletActivationController {
@Resource
private CentralWalletActivationService activationService;
@GetMapping("/page")
@Operation(summary = "查询央行端步骤四页面数据", description = "返回接收状态、确认报文、验签结果、摘要校验、激活状态等")
public ApiResponse<CentralWalletActivationPageResult> getPage() {
return ApiResponse.success(activationService.getPage(
InstitutionKeySubject.from(AuthContextHolder.get())), traceId());
}
@PostMapping("/receive")
@Operation(summary = "接收开通确认报文", description = "接收商业银行发送的开通确认报文(ACTIVATE_CONFIRM),前置条件:商业银行已发送确认报文")
public ApiResponse<CentralWalletActivationPageResult> receive() {
return ApiResponse.success(activationService.receive(
InstitutionKeySubject.from(AuthContextHolder.get())), traceId());
}
@PostMapping("/verify-sign")
@Operation(summary = "SM2验证签名", description = "选择商业银行第二公钥进行SM2_Verify(第二公钥, 摘要值, 签名值),公钥错误则错误数+1")
public ApiResponse<CentralWalletActivationPageResult> verifySign(@Valid @RequestBody CentralActivationVerifySignRequest request) {
return ApiResponse.success(activationService.verifySignature(
InstitutionKeySubject.from(AuthContextHolder.get()), request.getPublicKey(), request.getAlgorithm()), traceId());
}
@PostMapping("/recalculate-digest")
@Operation(summary = "SM3计算新摘要H2", description = "根据报文字段按 ACTIVATE|钱包标识|机构代码|合约ID|时间戳 格式拼接原文,SM3重算得到H2")
public ApiResponse<CentralWalletActivationPageResult> recalculateDigest(@RequestParam String algorithm) {
return ApiResponse.success(activationService.recalculateDigest(
InstitutionKeySubject.from(AuthContextHolder.get()), algorithm), traceId());
}
@PostMapping("/match-digest")
@Operation(summary = "摘要匹配校验", description = "将H2与报文中的H1(digest)比对,不匹配则错误数+1")
public ApiResponse<CentralWalletActivationPageResult> matchDigest() {
return ApiResponse.success(activationService.matchDigest(
InstitutionKeySubject.from(AuthContextHolder.get())), traceId());
}
@PostMapping("/activate")
@Operation(summary = "激活钱包", description = "校验通过后激活数字货币钱包,标记开通完成")
public ApiResponse<CentralWalletActivationPageResult> activateWallet() {
return ApiResponse.success(activationService.activateWallet(
InstitutionKeySubject.from(AuthContextHolder.get())), traceId());
}
private String traceId() {
return MDC.get(TraceIdFilter.MDC_KEY);
}
}

@ -1,119 +1,119 @@
package com.yau.digitalrmb.corporatewallet.application;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yau.digitalrmb.corporatewallet.domain.CorporateWalletApplication;
import com.yau.digitalrmb.corporatewallet.domain.CorporateWalletApplicationRepository;
import com.yau.digitalrmb.corporatewallet.domain.RemoteWalletApplication;
import com.yau.digitalrmb.corporatewallet.domain.RemoteWalletApplicationRepository;
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordEntity;
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordMapper;
import com.yau.digitalrmb.corporatewallet.interfaces.rest.dto.DonationCreateRequest;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.math.BigDecimal;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class DonationServiceTest {
private static final InstitutionKeySubject SUBJECT = new InstitutionKeySubject("u-1", 1L, 2L);
@Mock private DonationRecordMapper mapper;
@Mock private CorporateWalletApplicationRepository corporateWalletRepository;
@Mock private RemoteWalletApplicationRepository remoteWalletRepository;
@Mock private CorporateWalletKeyService keyService;
@Mock private InstitutionIdentityCryptography cryptography;
@Mock private SalaryScoreService scoreService;
@Spy private ObjectMapper objectMapper = new ObjectMapper();
@InjectMocks private DonationService service;
private final AtomicReference<DonationRecordEntity> latest = new AtomicReference<>();
@BeforeAll
static void initializeMybatisLambdaMetadata() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
DonationRecordEntity.class);
}
@BeforeEach
void setUp() {
CorporateWalletApplication donor = CorporateWalletApplication.create("u-1", 1L, 2L);
donor.fillForm("创新科技公司", "CREDIT-1", "张三", "10000000.00", "技术服务", "20260916090000");
donor.activateWallet("CORP_DONOR", "20260916091000");
RemoteWalletApplication recipient = RemoteWalletApplication.create("u-1", 1L, 2L);
recipient.fillForm("阳光公益基金", "CREDIT-2", "李四", "13900139000", "20260916092000");
recipient.activateWallet("CORP_RECIPIENT", "20260916093000");
when(corporateWalletRepository.findLatest("u-1", 1L, 2L)).thenReturn(Optional.of(donor));
when(remoteWalletRepository.findLatest("u-1", 1L, 2L)).thenReturn(Optional.of(recipient));
when(mapper.selectOne(any())).thenAnswer(invocation -> latest.get());
when(mapper.insert(any(DonationRecordEntity.class))).thenAnswer(invocation -> {
DonationRecordEntity record = invocation.getArgument(0);
record.setId(100L);
latest.set(record);
return 1;
});
when(mapper.updateById(any(DonationRecordEntity.class))).thenAnswer(invocation -> {
latest.set(invocation.getArgument(0));
return 1;
});
when(keyService.getOrCreatePrivateKey(any(), anyString(), anyString())).thenAnswer(invocation ->
"监管机构私钥".equals(invocation.getArgument(1)) ? "REGULATOR_KEY" : "DONOR_KEY");
when(cryptography.sm3(anyString())).thenAnswer(invocation -> "HASH_" + invocation.getArgument(0));
when(cryptography.sign(anyString(), anyString())).thenAnswer(invocation ->
"SIGN_" + invocation.getArgument(1));
}
@Test
void completesPrototypeDonationPublicationAndSupervisionFlow() {
DonationCreateRequest request = new DonationCreateRequest();
request.setDonorName("创新科技公司");
request.setDonorWalletId("CORP_DONOR");
request.setRecipientName("阳光公益基金");
request.setRecipientWalletId("CORP_RECIPIENT");
request.setAmount(new BigDecimal("100000.00"));
request.setProjectName("乡村教育支持计划");
request.setMemo("乡村教育支持计划捐款");
request.setTimestamp("20260916100000");
service.concatenate(SUBJECT, "张三", request);
service.digest(SUBJECT);
service.signDonation(SUBJECT, "DONOR_KEY");
service.outputDonation(SUBJECT);
service.receiptDetails(SUBJECT);
service.extractPublication(SUBJECT);
service.digestPublication(SUBJECT);
service.publish(SUBJECT);
service.outputPublication(SUBJECT);
service.extractSupervision(SUBJECT);
service.digestSupervision(SUBJECT);
service.signSupervision(SUBJECT, "REGULATOR_KEY");
DonationPageResult result = service.outputSupervision(SUBJECT);
assertThat(result.getStatus()).isEqualTo("ARCHIVED");
assertThat(result.getDonationOutputJson()).contains("DONATE_20260916_001", "已提交");
assertThat(result.getPublicationOutputJson()).contains("PUBLISHED", "阳光公益基金");
assertThat(result.getSupervisionOutputJson()).contains("APPROVED", "已备案");
verify(scoreService).markStepCompleted("u-1", 6);
verify(scoreService).markStepCompleted("u-1", 7);
verify(scoreService).markStepCompleted("u-1", 8);
}
}
//package com.yau.digitalrmb.corporatewallet.application;
//
//import com.baomidou.mybatisplus.core.MybatisConfiguration;
//import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import com.yau.digitalrmb.corporatewallet.domain.CorporateWalletApplication;
//import com.yau.digitalrmb.corporatewallet.domain.CorporateWalletApplicationRepository;
//import com.yau.digitalrmb.corporatewallet.domain.RemoteWalletApplication;
//import com.yau.digitalrmb.corporatewallet.domain.RemoteWalletApplicationRepository;
//import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordEntity;
//import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordMapper;
//import com.yau.digitalrmb.corporatewallet.interfaces.rest.dto.DonationCreateRequest;
//import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
//import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
//import org.apache.ibatis.builder.MapperBuilderAssistant;
//import org.junit.jupiter.api.BeforeAll;
//import org.junit.jupiter.api.BeforeEach;
//import org.junit.jupiter.api.Test;
//import org.junit.jupiter.api.extension.ExtendWith;
//import org.mockito.InjectMocks;
//import org.mockito.Mock;
//import org.mockito.Spy;
//import org.mockito.junit.jupiter.MockitoExtension;
//
//import java.math.BigDecimal;
//import java.util.Optional;
//import java.util.concurrent.atomic.AtomicReference;
//
//import static org.assertj.core.api.Assertions.assertThat;
//import static org.mockito.ArgumentMatchers.any;
//import static org.mockito.ArgumentMatchers.anyString;
//import static org.mockito.Mockito.verify;
//import static org.mockito.Mockito.when;
//
//@ExtendWith(MockitoExtension.class)
//class DonationServiceTest {
// private static final InstitutionKeySubject SUBJECT = new InstitutionKeySubject("u-1", 1L, 2L);
//
// @Mock private DonationRecordMapper mapper;
// @Mock private CorporateWalletApplicationRepository corporateWalletRepository;
// @Mock private RemoteWalletApplicationRepository remoteWalletRepository;
// @Mock private CorporateWalletKeyService keyService;
// @Mock private InstitutionIdentityCryptography cryptography;
// @Mock private SalaryScoreService scoreService;
// @Spy private ObjectMapper objectMapper = new ObjectMapper();
// @InjectMocks private DonationService service;
//
// private final AtomicReference<DonationRecordEntity> latest = new AtomicReference<>();
//
// @BeforeAll
// static void initializeMybatisLambdaMetadata() {
// TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
// DonationRecordEntity.class);
// }
//
// @BeforeEach
// void setUp() {
// CorporateWalletApplication donor = CorporateWalletApplication.create("u-1", 1L, 2L);
// donor.fillForm("创新科技公司", "CREDIT-1", "张三", "10000000.00", "技术服务", "20260916090000");
// donor.activateWallet("CORP_DONOR", "20260916091000");
// RemoteWalletApplication recipient = RemoteWalletApplication.create("u-1", 1L, 2L);
// recipient.fillForm("阳光公益基金", "CREDIT-2", "李四", "13900139000", "20260916092000");
// recipient.activateWallet("CORP_RECIPIENT", "20260916093000");
//
// when(corporateWalletRepository.findLatest("u-1", 1L, 2L)).thenReturn(Optional.of(donor));
// when(remoteWalletRepository.findLatest("u-1", 1L, 2L)).thenReturn(Optional.of(recipient));
// when(mapper.selectOne(any())).thenAnswer(invocation -> latest.get());
// when(mapper.insert(any(DonationRecordEntity.class))).thenAnswer(invocation -> {
// DonationRecordEntity record = invocation.getArgument(0);
// record.setId(100L);
// latest.set(record);
// return 1;
// });
// when(mapper.updateById(any(DonationRecordEntity.class))).thenAnswer(invocation -> {
// latest.set(invocation.getArgument(0));
// return 1;
// });
// when(keyService.getOrCreatePrivateKey(any(), anyString(), anyString())).thenAnswer(invocation ->
// "监管机构私钥".equals(invocation.getArgument(1)) ? "REGULATOR_KEY" : "DONOR_KEY");
// when(cryptography.sm3(anyString())).thenAnswer(invocation -> "HASH_" + invocation.getArgument(0));
// when(cryptography.sign(anyString(), anyString())).thenAnswer(invocation ->
// "SIGN_" + invocation.getArgument(1));
// }
//
// @Test
// void completesPrototypeDonationPublicationAndSupervisionFlow() {
// DonationCreateRequest request = new DonationCreateRequest();
// request.setDonorName("创新科技公司");
// request.setDonorWalletId("CORP_DONOR");
// request.setRecipientName("阳光公益基金");
// request.setRecipientWalletId("CORP_RECIPIENT");
// request.setAmount(new BigDecimal("100000.00"));
// request.setProjectName("乡村教育支持计划");
// request.setMemo("乡村教育支持计划捐款");
// request.setTimestamp("20260916100000");
//
// service.concatenate(SUBJECT, "张三", request);
// service.digest(SUBJECT);
// service.signDonation(SUBJECT, "DONOR_KEY");
// service.outputDonation(SUBJECT);
// service.receiptDetails(SUBJECT);
// service.extractPublication(SUBJECT);
// service.digestPublication(SUBJECT);
// service.publish(SUBJECT);
// service.outputPublication(SUBJECT);
// service.extractSupervision(SUBJECT);
// service.digestSupervision(SUBJECT);
// service.signSupervision(SUBJECT, "REGULATOR_KEY");
// DonationPageResult result = service.outputSupervision(SUBJECT);
//
// assertThat(result.getStatus()).isEqualTo("ARCHIVED");
// assertThat(result.getDonationOutputJson()).contains("DONATE_20260916_001", "已提交");
// assertThat(result.getPublicationOutputJson()).contains("PUBLISHED", "阳光公益基金");
// assertThat(result.getSupervisionOutputJson()).contains("APPROVED", "已备案");
// verify(scoreService).markStepCompleted("u-1", 6);
// verify(scoreService).markStepCompleted("u-1", 7);
// verify(scoreService).markStepCompleted("u-1", 8);
// }
//}

Loading…
Cancel
Save