feat:文件上传的功能开发
commit
dfe131edf1
@ -1,14 +1,19 @@
|
||||
package com.yau.digitalrmb;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class DigitalRmbApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DigitalRmbApplication.class, args);
|
||||
ConfigurableApplicationContext context = SpringApplication.run(DigitalRmbApplication.class, args);
|
||||
log.info("数字人民币教学仿真后端启动完成,端口:{}",
|
||||
context.getEnvironment().getProperty("server.port"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package com.yau.digitalrmb.corporatewallet.application;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Schema(description = "模块六创新应用场景重新实训结果")
|
||||
public class CorporateInnovationTrainingResetResult {
|
||||
@Schema(description = "是否重置成功")
|
||||
private final boolean reset;
|
||||
@Schema(description = "逻辑删除的业务记录总数")
|
||||
private final int deletedRecordCount;
|
||||
@Schema(description = "处理说明")
|
||||
private final String message;
|
||||
|
||||
public CorporateInnovationTrainingResetResult(int deletedRecordCount) {
|
||||
this.reset = true;
|
||||
this.deletedRecordCount = deletedRecordCount;
|
||||
this.message = "数字人民币创新应用场景步骤数据已逻辑删除,可从模块六第一步重新开始";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
package com.yau.digitalrmb.corporatewallet.application;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.CorporateWalletApplicationEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.CorporateWalletApplicationMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationReceiptEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationReceiptMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationSupervisionEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationSupervisionMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.RemoteWalletApplicationEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.RemoteWalletApplicationMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.SalaryBatchEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.SalaryBatchMapper;
|
||||
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
|
||||
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsEntity;
|
||||
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsMapper;
|
||||
import com.yau.digitalrmb.progress.application.UserStepProgressNewService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class CorporateInnovationTrainingResetService {
|
||||
private static final String TRAINING_ID = "innovation-scene";
|
||||
|
||||
@Resource private CorporateWalletApplicationMapper corporateWalletMapper;
|
||||
@Resource private RemoteWalletApplicationMapper remoteWalletMapper;
|
||||
@Resource private SalaryBatchMapper salaryBatchMapper;
|
||||
@Resource private DonationMapper donationMapper;
|
||||
@Resource private DonationReceiptMapper donationReceiptMapper;
|
||||
@Resource private DonationSupervisionMapper donationSupervisionMapper;
|
||||
@Resource private StuModuleScoreDetailsMapper scoreDetailsMapper;
|
||||
@Resource private UserStepProgressNewService stepProgressService;
|
||||
|
||||
@Transactional
|
||||
public CorporateInnovationTrainingResetResult reset(InstitutionKeySubject subject) {
|
||||
int count = 0;
|
||||
count += logicalDeleteCorporateWalletApplications(subject);
|
||||
count += logicalDeleteRemoteWalletApplications(subject);
|
||||
count += logicalDeleteSalaryBatches(subject);
|
||||
count += logicalDeleteDonationRecords(subject);
|
||||
count += logicalDeleteScore(subject.getUserId());
|
||||
stepProgressService.reset(subject.getUserId(), TRAINING_ID);
|
||||
return new CorporateInnovationTrainingResetResult(count);
|
||||
}
|
||||
|
||||
private int logicalDeleteCorporateWalletApplications(InstitutionKeySubject subject) {
|
||||
return corporateWalletMapper.update(null, new LambdaUpdateWrapper<CorporateWalletApplicationEntity>()
|
||||
.eq(CorporateWalletApplicationEntity::getUserId, subject.getUserId())
|
||||
.eq(CorporateWalletApplicationEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(CorporateWalletApplicationEntity::getClassId, subject.getClassId())
|
||||
.set(CorporateWalletApplicationEntity::getDeleted, true));
|
||||
}
|
||||
|
||||
private int logicalDeleteRemoteWalletApplications(InstitutionKeySubject subject) {
|
||||
return remoteWalletMapper.update(null, new LambdaUpdateWrapper<RemoteWalletApplicationEntity>()
|
||||
.eq(RemoteWalletApplicationEntity::getUserId, subject.getUserId())
|
||||
.eq(RemoteWalletApplicationEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(RemoteWalletApplicationEntity::getClassId, subject.getClassId())
|
||||
.set(RemoteWalletApplicationEntity::getDeleted, true));
|
||||
}
|
||||
|
||||
private int logicalDeleteSalaryBatches(InstitutionKeySubject subject) {
|
||||
return salaryBatchMapper.update(null, new LambdaUpdateWrapper<SalaryBatchEntity>()
|
||||
.eq(SalaryBatchEntity::getUserId, subject.getUserId())
|
||||
.eq(SalaryBatchEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(SalaryBatchEntity::getClassId, subject.getClassId())
|
||||
.set(SalaryBatchEntity::getDeleted, true));
|
||||
}
|
||||
|
||||
private int logicalDeleteDonationRecords(InstitutionKeySubject subject) {
|
||||
int count = 0;
|
||||
count += donationMapper.update(null, new LambdaUpdateWrapper<DonationEntity>()
|
||||
.eq(DonationEntity::getUserId, subject.getUserId())
|
||||
.eq(DonationEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(DonationEntity::getClassId, subject.getClassId())
|
||||
.set(DonationEntity::getDeleted, true));
|
||||
count += donationReceiptMapper.update(null, new LambdaUpdateWrapper<DonationReceiptEntity>()
|
||||
.eq(DonationReceiptEntity::getUserId, subject.getUserId())
|
||||
.eq(DonationReceiptEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(DonationReceiptEntity::getClassId, subject.getClassId())
|
||||
.set(DonationReceiptEntity::getDeleted, true));
|
||||
count += donationSupervisionMapper.update(null, new LambdaUpdateWrapper<DonationSupervisionEntity>()
|
||||
.eq(DonationSupervisionEntity::getUserId, subject.getUserId())
|
||||
.eq(DonationSupervisionEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(DonationSupervisionEntity::getClassId, subject.getClassId())
|
||||
.set(DonationSupervisionEntity::getDeleted, true));
|
||||
return count;
|
||||
}
|
||||
|
||||
private int logicalDeleteScore(String userId) {
|
||||
return scoreDetailsMapper.update(null, new LambdaUpdateWrapper<StuModuleScoreDetailsEntity>()
|
||||
.eq(StuModuleScoreDetailsEntity::getUserId, userId)
|
||||
.eq(StuModuleScoreDetailsEntity::getMoudule, SalaryScoreService.MODULE_NAME)
|
||||
.set(StuModuleScoreDetailsEntity::getDeleted, true));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.yau.digitalrmb.corporatewallet.application;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@Schema(description = "公益捐赠三步流程页面数据")
|
||||
public class DonationPageResult {
|
||||
private final String donorName;
|
||||
private final String donorWalletId;
|
||||
private final String recipientName;
|
||||
private final String recipientWalletId;
|
||||
private final String amount;
|
||||
private final String projectName;
|
||||
private final String memo;
|
||||
private final String donationTimestamp;
|
||||
private final String donationId;
|
||||
private final String donationMessage;
|
||||
private final String donationDigest;
|
||||
private final String donorPrivateKey;
|
||||
private final String donorSignature;
|
||||
private final String donationOutputJson;
|
||||
private final String receiptStatus;
|
||||
private final String publicationTimestamp;
|
||||
private final String publicationMessage;
|
||||
private final String publicationDigest;
|
||||
private final String publicationId;
|
||||
private final String publicationTitle;
|
||||
private final String publicationContent;
|
||||
private final String publicationUrl;
|
||||
private final String publicationOutputJson;
|
||||
private final String supervisionTimestamp;
|
||||
private final String supervisionMessage;
|
||||
private final String supervisionDigest;
|
||||
private final String regulatorPrivateKey;
|
||||
private final String regulatorSignature;
|
||||
private final String approvalId;
|
||||
private final String approvalTime;
|
||||
private final String recordNumber;
|
||||
private final String supervisionOutputJson;
|
||||
private final String status;
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.yau.digitalrmb.corporatewallet.infrastructure;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.yau.digitalrmb.shared.infrastructure.persistence.AuditableEntity;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("donation_record")
|
||||
public class DonationRecordEntity extends AuditableEntity {
|
||||
@TableField("user_id") private String userId;
|
||||
@TableField("school_id") private Long schoolId;
|
||||
@TableField("class_id") private Long classId;
|
||||
@TableField("donation_id") private String donationId;
|
||||
@TableField("donor_name") private String donorName;
|
||||
@TableField("donor_wallet_id") private String donorWalletId;
|
||||
@TableField("recipient_name") private String recipientName;
|
||||
@TableField("recipient_wallet_id") private String recipientWalletId;
|
||||
private BigDecimal amount;
|
||||
@TableField("project_name") private String projectName;
|
||||
private String memo;
|
||||
@TableField("donation_timestamp") private String donationTimestamp;
|
||||
@TableField("donation_message") private String donationMessage;
|
||||
@TableField("donation_digest") private String donationDigest;
|
||||
@TableField("donor_signature") private String donorSignature;
|
||||
@TableField("donation_output_json") private String donationOutputJson;
|
||||
@TableField("publication_timestamp") private String publicationTimestamp;
|
||||
@TableField("publication_message") private String publicationMessage;
|
||||
@TableField("publication_digest") private String publicationDigest;
|
||||
@TableField("publication_id") private String publicationId;
|
||||
@TableField("publication_title") private String publicationTitle;
|
||||
@TableField("publication_content") private String publicationContent;
|
||||
@TableField("publication_url") private String publicationUrl;
|
||||
@TableField("publication_output_json") private String publicationOutputJson;
|
||||
@TableField("supervision_timestamp") private String supervisionTimestamp;
|
||||
@TableField("supervision_message") private String supervisionMessage;
|
||||
@TableField("supervision_digest") private String supervisionDigest;
|
||||
@TableField("regulator_signature") private String regulatorSignature;
|
||||
@TableField("approval_id") private String approvalId;
|
||||
@TableField("approval_time") private String approvalTime;
|
||||
@TableField("record_number") private String recordNumber;
|
||||
@TableField("supervision_output_json") private String supervisionOutputJson;
|
||||
private String status;
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.yau.digitalrmb.corporatewallet.infrastructure;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface DonationRecordMapper extends BaseMapper<DonationRecordEntity> {
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.yau.digitalrmb.corporatewallet.interfaces.rest;
|
||||
|
||||
import com.yau.digitalrmb.corporatewallet.application.CorporateInnovationTrainingResetResult;
|
||||
import com.yau.digitalrmb.corporatewallet.application.CorporateInnovationTrainingResetService;
|
||||
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 io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/corporate-wallet/training")
|
||||
@Tag(name = "模块六:数字人民币创新应用场景实验管理")
|
||||
public class CorporateInnovationTrainingResetController {
|
||||
@Resource private CorporateInnovationTrainingResetService resetService;
|
||||
|
||||
@PostMapping("/reset")
|
||||
@Operation(summary = "重新开始数字人民币创新应用场景实验",
|
||||
description = "逻辑删除当前主体的对公钱包申请、工资批次、模块六成绩和步骤进度;保留模块六密钥,可从模块六第一步重新开始。")
|
||||
public ApiResponse<CorporateInnovationTrainingResetResult> reset() {
|
||||
return ApiResponse.success(resetService.reset(
|
||||
InstitutionKeySubject.from(AuthContextHolder.get())), traceId());
|
||||
}
|
||||
|
||||
private String traceId() {
|
||||
return MDC.get(TraceIdFilter.MDC_KEY);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.yau.digitalrmb.corporatewallet.interfaces.rest.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.DecimalMin;
|
||||
import javax.validation.constraints.Digits;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@Schema(description = "拼接捐赠原文请求")
|
||||
public class DonationCreateRequest {
|
||||
@NotBlank @Size(max = 200) private String donorName;
|
||||
@NotBlank @Size(max = 128) private String donorWalletId;
|
||||
@NotBlank @Size(max = 200) private String recipientName;
|
||||
@NotBlank @Size(max = 128) private String recipientWalletId;
|
||||
@NotNull @DecimalMin(value = "0.01") @Digits(integer = 18, fraction = 2) private BigDecimal amount;
|
||||
@NotBlank @Size(max = 200) private String projectName;
|
||||
@NotBlank @Size(max = 500) private String memo;
|
||||
@NotBlank @Pattern(regexp = "\\d{14}", message = "时间戳格式应为yyyyMMddHHmmss") private String timestamp;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
-- 模块六公益捐赠三步流程增量建表脚本。
|
||||
-- 已有环境升级时执行;全新环境由 schema.sql 自动创建。
|
||||
CREATE TABLE IF NOT EXISTS donation_record (
|
||||
id BIGINT PRIMARY KEY,
|
||||
user_id VARCHAR(36) NOT NULL,
|
||||
school_id BIGINT NOT NULL,
|
||||
class_id BIGINT NOT NULL,
|
||||
donation_id VARCHAR(64),
|
||||
donor_name VARCHAR(200),
|
||||
donor_wallet_id VARCHAR(128),
|
||||
recipient_name VARCHAR(200),
|
||||
recipient_wallet_id VARCHAR(128),
|
||||
amount DECIMAL(20, 2),
|
||||
project_name VARCHAR(200),
|
||||
memo VARCHAR(500),
|
||||
donation_timestamp VARCHAR(20),
|
||||
donation_message TEXT,
|
||||
donation_digest VARCHAR(128),
|
||||
donor_signature TEXT,
|
||||
donation_output_json TEXT,
|
||||
publication_timestamp VARCHAR(20),
|
||||
publication_message TEXT,
|
||||
publication_digest VARCHAR(128),
|
||||
publication_id VARCHAR(64),
|
||||
publication_title VARCHAR(300),
|
||||
publication_content VARCHAR(1000),
|
||||
publication_url VARCHAR(500),
|
||||
publication_output_json TEXT,
|
||||
supervision_timestamp VARCHAR(20),
|
||||
supervision_message TEXT,
|
||||
supervision_digest VARCHAR(128),
|
||||
regulator_signature TEXT,
|
||||
approval_id VARCHAR(64),
|
||||
approval_time VARCHAR(20),
|
||||
record_number VARCHAR(64),
|
||||
supervision_output_json TEXT,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'DRAFT',
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL,
|
||||
created_by VARCHAR(64) NOT NULL,
|
||||
updated_by VARCHAR(64) NOT NULL,
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
INDEX idx_donation_subject (user_id, school_id, class_id, deleted, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='模块六-公益捐赠记录表';
|
||||
@ -0,0 +1,67 @@
|
||||
package com.yau.digitalrmb.corporatewallet.application;
|
||||
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.CorporateWalletApplicationMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.CorporateWalletApplicationEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.RemoteWalletApplicationEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.RemoteWalletApplicationMapper;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.SalaryBatchEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.SalaryBatchMapper;
|
||||
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
|
||||
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsMapper;
|
||||
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsEntity;
|
||||
import com.yau.digitalrmb.progress.application.UserStepProgressNewService;
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CorporateInnovationTrainingResetServiceTest {
|
||||
private static final InstitutionKeySubject SUBJECT =
|
||||
new InstitutionKeySubject("reset-user", 1001L, 2001L);
|
||||
|
||||
@Mock private CorporateWalletApplicationMapper corporateWalletMapper;
|
||||
@Mock private RemoteWalletApplicationMapper remoteWalletMapper;
|
||||
@Mock private SalaryBatchMapper salaryBatchMapper;
|
||||
@Mock private DonationRecordMapper donationRecordMapper;
|
||||
@Mock private StuModuleScoreDetailsMapper scoreDetailsMapper;
|
||||
@Mock private UserStepProgressNewService stepProgressService;
|
||||
@InjectMocks private CorporateInnovationTrainingResetService service;
|
||||
|
||||
@BeforeAll
|
||||
static void initializeMybatisLambdaMetadata() {
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
||||
TableInfoHelper.initTableInfo(assistant, CorporateWalletApplicationEntity.class);
|
||||
TableInfoHelper.initTableInfo(assistant, RemoteWalletApplicationEntity.class);
|
||||
TableInfoHelper.initTableInfo(assistant, SalaryBatchEntity.class);
|
||||
TableInfoHelper.initTableInfo(assistant, DonationRecordEntity.class);
|
||||
TableInfoHelper.initTableInfo(assistant, StuModuleScoreDetailsEntity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetsModuleSixBusinessDataScoreAndNavigationProgress() {
|
||||
when(corporateWalletMapper.update(any(), any())).thenReturn(2);
|
||||
when(remoteWalletMapper.update(any(), any())).thenReturn(3);
|
||||
when(salaryBatchMapper.update(any(), any())).thenReturn(4);
|
||||
when(donationRecordMapper.update(any(), any())).thenReturn(5);
|
||||
when(scoreDetailsMapper.update(any(), any())).thenReturn(1);
|
||||
|
||||
CorporateInnovationTrainingResetResult result = service.reset(SUBJECT);
|
||||
|
||||
assertThat(result.isReset()).isTrue();
|
||||
assertThat(result.getDeletedRecordCount()).isEqualTo(15);
|
||||
verify(stepProgressService).reset("reset-user", "innovation-scene");
|
||||
}
|
||||
}
|
||||
@ -0,0 +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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package com.yau.digitalrmb.corporatewallet.application;
|
||||
|
||||
import com.yau.digitalrmb.corporatewallet.domain.CorporateWalletApplication;
|
||||
import com.yau.digitalrmb.corporatewallet.domain.CorporateWalletApplicationRepository;
|
||||
import com.yau.digitalrmb.corporatewallet.domain.SalaryBatch;
|
||||
import com.yau.digitalrmb.corporatewallet.domain.SalaryBatchRepository;
|
||||
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
|
||||
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
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.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SalaryBatchServiceTest {
|
||||
private static final InstitutionKeySubject SUBJECT =
|
||||
new InstitutionKeySubject("salary-user", 1001L, 2001L);
|
||||
private static final String PRIVATE_KEY = "OPERATOR_PRIVATE_KEY";
|
||||
|
||||
@Mock private SalaryBatchRepository repository;
|
||||
@Mock private CorporateWalletApplicationRepository corporateWalletRepository;
|
||||
@Mock private InstitutionIdentityCryptography cryptography;
|
||||
@Mock private SalaryScoreService scoreService;
|
||||
@Mock private CorporateWalletKeyService keyService;
|
||||
@InjectMocks private SalaryBatchService service;
|
||||
|
||||
@Test
|
||||
void startsFreshBatchAndSignsCurrentDigestWhenWorkflowRunsAgain() {
|
||||
AtomicReference<SalaryBatch> latest = new AtomicReference<>();
|
||||
AtomicLong ids = new AtomicLong(10L);
|
||||
when(repository.save(any(SalaryBatch.class))).thenAnswer(invocation -> {
|
||||
SalaryBatch batch = invocation.getArgument(0);
|
||||
batch.setId(ids.incrementAndGet());
|
||||
latest.set(batch);
|
||||
return batch;
|
||||
});
|
||||
when(repository.findLatest(SUBJECT.getUserId(), SUBJECT.getSchoolId(), SUBJECT.getClassId()))
|
||||
.thenAnswer(invocation -> Optional.ofNullable(latest.get()));
|
||||
|
||||
CorporateWalletApplication wallet = CorporateWalletApplication.create(
|
||||
SUBJECT.getUserId(), SUBJECT.getSchoolId(), SUBJECT.getClassId());
|
||||
wallet.setWalletId("CORP_WALLET_001");
|
||||
when(corporateWalletRepository.findLatest(
|
||||
SUBJECT.getUserId(), SUBJECT.getSchoolId(), SUBJECT.getClassId()))
|
||||
.thenReturn(Optional.of(wallet));
|
||||
when(cryptography.sm3(any(String.class))).thenReturn("DIGEST_ONE", "DIGEST_TWO");
|
||||
when(keyService.getOrCreatePrivateKey(
|
||||
any(InstitutionKeySubject.class), eq("数币操作员私钥"), eq("王五")))
|
||||
.thenReturn(PRIVATE_KEY);
|
||||
when(cryptography.sign(PRIVATE_KEY, "DIGEST_ONE")).thenReturn("SIGNATURE_ONE");
|
||||
when(cryptography.sign(PRIVATE_KEY, "DIGEST_TWO")).thenReturn("SIGNATURE_TWO");
|
||||
|
||||
service.fetchBlockchain(SUBJECT, "tester");
|
||||
service.concatenate(SUBJECT);
|
||||
SalaryDigestResult firstDigest = service.computeDigest(SUBJECT);
|
||||
SalaryBatch firstBatch = latest.get();
|
||||
assertThat(firstBatch.getStatus()).isEqualTo(SalaryBatch.Status.DIGESTED);
|
||||
SalarySignatureResult firstSignature = service.sign(SUBJECT, PRIVATE_KEY);
|
||||
|
||||
service.fetchBlockchain(SUBJECT, "tester");
|
||||
service.concatenate(SUBJECT);
|
||||
SalaryDigestResult secondDigest = service.computeDigest(SUBJECT);
|
||||
SalaryBatch secondBatch = latest.get();
|
||||
assertThat(secondBatch.getStatus()).isEqualTo(SalaryBatch.Status.DIGESTED);
|
||||
SalarySignatureResult secondSignature = service.sign(SUBJECT, PRIVATE_KEY);
|
||||
|
||||
assertThat(secondBatch).isNotSameAs(firstBatch);
|
||||
assertThat(secondBatch.getId()).isNotEqualTo(firstBatch.getId());
|
||||
assertThat(firstDigest.getDigestValue()).isEqualTo("DIGEST_ONE");
|
||||
assertThat(secondDigest.getDigestValue()).isEqualTo("DIGEST_TWO");
|
||||
assertThat(firstSignature.getSignature()).isEqualTo("SIGNATURE_ONE");
|
||||
assertThat(secondSignature.getSignature()).isEqualTo("SIGNATURE_TWO");
|
||||
|
||||
verify(repository, times(2)).save(any(SalaryBatch.class));
|
||||
verify(cryptography).sign(PRIVATE_KEY, "DIGEST_ONE");
|
||||
verify(cryptography).sign(PRIVATE_KEY, "DIGEST_TWO");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue