客商评分提交校验、复评状态与空评分持久化

提交审批前必须完成且仅保留一条自评,审核通过前必须完成复评。
列表返回最新复评状态;删除最后一条评分会落库;评分为 0 时不再被当成空值覆盖。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 20:21:43 +08:00
parent 14c1d9dff0
commit 3e23507b3a
3 changed files with 39 additions and 11 deletions
@@ -98,6 +98,10 @@ public class CustomerArchiveVO extends CustomerArchive {
@Schema(description = "资金使用率(百分比)") @Schema(description = "资金使用率(百分比)")
private BigDecimal fundUseRate; private BigDecimal fundUseRate;
@TableField(exist = false)
@Schema(description = "评分复评状态")
private String reviewStatus;
@TableField(exist = false) @TableField(exist = false)
@Schema(description = "已付金额合计(元)") @Schema(description = "已付金额合计(元)")
private BigDecimal usedFundLimit; private BigDecimal usedFundLimit;
@@ -46,6 +46,7 @@
<result column="current_node" property="currentNode"/> <result column="current_node" property="currentNode"/>
<result column="current_processor" property="currentProcessor"/> <result column="current_processor" property="currentProcessor"/>
<result column="approved_time" property="approvedTime"/> <result column="approved_time" property="approvedTime"/>
<result column="review_status" property="reviewStatus"/>
</resultMap> </resultMap>
<select id="selectCustomerArchivePage" resultMap="customerArchiveResultMap"> <select id="selectCustomerArchivePage" resultMap="customerArchiveResultMap">
@@ -92,7 +93,15 @@
approval_status, approval_status,
current_node, current_node,
current_processor, current_processor,
approved_time approved_time,
(
SELECT s.review_status
FROM blade_customer_credit_score s
WHERE s.customer_id = blade_customer_archive.id
AND (s.is_deleted = 0 OR s.is_deleted IS NULL)
ORDER BY s.score_date DESC, s.update_time DESC, s.id DESC
LIMIT 1
) AS review_status
FROM blade_customer_archive FROM blade_customer_archive
WHERE is_deleted = 0 WHERE is_deleted = 0
<if test="userId != null"> <if test="userId != null">
@@ -213,6 +213,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
CustomerArchiveVO detail = detail(id); CustomerArchiveVO detail = detail(id);
prepare(detail); prepare(detail);
validateBase(detail); validateBase(detail);
validateScoreForApproval(detail);
validateApproval(detail); validateApproval(detail);
CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class)); CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class));
CustomerArchive after = copyCustomer(before); CustomerArchive after = copyCustomer(before);
@@ -323,6 +324,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
log.info("流程结束审核通过跳过,客商已是审核通过 id={}", id); log.info("流程结束审核通过跳过,客商已是审核通过 id={}", id);
return true; return true;
} }
validateReviewCompleted(buildDetail(before));
CustomerArchive after = copyCustomer(before); CustomerArchive after = copyCustomer(before);
after.setAccessType(ACCESS_FORMAL); after.setAccessType(ACCESS_FORMAL);
after.setApprovalStatus(APPROVAL_APPROVED); after.setApprovalStatus(APPROVAL_APPROVED);
@@ -383,6 +385,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
CustomerArchiveVO detail = detail(id); CustomerArchiveVO detail = detail(id);
prepare(detail); prepare(detail);
validateBase(detail); validateBase(detail);
validateReviewCompleted(detail);
// 临时客商审核通过时由流程转为正式客商,不要求补齐正式客商材料。 // 临时客商审核通过时由流程转为正式客商,不要求补齐正式客商材料。
validateApproval(detail, Objects.equals(detail.getAccessType(), ACCESS_TEMPORARY)); validateApproval(detail, Objects.equals(detail.getAccessType(), ACCESS_TEMPORARY));
CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class)); CustomerArchive before = Objects.requireNonNull(BeanUtil.copyProperties(detail, CustomerArchive.class));
@@ -556,16 +559,30 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
validateApproval(customer, false); validateApproval(customer, false);
} }
private void validateReviewCompleted(CustomerArchiveVO customer) {
List<CustomerCreditScoreVO> scores = customer.getScores() == null ? List.of() : customer.getScores();
boolean reviewCompleted = scores.size() == 1 && "已完成".equals(scores.get(0).getReviewStatus());
if (!reviewCompleted) {
throw new ServiceException("请先完成复评");
}
}
private void validateScoreForApproval(CustomerArchiveVO customer) {
List<CustomerCreditScoreVO> scores = customer.getScores() == null ? List.of() : customer.getScores();
if (scores.size() > 1) {
throw new ServiceException("评分记录只能有一条");
}
boolean selfCompleted = scores.size() == 1 && "已完成".equals(scores.get(0).getSelfStatus());
if (!selfCompleted) {
throw new ServiceException("提交审批前必须完成自评");
}
}
private void validateApproval(CustomerArchiveVO customer, boolean temporaryToFormal) { private void validateApproval(CustomerArchiveVO customer, boolean temporaryToFormal) {
if (Objects.equals(customer.getAccessType(), ACCESS_FORMAL) && !temporaryToFormal) { if (Objects.equals(customer.getAccessType(), ACCESS_FORMAL) && !temporaryToFormal) {
if (Func.isEmpty(customer.getQualificationAttachments())) { if (Func.isEmpty(customer.getQualificationAttachments())) {
throw new ServiceException("正式客商提交审核前必须上传客商材料"); throw new ServiceException("正式客商提交审核前必须上传客商材料");
} }
boolean hasCompletedScore = customer.getScores().stream()
.anyMatch(score -> "已完成".equals(score.getSelfStatus()) || "已完成".equals(score.getReviewStatus()) || Func.isNotEmpty(score.getFinalScore()));
if (!hasCompletedScore) {
throw new ServiceException("正式客商提交审核前必须完成信用评分");
}
} }
} }
@@ -597,11 +614,9 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
insertContacts(customerId, customer.getContacts()); insertContacts(customerId, customer.getContacts());
insertReceiptAccounts(customerId, customer.getReceiptAccounts()); insertReceiptAccounts(customerId, customer.getReceiptAccounts());
insertInvoices(customerId, customer); insertInvoices(customerId, customer);
if (Func.isNotEmpty(customer.getScores())) {
deleteScores(List.of(customerId)); deleteScores(List.of(customerId));
insertScores(customerId, customer.getScores()); insertScores(customerId, customer.getScores());
} }
}
private void insertContacts(Long customerId, List<CustomerContactVO> contacts) { private void insertContacts(Long customerId, List<CustomerContactVO> contacts) {
for (CustomerContactVO contactVO : contacts) { for (CustomerContactVO contactVO : contacts) {
@@ -774,7 +789,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
} }
for (int detailIndex = 0; detailIndex < details.size(); detailIndex++) { for (int detailIndex = 0; detailIndex < details.size(); detailIndex++) {
CustomerCreditScoreDetailVO detailVO = details.get(detailIndex); CustomerCreditScoreDetailVO detailVO = details.get(detailIndex);
if (Func.isEmpty(detailVO.getScore())) { if (detailVO.getScore() == null) {
detailVO.setScore(resolveFullScore(detailVO.getOptionsJson())); detailVO.setScore(resolveFullScore(detailVO.getOptionsJson()));
} }
CustomerCreditScoreDetail detail = Objects.requireNonNull(BeanUtil.copyProperties(detailVO, CustomerCreditScoreDetail.class)); CustomerCreditScoreDetail detail = Objects.requireNonNull(BeanUtil.copyProperties(detailVO, CustomerCreditScoreDetail.class));