From 0eb65296cc3c86c968dcbd7183aec02aa9c80728 Mon Sep 17 00:00:00 2001 From: Yangk Date: Wed, 24 Dec 2025 14:54:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(crm/TripApply):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=87=BA=E5=B7=AE=E7=94=B3=E8=AF=B7=E5=90=8E=E7=AB=AF=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CrmBusinessTripApplyController.java | 18 +- .../crm/domain/vo/CrmBusinessTripApplyVo.java | 22 +- .../service/ICrmBusinessTripApplyService.java | 8 + .../impl/CrmBusinessTripApplyServiceImpl.java | 215 ++++++++++++++---- .../oa/crm/CrmBusinessTripApplyMapper.xml | 34 ++- 5 files changed, 236 insertions(+), 61 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmBusinessTripApplyController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmBusinessTripApplyController.java index 5f72aeda..a17f17c1 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmBusinessTripApplyController.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/controller/CrmBusinessTripApplyController.java @@ -64,8 +64,7 @@ public class CrmBusinessTripApplyController extends BaseController { */ @SaCheckPermission("oa/crm:businessTripApply:query") @GetMapping("/{tripId}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable("tripId") Long tripId) { + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable("tripId") Long tripId) { return R.ok(crmBusinessTripApplyService.queryById(tripId)); } @@ -99,8 +98,7 @@ public class CrmBusinessTripApplyController extends BaseController { @SaCheckPermission("oa/crm:businessTripApply:remove") @Log(title = "出差申请", businessType = BusinessType.DELETE) @DeleteMapping("/{tripIds}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable("tripIds") Long[] tripIds) { + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable("tripIds") Long[] tripIds) { return toAjax(crmBusinessTripApplyService.deleteWithValidByIds(List.of(tripIds), true)); } @@ -113,4 +111,16 @@ public class CrmBusinessTripApplyController extends BaseController { return R.ok(list); } + /** + * 提交出差申请并提交流程 + */ + @SaCheckPermission("oa/crm:businessTripApply:add") + @Log(title = "出差申请", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/submitAndFlowStart") + public R submitAndFlowStart( + @Validated(AddGroup.class) @RequestBody CrmBusinessTripApplyBo bo) { + return R.ok(crmBusinessTripApplyService.submitAndFlowStart(bo)); + } + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmBusinessTripApplyVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmBusinessTripApplyVo.java index d0d1d591..4ca5a6b7 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmBusinessTripApplyVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/domain/vo/CrmBusinessTripApplyVo.java @@ -17,8 +17,6 @@ import java.io.Serial; import java.io.Serializable; import java.util.Date; - - /** * 出差申请视图对象 crm_business_trip_apply * @@ -54,7 +52,6 @@ public class CrmBusinessTripApplyVo implements Serializable { /** * 申请人ID */ - @ExcelProperty(value = "申请人ID") private Long applicantId; /** @@ -66,7 +63,6 @@ public class CrmBusinessTripApplyVo implements Serializable { /** * 申请人部门ID */ - @ExcelProperty(value = "申请人部门ID") private Long deptId; /** @@ -108,15 +104,25 @@ public class CrmBusinessTripApplyVo implements Serializable { /** * 项目ID */ - @ExcelProperty(value = "项目ID") private Long projectId; /** * 客户ID */ - @ExcelProperty(value = "客户ID") private Long customerId; + /** + * 项目名称 + */ + @ExcelProperty(value = "项目名称") + private String projectName; + + /** + * 项目号 + */ + @ExcelProperty(value = "项目号") + private String projectCode; + /** * 交流对象 */ @@ -126,7 +132,8 @@ public class CrmBusinessTripApplyVo implements Serializable { /** * 业务方向 */ - @ExcelProperty(value = "业务方向") + @ExcelProperty(value = "业务方向", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "business_direction") private String businessDirection; /** @@ -179,5 +186,4 @@ public class CrmBusinessTripApplyVo implements Serializable { @ExcelDictFormat(readConverterExp = "多=个用逗号分隔") private String ossId; - } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmBusinessTripApplyService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmBusinessTripApplyService.java index a33ab04e..036447f3 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmBusinessTripApplyService.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/ICrmBusinessTripApplyService.java @@ -66,4 +66,12 @@ public interface ICrmBusinessTripApplyService { * @return 是否删除成功 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 提交出差申请并提交流程 + * + * @param bo + * @return + */ + CrmBusinessTripApplyVo submitAndFlowStart(CrmBusinessTripApplyBo bo); } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmBusinessTripApplyServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmBusinessTripApplyServiceImpl.java index 85e3f64a..fe1cc37b 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmBusinessTripApplyServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/crm/service/impl/CrmBusinessTripApplyServiceImpl.java @@ -2,9 +2,9 @@ package org.dromara.oa.crm.service.impl; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; - import org.dromara.common.mybatis.core.page.TableDataInfo; - import org.dromara.common.mybatis.core.page.PageQuery; - import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -15,11 +15,25 @@ import org.dromara.oa.crm.domain.vo.CrmBusinessTripApplyVo; import org.dromara.oa.crm.domain.CrmBusinessTripApply; import org.dromara.oa.crm.mapper.CrmBusinessTripApplyMapper; import org.dromara.oa.crm.service.ICrmBusinessTripApplyService; +import org.dromara.oa.erp.domain.ErpProjectInfo; import java.util.List; import java.util.Map; import java.util.Collection; +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.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.transaction.annotation.Transactional; +import org.dromara.common.tenant.helper.TenantHelper; +import cn.hutool.core.convert.Convert; + /** * 出差申请Service业务层处理 * @@ -28,10 +42,14 @@ import java.util.Collection; */ @RequiredArgsConstructor @Service +@Slf4j public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplyService { private final CrmBusinessTripApplyMapper baseMapper; + @DubboReference(timeout = 30000) + private RemoteWorkflowService remoteWorkflowService; + /** * 查询出差申请 * @@ -39,23 +57,30 @@ public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplySer * @return 出差申请 */ @Override - public CrmBusinessTripApplyVo queryById(Long tripId){ - return baseMapper.selectVoById(tripId); + public CrmBusinessTripApplyVo queryById(Long tripId) { + MPJLambdaWrapper lqw = JoinWrappers.lambda(CrmBusinessTripApply.class) + .selectAll(CrmBusinessTripApply.class) + .selectAs(ErpProjectInfo::getProjectName, CrmBusinessTripApplyVo::getProjectName) + .selectAs(ErpProjectInfo::getProjectCode, CrmBusinessTripApplyVo::getProjectCode) + .leftJoin(ErpProjectInfo.class, ErpProjectInfo::getProjectId, CrmBusinessTripApply::getProjectId) + .eq(CrmBusinessTripApply::getTripId, tripId); + return baseMapper.selectJoinOne(CrmBusinessTripApplyVo.class, lqw); } - /** - * 分页查询出差申请列表 - * - * @param bo 查询条件 - * @param pageQuery 分页参数 - * @return 出差申请分页列表 - */ - @Override - public TableDataInfo queryPageList(CrmBusinessTripApplyBo bo, PageQuery pageQuery) { - MPJLambdaWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); - return TableDataInfo.build(result); - } + /** + * 分页查询出差申请列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 出差申请分页列表 + */ + @Override + public TableDataInfo queryPageList(CrmBusinessTripApplyBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectJoinPage(pageQuery.build(), CrmBusinessTripApplyVo.class, + lqw); + return TableDataInfo.build(result); + } /** * 查询符合条件的出差申请列表 @@ -66,37 +91,46 @@ public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplySer @Override public List queryList(CrmBusinessTripApplyBo bo) { MPJLambdaWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return baseMapper.selectJoinList(CrmBusinessTripApplyVo.class, lqw); } private MPJLambdaWrapper buildQueryWrapper(CrmBusinessTripApplyBo bo) { Map params = bo.getParams(); MPJLambdaWrapper lqw = JoinWrappers.lambda(CrmBusinessTripApply.class) - .selectAll(CrmBusinessTripApply.class) - .eq(CrmBusinessTripApply::getDelFlag, "0") - .eq(StringUtils.isNotBlank(bo.getApplyCode()), CrmBusinessTripApply::getApplyCode, bo.getApplyCode()) - .eq(StringUtils.isNotBlank(bo.getTripType()), CrmBusinessTripApply::getTripType, bo.getTripType()) - .eq(bo.getApplicantId() != null, CrmBusinessTripApply::getApplicantId, bo.getApplicantId()) - .like(StringUtils.isNotBlank(bo.getApplicantName()), CrmBusinessTripApply::getApplicantName, bo.getApplicantName()) - .eq(bo.getDeptId() != null, CrmBusinessTripApply::getDeptId, bo.getDeptId()) - .like(StringUtils.isNotBlank(bo.getDeptName()), CrmBusinessTripApply::getDeptName, bo.getDeptName()) - .eq(StringUtils.isNotBlank(bo.getTripLocation()), CrmBusinessTripApply::getTripLocation, bo.getTripLocation()) - .eq(bo.getStartTime() != null, CrmBusinessTripApply::getStartTime, bo.getStartTime()) - .eq(bo.getEndTime() != null, CrmBusinessTripApply::getEndTime, bo.getEndTime()) - .eq(bo.getDurationDays() != null, CrmBusinessTripApply::getDurationDays, bo.getDurationDays()) - .eq(StringUtils.isNotBlank(bo.getTripReason()), CrmBusinessTripApply::getTripReason, bo.getTripReason()) - .eq(bo.getProjectId() != null, CrmBusinessTripApply::getProjectId, bo.getProjectId()) - .eq(bo.getCustomerId() != null, CrmBusinessTripApply::getCustomerId, bo.getCustomerId()) - .eq(StringUtils.isNotBlank(bo.getExchangeObject()), CrmBusinessTripApply::getExchangeObject, bo.getExchangeObject()) - .eq(StringUtils.isNotBlank(bo.getBusinessDirection()), CrmBusinessTripApply::getBusinessDirection, bo.getBusinessDirection()) - .eq(StringUtils.isNotBlank(bo.getExchangePurpose()), CrmBusinessTripApply::getExchangePurpose, bo.getExchangePurpose()) - .eq(StringUtils.isNotBlank(bo.getExchangeProcess()), CrmBusinessTripApply::getExchangeProcess, bo.getExchangeProcess()) - .like(StringUtils.isNotBlank(bo.getMeetingName()), CrmBusinessTripApply::getMeetingName, bo.getMeetingName()) - .eq(StringUtils.isNotBlank(bo.getFeedback()), CrmBusinessTripApply::getFeedback, bo.getFeedback()) - .eq(StringUtils.isNotBlank(bo.getTripStatus()), CrmBusinessTripApply::getTripStatus, bo.getTripStatus()) - .eq(StringUtils.isNotBlank(bo.getFlowStatus()), CrmBusinessTripApply::getFlowStatus, bo.getFlowStatus()) - .eq(StringUtils.isNotBlank(bo.getOssId()), CrmBusinessTripApply::getOssId, bo.getOssId()) -; + .selectAll(CrmBusinessTripApply.class) + .selectAs(ErpProjectInfo::getProjectName, CrmBusinessTripApplyVo::getProjectName) + .selectAs(ErpProjectInfo::getProjectCode, CrmBusinessTripApplyVo::getProjectCode) + .leftJoin(ErpProjectInfo.class, ErpProjectInfo::getProjectId, CrmBusinessTripApply::getProjectId) + .eq(CrmBusinessTripApply::getDelFlag, "0") + .eq(StringUtils.isNotBlank(bo.getApplyCode()), CrmBusinessTripApply::getApplyCode, bo.getApplyCode()) + .eq(StringUtils.isNotBlank(bo.getTripType()), CrmBusinessTripApply::getTripType, bo.getTripType()) + .eq(bo.getApplicantId() != null, CrmBusinessTripApply::getApplicantId, bo.getApplicantId()) + .like(StringUtils.isNotBlank(bo.getApplicantName()), CrmBusinessTripApply::getApplicantName, + bo.getApplicantName()) + .eq(bo.getDeptId() != null, CrmBusinessTripApply::getDeptId, bo.getDeptId()) + .like(StringUtils.isNotBlank(bo.getDeptName()), CrmBusinessTripApply::getDeptName, bo.getDeptName()) + .eq(StringUtils.isNotBlank(bo.getTripLocation()), CrmBusinessTripApply::getTripLocation, + bo.getTripLocation()) + .eq(bo.getStartTime() != null, CrmBusinessTripApply::getStartTime, bo.getStartTime()) + .eq(bo.getEndTime() != null, CrmBusinessTripApply::getEndTime, bo.getEndTime()) + .eq(bo.getDurationDays() != null, CrmBusinessTripApply::getDurationDays, bo.getDurationDays()) + .eq(StringUtils.isNotBlank(bo.getTripReason()), CrmBusinessTripApply::getTripReason, bo.getTripReason()) + .eq(bo.getProjectId() != null, CrmBusinessTripApply::getProjectId, bo.getProjectId()) + .eq(bo.getCustomerId() != null, CrmBusinessTripApply::getCustomerId, bo.getCustomerId()) + .eq(StringUtils.isNotBlank(bo.getExchangeObject()), CrmBusinessTripApply::getExchangeObject, + bo.getExchangeObject()) + .eq(StringUtils.isNotBlank(bo.getBusinessDirection()), CrmBusinessTripApply::getBusinessDirection, + bo.getBusinessDirection()) + .eq(StringUtils.isNotBlank(bo.getExchangePurpose()), CrmBusinessTripApply::getExchangePurpose, + bo.getExchangePurpose()) + .eq(StringUtils.isNotBlank(bo.getExchangeProcess()), CrmBusinessTripApply::getExchangeProcess, + bo.getExchangeProcess()) + .like(StringUtils.isNotBlank(bo.getMeetingName()), CrmBusinessTripApply::getMeetingName, + bo.getMeetingName()) + .eq(StringUtils.isNotBlank(bo.getFeedback()), CrmBusinessTripApply::getFeedback, bo.getFeedback()) + .eq(StringUtils.isNotBlank(bo.getTripStatus()), CrmBusinessTripApply::getTripStatus, bo.getTripStatus()) + .eq(StringUtils.isNotBlank(bo.getFlowStatus()), CrmBusinessTripApply::getFlowStatus, bo.getFlowStatus()) + .eq(StringUtils.isNotBlank(bo.getOssId()), CrmBusinessTripApply::getOssId, bo.getOssId()); return lqw; } @@ -133,8 +167,8 @@ public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplySer /** * 保存前的数据校验 */ - private void validEntityBeforeSave(CrmBusinessTripApply entity){ - //TODO 做一些数据校验,如唯一约束 + private void validEntityBeforeSave(CrmBusinessTripApply entity) { + // TODO 做一些数据校验,如唯一约束 } /** @@ -146,9 +180,94 @@ public class CrmBusinessTripApplyServiceImpl implements ICrmBusinessTripApplySer */ @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if(isValid){ - //TODO 做一些业务上的校验,判断是否需要校验 + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 } return baseMapper.deleteByIds(ids) > 0; } + + /** + * 提交出差申请并提交流程 + * + * @param bo + * @return + */ + @Override + @GlobalTransactional(rollbackFor = Exception.class) + public CrmBusinessTripApplyVo submitAndFlowStart(CrmBusinessTripApplyBo bo) { + if (bo.getTripId() == null) { + this.insertByBo(bo); + } else { + this.updateByBo(bo); + } + + if (bo.getVariables() == null) { + bo.setVariables(new java.util.HashMap<>()); + } + Map variables = bo.getVariables(); + + if (StringUtils.isNotBlank(bo.getTripType())) { + variables.put("tripType", bo.getTripType()); + } + + RemoteStartProcess startProcess = new RemoteStartProcess(); + startProcess.setBusinessId(bo.getTripId().toString()); + startProcess.setFlowCode(bo.getFlowCode()); + startProcess.setVariables(variables); + startProcess.setBizExt(bo.getBizExt()); + if (bo.getBizExt() != null) { + bo.getBizExt().setBusinessId(startProcess.getBusinessId()); + } + + boolean flag = remoteWorkflowService.startCompleteTask(startProcess); + if (!flag) { + throw new ServiceException("流程发起异常"); + } + + return this.queryById(bo.getTripId()); + } + + /** + * 总体流程监听 + * + * @param processEvent 参数 + */ + @EventListener(condition = "#processEvent.flowCode == 'OABT'") + public void processHandler(ProcessEvent processEvent) { + TenantHelper.dynamic(processEvent.getTenantId(), () -> { + log.info("【出差申请流程监听】开始处理: key={}, status={}", processEvent.getFlowCode(), processEvent.getStatus()); + + Long tripId = Convert.toLong(processEvent.getBusinessId()); + CrmBusinessTripApply tripApply = baseMapper.selectById(tripId); + + if (tripApply == null) { + log.error("未找到对应的出差申请单据,id={}", tripId); + return; + } + + tripApply.setFlowStatus(processEvent.getStatus()); + String status = processEvent.getStatus(); + + // A. 审批中 (Waiting) + if (BusinessStatusEnum.WAITING.getStatus().equals(status)) { + tripApply.setTripStatus("2"); + } + // B. 流程结束/审批通过 (Finish) + else if (BusinessStatusEnum.FINISH.getStatus().equals(status)) { + tripApply.setTripStatus("3"); + } + // C. 驳回/撤销 + else if (BusinessStatusEnum.BACK.getStatus().equals(status) + || BusinessStatusEnum.CANCEL.getStatus().equals(status)) { + tripApply.setTripStatus("1"); + } + // D. 作废/终止 + else if (BusinessStatusEnum.INVALID.getStatus().equals(status) + || BusinessStatusEnum.TERMINATION.getStatus().equals(status)) { + tripApply.setTripStatus("4"); + } + + baseMapper.updateById(tripApply); + }); + } } diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmBusinessTripApplyMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmBusinessTripApplyMapper.xml index 5866f621..e7b7eb91 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmBusinessTripApplyMapper.xml +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/crm/CrmBusinessTripApplyMapper.xml @@ -7,7 +7,39 @@