From c4a98bdfe1ed9a54d103c5729d83b64bb53cc28f Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期二, 25 八月 2026 09:08:07 +0800
Subject: [PATCH] feat(erp): 采购订单审批接入协同办公(BPM)

---
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java                                     |    3 +
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java             |   18 ++++++++-
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java     |   20 ++++++++++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseOrderStatusListener.java |   54 +++++++++++++++++++++++++++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java                |    4 ++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java                 |    8 ++++
 6 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
index df706ac..f779a39 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
@@ -13,6 +13,7 @@
 import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;
 import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
 import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO;
+import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
 import cn.iocoder.yudao.module.erp.enums.ErpPurchaseOrderSourceTypeEnum;
 import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
 import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
@@ -40,7 +41,9 @@
 import java.util.Map;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -170,6 +173,23 @@
         return success(purchaseOrderService.confirmReceipt(id));
     }
 
+    @PostMapping("/reverse-approve")
+    @Operation(summary = "鍙嶅鎵癸紙鍗忓悓鍔炲叕宸插姙浠诲姟涓挙鍥炲凡瀹℃牳璁㈠崟锛岀洿鎺ラ噸鏂拌繘鍏ュ緟鍔炲鎵癸級")
+    @Parameter(name = "processInstanceId", description = "娴佺▼瀹炰緥缂栧彿", required = true)
+    @PreAuthorize("@ss.hasPermission('erp:purchase-order:update-status')")
+    public CommonResult<Boolean> reverseApprove(@RequestParam("processInstanceId") String processInstanceId) {
+        // 1. 鏍规嵁娴佺▼瀹炰緥缂栧彿鎵惧埌閲囪喘璁㈠崟
+        ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrderByProcessInstanceId(processInstanceId);
+        if (purchaseOrder == null) {
+            throw exception(PURCHASE_ORDER_NOT_EXISTS);
+        }
+        // 2. 鍙嶅鏍革細宸插鏍� -> 鏈鏍革紙鍚叆搴�/閫�璐ф牎楠岋級
+        purchaseOrderService.updatePurchaseOrderStatus(purchaseOrder.getId(), ErpAuditStatus.PROCESS.getStatus());
+        // 3. 鑷姩閲嶆柊鎻愪氦瀹℃壒 -> 璁㈠崟鐩存帴鍥炲埌鍗忓悓鍔炲叕寰呭姙浠诲姟
+        purchaseOrderService.submitPurchaseOrder(purchaseOrder.getId(), "purchase_order_approve", getLoginUserId());
+        return success(true);
+    }
+
     private PageResult<ErpPurchaseOrderRespVO> buildPurchaseOrderVOPageResult(PageResult<ErpPurchaseOrderDO> pageResult) {
         if (CollUtil.isEmpty(pageResult.getList())) {
             return PageResult.empty(pageResult.getTotal());
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
index 9f7aca0..1caa99f 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/purchase/ErpPurchaseOrderMapper.java
@@ -74,6 +74,10 @@
         return selectOne(ErpPurchaseOrderDO::getNo, no);
     }
 
+    default ErpPurchaseOrderDO selectByProcessInstanceId(String processInstanceId) {
+        return selectOne(ErpPurchaseOrderDO::getProcessInstanceId, processInstanceId);
+    }
+
     default List<ErpPurchaseOrderDO> selectList(Long supplierId, Integer status) {
         return selectList(new LambdaQueryWrapperX<ErpPurchaseOrderDO>()
                 .eqIfPresent(ErpPurchaseOrderDO::getSupplierId, supplierId)
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
index 849625d..db7bbee 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
@@ -20,7 +20,8 @@
     PROCESS(10, "鏈鏍�"), // 鏈鏍�/鑽夌
     APPROVING(15, "瀹℃壒涓�"), // 瀹℃壒涓�
     APPROVE(20, "宸插鏍�"), // 瀹℃牳閫氳繃
-    CANCEL(30, "宸蹭綔搴�"); // 宸蹭綔搴�/宸叉挙閿�
+    CANCEL(30, "宸蹭綔搴�"), // 宸蹭綔搴�/宸叉挙閿�
+    REJECT(35, "宸查┏鍥�"); // 瀹℃壒琚┏鍥�
 
     public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpAuditStatus::getStatus).toArray(Integer[]::new);
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
index 120cbd2..d5c204f 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
@@ -98,6 +98,14 @@
     ErpPurchaseOrderDO getPurchaseOrder(Long id);
 
     /**
+     * 鏍规嵁 BPM 娴佺▼瀹炰緥缂栧彿鏌ヨ閲囪喘璁㈠崟锛堢敤浜庡崗鍚屽姙鍏弽瀹℃壒锛�
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥缂栧彿
+     * @return 閲囪喘璁㈠崟
+     */
+    ErpPurchaseOrderDO getPurchaseOrderByProcessInstanceId(String processInstanceId);
+
+    /**
      * 鏍¢獙閲囪喘璁㈠崟锛屽凡缁忓鏍搁�氳繃
      *
      * @param id 缂栧彿
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
index 083095f..8809791 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
@@ -41,6 +41,7 @@
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
 
 /**
@@ -56,7 +57,11 @@
     /**
      * BPM 閲囪喘璁㈠崟瀹℃壒鍒嗙被缂栫爜
      */
-    private static final String PURCHASE_ORDER_APPROVE_CATEGORY_CODE = "purchase_request_approve";
+    private static final String PURCHASE_ORDER_APPROVE_CATEGORY_CODE = "purchase_order_approve";
+    /**
+     * BPM 閲囪喘璁㈠崟瀹℃壒娴佺▼瀹氫箟 Key锛堝垱寤鸿鍗曞悗鑷姩鎻愪氦鍒板崗鍚屽姙鍏鎵癸級
+     */
+    private static final String PURCHASE_ORDER_APPROVE_PROCESS_DEFINITION_KEY = "purchase_order_approve";
 
     @Resource
     private ErpPurchaseOrderMapper purchaseOrderMapper;
@@ -119,6 +124,10 @@
         // 2.3 缁戝畾闄勪欢
         storageAttachmentApi.bindAttachments("file", "erp_purchase_order",
                 purchaseOrder.getId(), createReqVO.getBlobIds());
+
+        // 3. 鍒涘缓鍚庤嚜鍔ㄦ彁浜ゅ鎵瑰埌鍗忓悓鍔炲叕锛堝鎵瑰湪鍗忓悓鍔炲叕瀹屾垚锛屼笉鍦ㄦ湰椤甸潰瀹℃壒锛�
+        submitPurchaseOrder(purchaseOrder.getId(), PURCHASE_ORDER_APPROVE_PROCESS_DEFINITION_KEY, getLoginUserId());
+
         return purchaseOrder.getId();
     }
 
@@ -288,7 +297,7 @@
         if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) {
             return ErpAuditStatus.APPROVE.getStatus();
         } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) {
-            return ErpAuditStatus.PROCESS.getStatus();
+            return ErpAuditStatus.REJECT.getStatus();
         } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) {
             return ErpAuditStatus.CANCEL.getStatus();
         }
@@ -406,6 +415,11 @@
     }
 
     @Override
+    public ErpPurchaseOrderDO getPurchaseOrderByProcessInstanceId(String processInstanceId) {
+        return purchaseOrderMapper.selectByProcessInstanceId(processInstanceId);
+    }
+
+    @Override
     public ErpPurchaseOrderDO validatePurchaseOrder(Long id) {
         ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
         if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseOrderStatusListener.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseOrderStatusListener.java
new file mode 100644
index 0000000..62fdb1d
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseOrderStatusListener.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.erp.service.purchase.listener;
+
+import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
+import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
+import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseOrderService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * 閲囪喘璁㈠崟瀹℃壒鐘舵�佺洃鍚櫒
+ *
+ * 鐩戝惉 BPM 娴佺▼瀹炰緥鐘舵�佸彉鏇翠簨浠讹紝鏇存柊閲囪喘璁㈠崟鐨勫鎵圭姸鎬侊紙鍗忓悓鍔炲叕瀹℃壒鍚庡洖鍐欙級
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Component
+@Slf4j
+public class ErpPurchaseOrderStatusListener extends BpmProcessInstanceStatusEventListener {
+
+    /**
+     * 閲囪喘璁㈠崟瀹℃壒娴佺▼瀹氫箟 Key
+     */
+    private static final String PROCESS_DEFINITION_KEY = "purchase_order_approve";
+
+    @Resource
+    private ErpPurchaseOrderService purchaseOrderService;
+
+    @Override
+    protected String getProcessDefinitionKey() {
+        return PROCESS_DEFINITION_KEY;
+    }
+
+    @Override
+    protected void onEvent(BpmProcessInstanceStatusEvent event) {
+        Long orderId;
+        try {
+            orderId = Long.parseLong(event.getBusinessKey());
+        } catch (NumberFormatException e) {
+            log.warn("[onEvent] businessKey({}) 涓嶆槸鏈夋晥鐨勯噰璐鍗曠紪鍙�", event.getBusinessKey());
+            return;
+        }
+
+        log.info("[onEvent] 鏀跺埌BPM瀹℃壒浜嬩欢锛宱rderId={}, status={}", orderId, event.getStatus());
+
+        try {
+            purchaseOrderService.updatePurchaseOrderAuditStatus(orderId, event.getStatus());
+            log.info("[onEvent] 閲囪喘璁㈠崟({}) 瀹℃壒鐘舵�佹洿鏂板畬鎴�", orderId);
+        } catch (Exception e) {
+            log.error("[onEvent] 鏇存柊閲囪喘璁㈠崟瀹℃壒鐘舵�佸け璐ワ紝orderId={}", orderId, e);
+        }
+    }
+
+}

--
Gitblit v1.9.3