From 8ec44bf9eec68779900839f758a908b44cba1b34 Mon Sep 17 00:00:00 2001 From: yinq Date: Sat, 7 Mar 2026 14:39:18 +0800 Subject: [PATCH] =?UTF-8?q?1.0.75=20=E5=90=88=E5=90=8C=E6=BF=80=E6=B4=BB?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=EF=BC=88=E5=A4=9A=E4=B8=AA=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=88=86=E9=87=91=E9=A2=9D=EF=BC=89=E6=B7=BB=E5=8A=A0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=A0=87=E8=AF=86=EF=BC=88=E5=8F=AF=E7=94=9F=E6=88=90?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=88=96=E9=80=89=E6=8B=A9=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E3=80=90=E7=9D=BF=E5=8E=9F=E5=AE=A1=E6=89=B9=E3=80=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oa/erp/domain/ErpProjectContracts.java | 5 + .../erp/domain/bo/ErpProjectContractsBo.java | 59 ++++++- .../erp/domain/vo/ErpProjectContractsVo.java | 35 ++++ .../impl/ErpContractOrderServiceImpl.java | 155 ++++++++++++++++++ .../impl/ErpProjectInfoServiceImpl.java | 1 + .../oa/erp/ErpProjectContractsMapper.xml | 7 + 6 files changed, 259 insertions(+), 3 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectContracts.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectContracts.java index 457964bb..422d3e91 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectContracts.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectContracts.java @@ -52,6 +52,11 @@ public class ErpProjectContracts extends TenantEntity { */ private String activeFlag; + /** + * 项目来源(1新增项目 0选择已有项目) + */ + private String projectSource; + /** * 删除标志(0代表存在 1代表删除) */ diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectContractsBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectContractsBo.java index c89e2a5f..f36deaa0 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectContractsBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectContractsBo.java @@ -2,12 +2,11 @@ package org.dromara.oa.erp.domain.bo; import org.dromara.oa.erp.domain.ErpProjectContracts; import org.dromara.common.mybatis.core.domain.BaseEntity; -import org.dromara.common.core.validate.AddGroup; -import org.dromara.common.core.validate.EditGroup; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import lombok.EqualsAndHashCode; -import jakarta.validation.constraints.*; + +import java.math.BigDecimal; /** * 项目关联合同业务对象 erp_project_contracts @@ -50,5 +49,59 @@ public class ErpProjectContractsBo extends BaseEntity { */ private String activeFlag; + /** + * 项目来源(1新增项目 0选择已有项目) + */ + private String projectSource; + + /** + * 项目编号 + */ + private String projectCode; + + /** + * 项目名称 + */ + private String projectName; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 项目经理 + */ + private Long managerId; + + /** + * 部门负责人 + */ + private Long chargeId; + + /** + * 分管副总 + */ + private Long deputyId; + + /** + * 抄送人员 + */ + private String peopleId; + + /** + * 项目金额 + */ + private BigDecimal amount; + + /** + * 项目类别(1销售(实施、物流) 2销售(备件)) + */ + private String projectCategory; + + /** + * 是否备件类(1是 0否) + */ + private String spareFlag; } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectContractsVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectContractsVo.java index 74268d59..0c25ed35 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectContractsVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectContractsVo.java @@ -66,6 +66,17 @@ public class ErpProjectContractsVo implements Serializable { @ExcelDictFormat(dictType = "active_flag") private String activeFlag; + /** + * 项目来源(1新增项目 0选择已有项目) + */ + private String projectSource; + + /** + * 项目编号 + */ + @ExcelProperty(value = "项目编号") + private String projectCode; + /** * 项目名称 */ @@ -90,6 +101,24 @@ public class ErpProjectContractsVo implements Serializable { @ExcelProperty(value = "合同总价") private BigDecimal totalPrice; + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; + + /** + * 项目经理名称 + */ + @ExcelProperty(value = "项目经理名称") + private String managerName; + + /** + * 项目金额 + */ + @ExcelProperty(value = "项目金额") + private BigDecimal amount; + /** * 业务方向 */ @@ -104,5 +133,11 @@ public class ErpProjectContractsVo implements Serializable { @ExcelDictFormat(dictType = "contract_status") private String contractStatus; + /** + * 项目类别(1销售(实施、物流) 2销售(备件)) + */ + @ExcelProperty(value = "项目类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "project_category") + private String projectCategory; } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpContractOrderServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpContractOrderServiceImpl.java index f0deb5f9..3b3f95f9 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpContractOrderServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpContractOrderServiceImpl.java @@ -13,17 +13,23 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.oa.erp.domain.ErpContractInfo; import org.dromara.oa.erp.domain.ErpProjectInfo; +import org.dromara.oa.erp.domain.ErpProjectContracts; import org.dromara.oa.erp.domain.ErpProjectPlan; import org.dromara.oa.erp.domain.ErpProjectPlanStage; import org.dromara.oa.erp.domain.bo.ErpProjectInfoBo; +import org.dromara.oa.erp.domain.bo.ErpProjectContractsBo; import org.dromara.oa.erp.domain.bo.ErpProjectPlanStageBo; import org.dromara.oa.erp.domain.vo.ErpContractInfoVo; import org.dromara.oa.erp.domain.vo.ErpProjectInfoVo; import org.dromara.oa.erp.mapper.ErpContractInfoMapper; import org.dromara.oa.erp.mapper.ErpProjectInfoMapper; +import org.dromara.oa.erp.mapper.ErpProjectContractsMapper; import org.dromara.oa.erp.mapper.ErpProjectPlanMapper; import org.dromara.oa.erp.mapper.ErpProjectPlanStageMapper; import org.dromara.oa.erp.service.IErpContractOrderService; +import org.dromara.oa.erp.service.IErpProjectInfoService; +import org.dromara.oa.erp.service.IErpProjectTypeService; +import org.dromara.oa.erp.domain.vo.ErpProjectTypeVo; import org.dromara.oa.erp.constant.ProjectCategoryConstant; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; @@ -33,6 +39,7 @@ import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.dromara.system.api.RemoteCodeRuleService; import org.dromara.workflow.api.RemoteWorkflowService; import org.dromara.workflow.api.domain.RemoteStartProcess; +import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt; import org.dromara.workflow.api.event.ProcessEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; @@ -41,6 +48,7 @@ import org.apache.dubbo.config.annotation.DubboReference; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import cn.hutool.core.map.MapUtil; import cn.hutool.core.convert.Convert; @@ -60,7 +68,13 @@ public class ErpContractOrderServiceImpl implements IErpContractOrderService { private final ErpProjectPlanMapper projectPlanMapper; private final ErpProjectPlanStageMapper planStageMapper; private final ErpContractInfoMapper contractInfoMapper; + private final ErpProjectContractsMapper projectContractsMapper; + private final IErpProjectInfoService projectInfoService; + private final IErpProjectTypeService projectTypeService; + + /** 项目申请流程编码(新增项目提交时使用) */ + private static final String FLOW_CODE_PROJECT = "xmsq"; @DubboReference(timeout = 300000) private RemoteWorkflowService remoteWorkflowService; @@ -182,9 +196,97 @@ public class ErpContractOrderServiceImpl implements IErpContractOrderService { // 保存项目阶段计划列表 savePlanStageList(projectInfo, bo.getPlanStageList()); + // 保存项目关联合同列表 + saveProjectContractsByContract(bo); + return MapstructUtils.convert(projectInfo, ErpProjectInfoVo.class); } + /** + * 根据合同维度维护项目关联合同列表 + * + * @param bo 合同订单(项目信息)业务对象 + */ + private void saveProjectContractsByContract(ErpProjectInfoBo bo) { + Long contractId = bo.getContractId(); + if (contractId == null) { + return; + } + List projectContractsList = bo.getProjectContractsList(); + if (projectContractsList == null) { + return; + } + + MPJLambdaWrapper lqwRecord = JoinWrappers.lambda(ErpProjectContracts.class); + lqwRecord.eq(ErpProjectContracts::getContractId, contractId); + List projectContractsOldList = projectContractsMapper.selectList(lqwRecord); + + if (!projectContractsList.isEmpty()) { + // 原有关联 ID 集合 + Set oldIds = projectContractsOldList.stream() + .map(ErpProjectContracts::getProjectContractsId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 保存新列表(新增项目 + 新增/更新关联合同) + for (ErpProjectContractsBo projectContractsBo : projectContractsList) { + // 项目来源默认:无 projectId 为新增项目(1),否则为选择已有项目(0) + if (StringUtils.isBlank(projectContractsBo.getProjectSource())) { + projectContractsBo.setProjectSource(projectContractsBo.getProjectId() == null ? "1" : "0"); + } + // 如果没有项目ID,说明需要在同一事务中新增项目 + if (projectContractsBo.getProjectId() == null) { + ErpProjectInfo newProject = new ErpProjectInfo(); + newProject.setProjectCode(projectContractsBo.getProjectCode()); + newProject.setProjectName(projectContractsBo.getProjectName()); + newProject.setDeptId(projectContractsBo.getDeptId()); + newProject.setManagerId(projectContractsBo.getManagerId()); + newProject.setChargeId(projectContractsBo.getChargeId()); + newProject.setDeputyId(projectContractsBo.getDeputyId()); + newProject.setPeopleId(projectContractsBo.getPeopleId()); + newProject.setAmount(projectContractsBo.getAmount()); + newProject.setProjectCategory(projectContractsBo.getProjectCategory()); + newProject.setSpareFlag(projectContractsBo.getSpareFlag()); + newProject.setBusinessDirection(bo.getBusinessDirection()); + newProject.setProjectTypeId(bo.getProjectTypeId()); + newProject.setContractFlag("1"); + newProject.setProjectStatus(OAStatusEnum.DRAFT.getStatus()); + newProject.setFlowStatus(BusinessStatusEnum.DRAFT.getStatus()); + validEntityBeforeSave(newProject); + projectInfoMapper.insert(newProject); + projectContractsBo.setProjectId(newProject.getProjectId()); + } + + projectContractsBo.setContractId(contractId); + ErpProjectContracts projectContracts = MapstructUtils.convert(projectContractsBo, ErpProjectContracts.class); + if (projectContracts.getProjectContractsId() == null || !oldIds.contains(projectContracts.getProjectContractsId())) { + projectContracts.setProjectContractsId(null); + projectContractsMapper.insert(projectContracts); + } else { + projectContractsMapper.insertOrUpdate(projectContracts); + } + } + + // 删除在旧列表中但不在新列表中的关联 + Set existingIds = projectContractsList.stream() + .map(ErpProjectContractsBo::getProjectContractsId) + .filter(Objects::nonNull) + .filter(oldIds::contains) + .collect(Collectors.toSet()); + List toDelete = projectContractsOldList.stream() + .filter(contract -> !existingIds.contains(contract.getProjectContractsId())) + .toList(); + for (ErpProjectContracts item : toDelete) { + projectContractsMapper.deleteById(item.getProjectContractsId()); + } + } else { + // 新列表为空:删除该合同下所有原有关联 + for (ErpProjectContracts item : projectContractsOldList) { + projectContractsMapper.deleteById(item.getProjectContractsId()); + } + } + } + /** * 修改合同订单(项目信息) * @@ -218,11 +320,64 @@ public class ErpContractOrderServiceImpl implements IErpContractOrderService { startProcess.setBizExt(bo.getBizExt()); bo.getBizExt().setBusinessId(startProcess.getBusinessId()); bo.getBizExt().setBusinessCode(bo.getProjectCode()); + // 含有选择已有项目(projectSource=0):走主流程“提交合同订单并提交流程”,variables 中由前端传入 hasExistProject boolean flagOne = remoteWorkflowService.startCompleteTask(startProcess); if (!flagOne) { throw new ServiceException("流程发起异常"); } + // 关联项目:仅新增项目走项目申请流程;选择已有项目不走 xzxm,仅走主流程(variables 含 hasExistProject) + List projectContractsList = bo.getProjectContractsList(); + if (CollUtil.isNotEmpty(projectContractsList)) { + for (ErpProjectContractsBo pc : projectContractsList) { + if (pc.getProjectId() == null) { + continue; + } + try { + if ("1".equals(pc.getProjectSource())) { + // 新增项目:走项目申请流程,variables/bizExt 与项目申请页 edit 保持一致 + ErpProjectInfoVo projectVo = projectInfoService.queryById(pc.getProjectId()); + if (projectVo != null) { + ErpProjectInfoBo projectBo = new ErpProjectInfoBo(); + projectBo.setProjectId(projectVo.getProjectId()); + projectBo.setProjectCode(projectVo.getProjectCode()); + projectBo.setProjectName(projectVo.getProjectName()); + projectBo.setContractFlag(projectVo.getContractFlag()); + projectBo.setProjectTypeId(projectVo.getProjectTypeId()); + projectBo.setFlowCode(FLOW_CODE_PROJECT); + Map variables = projectBo.getVariables(); + variables.put("ignore", true); + variables.put("projectId", projectVo.getProjectId()); + variables.put("projectCode", projectVo.getProjectCode()); + variables.put("projectName", projectVo.getProjectName()); + variables.put("contractFlag", Convert.toInt(projectVo.getContractFlag())); + Long projectTypeParentId = null; + if (projectVo.getProjectTypeId() != null) { + ErpProjectTypeVo projectTypeVo = projectTypeService.queryById(projectVo.getProjectTypeId()); + if (projectTypeVo != null) { + projectTypeParentId = projectTypeVo.getParentId(); + } + } + variables.put("projectTypeParentId", projectTypeParentId); + RemoteFlowInstanceBizExt projectBizExt = projectBo.getBizExt(); + if (projectBizExt == null) { + projectBizExt = new RemoteFlowInstanceBizExt(); + projectBo.setBizExt(projectBizExt); + } + projectBizExt.setBusinessId(String.valueOf(projectVo.getProjectId())); + projectBizExt.setBusinessCode(projectVo.getProjectCode()); + projectBizExt.setBusinessTitle(projectVo.getProjectName() != null ? projectVo.getProjectName() + "市场项目审批" : "市场项目审批"); + projectBo.setProjectStatus(OAStatusEnum.APPROVING.getStatus()); + projectBo.setFlowStatus(BusinessStatusEnum.WAITING.getStatus()); + projectInfoService.projectSubmitAndFlowStart(projectBo); + } + } + } catch (Exception e) { + log.warn("关联项目流程发起失败 projectId={} projectSource={}", pc.getProjectId(), pc.getProjectSource(), e); + } + } + } + return vo; } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectInfoServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectInfoServiceImpl.java index 2318327c..11a6cae3 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectInfoServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectInfoServiceImpl.java @@ -150,6 +150,7 @@ public class ErpProjectInfoServiceImpl implements IErpProjectInfoService { for (ErpProjectContractsBo projectContractsBo : projectContractsList) { projectContractsBo.setProjectId(add.getProjectId()); ErpProjectContracts projectContracts = MapstructUtils.convert(projectContractsBo, ErpProjectContracts.class); + projectContracts.setProjectContractsId(null); projectContractsMapper.insert(projectContracts); } } diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectContractsMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectContractsMapper.xml index ec19551c..7f7e96de 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectContractsMapper.xml +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectContractsMapper.xml @@ -14,13 +14,18 @@ t.sort_order, t.remark, t.active_flag, + t.project_source, t.del_flag, t.create_dept, t.create_by, t.create_time, t.update_by, t.update_time, + e.project_code, e.project_name, + d.dept_name deptName, + u.nick_name managerName, + e.amount amount, c.contract_code, c.contract_name, c.total_price, @@ -28,6 +33,8 @@ c.contract_status from erp_project_contracts t left join erp_project_info e on e.project_id = t.project_id + left join sys_dept d on d.dept_id = e.dept_id + left join sys_user u on u.user_id = e.manager_id left join erp_contract_info c on c.contract_id = t.contract_id ${ew.getCustomSqlSegment}