huminmin
3 天以前 7ab45e2b02facae5685ba879d150de6dc0032aa4
Merge branch 'dev_New_pro' of http://114.132.189.42:9002/r/product-inventory-management-after into dev_New_pro
已添加96个文件
已修改21个文件
6410 ■■■■■ 文件已修改
src/main/java/com/ruoyi/approve/bean/dto/ApprovalInstanceDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateDto.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeApproverDto.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/dto/ApprovalTemplateNodeDto.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/dto/FinReimbursementDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/ApprovalInstanceVo.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeApproverVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateNodeVo.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/ApprovalTemplateVo.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalInstanceController.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalInstanceNodeController.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalRecordController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalTaskController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalTemplateController.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeApproverController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/ApprovalTemplateNodeController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/FinReimbursementDetailController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/controller/FinReimbursementTravelController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalInstanceNodeMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalRecordMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalTaskMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeApproverMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/ApprovalTemplateNodeMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/FinReimbursementDetailMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/mapper/FinReimbursementTravelMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalInstance.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalInstanceNode.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalRecord.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalTask.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalTemplate.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNode.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApprovalTemplateNodeApprover.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/FinReimbursementDetail.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/FinReimbursementTravel.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalInstanceNodeService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalInstanceService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalRecordService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalTaskService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeApproverService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalTemplateNodeService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/ApprovalTemplateService.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/FinReimbursementDetailService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/FinReimbursementService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/FinReimbursementTravelService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceNodeServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java 753 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalRecordServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalTaskServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeApproverServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateNodeServiceImpl.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApprovalTemplateServiceImpl.java 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/FinReimbursementDetailServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java 544 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/FinReimbursementTravelServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/utils/ApproveProcessConfigNodeUtils.java 363 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsController.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeDeptController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/controller/EnterpriseNewsScopeUserController.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/dto/EnterpriseNewsDto.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeDeptMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/mapper/EnterpriseNewsScopeUserMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNews.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeDept.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/pojo/EnterpriseNewsScopeUser.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeDeptService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsScopeUserService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/EnterpriseNewsService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeDeptServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsScopeUserServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/service/impl/EnterpriseNewsServiceImpl.java 409 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/collaborativeApproval/vo/EnterpriseNewsVo.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/ApprovalStatusEnum.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/EnterpriseNewsStatusEnum.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/SalesQuotationStatusEnum.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/ShippingStatusEnum.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/common/enums/TypeEnums.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/mapper/SysUserDeptMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/project/system/service/impl/SysNoticeServiceImpl.java 169 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/utils/QualityInspectHelper.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesQuotationServiceImpl.java 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/StaffOnJobController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/dto/StaffOnJobDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/StaffOnJobMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/IStaffOnJobService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffLeaveServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyOperationController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/technology/controller/TechnologyOperationParamController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalInstanceMapper.xml 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalInstanceNodeMapper.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalRecordMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalTaskMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalTemplateMapper.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalTemplateNodeApproverMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApprovalTemplateNodeMapper.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/FinReimbursementDetailMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/FinReimbursementMapper.xml 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/FinReimbursementTravelMapper.xml 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/EnterpriseNewsMapper.xml 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeDeptMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/collaborativeApproval/EnterpriseNewsScopeUserMapper.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffLeaveMapper.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/StaffOnJobMapper.xml 91 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/system/SysNoticeMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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;
}
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;
}
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 {
}
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;
}
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;
}
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;
}
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 {
}
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;
}
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;
}
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;
}
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);
    }
}
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 {
}
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 {
}
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 {
}
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));
    }
}
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 {
}
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 {
}
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));
    }
}
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 {
}
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 {
}
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);
}
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> {
}
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> {
}
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> {
}
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);
}
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> {
}
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> {
}
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> {
}
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);
}
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> {
}
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;
    /**
     * ç”³è¯·äººID
     */
    @Schema(description ="申请人ID")
    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;
}
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;
}
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;
    /**
     * æ“ä½œäººID
     */
    @Schema(description ="操作人ID")
    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;
}
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;
    /**
     * å®¡æ‰¹äººID
     */
    @Schema(description ="审批人ID")
    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;
}
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;
}
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;
    /**
     * å®¡æ‰¹æ–¹å¼ï¼šAND会签,OR或签
     */
    @Schema(description ="审批方式:AND会签,OR或签")
    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;
}
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;
    /**
     * å®¡æ‰¹äººID
     */
    @Schema(description ="审批人ID")
    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;
}
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;
    /**
     * ç”³è¯·äººID
     */
    @Schema(description = "申请人ID")
    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-草稿,IN_APPROVAL-审批中,APPROVED-审批通过,REJECTED-审批驳回,WITHDRAWN-已撤回,PAID-已付款
     */
    @Schema(description = "单据状态:DRAFT-草稿,IN_APPROVAL-审批中,APPROVED-审批通过,REJECTED-审批驳回,WITHDRAWN-已撤回,PAID-已付款")
    private String billStatus;
    /**
     * å®¡æ‰¹é€šè¿‡æ—¶é—´
     */
    @Schema(description = "审批通过时间")
    private LocalDateTime approvedTime;
    /**
     * ä»˜æ¬¾æ—¶é—´
     */
    @Schema(description = "付款时间")
    private LocalDateTime paidTime;
    /**
     * ç”Ÿæˆçš„财务支出记录ID,对应 account_expense.id
     */
    @Schema(description = "生成的财务支出记录ID,对应 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;
}
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;
    /**
     * æŠ¥é”€å•ID,对应 fin_reimbursement.id
     */
    @Schema(description = "报销单ID,对应 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;
}
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;
    /**
     * æŠ¥é”€å•ID,对应 fin_reimbursement.id
     */
    @Schema(description = "报销单ID,对应 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;
}
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> {
}
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);
}
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> {
}
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> {
}
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> {
}
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);
}
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);
}
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> {
}
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);
}
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> {
}
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 {
}
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);
    }
}
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 {
}
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 {
}
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 {
}
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;
    }
}
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;
    }
}
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 {
}
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());
        // æ–°æ˜Žç»†ä¸­æœ‰ID的 â†’ æ›´æ–°ï¼›æ— 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("报销单ID不能为空");
        }
        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("报销单ID不能为空");
        }
        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);
    }
}
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 {
}
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
        );
    }
}
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");
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));
    }
}
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 {
}
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 {
}
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;
}
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);
}
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> {
}
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> {
}
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 = "正文 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;
}
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;
}
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;
}
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> {
}
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> {
}
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);
}
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 {
}
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 {
}
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("正文不能为空");
        }
        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);
    }
}
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;
}
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() : "未知状态";
    }
}
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);
    }
}
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;
    }
}
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;
    }
}
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 å¯¹åº”的枚举,未匹配返回null
     */
    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() : "自定义审批";
    }
}
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}")
src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
@@ -13,4 +13,5 @@
    R deleteRouteItem(Long id);
    int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
}
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);
}
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;
        }
    }
}
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;
}
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);
        }
    }
}
src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
@@ -17,4 +17,6 @@
     */
    // å®¡æ‰¹äºº
    private String approveUserIds;
    private Long templateId;
}
src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -47,5 +47,9 @@
    //发货数量
    private BigDecimal totalQuantity;
    private Long templateId;
    private String templateName;
}
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
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));
    }
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;
}
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);
}
}
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);
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());
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删除教育经历,工作经历,紧急联系人
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));
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));
src/main/resources/mapper/approve/ApprovalInstanceMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.approve.mapper.ApprovalInstanceMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.ApprovalInstance">
        <id column="id" property="id" />
        <result column="instance_no" property="instanceNo" />
        <result column="template_id" property="templateId" />
        <result column="template_name" property="templateName" />
        <result column="business_id" property="businessId" />
        <result column="business_type" property="businessType" />
        <result column="title" property="title" />
        <result column="status" property="status" />
        <result column="current_level" property="currentLevel" />
        <result column="applicant_id" property="applicantId" />
        <result column="applicant_name" property="applicantName" />
        <result column="apply_time" property="applyTime" />
        <result column="finish_time" property="finishTime" />
        <result column="create_user" property="createUser" />
        <result column="create_time" property="createTime" />
        <result column="update_user" property="updateUser" />
        <result column="update_time" property="updateTime" />
        <result column="deleted" property="deleted" />
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.approve.bean.vo.ApprovalInstanceVo">
        select ai.*,su.nick_name as create_user_name from
        approval_instance ai
        left join sys_user su on ai.create_user = su.user_id
        <where>
            deleted = 0
            <if test="ew.instanceNo != null">
                and ai.instance_no like concat('%',#{ew.instanceNo},'%')
            </if>
            <if test="ew.templateName != null">
                and ai.template_name like concat('%',#{ew.templateName},'%')
            </if>
            <if test="ew.templateId != null ">
                and ai. template_id = #{ew.templateId}
            </if>
            <if test="ew.businessType != null ">
                and ai.business_type = #{ew.businessType}
            </if>
            <if test="ew.createTimeStart != null and ew.createTimeEnd != null">
                and ai.apply_time &gt;= #{ew.createTimeStart}
                and ai.apply_time &lt;= #{ew.createTimeEnd}
            </if>
            <if test="ew.status != null">
                and ai.status = #{ew.status}
            </if>
            <if test="ew.applicantName != null and ew.applicantName !=''">
                and ai.applicant_name = #{ew.applicantName}
            </if>
        </where>
        order by ai.id desc
    </select>
</mapper>
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>
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>
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>
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>
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>
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>
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>
src/main/resources/mapper/approve/FinReimbursementMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.approve.mapper.FinReimbursementMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.approve.pojo.FinReimbursement">
        <id column="id" property="id" />
        <result column="bill_no" property="billNo" />
        <result column="reimbursement_type" property="reimbursementType" />
        <result column="expense_type" property="expenseType" />
        <result column="applicant_id" property="applicantId" />
        <result column="applicant_code" property="applicantCode" />
        <result column="applicant_name" property="applicantName" />
        <result column="applicant_dept_id" property="applicantDeptId" />
        <result column="applicant_dept_name" property="applicantDeptName" />
        <result column="reason" property="reason" />
        <result column="apply_amount" property="applyAmount" />
        <result column="detail_total_amount" property="detailTotalAmount" />
        <result column="payee_name" property="payeeName" />
        <result column="payee_account" property="payeeAccount" />
        <result column="payee_bank" property="payeeBank" />
        <result column="approval_instance_id" property="approvalInstanceId" />
        <result column="approve_process_id" property="approveProcessId" />
        <result column="bill_status" property="billStatus" />
        <result column="approved_time" property="approvedTime" />
        <result column="paid_time" property="paidTime" />
        <result column="account_expense_id" property="accountExpenseId" />
        <result column="remark" property="remark" />
        <result column="tenant_id" property="tenantId" />
        <result column="create_user" property="createUser" />
        <result column="create_time" property="createTime" />
        <result column="update_user" property="updateUser" />
        <result column="update_time" property="updateTime" />
        <result column="dept_id" property="deptId" />
        <result column="deleted" property="deleted" />
    </resultMap>
    <select id="listPage" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo">
        select fin_reimbursement.*,
        fin_reimbursement_travel.start_time ,
        fin_reimbursement_travel.end_time
               from
                fin_reimbursement
            left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id
        <where>
            <if test="ew.billNo != null and ew.billNo != ''">
                bill_no like concat('%',#{ew.billNo},'%')
            </if>
            <if test="ew.applicantName != null and ew.applicantName != ''">
                and applicant_name like concat('%',#{ew.applicantName},'%')
            </if>
            <if test="ew.applicantCode != null and ew.applicantCode != ''">
                and applicant_code like concat('%',#{ew.applicantCode},'%')
            </if>
            <if test="ew.createTimeStart != null and ew.createTimeStart !='' and ew.createTimeEnd != null and ew.createTimeEnd != ''">
                and create_time &gt;= #{ew.createTimeStart}
                and create_time &lt;= #{ew.createTimeEnd}
            </if>
        </where>
    </select>
</mapper>
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>
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>
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>
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>
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>
src/main/resources/mapper/staff/StaffOnJobMapper.xml
@@ -3,29 +3,82 @@
<mapper namespace="com.ruoyi.staff.mapper.StaffOnJobMapper">
    <select id="staffOnJobListPage" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
        SELECT
        staff_on_job.*,
        sp.post_name as postName,
        sd.dept_name as deptName,
        MIN(t1.contract_start_time) as contract_start_time,  -- å–最早合同开始时间
        MAX(t1.contract_end_time) as contract_end_time
        staff_on_job.id,
        staff_on_job.staff_state,
        staff_on_job.staff_no,
        staff_on_job.staff_name,
        staff_on_job.sex,
        staff_on_job.native_place,
        staff_on_job.sys_post_id,
        staff_on_job.sys_dept_id,
        staff_on_job.role_id,
        staff_on_job.adress,
        staff_on_job.first_study,
        staff_on_job.profession,
        staff_on_job.identity_card,
        staff_on_job.age,
        staff_on_job.phone,
        staff_on_job.contract_term,
        staff_on_job.contract_expire_time,
        staff_on_job.trial_end_date,
        staff_on_job.trial_start_date,
        staff_on_job.sign_date,
        staff_on_job.salary_select,
        staff_on_job.pro_salary,
        staff_on_job.date_select,
        staff_on_job.remark,
        staff_on_job.create_time,
        staff_on_job.create_user,
        staff_on_job.update_time,
        staff_on_job.update_user,
        staff_on_job.tenant_id,
        staff_on_job.alias,
        staff_on_job.birth_date,
        staff_on_job.nation,
        staff_on_job.marital_status,
        staff_on_job.pro_term,
        staff_on_job.positive_date,
        staff_on_job.basic_salary,
        staff_on_job.dept_id,
        sp.post_name AS post_name,
        sd.dept_name AS dept_name,
        MIN(t1.contract_start_time) AS contract_start_time,
        MAX(t1.contract_end_time) AS contract_end_time,
        (
        SELECT GROUP_CONCAT(sec.contact_name SEPARATOR ',')
        FROM staff_emergency_contact sec
        WHERE sec.staff_on_job_id = staff_on_job.id
        ) AS emergency_contact,
        (
        SELECT GROUP_CONCAT(sec.contact_phone SEPARATOR ',')
        FROM staff_emergency_contact sec
        WHERE sec.staff_on_job_id = staff_on_job.id
        ) AS emergency_contact_phone
        FROM staff_on_job
        LEFT JOIN sys_post sp ON sp.post_id = staff_on_job.sys_post_id
        LEFT JOIN sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id
        LEFT JOIN staff_contract as t1 ON t1.staff_on_job_id = staff_on_job.id
        WHERE 1=1
        <if test="staffOnJob.staffState != null">
            AND staff_state = #{staffOnJob.staffState}
        </if>
        <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
            AND staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
        </if>
        <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' ">
            AND contract_expire_time &gt;= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
        </if>
        <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
            AND contract_expire_time &lt;= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d')
        </if>
        LEFT JOIN staff_contract AS t1 ON t1.staff_on_job_id = staff_on_job.id
        <where>
            <if test="staffOnJob.staffState != null">
                AND staff_on_job.staff_state = #{staffOnJob.staffState}
            </if>
            <if test="staffOnJob.staffName != null and staffOnJob.staffName != '' ">
                AND staff_on_job.staff_name LIKE CONCAT('%',#{staffOnJob.staffName},'%')
            </if>
            <if test="staffOnJob.entryDateStart != null and staffOnJob.entryDateStart != '' ">
                AND staff_on_job.contract_expire_time &gt;= DATE_FORMAT(#{staffOnJob.entryDateStart},'%Y-%m-%d')
            </if>
            <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' ">
                AND staff_on_job.contract_expire_time &lt;= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d')
            </if>
            <if test="staffOnJob.sysDeptId != null">
                AND staff_on_job.sys_dept_id = #{staffOnJob.sysDeptId}
            </if>
        </where>
        GROUP BY staff_on_job.id
        <if test="staffOnJob.contractStartTime != null">
            HAVING MIN(t1.contract_start_time) = #{staffOnJob.contractStartTime}
        </if>
    </select>
    <select id="staffOnJobList" resultType="com.ruoyi.staff.dto.StaffOnJobDto">
        SELECT
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">