Merge module 6 charity workflow
commit
87f93ae614
@ -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,82 @@
|
||||
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.DonationRecordEntity;
|
||||
import com.yau.digitalrmb.corporatewallet.infrastructure.DonationRecordMapper;
|
||||
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 DonationRecordMapper donationRecordMapper;
|
||||
@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) {
|
||||
return donationRecordMapper.update(null, new LambdaUpdateWrapper<DonationRecordEntity>()
|
||||
.eq(DonationRecordEntity::getUserId, subject.getUserId())
|
||||
.eq(DonationRecordEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(DonationRecordEntity::getClassId, subject.getClassId())
|
||||
.set(DonationRecordEntity::getDeleted, true));
|
||||
}
|
||||
|
||||
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,418 @@
|
||||
package com.yau.digitalrmb.corporatewallet.application;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
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 com.yau.digitalrmb.shared.api.ErrorCode;
|
||||
import com.yau.digitalrmb.shared.exception.BusinessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class DonationService {
|
||||
private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
private static final String DONOR_KEY_TYPE = "企业法人私钥";
|
||||
private static final String REGULATOR_KEY_TYPE = "监管机构私钥";
|
||||
private static final String REGULATOR_NAME = "民政部公益监管中心";
|
||||
|
||||
private enum Status {
|
||||
DRAFT, CONCATENATED, DIGESTED, SIGNED, SUBMITTED, RECEIVED,
|
||||
PUBLICATION_EXTRACTED, PUBLICATION_DIGESTED, PUBLICATION_READY, PUBLISHED,
|
||||
SUPERVISION_EXTRACTED, SUPERVISION_DIGESTED, SUPERVISION_SIGNED, ARCHIVED
|
||||
}
|
||||
|
||||
@Resource private DonationRecordMapper mapper;
|
||||
@Resource private CorporateWalletApplicationRepository corporateWalletRepository;
|
||||
@Resource private RemoteWalletApplicationRepository remoteWalletRepository;
|
||||
@Resource private CorporateWalletKeyService keyService;
|
||||
@Resource private InstitutionIdentityCryptography cryptography;
|
||||
@Resource private SalaryScoreService scoreService;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult page(InstitutionKeySubject subject, String operator) {
|
||||
DonationRecordEntity record = findLatest(subject);
|
||||
CorporateWalletApplication donor = donorWallet(subject);
|
||||
RemoteWalletApplication recipient = recipientWallet(subject);
|
||||
return result(subject, record, donor, recipient, operator);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult concatenate(InstitutionKeySubject subject, String operator,
|
||||
DonationCreateRequest request) {
|
||||
CorporateWalletApplication donor = requiredDonorWallet(subject);
|
||||
RemoteWalletApplication recipient = requiredRecipientWallet(subject);
|
||||
validateWalletInputs(donor, recipient, request);
|
||||
|
||||
DonationRecordEntity record = findLatest(subject);
|
||||
if (record == null || status(record).ordinal() >= Status.SUBMITTED.ordinal()) {
|
||||
record = new DonationRecordEntity();
|
||||
record.setUserId(subject.getUserId());
|
||||
record.setSchoolId(subject.getSchoolId());
|
||||
record.setClassId(subject.getClassId());
|
||||
record.setDeleted(false);
|
||||
record.setStatus(Status.DRAFT.name());
|
||||
}
|
||||
|
||||
String amount = money(request.getAmount());
|
||||
String message = String.join("|", "DONATE", request.getDonorWalletId(),
|
||||
request.getRecipientWalletId(), amount, request.getProjectName().trim(),
|
||||
request.getMemo().trim(), request.getTimestamp());
|
||||
record.setDonationId("DONATE_" + request.getTimestamp().substring(0, 8) + "_001");
|
||||
record.setDonorName(request.getDonorName().trim());
|
||||
record.setDonorWalletId(request.getDonorWalletId().trim());
|
||||
record.setRecipientName(request.getRecipientName().trim());
|
||||
record.setRecipientWalletId(request.getRecipientWalletId().trim());
|
||||
record.setAmount(request.getAmount().setScale(2));
|
||||
record.setProjectName(request.getProjectName().trim());
|
||||
record.setMemo(request.getMemo().trim());
|
||||
record.setDonationTimestamp(request.getTimestamp());
|
||||
record.setDonationMessage(message);
|
||||
record.setDonationDigest(null);
|
||||
record.setDonorSignature(null);
|
||||
record.setDonationOutputJson(null);
|
||||
record.setStatus(Status.CONCATENATED.name());
|
||||
save(record);
|
||||
return result(subject, record, donor, recipient, operator);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult digest(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.CONCATENATED, "请先拼接捐赠原文");
|
||||
if (record.getDonationDigest() == null) {
|
||||
record.setDonationDigest(cryptography.sm3(record.getDonationMessage()));
|
||||
record.setStatus(Status.DIGESTED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getDonorName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult signDonation(InstitutionKeySubject subject, String providedPrivateKey) {
|
||||
DonationRecordEntity record = required(subject, Status.DIGESTED, "请先生成捐赠摘要");
|
||||
String privateKey = keyService.getOrCreatePrivateKey(subject, DONOR_KEY_TYPE, record.getDonorName());
|
||||
validatePrivateKey(subject, privateKey, providedPrivateKey, "企业法人私钥不正确,错误次数+1");
|
||||
if (record.getDonorSignature() == null) {
|
||||
record.setDonorSignature(cryptography.sign(privateKey, record.getDonationDigest()));
|
||||
record.setStatus(Status.SIGNED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getDonorName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult outputDonation(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.SIGNED, "请先完成企业法人SM2签名");
|
||||
if (record.getDonationOutputJson() == null) {
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("donationId", record.getDonationId());
|
||||
output.put("corpWalletId", record.getDonorWalletId());
|
||||
output.put("orgWalletId", record.getRecipientWalletId());
|
||||
output.put("amount", money(record.getAmount()));
|
||||
output.put("project", record.getProjectName());
|
||||
output.put("memo", record.getMemo());
|
||||
output.put("digest", record.getDonationDigest());
|
||||
output.put("corpSignature", record.getDonorSignature());
|
||||
output.put("timestamp", record.getDonationTimestamp());
|
||||
output.put("status", "已提交");
|
||||
record.setDonationOutputJson(json(output));
|
||||
record.setStatus(Status.SUBMITTED.name());
|
||||
mapper.updateById(record);
|
||||
scoreService.markStepCompleted(subject.getUserId(), 6);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getDonorName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult receiptDetails(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.SUBMITTED, "请先由捐赠企业提交捐赠报文");
|
||||
if (status(record).ordinal() < Status.RECEIVED.ordinal()) {
|
||||
record.setStatus(Status.RECEIVED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getRecipientName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult extractPublication(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.RECEIVED, "请先查看捐款明细");
|
||||
if (record.getPublicationMessage() == null) {
|
||||
String timestamp = now();
|
||||
String title = publicationTitle(record, timestamp);
|
||||
record.setPublicationTimestamp(timestamp);
|
||||
record.setPublicationTitle(title);
|
||||
record.setPublicationMessage(String.join("|", "PUBLIC", record.getRecipientWalletId(),
|
||||
title, record.getDonorName(), money(record.getAmount()),
|
||||
record.getProjectName(), timestamp));
|
||||
record.setStatus(Status.PUBLICATION_EXTRACTED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getRecipientName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult digestPublication(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.PUBLICATION_EXTRACTED, "请先提取公示拼接原文");
|
||||
if (record.getPublicationDigest() == null) {
|
||||
record.setPublicationDigest(cryptography.sm3(record.getPublicationMessage()));
|
||||
record.setStatus(Status.PUBLICATION_DIGESTED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getRecipientName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult publish(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.PUBLICATION_DIGESTED, "请先生成资金公示摘要");
|
||||
if (record.getPublicationId() == null) {
|
||||
String month = record.getPublicationTimestamp().substring(0, 6);
|
||||
record.setPublicationId("PUBLIC_" + record.getPublicationTimestamp().substring(0, 8) + "_001");
|
||||
record.setPublicationContent("本月收到" + record.getDonorName() + "捐赠" + moneyWithGrouping(record.getAmount())
|
||||
+ "元,用于" + record.getProjectName());
|
||||
record.setPublicationUrl("https://www.sunshine.org/donate/" + month);
|
||||
record.setStatus(Status.PUBLICATION_READY.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getRecipientName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult outputPublication(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.PUBLICATION_READY, "请先发布资金公示");
|
||||
if (record.getPublicationOutputJson() == null) {
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("publicationId", record.getPublicationId());
|
||||
output.put("orgId", record.getRecipientWalletId());
|
||||
output.put("title", record.getPublicationTitle());
|
||||
output.put("content", record.getPublicationContent());
|
||||
output.put("url", record.getPublicationUrl());
|
||||
output.put("digest", record.getPublicationDigest());
|
||||
output.put("publishTime", record.getPublicationTimestamp());
|
||||
output.put("status", "PUBLISHED");
|
||||
record.setPublicationOutputJson(json(output));
|
||||
record.setStatus(Status.PUBLISHED.name());
|
||||
mapper.updateById(record);
|
||||
scoreService.markStepCompleted(subject.getUserId(), 7);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), record.getRecipientName());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult extractSupervision(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.PUBLISHED, "请先完成受赠机构资金公示");
|
||||
if (record.getSupervisionMessage() == null) {
|
||||
String timestamp = now();
|
||||
record.setSupervisionTimestamp(timestamp);
|
||||
record.setSupervisionMessage(String.join("|", "SUPERVISE", record.getDonationId(),
|
||||
REGULATOR_NAME, money(record.getAmount()), record.getProjectName(), timestamp));
|
||||
record.setStatus(Status.SUPERVISION_EXTRACTED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), REGULATOR_NAME);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult digestSupervision(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.SUPERVISION_EXTRACTED, "请先提取监管审核原文");
|
||||
if (record.getSupervisionDigest() == null) {
|
||||
record.setSupervisionDigest(cryptography.sm3(record.getSupervisionMessage()));
|
||||
record.setStatus(Status.SUPERVISION_DIGESTED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), REGULATOR_NAME);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult signSupervision(InstitutionKeySubject subject, String providedPrivateKey) {
|
||||
DonationRecordEntity record = required(subject, Status.SUPERVISION_DIGESTED, "请先生成监管审核摘要");
|
||||
String privateKey = keyService.getOrCreatePrivateKey(subject, REGULATOR_KEY_TYPE, REGULATOR_NAME);
|
||||
validatePrivateKey(subject, privateKey, providedPrivateKey, "监管机构私钥不正确,错误次数+1");
|
||||
if (record.getRegulatorSignature() == null) {
|
||||
record.setRegulatorSignature(cryptography.sign(privateKey, record.getSupervisionDigest()));
|
||||
record.setStatus(Status.SUPERVISION_SIGNED.name());
|
||||
mapper.updateById(record);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), REGULATOR_NAME);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public DonationPageResult outputSupervision(InstitutionKeySubject subject) {
|
||||
DonationRecordEntity record = required(subject, Status.SUPERVISION_SIGNED, "请先完成监管机构SM2签名");
|
||||
if (record.getSupervisionOutputJson() == null) {
|
||||
String date = record.getSupervisionTimestamp().substring(0, 8);
|
||||
record.setApprovalId("APPROVE_" + date + "_001");
|
||||
record.setApprovalTime(record.getSupervisionTimestamp());
|
||||
record.setRecordNumber("RECORD_" + date + "_001");
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("approvalId", record.getApprovalId());
|
||||
output.put("donationId", record.getDonationId());
|
||||
output.put("supervisor", REGULATOR_NAME);
|
||||
output.put("approvalResult", "APPROVED");
|
||||
output.put("supervisorSignature", record.getRegulatorSignature());
|
||||
output.put("approvalTime", record.getApprovalTime());
|
||||
output.put("recordNumber", record.getRecordNumber());
|
||||
output.put("status", "已备案");
|
||||
record.setSupervisionOutputJson(json(output));
|
||||
record.setStatus(Status.ARCHIVED.name());
|
||||
mapper.updateById(record);
|
||||
scoreService.markStepCompleted(subject.getUserId(), 8);
|
||||
}
|
||||
return result(subject, record, donorWallet(subject), recipientWallet(subject), REGULATOR_NAME);
|
||||
}
|
||||
|
||||
private DonationPageResult result(InstitutionKeySubject subject, DonationRecordEntity record,
|
||||
CorporateWalletApplication donor, RemoteWalletApplication recipient,
|
||||
String operator) {
|
||||
String donorName = record != null ? record.getDonorName() : donor == null ? null : donor.getCorpName();
|
||||
String recipientName = record != null ? record.getRecipientName() : recipient == null ? null : recipient.getCorpName();
|
||||
String effectiveOperator = operator == null || operator.trim().isEmpty() ? "企业法人" : operator;
|
||||
String donorKey = keyService.getOrCreatePrivateKey(subject, DONOR_KEY_TYPE, effectiveOperator);
|
||||
String regulatorKey = keyService.getOrCreatePrivateKey(subject, REGULATOR_KEY_TYPE, REGULATOR_NAME);
|
||||
return DonationPageResult.builder()
|
||||
.donorName(donorName)
|
||||
.donorWalletId(record != null ? record.getDonorWalletId() : donor == null ? null : donor.getWalletId())
|
||||
.recipientName(recipientName)
|
||||
.recipientWalletId(record != null ? record.getRecipientWalletId() : recipient == null ? null : recipient.getWalletId())
|
||||
.amount(record == null || record.getAmount() == null ? "100000.00" : money(record.getAmount()))
|
||||
.projectName(record == null ? "乡村教育支持计划" : record.getProjectName())
|
||||
.memo(record == null ? "乡村教育支持计划捐款" : record.getMemo())
|
||||
.donationTimestamp(record == null ? null : record.getDonationTimestamp())
|
||||
.donationId(record == null ? null : record.getDonationId())
|
||||
.donationMessage(record == null ? null : record.getDonationMessage())
|
||||
.donationDigest(record == null ? null : record.getDonationDigest())
|
||||
.donorPrivateKey(donorKey)
|
||||
.donorSignature(record == null ? null : record.getDonorSignature())
|
||||
.donationOutputJson(record == null ? null : record.getDonationOutputJson())
|
||||
.receiptStatus(record != null && status(record).ordinal() >= Status.RECEIVED.ordinal() ? "已到账" : null)
|
||||
.publicationTimestamp(record == null ? null : record.getPublicationTimestamp())
|
||||
.publicationMessage(record == null ? null : record.getPublicationMessage())
|
||||
.publicationDigest(record == null ? null : record.getPublicationDigest())
|
||||
.publicationId(record == null ? null : record.getPublicationId())
|
||||
.publicationTitle(record == null ? null : record.getPublicationTitle())
|
||||
.publicationContent(record == null ? null : record.getPublicationContent())
|
||||
.publicationUrl(record == null ? null : record.getPublicationUrl())
|
||||
.publicationOutputJson(record == null ? null : record.getPublicationOutputJson())
|
||||
.supervisionTimestamp(record == null ? null : record.getSupervisionTimestamp())
|
||||
.supervisionMessage(record == null ? null : record.getSupervisionMessage())
|
||||
.supervisionDigest(record == null ? null : record.getSupervisionDigest())
|
||||
.regulatorPrivateKey(regulatorKey)
|
||||
.regulatorSignature(record == null ? null : record.getRegulatorSignature())
|
||||
.approvalId(record == null ? null : record.getApprovalId())
|
||||
.approvalTime(record == null ? null : record.getApprovalTime())
|
||||
.recordNumber(record == null ? null : record.getRecordNumber())
|
||||
.supervisionOutputJson(record == null ? null : record.getSupervisionOutputJson())
|
||||
.status(record == null ? null : record.getStatus())
|
||||
.build();
|
||||
}
|
||||
|
||||
private DonationRecordEntity required(InstitutionKeySubject subject, Status minimum, String message) {
|
||||
DonationRecordEntity record = findLatest(subject);
|
||||
if (record == null || status(record).ordinal() < minimum.ordinal()) {
|
||||
throw new BusinessException(ErrorCode.VALIDATION_ERROR, message);
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
private DonationRecordEntity findLatest(InstitutionKeySubject subject) {
|
||||
return mapper.selectOne(new LambdaQueryWrapper<DonationRecordEntity>()
|
||||
.eq(DonationRecordEntity::getUserId, subject.getUserId())
|
||||
.eq(DonationRecordEntity::getSchoolId, subject.getSchoolId())
|
||||
.eq(DonationRecordEntity::getClassId, subject.getClassId())
|
||||
.eq(DonationRecordEntity::getDeleted, false)
|
||||
.orderByDesc(DonationRecordEntity::getCreatedAt)
|
||||
.orderByDesc(DonationRecordEntity::getId)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
private CorporateWalletApplication donorWallet(InstitutionKeySubject subject) {
|
||||
return corporateWalletRepository.findLatest(subject.getUserId(), subject.getSchoolId(), subject.getClassId())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private RemoteWalletApplication recipientWallet(InstitutionKeySubject subject) {
|
||||
return remoteWalletRepository.findLatest(subject.getUserId(), subject.getSchoolId(), subject.getClassId())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private CorporateWalletApplication requiredDonorWallet(InstitutionKeySubject subject) {
|
||||
CorporateWalletApplication wallet = donorWallet(subject);
|
||||
if (wallet == null || wallet.getWalletId() == null || wallet.getStatus() != CorporateWalletApplication.Status.ACTIVATED) {
|
||||
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成临柜开立对公钱包");
|
||||
}
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private RemoteWalletApplication requiredRecipientWallet(InstitutionKeySubject subject) {
|
||||
RemoteWalletApplication wallet = recipientWallet(subject);
|
||||
if (wallet == null || wallet.getWalletId() == null || wallet.getStatus() != RemoteWalletApplication.Status.ACTIVATED) {
|
||||
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成远程开立受赠机构对公钱包");
|
||||
}
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private void validateWalletInputs(CorporateWalletApplication donor, RemoteWalletApplication recipient,
|
||||
DonationCreateRequest request) {
|
||||
if (!same(donor.getCorpName(), request.getDonorName())
|
||||
|| !same(donor.getWalletId(), request.getDonorWalletId())) {
|
||||
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "捐赠企业或付款钱包与对公钱包开立结果不一致");
|
||||
}
|
||||
if (!same(recipient.getCorpName(), request.getRecipientName())
|
||||
|| !same(recipient.getWalletId(), request.getRecipientWalletId())) {
|
||||
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "受赠机构或收款钱包与远程开立结果不一致");
|
||||
}
|
||||
}
|
||||
|
||||
private void validatePrivateKey(InstitutionKeySubject subject, String expected, String actual, String message) {
|
||||
if (!same(expected, actual)) {
|
||||
scoreService.recordError(subject.getUserId());
|
||||
throw new BusinessException(ErrorCode.VALIDATION_ERROR, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void save(DonationRecordEntity record) {
|
||||
if (record.getId() == null) mapper.insert(record); else mapper.updateById(record);
|
||||
}
|
||||
|
||||
private String publicationTitle(DonationRecordEntity record, String timestamp) {
|
||||
int month = Integer.parseInt(timestamp.substring(4, 6));
|
||||
return record.getRecipientName() + timestamp.substring(0, 4) + "年" + month + "月捐赠收入公示";
|
||||
}
|
||||
|
||||
private String json(Map<String, Object> value) {
|
||||
try {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
|
||||
} catch (JsonProcessingException exception) {
|
||||
throw new IllegalStateException("公益捐赠报文序列化失败", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private Status status(DonationRecordEntity record) {
|
||||
return record.getStatus() == null ? Status.DRAFT : Status.valueOf(record.getStatus());
|
||||
}
|
||||
|
||||
private String now() { return LocalDateTime.now().format(TIMESTAMP_FORMAT); }
|
||||
|
||||
private String money(BigDecimal amount) { return amount.setScale(2).toPlainString(); }
|
||||
|
||||
private String moneyWithGrouping(BigDecimal amount) { return String.format("%,.2f", amount); }
|
||||
|
||||
private boolean same(String left, String right) {
|
||||
return left != null && right != null && left.trim().equalsIgnoreCase(right.trim());
|
||||
}
|
||||
}
|
||||
@ -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,103 @@
|
||||
package com.yau.digitalrmb.corporatewallet.interfaces.rest;
|
||||
|
||||
import com.yau.digitalrmb.corporatewallet.application.DonationPageResult;
|
||||
import com.yau.digitalrmb.corporatewallet.application.DonationService;
|
||||
import com.yau.digitalrmb.corporatewallet.interfaces.rest.dto.DonationCreateRequest;
|
||||
import com.yau.digitalrmb.corporatewallet.interfaces.rest.dto.DonationSignRequest;
|
||||
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
|
||||
import com.yau.digitalrmb.security.context.AuthContextHolder;
|
||||
import com.yau.digitalrmb.security.context.JwtUser;
|
||||
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.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.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/donations")
|
||||
@Tag(name = "模块六-3-企业公益捐赠", description = "捐赠企业发起、受赠机构公示、第三方监管审核三步流程")
|
||||
public class DonationController {
|
||||
@Resource private DonationService donationService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "查询公益捐赠三步流程页面数据")
|
||||
public ApiResponse<DonationPageResult> page() {
|
||||
JwtUser user = AuthContextHolder.get();
|
||||
return success(donationService.page(InstitutionKeySubject.from(user), user.getUsername()));
|
||||
}
|
||||
|
||||
@PostMapping("/concatenate")
|
||||
@Operation(summary = "拼接捐赠原文")
|
||||
public ApiResponse<DonationPageResult> concatenate(@Valid @RequestBody DonationCreateRequest request) {
|
||||
JwtUser user = AuthContextHolder.get();
|
||||
return success(donationService.concatenate(InstitutionKeySubject.from(user), user.getUsername(), request));
|
||||
}
|
||||
|
||||
@PostMapping("/digest")
|
||||
@Operation(summary = "生成捐赠原文SM3摘要")
|
||||
public ApiResponse<DonationPageResult> digest() { return success(donationService.digest(subject())); }
|
||||
|
||||
@PostMapping("/sign")
|
||||
@Operation(summary = "使用企业法人私钥对捐赠摘要进行SM2签名")
|
||||
public ApiResponse<DonationPageResult> sign(@Valid @RequestBody DonationSignRequest request) {
|
||||
return success(donationService.signDonation(subject(), request.getPrivateKey()));
|
||||
}
|
||||
|
||||
@PostMapping("/output")
|
||||
@Operation(summary = "输出并提交捐赠报文")
|
||||
public ApiResponse<DonationPageResult> output() { return success(donationService.outputDonation(subject())); }
|
||||
|
||||
@PostMapping("/receipt/details")
|
||||
@Operation(summary = "受赠机构查看到账捐赠明细")
|
||||
public ApiResponse<DonationPageResult> receiptDetails() { return success(donationService.receiptDetails(subject())); }
|
||||
|
||||
@PostMapping("/publication/extract")
|
||||
@Operation(summary = "提取资金公示拼接原文")
|
||||
public ApiResponse<DonationPageResult> extractPublication() { return success(donationService.extractPublication(subject())); }
|
||||
|
||||
@PostMapping("/publication/digest")
|
||||
@Operation(summary = "生成资金公示原文SM3摘要")
|
||||
public ApiResponse<DonationPageResult> digestPublication() { return success(donationService.digestPublication(subject())); }
|
||||
|
||||
@PostMapping("/publication/publish")
|
||||
@Operation(summary = "发布资金公示")
|
||||
public ApiResponse<DonationPageResult> publish() { return success(donationService.publish(subject())); }
|
||||
|
||||
@PostMapping("/publication/output")
|
||||
@Operation(summary = "输出资金公示报文")
|
||||
public ApiResponse<DonationPageResult> outputPublication() { return success(donationService.outputPublication(subject())); }
|
||||
|
||||
@PostMapping("/supervision/extract")
|
||||
@Operation(summary = "提取监管审核捐赠原文")
|
||||
public ApiResponse<DonationPageResult> extractSupervision() { return success(donationService.extractSupervision(subject())); }
|
||||
|
||||
@PostMapping("/supervision/digest")
|
||||
@Operation(summary = "生成监管审核原文SM3摘要")
|
||||
public ApiResponse<DonationPageResult> digestSupervision() { return success(donationService.digestSupervision(subject())); }
|
||||
|
||||
@PostMapping("/supervision/sign")
|
||||
@Operation(summary = "使用监管机构私钥对审核摘要进行SM2签名")
|
||||
public ApiResponse<DonationPageResult> signSupervision(@Valid @RequestBody DonationSignRequest request) {
|
||||
return success(donationService.signSupervision(subject(), request.getPrivateKey()));
|
||||
}
|
||||
|
||||
@PostMapping("/supervision/output")
|
||||
@Operation(summary = "输出监管审核备案报文")
|
||||
public ApiResponse<DonationPageResult> outputSupervision() { return success(donationService.outputSupervision(subject())); }
|
||||
|
||||
private InstitutionKeySubject subject() {
|
||||
return InstitutionKeySubject.from(AuthContextHolder.get());
|
||||
}
|
||||
|
||||
private ApiResponse<DonationPageResult> success(DonationPageResult result) {
|
||||
return ApiResponse.success(result, 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,13 @@
|
||||
package com.yau.digitalrmb.corporatewallet.interfaces.rest.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
@Data
|
||||
@Schema(description = "公益捐赠SM2签名请求")
|
||||
public class DonationSignRequest {
|
||||
@NotBlank(message = "私钥不能为空")
|
||||
private String privateKey;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue