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 >= #{ew.createTimeStart}
+ and ai.apply_time <= #{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 >= #{ew.createTimeStart}
+ and create_time <= #{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 >= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
- </if>
- <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
- AND contract_expire_time <= 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 >= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
+ </if>
+ <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
+ AND staff_on_job.contract_expire_time <= 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