From 5d34bdd3c426bb2ca5f6627424ae67984f745603 Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期三, 21 十月 2026 18:28:59 +0800
Subject: [PATCH] feat: 新增资产/用车/采购模块,完善课题、会员、财务与企微功能

---
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/reimbursement/FinanceReimbursementServiceImpl.java |  120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 108 insertions(+), 12 deletions(-)

diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/reimbursement/FinanceReimbursementServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/reimbursement/FinanceReimbursementServiceImpl.java
index 82a4924..445f470 100644
--- a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/reimbursement/FinanceReimbursementServiceImpl.java
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/reimbursement/FinanceReimbursementServiceImpl.java
@@ -2,8 +2,12 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.archive.api.document.ArchiveDocumentApi;
+import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentCreateReqDTO;
+import cn.iocoder.yudao.module.archive.api.document.dto.ArchiveDocumentRespDTO;
 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.dal.dataobject.definition.BpmCategoryDO;
@@ -20,6 +24,9 @@
 import cn.iocoder.yudao.module.finance.dal.redis.FinanceNoRedisDAO;
 import cn.iocoder.yudao.module.finance.enums.FinanceReimbursementStatusEnum;
 import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.engine.repository.ProcessDefinition;
@@ -57,6 +64,15 @@
      */
     public static final String REIMBURSEMENT_RECORD_TYPE = "finance_reimbursement";
 
+    /** 褰掓。鏉ユ簮绫诲瀷锛�4-璐㈠姟锛堝搴� archive 妯″潡 source_type锛� */
+    private static final Integer ARCHIVE_SOURCE_TYPE_FINANCE = 4;
+
+    /** 褰掓。瀵嗙骇锛�3-淇濆瘑锛堣储鍔℃。妗堥粯璁や繚瀵嗭級 */
+    private static final Integer ARCHIVE_SECRET_LEVEL_SECRET = 3;
+
+    /** 璐㈠姟妗f鍒嗙被缂栫爜锛堝搴� archive_category.code锛� */
+    private static final String ARCHIVE_CATEGORY_CODE_FINANCE = "finance";
+
     @Resource
     private FinanceReimbursementMapper reimbursementMapper;
     @Resource
@@ -67,6 +83,12 @@
 
     @Resource
     private StorageAttachmentApi storageAttachmentApi;
+
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @Resource
+    private ArchiveDocumentApi archiveDocumentApi;
 
     @Resource
     private BpmProcessInstanceApi processInstanceApi;
@@ -81,21 +103,26 @@
         // 1. 鎶ラ攢鍗曞彿锛氬厑璁哥敤鎴疯嚜瀹氫箟锛屾湭濉椂鐢辩郴缁熺敓鎴�
         String no = resolveCreateNo(createReqVO.getNo());
 
-        // 2.1 鎻掑叆鎶ラ攢鍗�
+        // 2.1 鐢宠浜猴細鏀寔浠d粬浜哄彂璧凤紱鏈寚瀹氭椂榛樿褰撳墠鐧诲綍鐢ㄦ埛锛堜唬褰曚汉鐢� creator 瀹¤瀛楁璁板綍锛�
+        Long applicantId = createReqVO.getApplicantId() != null
+                ? createReqVO.getApplicantId() : getLoginUserId();
+        validateApplicant(applicantId);
+
+        // 2.2 鎻掑叆鎶ラ攢鍗�
         FinanceReimbursementDO reimbursement = BeanUtils.toBean(createReqVO, FinanceReimbursementDO.class, in -> in
                 .setNo(no)
                 .setStatus(FinanceReimbursementStatusEnum.DRAFT.getStatus())
-                .setApplicantId(getLoginUserId())
+                .setApplicantId(applicantId)
                 .setApplyDate(createReqVO.getApplyDate() != null ? createReqVO.getApplyDate() : LocalDate.now()));
         reimbursementMapper.insert(reimbursement);
 
-        // 2.2 鎻掑叆鎶ラ攢鏄庣粏
+        // 2.3 鎻掑叆鎶ラ攢鏄庣粏
         List<FinanceReimbursementItemDO> items = buildItems(reimbursement.getId(), createReqVO.getItems());
         if (CollUtil.isNotEmpty(items)) {
             reimbursementItemMapper.insertBatch(items);
         }
 
-        // 2.3 缁戝畾鍙戠エ闄勪欢
+        // 2.4 缁戝畾鍙戠エ闄勪欢
         storageAttachmentApi.bindAttachments("file", REIMBURSEMENT_RECORD_TYPE,
                 reimbursement.getId(), createReqVO.getBlobIds());
 
@@ -116,6 +143,11 @@
 
         // 2.1 鏇存柊鎶ラ攢鍗�
         FinanceReimbursementDO updateObj = BeanUtils.toBean(updateReqVO, FinanceReimbursementDO.class);
+        // 鐢宠浜猴細浠d粬浜哄彂璧锋椂鍏佽淇敼瀹為檯鐢宠浜猴紱涓嶄紶鍒欎繚鎸佸師鍊�
+        if (updateReqVO.getApplicantId() != null) {
+            validateApplicant(updateReqVO.getApplicantId());
+            updateObj.setApplicantId(updateReqVO.getApplicantId());
+        }
         // 鎶ラ攢鍗曞彿锛氭湭濉〃绀轰笉鍙樻洿锛屽~浜嗗垯鏍¢獙鍚庤鐩�
         String customNo = StrUtil.trimToNull(updateReqVO.getNo());
         if (customNo == null || customNo.equals(reimbursement.getNo())) {
@@ -189,10 +221,12 @@
             throw exception(REIMBURSEMENT_SUBMIT_FAIL_ITEM_EMPTY);
         }
 
-        // 2. 鍒涘缓 BPM 娴佺▼瀹炰緥
+        // 2. 鍒涘缓 BPM 娴佺▼瀹炰緥锛屾妸瀹為檯鐢宠浜轰笌浠e綍浜哄啓鍏ユ祦绋嬪彉閲忥紝渚涘鎵规祦灞曠ず涓庤矾鐢变娇鐢�
+        Map<String, Object> variables = buildProcessVariables(reimbursement, userId);
         String processInstanceId = processInstanceApi.createProcessInstance(userId,
                 new BpmProcessInstanceCreateReqDTO()
                         .setProcessDefinitionKey(processDefinitionKey)
+                        .setVariables(variables)
                         .setBusinessKey(String.valueOf(id)));
 
         // 3. 鏇存柊鐘舵�佷负瀹℃壒涓�
@@ -204,18 +238,44 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void archiveReimbursement(Long id) {
-        // 1. 鏍¢獙瀛樺湪 + 宸查�氳繃鐘舵��
+    public String archiveReimbursement(Long id) {
+        // 1. 鏍¢獙瀛樺湪 + 鐘舵��
         FinanceReimbursementDO reimbursement = validateReimbursementExists(id);
-        if (!FinanceReimbursementStatusEnum.APPROVED.getStatus().equals(reimbursement.getStatus())) {
-            throw exception(REIMBURSEMENT_ARCHIVE_FAIL_STATUS);
+        boolean archived = FinanceReimbursementStatusEnum.ARCHIVED.getStatus().equals(reimbursement.getStatus());
+        if (archived && reimbursement.getArchiveDocumentId() != null) {
+            throw exception(REIMBURSEMENT_ALREADY_ARCHIVED, reimbursement.getNo(),
+                    StrUtil.blankToDefault(archiveNo(id), "鏈煡"));
+        }
+        if (!archived && !FinanceReimbursementStatusEnum.APPROVED.getStatus().equals(reimbursement.getStatus())) {
+            throw exception(REIMBURSEMENT_ARCHIVE_FAIL_STATUS, reimbursement.getNo(),
+                    FinanceReimbursementStatusEnum.nameOf(reimbursement.getStatus()));
         }
 
-        // 2. 鏇存柊涓哄凡褰掓。
+        // 2. 鏇存柊涓哄凡褰掓。锛堝巻鍙插凡褰掓。浣嗗皻鏈敓鎴愭。妗堟潯鐩殑锛岃烦杩囩姸鎬佹洿鏂帮紝浠呰ˉ鐢熸垚妗f锛�
+        if (!archived) {
+            reimbursementMapper.updateById(new FinanceReimbursementDO()
+                    .setId(id)
+                    .setStatus(FinanceReimbursementStatusEnum.ARCHIVED.getStatus())
+                    .setArchiveTime(LocalDateTime.now()));
+        }
+
+        // 3. 鎸夋潵婧愬崟鎹敓鎴愭。妗堟潯鐩紙璐㈠姟妗f榛樿銆屼繚瀵嗐�嶇骇锛夛紝骞跺洖鍐欐。妗� ID
+        List<Long> blobIds = storageAttachmentApi.listAttachments(REIMBURSEMENT_RECORD_TYPE, id).stream()
+                .map(StorageBlobRespDTO::getId).collect(Collectors.toList());
+        Long archiveDocumentId = archiveDocumentApi.archiveBySource(new ArchiveDocumentCreateReqDTO()
+                .setSourceType(ARCHIVE_SOURCE_TYPE_FINANCE)
+                .setSourceId(id)
+                .setSourceNo(reimbursement.getNo())
+                .setSourceTitle(reimbursement.getTitle())
+                .setCategoryCode(ARCHIVE_CATEGORY_CODE_FINANCE)
+                .setSecretLevel(ARCHIVE_SECRET_LEVEL_SECRET)
+                .setSummary("璐㈠姟鎶ラ攢鍑瘉褰掓。")
+                .setBlobIds(blobIds)).getCheckedData();
+
         reimbursementMapper.updateById(new FinanceReimbursementDO()
                 .setId(id)
-                .setStatus(FinanceReimbursementStatusEnum.ARCHIVED.getStatus())
-                .setArchiveTime(LocalDateTime.now()));
+                .setArchiveDocumentId(archiveDocumentId));
+        return StrUtil.blankToDefault(archiveNo(id), "鏈煡");
     }
 
     @Override
@@ -283,6 +343,13 @@
         return reimbursement;
     }
 
+    /** 鏌ヨ鏉ユ簮鍗曟嵁宸茬敓鎴愮殑妗f缂栧彿锛堟湭褰掓。鏃惰繑鍥� null锛� */
+    private String archiveNo(Long reimbursementId) {
+        ArchiveDocumentRespDTO archive = archiveDocumentApi
+                .getBySource(ARCHIVE_SOURCE_TYPE_FINANCE, reimbursementId).getCheckedData();
+        return archive != null ? archive.getNo() : null;
+    }
+
     /**
      * 瑙f瀽鏂板鏃剁殑鎶ラ攢鍗曞彿锛氬幓鎺夐灏剧┖鏍煎悗鑻ヤ负绌猴紝鍒欑敱绯荤粺鎸� BX + 鏃ユ湡 + 6 浣嶆祦姘寸敓鎴愶紝
      * 鍚﹀垯浣跨敤鐢ㄦ埛鑷畾涔夊崟鍙枫��
@@ -312,6 +379,35 @@
         }
     }
 
+    /**
+     * 鏍¢獙瀹為檯鐢宠浜哄瓨鍦ㄤ笖涓哄惎鐢ㄧ姸鎬�
+     */
+    private void validateApplicant(Long applicantId) {
+        AdminUserRespDTO applicant = applicantId != null ? adminUserApi.getUser(applicantId) : null;
+        if (applicant == null || !CommonStatusEnum.ENABLE.getStatus().equals(applicant.getStatus())) {
+            throw exception(REIMBURSEMENT_APPLICANT_NOT_EXISTS, applicantId);
+        }
+    }
+
+    /**
+     * 鏋勫缓鎶ラ攢瀹℃壒娴佺▼鍙橀噺锛氬疄闄呯敵璇蜂汉 + 浠e綍锛堝彂璧凤級浜�
+     *
+     * <p>瀹℃壒娴佷腑鍙�氳繃 applicantName 灞曠ず瀹為檯鐢宠浜猴紝閫氳繃 initiatorName 灞曠ず浠e綍浜猴紝
+     * 骞堕�氳繃 isProxy 鍒ゆ柇鏄惁涓轰唬浠栦汉鍙戣捣
+     */
+    private Map<String, Object> buildProcessVariables(FinanceReimbursementDO reimbursement, Long submitUserId) {
+        Long applicantId = reimbursement.getApplicantId();
+        AdminUserRespDTO applicant = applicantId != null ? adminUserApi.getUser(applicantId) : null;
+        AdminUserRespDTO submitter = submitUserId != null ? adminUserApi.getUser(submitUserId) : null;
+        Map<String, Object> variables = new HashMap<>(8);
+        variables.put("applicantId", applicantId);
+        variables.put("applicantName", applicant != null ? applicant.getNickname() : "");
+        variables.put("initiatorId", submitUserId);
+        variables.put("initiatorName", submitter != null ? submitter.getNickname() : "");
+        variables.put("isProxy", applicantId != null && !applicantId.equals(submitUserId));
+        return variables;
+    }
+
     private List<FinanceReimbursementItemDO> buildItems(Long reimbursementId,
                                                          List<FinanceReimbursementItemSaveReqVO> itemVOs) {
         if (CollUtil.isEmpty(itemVOs)) {

--
Gitblit v1.9.3