From a4ed0f250009167453ca637ce042ef8e0b69f613 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 24 十月 2026 19:30:00 +0800
Subject: [PATCH] feat(aftersales): 增加售后退货流程管理功能

---
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java |   76 ++++++++++++++++++++++++++++++--------
 1 files changed, 60 insertions(+), 16 deletions(-)

diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
index 02dad68..2a6d218 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
@@ -24,13 +24,18 @@
 import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService;
 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
+import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
+import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
 import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
+import cn.iocoder.yudao.module.system.api.approval.ApprovalConfigApi;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum;
 import com.mzt.logapi.context.LogRecordContext;
 import com.mzt.logapi.service.impl.DiffParseFunction;
 import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -40,6 +45,7 @@
 import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
 
@@ -50,7 +56,11 @@
  */
 @Service
 @Validated
+@Slf4j
 public class CrmReceivableServiceImpl implements CrmReceivableService {
+
+    private static final String RECEIVABLE_PROCESS_KEY = "crm-receivable-audit";
+    private static final String RECEIVABLE_APPROVE_BIZ_TYPE = "crm_receivable_approve";
 
     @Resource
     private CrmReceivableMapper receivableMapper;
@@ -72,6 +82,10 @@
     private AdminUserApi adminUserApi;
     @Resource
     private StorageAttachmentApi storageAttachmentApi;
+    @Resource
+    private BpmProcessInstanceApi bpmProcessInstanceApi;
+    @Resource
+    private ApprovalConfigApi approvalConfigApi;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -92,9 +106,9 @@
             throw exception(RECEIVABLE_NO_EXISTS);
         }
 
-        // 2.1 鎻掑叆鍥炴锛堝垱寤哄悗鍗充负鏈鏍哥姸鎬侊級
+        // 2.1 鎻掑叆鑽夌锛涙彁浜ゆ椂鍐嶅垱寤� BPM 娴佺▼
         CrmReceivableDO receivable = BeanUtils.toBean(createReqVO, CrmReceivableDO.class)
-                .setNo(no).setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus());
+                .setNo(no).setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
         receivableMapper.insert(receivable);
         // 2.2
 
@@ -227,8 +241,9 @@
         // 1.5 鏍¢獙鍙洖娆鹃噾棰濅笉瓒呰繃璇ュ紑绁ㄥ墿浣欏彲鍥炴閲戦
         validateReceivablePriceExceedsInvoiceRemaining(updateReqVO);
 
-        // 1.6 鍙湁鏈鏍革紝鍙互缂栬緫锛�
-        if (ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
+        // 1.6 鍙湁鑽夌鎴栭┏鍥炲悗鍙互缂栬緫
+        if (ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())
+                && ObjectUtil.notEqual(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.REJECT.getStatus())) {
             throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED);
         }
 
@@ -253,20 +268,48 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void updateReceivableStatus(Long id, Integer status) {
-        boolean approve = CrmAuditStatusEnum.APPROVE.getStatus().equals(status);
-        // 1.1 鏍¢獙瀛樺湪
+    @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
+    public void submitReceivable(Long id) {
         CrmReceivableDO receivable = validateReceivableExists(id);
-        // 1.2 鏍¢獙鐘舵��
-        if (receivable.getAuditStatus().equals(status)) {
-            throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
+        if (ObjectUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())
+                && ObjectUtil.notEqual(receivable.getAuditStatus(), CrmAuditStatusEnum.REJECT.getStatus())) {
+            throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
         }
-
-        // 2. 鏇存柊鐘舵��
+        // 鑽夌鏈熷彲鑳藉凡浜х敓鍏朵粬鍥炴锛屾彁浜ゆ椂閲嶆柊鏍¢獙鍚堝悓鍙婂紑绁ㄩ搴︺��
+        CrmReceivableSaveReqVO req = new CrmReceivableSaveReqVO().setId(id)
+                .setContractId(receivable.getContractId()).setInvoiceId(receivable.getInvoiceId())
+                .setCustomerId(receivable.getCustomerId()).setPrice(receivable.getPrice());
+        validateInvoiceForReceivable(req);
+        validateReceivablePriceExceedsLimit(req);
+        validateReceivablePriceExceedsInvoiceRemaining(req);
+        approvalConfigApi.validateApprovalEnabledAndGetApprovers(RECEIVABLE_APPROVE_BIZ_TYPE);
         int updateCount = receivableMapper.updateByIdAndStatus(id, receivable.getAuditStatus(),
-                new CrmReceivableDO().setAuditStatus(status));
+                new CrmReceivableDO().setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
         if (updateCount == 0) {
-            throw exception(approve ? RECEIVABLE_APPROVE_FAIL : RECEIVABLE_PROCESS_FAIL);
+            throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
+        }
+        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(getLoginUserId(),
+                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(RECEIVABLE_PROCESS_KEY)
+                        .setBusinessKey(String.valueOf(id)));
+        receivableMapper.updateById(new CrmReceivableDO().setId(id).setProcessInstanceId(processInstanceId));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateReceivableAuditStatus(Long id, Integer bpmStatus) {
+        Integer auditStatus;
+        if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmStatus)) {
+            auditStatus = CrmAuditStatusEnum.APPROVE.getStatus();
+        } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmStatus)) {
+            auditStatus = CrmAuditStatusEnum.REJECT.getStatus();
+        } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmStatus)) {
+            auditStatus = CrmAuditStatusEnum.CANCEL.getStatus();
+        } else {
+            return;
+        }
+        if (receivableMapper.updateByIdAndStatus(id, CrmAuditStatusEnum.PROCESS.getStatus(),
+                new CrmReceivableDO().setAuditStatus(auditStatus)) != 1) {
+            log.warn("[updateReceivableAuditStatus] 鍥炴({}) 宸蹭笉澶勪簬瀹℃壒涓紝蹇界暐 BPM 鐘舵��({})", id, bpmStatus);
         }
     }
 
@@ -282,8 +325,9 @@
         if (receivable.getPlanId() != null && receivablePlanService.getReceivablePlan(receivable.getPlanId()) != null) {
             throw exception(RECEIVABLE_DELETE_FAIL);
         }
-        // 1.3 瀹℃壒閫氳繃鏃讹紝涓嶅厑璁稿垹闄�
-        if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
+        // 瀹℃壒涓�佸鏍搁�氳繃涓嶅彲鍒犻櫎
+        if (ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())
+                || ObjUtil.equal(receivable.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
             throw exception(RECEIVABLE_DELETE_FAIL_IS_APPROVE);
         }
 

--
Gitblit v1.9.3