From a9db3088c58d9776f8aa25e5559e50ed8d6cf3d7 Mon Sep 17 00:00:00 2001 From: Yangk Date: Fri, 12 Dec 2025 17:19:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(erp/timesheet):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=B7=A5=E6=97=B6=E5=A1=AB=E6=8A=A5=E7=AE=A1=E7=90=86=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=AE=A1=E6=89=B9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ErpTimesheetInfoController.java | 10 ++ .../oa/erp/domain/ErpTimesheetDept.java | 5 + .../oa/erp/domain/ErpTimesheetProject.java | 4 + .../oa/erp/domain/bo/ErpTimesheetDeptBo.java | 5 + .../oa/erp/domain/bo/ErpTimesheetInfoBo.java | 8 +- .../erp/domain/bo/ErpTimesheetProjectBo.java | 5 + .../oa/erp/domain/vo/ErpTimesheetDeptVo.java | 5 + .../oa/erp/domain/vo/ErpTimesheetInfoVo.java | 3 +- .../erp/domain/vo/ErpTimesheetProjectVo.java | 5 + .../impl/ErpTimesheetInfoServiceImpl.java | 115 ++++++++++++++---- 10 files changed, 139 insertions(+), 26 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetInfoController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetInfoController.java index a6a59643..b8c418d8 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetInfoController.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetInfoController.java @@ -113,4 +113,14 @@ public class ErpTimesheetInfoController extends BaseController { return R.ok(list); } + /** + * 提交项目工时填报并提交流程 + */ + @SaCheckPermission("oa/erp:timesheetInfo:add") + @Log(title = "工时填报", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/submitAndFlowStart") + public R submitAndFlowStart(@Validated(AddGroup.class) @RequestBody ErpTimesheetInfoBo bo) { + return R.ok(erpTimesheetInfoService.submitAndFlowStart(bo)); + } } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetDept.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetDept.java index 6b6712c4..78a5ee76 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetDept.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetDept.java @@ -64,5 +64,10 @@ public class ErpTimesheetDept extends TenantEntity { @TableLogic private String delFlag; + /** + * 指定审批人ID + */ + private Long approverId; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetProject.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetProject.java index 271770f1..dac8f4eb 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetProject.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpTimesheetProject.java @@ -74,5 +74,9 @@ public class ErpTimesheetProject extends TenantEntity { @TableLogic private String delFlag; + /** + * 指定审批人ID + */ + private Long approverId; } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetDeptBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetDeptBo.java index 2045ee32..af065c82 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetDeptBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetDeptBo.java @@ -57,5 +57,10 @@ public class ErpTimesheetDeptBo extends BaseEntity { */ private Long hours; + /** + * 指定审批人ID + */ + private Long approverId; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetInfoBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetInfoBo.java index 8d312a26..0c32eeb1 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetInfoBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetInfoBo.java @@ -8,6 +8,8 @@ import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import lombok.EqualsAndHashCode; import jakarta.validation.constraints.*; + +import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.Map; @@ -64,17 +66,17 @@ public class ErpTimesheetInfoBo extends BaseEntity { /** * 总工时(自动累计) */ - private Long totalHours; + private BigDecimal totalHours; /** * 部门工时(根据部门工作自动累计) */ - private Long deptHours; + private BigDecimal deptHours; /** * 项目工时(根据项目工作自动累计) */ - private Long projectHours; + private BigDecimal projectHours; /** * 工时填报状态(1暂存 2审批中 3已审批 4作废) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetProjectBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetProjectBo.java index 70c9832c..99792a70 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetProjectBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetProjectBo.java @@ -67,5 +67,10 @@ public class ErpTimesheetProjectBo extends BaseEntity { */ private Long hours; + /** + * 指定审批人ID + */ + private Long approverId; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetDeptVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetDeptVo.java index f78fec85..ce780739 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetDeptVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetDeptVo.java @@ -70,5 +70,10 @@ public class ErpTimesheetDeptVo implements Serializable { @ExcelProperty(value = "工时") private Long hours; + /** + * 指定审批人ID + */ + private Long approverId; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetInfoVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetInfoVo.java index fbb1dd60..b18724da 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetInfoVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetInfoVo.java @@ -100,8 +100,7 @@ public class ErpTimesheetInfoVo implements Serializable { /** * 流程状态 */ - @ExcelProperty(value = "流程状态", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "flow_status") + @ExcelProperty(value = "流程状态") private String flowStatus; /** diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetProjectVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetProjectVo.java index 7ff72029..cd5c0b98 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetProjectVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetProjectVo.java @@ -82,5 +82,10 @@ public class ErpTimesheetProjectVo implements Serializable { @ExcelProperty(value = "工时") private Long hours; + /** + * 指定审批人ID + */ + private Long approverId; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetInfoServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetInfoServiceImpl.java index c73c75e5..0fe1a628 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetInfoServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetInfoServiceImpl.java @@ -1,9 +1,12 @@ package org.dromara.oa.erp.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.seata.spring.annotation.GlobalTransactional; +import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; @@ -14,6 +17,7 @@ import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; +import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.oa.erp.domain.ErpTimesheetDept; import org.dromara.oa.erp.domain.ErpTimesheetProject; import org.dromara.oa.erp.domain.bo.ErpTimesheetDeptBo; @@ -24,6 +28,8 @@ import org.dromara.oa.erp.service.IErpTimesheetDeptService; import org.dromara.oa.erp.service.IErpTimesheetProjectService; import org.dromara.workflow.api.RemoteWorkflowService; import org.dromara.workflow.api.domain.RemoteStartProcess; +import org.dromara.workflow.api.event.ProcessEvent; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.dromara.oa.erp.domain.bo.ErpTimesheetInfoBo; import org.dromara.oa.erp.domain.vo.ErpTimesheetInfoVo; @@ -32,10 +38,8 @@ import org.dromara.oa.erp.mapper.ErpTimesheetInfoMapper; import org.dromara.oa.erp.service.IErpTimesheetInfoService; import org.springframework.transaction.annotation.Transactional; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Collection; +import java.util.*; +import java.util.stream.Collectors; /** * 工时填报Service业务层处理 @@ -45,6 +49,7 @@ import java.util.Collection; */ @RequiredArgsConstructor @Service +@Slf4j public class ErpTimesheetInfoServiceImpl implements IErpTimesheetInfoService { private final ErpTimesheetInfoMapper baseMapper; @@ -240,42 +245,110 @@ public class ErpTimesheetInfoServiceImpl implements IErpTimesheetInfoService { @Override @GlobalTransactional(rollbackFor = Exception.class) // 开启全局事务 public ErpTimesheetInfoVo submitAndFlowStart(ErpTimesheetInfoBo bo) { - - ErpTimesheetInfo add = MapstructUtils.convert(bo, ErpTimesheetInfo.class); - validEntityBeforeSave(add); - if (bo.getTimesheetId() == null) { this.insertByBo(bo); - } else { this.updateByBo(bo); } - // 准备启动流程参数 - // 后端发起需要忽略权限 if (bo.getVariables() == null) { bo.setVariables(new HashMap<>()); } - bo.getVariables().put("ignore", true); + Map variables = bo.getVariables(); + + // --- 处理部门工时审批人 --- + List deptList = bo.getTimesheetDeptList(); + List deptApprovers = new ArrayList<>(); + if (CollUtil.isNotEmpty(deptList)) { + deptApprovers = deptList.stream() + .map(ErpTimesheetDeptBo::getApproverId) + .filter(Objects::nonNull) + .map(String::valueOf) + .distinct() + .collect(Collectors.toList()); + } + + // deptApprovers (审批人列表) + variables.put("deptApprovers", deptApprovers); + // hasDeptWork (是否有部门工时) - true/false + variables.put("hasDeptWork", CollUtil.isNotEmpty(deptApprovers)); + + + // --- 处理项目工时审批人 --- + List projectList = bo.getTimesheetProjectList(); + List projectApprovers = new ArrayList<>(); + if (CollUtil.isNotEmpty(projectList)) { + projectApprovers = projectList.stream() + .map(ErpTimesheetProjectBo::getApproverId) + .filter(Objects::nonNull) + .map(String::valueOf) + .distinct() + .collect(Collectors.toList()); + } + + variables.put("projectApprovers", projectApprovers); + variables.put("hasProjectWork", CollUtil.isNotEmpty(projectApprovers)); + + RemoteStartProcess startProcess = new RemoteStartProcess(); startProcess.setBusinessId(bo.getTimesheetId().toString()); startProcess.setFlowCode(bo.getFlowCode()); - startProcess.setVariables(bo.getVariables()); + startProcess.setVariables(variables); startProcess.setBizExt(bo.getBizExt()); - - // 确保 BizExt 里也有 BusinessId if (bo.getBizExt() != null) { bo.getBizExt().setBusinessId(startProcess.getBusinessId()); } - // 调用远程服务启动流程 - // startCompleteTask 表示“启动并自动完成第一个发起节点”,直接流转到下一个审批人 - boolean flagOne = remoteWorkflowService.startCompleteTask(startProcess); - - if (!flagOne) { + boolean flag = remoteWorkflowService.startCompleteTask(startProcess); + if (!flag) { throw new ServiceException("流程发起异常"); } - return MapstructUtils.convert(add, ErpTimesheetInfoVo.class); + return MapstructUtils.convert(baseMapper.selectById(bo.getTimesheetId()), ErpTimesheetInfoVo.class); } + + /** + * 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成等) + * + * @param processEvent 参数 + */ + @EventListener(condition = "#processEvent.flowCode == 'OATS'") + public void processHandler(ProcessEvent processEvent) { + TenantHelper.dynamic(processEvent.getTenantId(), () -> { + log.info("【工时填报流程监听】开始处理: key={}, status={}", processEvent.getFlowCode(), processEvent.getStatus()); + + Long timeSheetId = Convert.toLong(processEvent.getBusinessId()); + ErpTimesheetInfo timeSheetInfo = baseMapper.selectById(timeSheetId); + + if (timeSheetInfo == null) { + log.error("未找到对应的工时单据,id={}", timeSheetId); + return; + } + + timeSheetInfo.setFlowStatus(processEvent.getStatus()); + String status = processEvent.getStatus(); + + // A. 审批中 (Waiting) + if (BusinessStatusEnum.WAITING.getStatus().equals(status)) { + timeSheetInfo.setTimesheetStatus("2"); + } + // B. 流程结束/审批通过 (Finish) + else if (BusinessStatusEnum.FINISH.getStatus().equals(status)) { + timeSheetInfo.setTimesheetStatus("3"); + } + // C. 驳回/撤销 + else if (BusinessStatusEnum.BACK.getStatus().equals(status) + || BusinessStatusEnum.CANCEL.getStatus().equals(status)) { + timeSheetInfo.setTimesheetStatus("1"); + } + // D. 作废/终止 + else if (BusinessStatusEnum.INVALID.getStatus().equals(status) + || BusinessStatusEnum.TERMINATION.getStatus().equals(status)) { + timeSheetInfo.setTimesheetStatus("4"); + } + + baseMapper.updateById(timeSheetInfo); + }); + } + }