From 7ab45e2b02facae5685ba879d150de6dc0032aa4 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期一, 25 五月 2026 10:38:59 +0800
Subject: [PATCH] Merge branch 'dev_New_pro' of http://114.132.189.42:9002/r/product-inventory-management-after into dev_New_pro

---
 src/main/java/com/ruoyi/approve/controller/ApprovalTaskController.java                             |   18 
 src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceMapper.java                                 |   24 
 src/main/java/com/ruoyi/approve/pojo/FinReimbursementTravel.java                                   |  162 +
 src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeDeptMapper.java            |   18 
 src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeController.java                     |   18 
 src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsMapper.java                     |   24 
 src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java                              |    1 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeServiceImpl.java                  |   62 
 src/main/java/com/ruoyi/approve/pojo/FinReimbursementDetail.java                                   |  157 +
 src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeService.java                           |   20 
 src/main/resources/mapper/approve/FinReimbursementTravelMapper.xml                                 |   29 
 src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java              |    2 
 src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeDeptMapper.xml                  |   13 
 src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeUserMapper.java            |   18 
 src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java                   |    8 
 src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeDept.java                    |   59 
 src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java                                      |    2 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalTaskServiceImpl.java                          |   20 
 src/main/java/com/ruoyi/common/enums/ApprovalStatusEnum.java                                       |   49 
 src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java                                             |    4 
 src/main/java/com/ruoyi/approve/mapper/ApprovalTaskMapper.java                                     |   18 
 src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java                                    |   34 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateServiceImpl.java                      |  250 ++
 src/main/java/com/ruoyi/approve/service/ApprovalInstanceNodeService.java                           |   16 
 src/main/java/com/ruoyi/approve/pojo/ApprovalInstance.java                                         |  152 +
 src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java                                 |   24 
 src/main/resources/mapper/approve/FinReimbursementMapper.xml                                       |   61 
 src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java                                         |  210 +
 src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceNodeServiceImpl.java                  |   20 
 src/main/java/com/ruoyi/approve/mapper/FinReimbursementDetailMapper.java                           |   18 
 src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateDto.java                                  |   12 
 src/main/java/com/ruoyi/quality/utils/QualityInspectHelper.java                                    |   73 
 src/main/resources/mapper/approve/ApprovalInstanceMapper.xml                                       |   58 
 src/main/java/com/ruoyi/approve/pojo/ApprovalTask.java                                             |  128 +
 src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java                         |   60 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java                      |  753 +++++++
 src/main/java/com/ruoyi/approve/service/ApprovalRecordService.java                                 |   16 
 src/main/java/com/ruoyi/approve/pojo/ApprovalRecord.java                                           |   98 
 src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeVo.java                                |   13 
 src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceNodeMapper.java                             |   18 
 src/main/java/com/ruoyi/approve/controller/ApprovalInstanceNodeController.java                     |   19 
 src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeUserMapper.xml                  |   13 
 src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java                          |   54 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalRecordServiceImpl.java                        |   20 
 src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNode.java                                     |   66 
 src/main/java/com/ruoyi/approve/service/FinReimbursementTravelService.java                         |   16 
 src/main/resources/mapper/staff/StaffLeaveMapper.xml                                               |   21 
 src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateMapper.java                                 |   24 
 src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeMapper.java                             |   18 
 src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeUserServiceImpl.java |   20 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeApproverServiceImpl.java          |   20 
 src/main/java/com/ruoyi/approve/service/impl/FinReimbursementDetailServiceImpl.java                |   20 
 src/main/java/com/ruoyi/approve/pojo/ApprovalInstanceNode.java                                     |  106 
 src/main/resources/mapper/approve/ApprovalTemplateNodeMapper.xml                                   |   15 
 src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeDeptServiceImpl.java |   20 
 src/main/java/com/ruoyi/approve/controller/ApprovalRecordController.java                           |   18 
 src/main/resources/mapper/approve/ApprovalTemplateMapper.xml                                       |   33 
 src/main/resources/mapper/approve/ApprovalRecordMapper.xml                                         |   20 
 src/main/resources/mapper/approve/ApprovalTemplateNodeApproverMapper.xml                           |   16 
 src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeApproverMapper.java                     |   18 
 src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java                                            |    5 
 src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java                               |   25 
 src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java                                               |    2 
 src/main/java/com/ruoyi/common/enums/TypeEnums.java                                                |   70 
 src/main/java/com/ruoyi/approve/bean/dto/FinReimbursementDto.java                                  |   21 
 src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeDeptController.java    |   18 
 src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java                              |   31 
 src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeApproverService.java                   |   16 
 src/main/resources/mapper/approve/ApprovalTaskMapper.xml                                           |   24 
 src/main/resources/mapper/system/SysNoticeMapper.xml                                               |    5 
 src/main/java/com/ruoyi/common/enums/ShippingStatusEnum.java                                       |   42 
 src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeApproverController.java             |   18 
 src/main/java/com/ruoyi/approve/service/ApprovalTaskService.java                                   |   16 
 src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java                      |  544 +++++
 src/main/java/com/ruoyi/approve/mapper/ApprovalRecordMapper.java                                   |   18 
 src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsService.java                   |   29 
 src/main/resources/mapper/staff/StaffOnJobMapper.xml                                               |   91 
 src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java                |    2 
 src/main/java/com/ruoyi/approve/bean/dto/ApprovalInstanceDto.java                                  |   21 
 src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeUserService.java          |   16 
 src/main/java/com/ruoyi/approve/pojo/ApprovalTemplate.java                                         |  107 +
 src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeUser.java                    |   59 
 src/main/java/com/ruoyi/approve/controller/FinReimbursementTravelController.java                   |   18 
 src/main/java/com/ruoyi/approve/bean/vo/ApprovalInstanceVo.java                                    |   28 
 src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeUserController.java    |   18 
 src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java                                         |    4 
 src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml                           |   46 
 src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java                      |  169 +
 src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeDto.java                              |   12 
 src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java                                 |    2 
 src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNodeApprover.java                             |   76 
 src/main/java/com/ruoyi/common/enums/EnterpriseNewsStatusEnum.java                                 |  143 +
 src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeApproverVo.java                        |    9 
 src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNews.java                             |   88 
 src/main/java/com/ruoyi/approve/controller/ApprovalInstanceController.java                         |   67 
 src/main/java/com/ruoyi/collaborativeApproval/dto/EnterpriseNewsDto.java                           |   26 
 src/main/java/com/ruoyi/approve/controller/FinReimbursementDetailController.java                   |   18 
 src/main/java/com/ruoyi/approve/controller/ApprovalTemplateController.java                         |   74 
 src/main/java/com/ruoyi/approve/service/FinReimbursementDetailService.java                         |   16 
 src/main/java/com/ruoyi/approve/service/FinReimbursementService.java                               |   31 
 src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java                           |  363 +++
 src/main/java/com/ruoyi/common/enums/SalesQuotationStatusEnum.java                                 |   43 
 src/main/resources/mapper/approve/FinReimbursementDetailMapper.xml                                 |   29 
 src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java                             |   15 
 src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsServiceImpl.java          |  409 +++
 src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java                                           |    2 
 src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java                                          |    3 
 src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeDeptService.java          |   16 
 src/main/java/com/ruoyi/approve/service/ApprovalTemplateService.java                               |   34 
 src/main/resources/mapper/approve/ApprovalInstanceNodeMapper.xml                                   |   21 
 src/main/java/com/ruoyi/approve/service/impl/FinReimbursementTravelServiceImpl.java                |   20 
 src/main/java/com/ruoyi/approve/mapper/FinReimbursementTravelMapper.java                           |   18 
 src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateVo.java                                    |   14 
 src/main/java/com/ruoyi/approve/service/ApprovalInstanceService.java                               |   31 
 src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java             |    1 
 src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsController.java             |   61 
 src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeApproverDto.java                      |    8 
 117 files changed, 6,317 insertions(+), 93 deletions(-)

diff --git a/src/main/java/com/ruoyi/approve/bean/dto/ApprovalInstanceDto.java b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalInstanceDto.java
new file mode 100644
index 0000000..a2df482
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalInstanceDto.java
@@ -0,0 +1,21 @@
+package com.ruoyi.approve.bean.dto;
+
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ApprovalInstanceDto extends ApprovalInstance {
+
+    private String approveAction;
+
+    private String approveComment;
+
+    private String createTimeEnd;
+
+    private String createTimeStart;
+
+    private List<StorageBlobDTO> storageBlobDTOs;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateDto.java b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateDto.java
new file mode 100644
index 0000000..35952a5
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateDto.java
@@ -0,0 +1,12 @@
+package com.ruoyi.approve.bean.dto;
+
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ApprovalTemplateDto  extends ApprovalTemplate {
+
+    private List<ApprovalTemplateNodeDto> nodes;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeApproverDto.java b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeApproverDto.java
new file mode 100644
index 0000000..8382619
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeApproverDto.java
@@ -0,0 +1,8 @@
+package com.ruoyi.approve.bean.dto;
+
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import lombok.Data;
+
+@Data
+public class ApprovalTemplateNodeApproverDto extends ApprovalTemplateNodeApprover {
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeDto.java b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeDto.java
new file mode 100644
index 0000000..e85aee9
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeDto.java
@@ -0,0 +1,12 @@
+package com.ruoyi.approve.bean.dto;
+
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ApprovalTemplateNodeDto extends ApprovalTemplateNode {
+
+    private List<ApprovalTemplateNodeApproverDto> approvers;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/dto/FinReimbursementDto.java b/src/main/java/com/ruoyi/approve/bean/dto/FinReimbursementDto.java
new file mode 100644
index 0000000..2f14376
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/dto/FinReimbursementDto.java
@@ -0,0 +1,21 @@
+package com.ruoyi.approve.bean.dto;
+
+import com.ruoyi.approve.pojo.FinReimbursement;
+import com.ruoyi.approve.pojo.FinReimbursementDetail;
+import com.ruoyi.approve.pojo.FinReimbursementTravel;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FinReimbursementDto extends FinReimbursement {
+
+    private String createTimeStart;
+    private String createTimeEnd;
+
+    private FinReimbursementTravel  travel;
+    private List<FinReimbursementDetail> details;
+    private List<ApprovalTemplateNodeDto> nodes;
+    private List<StorageBlobDTO> storageBlobDTOs;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/ApprovalInstanceVo.java b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalInstanceVo.java
new file mode 100644
index 0000000..552ed0a
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalInstanceVo.java
@@ -0,0 +1,28 @@
+package com.ruoyi.approve.bean.vo;
+
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.approve.pojo.ApprovalRecord;
+import com.ruoyi.approve.pojo.ApprovalTask;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ApprovalInstanceVo extends ApprovalInstance {
+    //褰撳墠鐢ㄦ埛鏄惁鍙互瀹℃壒
+    @Schema(description = "褰撳墠鐢ㄦ埛鏄惁鍙互瀹℃壒")
+    private Boolean isApprove;
+
+    //瀹℃壒娴佺▼
+    private List<ApprovalTask> tasks;
+
+    //瀹℃壒璁板綍
+    private List<ApprovalRecord>  records;
+
+    @Schema(description = "涓氬姟鍚嶇О")
+    private String businessName;
+
+    private List<StorageBlobVO> storageBlobVOList;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeApproverVo.java b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeApproverVo.java
new file mode 100644
index 0000000..4c0ce0f
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeApproverVo.java
@@ -0,0 +1,9 @@
+package com.ruoyi.approve.bean.vo;
+
+
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import lombok.Data;
+
+@Data
+public class ApprovalTemplateNodeApproverVo extends ApprovalTemplateNodeApprover {
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeVo.java b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeVo.java
new file mode 100644
index 0000000..9acd0af
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeVo.java
@@ -0,0 +1,13 @@
+package com.ruoyi.approve.bean.vo;
+
+
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ApprovalTemplateNodeVo extends ApprovalTemplateNode {
+
+    private List<ApprovalTemplateNodeApproverVo> approvers;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateVo.java b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateVo.java
new file mode 100644
index 0000000..2793293
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateVo.java
@@ -0,0 +1,14 @@
+package com.ruoyi.approve.bean.vo;
+
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class ApprovalTemplateVo extends ApprovalTemplate {
+
+    private List<ApprovalTemplateNodeVo> nodes;
+
+    private String createdUserName;
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java b/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java
new file mode 100644
index 0000000..5706ff3
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java
@@ -0,0 +1,34 @@
+package com.ruoyi.approve.bean.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.approve.pojo.*;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class FinReimbursementVo extends FinReimbursement {
+
+
+    private String createTimeStart;
+    private String createTimeEnd;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime startTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+
+    private FinReimbursementTravel travel;
+    private List<FinReimbursementDetail> details;
+    //瀹℃壒娴佺▼
+    private List<ApprovalTask> tasks;
+
+    //瀹℃壒璁板綍
+    private List<ApprovalRecord>  records;
+    private List<StorageBlobVO> storageBlobVOList;
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalInstanceController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalInstanceController.java
new file mode 100644
index 0000000..36dcfc3
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalInstanceController.java
@@ -0,0 +1,67 @@
+package com.ruoyi.approve.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.bean.vo.ApprovalInstanceVo;
+import com.ruoyi.approve.service.ApprovalInstanceService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹℃壒瀹炰緥琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:46
+ */
+@RestController
+@RequestMapping("/approvalInstance")
+@Tag(name = "瀹℃壒瀹炰緥琛�")
+@AllArgsConstructor
+public class ApprovalInstanceController extends BaseController {
+
+    private final ApprovalInstanceService approvalInstanceService;
+    @GetMapping("/listPage")
+    @Operation(summary = "鍒嗛〉鏌ヨ")
+    @Log(title = "瀹℃壒鍒楄〃鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
+        return approvalInstanceService.listPage(page, approvalInstanceDto);
+    }
+
+    @PostMapping("/save")
+    @Operation(summary = "淇濆瓨")
+    @Log(title = "瀹℃壒鍒楄〃淇濆瓨", businessType = BusinessType.INSERT)
+    public R save(@RequestBody ApprovalInstanceDto approvalInstanceDto) {
+        return approvalInstanceService.add(approvalInstanceDto) ? R.ok() : R.fail();
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "鏇存柊")
+    @Log(title = "瀹℃壒鍒楄〃鏇存柊", businessType = BusinessType.UPDATE)
+    public R update(@RequestBody ApprovalInstanceDto approvalInstanceDto) {
+        return approvalInstanceService.update(approvalInstanceDto) ? R.ok() : R.fail();
+    }
+
+    @DeleteMapping("/delete")
+    @Log(title = "瀹℃壒鍒楄〃鍒犻櫎", businessType = BusinessType.DELETE)
+    @Operation(summary = "鍒犻櫎")
+    public R delete(@RequestBody List<Long> ids) {
+        return approvalInstanceService.delete(ids) ? R.ok() : R.fail();
+    }
+
+    @Operation(summary = "瀹℃壒")
+    @PostMapping("/approve")
+    @Log(title = "瀹℃壒鍒楄〃瀹℃壒", businessType = BusinessType.UPDATE)
+    public R approve(@RequestBody ApprovalInstanceDto approvalInstanceDto) {
+        return approvalInstanceService.approve(approvalInstanceDto);
+    }
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalInstanceNodeController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalInstanceNodeController.java
new file mode 100644
index 0000000..e42af42
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalInstanceNodeController.java
@@ -0,0 +1,19 @@
+package com.ruoyi.approve.controller;
+
+import com.ruoyi.framework.web.controller.BaseController;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 瀹℃壒鑺傜偣瀹炰緥琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:54
+ */
+@RestController
+@RequestMapping("/approvalInstanceNode")
+public class ApprovalInstanceNodeController extends BaseController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalRecordController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalRecordController.java
new file mode 100644
index 0000000..2d6f9dd
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalRecordController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 瀹℃壒璁板綍琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:28:21
+ */
+@RestController
+@RequestMapping("/approvalRecord")
+public class ApprovalRecordController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalTaskController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalTaskController.java
new file mode 100644
index 0000000..8682caa
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalTaskController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 瀹℃壒浠诲姟琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:32:37
+ */
+@RestController
+@RequestMapping("/approvalTask")
+public class ApprovalTaskController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateController.java
new file mode 100644
index 0000000..674d278
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateController.java
@@ -0,0 +1,74 @@
+package com.ruoyi.approve.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateDto;
+import com.ruoyi.approve.bean.vo.ApprovalTemplateVo;
+import com.ruoyi.approve.service.ApprovalTemplateService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺嬮亾杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:08
+ */
+@RestController
+@RequestMapping("/approvalTemplate")
+@Tag(name = "瀹℃壒妯℃澘琛�")
+@AllArgsConstructor
+public class ApprovalTemplateController  extends BaseController {
+
+    private final ApprovalTemplateService approvalTemplateService;
+
+    @GetMapping("/listPage")
+    @Operation(summary = "鍒嗛〉鏌ヨ")
+    @Log(title = "瀹℃壒妯℃澘鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    public R listPage(Page<ApprovalTemplateVo> page, ApprovalTemplateDto approvalTemplateDto) {
+        return R.ok(approvalTemplateService.listPage(page, approvalTemplateDto));
+    }
+
+    @PostMapping("/add")
+    @Operation(summary = "娣诲姞")
+    @Log(title = "娣诲姞瀹℃壒妯℃澘", businessType = BusinessType.INSERT)
+    public R add(@RequestBody ApprovalTemplateDto approvalTemplateDto) {
+        return R.ok(approvalTemplateService.saveApprovalTemplateDto(approvalTemplateDto));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "淇敼")
+    @Log(title = "淇敼瀹℃壒妯℃澘", businessType = BusinessType.UPDATE)
+    public R update(@RequestBody ApprovalTemplateDto approvalTemplateDto) {
+        return R.ok(approvalTemplateService.updateApprovalTemplateDto(approvalTemplateDto));
+    }
+
+    @PostMapping("/delete")
+    @Operation(summary = "鍒犻櫎")
+    @Log(title = "鍒犻櫎瀹℃壒妯℃澘", businessType = BusinessType.DELETE)
+    public R delete(@RequestBody List<Long> ids) {
+        return R.ok(approvalTemplateService.delete(ids));
+    }
+
+    @GetMapping("/list/{type}")
+    @Operation(summary = "鏌ヨ鎵�鏈夊鎵规ā鏉�")
+    public R list(@PathVariable("type") Integer type) {
+        return R.ok(approvalTemplateService.listApprovalTemplateVo(type));
+    }
+
+    @GetMapping("/detail/{id}")
+    @Operation(summary = "鏌ヨ瀹℃壒妯℃澘璇︽儏")
+    @Log(title = "鏌ヨ瀹℃壒妯℃澘璇︽儏", businessType = BusinessType.OTHER)
+    public R detail(@PathVariable("id") Long id) {
+        return R.ok(approvalTemplateService.getApprovalTemplateVoById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeApproverController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeApproverController.java
new file mode 100644
index 0000000..b0e2bff
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeApproverController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣瀹℃壒浜鸿〃 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:30
+ */
+@RestController
+@RequestMapping("/approvalTemplateNodeApprover")
+public class ApprovalTemplateNodeApproverController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeController.java b/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeController.java
new file mode 100644
index 0000000..5a1ed88
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:19
+ */
+@RestController
+@RequestMapping("/approvalTemplateNode")
+public class ApprovalTemplateNodeController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java b/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java
new file mode 100644
index 0000000..a2eecdb
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java
@@ -0,0 +1,60 @@
+package com.ruoyi.approve.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.FinReimbursementDto;
+import com.ruoyi.approve.bean.vo.FinReimbursementVo;
+import com.ruoyi.approve.service.FinReimbursementService;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曚富琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:15
+ */
+@RestController
+@RequestMapping("/finReimbursement")
+@Tag(name = "鎶ラ攢鍗曚富琛�", description = "鎶ラ攢鍗曚富琛�")
+@AllArgsConstructor
+public class FinReimbursementController {
+
+    private final FinReimbursementService finReimbursementService;
+
+    @GetMapping("/listPage")
+    @Operation(summary = "鍒嗛〉鏌ヨ")
+    public R listPage(Page<FinReimbursementVo> page, FinReimbursementDto finReimbursementDto) {
+        return R.ok(finReimbursementService.listPage(finReimbursementDto, page));
+    }
+
+    @PostMapping("/save")
+    @Operation(summary = "淇濆瓨")
+    public R save(@RequestBody FinReimbursementDto finReimbursementDto) {
+        return R.ok(finReimbursementService.add(finReimbursementDto));
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "璇︽儏")
+    public R detail(Long id) {
+        return R.ok(finReimbursementService.detail(id));
+    }
+
+    @PostMapping("/update")
+    @Operation(summary = "淇敼")
+    public R update(@RequestBody FinReimbursementDto finReimbursementDto) {
+        return R.ok(finReimbursementService.update(finReimbursementDto));
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎")
+    public R delete(@RequestBody List<Long> ids) {
+        return R.ok(finReimbursementService.delete(ids));
+    }
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/FinReimbursementDetailController.java b/src/main/java/com/ruoyi/approve/controller/FinReimbursementDetailController.java
new file mode 100644
index 0000000..18e4f73
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/FinReimbursementDetailController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曟槑缁嗚〃 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:38
+ */
+@RestController
+@RequestMapping("/finReimbursementDetail")
+public class FinReimbursementDetailController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/controller/FinReimbursementTravelController.java b/src/main/java/com/ruoyi/approve/controller/FinReimbursementTravelController.java
new file mode 100644
index 0000000..0e67feb
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/controller/FinReimbursementTravelController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 宸梾鎶ラ攢鎵╁睍琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:47
+ */
+@RestController
+@RequestMapping("/finReimbursementTravel")
+public class FinReimbursementTravelController {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceMapper.java
new file mode 100644
index 0000000..a541b90
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceMapper.java
@@ -0,0 +1,24 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.bean.vo.ApprovalInstanceVo;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 瀹℃壒瀹炰緥琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:46
+ */
+@Mapper
+public interface ApprovalInstanceMapper extends BaseMapper<ApprovalInstance> {
+
+    IPage<ApprovalInstanceVo> listPage(Page<ApprovalInstanceVo> page,@Param("ew") ApprovalInstanceDto approvalInstanceDto);
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceNodeMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceNodeMapper.java
new file mode 100644
index 0000000..8787e55
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceNodeMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.ApprovalInstanceNode;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹℃壒鑺傜偣瀹炰緥琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:54
+ */
+@Mapper
+public interface ApprovalInstanceNodeMapper extends BaseMapper<ApprovalInstanceNode> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalRecordMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalRecordMapper.java
new file mode 100644
index 0000000..fcf5ec6
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalRecordMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.ApprovalRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹℃壒璁板綍琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:28:21
+ */
+@Mapper
+public interface ApprovalRecordMapper extends BaseMapper<ApprovalRecord> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalTaskMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalTaskMapper.java
new file mode 100644
index 0000000..2531d8e
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalTaskMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.ApprovalTask;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹℃壒浠诲姟琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:32:37
+ */
+@Mapper
+public interface ApprovalTaskMapper extends BaseMapper<ApprovalTask> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateMapper.java
new file mode 100644
index 0000000..693f4b2
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateMapper.java
@@ -0,0 +1,24 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateDto;
+import com.ruoyi.approve.bean.vo.ApprovalTemplateVo;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:08
+ */
+@Mapper
+public interface ApprovalTemplateMapper extends BaseMapper<ApprovalTemplate> {
+
+    IPage<ApprovalTemplateVo> listPage(Page<ApprovalTemplateVo> page,@Param("ew") ApprovalTemplateDto approvalTemplateDto);
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeApproverMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeApproverMapper.java
new file mode 100644
index 0000000..d96f1e3
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeApproverMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣瀹℃壒浜鸿〃 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:30
+ */
+@Mapper
+public interface ApprovalTemplateNodeApproverMapper extends BaseMapper<ApprovalTemplateNodeApprover> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeMapper.java b/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeMapper.java
new file mode 100644
index 0000000..dc4a67f
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:19
+ */
+@Mapper
+public interface ApprovalTemplateNodeMapper extends BaseMapper<ApprovalTemplateNode> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/FinReimbursementDetailMapper.java b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementDetailMapper.java
new file mode 100644
index 0000000..edc039a
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementDetailMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.FinReimbursementDetail;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曟槑缁嗚〃 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:38
+ */
+@Mapper
+public interface FinReimbursementDetailMapper extends BaseMapper<FinReimbursementDetail> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java
new file mode 100644
index 0000000..19ac354
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java
@@ -0,0 +1,24 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.FinReimbursementDto;
+import com.ruoyi.approve.bean.vo.FinReimbursementVo;
+import com.ruoyi.approve.pojo.FinReimbursement;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曚富琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:15
+ */
+@Mapper
+public interface FinReimbursementMapper extends BaseMapper<FinReimbursement> {
+
+    IPage<FinReimbursementVo> listPage(@Param("ew") FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page);
+}
diff --git a/src/main/java/com/ruoyi/approve/mapper/FinReimbursementTravelMapper.java b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementTravelMapper.java
new file mode 100644
index 0000000..bc5e1b8
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementTravelMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.approve.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.approve.pojo.FinReimbursementTravel;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 宸梾鎶ラ攢鎵╁睍琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:47
+ */
+@Mapper
+public interface FinReimbursementTravelMapper extends BaseMapper<FinReimbursementTravel> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalInstance.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalInstance.java
new file mode 100644
index 0000000..77d8d58
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalInstance.java
@@ -0,0 +1,152 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒瀹炰緥琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:46
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_instance")
+@ApiModel(value = "ApprovalInstance瀵硅薄", description = "瀹℃壒瀹炰緥琛�")
+public class ApprovalInstance implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 瀹℃壒瀹炰緥ID
+     */
+    @Schema(description ="瀹℃壒瀹炰緥ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 瀹℃壒缂栧彿
+     */
+    @Schema(description ="瀹℃壒缂栧彿")
+    private String instanceNo;
+
+    /**
+     * 妯℃澘ID
+     */
+    @Schema(description ="妯℃澘ID")
+    private Long templateId;
+
+    /**
+     * 妯℃澘鍚嶇О
+     */
+    @Schema(description ="妯℃澘鍚嶇О")
+    private String templateName;
+
+    /**
+     * 涓氬姟ID
+     */
+    @Schema(description ="涓氬姟ID")
+    private Long businessId;
+
+    /**
+     * 涓氬姟绫诲瀷
+     */
+    @Schema(description ="涓氬姟绫诲瀷")
+    private Long businessType;
+
+    /**
+     * 瀹℃壒鏍囬
+     */
+    @Schema(description ="瀹℃壒鏍囬")
+    private String title;
+
+    /**
+     * 瀹℃壒鐘舵��
+     */
+    @Schema(description ="瀹℃壒鐘舵�� PENDING - 寰呭鎵�/杩涜涓�  APPROVED - 宸查�氳繃/宸插畬鎴�  REJECTED - 宸查┏鍥�")
+    private String status;
+
+    /**
+     * 褰撳墠瀹℃壒绾у埆
+     */
+    @Schema(description ="褰撳墠瀹℃壒绾у埆")
+    private Integer currentLevel;
+
+    /**
+     * 鐢宠浜篒D
+     */
+    @Schema(description ="鐢宠浜篒D")
+    private Long applicantId;
+
+    /**
+     * 鐢宠浜哄悕绉�
+     */
+    @Schema(description ="鐢宠浜哄悕绉�")
+    private String applicantName;
+
+    /**
+     * 鐢宠鏃堕棿
+     */
+    @Schema(description ="鐢宠鏃堕棿")
+    private LocalDateTime applyTime;
+
+    /**
+     * 瀹屾垚鏃堕棿
+     */
+    @Schema(description ="瀹屾垚鏃堕棿")
+    private LocalDateTime finishTime;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description ="鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description ="鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description ="鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description ="鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+    /**
+     * 閫昏緫鍒犻櫎
+     */
+    @Schema(description ="閫昏緫鍒犻櫎")
+    private Byte deleted;
+
+    @Schema(description = "琛ㄥ崟鏁版嵁")
+    private String formConfig;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalInstanceNode.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalInstanceNode.java
new file mode 100644
index 0000000..621ce1f
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalInstanceNode.java
@@ -0,0 +1,106 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒鑺傜偣瀹炰緥琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:54
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_instance_node")
+@ApiModel(value = "ApprovalInstanceNode瀵硅薄", description = "瀹℃壒鑺傜偣瀹炰緥琛�")
+public class ApprovalInstanceNode implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 鑺傜偣瀹炰緥ID
+     */
+    @Schema(description ="鑺傜偣瀹炰緥ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 瀹℃壒瀹炰緥ID
+     */
+    @Schema(description ="瀹℃壒瀹炰緥ID")
+    private Long instanceId;
+
+    /**
+     * 瀹℃壒绾у埆
+     */
+    @Schema(description ="瀹℃壒绾у埆")
+    private Integer levelNo;
+
+    /**
+     * 瀹℃壒绫诲瀷
+     */
+    @Schema(description ="瀹℃壒绫诲瀷")
+    private String approveType;
+
+    /**
+     * 鑺傜偣鐘舵��
+     */
+    @Schema(description ="鑺傜偣鐘舵�� PENDING - 寰呭鐞� APPROVED - 宸查�氳繃 REJECTED - 宸查┏鍥�")
+    private String status;
+
+    /**
+     * 寮�濮嬫椂闂�
+     */
+    @Schema(description ="寮�濮嬫椂闂�")
+    private LocalDateTime startTime;
+
+    /**
+     * 瀹屾垚鏃堕棿
+     */
+    @Schema(description ="瀹屾垚鏃堕棿")
+    private LocalDateTime finishTime;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description ="鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description ="鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description ="鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description ="鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 閫昏緫鍒犻櫎
+     */
+    @Schema(description ="閫昏緫鍒犻櫎")
+    private Byte deleted;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalRecord.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalRecord.java
new file mode 100644
index 0000000..21bc52b
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalRecord.java
@@ -0,0 +1,98 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒璁板綍琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:28:21
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_record")
+@ApiModel(value = "ApprovalRecord瀵硅薄", description = "瀹℃壒璁板綍琛�")
+public class ApprovalRecord implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 瀹℃壒璁板綍ID
+     */
+    @Schema(description ="瀹℃壒璁板綍ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 瀹℃壒瀹炰緥ID
+     */
+    @Schema(description ="瀹℃壒瀹炰緥ID")
+    private Long instanceId;
+
+    /**
+     * 鑺傜偣瀹炰緥ID
+     */
+    @Schema(description ="鑺傜偣瀹炰緥ID")
+    private Long nodeId;
+
+    /**
+     * 瀹℃壒浠诲姟ID
+     */
+    @Schema(description ="瀹℃壒浠诲姟ID")
+    private Long taskId;
+
+    /**
+     * 鎿嶄綔浜篒D
+     */
+    @Schema(description ="鎿嶄綔浜篒D")
+    private Long operatorId;
+
+    /**
+     * 鎿嶄綔浜哄悕绉�
+     */
+    @Schema(description ="鎿嶄綔浜哄悕绉�")
+    private String operatorName;
+
+    /**
+     * 鎿嶄綔绫诲瀷
+     */
+    @Schema(description ="鎿嶄綔绫诲瀷")
+    private String action;
+
+    /**
+     * 瀹℃壒鎰忚
+     */
+    @Schema(description ="瀹℃壒鎰忚")
+    private String comment;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description ="鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description ="鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 閫昏緫鍒犻櫎
+     */
+    @Schema(description ="閫昏緫鍒犻櫎")
+    private Byte deleted;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalTask.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalTask.java
new file mode 100644
index 0000000..d78c964
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalTask.java
@@ -0,0 +1,128 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒浠诲姟琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:32:37
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_task")
+@ApiModel(value = "ApprovalTask瀵硅薄", description = "瀹℃壒浠诲姟琛�")
+public class ApprovalTask implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 瀹℃壒浠诲姟ID
+     */
+    @Schema(description ="瀹℃壒浠诲姟ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 瀹℃壒瀹炰緥ID
+     */
+    @Schema(description ="瀹℃壒瀹炰緥ID")
+    private Long instanceId;
+
+    /**
+     * 鑺傜偣瀹炰緥ID
+     */
+    @Schema(description ="鑺傜偣瀹炰緥ID")
+    private Long nodeId;
+
+    /**
+     * 瀹℃壒绾у埆
+     */
+    @Schema(description ="瀹℃壒绾у埆")
+    private Integer levelNo;
+
+    /**
+     * 瀹℃壒浜篒D
+     */
+    @Schema(description ="瀹℃壒浜篒D")
+    private Long approverId;
+
+    /**
+     * 瀹℃壒浜哄悕绉�
+     */
+    @Schema(description ="瀹℃壒浜哄悕绉�")
+    private String approverName;
+
+    /**
+     * 浠诲姟鐘舵��
+     */
+    @Schema(description ="浠诲姟鐘舵�� PENDING - 寰呭鎵� APPROVED - 宸插悓鎰�  REJECTED - 宸叉嫆缁�")
+    private String taskStatus;
+
+    /**
+     * 瀹℃壒鏃堕棿
+     */
+    @Schema(description ="瀹℃壒鏃堕棿")
+    private LocalDateTime approveTime;
+
+    /**
+     * 瀹℃壒鎰忚
+     */
+    @Schema(description ="瀹℃壒鎰忚")
+    private String comment;
+
+    /**
+     * 鏄惁宸茶
+     */
+    @Schema(description ="鏄惁宸茶")
+    private Byte isRead;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description ="鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description ="鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description ="鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description ="鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 閫昏緫鍒犻櫎
+     */
+    @Schema(description ="閫昏緫鍒犻櫎")
+    private Byte deleted;
+
+    @Schema(description ="閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplate.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplate.java
new file mode 100644
index 0000000..272b945
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplate.java
@@ -0,0 +1,107 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:08
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_template")
+@ApiModel(value = "ApprovalTemplate瀵硅薄", description = "瀹℃壒妯℃澘琛�")
+public class ApprovalTemplate implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 妯℃澘ID
+     */
+    @Schema(description ="妯℃澘ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 妯℃澘鍚嶇О
+     */
+    @Schema(description ="妯℃澘鍚嶇О")
+    private String templateName;
+
+    /**
+     * 鍚敤鐘舵�侊細1鍚敤锛�0鍋滅敤
+     */
+    @Schema(description ="鍚敤鐘舵�侊細1鍚敤锛�0鍋滅敤")
+    private Byte enabled;
+
+    /**
+     * 妯℃澘璇存槑
+     */
+    @Schema(description ="妯℃澘璇存槑")
+    private String description;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description ="鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description ="鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description ="鏇存柊浜�")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description ="鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    /**
+     * 閫昏緫鍒犻櫎锛�0鍚︼紝1鏄�
+     */
+    @Schema(description ="閫昏緫鍒犻櫎锛�0鍚︼紝1鏄�")
+    private Integer deleted;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    @Schema(description = "琛ㄥ崟閰嶇疆")
+    private String formConfig;
+
+    @Schema(description = "妯℃澘绫诲瀷锛�0绯荤粺鍐呯疆锛�1鑷畾涔�")
+    private Integer templateType;
+
+    @Schema(description = "涓氬姟绫诲瀷")
+    private Long businessType;
+
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNode.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNode.java
new file mode 100644
index 0000000..cd77198
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNode.java
@@ -0,0 +1,66 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:19
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_template_node")
+@ApiModel(value = "ApprovalTemplateNode瀵硅薄", description = "瀹℃壒妯℃澘鑺傜偣琛�")
+public class ApprovalTemplateNode implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 鑺傜偣ID
+     */
+    @Schema(description ="鑺傜偣ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 瀹℃壒妯℃澘ID
+     */
+    @Schema(description ="瀹℃壒妯℃澘ID")
+    private Long templateId;
+
+    /**
+     * 瀹℃壒绾у埆锛屼粠1寮�濮�
+     */
+    @Schema(description ="瀹℃壒绾у埆锛屼粠1寮�濮�")
+    private Integer levelNo;
+
+    /**
+     * 瀹℃壒鏂瑰紡锛欰ND浼氱锛孫R鎴栫
+     */
+    @Schema(description ="瀹℃壒鏂瑰紡锛欰ND浼氱锛孫R鎴栫")
+    private String approveType;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNodeApprover.java b/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNodeApprover.java
new file mode 100644
index 0000000..4113bff
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNodeApprover.java
@@ -0,0 +1,76 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣瀹℃壒浜鸿〃
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:30
+ */
+@Getter
+@Setter
+@ToString
+@TableName("approval_template_node_approver")
+@ApiModel(value = "ApprovalTemplateNodeApprover瀵硅薄", description = "瀹℃壒妯℃澘鑺傜偣瀹℃壒浜鸿〃")
+public class ApprovalTemplateNodeApprover implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭ID
+     */
+    @Schema(description = "涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 瀹℃壒鑺傜偣ID
+     */
+    @Schema(description ="瀹℃壒鑺傜偣ID")
+    private Long nodeId;
+
+    /**
+     * 瀹℃壒妯℃澘ID
+     */
+    @Schema(description ="瀹℃壒妯℃澘ID")
+    private Long templateId;
+
+    /**
+     * 瀹℃壒浜篒D
+     */
+    @Schema(description ="瀹℃壒浜篒D")
+    private Long approverId;
+
+    /**
+     * 瀹℃壒浜哄悕绉板啑浣�
+     */
+    @Schema(description ="瀹℃壒浜哄悕绉板啑浣�")
+    private String approverName;
+
+    /**
+     * 瀹℃壒浜烘帓搴�
+     */
+    @Schema(description ="瀹℃壒浜烘帓搴�")
+    private Integer sortNo;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+    @TableField(fill = FieldFill.INSERT)
+    private Long deleted ;
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java b/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java
new file mode 100644
index 0000000..7d5ad9c
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java
@@ -0,0 +1,210 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曚富琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:15
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_reimbursement")
+@ApiModel(value = "FinReimbursement瀵硅薄", description = "鎶ラ攢鍗曚富琛�")
+public class FinReimbursement implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭ID
+     */
+    @Schema(description = "涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鎶ラ攢鍗曞彿
+     */
+    @Schema(description = "鎶ラ攢鍗曞彿")
+    private String billNo;
+
+    /**
+     * 鎶ラ攢绫诲瀷锛�1-宸梾鎶ラ攢锛�2-璐圭敤鎶ラ攢
+     */
+    @Schema(description = "鎶ラ攢绫诲瀷锛�1-宸梾鎶ラ攢锛�2-璐圭敤鎶ラ攢")
+    private Byte reimbursementType;
+
+    /**
+     * 璐圭敤绫诲瀷锛氬樊鏃呰垂/鍔炲叕閲囪喘/涓氬姟鎷涘緟/浜ら�氳垂/閫氳璐�/鍏朵粬
+     */
+    @Schema(description = "璐圭敤绫诲瀷锛氬樊鏃呰垂/鍔炲叕閲囪喘/涓氬姟鎷涘緟/浜ら�氳垂/閫氳璐�/鍏朵粬")
+    private String expenseType;
+
+    /**
+     * 鐢宠浜篒D
+     */
+    @Schema(description = "鐢宠浜篒D")
+    private Long applicantId;
+
+    /**
+     * 鍛樺伐缂栧彿
+     */
+    @Schema(description = "鍛樺伐缂栧彿")
+    private String applicantCode;
+
+    /**
+     * 鍛樺伐濮撳悕
+     */
+    @Schema(description = "鍛樺伐濮撳悕")
+    private String applicantName;
+
+    /**
+     * 鐢宠閮ㄩ棬ID
+     */
+    @Schema(description = "鐢宠閮ㄩ棬ID")
+    private Long applicantDeptId;
+
+    /**
+     * 鐢宠閮ㄩ棬鍚嶇О
+     */
+    @Schema(description = "鐢宠閮ㄩ棬鍚嶇О")
+    private String applicantDeptName;
+
+    /**
+     * 鎶ラ攢鍘熷洜
+     */
+    @Schema(description = "鎶ラ攢鍘熷洜")
+    private String reason;
+
+    /**
+     * 鐢宠閲戦
+     */
+    @Schema(description = "鐢宠閲戦")
+    private BigDecimal applyAmount;
+
+    /**
+     * 鏄庣粏姹囨�婚噾棰�
+     */
+    @Schema(description = "鏄庣粏姹囨�婚噾棰�")
+    private BigDecimal detailTotalAmount;
+
+    /**
+     * 鏀舵浜�
+     */
+    @Schema(description = "鏀舵浜�")
+    private String payeeName;
+
+    /**
+     * 鏀舵璐﹀彿
+     */
+    @Schema(description = "鏀舵璐﹀彿")
+    private String payeeAccount;
+
+    /**
+     * 寮�鎴锋敮琛�
+     */
+    @Schema(description = "寮�鎴锋敮琛�")
+    private String payeeBank;
+
+    /**
+     * 瀹℃壒瀹炰緥ID锛屽搴� approval_instance.id
+     */
+    @Schema(description = "瀹℃壒瀹炰緥ID锛屽搴� approval_instance.id")
+    private Long approvalInstanceId;
+
+    /**
+     * 瀹℃壒娴佺▼ID锛屽搴� approve_process.id
+     */
+    @Schema(description = "瀹℃壒娴佺▼ID锛屽搴� approve_process.id")
+    private Long approveProcessId;
+
+    /**
+     * 鍗曟嵁鐘舵�侊細DRAFT-鑽夌锛孖N_APPROVAL-瀹℃壒涓紝APPROVED-瀹℃壒閫氳繃锛孯EJECTED-瀹℃壒椹冲洖锛學ITHDRAWN-宸叉挙鍥烇紝PAID-宸蹭粯娆�
+     */
+    @Schema(description = "鍗曟嵁鐘舵�侊細DRAFT-鑽夌锛孖N_APPROVAL-瀹℃壒涓紝APPROVED-瀹℃壒閫氳繃锛孯EJECTED-瀹℃壒椹冲洖锛學ITHDRAWN-宸叉挙鍥烇紝PAID-宸蹭粯娆�")
+    private String billStatus;
+
+    /**
+     * 瀹℃壒閫氳繃鏃堕棿
+     */
+    @Schema(description = "瀹℃壒閫氳繃鏃堕棿")
+    private LocalDateTime approvedTime;
+
+    /**
+     * 浠樻鏃堕棿
+     */
+    @Schema(description = "浠樻鏃堕棿")
+    private LocalDateTime paidTime;
+
+    /**
+     * 鐢熸垚鐨勮储鍔℃敮鍑鸿褰旾D锛屽搴� account_expense.id
+     */
+    @Schema(description = "鐢熸垚鐨勮储鍔℃敮鍑鸿褰旾D锛屽搴� account_expense.id")
+    private Long accountExpenseId;
+
+    /**
+     * 澶囨敞
+     */
+    @Schema(description = "澶囨敞")
+    private String remark;
+
+    /**
+     * 绉熸埛ID
+     */
+    @Schema(description = "绉熸埛ID")
+    private Long tenantId;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description = "鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description = "鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description = "鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 褰掑睘閮ㄩ棬ID
+     */
+    @Schema(description = "褰掑睘閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /**
+     * 閫昏緫鍒犻櫎锛�0-鍚︼紝1-鏄�
+     */
+    @Schema(description = "閫昏緫鍒犻櫎锛�0-鍚︼紝1-鏄�")
+    private Byte deleted;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/FinReimbursementDetail.java b/src/main/java/com/ruoyi/approve/pojo/FinReimbursementDetail.java
new file mode 100644
index 0000000..5d0220e
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/FinReimbursementDetail.java
@@ -0,0 +1,157 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曟槑缁嗚〃
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:38
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_reimbursement_detail")
+@ApiModel(value = "FinReimbursementDetail瀵硅薄", description = "鎶ラ攢鍗曟槑缁嗚〃")
+public class FinReimbursementDetail implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭ID
+     */
+    @Schema(description = "涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鎶ラ攢鍗旾D锛屽搴� fin_reimbursement.id
+     */
+    @Schema(description = "鎶ラ攢鍗旾D锛屽搴� fin_reimbursement.id")
+    private Long reimbursementId;
+
+    /**
+     * 鏄庣粏琛屽彿
+     */
+    @Schema(description = "鏄庣粏琛屽彿")
+    private Integer rowNo;
+
+    /**
+     * 鍙戠エ鏃ユ湡
+     */
+    @Schema(description = "鍙戠エ鏃ユ湡")
+    private LocalDate invoiceDate;
+
+    /**
+     * 璐圭敤绉戠洰
+     */
+    @Schema(description = "璐圭敤绉戠洰")
+    private String expenseCategory;
+
+    /**
+     * 閲戦
+     */
+    @Schema(description = "閲戦")
+    private BigDecimal amount;
+
+    /**
+     * 鎻忚堪
+     */
+    @Schema(description = "鎻忚堪")
+    private String description;
+
+    /**
+     * 鍙戠エ鍙风爜
+     */
+    @Schema(description = "鍙戠エ鍙风爜")
+    private String invoiceNo;
+
+    /**
+     * 鍙戠エ绫诲瀷
+     */
+    @Schema(description = "鍙戠エ绫诲瀷")
+    private String invoiceType;
+
+    /**
+     * 绁ㄩ潰閲戦
+     */
+    @Schema(description = "绁ㄩ潰閲戦")
+    private BigDecimal invoiceAmount;
+
+    /**
+     * 绋庣巼
+     */
+    @Schema(description = "绋庣巼")
+    private BigDecimal taxRate;
+
+    /**
+     * 绋庨
+     */
+    @Schema(description = "绋庨")
+    private BigDecimal taxAmount;
+
+    /**
+     * 澶囨敞
+     */
+    @Schema(description = "澶囨敞")
+    private String remark;
+
+    /**
+     * 绉熸埛ID
+     */
+    @Schema(description = "绉熸埛ID")
+    private Long tenantId;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description = "鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description = "鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description = "鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 褰掑睘閮ㄩ棬ID
+     */
+    @Schema(description = "褰掑睘閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /**
+     * 閫昏緫鍒犻櫎锛�0-鍚︼紝1-鏄�
+     */
+    @Schema(description = "閫昏緫鍒犻櫎锛�0-鍚︼紝1-鏄�")
+    private Byte deleted;
+}
diff --git a/src/main/java/com/ruoyi/approve/pojo/FinReimbursementTravel.java b/src/main/java/com/ruoyi/approve/pojo/FinReimbursementTravel.java
new file mode 100644
index 0000000..10c607c
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/pojo/FinReimbursementTravel.java
@@ -0,0 +1,162 @@
+package com.ruoyi.approve.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 宸梾鎶ラ攢鎵╁睍琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:47
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_reimbursement_travel")
+@ApiModel(value = "FinReimbursementTravel瀵硅薄", description = "宸梾鎶ラ攢鎵╁睍琛�")
+public class FinReimbursementTravel implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭ID
+     */
+    @Schema(description = "涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鎶ラ攢鍗旾D锛屽搴� fin_reimbursement.id
+     */
+    @Schema(description = "鎶ラ攢鍗旾D锛屽搴� fin_reimbursement.id")
+    private Long reimbursementId;
+
+    /**
+     * 鍑哄樊寮�濮嬫椂闂�
+     */
+    @Schema(description = "鍑哄樊寮�濮嬫椂闂�")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime startTime;
+
+    /**
+     * 鍑哄樊缁撴潫鏃堕棿
+     */
+    @Schema(description = "鍑哄樊缁撴潫鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+
+    /**
+     * 鍑哄樊澶╂暟
+     */
+    @Schema(description = "鍑哄樊澶╂暟")
+    private BigDecimal travelDays;
+
+    /**
+     * 鍑哄樊鍦�/鍑哄彂鍩庡競
+     */
+    @Schema(description = "鍑哄樊鍦�/鍑哄彂鍩庡競")
+    private String departureCity;
+
+    /**
+     * 鐩殑鍦�/鐩殑鍩庡競
+     */
+    @Schema(description = "鐩殑鍦�/鐩殑鍩庡競")
+    private String destinationCity;
+
+    /**
+     * 閰掑簵鏍囧噯
+     */
+    @Schema(description = "閰掑簵鏍囧噯")
+    private BigDecimal hotelStandard;
+
+    /**
+     * 浣忓澶╂暟
+     */
+    @Schema(description = "浣忓澶╂暟")
+    private BigDecimal lodgingDays;
+
+    /**
+     * 鐢熸椿琛ヨ创
+     */
+    @Schema(description = "鐢熸椿琛ヨ创")
+    private BigDecimal mealAllowance;
+
+    /**
+     * 浜ら�氳ˉ璐�
+     */
+    @Schema(description = "浜ら�氳ˉ璐�")
+    private BigDecimal transportAllowance;
+
+    /**
+     * 浣忓闄愰
+     */
+    @Schema(description = "浣忓闄愰")
+    private BigDecimal lodgingLimit;
+
+    /**
+     * 鐗规壒鏍囪鏂囨湰锛屽鍦ㄦ爣鍑嗚寖鍥村唴/瓒呮爣鐗规壒
+     */
+    @Schema(description = "鐗规壒鏍囪鏂囨湰锛屽鍦ㄦ爣鍑嗚寖鍥村唴/瓒呮爣鐗规壒")
+    private String standardTag;
+
+    /**
+     * 鏄惁鍦ㄦ爣鍑嗗唴锛�1-鏄紝0-鍚�
+     */
+    @Schema(description = "鏄惁鍦ㄦ爣鍑嗗唴锛�1-鏄紝0-鍚�")
+    private Byte withinStandard;
+
+    /**
+     * 绉熸埛ID
+     */
+    @Schema(description = "绉熸埛ID")
+    private Long tenantId;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @Schema(description = "鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊浜�
+     */
+    @Schema(description = "鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @Schema(description = "鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 褰掑睘閮ㄩ棬ID
+     */
+    @Schema(description = "褰掑睘閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalInstanceNodeService.java b/src/main/java/com/ruoyi/approve/service/ApprovalInstanceNodeService.java
new file mode 100644
index 0000000..8fa7c60
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalInstanceNodeService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.pojo.ApprovalInstanceNode;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 瀹℃壒鑺傜偣瀹炰緥琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:54
+ */
+public interface ApprovalInstanceNodeService extends IService<ApprovalInstanceNode> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalInstanceService.java b/src/main/java/com/ruoyi/approve/service/ApprovalInstanceService.java
new file mode 100644
index 0000000..dfb9517
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalInstanceService.java
@@ -0,0 +1,31 @@
+package com.ruoyi.approve.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.bean.vo.ApprovalInstanceVo;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.framework.web.domain.R;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹℃壒瀹炰緥琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:46
+ */
+public interface ApprovalInstanceService extends IService<ApprovalInstance> {
+
+    R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto);
+
+    Boolean add(ApprovalInstanceDto approvalInstanceDto);
+
+    Boolean update(ApprovalInstanceDto approvalInstanceDto);
+
+    Boolean delete(List<Long> ids);
+
+    R approve(ApprovalInstanceDto approvalInstanceDto);
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalRecordService.java b/src/main/java/com/ruoyi/approve/service/ApprovalRecordService.java
new file mode 100644
index 0000000..041571c
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalRecordService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.pojo.ApprovalRecord;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 瀹℃壒璁板綍琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:28:21
+ */
+public interface ApprovalRecordService extends IService<ApprovalRecord> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalTaskService.java b/src/main/java/com/ruoyi/approve/service/ApprovalTaskService.java
new file mode 100644
index 0000000..491e57d
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalTaskService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.pojo.ApprovalTask;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 瀹℃壒浠诲姟琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:32:37
+ */
+public interface ApprovalTaskService extends IService<ApprovalTask> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeApproverService.java b/src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeApproverService.java
new file mode 100644
index 0000000..e335276
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeApproverService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣瀹℃壒浜鸿〃 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:30
+ */
+public interface ApprovalTemplateNodeApproverService extends IService<ApprovalTemplateNodeApprover> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeService.java b/src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeService.java
new file mode 100644
index 0000000..4a8d6a8
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeService.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto;
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:19
+ */
+public interface ApprovalTemplateNodeService extends IService<ApprovalTemplateNode> {
+
+    Boolean saveApprovalTemplateNode(Long id, List<ApprovalTemplateNodeDto> nodes);
+}
diff --git a/src/main/java/com/ruoyi/approve/service/ApprovalTemplateService.java b/src/main/java/com/ruoyi/approve/service/ApprovalTemplateService.java
new file mode 100644
index 0000000..38bbabc
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/ApprovalTemplateService.java
@@ -0,0 +1,34 @@
+package com.ruoyi.approve.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateDto;
+import com.ruoyi.approve.bean.vo.ApprovalTemplateVo;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:08
+ */
+public interface ApprovalTemplateService extends IService<ApprovalTemplate> {
+
+    IPage<ApprovalTemplateVo> listPage(Page<ApprovalTemplateVo> page, ApprovalTemplateDto approvalTemplateDto);
+
+    Boolean saveApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto);
+
+    Boolean updateApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto);
+
+    Boolean delete(List<Long> ids);
+
+    List<ApprovalTemplateVo> listApprovalTemplateVo(Integer type);
+
+    ApprovalTemplateVo getApprovalTemplateVoById(Long id);
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/FinReimbursementDetailService.java b/src/main/java/com/ruoyi/approve/service/FinReimbursementDetailService.java
new file mode 100644
index 0000000..0279c25
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/FinReimbursementDetailService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.pojo.FinReimbursementDetail;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曟槑缁嗚〃 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:38
+ */
+public interface FinReimbursementDetailService extends IService<FinReimbursementDetail> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java b/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java
new file mode 100644
index 0000000..c14b943
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java
@@ -0,0 +1,31 @@
+package com.ruoyi.approve.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.approve.bean.dto.FinReimbursementDto;
+import com.ruoyi.approve.bean.vo.FinReimbursementVo;
+import com.ruoyi.approve.pojo.FinReimbursement;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曚富琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:15
+ */
+public interface FinReimbursementService extends IService<FinReimbursement> {
+
+    IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page);
+
+    Boolean add(FinReimbursementDto finReimbursementDto);
+
+    FinReimbursementVo detail(Long id);
+
+    Boolean update(FinReimbursementDto finReimbursementDto);
+
+    Boolean delete(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/approve/service/FinReimbursementTravelService.java b/src/main/java/com/ruoyi/approve/service/FinReimbursementTravelService.java
new file mode 100644
index 0000000..0733d1b
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/FinReimbursementTravelService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.approve.service;
+
+import com.ruoyi.approve.pojo.FinReimbursementTravel;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 宸梾鎶ラ攢鎵╁睍琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:47
+ */
+public interface FinReimbursementTravelService extends IService<FinReimbursementTravel> {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceNodeServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceNodeServiceImpl.java
new file mode 100644
index 0000000..9ea1963
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceNodeServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service.impl;
+
+import com.ruoyi.approve.pojo.ApprovalInstanceNode;
+import com.ruoyi.approve.mapper.ApprovalInstanceNodeMapper;
+import com.ruoyi.approve.service.ApprovalInstanceNodeService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 瀹℃壒鑺傜偣瀹炰緥琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:27:54
+ */
+@Service
+public class ApprovalInstanceNodeServiceImpl extends ServiceImpl<ApprovalInstanceNodeMapper, ApprovalInstanceNode> implements ApprovalInstanceNodeService {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
new file mode 100644
index 0000000..1fb2333
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -0,0 +1,753 @@
+package com.ruoyi.approve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.bean.vo.ApprovalInstanceVo;
+import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
+import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper;
+import com.ruoyi.approve.mapper.FinReimbursementMapper;
+import com.ruoyi.approve.pojo.*;
+import com.ruoyi.approve.service.*;
+import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
+import com.ruoyi.common.enums.*;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.procurementrecord.utils.StockUtils;
+import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
+import com.ruoyi.purchase.pojo.PurchaseLedger;
+import com.ruoyi.quality.utils.QualityInspectHelper;
+import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.mapper.SalesQuotationMapper;
+import com.ruoyi.sales.mapper.ShippingInfoMapper;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.pojo.SalesQuotation;
+import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.staff.mapper.HolidayApplicationMapper;
+import com.ruoyi.staff.pojo.HolidayApplication;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 瀹℃壒瀹炰緥鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @since 2026-05-18 03:27:46
+ */
+@Service
+@RequiredArgsConstructor
+public class ApprovalInstanceServiceImpl extends ServiceImpl<ApprovalInstanceMapper, ApprovalInstance> implements ApprovalInstanceService {
+
+    private static final String ENTERPRISE_NEWS_STATUS_PUBLISHED = "PUBLISHED";
+    private static final String ENTERPRISE_NEWS_STATUS_REJECTED = "REJECTED";
+
+    private final ApprovalInstanceMapper approvalInstanceMapper;
+    private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils;
+    private final ApprovalInstanceNodeService approvalInstanceNodeService;
+    private final ApprovalTaskService approvalTaskService;
+    private final ApprovalRecordService approvalRecordService;
+    private final ApprovalTemplateNodeService approvalTemplateNodeService;
+    private final FinReimbursementMapper finReimbursementMapper;
+    private final FileUtil fileUtil;
+    private final ISysNoticeService sysNoticeService;
+    private final PurchaseLedgerMapper purchaseLedgerMapper;
+    private final SalesLedgerProductMapper salesLedgerProductMapper;
+    private final StockUtils stockUtils;
+    private final SalesQuotationMapper salesQuotationMapper;
+    private final ShippingInfoMapper shippingInfoMapper;
+    private final QualityInspectHelper qualityInspectHelper;
+    private final EnterpriseNewsScopeUserMapper enterpriseNewsScopeUserMapper;
+    private final SysUserMapper sysUserMapper;
+    private final SysUserDeptMapper sysUserDeptMapper;
+    private final SysDeptMapper sysDeptMapper;
+    private final HolidayApplicationMapper holidayApplicationMapper;
+    private final EnterpriseNewsMapper enterpriseNewsMapper;
+    private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
+    private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
+
+    @Override
+    public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
+        IPage<ApprovalInstanceVo> approvalInstanceVoIPage = approvalInstanceMapper.listPage(page, approvalInstanceDto);
+
+        List<ApprovalInstanceVo> records = approvalInstanceVoIPage.getRecords();
+        if (records == null || records.isEmpty()) {
+            return R.ok(approvalInstanceVoIPage);
+        }
+        records.forEach(vo -> {
+            vo.setBusinessName(TypeEnums.getLabelByValue(vo.getBusinessType()));
+        });
+        Long currentUserId = SecurityUtils.getUserId();
+
+        List<Long> instanceIds = records.stream()
+                .map(ApprovalInstanceVo::getId)
+                .filter(id -> id != null)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (!instanceIds.isEmpty()) {
+            Map<Long, List<ApprovalRecord>> recordMap = approvalRecordService.list(
+                    Wrappers.<ApprovalRecord>lambdaQuery()
+                            .in(ApprovalRecord::getInstanceId, instanceIds)
+                            .eq(ApprovalRecord::getDeleted, 0)
+            ).stream().collect(Collectors.groupingBy(ApprovalRecord::getInstanceId));
+            Map<Long, List<ApprovalTask>> taskMap = approvalTaskService.list(
+                    Wrappers.<ApprovalTask>lambdaQuery()
+                            .in(ApprovalTask::getInstanceId, instanceIds)
+                            .eq(ApprovalTask::getDeleted, 0)
+            ).stream().collect(Collectors.groupingBy(ApprovalTask::getInstanceId));
+
+            for (ApprovalInstanceVo vo : records) {
+                vo.setIsApprove(approveProcessConfigNodeUtils.isCurrentApprover(vo.getId(), currentUserId));
+                vo.setRecords(recordMap.getOrDefault(vo.getId(), new ArrayList<>()));
+                vo.setTasks(taskMap.getOrDefault(vo.getId(), new ArrayList<>()));
+                vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.APPROVAL_INSTANCE, vo.getId()));
+            }
+
+        }
+        return R.ok(approvalInstanceVoIPage);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(ApprovalInstanceDto approvalInstanceDto) {
+        String instanceNo = OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no");
+        approvalInstanceDto.setInstanceNo(instanceNo);
+        approvalInstanceDto.setStatus("PENDING");
+        approvalInstanceDto.setCurrentLevel(1);
+        boolean saved = this.save(approvalInstanceDto);
+        if (!saved) {
+            return false;
+        }
+        approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstanceDto);
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
+        sendApproveNotice(approvalInstanceDto, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(ApprovalInstanceDto approvalInstanceDto) {
+        if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
+            return false;
+        }
+        // 鍒ゆ柇鏄惁鏈夋鍦ㄨ繘琛岀殑瀹℃壒浠诲姟锛屾湁鍒欎笉鍏佽淇敼
+        long pendingTaskCount = approvalTaskService.count(
+                Wrappers.<ApprovalTask>lambdaQuery()
+                        .eq(ApprovalTask::getInstanceId, approvalInstanceDto.getId())
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+        );
+        if (pendingTaskCount > 0) {
+            throw new ServiceException("璇ュ鎵瑰崟鏈夋鍦ㄨ繘琛岀殑瀹℃壒浠诲姟锛屼笉鍏佽淇敼");
+        }
+        boolean updated = this.updateById(approvalInstanceDto);
+        if (!updated) {
+            return false;
+        }
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return false;
+        }
+        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, ids);
+
+        int instanceRows = approvalInstanceMapper.update(
+                null,
+                Wrappers.<ApprovalInstance>lambdaUpdate()
+                        .in(ApprovalInstance::getId, ids)
+                        .eq(ApprovalInstance::getDeleted, 0)
+                        .set(ApprovalInstance::getDeleted, (byte) 1)
+        );
+
+        LambdaUpdateWrapper<ApprovalInstanceNode> nodeUpdateWrapper = Wrappers.lambdaUpdate();
+        nodeUpdateWrapper.in(ApprovalInstanceNode::getInstanceId, ids)
+                .eq(ApprovalInstanceNode::getDeleted, 0)
+                .set(ApprovalInstanceNode::getDeleted, (byte) 1);
+        approvalInstanceNodeService.update(nodeUpdateWrapper);
+
+        LambdaUpdateWrapper<ApprovalTask> taskUpdateWrapper = Wrappers.lambdaUpdate();
+        taskUpdateWrapper.in(ApprovalTask::getInstanceId, ids)
+                .eq(ApprovalTask::getDeleted, 0)
+                .set(ApprovalTask::getDeleted, (byte) 1);
+        approvalTaskService.update(taskUpdateWrapper);
+
+        LambdaUpdateWrapper<ApprovalRecord> recordUpdateWrapper = Wrappers.lambdaUpdate();
+        recordUpdateWrapper.in(ApprovalRecord::getInstanceId, ids)
+                .eq(ApprovalRecord::getDeleted, 0)
+                .set(ApprovalRecord::getDeleted, (byte) 1);
+        approvalRecordService.update(recordUpdateWrapper);
+
+        return instanceRows > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R approve(ApprovalInstanceDto approvalInstanceDto) {
+        if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
+            return R.fail("瀹℃壒瀹炰緥 ID 涓嶈兘涓虹┖");
+        }
+        String approveAction = normalizeApproveAction(approvalInstanceDto.getApproveAction());
+        if (approveAction == null) {
+            return R.fail("瀹℃壒鍔ㄤ綔鍙敮鎸� APPROVED 鎴� REJECTED");
+        }
+
+        ApprovalInstance instance = getPendingApprovalInstance(approvalInstanceDto.getId());
+        if (instance == null) {
+            return R.fail("瀹℃壒瀹炰緥涓嶅瓨鍦�");
+        }
+
+        ApprovalInstanceNode currentNode = approveProcessConfigNodeUtils.getCurrentNode(instance.getId());
+        if (currentNode == null) {
+            return R.fail("褰撳墠娌℃湁寰呭鐞嗙殑瀹℃壒鑺傜偣");
+        }
+
+        Long currentUserId = SecurityUtils.getUserId();
+        ApprovalTask currentTask = approveProcessConfigNodeUtils.getCurrentUserTask(instance.getId(), currentUserId);
+        if (currentTask == null) {
+            return R.fail("褰撳墠鐢ㄦ埛娌℃湁鍙鎵逛换鍔�");
+        }
+
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        String operatorName = loginUser.getUser() != null ? loginUser.getUser().getNickName() : SecurityUtils.getUsername();
+        LocalDateTime now = LocalDateTime.now();
+
+        if (!updateCurrentTask(approvalInstanceDto, approveAction, currentTask, now)) {
+            return R.fail("褰撳墠浠诲姟宸茶澶勭悊锛岃鍒锋柊鍚庨噸璇�");
+        }
+
+        saveApprovalRecord(
+                instance.getId(),
+                currentNode.getId(),
+                currentTask.getId(),
+                currentUserId,
+                operatorName,
+                approveAction,
+                approvalInstanceDto.getApproveComment()
+        );
+        //瀹℃壒鎷掔粷鐨勫鐞�
+        if ("REJECTED".equals(approveAction)) {
+            return rejectCurrentNode(instance, currentNode, now);
+        }
+        if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) {
+            return R.ok("瀹℃壒鎴愬姛锛岀瓑寰呭叾浠栧鎵逛汉澶勭悊");
+        }
+
+        return approveAndMoveNext(instance, currentNode, approvalInstanceDto, now);
+    }
+
+    private String normalizeApproveAction(String approveAction) {
+        if (!StringUtils.hasText(approveAction)) {
+            return null;
+        }
+        String normalizedAction = approveAction.trim().toUpperCase(Locale.ROOT);
+        return "APPROVED".equals(normalizedAction) || "REJECTED".equals(normalizedAction)
+                ? normalizedAction
+                : null;
+    }
+
+    private ApprovalInstance getPendingApprovalInstance(Long instanceId) {
+        return this.getOne(
+                new LambdaQueryWrapper<ApprovalInstance>()
+                        .eq(ApprovalInstance::getId, instanceId)
+                        .eq(ApprovalInstance::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+    }
+
+    private boolean updateCurrentTask(ApprovalInstanceDto approvalInstanceDto,
+                                      String approveAction,
+                                      ApprovalTask currentTask,
+                                      LocalDateTime now) {
+        // 浠呭厑璁稿緟瀹℃壒浠诲姟琚垚鍔熷鐞嗕竴娆★紝閬垮厤骞跺彂涓嬮噸澶嶅鎵规垚鍔熴��
+        return approvalTaskService.update(
+                Wrappers.<ApprovalTask>lambdaUpdate()
+                        .eq(ApprovalTask::getId, currentTask.getId())
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .set(ApprovalTask::getTaskStatus, approveAction)
+                        .set(ApprovalTask::getComment, approvalInstanceDto.getApproveComment())
+                        .set(ApprovalTask::getApproveTime, now)
+                        .set(ApprovalTask::getIsRead, (byte) 1)
+        );
+    }
+
+    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) {
+        if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) {
+            return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
+        }
+
+        closePendingTasks(instance.getId(), currentNode.getId());
+        instance.setStatus("REJECTED");
+        instance.setFinishTime(now);
+        this.updateById(instance);
+        // 椹冲洖瀵瑰簲鐨勪紒涓氭柊闂伙紝 宸梾鎶ラ攢
+        if (instance.getBusinessType().equals(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode())) {
+            enterpriseNewsMapper.update(
+                    new LambdaUpdateWrapper<EnterpriseNews>()
+                            .eq(EnterpriseNews::getId, instance.getBusinessId())
+                            .set(EnterpriseNews::getStatus, "REJECTED")
+            );
+        }else if (instance.getBusinessType().equals(TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode())||instance.getBusinessType().equals(TypeEnums.EXPENSE_APPROVAL.getCode())) {
+            finReimbursementMapper.update(
+                    new LambdaUpdateWrapper<FinReimbursement>()
+                            .eq(FinReimbursement::getId, instance.getBusinessId())
+                            .set(FinReimbursement::getBillStatus, "REJECTED")
+            );
+        }
+        return R.ok("瀹℃壒宸查┏鍥�");
+    }
+
+    private R approveAndMoveNext(ApprovalInstance instance,
+                                 ApprovalInstanceNode currentNode,
+                                 ApprovalInstanceDto approvalInstanceDto,
+                                 LocalDateTime now) {
+        if (!updateCurrentNodeStatus(currentNode.getId(), "APPROVED", now)) {
+            return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
+        }
+
+        closePendingTasks(instance.getId(), currentNode.getId());
+
+        int nextLevel = currentNode.getLevelNo() + 1;
+        ApprovalInstanceNode nextInstanceNode = approvalInstanceNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalInstanceNode>()
+                        .eq(ApprovalInstanceNode::getInstanceId, instance.getId())
+                        .eq(ApprovalInstanceNode::getLevelNo, nextLevel)
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .orderByAsc(ApprovalInstanceNode::getId)
+                        .last("LIMIT 1")
+        );
+
+        if (nextInstanceNode != null) {
+            if (!activateNextInstanceNode(nextInstanceNode.getId(), now)) {
+                return R.ok("涓嬩竴瀹℃壒鑺傜偣宸茶婵�娲伙紝璇峰埛鏂板悗閲嶈瘯");
+            }
+            instance.setCurrentLevel(nextLevel);
+            instance.setStatus("PENDING");
+            this.updateById(instance);
+            List<ApprovalTask> nextTasks = approvalTaskService.list(
+                    Wrappers.<ApprovalTask>lambdaQuery()
+                            .eq(ApprovalTask::getInstanceId, instance.getId())
+                            .eq(ApprovalTask::getNodeId, nextInstanceNode.getId())
+                            .eq(ApprovalTask::getTaskStatus, "PENDING")
+                            .eq(ApprovalTask::getDeleted, 0)
+            );
+            sendApproveNotice(instance, nextTasks);
+            return R.ok("瀹℃壒鎴愬姛锛屽凡娴佽浆鍒颁笅涓�鑺傜偣");
+        }
+
+        ApprovalTemplateNode nextTemplateNode = approvalTemplateNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNode::getLevelNo, nextLevel)
+                        .orderByAsc(ApprovalTemplateNode::getId)
+                        .last("LIMIT 1")
+        );
+
+        if (nextTemplateNode == null) {
+            instance.setStatus("APPROVED");
+            instance.setFinishTime(now);
+            this.updateById(instance);
+            handleBusinessAfterApprovalFinished(instance);
+            return R.ok("瀹℃壒宸插畬鎴�");
+        }
+
+        instance.setCurrentLevel(nextLevel);
+        instance.setStatus("PENDING");
+        this.updateById(instance);
+        approveProcessConfigNodeUtils.createCurrentNodeAndTasks(instance, false);
+        sendApproveNotice(instance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
+        return R.ok("瀹℃壒鎴愬姛锛屽凡娴佽浆鍒颁笅涓�鑺傜偣");
+    }
+
+    private boolean activateNextInstanceNode(Long nodeId, LocalDateTime now) {
+        return approvalInstanceNodeService.update(
+                Wrappers.<ApprovalInstanceNode>lambdaUpdate()
+                        .eq(ApprovalInstanceNode::getId, nodeId)
+                        .eq(ApprovalInstanceNode::getStatus, "WAITING")
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .set(ApprovalInstanceNode::getStatus, "PENDING")
+                        .set(ApprovalInstanceNode::getStartTime, now)
+        );
+    }
+
+    private boolean updateCurrentNodeStatus(Long nodeId, String targetStatus, LocalDateTime now) {
+        // 浠呭厑璁镐竴涓姹傚皢褰撳墠鑺傜偣浠庡緟澶勭悊鎺ㄨ繘鍒扮洰鏍囩姸鎬侊紝閬垮厤閲嶅娴佽浆銆�
+        return approvalInstanceNodeService.update(
+                Wrappers.<ApprovalInstanceNode>lambdaUpdate()
+                        .eq(ApprovalInstanceNode::getId, nodeId)
+                        .eq(ApprovalInstanceNode::getStatus, "PENDING")
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .set(ApprovalInstanceNode::getStatus, targetStatus)
+                        .set(ApprovalInstanceNode::getFinishTime, now)
+        );
+    }
+
+    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) {
+        String status = instance.getStatus();
+        Long businessType = instance.getBusinessType();
+        if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) {
+            handlePurchaseApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.QUOTATION_APPROVAL.getCode().equals(businessType)) {
+            handleSalesQuotationApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.SHIPPING_APPROVAL.getCode().equals(businessType)) {
+            handleShippingApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType)
+                || TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) {
+            handleReimbursementApprovalFinished(instance, status);
+            return;
+        }
+        // 椹冲洖瀵瑰簲鐨勪紒涓氭柊闂汇�佸姞鐝敵璇枫�佽鍋囩敵璇峰彲浠ラ噸鏂板啀鎻愪氦
+        if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType)
+                || TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) {
+            handleHolidayApplicationApprovalFinished(instance, status);
+            return;
+        }
+        if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) {
+            handleNewsApprovalFinished(instance, status);
+        }
+    }
+
+    private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+
+        FinReimbursement reimbursement = new FinReimbursement();
+        reimbursement.setId(instance.getBusinessId());
+        if ("APPROVED".equals(status)) {
+            reimbursement.setBillStatus("APPROVED");
+            reimbursement.setApprovedTime(instance.getFinishTime());
+        } else if ("REJECTED".equals(status)) {
+            reimbursement.setBillStatus("REJECTED");
+        } else if ("PENDING".equals(status)) {
+            reimbursement.setBillStatus("IN_APPROVAL");
+        } else {
+            return;
+        }
+        finReimbursementMapper.updateById(reimbursement);
+    }
+
+    private void handleNewsApprovalFinished(ApprovalInstance instance, String status) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+        EnterpriseNews enterpriseNews = new EnterpriseNews();
+        enterpriseNews.setId(instance.getBusinessId());
+        if ("APPROVED".equals(status)) {
+            enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_PUBLISHED);
+            enterpriseNewsMapper.updateById(enterpriseNews);
+            sendEnterpriseNewsNotice(instance.getBusinessId());
+            return;
+        }
+        if ("REJECTED".equals(status)) {
+            enterpriseNews.setStatus(ENTERPRISE_NEWS_STATUS_REJECTED);
+            enterpriseNewsMapper.updateById(enterpriseNews);
+        }
+    }
+
+    private void handleHolidayApplicationApprovalFinished(ApprovalInstance instance, String status) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+        HolidayApplication holidayApplication = new HolidayApplication();
+        holidayApplication.setId(instance.getBusinessId());
+        if ("APPROVED".equals(status)) {
+            holidayApplication.setStatus("APPROVED");
+        } else if ("REJECTED".equals(status)) {
+            holidayApplication.setStatus("REJECTED");
+        } else if ("PENDING".equals(status)) {
+            holidayApplication.setStatus("PENDING");
+        } else {
+            return;
+        }
+        holidayApplicationMapper.updateById(holidayApplication);
+    }
+
+    private void handlePurchaseApprovalFinished(ApprovalInstance instance, String status) {
+        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(
+                new LambdaQueryWrapper<PurchaseLedger>()
+                        .eq(PurchaseLedger::getPurchaseContractNumber, instance.getTitle())
+                        .last("limit 1")
+        );
+        if (purchaseLedger == null) {
+            return;
+        }
+
+        if ("APPROVED".equals(status)) {
+            purchaseLedger.setApprovalStatus(ApprovalStatusEnum.APPROVED.getCode());
+            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(
+                    new QueryWrapper<SalesLedgerProduct>().lambda()
+                            .eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
+                            .eq(SalesLedgerProduct::getType, 2)
+            );
+            for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
+                if (salesLedgerProduct.getIsChecked()) {
+                    qualityInspectHelper.addQualityInspect(purchaseLedger, salesLedgerProduct);
+                } else {
+                    stockUtils.addStockWithBatchNo(
+                            salesLedgerProduct.getProductModelId(),
+                            salesLedgerProduct.getQuantity(),
+                            StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(),
+                            purchaseLedger.getId(),
+                            purchaseLedger.getPurchaseContractNumber() + "-" + salesLedgerProduct.getId()
+                    );
+                }
+            }
+        } else if ("REJECTED".equals(status)) {
+            purchaseLedger.setApprovalStatus(ApprovalStatusEnum.REJECTED.getCode());
+        } else if ("PENDING".equals(status)) {
+            purchaseLedger.setApprovalStatus(ApprovalStatusEnum.IN_PROGRESS.getCode());
+        }
+        purchaseLedgerMapper.updateById(purchaseLedger);
+    }
+
+    private void handleSalesQuotationApprovalFinished(ApprovalInstance instance, String status) {
+        SalesQuotation salesQuote = salesQuotationMapper.selectOne(
+                new LambdaQueryWrapper<SalesQuotation>()
+                        .eq(SalesQuotation::getQuotationNo, instance.getTitle())
+                        .last("limit 1")
+        );
+        if (salesQuote == null) {
+            return;
+        }
+
+        if ("APPROVED".equals(status)) {
+            salesQuote.setStatus(SalesQuotationStatusEnum.APPROVED.getCode());
+        } else if ("REJECTED".equals(status)) {
+            salesQuote.setStatus(SalesQuotationStatusEnum.REJECTED.getCode());
+        } else if ("PENDING".equals(status)) {
+            salesQuote.setStatus(SalesQuotationStatusEnum.IN_PROGRESS.getCode());
+        }
+        salesQuotationMapper.updateById(salesQuote);
+    }
+
+    private void handleShippingApprovalFinished(ApprovalInstance instance, String status) {
+        ShippingInfo shippingInfo = shippingInfoMapper.selectOne(
+                new LambdaQueryWrapper<ShippingInfo>()
+                        .eq(ShippingInfo::getShippingNo, instance.getTitle())
+                        .orderByDesc(ShippingInfo::getCreateTime)
+                        .last("limit 1")
+        );
+        if (shippingInfo == null) {
+            return;
+        }
+
+        if ("APPROVED".equals(status)) {
+            shippingInfo.setStatus(ShippingStatusEnum.APPROVED.getCode());
+            shippingInfo.setShippingDate(new Date());
+            stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId());
+        } else if ("REJECTED".equals(status)) {
+            stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
+            shippingInfo.setStatus(ShippingStatusEnum.REJECTED.getCode());
+        } else if ("PENDING".equals(status)) {
+            shippingInfo.setStatus(ShippingStatusEnum.IN_PROGRESS.getCode());
+        }
+        shippingInfoMapper.updateById(shippingInfo);
+    }
+
+    private List<ApprovalTask> createNodeAndTasks(ApprovalInstance instance, ApprovalTemplateNode templateNode) {
+        List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverMapper.selectList(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId())
+                        .eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
+                        .orderByAsc(ApprovalTemplateNodeApprover::getSortNo)
+        );
+        if (approvers == null || approvers.isEmpty()) {
+            throw new RuntimeException("涓嬩竴瀹℃壒鑺傜偣鏈厤缃鎵逛汉");
+        }
+
+        ApprovalInstanceNode instanceNode = new ApprovalInstanceNode();
+        instanceNode.setInstanceId(instance.getId());
+        instanceNode.setLevelNo(templateNode.getLevelNo());
+        instanceNode.setApproveType(templateNode.getApproveType());
+        instanceNode.setStatus("PENDING");
+        instanceNode.setStartTime(LocalDateTime.now());
+        instanceNode.setDeleted((byte) 0);
+        approvalInstanceNodeService.save(instanceNode);
+
+        List<ApprovalTask> taskList = new ArrayList<>(approvers.size());
+        for (ApprovalTemplateNodeApprover approver : approvers) {
+            ApprovalTask task = new ApprovalTask();
+            task.setInstanceId(instance.getId());
+            task.setNodeId(instanceNode.getId());
+            task.setLevelNo(instanceNode.getLevelNo());
+            task.setApproverId(approver.getApproverId());
+            task.setApproverName(approver.getApproverName());
+            task.setTaskStatus("PENDING");
+            task.setIsRead((byte) 0);
+            task.setDeleted((byte) 0);
+            taskList.add(task);
+        }
+        approvalTaskService.saveBatch(taskList);
+        return taskList;
+    }
+
+    private void sendApproveNotice(ApprovalInstance instance, List<ApprovalTask> tasks) {
+        if (instance == null || tasks == null || tasks.isEmpty()) {
+            return;
+        }
+
+        List<Long> approverIds = tasks.stream()
+                .map(ApprovalTask::getApproverId)
+                .filter(id -> id != null && id > 0)
+                .distinct()
+                .collect(Collectors.toList());
+        if (approverIds.isEmpty()) {
+            return;
+        }
+
+        String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "瀹℃壒鎻愰啋";
+        String message = "瀹℃壒鍗曞彿 " + instance.getInstanceNo() + " 闇�瑕佹偍瀹℃壒";
+        String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId();
+        sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
+    }
+
+    private void sendEnterpriseNewsNotice(Long newsId) {
+        EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId);
+        if (enterpriseNews == null) {
+            return;
+        }
+        List<Long> userIds = getEnterpriseNewsNoticeUserIds(enterpriseNews);
+        if (userIds == null || userIds.isEmpty()) {
+            return;
+        }
+        String title = "浼佷笟鏂伴椈";
+        String message = "鎮ㄦ湁鏂扮殑浼佷笟鏂伴椈銆�" + enterpriseNews.getTitle() + "銆嬭鍙婃椂鏌ラ槄";
+        String jumpPath = "/officeProcessAutomation/EnterpriseNews?id=" + newsId;
+        sysNoticeService.simpleNoticeByUser(title, message, userIds, jumpPath);
+    }
+
+    private List<Long> getEnterpriseNewsNoticeUserIds(EnterpriseNews enterpriseNews) {
+        if (enterpriseNews == null || !org.springframework.util.StringUtils.hasText(enterpriseNews.getReadScope())) {
+            return Collections.emptyList();
+        }
+        String readScope = enterpriseNews.getReadScope().trim();
+        if ("all".equals(readScope)) {
+            return sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>()
+                            .select(SysUser::getUserId)
+                            .eq(SysUser::getDelFlag, "0"))
+                    .stream()
+                    .map(SysUser::getUserId)
+                    .filter(id -> id != null && id > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+        }
+        if ("dept".equals(readScope)) {
+            List<Long> deptIds = enterpriseNewsScopeDeptMapper.selectList(
+                            new LambdaQueryWrapper<EnterpriseNewsScopeDept>()
+                                    .eq(EnterpriseNewsScopeDept::getNewsId, enterpriseNews.getId()))
+                    .stream()
+                    .map(EnterpriseNewsScopeDept::getDeptId)
+                    .filter(id -> id != null && id > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+            if (deptIds.isEmpty()) {
+                return Collections.emptyList();
+            }
+            return sysUserDeptMapper.selectDistinctUserIdsByDeptIds(collectDeptIdsWithChildren(deptIds));
+        }
+        if ("custom".equals(readScope)) {
+            return enterpriseNewsScopeUserMapper.selectList(
+                            new LambdaQueryWrapper<EnterpriseNewsScopeUser>()
+                                    .eq(EnterpriseNewsScopeUser::getNewsId, enterpriseNews.getId()))
+                    .stream()
+                    .map(EnterpriseNewsScopeUser::getUserId)
+                    .filter(id -> id != null && id > 0)
+                    .distinct()
+                    .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    private List<Long> collectDeptIdsWithChildren(List<Long> deptIds) {
+        Set<Long> allDeptIds = new LinkedHashSet<>();
+        for (Long deptId : deptIds) {
+            if (deptId == null) {
+                continue;
+            }
+            allDeptIds.add(deptId);
+            List<SysDept> children = sysDeptMapper.selectChildrenDeptById(deptId);
+            if (children != null && !children.isEmpty()) {
+                for (SysDept child : children) {
+                    if (child != null && child.getDeptId() != null) {
+                        allDeptIds.add(child.getDeptId());
+                    }
+                }
+            }
+        }
+        return new ArrayList<>(allDeptIds);
+    }
+
+    private void closePendingTasks(Long instanceId, Long nodeId) {
+        LambdaUpdateWrapper<ApprovalTask> updateWrapper = Wrappers.lambdaUpdate();
+        updateWrapper.eq(ApprovalTask::getInstanceId, instanceId)
+                .eq(ApprovalTask::getNodeId, nodeId)
+                .eq(ApprovalTask::getTaskStatus, "PENDING")
+                .eq(ApprovalTask::getDeleted, 0)
+                .set(ApprovalTask::getDeleted, (byte) 1);
+        approvalTaskService.update(updateWrapper);
+    }
+
+    private void saveApprovalRecord(Long instanceId,
+                                    Long nodeId,
+                                    Long taskId,
+                                    Long operatorId,
+                                    String operatorName,
+                                    String action,
+                                    String comment) {
+        ApprovalRecord record = new ApprovalRecord();
+        record.setInstanceId(instanceId);
+        record.setNodeId(nodeId);
+        record.setTaskId(taskId);
+        record.setOperatorId(operatorId);
+        record.setOperatorName(operatorName);
+        record.setAction(action);
+        record.setComment(comment);
+        record.setDeleted((byte) 0);
+        approvalRecordService.save(record);
+    }
+
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalRecordServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalRecordServiceImpl.java
new file mode 100644
index 0000000..50911e4
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalRecordServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service.impl;
+
+import com.ruoyi.approve.pojo.ApprovalRecord;
+import com.ruoyi.approve.mapper.ApprovalRecordMapper;
+import com.ruoyi.approve.service.ApprovalRecordService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 瀹℃壒璁板綍琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:28:21
+ */
+@Service
+public class ApprovalRecordServiceImpl extends ServiceImpl<ApprovalRecordMapper, ApprovalRecord> implements ApprovalRecordService {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalTaskServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTaskServiceImpl.java
new file mode 100644
index 0000000..14e10ca
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTaskServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service.impl;
+
+import com.ruoyi.approve.pojo.ApprovalTask;
+import com.ruoyi.approve.mapper.ApprovalTaskMapper;
+import com.ruoyi.approve.service.ApprovalTaskService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 瀹℃壒浠诲姟琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 03:32:37
+ */
+@Service
+public class ApprovalTaskServiceImpl extends ServiceImpl<ApprovalTaskMapper, ApprovalTask> implements ApprovalTaskService {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeApproverServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeApproverServiceImpl.java
new file mode 100644
index 0000000..0921c35
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeApproverServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service.impl;
+
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper;
+import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣瀹℃壒浜鸿〃 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:30
+ */
+@Service
+public class ApprovalTemplateNodeApproverServiceImpl extends ServiceImpl<ApprovalTemplateNodeApproverMapper, ApprovalTemplateNodeApprover> implements ApprovalTemplateNodeApproverService {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeServiceImpl.java
new file mode 100644
index 0000000..ca65a5f
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeServiceImpl.java
@@ -0,0 +1,62 @@
+package com.ruoyi.approve.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeApproverDto;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto;
+import com.ruoyi.approve.mapper.ApprovalTemplateNodeMapper;
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService;
+import com.ruoyi.approve.service.ApprovalTemplateNodeService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鑺傜偣琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-18 11:20:19
+ */
+@Service
+@RequiredArgsConstructor
+public class ApprovalTemplateNodeServiceImpl extends ServiceImpl<ApprovalTemplateNodeMapper, ApprovalTemplateNode> implements ApprovalTemplateNodeService {
+
+    private final ApprovalTemplateNodeMapper approvalTemplateNodeMapper;
+    private final ApprovalTemplateNodeApproverService approvalTemplateNodeApproverService;
+    @Override
+    public Boolean saveApprovalTemplateNode(Long templateId, List<ApprovalTemplateNodeDto> nodes) {
+        if (nodes == null || nodes.isEmpty()) {
+            throw new RuntimeException("鑺傜偣鍒楄〃涓嶈兘涓虹┖");
+        }
+
+        List<ApprovalTemplateNodeApprover> approverList = new ArrayList<>();
+
+        for (ApprovalTemplateNodeDto nodeDto : nodes) {
+            ApprovalTemplateNode node = new ApprovalTemplateNode();
+            BeanUtils.copyProperties(nodeDto, node);
+            node.setTemplateId(templateId);
+            approvalTemplateNodeMapper.insert(node);
+
+            List<ApprovalTemplateNodeApproverDto> approvers = nodeDto.getApprovers();
+            if (approvers == null || approvers.isEmpty()) {
+                throw new RuntimeException("鑺傜偣瀹℃壒浜轰笉鑳戒负绌�");
+            }
+            for (ApprovalTemplateNodeApproverDto approverDto : approvers) {
+                ApprovalTemplateNodeApprover approver = new ApprovalTemplateNodeApprover();
+                BeanUtils.copyProperties(approverDto, approver);
+                approver.setNodeId(node.getId());
+                approver.setTemplateId(templateId);
+                approver.setDeleted(0L);
+                approverList.add(approver);
+            }
+        }
+        approvalTemplateNodeApproverService.saveBatch(approverList);
+        return true;
+    }
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateServiceImpl.java
new file mode 100644
index 0000000..af5a774
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateServiceImpl.java
@@ -0,0 +1,250 @@
+package com.ruoyi.approve.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateDto;
+import com.ruoyi.approve.bean.vo.ApprovalTemplateNodeApproverVo;
+import com.ruoyi.approve.bean.vo.ApprovalTemplateNodeVo;
+import com.ruoyi.approve.bean.vo.ApprovalTemplateVo;
+import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
+import com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import com.ruoyi.approve.service.ApprovalTemplateNodeService;
+import com.ruoyi.approve.service.ApprovalTemplateService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 瀹℃壒妯℃澘鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @since 2026-05-18 11:20:08
+ */
+@Service
+@RequiredArgsConstructor
+public class ApprovalTemplateServiceImpl extends ServiceImpl<ApprovalTemplateMapper, ApprovalTemplate> implements ApprovalTemplateService {
+
+    private final ApprovalTemplateMapper approvalTemplateMapper;
+    private final ApprovalTemplateNodeService approvalTemplateNodeService;
+    private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
+
+    @Override
+    public IPage<ApprovalTemplateVo> listPage(Page<ApprovalTemplateVo> page, ApprovalTemplateDto approvalTemplateDto) {
+        IPage<ApprovalTemplateVo> approvalTemplateVoIPage = approvalTemplateMapper.listPage(page, approvalTemplateDto);
+        fillTemplateVoNodes(approvalTemplateVoIPage.getRecords());
+        return approvalTemplateVoIPage;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean saveApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto) {
+        approvalTemplateMapper.insert(approvalTemplateDto);
+        approvalTemplateNodeService.remove(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, approvalTemplateDto.getId())
+        );
+        approvalTemplateNodeApproverMapper.delete(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .eq(ApprovalTemplateNodeApprover::getTemplateId, approvalTemplateDto.getId())
+        );
+        return approvalTemplateNodeService.saveApprovalTemplateNode(
+                approvalTemplateDto.getId(),
+                approvalTemplateDto.getNodes()
+        );
+    }
+
+    @Override
+    public Boolean updateApprovalTemplateDto(ApprovalTemplateDto approvalTemplateDto) {
+        approvalTemplateMapper.updateById(approvalTemplateDto);
+        approvalTemplateNodeService.remove(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, approvalTemplateDto.getId())
+        );
+        approvalTemplateNodeApproverMapper.delete(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .eq(ApprovalTemplateNodeApprover::getTemplateId, approvalTemplateDto.getId())
+        );
+        return approvalTemplateNodeService.saveApprovalTemplateNode(
+                approvalTemplateDto.getId(),
+                approvalTemplateDto.getNodes()
+        );
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return false;
+        }
+        ApprovalTemplate updateEntity = new ApprovalTemplate();
+        updateEntity.setDeleted(1);
+        LambdaUpdateWrapper<ApprovalTemplate> updateWrapper = Wrappers.lambdaUpdate();
+        updateWrapper.in(ApprovalTemplate::getId, ids)
+                .eq(ApprovalTemplate::getDeleted, 0);
+
+        int rows = approvalTemplateMapper.update(updateEntity, updateWrapper);
+        return rows == ids.size();
+    }
+
+    @Override
+    public List<ApprovalTemplateVo> listApprovalTemplateVo(Integer type) {
+        List<ApprovalTemplate> templateList = this.list(
+                new LambdaQueryWrapper<ApprovalTemplate>()
+                        .eq(ApprovalTemplate::getDeleted, 0)
+                        .eq(ApprovalTemplate::getEnabled, 1)
+                        .orderByDesc(ApprovalTemplate::getTemplateType)
+                        .orderByDesc(ApprovalTemplate::getId)
+        );
+        if (CollUtil.isEmpty(templateList)) {
+            return Collections.emptyList();
+        }
+
+        List<ApprovalTemplateVo> templateVos = templateList.stream()
+                .map(template -> {
+                    ApprovalTemplateVo templateVo = new ApprovalTemplateVo();
+                    BeanUtils.copyProperties(template, templateVo);
+                    return templateVo;
+                })
+                .collect(Collectors.toList());
+        fillTemplateVoNodes(templateVos);
+        return templateVos;
+    }
+
+    @Override
+    public ApprovalTemplateVo getApprovalTemplateVoById(Long id) {
+        if (id == null) {
+            throw new IllegalArgumentException("鍙傛暟 id 涓嶈兘涓虹┖");
+        }
+
+        ApprovalTemplate template = this.getOne(
+                new LambdaQueryWrapper<ApprovalTemplate>()
+                        .eq(ApprovalTemplate::getId, id)
+                        .eq(ApprovalTemplate::getDeleted, 0)
+        );
+        if (template == null) {
+            throw new IllegalArgumentException("妯℃澘涓嶅瓨鍦�");
+        }
+
+        List<ApprovalTemplateNode> nodeList = approvalTemplateNodeService.list(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, id)
+                        .orderByAsc(ApprovalTemplateNode::getLevelNo)
+        );
+
+        List<ApprovalTemplateNodeApprover> approverList = approvalTemplateNodeApproverMapper.selectList(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .eq(ApprovalTemplateNodeApprover::getTemplateId, id)
+                        .eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
+        );
+
+        Map<Long, List<ApprovalTemplateNode>> nodeMap = nodeList.stream()
+                .collect(Collectors.groupingBy(ApprovalTemplateNode::getTemplateId));
+
+        Map<Long, List<ApprovalTemplateNodeApprover>> approverMap = approverList.stream()
+                .collect(Collectors.groupingBy(ApprovalTemplateNodeApprover::getNodeId));
+
+        return buildTemplateVo(template, nodeMap, approverMap);
+    }
+
+    /**
+     * 鎵归噺濉厖妯℃澘鑺傜偣鍙婅妭鐐瑰鎵逛汉锛岄伩鍏嶅惊鐜煡搴撱��
+     */
+    private void fillTemplateVoNodes(List<ApprovalTemplateVo> templateVos) {
+        if (CollUtil.isEmpty(templateVos)) {
+            return;
+        }
+
+        List<Long> templateIds = templateVos.stream()
+                .map(ApprovalTemplateVo::getId)
+                .collect(Collectors.toList());
+
+        List<ApprovalTemplateNode> nodeList = approvalTemplateNodeService.list(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .in(ApprovalTemplateNode::getTemplateId, templateIds)
+                        .orderByAsc(ApprovalTemplateNode::getLevelNo)
+        );
+
+        List<ApprovalTemplateNodeApprover> approverList = approvalTemplateNodeApproverMapper.selectList(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .in(ApprovalTemplateNodeApprover::getTemplateId, templateIds)
+                        .eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
+        );
+
+        Map<Long, List<ApprovalTemplateNode>> nodeMap = nodeList.stream()
+                .collect(Collectors.groupingBy(ApprovalTemplateNode::getTemplateId));
+
+        Map<Long, List<ApprovalTemplateNodeApprover>> approverMap = approverList.stream()
+                .collect(Collectors.groupingBy(ApprovalTemplateNodeApprover::getNodeId));
+
+        templateVos.forEach(templateVo -> templateVo.setNodes(
+                nodeMap.getOrDefault(templateVo.getId(), Collections.emptyList())
+                        .stream()
+                        .sorted(Comparator.comparing(
+                                ApprovalTemplateNode::getLevelNo,
+                                Comparator.nullsLast(Integer::compareTo)
+                        ))
+                        .map(node ->  buildNodeVo(node, approverMap))
+                        .collect(Collectors.toList())
+        ));
+    }
+
+    private ApprovalTemplateVo buildTemplateVo(ApprovalTemplate template,
+                                               Map<Long, List<ApprovalTemplateNode>> nodeMap,
+                                               Map<Long, List<ApprovalTemplateNodeApprover>> approverMap) {
+        ApprovalTemplateVo templateVo = new ApprovalTemplateVo();
+        BeanUtils.copyProperties(template, templateVo);
+
+        List<ApprovalTemplateNodeVo> nodeVos = nodeMap
+                .getOrDefault(template.getId(), Collections.emptyList())
+                .stream()
+                .sorted(Comparator.comparing(
+                        ApprovalTemplateNode::getLevelNo,
+                        Comparator.nullsLast(Integer::compareTo)
+                ))
+                .map(node -> buildNodeVo(node, approverMap))
+                .collect(Collectors.toList());
+
+        templateVo.setNodes(nodeVos);
+        return templateVo;
+    }
+
+    private ApprovalTemplateNodeVo buildNodeVo(ApprovalTemplateNode node,
+                                               Map<Long, List<ApprovalTemplateNodeApprover>> approverMap) {
+        ApprovalTemplateNodeVo nodeVo = new ApprovalTemplateNodeVo();
+        BeanUtils.copyProperties(node, nodeVo);
+
+        List<ApprovalTemplateNodeApproverVo> approverVos = approverMap
+                .getOrDefault(node.getId(), Collections.emptyList())
+                .stream()
+                .sorted(Comparator.comparing(
+                        ApprovalTemplateNodeApprover::getSortNo,
+                        Comparator.nullsLast(Integer::compareTo)
+                ))
+                .map(this::buildApproverVo)
+                .collect(Collectors.toList());
+
+        nodeVo.setApprovers(approverVos);
+        return nodeVo;
+    }
+
+    private ApprovalTemplateNodeApproverVo buildApproverVo(ApprovalTemplateNodeApprover approver) {
+        ApprovalTemplateNodeApproverVo approverVo = new ApprovalTemplateNodeApproverVo();
+        BeanUtils.copyProperties(approver, approverVo);
+        return approverVo;
+    }
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementDetailServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementDetailServiceImpl.java
new file mode 100644
index 0000000..1897440
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementDetailServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service.impl;
+
+import com.ruoyi.approve.pojo.FinReimbursementDetail;
+import com.ruoyi.approve.mapper.FinReimbursementDetailMapper;
+import com.ruoyi.approve.service.FinReimbursementDetailService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曟槑缁嗚〃 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:38
+ */
+@Service
+public class FinReimbursementDetailServiceImpl extends ServiceImpl<FinReimbursementDetailMapper, FinReimbursementDetail> implements FinReimbursementDetailService {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java
new file mode 100644
index 0000000..1861d83
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java
@@ -0,0 +1,544 @@
+package com.ruoyi.approve.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeApproverDto;
+import com.ruoyi.approve.bean.dto.ApprovalTemplateNodeDto;
+import com.ruoyi.approve.bean.dto.FinReimbursementDto;
+import com.ruoyi.approve.bean.vo.FinReimbursementVo;
+import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
+import com.ruoyi.approve.mapper.FinReimbursementDetailMapper;
+import com.ruoyi.approve.mapper.FinReimbursementMapper;
+import com.ruoyi.approve.mapper.FinReimbursementTravelMapper;
+import com.ruoyi.approve.pojo.*;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.approve.service.*;
+import com.ruoyi.common.enums.TypeEnums;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 鎶ラ攢鍗曚富琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:15
+ */
+@Service
+@RequiredArgsConstructor
+public class FinReimbursementServiceImpl extends ServiceImpl<FinReimbursementMapper, FinReimbursement> implements FinReimbursementService {
+
+    private static final String BILL_STATUS_DRAFT = "DRAFT";
+    private static final String BILL_STATUS_IN_APPROVAL = "IN_APPROVAL";
+    private static final String NODE_STATUS_WAITING = "WAITING";
+
+    private final ApprovalInstanceMapper approvalInstanceMapper;
+    private final ApprovalInstanceService approvalInstanceService;
+    private final ApprovalInstanceNodeService approvalInstanceNodeService;
+    private final ApprovalTaskService approvalTaskService;
+    private final ApprovalRecordService approvalRecordService;
+    private final FinReimbursementMapper finReimbursementMapper;
+    private final FinReimbursementTravelMapper finReimbursementTravelMapper;
+    private final FinReimbursementDetailMapper finReimbursementDetailMapper;
+    private final FileUtil fileUtil;
+    private final ISysNoticeService sysNoticeService;
+    @Override
+    public IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page) {
+        IPage<FinReimbursementVo> finReimbursementVoIPage = finReimbursementMapper.listPage(finReimbursementDto, page);
+        finReimbursementVoIPage.getRecords().forEach(vo -> {
+            vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_REIMBURSEMENT,  vo.getId()));
+        });
+
+        return finReimbursementVoIPage;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(FinReimbursementDto finReimbursementDto) {
+        String billStatus = validateAddParam(finReimbursementDto);
+
+        // 鐢熸垚鎶ラ攢鍗曞彿
+        String billNo = OrderUtils.countTodayByCreateTime(finReimbursementMapper, "BXD", "bill_no");
+        List<FinReimbursementDetail> details = finReimbursementDto.getDetails();
+        BigDecimal totalAmount = details.stream()
+                .map(FinReimbursementDetail::getAmount)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        FinReimbursement reimbursement = buildReimbursement(finReimbursementDto, billNo, totalAmount, billStatus);
+        // 淇濆瓨鎶ラ攢鍗曚富琛�
+        boolean saved = this.save(reimbursement);
+        if (!saved || reimbursement.getId() == null) {
+            throw new ServiceException("鏂板鎶ラ攢鍗曞け璐�");
+        }
+        Long reimbursementId = reimbursement.getId();
+
+        // 淇濆瓨宸梾鎶ラ攢鎵╁睍淇℃伅锛堟姤閿�绫诲瀷涓哄樊鏃呮姤閿�鏃讹級
+        FinReimbursementTravel travel = finReimbursementDto.getTravel();
+        if (isTravelReimbursement(finReimbursementDto.getReimbursementType())) {
+            travel.setReimbursementId(reimbursementId);
+            int travelRows = finReimbursementTravelMapper.insert(travel);
+            if (travelRows != 1) {
+                throw new ServiceException("鏂板宸梾鎶ラ攢鎵╁睍淇℃伅澶辫触");
+            }
+        }
+
+        // 淇濆瓨鎶ラ攢鍗曟槑缁�
+        for (int i = 0; i < details.size(); i++) {
+            FinReimbursementDetail detail = details.get(i);
+            detail.setId(null);
+            detail.setReimbursementId(reimbursementId);
+            detail.setRowNo(i + 1);
+            int detailRows = finReimbursementDetailMapper.insert(detail);
+            if (detailRows != 1) {
+                throw new ServiceException("鏂板鎶ラ攢鍗曟槑缁嗗け璐�");
+            }
+        }
+
+        if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) {
+            startApproval(reimbursement, finReimbursementDto);
+        }
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementId, finReimbursementDto.getStorageBlobDTOs());
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(FinReimbursementDto finReimbursementDto) {
+        String billStatus = validateUpdateParam(finReimbursementDto);
+
+        Long reimbursementId = finReimbursementDto.getId();
+        FinReimbursement existing = finReimbursementMapper.selectById(reimbursementId);
+        if (existing == null) {
+            throw new ServiceException("鎶ラ攢鍗曚笉瀛樺湪");
+        }
+
+        // 璁$畻鏄庣粏姹囨�婚噾棰�
+        List<FinReimbursementDetail> details = finReimbursementDto.getDetails();
+        BigDecimal totalAmount = details.stream()
+                .map(FinReimbursementDetail::getAmount)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        // 鏇存柊涓昏〃
+        FinReimbursement reimbursement = buildReimbursement(
+                finReimbursementDto,
+                existing.getBillNo(),
+                totalAmount,
+                billStatus
+        );
+        reimbursement.setId(reimbursementId);
+        int mainRows = finReimbursementMapper.updateById(reimbursement);
+        if (mainRows != 1) {
+            throw new ServiceException("鏇存柊鎶ラ攢鍗曚富琛ㄥけ璐�");
+        }
+
+        // 鏌ヨ鏁版嵁搴撲腑宸叉湁鐨勬槑缁�
+        List<FinReimbursementDetail> existingDetails = finReimbursementDetailMapper.selectList(
+                new LambdaQueryWrapper<FinReimbursementDetail>()
+                        .eq(FinReimbursementDetail::getReimbursementId, reimbursementId));
+        Set<Long> existingDetailIds = existingDetails.stream()
+                .map(FinReimbursementDetail::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        // 鏂版槑缁嗕腑鏈塈D鐨� 鈫� 鏇存柊锛涙棤ID鐨� 鈫� 鏂板
+        Set<Long> submittedDetailIds = new HashSet<>();
+        for (int i = 0; i < details.size(); i++) {
+            FinReimbursementDetail detail = details.get(i);
+            detail.setReimbursementId(reimbursementId);
+            detail.setRowNo(i + 1);
+            if (detail.getId() != null && existingDetailIds.contains(detail.getId())) {
+                finReimbursementDetailMapper.updateById(detail);
+                submittedDetailIds.add(detail.getId());
+            } else {
+                detail.setId(null);
+                finReimbursementDetailMapper.insert(detail);
+            }
+        }
+
+        // 鏁版嵁搴撲腑宸叉湁浣嗘柊鏄庣粏涓病鏈夌殑 鈫� 鍒犻櫎
+        for (Long existingId : existingDetailIds) {
+            if (!submittedDetailIds.contains(existingId)) {
+                finReimbursementDetailMapper.deleteById(existingId);
+            }
+        }
+
+        // 宸梾鎵╁睍锛氭湁鍒欐洿鏂帮紝鏃犲垯鏂板
+        FinReimbursementTravel existingTravel = finReimbursementTravelMapper.selectOne(
+                new LambdaQueryWrapper<FinReimbursementTravel>()
+                        .eq(FinReimbursementTravel::getReimbursementId, reimbursementId)
+                        .last("LIMIT 1"));
+        FinReimbursementTravel travel = finReimbursementDto.getTravel();
+        if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && travel != null) {
+            travel.setReimbursementId(reimbursementId);
+            if (existingTravel != null) {
+                travel.setId(existingTravel.getId());
+                finReimbursementTravelMapper.updateById(travel);
+            } else {
+                travel.setId(null);
+                finReimbursementTravelMapper.insert(travel);
+            }
+        }
+
+        resetApprovalFlow(existing, reimbursementId);
+        if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) {
+            reimbursement.setApprovalInstanceId(null);
+            startApproval(reimbursement, finReimbursementDto);
+        }
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, reimbursementId, finReimbursementDto.getStorageBlobDTOs());
+
+        return true;
+    }
+
+    @Override
+    public FinReimbursementVo detail(Long id) {
+        if (id == null ) {
+            throw new ServiceException("鎶ラ攢鍗旾D涓嶈兘涓虹┖");
+        }
+
+        FinReimbursement reimbursement = finReimbursementMapper.selectById(id);
+        if (reimbursement == null) {
+            throw new ServiceException("鎶ラ攢鍗曚笉瀛樺湪");
+        }
+
+        FinReimbursementVo vo = new FinReimbursementVo();
+        vo.setId(reimbursement.getId());
+        vo.setBillNo(reimbursement.getBillNo());
+        vo.setReimbursementType(reimbursement.getReimbursementType());
+        vo.setExpenseType(reimbursement.getExpenseType());
+        vo.setApplicantId(reimbursement.getApplicantId());
+        vo.setApplicantCode(reimbursement.getApplicantCode());
+        vo.setApplicantName(reimbursement.getApplicantName());
+        vo.setApplicantDeptId(reimbursement.getApplicantDeptId());
+        vo.setApplicantDeptName(reimbursement.getApplicantDeptName());
+        vo.setReason(reimbursement.getReason());
+        vo.setApplyAmount(reimbursement.getApplyAmount());
+        vo.setDetailTotalAmount(reimbursement.getDetailTotalAmount());
+        vo.setPayeeName(reimbursement.getPayeeName());
+        vo.setPayeeAccount(reimbursement.getPayeeAccount());
+        vo.setPayeeBank(reimbursement.getPayeeBank());
+        vo.setApprovalInstanceId(reimbursement.getApprovalInstanceId());
+        vo.setApproveProcessId(reimbursement.getApproveProcessId());
+        vo.setBillStatus(reimbursement.getBillStatus());
+        vo.setApprovedTime(reimbursement.getApprovedTime());
+        vo.setPaidTime(reimbursement.getPaidTime());
+        vo.setAccountExpenseId(reimbursement.getAccountExpenseId());
+        vo.setRemark(reimbursement.getRemark());
+        vo.setTenantId(reimbursement.getTenantId());
+        vo.setCreateUser(reimbursement.getCreateUser());
+        vo.setCreateTime(reimbursement.getCreateTime());
+        vo.setUpdateUser(reimbursement.getUpdateUser());
+        vo.setUpdateTime(reimbursement.getUpdateTime());
+        vo.setDeptId(reimbursement.getDeptId());
+        vo.setDeleted(reimbursement.getDeleted());
+
+        vo.setDetails(finReimbursementDetailMapper.selectList(
+                new LambdaQueryWrapper<FinReimbursementDetail>()
+                        .eq(FinReimbursementDetail::getReimbursementId, reimbursement.getId())
+                        .orderByAsc(FinReimbursementDetail::getRowNo)
+        ));
+
+        if (isTravelReimbursement(reimbursement.getReimbursementType())) {
+            vo.setTravel(finReimbursementTravelMapper.selectOne(
+                    new LambdaQueryWrapper<FinReimbursementTravel>()
+                            .eq(FinReimbursementTravel::getReimbursementId, reimbursement.getId())
+                            .last("LIMIT 1")
+            ));
+        }
+        vo.setStorageBlobVOList(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.FIN_REIMBURSEMENT, reimbursement.getId()));
+        //瀹℃壒璁板綍杩斿洖
+        vo.setTasks(approvalTaskService.list(new LambdaQueryWrapper<ApprovalTask>().eq(ApprovalTask::getInstanceId, reimbursement.getApprovalInstanceId())));
+        vo.setRecords(approvalRecordService.list(new LambdaQueryWrapper<ApprovalRecord>().eq(ApprovalRecord::getInstanceId, reimbursement.getApprovalInstanceId())));
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean delete(List<Long> ids) {
+        fileUtil.deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum.FILE, RecordTypeEnum.FIN_REIMBURSEMENT, ids);
+        //鍏堝垹闄ゆ槑缁�
+        finReimbursementDetailMapper.delete(new LambdaQueryWrapper<FinReimbursementDetail>().in(FinReimbursementDetail::getReimbursementId, ids));
+        //鍒犻櫎宸梾
+        finReimbursementTravelMapper.delete(new LambdaQueryWrapper<FinReimbursementTravel>().in(FinReimbursementTravel::getReimbursementId, ids));
+        //鍒犻櫎涓昏〃
+        int rows = finReimbursementMapper.delete(new LambdaQueryWrapper<FinReimbursement>().in(FinReimbursement::getId, ids));
+        return rows == ids.size();
+    }
+
+    private String validateUpdateParam(FinReimbursementDto finReimbursementDto) {
+        if (finReimbursementDto == null || finReimbursementDto.getId() == null) {
+            throw new ServiceException("鎶ラ攢鍗旾D涓嶈兘涓虹┖");
+        }
+        if (finReimbursementDto.getReimbursementType() == null) {
+            throw new ServiceException("鎶ラ攢绫诲瀷涓嶈兘涓虹┖");
+        }
+        String billStatus = normalizeBillStatus(finReimbursementDto.getBillStatus());
+        if (billStatus == null) {
+            throw new ServiceException("鍗曟嵁鐘舵�佸彧鏀寔 DRAFT 鎴� IN_APPROVAL");
+        }
+        if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) {
+            validateApprovalNodes(finReimbursementDto.getNodes());
+        }
+        List<FinReimbursementDetail> details = finReimbursementDto.getDetails();
+        if (details == null || details.isEmpty()) {
+            throw new ServiceException("鎶ラ攢鍗曟槑缁嗕笉鑳戒负绌�");
+        }
+        for (FinReimbursementDetail detail : details) {
+            if (detail == null) {
+                throw new ServiceException("鎶ラ攢鍗曟槑缁嗕笉鑳戒负绌�");
+            }
+            if (detail.getAmount() == null || detail.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
+                throw new ServiceException("鎶ラ攢鍗曟槑缁嗛噾棰濆繀椤诲ぇ浜�0");
+            }
+        }
+        if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() == null) {
+            throw new ServiceException("宸梾鎶ラ攢蹇呴』濉啓宸梾鎵╁睍淇℃伅");
+        }
+        if (!isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() != null) {
+            throw new ServiceException("闈炲樊鏃呮姤閿�涓嶅厑璁稿~鍐欏樊鏃呮墿灞曚俊鎭�");
+        }
+        return billStatus;
+    }
+
+    private String validateAddParam(FinReimbursementDto finReimbursementDto) {
+        if (finReimbursementDto == null) {
+            throw new ServiceException("鎶ラ攢鍗曟暟鎹笉鑳戒负绌�");
+        }
+        if (finReimbursementDto.getReimbursementType() == null) {
+            throw new ServiceException("鎶ラ攢绫诲瀷涓嶈兘涓虹┖");
+        }
+        String billStatus = normalizeBillStatus(finReimbursementDto.getBillStatus());
+        if (billStatus == null) {
+            throw new ServiceException("鍗曟嵁鐘舵�佸彧鏀寔 DRAFT 鎴� IN_APPROVAL");
+        }
+        if (BILL_STATUS_IN_APPROVAL.equals(billStatus)) {
+            validateApprovalNodes(finReimbursementDto.getNodes());
+        }
+        List<FinReimbursementDetail> details = finReimbursementDto.getDetails();
+        if (details == null || details.isEmpty()) {
+            throw new ServiceException("鎶ラ攢鍗曟槑缁嗕笉鑳戒负绌�");
+        }
+        for (FinReimbursementDetail detail : details) {
+            if (detail == null) {
+                throw new ServiceException("鎶ラ攢鍗曟槑缁嗕笉鑳戒负绌�");
+            }
+            if (detail.getAmount() == null || detail.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
+                throw new ServiceException("鎶ラ攢鍗曟槑缁嗛噾棰濆繀椤诲ぇ浜�0");
+            }
+        }
+        if (isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() == null) {
+            throw new ServiceException("宸梾鎶ラ攢蹇呴』濉啓宸梾鎵╁睍淇℃伅");
+        }
+        if (!isTravelReimbursement(finReimbursementDto.getReimbursementType()) && finReimbursementDto.getTravel() != null) {
+            throw new ServiceException("闈炲樊鏃呮姤閿�涓嶅厑璁稿~鍐欏樊鏃呮墿灞曚俊鎭�");
+        }
+        return billStatus;
+    }
+
+    private FinReimbursement buildReimbursement(FinReimbursementDto finReimbursementDto, String billNo, BigDecimal totalAmount, String billStatus) {
+        FinReimbursement reimbursement = new FinReimbursement();
+        reimbursement.setId(null);
+        reimbursement.setBillNo(billNo);
+        reimbursement.setReimbursementType(finReimbursementDto.getReimbursementType());
+        reimbursement.setExpenseType(finReimbursementDto.getExpenseType());
+        reimbursement.setApplicantId(finReimbursementDto.getApplicantId());
+        reimbursement.setApplicantCode(finReimbursementDto.getApplicantCode());
+        reimbursement.setApplicantName(finReimbursementDto.getApplicantName());
+        reimbursement.setApplicantDeptId(finReimbursementDto.getApplicantDeptId());
+        reimbursement.setApplicantDeptName(finReimbursementDto.getApplicantDeptName());
+        reimbursement.setReason(finReimbursementDto.getReason());
+        reimbursement.setApplyAmount(finReimbursementDto.getApplyAmount());
+        reimbursement.setDetailTotalAmount(totalAmount);
+        reimbursement.setPayeeName(finReimbursementDto.getPayeeName());
+        reimbursement.setPayeeAccount(finReimbursementDto.getPayeeAccount());
+        reimbursement.setPayeeBank(finReimbursementDto.getPayeeBank());
+        reimbursement.setRemark(finReimbursementDto.getRemark());
+        reimbursement.setTenantId(finReimbursementDto.getTenantId());
+        reimbursement.setApproveProcessId(null);
+        reimbursement.setBillStatus(billStatus);
+        return reimbursement;
+    }
+
+    private void startApproval(FinReimbursement reimbursement, FinReimbursementDto finReimbursementDto) {
+        Long businessType = resolveBusinessType(finReimbursementDto.getReimbursementType());
+        ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto();
+        approvalInstanceDto.setInstanceNo(OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no"));
+        approvalInstanceDto.setBusinessId(reimbursement.getId());
+        approvalInstanceDto.setTemplateId(null);
+        approvalInstanceDto.setTemplateName(TypeEnums.getLabelByValue(businessType) + "瀹℃壒");
+        approvalInstanceDto.setBusinessType(businessType);
+        approvalInstanceDto.setTitle("鎶ラ攢鍗曞彿锛�" + reimbursement.getBillNo());
+        approvalInstanceDto.setApplicantId(reimbursement.getApplicantId() != null ? reimbursement.getApplicantId() : SecurityUtils.getUserId());
+        approvalInstanceDto.setApplicantName(reimbursement.getApplicantName() != null ? reimbursement.getApplicantName() : SecurityUtils.getLoginUser().getNickName());
+        approvalInstanceDto.setApplyTime(LocalDateTime.now());
+        approvalInstanceDto.setStatus("PENDING");
+        approvalInstanceDto.setCurrentLevel(1);
+
+        boolean approvalSaved = approvalInstanceService.save(approvalInstanceDto);
+        if (!approvalSaved || approvalInstanceDto.getId() == null) {
+            throw new ServiceException("鍙戣捣瀹℃壒澶辫触");
+        }
+        List<ApprovalTask> firstTasks = createApprovalNodes(approvalInstanceDto, finReimbursementDto.getNodes());
+        sendApproveNotice(approvalInstanceDto, firstTasks);
+
+        FinReimbursement update = new FinReimbursement();
+        update.setId(reimbursement.getId());
+        update.setApprovalInstanceId(approvalInstanceDto.getId());
+        update.setBillStatus(BILL_STATUS_IN_APPROVAL);
+        int rows = finReimbursementMapper.updateById(update);
+        if (rows != 1) {
+            throw new ServiceException("鍥炲~瀹℃壒瀹炰緥澶辫触");
+        }
+    }
+
+    private List<ApprovalTask> createApprovalNodes(ApprovalInstanceDto approvalInstanceDto, List<ApprovalTemplateNodeDto> nodes) {
+        List<ApprovalTask> firstTasks = Collections.emptyList();
+        for (int i = 0; i < nodes.size(); i++) {
+            ApprovalTemplateNodeDto nodeDto = nodes.get(i);
+            ApprovalInstanceNode instanceNode = new ApprovalInstanceNode();
+            instanceNode.setInstanceId(approvalInstanceDto.getId());
+            instanceNode.setLevelNo(nodeDto.getLevelNo());
+            instanceNode.setApproveType(nodeDto.getApproveType());
+            instanceNode.setStatus(i == 0 ? "PENDING" : NODE_STATUS_WAITING);
+            instanceNode.setStartTime(i == 0 ? LocalDateTime.now() : null);
+            instanceNode.setDeleted((byte) 0);
+            approvalInstanceNodeService.save(instanceNode);
+
+            List<ApprovalTask> tasks = nodeDto.getApprovers().stream().map(approver -> {
+                ApprovalTask task = new ApprovalTask();
+                task.setInstanceId(approvalInstanceDto.getId());
+                task.setNodeId(instanceNode.getId());
+                task.setLevelNo(instanceNode.getLevelNo());
+                task.setApproverId(approver.getApproverId());
+                task.setApproverName(approver.getApproverName());
+                task.setTaskStatus("PENDING");
+                task.setIsRead((byte) 0);
+                task.setDeleted((byte) 0);
+                return task;
+            }).collect(Collectors.toList());
+            approvalTaskService.saveBatch(tasks);
+
+            if (i == 0) {
+                firstTasks = tasks;
+                ApprovalRecord record = new ApprovalRecord();
+                record.setInstanceId(approvalInstanceDto.getId());
+                record.setNodeId(instanceNode.getId());
+                record.setOperatorId(approvalInstanceDto.getApplicantId());
+                record.setOperatorName(approvalInstanceDto.getApplicantName());
+                record.setAction("SUBMIT");
+                record.setComment("鍙戣捣瀹℃壒");
+                record.setDeleted((byte) 0);
+                approvalRecordService.save(record);
+            }
+        }
+        return firstTasks;
+    }
+
+    private void validateApprovalNodes(List<ApprovalTemplateNodeDto> nodes) {
+        if (nodes == null || nodes.isEmpty()) {
+            throw new ServiceException("鎻愪氦瀹℃壒鏃跺鎵硅妭鐐逛笉鑳戒负绌�");
+        }
+        for (int i = 0; i < nodes.size(); i++) {
+            ApprovalTemplateNodeDto node = nodes.get(i);
+            if (node == null) {
+                throw new ServiceException("瀹℃壒鑺傜偣涓嶈兘涓虹┖");
+            }
+            if (node.getLevelNo() == null) {
+                node.setLevelNo(i + 1);
+            }
+            if (!StringUtils.hasText(node.getApproveType())) {
+                throw new ServiceException("瀹℃壒鑺傜偣瀹℃壒鏂瑰紡涓嶈兘涓虹┖");
+            }
+            List<ApprovalTemplateNodeApproverDto> approvers = node.getApprovers();
+            if (approvers == null || approvers.isEmpty()) {
+                throw new ServiceException("瀹℃壒鑺傜偣瀹℃壒浜轰笉鑳戒负绌�");
+            }
+            for (ApprovalTemplateNodeApproverDto approver : approvers) {
+                if (approver == null || approver.getApproverId() == null) {
+                    throw new ServiceException("瀹℃壒浜轰笉鑳戒负绌�");
+                }
+            }
+        }
+    }
+
+    private void sendApproveNotice(ApprovalInstanceDto instance, List<ApprovalTask> tasks) {
+        if (instance == null || tasks == null || tasks.isEmpty()) {
+            return;
+        }
+        List<Long> approverIds = tasks.stream()
+                .map(ApprovalTask::getApproverId)
+                .filter(id -> id != null && id > 0)
+                .distinct()
+                .collect(Collectors.toList());
+        if (approverIds.isEmpty()) {
+            return;
+        }
+        String title = "鎶ラ攢瀹℃壒";
+        String message = "瀹℃壒鍗曞彿 " + instance.getInstanceNo() + " 闇�瑕佹偍瀹℃壒";
+        String jumpPath = "/approvalInstance?id=" + instance.getId();
+        sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
+    }
+
+    private void resetApprovalFlow(FinReimbursement existing, Long reimbursementId) {
+        if (existing == null || existing.getApprovalInstanceId() == null) {
+            return;
+        }
+        Long approvalInstanceId = existing.getApprovalInstanceId();
+        if (!"REJECTED".equals(existing.getBillStatus())) {
+            approvalInstanceService.delete(Collections.singletonList(approvalInstanceId));
+        }
+        clearApprovalBinding(reimbursementId);
+    }
+
+    private void clearApprovalBinding(Long reimbursementId) {
+        int rows = finReimbursementMapper.update(
+                null,
+                Wrappers.<FinReimbursement>lambdaUpdate()
+                        .eq(FinReimbursement::getId, reimbursementId)
+                        .set(FinReimbursement::getApprovalInstanceId, null)
+        );
+        if (rows != 1) {
+            throw new ServiceException("閲嶇疆瀹℃壒娴佺▼澶辫触");
+        }
+    }
+
+    private Long resolveBusinessType(Byte reimbursementType) {
+        return isTravelReimbursement(reimbursementType)
+                ? TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode()
+                : TypeEnums.EXPENSE_APPROVAL.getCode();
+    }
+
+    private String normalizeBillStatus(String billStatus) {
+        if (billStatus == null) {
+            return BILL_STATUS_DRAFT;
+        }
+        String normalized = billStatus.trim().toUpperCase();
+        if (BILL_STATUS_DRAFT.equals(normalized) || BILL_STATUS_IN_APPROVAL.equals(normalized)) {
+            return normalized;
+        }
+        return null;
+    }
+
+    private boolean isTravelReimbursement(Byte reimbursementType) {
+        return Byte.valueOf((byte) 1).equals(reimbursementType);
+    }
+}
diff --git a/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementTravelServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementTravelServiceImpl.java
new file mode 100644
index 0000000..3329f9a
--- /dev/null
+++ b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementTravelServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.approve.service.impl;
+
+import com.ruoyi.approve.pojo.FinReimbursementTravel;
+import com.ruoyi.approve.mapper.FinReimbursementTravelMapper;
+import com.ruoyi.approve.service.FinReimbursementTravelService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 宸梾鎶ラ攢鎵╁睍琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-21 09:56:47
+ */
+@Service
+public class FinReimbursementTravelServiceImpl extends ServiceImpl<FinReimbursementTravelMapper, FinReimbursementTravel> implements FinReimbursementTravelService {
+
+}
diff --git a/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java b/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
index 4ae03e6..239726a 100644
--- a/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
+++ b/src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java
@@ -1,9 +1,370 @@
 package com.ruoyi.approve.utils;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.approve.pojo.ApprovalInstanceNode;
+import com.ruoyi.approve.pojo.ApprovalRecord;
+import com.ruoyi.approve.pojo.ApprovalTask;
+import com.ruoyi.approve.pojo.ApprovalTemplateNode;
+import com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover;
+import com.ruoyi.approve.service.ApprovalInstanceNodeService;
+import com.ruoyi.approve.service.ApprovalRecordService;
+import com.ruoyi.approve.service.ApprovalTaskService;
+import com.ruoyi.approve.service.ApprovalTemplateNodeApproverService;
+import com.ruoyi.approve.service.ApprovalTemplateNodeService;
 import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 瀹℃壒娴佺▼鑺傜偣宸ュ叿绫�
+ */
+@Component
 @RequiredArgsConstructor
 public class ApproveProcessConfigNodeUtils {
 
+    private final ApprovalInstanceNodeService instanceNodeService;
+    private final ApprovalTaskService approvalTaskService;
+    private final ApprovalRecordService approvalRecordService;
+    private final ApprovalTemplateNodeService approvalTemplateNodeService;
+    private final ApprovalTemplateNodeApproverService approvalTemplateNodeApproverService;
 
-}
+    /**
+     * 鎸夊綋鍓嶅眰绾у垱寤哄鎵硅妭鐐瑰拰瀹℃壒浠诲姟銆�
+     * 璇ラ噸杞戒細鍚屾椂鍐欏叆涓�鏉″彂璧峰鎵硅褰曘��
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance) {
+        return createCurrentNodeAndTasks(instance, true);
+    }
+
+    /**
+     * 鎸夊綋鍓嶅眰绾у垱寤哄鎵硅妭鐐瑰拰瀹℃壒浠诲姟銆�
+     *
+     * @param instance 瀹℃壒瀹炰緥
+     * @param createSubmitRecord 鏄惁鍒涘缓鍙戣捣瀹℃壒璁板綍
+     * @return 鍒涘缓鍑虹殑褰撳墠鑺傜偣瀹炰緥
+     */
+    @Transactional(rollbackFor = Exception.class)
+    public ApprovalInstanceNode createCurrentNodeAndTasks(ApprovalInstance instance, boolean createSubmitRecord) {
+        if (instance == null || instance.getId() == null) {
+            throw new RuntimeException("瀹℃壒瀹炰緥涓嶈兘涓虹┖");
+        }
+        if (instance.getTemplateId() == null) {
+            throw new RuntimeException("瀹℃壒妯℃澘涓嶈兘涓虹┖");
+        }
+
+        Integer currentLevel = instance.getCurrentLevel() == null ? 1 : instance.getCurrentLevel();
+
+        ApprovalInstanceNode existsNode = instanceNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalInstanceNode>()
+                        .eq(ApprovalInstanceNode::getInstanceId, instance.getId())
+                        .eq(ApprovalInstanceNode::getLevelNo, currentLevel)
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+        if (existsNode != null) {
+            return existsNode;
+        }
+
+        ApprovalTemplateNode templateNode = approvalTemplateNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalTemplateNode>()
+                        .eq(ApprovalTemplateNode::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNode::getLevelNo, currentLevel)
+                        .orderByAsc(ApprovalTemplateNode::getId)
+                        .last("LIMIT 1")
+        );
+        if (templateNode == null) {
+            throw new RuntimeException("鏈壘鍒板綋鍓嶅眰绾у搴旂殑瀹℃壒妯℃澘鑺傜偣");
+        }
+
+        List<ApprovalTemplateNodeApprover> approvers = approvalTemplateNodeApproverService.list(
+                new LambdaQueryWrapper<ApprovalTemplateNodeApprover>()
+                        .eq(ApprovalTemplateNodeApprover::getTemplateId, instance.getTemplateId())
+                        .eq(ApprovalTemplateNodeApprover::getNodeId, templateNode.getId())
+                        .eq(ApprovalTemplateNodeApprover::getDeleted, 0L)
+                        .orderByAsc(ApprovalTemplateNodeApprover::getSortNo)
+        );
+        if (approvers == null || approvers.isEmpty()) {
+            throw new RuntimeException("褰撳墠瀹℃壒鑺傜偣鏈厤缃鎵逛汉");
+        }
+
+        ApprovalInstanceNode instanceNode = new ApprovalInstanceNode();
+        instanceNode.setInstanceId(instance.getId());
+        instanceNode.setLevelNo(templateNode.getLevelNo());
+        instanceNode.setApproveType(templateNode.getApproveType());
+        instanceNode.setStatus("PENDING");
+        instanceNode.setStartTime(LocalDateTime.now());
+        instanceNode.setDeleted((byte) 0);
+        instanceNodeService.save(instanceNode);
+
+        List<ApprovalTask> taskList = new ArrayList<>(approvers.size());
+        for (ApprovalTemplateNodeApprover approver : approvers) {
+            ApprovalTask task = new ApprovalTask();
+            task.setInstanceId(instance.getId());
+            task.setNodeId(instanceNode.getId());
+            task.setLevelNo(instanceNode.getLevelNo());
+            task.setApproverId(approver.getApproverId());
+            task.setApproverName(approver.getApproverName());
+            task.setTaskStatus("PENDING");
+            task.setIsRead((byte) 0);
+            task.setDeleted((byte) 0);
+            taskList.add(task);
+        }
+        approvalTaskService.saveBatch(taskList);
+
+        if (createSubmitRecord) {
+            ApprovalRecord record = new ApprovalRecord();
+            record.setInstanceId(instance.getId());
+            record.setNodeId(instanceNode.getId());
+            record.setOperatorId(instance.getApplicantId());
+            record.setOperatorName(instance.getApplicantName());
+            record.setAction("SUBMIT");
+            record.setComment("鍙戣捣瀹℃壒");
+            record.setDeleted((byte) 0);
+            approvalRecordService.save(record);
+        }
+
+        return instanceNode;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠寰呭鐞嗚妭鐐广��
+     */
+    public ApprovalInstanceNode getCurrentNode(Long instanceId) {
+        if (instanceId == null) {
+            return null;
+        }
+
+        return instanceNodeService.getOne(
+                new LambdaQueryWrapper<ApprovalInstanceNode>()
+                        .eq(ApprovalInstanceNode::getInstanceId, instanceId)
+                        .eq(ApprovalInstanceNode::getStatus, "PENDING")
+                        .eq(ApprovalInstanceNode::getDeleted, 0)
+                        .orderByAsc(ApprovalInstanceNode::getLevelNo)
+                        .last("LIMIT 1")
+        );
+    }
+
+    /**
+     * 鏌ヨ褰撳墠瀹℃壒灞傜骇銆�
+     */
+    public Integer getCurrentLevel(Long instanceId) {
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        return currentNode != null ? currentNode.getLevelNo() : null;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣涓嬬殑寰呭鎵逛换鍔°��
+     */
+    public List<ApprovalTask> getCurrentPendingTasks(Long instanceId) {
+        if (instanceId == null) {
+            return List.of();
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return List.of();
+        }
+
+        return approvalTaskService.list(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .orderByAsc(ApprovalTask::getLevelNo)
+        );
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鍦ㄥ綋鍓嶈妭鐐逛笂鐨勫緟瀹℃壒浠诲姟銆�
+     */
+    public ApprovalTask getCurrentUserTask(Long instanceId, Long userId) {
+        if (instanceId == null || userId == null) {
+            return null;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return null;
+        }
+
+        return approvalTaskService.getOne(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getApproverId, userId)
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁鏄綋鍓嶅鎵逛汉銆�
+     */
+    public boolean isCurrentApprover(Long instanceId, Long userId) {
+        return getCurrentUserTask(instanceId, userId) != null;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣鐨勫鎵逛汉 ID 鍒楄〃銆�
+     */
+    public List<Long> getCurrentNodeApproverIds(Long instanceId) {
+        return getCurrentPendingTasks(instanceId).stream()
+                .map(ApprovalTask::getApproverId)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣鍓╀綑寰呭鎵逛汉鏁般��
+     */
+    public int getRemainingApproverCount(Long instanceId) {
+        return getCurrentPendingTasks(instanceId).size();
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣宸插悓鎰忎汉鏁般��
+     */
+    public int getApprovedCount(Long instanceId) {
+        if (instanceId == null) {
+            return 0;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return 0;
+        }
+
+        return Math.toIntExact(approvalTaskService.count(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getTaskStatus, "APPROVED")
+                        .eq(ApprovalTask::getDeleted, 0)
+        ));
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鑺傜偣宸叉嫆缁濅汉鏁般��
+     */
+    public int getRejectedCount(Long instanceId) {
+        if (instanceId == null) {
+            return 0;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return 0;
+        }
+
+        return Math.toIntExact(approvalTaskService.count(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getTaskStatus, "REJECTED")
+                        .eq(ApprovalTask::getDeleted, 0)
+        ));
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鑺傜偣鏄惁鍙互娴佽浆鍒颁笅涓�灞傘��
+     */
+    public boolean canProceedToNextLevel(Long instanceId, String approveType) {
+        if (instanceId == null || approveType == null) {
+            return false;
+        }
+
+        if (getRejectedCount(instanceId) > 0) {
+            return false;
+        }
+
+        int totalApproverCount = getCurrentPendingTasks(instanceId).size() + getApprovedCount(instanceId);
+        int approvedCount = getApprovedCount(instanceId);
+
+        if ("AND".equalsIgnoreCase(approveType)) {
+            return approvedCount > 0 && approvedCount == totalApproverCount;
+        }
+        if ("OR".equalsIgnoreCase(approveType)) {
+            return approvedCount > 0;
+        }
+        return false;
+    }
+
+    /**
+     * 鏌ヨ褰撳墠鐢ㄦ埛鍦ㄥ綋鍓嶈妭鐐逛笂鐨勪换鍔$姸鎬併��
+     */
+    public String getUserTaskStatus(Long instanceId, Long userId) {
+        if (instanceId == null || userId == null) {
+            return null;
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return null;
+        }
+
+        ApprovalTask task = approvalTaskService.getOne(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getInstanceId, instanceId)
+                        .eq(ApprovalTask::getNodeId, currentNode.getId())
+                        .eq(ApprovalTask::getApproverId, userId)
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .last("LIMIT 1")
+        );
+
+        return task != null ? task.getTaskStatus() : null;
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾鐢ㄦ埛鐨勫叏閮ㄥ緟瀹℃壒浠诲姟銆�
+     */
+    public List<ApprovalTask> getUserAllPendingTasks(Long userId) {
+        if (userId == null) {
+            return List.of();
+        }
+
+        return approvalTaskService.list(
+                new LambdaQueryWrapper<ApprovalTask>()
+                        .eq(ApprovalTask::getApproverId, userId)
+                        .eq(ApprovalTask::getTaskStatus, "PENDING")
+                        .eq(ApprovalTask::getDeleted, 0)
+                        .orderByDesc(ApprovalTask::getCreateTime)
+        );
+    }
+
+    /**
+     * 鏌ヨ瀹℃壒瀹炰緥鐨勮繘搴︽憳瑕併��
+     */
+    public String getApprovalProgress(Long instanceId) {
+        if (instanceId == null) {
+            return "鏃犳晥鐨勫鎵瑰疄渚�";
+        }
+
+        ApprovalInstanceNode currentNode = getCurrentNode(instanceId);
+        if (currentNode == null) {
+            return "瀹℃壒宸插畬鎴愭垨灏氭湭寮�濮�";
+        }
+
+        int approvedCount = getApprovedCount(instanceId);
+        int rejectedCount = getRejectedCount(instanceId);
+        int pendingCount = getRemainingApproverCount(instanceId);
+        int totalCount = approvedCount + rejectedCount + pendingCount;
+
+        return String.format(
+                "绗�%d绾у鎵癸細鎬讳汉鏁�=%d锛屽凡鍚屾剰=%d锛屽凡鎷掔粷=%d锛屽緟瀹℃壒=%d",
+                currentNode.getLevelNo(),
+                totalCount,
+                approvedCount,
+                rejectedCount,
+                pendingCount
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
index 6fa2822..506a9d8 100644
--- a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -194,7 +194,12 @@
     // Account
     SALES_REFUND_AMOUNT_ORDER("sales_refund_amount_order"),
     SALES_RECEIPT_RETURN("sales_receipt_return"),
+    ACCOUNT_EXPENSE("account_expense"),
+    FIN_REIMBURSEMENT("fin_reimbursement"),
     FIN_VOUCHER("fin_voucher"),
+    ACCOUNT_FILE("account_file"),
+    ENTERPRISE_NEWS("enterprise_news"),
+    APPROVAL_INSTANCE("approval_instance"),
     ACCOUNT_INVOICE_APPLICATION("account_invoice_application"),
     ACCOUNT_PURCHASE_INVOICE("account_purchase_invoice");
 
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsController.java
new file mode 100644
index 0000000..36f1e21
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsController.java
@@ -0,0 +1,61 @@
+package com.ruoyi.collaborativeApproval.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto;
+import com.ruoyi.collaborativeApproval.service.EnterpriseNewsService;
+import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:50:59
+ */
+@RestController
+@RequestMapping("/enterpriseNews")
+@Tag(name = "浼佷笟鏂伴椈琛�")
+@AllArgsConstructor
+public class EnterpriseNewsController {
+
+    private final EnterpriseNewsService enterpriseNewsService;
+
+    @Operation(summary = "鍒嗛〉鏌ヨ")
+    @GetMapping("/listPage")
+    @Log(title = "浼佷笟鏂伴椈鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
+    public R listPage(Page<EnterpriseNewsVo>  page , EnterpriseNewsDto enterpriseNewsDto) {
+        return R.ok(enterpriseNewsService.listPage(page, enterpriseNewsDto));
+    }
+
+    @PostMapping("/save")
+    @Operation(summary = "淇濆瓨")
+    @Log(title = "淇濆瓨浼佷笟鏂伴椈", businessType = BusinessType.INSERT)
+    public R save(@RequestBody EnterpriseNewsDto enterpriseNewsDto) {
+        return R.ok(enterpriseNewsService.add(enterpriseNewsDto));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "鏇存柊")
+    @Log(title = "鏇存柊浼佷笟鏂伴椈", businessType = BusinessType.UPDATE)
+    public R update(@RequestBody EnterpriseNewsDto enterpriseNewsDto) {
+        return R.ok(enterpriseNewsService.updateEnterpriseNewsDto(enterpriseNewsDto));
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎")
+    @Log(title = "鍒犻櫎浼佷笟鏂伴椈", businessType = BusinessType.DELETE)
+    public R delete(@RequestBody List<Long> ids) {
+        return R.ok(enterpriseNewsService.delete(ids));
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeDeptController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeDeptController.java
new file mode 100644
index 0000000..8c68d1a
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeDeptController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.collaborativeApproval.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿閮ㄩ棬琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:12
+ */
+@RestController
+@RequestMapping("/enterpriseNewsScopeDept")
+public class EnterpriseNewsScopeDeptController {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeUserController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeUserController.java
new file mode 100644
index 0000000..7ceba13
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeUserController.java
@@ -0,0 +1,18 @@
+package com.ruoyi.collaborativeApproval.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿鐢ㄦ埛琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:23
+ */
+@RestController
+@RequestMapping("/enterpriseNewsScopeUser")
+public class EnterpriseNewsScopeUserController {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/dto/EnterpriseNewsDto.java b/src/main/java/com/ruoyi/collaborativeApproval/dto/EnterpriseNewsDto.java
new file mode 100644
index 0000000..108cf66
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/dto/EnterpriseNewsDto.java
@@ -0,0 +1,26 @@
+package com.ruoyi.collaborativeApproval.dto;
+
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class EnterpriseNewsDto extends EnterpriseNews {
+
+    private String createUserName;
+
+    private List<Long> deptIds;
+
+    private List<Long> userIds;
+
+    private Long templateId;
+    private String templateName;
+
+    private String createTimeStart;
+    private String createTimeEnd;
+
+    private List<StorageBlobDTO> storageBlobDTOs;
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsMapper.java b/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsMapper.java
new file mode 100644
index 0000000..799ae82
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsMapper.java
@@ -0,0 +1,24 @@
+package com.ruoyi.collaborativeApproval.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:50:59
+ */
+@Mapper
+public interface EnterpriseNewsMapper extends BaseMapper<EnterpriseNews> {
+
+    IPage<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page,@Param("enterpriseNewsDto") EnterpriseNewsDto enterpriseNewsDto);
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeDeptMapper.java b/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeDeptMapper.java
new file mode 100644
index 0000000..5b4639f
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeDeptMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.collaborativeApproval.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿閮ㄩ棬琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:12
+ */
+@Mapper
+public interface EnterpriseNewsScopeDeptMapper extends BaseMapper<EnterpriseNewsScopeDept> {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeUserMapper.java b/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeUserMapper.java
new file mode 100644
index 0000000..ab9a64f
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeUserMapper.java
@@ -0,0 +1,18 @@
+package com.ruoyi.collaborativeApproval.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿鐢ㄦ埛琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:23
+ */
+@Mapper
+public interface EnterpriseNewsScopeUserMapper extends BaseMapper<EnterpriseNewsScopeUser> {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNews.java b/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNews.java
new file mode 100644
index 0000000..f5e001b
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNews.java
@@ -0,0 +1,88 @@
+package com.ruoyi.collaborativeApproval.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 浼佷笟鏂伴椈琛�
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:50:59
+ */
+@Getter
+@Setter
+@ToString
+@TableName("enterprise_news")
+@ApiModel(value = "EnterpriseNews瀵硅薄", description = "浼佷笟鏂伴椈琛�")
+public class EnterpriseNews implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "缂栧彿 ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @Schema(description = "鏍囬 Title")
+    private String title;
+
+    @Schema(description = "鎽樿 Summary")
+    private String summary;
+
+    @Schema(description = "姝f枃 Content")
+    private String content;
+
+    @Schema(description = "鍒嗙被 Category")
+    private String category;
+
+    @Schema(description = "闃呰鑼冨洿 Read scope: all 鍏ㄥ憳, dept 閮ㄩ棬, custom 鑷畾涔�")
+    private String readScope;
+
+    @Schema(description = "鏄惁蹇呰 Required flag: 0 鍚�, 1 鏄�")
+    private Byte isRequired;
+
+    @Schema(description = "鐘舵�� Status: DRAFT 鑽夌, PENDING 寰呭鎵�, PUBLISHED 宸插彂甯�, REJECTED 椹冲洖, OFFLINE 宸蹭笅绾�")
+    private String status;
+
+    @Schema(description = "搴旇浜烘暟 Required read count")
+    private Integer requiredReadCount;
+
+    @Schema(description = "宸茶浜烘暟 Read count")
+    private Integer readCount;
+
+    @Schema(description = "鍒涘缓浜� Create user")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    @Schema(description = "鍒涘缓鏃堕棿 Create time")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
+    private LocalDateTime createTime;
+
+    @Schema(description = "鏇存柊浜� Update user")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    @Schema(description = "鏇存柊鏃堕棿 Update time")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss", iso = DateTimeFormat.ISO.DATE_TIME)
+    private LocalDateTime updateTime;
+
+    @Schema(description = "閮ㄩ棬ID Dept ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeDept.java b/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeDept.java
new file mode 100644
index 0000000..97ff9de
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeDept.java
@@ -0,0 +1,59 @@
+package com.ruoyi.collaborativeApproval.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿閮ㄩ棬琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:12
+ */
+@Getter
+@Setter
+@ToString
+@TableName("enterprise_news_scope_dept")
+@ApiModel(value = "EnterpriseNewsScopeDept瀵硅薄", description = "浼佷笟鏂伴椈闃呰鑼冨洿閮ㄩ棬琛�")
+public class EnterpriseNewsScopeDept implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 缂栧彿
+     */
+    @ApiModelProperty("缂栧彿")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 浼佷笟鏂伴椈ID
+     */
+    @ApiModelProperty("浼佷笟鏂伴椈ID")
+    private Long newsId;
+
+    /**
+     * 閮ㄩ棬ID
+     */
+    @ApiModelProperty("閮ㄩ棬ID")
+    private Long deptId;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeUser.java b/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeUser.java
new file mode 100644
index 0000000..9d43df2
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeUser.java
@@ -0,0 +1,59 @@
+package com.ruoyi.collaborativeApproval.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿鐢ㄦ埛琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:23
+ */
+@Getter
+@Setter
+@ToString
+@TableName("enterprise_news_scope_user")
+@ApiModel(value = "EnterpriseNewsScopeUser瀵硅薄", description = "浼佷笟鏂伴椈闃呰鑼冨洿鐢ㄦ埛琛�")
+public class EnterpriseNewsScopeUser implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 缂栧彿
+     */
+    @ApiModelProperty("缂栧彿")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 浼佷笟鏂伴椈ID
+     */
+    @ApiModelProperty("浼佷笟鏂伴椈ID")
+    private Long newsId;
+
+    /**
+     * 鐢ㄦ埛ID
+     */
+    @ApiModelProperty("鐢ㄦ埛ID")
+    private Long userId;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeDeptService.java b/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeDeptService.java
new file mode 100644
index 0000000..b41a7d8
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeDeptService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.collaborativeApproval.service;
+
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿閮ㄩ棬琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:12
+ */
+public interface EnterpriseNewsScopeDeptService extends IService<EnterpriseNewsScopeDept> {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeUserService.java b/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeUserService.java
new file mode 100644
index 0000000..28ac6c7
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeUserService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.collaborativeApproval.service;
+
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿鐢ㄦ埛琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:23
+ */
+public interface EnterpriseNewsScopeUserService extends IService<EnterpriseNewsScopeUser> {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsService.java b/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsService.java
new file mode 100644
index 0000000..21e7e6c
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsService.java
@@ -0,0 +1,29 @@
+package com.ruoyi.collaborativeApproval.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:50:59
+ */
+public interface EnterpriseNewsService extends IService<EnterpriseNews> {
+
+    IPage<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page, EnterpriseNewsDto enterpriseNewsDto);
+
+    Boolean add(EnterpriseNewsDto enterpriseNewsDto);
+
+    Boolean updateEnterpriseNewsDto(EnterpriseNewsDto enterpriseNewsDto);
+
+    Boolean delete(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeDeptServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeDeptServiceImpl.java
new file mode 100644
index 0000000..22283c7
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeDeptServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.collaborativeApproval.service.impl;
+
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper;
+import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeDeptService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿閮ㄩ棬琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:12
+ */
+@Service
+public class EnterpriseNewsScopeDeptServiceImpl extends ServiceImpl<EnterpriseNewsScopeDeptMapper, EnterpriseNewsScopeDept> implements EnterpriseNewsScopeDeptService {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeUserServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeUserServiceImpl.java
new file mode 100644
index 0000000..db1521e
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeUserServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.collaborativeApproval.service.impl;
+
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper;
+import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 浼佷笟鏂伴椈闃呰鑼冨洿鐢ㄦ埛琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:51:23
+ */
+@Service
+public class EnterpriseNewsScopeUserServiceImpl extends ServiceImpl<EnterpriseNewsScopeUserMapper, EnterpriseNewsScopeUser> implements EnterpriseNewsScopeUserService {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsServiceImpl.java
new file mode 100644
index 0000000..2d89e2c
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsServiceImpl.java
@@ -0,0 +1,409 @@
+package com.ruoyi.collaborativeApproval.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.approve.mapper.ApprovalInstanceMapper;
+import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
+import com.ruoyi.approve.pojo.ApprovalInstance;
+import com.ruoyi.approve.pojo.ApprovalTask;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import com.ruoyi.approve.service.ApprovalInstanceService;
+import com.ruoyi.approve.utils.ApproveProcessConfigNodeUtils;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.collaborativeApproval.dto.EnterpriseNewsDto;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser;
+import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeDeptService;
+import com.ruoyi.collaborativeApproval.service.EnterpriseNewsScopeUserService;
+import com.ruoyi.collaborativeApproval.service.EnterpriseNewsService;
+import com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo;
+import com.ruoyi.common.enums.TypeEnums;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserDeptMapper;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 浼佷笟鏂伴椈琛ㄦ湇鍔″疄鐜扮被
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-20 11:50:59
+ */
+@Service
+@RequiredArgsConstructor
+public class EnterpriseNewsServiceImpl extends ServiceImpl<EnterpriseNewsMapper, EnterpriseNews> implements EnterpriseNewsService {
+
+    private static final String READ_SCOPE_ALL = "all";
+    private static final String READ_SCOPE_DEPT = "dept";
+    private static final String READ_SCOPE_CUSTOM = "custom";
+
+    private static final String STATUS_DRAFT = "DRAFT";
+    private static final String STATUS_PENDING = "PENDING";
+    private static final String STATUS_PUBLISHED = "PUBLISHED";
+    private static final String STATUS_REJECTED = "REJECTED";
+    private static final String STATUS_OFFLINE = "OFFLINE";
+
+    private final EnterpriseNewsMapper enterpriseNewsMapper;
+    private final EnterpriseNewsScopeDeptService enterpriseNewsScopeDeptService;
+    private final EnterpriseNewsScopeUserService enterpriseNewsScopeUserService;
+    private final SysUserMapper sysUserMapper;
+    private final SysDeptMapper sysDeptMapper;
+    private final SysUserDeptMapper sysUserDeptMapper;
+    private final ApprovalInstanceMapper approvalInstanceMapper;
+    private final ApprovalInstanceService approvalInstanceService;
+    private final ApprovalTemplateMapper approvalTemplateMapper;
+    private final ApproveProcessConfigNodeUtils approveProcessConfigNodeUtils;
+    private final ISysNoticeService sysNoticeService;
+    private final FileUtil fileUtil;
+
+    @Override
+    public IPage<EnterpriseNewsVo> listPage(Page<EnterpriseNewsVo> page, EnterpriseNewsDto enterpriseNewsDto) {
+        IPage<EnterpriseNewsVo> enterpriseNewsVoIPage = enterpriseNewsMapper.listPage(page, enterpriseNewsDto);
+        enterpriseNewsVoIPage.getRecords().forEach(enterpriseNewsVo -> {
+            enterpriseNewsVo.setStorageBlobDTOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNewsVo.getId()));
+        });
+        return enterpriseNewsVoIPage;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(EnterpriseNewsDto enterpriseNewsDto) {
+        validateForSave(enterpriseNewsDto);
+        String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope());
+        List<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds());
+        List<Long> userIds = distinctIds(enterpriseNewsDto.getUserIds());
+
+        EnterpriseNews enterpriseNews = new EnterpriseNews();
+        BeanUtils.copyProperties(enterpriseNewsDto, enterpriseNews);
+        enterpriseNews.setReadScope(readScope);
+        enterpriseNews.setIsRequired(enterpriseNewsDto.getIsRequired() == null ? (byte) 0 : enterpriseNewsDto.getIsRequired());
+        enterpriseNews.setStatus(normalizeStatus(enterpriseNewsDto.getStatus(), STATUS_DRAFT));
+        enterpriseNews.setReadCount(0);
+        enterpriseNews.setRequiredReadCount(calculateRequiredReadCount(readScope, deptIds, userIds));
+
+        Long[] loginDeptIds = SecurityUtils.getDeptId();
+        if (StringUtils.isNotEmpty(loginDeptIds)) {
+            enterpriseNews.setDeptId(loginDeptIds[0]);
+        }
+
+        if (!save(enterpriseNews) || enterpriseNews.getId() == null) {
+            throw new ServiceException("鏂板浼佷笟鏂伴椈澶辫触");
+        }
+
+        saveReadScopeRelations(enterpriseNews.getId(), readScope, deptIds, userIds);
+        if (STATUS_PENDING.equals(enterpriseNews.getStatus())) {
+            startEnterpriseNewsApproval(enterpriseNews, enterpriseNewsDto);
+        }
+        //娣诲姞闄勪欢
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNews.getId(), enterpriseNewsDto.getStorageBlobDTOs());
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateEnterpriseNewsDto(EnterpriseNewsDto enterpriseNewsDto) {
+        if (enterpriseNewsDto == null || enterpriseNewsDto.getId() == null) {
+            throw new ServiceException("浼佷笟鏂伴椈ID涓嶈兘涓虹┖");
+        }
+
+        EnterpriseNews oldEnterpriseNews = getById(enterpriseNewsDto.getId());
+        if (oldEnterpriseNews == null) {
+            throw new ServiceException("浼佷笟鏂伴椈涓嶅瓨鍦�");
+        }
+        if (!STATUS_DRAFT.equals(oldEnterpriseNews.getStatus())
+                && !STATUS_REJECTED.equals(oldEnterpriseNews.getStatus())) {
+            throw new ServiceException("寰呭鎵规垨宸插彂甯冪殑浼佷笟鏂伴椈涓嶅厑璁镐慨鏀�");
+        }
+
+        validateForSave(enterpriseNewsDto);
+        String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope());
+        List<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds());
+        List<Long> userIds = distinctIds(enterpriseNewsDto.getUserIds());
+
+        EnterpriseNews enterpriseNews = new EnterpriseNews();
+        BeanUtils.copyProperties(enterpriseNewsDto, enterpriseNews);
+        enterpriseNews.setReadScope(readScope);
+        enterpriseNews.setIsRequired(enterpriseNewsDto.getIsRequired() == null ? oldEnterpriseNews.getIsRequired() : enterpriseNewsDto.getIsRequired());
+        enterpriseNews.setStatus(normalizeStatus(enterpriseNewsDto.getStatus(), oldEnterpriseNews.getStatus()));
+        enterpriseNews.setReadCount(oldEnterpriseNews.getReadCount());
+        enterpriseNews.setRequiredReadCount(calculateRequiredReadCount(readScope, deptIds, userIds));
+        enterpriseNews.setCreateUser(oldEnterpriseNews.getCreateUser());
+        enterpriseNews.setCreateTime(oldEnterpriseNews.getCreateTime());
+        enterpriseNews.setDeptId(oldEnterpriseNews.getDeptId());
+
+        if (!updateById(enterpriseNews)) {
+            throw new ServiceException("淇敼浼佷笟鏂伴椈澶辫触");
+        }
+
+        clearReadScopeRelations(enterpriseNews.getId());
+        saveReadScopeRelations(enterpriseNews.getId(), readScope, deptIds, userIds);
+        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.ENTERPRISE_NEWS, enterpriseNews.getId(), enterpriseNewsDto.getStorageBlobDTOs());
+        if (STATUS_PENDING.equals(enterpriseNews.getStatus())) {
+            resetEnterpriseNewsApprovalFlow(oldEnterpriseNews);
+            startEnterpriseNewsApproval(enterpriseNews, enterpriseNewsDto);
+        }
+        return true;
+    }
+
+    @Override
+    public Boolean delete(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return false;
+        }
+        if (!removeByIds(ids)) {
+            throw new ServiceException("鍒犻櫎浼佷笟鏂伴椈澶辫触");
+        }
+        ids.forEach(this::clearReadScopeRelations);
+        return true;
+    }
+
+    private void validateForSave(EnterpriseNewsDto enterpriseNewsDto) {
+        if (enterpriseNewsDto == null) {
+            throw new ServiceException("浼佷笟鏂伴椈鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(enterpriseNewsDto.getTitle())) {
+            throw new ServiceException("鏍囬涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(enterpriseNewsDto.getContent())) {
+            throw new ServiceException("姝f枃涓嶈兘涓虹┖");
+        }
+
+        normalizeStatus(enterpriseNewsDto.getStatus(), STATUS_DRAFT);
+        String readScope = normalizeReadScope(enterpriseNewsDto.getReadScope());
+        List<Long> deptIds = distinctIds(enterpriseNewsDto.getDeptIds());
+        List<Long> userIds = distinctIds(enterpriseNewsDto.getUserIds());
+
+        if (READ_SCOPE_DEPT.equals(readScope) && StringUtils.isEmpty(deptIds)) {
+            throw new ServiceException("璇烽�夋嫨闃呰鑼冨洿閮ㄩ棬");
+        }
+        if (READ_SCOPE_CUSTOM.equals(readScope) && StringUtils.isEmpty(userIds)) {
+            throw new ServiceException("璇烽�夋嫨鑷畾涔夐槄璇讳汉鍛�");
+        }
+
+        validateDeptIds(deptIds);
+        validateUserIds(userIds);
+    }
+
+    private String normalizeReadScope(String readScope) {
+        String normalized = StringUtils.isEmpty(readScope) ? READ_SCOPE_ALL : readScope.trim();
+        if (!READ_SCOPE_ALL.equals(normalized)
+                && !READ_SCOPE_DEPT.equals(normalized)
+                && !READ_SCOPE_CUSTOM.equals(normalized)) {
+            throw new ServiceException("闃呰鑼冨洿涓嶅悎娉�");
+        }
+        return normalized;
+    }
+
+    private String normalizeStatus(String status, String defaultStatus) {
+        String normalized = StringUtils.isEmpty(status) ? defaultStatus : status.trim().toUpperCase();
+        if (!STATUS_DRAFT.equals(normalized)
+                && !STATUS_PENDING.equals(normalized)
+                && !STATUS_PUBLISHED.equals(normalized)
+                && !STATUS_REJECTED.equals(normalized)
+                && !STATUS_OFFLINE.equals(normalized)) {
+            throw new ServiceException("浼佷笟鏂伴椈鐘舵�佷笉鍚堟硶");
+        }
+        return normalized;
+    }
+
+    private void validateDeptIds(List<Long> deptIds) {
+        if (StringUtils.isEmpty(deptIds)) {
+            return;
+        }
+        for (Long deptId : deptIds) {
+            SysDept sysDept = sysDeptMapper.selectDeptById(deptId);
+            if (deptId == null || sysDept == null) {
+                throw new ServiceException("闃呰鑼冨洿閮ㄩ棬涓嶅瓨鍦�");
+            }
+        }
+    }
+
+    private void validateUserIds(List<Long> userIds) {
+        if (StringUtils.isEmpty(userIds)) {
+            return;
+        }
+        List<SysUser> users = sysUserMapper.selectUsersByIds(userIds);
+        if (users.size() != userIds.size()) {
+            throw new ServiceException("鑷畾涔夐槄璇讳汉鍛樺寘鍚棤鏁堢敤鎴�");
+        }
+    }
+
+    private Integer calculateRequiredReadCount(String readScope, List<Long> deptIds, List<Long> userIds) {
+        if (READ_SCOPE_ALL.equals(readScope)) {
+            Long count = sysUserMapper.selectCount(new LambdaQueryWrapper<SysUser>()
+                    .eq(SysUser::getDelFlag, "0"));
+            return count == null ? 0 : count.intValue();
+        }
+        if (READ_SCOPE_DEPT.equals(readScope)) {
+            List<Long> allDeptIds = collectDeptIdsWithChildren(deptIds);
+            if (StringUtils.isEmpty(allDeptIds)) {
+                return 0;
+            }
+            Long count = sysUserDeptMapper.countDistinctUserIdsByDeptIds(allDeptIds);
+            return count == null ? 0 : count.intValue();
+        }
+        return userIds.size();
+    }
+
+    private List<Long> collectDeptIdsWithChildren(List<Long> deptIds) {
+        Set<Long> allDeptIds = new LinkedHashSet<>();
+        for (Long deptId : deptIds) {
+            if (deptId == null) {
+                continue;
+            }
+            allDeptIds.add(deptId);
+            List<SysDept> children = sysDeptMapper.selectChildrenDeptById(deptId);
+            if (StringUtils.isNotEmpty(children)) {
+                for (SysDept child : children) {
+                    allDeptIds.add(child.getDeptId());
+                }
+            }
+        }
+        return new ArrayList<>(allDeptIds);
+    }
+
+    private void saveReadScopeRelations(Long newsId, String readScope, List<Long> deptIds, List<Long> userIds) {
+        if (READ_SCOPE_DEPT.equals(readScope)) {
+            List<EnterpriseNewsScopeDept> scopeDeptList = new ArrayList<>();
+            for (Long deptId : deptIds) {
+                EnterpriseNewsScopeDept scopeDept = new EnterpriseNewsScopeDept();
+                scopeDept.setNewsId(newsId);
+                scopeDept.setDeptId(deptId);
+                scopeDeptList.add(scopeDept);
+            }
+            if (StringUtils.isNotEmpty(scopeDeptList)) {
+                enterpriseNewsScopeDeptService.saveBatch(scopeDeptList);
+            }
+            return;
+        }
+
+        if (READ_SCOPE_CUSTOM.equals(readScope)) {
+            List<EnterpriseNewsScopeUser> scopeUserList = new ArrayList<>();
+            for (Long userId : userIds) {
+                EnterpriseNewsScopeUser scopeUser = new EnterpriseNewsScopeUser();
+                scopeUser.setNewsId(newsId);
+                scopeUser.setUserId(userId);
+                scopeUserList.add(scopeUser);
+            }
+            if (StringUtils.isNotEmpty(scopeUserList)) {
+                enterpriseNewsScopeUserService.saveBatch(scopeUserList);
+            }
+        }
+    }
+
+    private void clearReadScopeRelations(Long newsId) {
+        enterpriseNewsScopeDeptService.remove(new LambdaQueryWrapper<EnterpriseNewsScopeDept>()
+                .eq(EnterpriseNewsScopeDept::getNewsId, newsId));
+        enterpriseNewsScopeUserService.remove(new LambdaQueryWrapper<EnterpriseNewsScopeUser>()
+                .eq(EnterpriseNewsScopeUser::getNewsId, newsId));
+    }
+
+    private void resetEnterpriseNewsApprovalFlow(EnterpriseNews oldEnterpriseNews) {
+        if (oldEnterpriseNews == null || !STATUS_DRAFT.equals(oldEnterpriseNews.getStatus())) {
+            return;
+        }
+        List<Long> approvalInstanceIds = approvalInstanceMapper.selectList(new LambdaQueryWrapper<ApprovalInstance>()
+                        .eq(ApprovalInstance::getBusinessId, oldEnterpriseNews.getId())
+                        .eq(ApprovalInstance::getBusinessType, TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode())
+                        .eq(ApprovalInstance::getDeleted, (byte) 0))
+                .stream()
+                .map(ApprovalInstance::getId)
+                .filter(id -> id != null && id > 0)
+                .collect(Collectors.toList());
+        if (StringUtils.isEmpty(approvalInstanceIds)) {
+            return;
+        }
+        approvalInstanceService.delete(approvalInstanceIds);
+    }
+
+    private List<Long> distinctIds(List<Long> ids) {
+        if (StringUtils.isEmpty(ids)) {
+            return new ArrayList<>();
+        }
+        Set<Long> distinctSet = new LinkedHashSet<>();
+        for (Long id : ids) {
+            if (id != null) {
+                distinctSet.add(id);
+            }
+        }
+        return new ArrayList<>(distinctSet);
+    }
+
+    private void startEnterpriseNewsApproval(EnterpriseNews enterpriseNews, EnterpriseNewsDto enterpriseNewsDto) {
+        if (enterpriseNewsDto.getTemplateId() == null) {
+            throw new ServiceException("瀹℃壒妯℃澘涓嶈兘涓虹┖");
+        }
+
+        String templateName = enterpriseNewsDto.getTemplateName();
+        if (StringUtils.isEmpty(templateName)) {
+            ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectById(enterpriseNewsDto.getTemplateId());
+            if (approvalTemplate == null) {
+                throw new ServiceException("瀹℃壒妯℃澘涓嶅瓨鍦�");
+            }
+            templateName = approvalTemplate.getTemplateName();
+        }
+
+        ApprovalInstance approvalInstance = new ApprovalInstance();
+        approvalInstance.setInstanceNo(OrderUtils.countTodayByCreateTime(approvalInstanceMapper, "SP", "instance_no"));
+        approvalInstance.setTemplateId(enterpriseNewsDto.getTemplateId());
+        approvalInstance.setTemplateName(templateName);
+        approvalInstance.setBusinessId(enterpriseNews.getId());
+        approvalInstance.setBusinessType(TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode());
+        approvalInstance.setTitle(enterpriseNews.getTitle());
+        approvalInstance.setStatus("PENDING");
+        approvalInstance.setCurrentLevel(1);
+        approvalInstance.setApplicantId(SecurityUtils.getUserId());
+        approvalInstance.setApplicantName(SecurityUtils.getLoginUser().getNickName());
+        approvalInstance.setApplyTime(LocalDateTime.now());
+        approvalInstance.setDeleted((byte) 0);
+        approvalInstance.setCreateUser(SecurityUtils.getUserId());
+        approvalInstance.setUpdateUser(SecurityUtils.getUserId());
+        approvalInstance.setDeptId(enterpriseNews.getDeptId());
+        approvalInstanceMapper.insert(approvalInstance);
+
+        approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstance);
+        sendApproveNotice(approvalInstance, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstance.getId()));
+    }
+
+    private void sendApproveNotice(ApprovalInstance instance, List<ApprovalTask> tasks) {
+        if (instance == null || tasks == null || tasks.isEmpty()) {
+            return;
+        }
+        List<Long> approverIds = tasks.stream()
+                .map(ApprovalTask::getApproverId)
+                .filter(id -> id != null && id > 0)
+                .distinct()
+                .collect(Collectors.toList());
+        if (approverIds.isEmpty()) {
+            return;
+        }
+
+        String title = StringUtils.isNotEmpty(instance.getTemplateName()) ? instance.getTemplateName() : "瀹℃壒鎻愰啋";
+        String message = "瀹℃壒鍗曞彿 " + instance.getInstanceNo() + " 闇�瑕佹偍瀹℃壒";
+        String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list/?id=" + instance.getId();
+        sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
+    }
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java b/src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java
new file mode 100644
index 0000000..66123b9
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java
@@ -0,0 +1,15 @@
+package com.ruoyi.collaborativeApproval.vo;
+
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class EnterpriseNewsVo extends EnterpriseNews {
+
+    private String createUserName;
+
+    private List<StorageBlobVO> storageBlobDTOs;
+}
diff --git a/src/main/java/com/ruoyi/common/enums/ApprovalStatusEnum.java b/src/main/java/com/ruoyi/common/enums/ApprovalStatusEnum.java
new file mode 100644
index 0000000..8913ebd
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/ApprovalStatusEnum.java
@@ -0,0 +1,49 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 瀹℃壒鐘舵�佹灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum ApprovalStatusEnum implements BaseEnum<Integer> {
+
+    DRAFT(0, "鑽夌"),
+    PENDING(1, "寰呭鎵�"),
+    IN_PROGRESS(2, "瀹℃壒涓�"),
+    APPROVED(3, "宸查�氳繃"),
+    REJECTED(4, "宸查┏鍥�");
+
+
+    private final Integer value;
+    private final String label;
+
+    @Override
+    public Integer getCode() {
+        return value;
+    }
+
+    @Override
+    public String getValue() {
+        return label;
+    }
+
+    public static ApprovalStatusEnum fromValue(Integer value) {
+        if (value == null) {
+            return null;
+        }
+        for (ApprovalStatusEnum status : values()) {
+            if (status.getCode().equals(value)) {
+                return status;
+            }
+        }
+        return null;
+    }
+
+    public static String getLabelByValue(Integer value) {
+        ApprovalStatusEnum statusEnum = fromValue(value);
+        return statusEnum != null ? statusEnum.getValue() : "鏈煡鐘舵��";
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/enums/EnterpriseNewsStatusEnum.java b/src/main/java/com/ruoyi/common/enums/EnterpriseNewsStatusEnum.java
new file mode 100644
index 0000000..8e6e480
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/EnterpriseNewsStatusEnum.java
@@ -0,0 +1,143 @@
+package com.ruoyi.common.enums;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+
+/**
+ * 浼佷笟鏂伴椈鐘舵�佹灇涓剧被
+ *
+ * @author ruoyi
+ * @date 2026-05-20
+ */
+@Schema(description = "浼佷笟鏂伴椈鐘舵�佹灇涓�")
+public enum EnterpriseNewsStatusEnum implements BaseEnum<String> {
+
+    /**
+     * 鑽夌
+     */
+    @Schema(description = "鑽夌")
+    DRAFT("DRAFT", "鑽夌"),
+
+    /**
+     * 寰呭鎵�
+     */
+    @Schema(description = "寰呭鎵�")
+    PENDING("PENDING", "寰呭鎵�"),
+
+    /**
+     * 宸插彂甯�
+     */
+    @Schema(description = "宸插彂甯�")
+    PUBLISHED("PUBLISHED", "宸插彂甯�"),
+
+    /**
+     * 椹冲洖
+     */
+    @Schema(description = "椹冲洖")
+    REJECTED("REJECTED", "椹冲洖"),
+
+    /**
+     * 宸蹭笅绾�
+     */
+    @Schema(description = "宸蹭笅绾�")
+    OFFLINE("OFFLINE", "宸蹭笅绾�");
+
+    /**
+     * 鐘舵�佺爜
+     */
+    private final String code;
+
+    /**
+     * 鐘舵�佹弿杩�
+     * -- GETTER --
+     *  鑾峰彇鐘舵�佹弿杩�
+     *
+     * @return 鐘舵�佹弿杩�
+
+     */
+    @Getter
+    private final String description;
+
+    EnterpriseNewsStatusEnum(String code, String description) {
+        this.code = code;
+        this.description = description;
+    }
+
+    /**
+     * 鑾峰彇鐘舵�佺爜
+     *
+     * @return 鐘舵�佺爜
+     */
+    @JsonValue
+    public String getCode() {
+        return code;
+    }
+
+    @Override
+    public String getValue() {
+        return "";
+    }
+
+    /**
+     * 鏍规嵁鐘舵�佺爜鑾峰彇鏋氫妇
+     *
+     * @param code 鐘舵�佺爜
+     * @return 鏋氫妇鍊�
+     */
+    @JsonCreator
+    public static EnterpriseNewsStatusEnum getByCode(String code) {
+        for (EnterpriseNewsStatusEnum status : values()) {
+            if (status.code.equals(code)) {
+                return status;
+            }
+        }
+        throw new IllegalArgumentException("Invalid enterprise news status code: " + code);
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓鸿崏绋跨姸鎬�
+     *
+     * @return 鏄惁涓鸿崏绋跨姸鎬�
+     */
+    public boolean isDraft() {
+        return DRAFT.equals(this);
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓哄緟瀹℃壒鐘舵��
+     *
+     * @return 鏄惁涓哄緟瀹℃壒鐘舵��
+     */
+    public boolean isPending() {
+        return PENDING.equals(this);
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓哄凡鍙戝竷鐘舵��
+     *
+     * @return 鏄惁涓哄凡鍙戝竷鐘舵��
+     */
+    public boolean isPublished() {
+        return PUBLISHED.equals(this);
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓洪┏鍥炵姸鎬�
+     *
+     * @return 鏄惁涓洪┏鍥炵姸鎬�
+     */
+    public boolean isRejected() {
+        return REJECTED.equals(this);
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓哄凡涓嬬嚎鐘舵��
+     *
+     * @return 鏄惁涓哄凡涓嬬嚎鐘舵��
+     */
+    public boolean isOffline() {
+        return OFFLINE.equals(this);
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/enums/SalesQuotationStatusEnum.java b/src/main/java/com/ruoyi/common/enums/SalesQuotationStatusEnum.java
new file mode 100644
index 0000000..0789c26
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/SalesQuotationStatusEnum.java
@@ -0,0 +1,43 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 閿�鍞姤浠风姸鎬佹灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum SalesQuotationStatusEnum implements BaseEnum<String> {
+
+    DRAFT("鑽夌", "鑽夌"),
+    PENDING("寰呭鎵�", "寰呭鎵�"),
+    IN_PROGRESS("瀹℃牳涓�", "瀹℃牳涓�"),
+    APPROVED("閫氳繃", "閫氳繃"),
+    REJECTED("鎷掔粷", "鎷掔粷");
+
+    private final String value;
+    private final String label;
+
+    @Override
+    public String getCode() {
+        return value;
+    }
+
+    @Override
+    public String getValue() {
+        return label;
+    }
+
+    public static SalesQuotationStatusEnum fromValue(String value) {
+        if (value == null) {
+            return null;
+        }
+        for (SalesQuotationStatusEnum status : values()) {
+            if (status.getCode().equals(value)) {
+                return status;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/enums/ShippingStatusEnum.java b/src/main/java/com/ruoyi/common/enums/ShippingStatusEnum.java
new file mode 100644
index 0000000..e8155f2
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/ShippingStatusEnum.java
@@ -0,0 +1,42 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 鍙戣揣瀹℃壒鐘舵�佹灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum ShippingStatusEnum implements BaseEnum<String> {
+
+    PENDING("寰呯‘璁�", "寰呯‘璁�"),
+    IN_PROGRESS("瀹℃牳涓�", "瀹℃牳涓�"),
+    APPROVED("瀹℃牳閫氳繃", "瀹℃牳閫氳繃"),
+    REJECTED("瀹℃牳鎷掔粷", "瀹℃牳鎷掔粷");
+
+    private final String value;
+    private final String label;
+
+    @Override
+    public String getCode() {
+        return value;
+    }
+
+    @Override
+    public String getValue() {
+        return label;
+    }
+
+    public static ShippingStatusEnum fromValue(String value) {
+        if (value == null) {
+            return null;
+        }
+        for (ShippingStatusEnum status : values()) {
+            if (status.getCode().equals(value)) {
+                return status;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/enums/TypeEnums.java b/src/main/java/com/ruoyi/common/enums/TypeEnums.java
new file mode 100644
index 0000000..ba1c1f3
--- /dev/null
+++ b/src/main/java/com/ruoyi/common/enums/TypeEnums.java
@@ -0,0 +1,70 @@
+package com.ruoyi.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum TypeEnums implements BaseEnum<Long> {
+
+    PUBLIC_OUT(1L, "鍏嚭绠$悊"),
+    LEAVE(2L, "璇峰亣绠$悊"),
+    BUSINESS_TRIP(3L, "鍑哄樊绠$悊"),
+    REIMBURSEMENT(4L, "鎶ラ攢绠$悊"),
+    PURCHASE_APPROVAL(5L, "閲囪喘瀹℃壒"),
+    QUOTATION_APPROVAL(6L, "鎶ヤ环瀹℃壒"),
+    SHIPPING_APPROVAL(7L, "鍙戣揣瀹℃壒"),
+    DANGEROUS_OPERATION(8L, "鍗遍櫓浣滀笟瀹℃壒"),
+    OFFICE_SUPPLIES(9L, "鍔炲叕鐢ㄥ搧瀹℃壒"),
+    REGULARIZATION_APPROVAL(10L, "杞瀹℃壒"),
+    TRANSFER_APPROVAL(11L, "璋冨姩瀹℃壒"),
+    RESIGNATION_APPROVAL(12L, "绂昏亴瀹℃壒"),
+    WORK_HANDOVER_APPROVAL(13L, "宸ヤ綔浜ゆ帴瀹℃壒"),
+    LEAVE_APPROVAL(14L, "璇峰亣瀹℃壒"),
+    OVERTIME_APPROVAL(15L, "鍔犵彮瀹℃壒"),
+    TRAVEL_REIMBURSEMENT_APPROVAL(16L, "鍑哄樊鎶ラ攢瀹℃壒"),
+    EXPENSE_APPROVAL(17L, "璐圭敤瀹℃壒"),
+    ENTERPRISE_NEWS_APPROVAL(18L, "浼佷笟鏂伴椈瀹℃壒");
+
+
+
+    private final Long value;
+    private final String label;
+
+    @Override
+    public Long getCode() {
+        return value;
+    }
+
+    @Override
+    public String getValue() {
+        return label;
+    }
+
+    /**
+     * 鏍规嵁鍊艰幏鍙栧搴旂殑鏋氫妇
+     * @param value 涓氬姟绫诲瀷鍊�
+     * @return 瀵瑰簲鐨勬灇涓撅紝鏈尮閰嶈繑鍥瀗ull
+     */
+    public static TypeEnums fromValue(Long value) {
+        if (value == null) {
+            return null;
+        }
+        for (TypeEnums type : values()) {
+            if (type.getCode().equals(value)) {
+                return type;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 鏍规嵁鍊艰幏鍙栨弿杩�
+     * @param value 涓氬姟绫诲瀷鍊�
+     * @return 涓氬姟绫诲瀷鎻忚堪锛屾湭鍖归厤杩斿洖"鑷畾涔夊鎵�"
+     */
+    public static String getLabelByValue(Long value) {
+        TypeEnums typeEnum = fromValue(value);
+        return typeEnum != null ? typeEnum.getValue() : "鑷畾涔夊鎵�";
+    }
+}
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java b/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
index 2765c10..833e737 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
@@ -39,7 +39,7 @@
     @PostMapping("/updateRouteItem")
     @Operation(summary = "淇敼鐢熶骇璁㈠崟鐨勫伐鑹鸿矾绾胯鎯�")
     public R updateRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) {
-        return R.ok(productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation));
+        return productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation);
     }
 
     @DeleteMapping("/deleteRouteItem/{id}")
diff --git a/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java b/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
index 8878006..e14b36f 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
@@ -13,4 +13,5 @@
     R deleteRouteItem(Long id);
 
     int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
+
 }
diff --git a/src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java b/src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java
index 8c0ca53..f5baaec 100644
--- a/src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java
+++ b/src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ruoyi.project.system.domain.SysUserDept;
 import com.ruoyi.project.system.domain.vo.SysUserDeptVo;
+import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
@@ -15,4 +16,28 @@
     List<SysUserDeptVo> userLoginFacotryList(@Param("userDeptVo") SysUserDeptVo userDeptVo);
 
     List<Map<String, Object>> setSchemeApplicableStaffUserInfo(@Param("ids") List<Long> ids);
+
+    @Select("<script>" +
+            "select count(distinct sud.user_id) " +
+            "from sys_user_dept sud " +
+            "inner join sys_user su on su.user_id = sud.user_id " +
+            "where su.del_flag = '0' " +
+            "and sud.dept_id in " +
+            "<foreach collection='deptIds' item='deptId' open='(' separator=',' close=')'>" +
+            "#{deptId}" +
+            "</foreach>" +
+            "</script>")
+    Long countDistinctUserIdsByDeptIds(@Param("deptIds") List<Long> deptIds);
+
+    @Select("<script>" +
+            "select distinct sud.user_id " +
+            "from sys_user_dept sud " +
+            "inner join sys_user su on su.user_id = sud.user_id " +
+            "where su.del_flag = '0' " +
+            "and sud.dept_id in " +
+            "<foreach collection='deptIds' item='deptId' open='(' separator=',' close=')'>" +
+            "#{deptId}" +
+            "</foreach>" +
+            "</script>")
+    List<Long> selectDistinctUserIdsByDeptIds(@Param("deptIds") List<Long> deptIds);
 }
diff --git a/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java b/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
index dcbcded..fdb610b 100644
--- a/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
+++ b/src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java
@@ -1,12 +1,14 @@
 package com.ruoyi.project.system.service.impl;
 
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.common.utils.SecurityUtils;
-import com.ruoyi.project.system.domain.SysDept;
-import com.ruoyi.project.system.domain.SysNotice;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper;
+import com.ruoyi.collaborativeApproval.pojo.EnterpriseNews;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.project.system.domain.SysDept;
+import com.ruoyi.project.system.domain.SysNotice;
 import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.domain.SysUserDept;
 import com.ruoyi.project.system.mapper.SysDeptMapper;
@@ -15,11 +17,15 @@
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.project.system.service.ISysNoticeService;
 import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.List;
-import java.util.stream.Collectors;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * 鍏憡 鏈嶅姟灞傚疄鐜�
@@ -28,13 +34,16 @@
  */
 @Service
 @RequiredArgsConstructor
-public class SysNoticeServiceImpl  extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
-
-    private final SysNoticeMapper noticeMapper;
-    private final SysUserMapper userMapper;
-    private final SysDeptMapper deptMapper;
-    private final SysUserDeptMapper userDeptMapper;
-    private final UnipushService unipushService;
+public class SysNoticeServiceImpl  extends ServiceImpl<SysNoticeMapper, SysNotice> implements ISysNoticeService {
+
+    private static final Pattern ENTERPRISE_NEWS_ID_PATTERN = Pattern.compile("[?&]id=(\\d+)");
+
+    private final SysNoticeMapper noticeMapper;
+    private final SysUserMapper userMapper;
+    private final SysDeptMapper deptMapper;
+    private final SysUserDeptMapper userDeptMapper;
+    private final UnipushService unipushService;
+    private final EnterpriseNewsMapper enterpriseNewsMapper;
 
     /**
      * 鏌ヨ鍏憡淇℃伅
@@ -77,11 +86,26 @@
      * @param notice 鍏憡淇℃伅
      * @return 缁撴灉
      */
-    @Override
-    public int updateNotice(SysNotice notice)
-    {
-        return noticeMapper.updateNotice(notice);
-    }
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int updateNotice(SysNotice notice)
+    {
+        if (notice == null || notice.getNoticeId() == null) {
+            return 0;
+        }
+        SysNotice dbNotice = noticeMapper.selectNoticeById(notice.getNoticeId());
+        if (dbNotice == null) {
+            return 0;
+        }
+        boolean needSyncNewsReadCount = isEnterpriseNewsNotice(dbNotice)
+                && notice.getStatus() != null
+                && !notice.getStatus().equals(dbNotice.getStatus());
+        int rows = noticeMapper.updateNotice(notice);
+        if (rows > 0 && needSyncNewsReadCount) {
+            syncEnterpriseNewsReadCount(dbNotice.getJumpPath());
+        }
+        return rows;
+    }
 
     /**
      * 鍒犻櫎鍏憡瀵硅薄
@@ -115,13 +139,20 @@
     }
 
     @Override
-    public int readAll() {
-        Long userId = SecurityUtils.getUserId();
-        return noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
-                .eq(SysNotice::getConsigneeId, userId)
-                .eq(SysNotice::getStatus, "0")
-                .set(SysNotice::getStatus, "1"));
-    }
+    public int readAll() {
+        Long userId = SecurityUtils.getUserId();
+        List<SysNotice> unreadNotices = noticeMapper.selectList(Wrappers.<SysNotice>lambdaQuery()
+                .eq(SysNotice::getConsigneeId, userId)
+                .eq(SysNotice::getStatus, "0"));
+        int rows = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
+                .eq(SysNotice::getConsigneeId, userId)
+                .eq(SysNotice::getStatus, "0")
+                .set(SysNotice::getStatus, "1"));
+        if (rows > 0) {
+            syncEnterpriseNewsReadCount(unreadNotices);
+        }
+        return rows;
+    }
 
     @Override
     public void simpleNoticeByUser(String title, String message, List<Long> consigneeId, String jumpPath) {
@@ -211,18 +242,72 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean appReadNotice(Long noticeId) {
-        if (noticeId == null) {
-            return false;
+    public boolean appReadNotice(Long noticeId) {
+        if (noticeId == null) {
+            return false;
         }
         SysNotice sysNotice = noticeMapper.selectNoticeById(noticeId);
         if (sysNotice == null) {
             return false;
-        }
-        sysNotice.setStatus("1");
-        return noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
-                .eq(SysNotice::getNoticeId, noticeId)
-                .eq(SysNotice::getStatus, "0")
-                .set(SysNotice::getStatus, "1")) > 0;
-    }
-}
+        }
+        sysNotice.setStatus("1");
+        boolean updated = noticeMapper.update(null, Wrappers.<SysNotice>lambdaUpdate()
+                .eq(SysNotice::getNoticeId, noticeId)
+                .eq(SysNotice::getStatus, "0")
+                .set(SysNotice::getStatus, "1")) > 0;
+        if (updated) {
+            syncEnterpriseNewsReadCount(sysNotice.getJumpPath());
+        }
+        return updated;
+    }
+
+    private boolean isEnterpriseNewsNotice(SysNotice sysNotice) {
+        return sysNotice != null
+                && sysNotice.getJumpPath() != null
+                && sysNotice.getJumpPath().startsWith("/enterpriseNews?id=");
+    }
+
+    private void syncEnterpriseNewsReadCount(List<SysNotice> notices) {
+        if (notices == null || notices.isEmpty()) {
+            return;
+        }
+        Set<String> jumpPaths = new HashSet<>();
+        for (SysNotice notice : notices) {
+            if (isEnterpriseNewsNotice(notice)) {
+                jumpPaths.add(notice.getJumpPath());
+            }
+        }
+        for (String jumpPath : jumpPaths) {
+            syncEnterpriseNewsReadCount(jumpPath);
+        }
+    }
+
+    private void syncEnterpriseNewsReadCount(String jumpPath) {
+        Long newsId = parseEnterpriseNewsId(jumpPath);
+        if (newsId == null) {
+            return;
+        }
+        long readCount = noticeMapper.selectCount(Wrappers.<SysNotice>lambdaQuery()
+                .eq(SysNotice::getStatus, "1")
+                .eq(SysNotice::getJumpPath, jumpPath));
+        EnterpriseNews enterpriseNews = new EnterpriseNews();
+        enterpriseNews.setId(newsId);
+        enterpriseNews.setReadCount((int) readCount);
+        enterpriseNewsMapper.updateById(enterpriseNews);
+    }
+
+    private Long parseEnterpriseNewsId(String jumpPath) {
+        if (jumpPath == null || !jumpPath.startsWith("/enterpriseNews")) {
+            return null;
+        }
+        Matcher matcher = ENTERPRISE_NEWS_ID_PATTERN.matcher(jumpPath);
+        if (!matcher.find()) {
+            return null;
+        }
+        try {
+            return Long.parseLong(matcher.group(1));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java b/src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
index b55242c..980c6bc 100644
--- a/src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
+++ b/src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
@@ -158,4 +158,7 @@
     @TableField(fill = FieldFill.INSERT)
     private Long deptId;
 
+    @Schema(description = "妯℃澘id")
+    private Long templateId;
+
 }
diff --git a/src/main/java/com/ruoyi/quality/utils/QualityInspectHelper.java b/src/main/java/com/ruoyi/quality/utils/QualityInspectHelper.java
new file mode 100644
index 0000000..766515a
--- /dev/null
+++ b/src/main/java/com/ruoyi/quality/utils/QualityInspectHelper.java
@@ -0,0 +1,73 @@
+package com.ruoyi.quality.utils;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.purchase.pojo.PurchaseLedger;
+import com.ruoyi.quality.mapper.QualityInspectMapper;
+import com.ruoyi.quality.mapper.QualityInspectParamMapper;
+import com.ruoyi.quality.mapper.QualityTestStandardMapper;
+import com.ruoyi.quality.mapper.QualityTestStandardParamMapper;
+import com.ruoyi.quality.pojo.QualityInspect;
+import com.ruoyi.quality.pojo.QualityInspectParam;
+import com.ruoyi.quality.pojo.QualityTestStandard;
+import com.ruoyi.quality.pojo.QualityTestStandardParam;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 璐ㄦ鍗曞垱寤哄伐鍏风被
+ */
+@Component
+@RequiredArgsConstructor
+public class QualityInspectHelper {
+
+    private final QualityInspectMapper qualityInspectMapper;
+    private final QualityTestStandardMapper qualityTestStandardMapper;
+    private final QualityTestStandardParamMapper qualityTestStandardParamMapper;
+    private final QualityInspectParamMapper qualityInspectParamMapper;
+
+    /**
+     * 鍒涘缓璐ㄦ鍗�
+     * @param purchaseLedger 閲囪喘鍙拌处
+     * @param saleProduct 閲囪喘浜у搧
+     */
+    public void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) {
+        QualityInspect qualityInspect = new QualityInspect();
+        qualityInspect.setInspectType(0);
+        qualityInspect.setSupplier(purchaseLedger.getSupplierName());
+        qualityInspect.setPurchaseLedgerId(purchaseLedger.getId());
+        qualityInspect.setProductId(saleProduct.getProductId());
+        qualityInspect.setProductName(saleProduct.getProductCategory());
+        qualityInspect.setModel(saleProduct.getSpecificationModel());
+        qualityInspect.setProductModelId(saleProduct.getProductModelId());
+        qualityInspect.setUnit(saleProduct.getUnit());
+        qualityInspect.setQuantity(saleProduct.getQuantity());
+        qualityInspectMapper.insert(qualityInspect);
+
+        List<QualityTestStandard> qualityTestStandardList = qualityTestStandardMapper
+                .getQualityTestStandardByProductId(saleProduct.getProductId(), 0, null);
+
+        if (qualityTestStandardList.isEmpty()) {
+            return;
+        }
+
+        QualityTestStandard firstStandard = qualityTestStandardList.get(0);
+        qualityInspect.setTestStandardId(firstStandard.getId());
+        qualityInspectMapper.updateById(qualityInspect);
+
+        List<QualityTestStandardParam> standardParams = qualityTestStandardParamMapper.selectList(
+                Wrappers.<QualityTestStandardParam>lambdaQuery()
+                        .eq(QualityTestStandardParam::getTestStandardId, firstStandard.getId()));
+
+        for (QualityTestStandardParam standardParam : standardParams) {
+            QualityInspectParam param = new QualityInspectParam();
+            BeanUtils.copyProperties(standardParam, param);
+            param.setId(null);
+            param.setInspectId(qualityInspect.getId());
+            qualityInspectParamMapper.insert(param);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java b/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
index 4841ffd..4b20ff8 100644
--- a/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
@@ -17,4 +17,6 @@
      */
     // 瀹℃壒浜�
     private String approveUserIds;
+
+    private Long templateId;
 }
diff --git a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
index 43409bc..724473a 100644
--- a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -47,5 +47,9 @@
     //鍙戣揣鏁伴噺
     private BigDecimal totalQuantity;
 
+    private Long templateId;
+
+    private String templateName;
+
 
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
index de86ad0..983e717 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java
@@ -7,10 +7,13 @@
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.approve.pojo.ApproveProcess;
-import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
+import com.ruoyi.approve.bean.dto.ApprovalInstanceDto;
 import com.ruoyi.approve.bean.vo.ApproveGetAndUpdateVo;
 import com.ruoyi.approve.bean.vo.ApproveProcessVO;
+import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
+import com.ruoyi.approve.pojo.ApproveProcess;
+import com.ruoyi.approve.service.ApprovalInstanceService;
+import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
 import com.ruoyi.basic.mapper.CustomerMapper;
 import com.ruoyi.basic.pojo.Customer;
 import com.ruoyi.common.enums.IsDeleteEnum;
@@ -30,8 +33,8 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDate;
-import java.util.Collections;
-import java.util.List;
+import java.time.LocalDateTime;
+import java.util.*;
 import java.util.stream.Collectors;
 
 @Service
@@ -44,6 +47,8 @@
 
     private final ApproveProcessServiceImpl approveProcessService;
     private final CustomerMapper customerMapper;
+    private final ApprovalTemplateMapper approvalTemplateMapper;
+    private final ApprovalInstanceService approvalInstanceService;
 
     @Override
     public IPage<SalesQuotationDto> listPage(Page page, SalesQuotationDto salesQuotationDto) {
@@ -51,10 +56,26 @@
         if(CollectionUtils.isEmpty(salesQuotationDtoIPage.getRecords())){
             return salesQuotationDtoIPage;
         }
-        salesQuotationDtoIPage.getRecords().forEach(record -> {
-            List<SalesQuotationProduct> products = salesQuotationProductMapper.selectBySalesQuotationId(record.getId());
-            record.setProducts(products);
-        });
+
+        // 鎵归噺鏌ヨ浜у搧锛岄伩鍏� N+1 闂
+        List<Long> quotationIds = salesQuotationDtoIPage.getRecords().stream()
+                .map(SalesQuotationDto::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!quotationIds.isEmpty()) {
+            List<SalesQuotationProduct> allProducts = salesQuotationProductMapper.selectList(
+                    new LambdaQueryWrapper<SalesQuotationProduct>()
+                            .in(SalesQuotationProduct::getSalesQuotationId, quotationIds)
+            );
+
+            Map<Long, List<SalesQuotationProduct>> productMap = allProducts.stream()
+                    .collect(Collectors.groupingBy(SalesQuotationProduct::getSalesQuotationId));
+
+            salesQuotationDtoIPage.getRecords().forEach(record ->
+                    record.setProducts(productMap.getOrDefault(record.getId(), new ArrayList<>()))
+            );
+        }
         return salesQuotationDtoIPage;
     }
 
@@ -93,10 +114,21 @@
         approveProcessVO.setPrice(salesQuotationDto.getTotalAmount());
         try {
             approveProcessService.addApprove(approveProcessVO);
-        }catch (Exception e){
-            log.error("SalesQuotationServiceImpl error:{}", e);
-            throw                                new RuntimeException("瀹℃壒澶辫触");
+        } catch (Exception e) {
+            log.error("SalesQuotationServiceImpl approve error for quotationNo: {}", e);
+            throw new RuntimeException("瀹℃壒澶辫触: " + e.getMessage(), e);
         }
+        // 鎶ヤ环瀹℃壒
+        ApprovalInstanceDto approvalInstanceDto = new ApprovalInstanceDto();
+        approvalInstanceDto.setTemplateId(salesQuotationDto.getTemplateId());
+        approvalInstanceDto.setBusinessId(salesQuotationDto.getId());
+        approvalInstanceDto.setBusinessType(7L);
+        approvalInstanceDto.setTitle("鎶ヤ环缂栧彿锛�" + quotationNo);
+        approvalInstanceDto.setApplicantId(SecurityUtils.getUserId());
+        approvalInstanceDto.setTemplateName(approvalTemplateMapper.selectById(salesQuotationDto.getTemplateId()).getTemplateName());
+        approvalInstanceDto.setApplicantName(SecurityUtils.getLoginUser().getNickName());
+        approvalInstanceDto.setApplyTime(LocalDateTime.now());
+        approvalInstanceService.add(approvalInstanceDto);
         return true;
     }
     @Override
diff --git a/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java b/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
index ab27019..8eb5334 100644
--- a/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
+++ b/src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java
@@ -39,7 +39,7 @@
      * @return
      */
     @GetMapping("/listPage")
-    public AjaxResult staffOnJobListPage(Page page, StaffOnJob staffOnJob) {
+    public AjaxResult staffOnJobListPage(Page page, StaffOnJobDto staffOnJob) {
         return AjaxResult.success(staffOnJobService.staffOnJobListPage(page, staffOnJob));
     }
 
diff --git a/src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java b/src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java
index 0a2c0ae..69fdbe8 100644
--- a/src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java
+++ b/src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java
@@ -25,4 +25,6 @@
     @JsonFormat(pattern = "yyyy-MM-dd")
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     private Date contractEndTime;
+
+    private Long sysDeptId;
 }
diff --git a/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java b/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
index de8a3c0..7cad87f 100644
--- a/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java
@@ -14,7 +14,7 @@
 @Mapper
 public interface StaffOnJobMapper extends BaseMapper<StaffOnJob> {
 
-    IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJob staffOnJob);
+    IPage<StaffOnJobDto> staffOnJobListPage(Page page, @Param("staffOnJob") StaffOnJobDto staffOnJob);
 
     List<StaffOnJobDto> staffOnJobList(@Param("staffOnJob") StaffOnJob staffOnJob);
 
@@ -42,4 +42,4 @@
      * @return 鍛樺伐鏁版嵁
      */
     StaffOnJob selectStaffByNickName(String staffName);
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java b/src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java
index a859d14..46588d1 100644
--- a/src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java
+++ b/src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java
@@ -14,7 +14,7 @@
 public interface IStaffOnJobService extends IService<StaffOnJob> {
 
 
-    IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob);
+    IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJobDto staffOnJob);
 
      StaffOnJobDto staffOnJobDetail(Long id);
 
diff --git a/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
index 548323d..4978d0f 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java
@@ -59,6 +59,7 @@
         StaffLeave staffLeave = new StaffLeave();
         staffLeave.setStaffOnJobId(staffLeaveDto.getStaffOnJobId());
         staffLeave.setReason(staffLeaveDto.getReason());
+        staffLeave.setLeaveDate(staffLeaveDto.getLeaveDate());
         String reason = staffLeaveDto.getReason();
         if (StaffLeaveReasonOther.getCode().equals(reason)){
             staffLeave.setRemark(staffLeaveDto.getRemark());
diff --git a/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
index 7c11041..1f20da2 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
@@ -7,6 +7,7 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.dto.WordDateDto;
 import com.ruoyi.project.system.domain.SysDept;
@@ -67,8 +68,9 @@
 
     //鍦ㄨ亴鍛樺伐鍙拌处鍒嗛〉鏌ヨ
     @Override
-    public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob) {
-        return staffOnJobMapper.staffOnJobListPage(page,staffOnJob);
+    public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJobDto staffOnJob) {
+        IPage<StaffOnJobDto> staffOnJobDtoIPage = staffOnJobMapper.staffOnJobListPage(page, staffOnJob);
+        return staffOnJobDtoIPage;
     }
 
     //鏂板鍏ヨ亴
@@ -83,6 +85,7 @@
         }
 
         // 鍒涘缓鍏ヨ亴鏁版嵁
+        syncStudyInfoFromEducation(staffOnJobPrams);
         staffOnJobPrams.setContractExpireTime(staffOnJobPrams.getContractEndTime());
         staffOnJobPrams.setStaffState(1);
         staffOnJobMapper.insert(staffOnJobPrams);
@@ -141,6 +144,7 @@
         // 缁戝畾瀛愯〃鏁版嵁
         bingingStaffOnJobExtra(id,staffOnJobParams);
         // 鏇存柊鍛樺伐鏁版嵁
+        syncStudyInfoFromEducation(staffOnJobParams);
         staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime());
         return staffOnJobMapper.updateById(staffOnJobParams);
     }
@@ -158,6 +162,7 @@
                     .forEach(staff -> staff.setStaffOnJobId(id)); // 璧嬪��
             staffEducationService.saveBatch(staffOnJobPrams.getStaffEducationList());
         }
+
         // 鏂板宸ヤ綔缁忓巻
         if(CollectionUtils.isNotEmpty(staffOnJobPrams.getStaffWorkExperienceList())){
             staffOnJobPrams.getStaffWorkExperienceList().stream()
@@ -174,6 +179,28 @@
         }
     }
 
+    private void syncStudyInfoFromEducation(StaffOnJob staffOnJobPrams) {
+        if (staffOnJobPrams == null || CollectionUtils.isEmpty(staffOnJobPrams.getStaffEducationList())) {
+            if (staffOnJobPrams != null) {
+                staffOnJobPrams.setFirstStudy("/");
+                staffOnJobPrams.setProfession("/");
+            }
+            return;
+        }
+        Optional<StaffEducation> matchedEducation = staffOnJobPrams.getStaffEducationList().stream()
+                .filter(Objects::nonNull)
+                .filter(education -> StringUtils.isNotEmpty(education.getMajor()))
+                .findFirst();
+        if (matchedEducation.isPresent()) {
+            StaffEducation education = matchedEducation.get();
+            staffOnJobPrams.setFirstStudy(education.getEducation());
+            staffOnJobPrams.setProfession(education.getMajor());
+            return;
+        }
+        staffOnJobPrams.setFirstStudy("/");
+        staffOnJobPrams.setProfession("/");
+    }
+
 
     /**
      * 閫氳繃鍛樺伐id鍒犻櫎鏁欒偛缁忓巻锛屽伐浣滅粡鍘嗭紝绱ф�ヨ仈绯讳汉
diff --git a/src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java b/src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java
index c24c93f..ecf4f38 100644
--- a/src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java
+++ b/src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java
@@ -24,28 +24,28 @@
     private final TechnologyOperationService technologyOperationService;
 
     @GetMapping("/listPage")
-    @Log(title = "Technology operation page", businessType = BusinessType.OTHER)
+    @Log(title = "宸ュ簭鍒嗛〉鏌ヨ", businessType = BusinessType.OTHER)
     @Operation(summary = "宸ュ簭鍒嗛〉鏌ヨ")
     public R<IPage<TechnologyOperationVo>> listPage(Page<TechnologyOperationDto> page, TechnologyOperationDto technologyOperationDto) {
         return R.ok(technologyOperationService.listPage(page, technologyOperationDto));
     }
 
     @PostMapping("/add")
-    @Log(title = "Add technology operation", businessType = BusinessType.INSERT)
+    @Log(title = "宸ュ簭鏂板", businessType = BusinessType.INSERT)
     @Operation(summary = "鏂板宸ュ簭")
     public R add(@RequestBody TechnologyOperationDto technologyOperationDto) {
         return technologyOperationService.add(technologyOperationDto);
     }
 
     @PutMapping("/update")
-    @Log(title = "Update technology operation", businessType = BusinessType.UPDATE)
+    @Log(title = "宸ュ簭鏇存柊", businessType = BusinessType.UPDATE)
     @Operation(summary = "淇敼宸ュ簭")
     public R update(@RequestBody com.ruoyi.technology.pojo.TechnologyOperation technologyOperation) {
         return R.ok(technologyOperationService.updateById(technologyOperation));
     }
 
     @DeleteMapping("/batchDelete")
-    @Log(title = "Delete technology operation", businessType = BusinessType.DELETE)
+    @Log(title = "鍒犻櫎宸ュ簭", businessType = BusinessType.DELETE)
     @Operation(summary = "鎵归噺鍒犻櫎宸ュ簭")
     public R batchDelete(@RequestBody List<Long> ids) {
         return R.ok(technologyOperationService.batchDelete(ids));
diff --git a/src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java b/src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java
index 061a50c..92bb773 100644
--- a/src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java
+++ b/src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java
@@ -36,7 +36,7 @@
     }
 
     @DeleteMapping("/batchDelete/{id}")
-    @Log(title = "Delete technology operation param", businessType = BusinessType.DELETE)
+    @Log(title = "鍒犻櫎宸ュ簭鍙傛暟", businessType = BusinessType.DELETE)
     @Operation(summary = "鍒犻櫎宸ュ簭鍙傛暟")
     public AjaxResult batchDelete(@PathVariable("id") Long id) {
         return AjaxResult.success(technologyOperationParamService.batchDelete(id));
diff --git a/src/main/resources/mapper/approve/ApprovalInstanceMapper.xml b/src/main/resources/mapper/approve/ApprovalInstanceMapper.xml
new file mode 100644
index 0000000..612f457
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalInstanceMapper.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalInstanceMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalInstance">
+        <id column="id" property="id" />
+        <result column="instance_no" property="instanceNo" />
+        <result column="template_id" property="templateId" />
+        <result column="template_name" property="templateName" />
+        <result column="business_id" property="businessId" />
+        <result column="business_type" property="businessType" />
+        <result column="title" property="title" />
+        <result column="status" property="status" />
+        <result column="current_level" property="currentLevel" />
+        <result column="applicant_id" property="applicantId" />
+        <result column="applicant_name" property="applicantName" />
+        <result column="apply_time" property="applyTime" />
+        <result column="finish_time" property="finishTime" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="deleted" property="deleted" />
+    </resultMap>
+    <select id="listPage" resultType="com.ruoyi.approve.bean.vo.ApprovalInstanceVo">
+        select ai.*,su.nick_name as create_user_name from
+        approval_instance ai
+        left join sys_user su on ai.create_user = su.user_id
+        <where>
+            deleted = 0
+            <if test="ew.instanceNo != null">
+                and ai.instance_no like concat('%',#{ew.instanceNo},'%')
+            </if>
+            <if test="ew.templateName != null">
+                and ai.template_name like concat('%',#{ew.templateName},'%')
+            </if>
+            <if test="ew.templateId != null ">
+                and ai. template_id = #{ew.templateId}
+            </if>
+            <if test="ew.businessType != null ">
+                and ai.business_type = #{ew.businessType}
+            </if>
+            <if test="ew.createTimeStart != null and ew.createTimeEnd != null">
+                and ai.apply_time &gt;= #{ew.createTimeStart}
+                and ai.apply_time &lt;= #{ew.createTimeEnd}
+            </if>
+            <if test="ew.status != null">
+                and ai.status = #{ew.status}
+            </if>
+            <if test="ew.applicantName != null and ew.applicantName !=''">
+                and ai.applicant_name = #{ew.applicantName}
+            </if>
+        </where>
+        order by ai.id desc
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/ApprovalInstanceNodeMapper.xml b/src/main/resources/mapper/approve/ApprovalInstanceNodeMapper.xml
new file mode 100644
index 0000000..560271b
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalInstanceNodeMapper.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalInstanceNodeMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalInstanceNode">
+        <id column="id" property="id" />
+        <result column="instance_id" property="instanceId" />
+        <result column="level_no" property="levelNo" />
+        <result column="approve_type" property="approveType" />
+        <result column="status" property="status" />
+        <result column="start_time" property="startTime" />
+        <result column="finish_time" property="finishTime" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="deleted" property="deleted" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/ApprovalRecordMapper.xml b/src/main/resources/mapper/approve/ApprovalRecordMapper.xml
new file mode 100644
index 0000000..db40692
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalRecordMapper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalRecordMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalRecord">
+        <id column="id" property="id" />
+        <result column="instance_id" property="instanceId" />
+        <result column="node_id" property="nodeId" />
+        <result column="task_id" property="taskId" />
+        <result column="operator_id" property="operatorId" />
+        <result column="operator_name" property="operatorName" />
+        <result column="action" property="action" />
+        <result column="comment" property="comment" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="deleted" property="deleted" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/ApprovalTaskMapper.xml b/src/main/resources/mapper/approve/ApprovalTaskMapper.xml
new file mode 100644
index 0000000..c079440
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalTaskMapper.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalTaskMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTask">
+        <id column="id" property="id" />
+        <result column="instance_id" property="instanceId" />
+        <result column="node_id" property="nodeId" />
+        <result column="level_no" property="levelNo" />
+        <result column="approver_id" property="approverId" />
+        <result column="approver_name" property="approverName" />
+        <result column="task_status" property="taskStatus" />
+        <result column="approve_time" property="approveTime" />
+        <result column="comment" property="comment" />
+        <result column="is_read" property="isRead" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="deleted" property="deleted" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/ApprovalTemplateMapper.xml b/src/main/resources/mapper/approve/ApprovalTemplateMapper.xml
new file mode 100644
index 0000000..a48432b
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalTemplateMapper.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalTemplateMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTemplate">
+        <id column="id" property="id" />
+        <result column="template_name" property="templateName" />
+        <result column="enabled" property="enabled" />
+        <result column="description" property="description" />
+        <result column="deleted" property="deleted" />
+        <result column="dept_id" property="deptId" />
+    </resultMap>
+    <select id="listPage" resultType="com.ruoyi.approve.bean.vo.ApprovalTemplateVo">
+        select at.*,su.nick_name as create_user_name  from
+        approval_template at
+        left join sys_user su on at.create_user = su.user_id
+        <where>
+            deleted = 0
+            <if test="ew.templateName != null">
+                and template_name like concat('%',#{ew.templateName},'%')
+            </if>
+            <if test="ew.templateType != null">
+                and template_type = #{ew.templateType}
+            </if>
+            <if test="ew.enabled != null">
+                and enabled = #{ew.enabled}
+            </if>
+        </where>
+        order by at.id desc
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/ApprovalTemplateNodeApproverMapper.xml b/src/main/resources/mapper/approve/ApprovalTemplateNodeApproverMapper.xml
new file mode 100644
index 0000000..dac42cc
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalTemplateNodeApproverMapper.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalTemplateNodeApproverMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTemplateNodeApprover">
+        <id column="id" property="id" />
+        <result column="node_id" property="nodeId" />
+        <result column="template_id" property="templateId" />
+        <result column="approver_id" property="approverId" />
+        <result column="approver_name" property="approverName" />
+        <result column="sort_no" property="sortNo" />
+        <result column="created_time" property="createdTime" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/ApprovalTemplateNodeMapper.xml b/src/main/resources/mapper/approve/ApprovalTemplateNodeMapper.xml
new file mode 100644
index 0000000..7956787
--- /dev/null
+++ b/src/main/resources/mapper/approve/ApprovalTemplateNodeMapper.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.ApprovalTemplateNodeMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalTemplateNode">
+        <id column="id" property="id" />
+        <result column="template_id" property="templateId" />
+        <result column="level_no" property="levelNo" />
+        <result column="approve_type" property="approveType" />
+        <result column="created_time" property="createdTime" />
+        <result column="updated_time" property="updatedTime" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/FinReimbursementDetailMapper.xml b/src/main/resources/mapper/approve/FinReimbursementDetailMapper.xml
new file mode 100644
index 0000000..f8a67bb
--- /dev/null
+++ b/src/main/resources/mapper/approve/FinReimbursementDetailMapper.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.FinReimbursementDetailMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursementDetail">
+        <id column="id" property="id" />
+        <result column="reimbursement_id" property="reimbursementId" />
+        <result column="row_no" property="rowNo" />
+        <result column="invoice_date" property="invoiceDate" />
+        <result column="expense_category" property="expenseCategory" />
+        <result column="amount" property="amount" />
+        <result column="description" property="description" />
+        <result column="invoice_no" property="invoiceNo" />
+        <result column="invoice_type" property="invoiceType" />
+        <result column="invoice_amount" property="invoiceAmount" />
+        <result column="tax_rate" property="taxRate" />
+        <result column="tax_amount" property="taxAmount" />
+        <result column="remark" property="remark" />
+        <result column="tenant_id" property="tenantId" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="dept_id" property="deptId" />
+        <result column="deleted" property="deleted" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/FinReimbursementMapper.xml b/src/main/resources/mapper/approve/FinReimbursementMapper.xml
new file mode 100644
index 0000000..72756c4
--- /dev/null
+++ b/src/main/resources/mapper/approve/FinReimbursementMapper.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.FinReimbursementMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursement">
+        <id column="id" property="id" />
+        <result column="bill_no" property="billNo" />
+        <result column="reimbursement_type" property="reimbursementType" />
+        <result column="expense_type" property="expenseType" />
+        <result column="applicant_id" property="applicantId" />
+        <result column="applicant_code" property="applicantCode" />
+        <result column="applicant_name" property="applicantName" />
+        <result column="applicant_dept_id" property="applicantDeptId" />
+        <result column="applicant_dept_name" property="applicantDeptName" />
+        <result column="reason" property="reason" />
+        <result column="apply_amount" property="applyAmount" />
+        <result column="detail_total_amount" property="detailTotalAmount" />
+        <result column="payee_name" property="payeeName" />
+        <result column="payee_account" property="payeeAccount" />
+        <result column="payee_bank" property="payeeBank" />
+        <result column="approval_instance_id" property="approvalInstanceId" />
+        <result column="approve_process_id" property="approveProcessId" />
+        <result column="bill_status" property="billStatus" />
+        <result column="approved_time" property="approvedTime" />
+        <result column="paid_time" property="paidTime" />
+        <result column="account_expense_id" property="accountExpenseId" />
+        <result column="remark" property="remark" />
+        <result column="tenant_id" property="tenantId" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="dept_id" property="deptId" />
+        <result column="deleted" property="deleted" />
+    </resultMap>
+    <select id="listPage" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo">
+        select fin_reimbursement.*,
+        fin_reimbursement_travel.start_time ,
+        fin_reimbursement_travel.end_time
+               from
+                fin_reimbursement
+            left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id
+        <where>
+            <if test="ew.billNo != null and ew.billNo != ''">
+                bill_no like concat('%',#{ew.billNo},'%')
+            </if>
+            <if test="ew.applicantName != null and ew.applicantName != ''">
+                and applicant_name like concat('%',#{ew.applicantName},'%')
+            </if>
+            <if test="ew.applicantCode != null and ew.applicantCode != ''">
+                and applicant_code like concat('%',#{ew.applicantCode},'%')
+            </if>
+            <if test="ew.createTimeStart != null and ew.createTimeStart !='' and ew.createTimeEnd != null and ew.createTimeEnd != ''">
+                and create_time &gt;= #{ew.createTimeStart}
+                and create_time &lt;= #{ew.createTimeEnd}
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/approve/FinReimbursementTravelMapper.xml b/src/main/resources/mapper/approve/FinReimbursementTravelMapper.xml
new file mode 100644
index 0000000..dc42863
--- /dev/null
+++ b/src/main/resources/mapper/approve/FinReimbursementTravelMapper.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.approve.mapper.FinReimbursementTravelMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursementTravel">
+        <id column="id" property="id" />
+        <result column="reimbursement_id" property="reimbursementId" />
+        <result column="start_time" property="startTime" />
+        <result column="end_time" property="endTime" />
+        <result column="travel_days" property="travelDays" />
+        <result column="departure_city" property="departureCity" />
+        <result column="destination_city" property="destinationCity" />
+        <result column="hotel_standard" property="hotelStandard" />
+        <result column="lodging_days" property="lodgingDays" />
+        <result column="meal_allowance" property="mealAllowance" />
+        <result column="transport_allowance" property="transportAllowance" />
+        <result column="lodging_limit" property="lodgingLimit" />
+        <result column="standard_tag" property="standardTag" />
+        <result column="within_standard" property="withinStandard" />
+        <result column="tenant_id" property="tenantId" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="dept_id" property="deptId" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml b/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml
new file mode 100644
index 0000000..fa45ea6
--- /dev/null
+++ b/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.collaborativeApproval.pojo.EnterpriseNews">
+        <id column="id" property="id" />
+        <result column="title" property="title" />
+        <result column="summary" property="summary" />
+        <result column="content" property="content" />
+        <result column="category" property="category" />
+        <result column="read_scope" property="readScope" />
+        <result column="is_required" property="isRequired" />
+        <result column="status" property="status" />
+        <result column="required_read_count" property="requiredReadCount" />
+        <result column="read_count" property="readCount" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="dept_id" property="deptId" />
+    </resultMap>
+    <select id="listPage" resultType="com.ruoyi.collaborativeApproval.vo.EnterpriseNewsVo">
+        select en.*, u.nick_name as create_user_name from
+        enterprise_news en
+        left join sys_user u on en.create_user = u.user_id
+        <where>
+            <if test="enterpriseNewsDto.title != null and enterpriseNewsDto.title != ''">
+                and en.title like concat('%',#{enterpriseNewsDto.title},'%')
+            </if>
+            <if test="enterpriseNewsDto.category != null and enterpriseNewsDto.category != ''">
+                and en.category = #{enterpriseNewsDto.category}
+            </if>
+            <if test="enterpriseNewsDto.status != null ">
+                and en.status = #{enterpriseNewsDto.status}
+            </if>
+            <if test="enterpriseNewsDto.createUser != null and enterpriseNewsDto.createUser != ''">
+                and en.create_user = #{enterpriseNewsDto.createUser}
+            </if>
+            <if test="enterpriseNewsDto.createTimeStart != null and enterpriseNewsDto.createTimeEnd != null">
+                and en.create_time between #{enterpriseNewsDto.createTimeStart} and #{enterpriseNewsDto.createTimeEnd}
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeDeptMapper.xml b/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeDeptMapper.xml
new file mode 100644
index 0000000..0b13f67
--- /dev/null
+++ b/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeDeptMapper.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeDeptMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeDept">
+        <id column="id" property="id" />
+        <result column="news_id" property="newsId" />
+        <result column="dept_id" property="deptId" />
+        <result column="create_time" property="createTime" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeUserMapper.xml b/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeUserMapper.xml
new file mode 100644
index 0000000..7f4ac4b
--- /dev/null
+++ b/src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeUserMapper.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.collaborativeApproval.mapper.EnterpriseNewsScopeUserMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.collaborativeApproval.pojo.EnterpriseNewsScopeUser">
+        <id column="id" property="id" />
+        <result column="news_id" property="newsId" />
+        <result column="user_id" property="userId" />
+        <result column="create_time" property="createTime" />
+    </resultMap>
+
+</mapper>
diff --git a/src/main/resources/mapper/staff/StaffLeaveMapper.xml b/src/main/resources/mapper/staff/StaffLeaveMapper.xml
index 4637ff0..8bc9607 100644
--- a/src/main/resources/mapper/staff/StaffLeaveMapper.xml
+++ b/src/main/resources/mapper/staff/StaffLeaveMapper.xml
@@ -18,15 +18,20 @@
         soj.emergency_contact as emergencyContact,
         soj.emergency_contact_phone as emergencyContactPhone,
         sp.post_name as postName,
-        sd.dept_name as deptName
+        sd.dept_name as deptName,
+        se.education as first_study,
+        se.major as profession
         FROM staff_leave
-        LEFT JOIN
-        staff_on_job soj ON soj.id = staff_leave.staff_on_job_id
-        LEFT JOIN
-        sys_post sp ON sp.post_id = soj.sys_post_id
-        LEFT JOIN
-        sys_dept sd ON sd.dept_id = soj.sys_dept_id
-        where 1=1
+        LEFT JOIN staff_on_job soj ON soj.id = staff_leave.staff_on_job_id
+        LEFT JOIN sys_post sp ON sp.post_id = soj.sys_post_id
+        LEFT JOIN sys_dept sd ON sd.dept_id = soj.sys_dept_id
+        LEFT JOIN staff_education se ON se.staff_on_job_id = staff_leave.staff_on_job_id
+        AND se.id = (
+        SELECT MAX(se2.id)
+        FROM staff_education se2
+        WHERE se2.staff_on_job_id = staff_leave.staff_on_job_id
+        )
+        WHERE 1=1
         <if test="c.staffName != null and c.staffName != '' ">
             AND soj.staff_name LIKE CONCAT('%',#{c.staffName},'%')
         </if>
diff --git a/src/main/resources/mapper/staff/StaffOnJobMapper.xml b/src/main/resources/mapper/staff/StaffOnJobMapper.xml
index a256ff8..2cbd1dc 100644
--- a/src/main/resources/mapper/staff/StaffOnJobMapper.xml
+++ b/src/main/resources/mapper/staff/StaffOnJobMapper.xml
@@ -3,29 +3,82 @@
 <mapper namespace="com.ruoyi.staff.mapper.StaffOnJobMapper">
     <select id="staffOnJobListPage" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
         SELECT
-        staff_on_job.*,
-        sp.post_name as postName,
-        sd.dept_name as deptName,
-        MIN(t1.contract_start_time) as contract_start_time,  -- 鍙栨渶鏃╁悎鍚屽紑濮嬫椂闂�
-        MAX(t1.contract_end_time) as contract_end_time
+        staff_on_job.id,
+        staff_on_job.staff_state,
+        staff_on_job.staff_no,
+        staff_on_job.staff_name,
+        staff_on_job.sex,
+        staff_on_job.native_place,
+        staff_on_job.sys_post_id,
+        staff_on_job.sys_dept_id,
+        staff_on_job.role_id,
+        staff_on_job.adress,
+        staff_on_job.first_study,
+        staff_on_job.profession,
+        staff_on_job.identity_card,
+        staff_on_job.age,
+        staff_on_job.phone,
+        staff_on_job.contract_term,
+        staff_on_job.contract_expire_time,
+        staff_on_job.trial_end_date,
+        staff_on_job.trial_start_date,
+        staff_on_job.sign_date,
+        staff_on_job.salary_select,
+        staff_on_job.pro_salary,
+        staff_on_job.date_select,
+        staff_on_job.remark,
+        staff_on_job.create_time,
+        staff_on_job.create_user,
+        staff_on_job.update_time,
+        staff_on_job.update_user,
+        staff_on_job.tenant_id,
+        staff_on_job.alias,
+        staff_on_job.birth_date,
+        staff_on_job.nation,
+        staff_on_job.marital_status,
+        staff_on_job.pro_term,
+        staff_on_job.positive_date,
+        staff_on_job.basic_salary,
+        staff_on_job.dept_id,
+        sp.post_name AS post_name,
+        sd.dept_name AS dept_name,
+        MIN(t1.contract_start_time) AS contract_start_time,
+        MAX(t1.contract_end_time) AS contract_end_time,
+        (
+        SELECT GROUP_CONCAT(sec.contact_name SEPARATOR ',')
+        FROM staff_emergency_contact sec
+        WHERE sec.staff_on_job_id = staff_on_job.id
+        ) AS emergency_contact,
+        (
+        SELECT GROUP_CONCAT(sec.contact_phone SEPARATOR ',')
+        FROM staff_emergency_contact sec
+        WHERE sec.staff_on_job_id = staff_on_job.id
+        ) AS emergency_contact_phone
         FROM staff_on_job
         LEFT JOIN sys_post sp ON sp.post_id = staff_on_job.sys_post_id
         LEFT JOIN sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id
-        LEFT JOIN staff_contract as t1 ON t1.staff_on_job_id = staff_on_job.id
-        WHERE 1=1
-        <if test="staffOnJob.staffState != null">
-            AND staff_state = #{staffOnJob.staffState}
-        </if>
-        <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
-            AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
-        </if>
-        <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' ">
-            AND contract_expire_time &gt;= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
-        </if>
-        <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
-            AND contract_expire_time &lt;= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d')
-        </if>
+        LEFT JOIN staff_contract AS t1 ON t1.staff_on_job_id = staff_on_job.id
+        <where>
+            <if test="staffOnJob.staffState != null">
+                AND staff_on_job.staff_state = #{staffOnJob.staffState}
+            </if>
+            <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
+                AND staff_on_job.staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
+            </if>
+            <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' ">
+                AND staff_on_job.contract_expire_time &gt;= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
+            </if>
+            <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
+                AND staff_on_job.contract_expire_time &lt;= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d')
+            </if>
+            <if test="staffOnJob.sysDeptId != null">
+                AND staff_on_job.sys_dept_id = #{staffOnJob.sysDeptId}
+            </if>
+        </where>
         GROUP BY staff_on_job.id
+        <if test="staffOnJob.contractStartTime != null">
+            HAVING MIN(t1.contract_start_time) = #{staffOnJob.contractStartTime}
+        </if>
     </select>
     <select id="staffOnJobList" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
         SELECT
diff --git a/src/main/resources/mapper/system/SysNoticeMapper.xml b/src/main/resources/mapper/system/SysNoticeMapper.xml
index 3dd5735..14372d8 100644
--- a/src/main/resources/mapper/system/SysNoticeMapper.xml
+++ b/src/main/resources/mapper/system/SysNoticeMapper.xml
@@ -10,11 +10,16 @@
         <result property="noticeType"     column="notice_type"     />
         <result property="noticeContent"  column="notice_content"  />
         <result property="status"         column="status"          />
+        <result property="senderId"       column="sender_id"       />
+        <result property="consigneeId"    column="consignee_id"    />
+        <result property="jumpPath"       column="jump_path"       />
+        <result property="appJumpPath"    column="app_jump_path"   />
         <result property="createBy"       column="create_by"       />
         <result property="createTime"     column="create_time"     />
         <result property="updateBy"       column="update_by"       />
         <result property="updateTime"     column="update_time"     />
         <result property="remark"         column="remark"          />
+        <result property="tenantId"       column="tenant_id"       />
     </resultMap>
 
     <sql id="selectNoticeVo">

--
Gitblit v1.9.3