From 92aef978cc6f57260fd4abf0d8503872c24a7e21 Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 12 Nov 2025 14:24:45 +0800 Subject: [PATCH 1/3] =?UTF-8?q?change(oa/erp):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=8F=98=E6=9B=B4=E7=94=B3=E8=AF=B7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增预算变更明细和进度变更明细关联字段 - 调整排序字段类型从Long为Integer - 优化项目变更提交逻辑与权限验证 - 完善项目变更查询接口及数据组装逻辑 - 增加进度变更明细连表查询支持 - 优化删除逻辑仅允许删除暂存状态记录 - 补充创建人名称字段便于前端展示 - 调整分页查询使用自定义VO列表方法 - 完善项目变更准备逻辑支持历史数据回显 - 优化工作流发起逻辑与异常处理机制 - 增加项目经理权限校验确保操作安全 - 调整接口返回类型统一为Void或Boolean - 补充Mapper XML中的用户昵称关联查询 - 移除冗余代码并优化事务管理配置 - 增加对项目计划阶段与预算明细初始化支持 - 优化项目变更次数计算逻辑避免重复计数 - 补充相关实体类字段注释提高可读性 - 调整代码结构提升整体模块清晰度 - 增加对历史变更数据继承逻辑的支持 - 优化前后端交互流程减少无效请求调用 --- .../ErpProjectChangeController.java | 4 +- .../oa/erp/domain/ErpProjectChange.java | 19 + .../oa/erp/domain/ErpProjectChangeBudget.java | 2 +- .../erp/domain/ErpProjectChangeProgress.java | 4 +- .../domain/bo/ErpProjectChangeBudgetBo.java | 2 +- .../domain/bo/ErpProjectChangeProgressBo.java | 2 +- .../domain/vo/ErpProjectChangeBudgetVo.java | 2 +- .../domain/vo/ErpProjectChangeProgressVo.java | 2 +- .../oa/erp/domain/vo/ErpProjectChangeVo.java | 7 +- .../ErpProjectChangeProgressMapper.java | 7 + .../impl/ErpProjectChangeServiceImpl.java | 520 +++++++++++------- .../mapper/oa/erp/ErpProjectChangeMapper.xml | 27 +- .../oa/erp/ErpProjectChangeProgressMapper.xml | 11 + 13 files changed, 396 insertions(+), 213 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpProjectChangeController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpProjectChangeController.java index 60a74ee8..bc271e1e 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpProjectChangeController.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpProjectChangeController.java @@ -132,8 +132,8 @@ public class ErpProjectChangeController extends BaseController { @Log(title = "项目变更申请", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/submitAndFlowStart") - public R submitAndFlowStart(@Validated(AddGroup.class) @RequestBody ErpProjectChangeBo bo) { - return R.ok(erpProjectChangeService.projectChangeSubmitAndFlowStart(bo)); + public R submitAndFlowStart(@Validated(AddGroup.class) @RequestBody ErpProjectChangeBo bo) { + return toAjax(erpProjectChangeService.projectChangeSubmitAndFlowStart(bo)); } } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChange.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChange.java index 7f2d833f..74a25485 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChange.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChange.java @@ -9,6 +9,7 @@ import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import java.io.Serial; +import java.util.List; /** * 项目变更申请对象 erp_project_change @@ -146,4 +147,22 @@ public class ErpProjectChange extends TenantEntity { @TableLogic private String delFlag; + /** + * 预算变更明细列表 + */ + @TableField(exist = false) + private List budgetList; + + /** + * 进度变更明细列表 + */ + @TableField(exist = false) + private List progressList; + + /** + * 创建人名称 + */ + @TableField(exist = false) + private String createName; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeBudget.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeBudget.java index 24b9c930..36599c96 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeBudget.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeBudget.java @@ -68,7 +68,7 @@ public class ErpProjectChangeBudget extends TenantEntity { /** * 排序顺序 */ - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeProgress.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeProgress.java index 42e6472f..39f2aed4 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeProgress.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectChangeProgress.java @@ -78,9 +78,9 @@ public class ErpProjectChangeProgress extends TenantEntity { private BigDecimal completionDegree; /** - * 排序顺序 + * 排序 */ - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeBudgetBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeBudgetBo.java index bbf6b8a6..3399bf3b 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeBudgetBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeBudgetBo.java @@ -67,7 +67,7 @@ public class ErpProjectChangeBudgetBo extends BaseEntity { /** * 排序顺序 */ - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeProgressBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeProgressBo.java index 64641fda..5cc382b6 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeProgressBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectChangeProgressBo.java @@ -75,7 +75,7 @@ public class ErpProjectChangeProgressBo extends BaseEntity { /** * 排序顺序 */ - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeBudgetVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeBudgetVo.java index c3a87632..f6e5fb00 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeBudgetVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeBudgetVo.java @@ -82,7 +82,7 @@ public class ErpProjectChangeBudgetVo implements Serializable { * 排序顺序 */ @ExcelProperty(value = "排序顺序") - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeProgressVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeProgressVo.java index e65708c7..482481d3 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeProgressVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeProgressVo.java @@ -97,7 +97,7 @@ public class ErpProjectChangeProgressVo implements Serializable { * 排序顺序 */ @ExcelProperty(value = "排序顺序") - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeVo.java index 421eb6a3..c548b6ca 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectChangeVo.java @@ -192,13 +192,16 @@ public class ErpProjectChangeVo implements Serializable { /** * 预算变更明细列表 */ - @TableField(exist = false) private List budgetList; /** * 进度变更明细列表 */ - @TableField(exist = false) private List progressList; + /** + * 创建人名称 + */ + private String createName; + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpProjectChangeProgressMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpProjectChangeProgressMapper.java index 4e7e2356..cc40a3fa 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpProjectChangeProgressMapper.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpProjectChangeProgressMapper.java @@ -109,5 +109,12 @@ public interface ErpProjectChangeProgressMapper extends BaseMapperPlus queryWrapper); + /** + * 查询进度变更明细(连表查询项目阶段信息) + * + * @param projectChangeId 项目变更ID + * @return 进度变更明细集合 + */ + List selectProgressWithStageInfo(@Param("projectChangeId") Long projectChangeId); } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectChangeServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectChangeServiceImpl.java index f1070935..26560545 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectChangeServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectChangeServiceImpl.java @@ -11,30 +11,34 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.ObjectUtils; 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 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.mybatis.core.page.TableDataInfo; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.oa.erp.domain.*; +import org.dromara.oa.erp.domain.bo.ErpProjectChangeBo; +import org.dromara.oa.erp.domain.vo.ErpProjectChangeVo; import org.dromara.oa.erp.mapper.*; +import org.dromara.oa.erp.service.IErpProjectChangeService; 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.dromara.common.satoken.utils.LoginHelper; import org.springframework.stereotype.Service; -import org.dromara.oa.erp.domain.bo.ErpProjectChangeBo; -import org.dromara.oa.erp.domain.vo.ErpProjectChangeVo; -import org.dromara.oa.erp.service.IErpProjectChangeService; import org.springframework.transaction.annotation.Transactional; +import org.dromara.common.satoken.utils.LoginHelper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; -import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; +import java.time.LocalDate; +import java.time.ZoneId; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.github.yulichang.toolkit.JoinWrappers; +import java.math.BigDecimal; /** * 项目变更申请Service业务层处理 @@ -47,12 +51,18 @@ import java.util.stream.Collectors; @Slf4j public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { + // 计划变更主子表 private final ErpProjectChangeMapper baseMapper; private final ErpProjectChangeBudgetMapper changeBudgetMapper; private final ErpProjectChangeProgressMapper changeProgressMapper; - private final ErpProjectInfoMapper projectInfoMapper; + //项目计划主子表 private final ErpProjectPlanMapper projectPlanMapper; private final ErpProjectPlanStageMapper planStageMapper; + //项目预算主子表 + private final ErpBudgetDetailMapper budgetDetailMapper; + private final ErpBudgetInfoMapper budgetInfoMapper; + //项目信息 + private final ErpProjectInfoMapper projectInfoMapper; @DubboReference(timeout = 30000) private RemoteWorkflowService remoteWorkflowService; @@ -91,6 +101,24 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { return projectChangeVo; } +/* @Override + public ErpProjectChangeVo queryById(Long projectChangeId){ + ErpProjectChangeVo vo = baseMapper.selectCustomErpProjectChangeVoById(projectChangeId); + if (vo != null) { + // 查询预算变更明细 + List budgetList = changeBudgetMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpProjectChangeBudget::getProjectChangeId, projectChangeId) + .orderByAsc(ErpProjectChangeBudget::getSortOrder) + ); + vo.setBudgetList(budgetList); + + // 查询进度变更明细(连表查询项目阶段信息) + List progressList = changeProgressMapper.selectProgressWithStageInfo(projectChangeId); + vo.setProgressList(progressList); + } + return vo; + }*/ /** * 分页查询项目变更申请列表 @@ -102,7 +130,7 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { @Override public TableDataInfo queryPageList(ErpProjectChangeBo bo, PageQuery pageQuery) { MPJLambdaWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = baseMapper.selectCustomErpProjectChangeVoList(pageQuery.build(), lqw); return TableDataInfo.build(result); } @@ -115,7 +143,7 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { @Override public List queryList(ErpProjectChangeBo bo) { MPJLambdaWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return baseMapper.selectCustomErpProjectChangeVoList(lqw); } private MPJLambdaWrapper buildQueryWrapper(ErpProjectChangeBo bo) { @@ -148,37 +176,50 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { return lqw; } + + /** * 新增项目变更申请 * * @param bo 项目变更申请 - * @return 是否新增成功 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean insertByBo(ErpProjectChangeBo bo) { + // 权限校验:只有项目经理才能提交 + validateProjectManager(bo.getProjectManagerId()); + ErpProjectChange add = MapstructUtils.convert(bo, ErpProjectChange.class); validEntityBeforeSave(add); - List budgetList = bo.getBudgetList(); - List progressList = bo.getProgressList(); + // 设置默认状态 + if (StringUtils.isBlank(add.getProjectChangeStatus())) { + add.setProjectChangeStatus("1"); // 暂存 + } + if (StringUtils.isBlank(add.getActiveFlag())) { + add.setActiveFlag("1"); + } boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setProjectChangeId(add.getProjectChangeId()); // 保存预算变更明细 - if (ObjectUtils.isNotEmpty(budgetList)) { - for (ErpProjectChangeBudget budget : budgetList) { + if (bo.getBudgetList() != null && !bo.getBudgetList().isEmpty()) { + for (int i = 0; i < bo.getBudgetList().size(); i++) { + ErpProjectChangeBudget budget = bo.getBudgetList().get(i); budget.setProjectChangeId(add.getProjectChangeId()); + budget.setSortOrder( (i + 1)); changeBudgetMapper.insert(budget); } } // 保存进度变更明细 - if (ObjectUtils.isNotEmpty(progressList)) { - for (ErpProjectChangeProgress progress : progressList) { + if (bo.getProgressList() != null && !bo.getProgressList().isEmpty()) { + for (int i = 0; i < bo.getProgressList().size(); i++) { + ErpProjectChangeProgress progress = bo.getProgressList().get(i); progress.setProjectChangeId(add.getProjectChangeId()); + progress.setSortOrder( (i + 1)); changeProgressMapper.insert(progress); } } @@ -190,93 +231,62 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { * 修改项目变更申请 * * @param bo 项目变更申请 - * @return 是否修改成功 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean updateByBo(ErpProjectChangeBo bo) { + // 权限校验:只有项目经理才能修改 + validateProjectManager(bo.getProjectManagerId()); + + // 状态校验:只有草稿状态才能修改 + ErpProjectChange existing = baseMapper.selectById(bo.getProjectChangeId()); + if (existing == null) { + throw new ServiceException("项目变更申请不存在"); + } + if (!"1".equals(existing.getProjectChangeStatus())) { + throw new ServiceException("只有草稿状态的变更申请才能修改"); + } + ErpProjectChange update = MapstructUtils.convert(bo, ErpProjectChange.class); validEntityBeforeSave(update); - List budgetList = bo.getBudgetList(); - List progressList = bo.getProgressList(); + boolean flag = baseMapper.updateById(update) > 0; + if (flag) { + // 删除原有的预算变更明细 + changeBudgetMapper.delete(Wrappers.lambdaQuery() + .eq(ErpProjectChangeBudget::getProjectChangeId, bo.getProjectChangeId())); - // 处理预算变更明细 - if (budgetList != null && !budgetList.isEmpty()) { - for (ErpProjectChangeBudget budget : budgetList) { - budget.setProjectChangeId(bo.getProjectChangeId()); - changeBudgetMapper.insertOrUpdate(budget); + // 删除原有的进度变更明细 + changeProgressMapper.delete(Wrappers.lambdaQuery() + .eq(ErpProjectChangeProgress::getProjectChangeId, bo.getProjectChangeId())); + + // 重新保存预算变更明细 + if (bo.getBudgetList() != null && !bo.getBudgetList().isEmpty()) { + for (int i = 0; i < bo.getBudgetList().size(); i++) { + ErpProjectChangeBudget budget = bo.getBudgetList().get(i); + budget.setChangeBudgetId(null); // 清空主键,让数据库重新生成 + budget.setProjectChangeId(bo.getProjectChangeId()); + budget.setSortOrder( (i + 1)); + changeBudgetMapper.insert(budget); + } } - // 删除前端未提交的旧数据 - Set existingBudgetIds = budgetList.stream() - .map(ErpProjectChangeBudget::getChangeBudgetId) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - MPJLambdaWrapper budgetLqw = JoinWrappers.lambda(ErpProjectChangeBudget.class); - budgetLqw.eq(ErpProjectChangeBudget::getProjectChangeId, bo.getProjectChangeId()); - List oldBudgetList = changeBudgetMapper.selectList(budgetLqw); - oldBudgetList.stream() - .filter(budget -> !existingBudgetIds.contains(budget.getChangeBudgetId())) - .forEach(budget -> changeBudgetMapper.deleteById(budget.getChangeBudgetId())); - } - // 处理进度变更明细 - if (progressList != null && !progressList.isEmpty()) { - for (ErpProjectChangeProgress progress : progressList) { - progress.setProjectChangeId(bo.getProjectChangeId()); - changeProgressMapper.insertOrUpdate(progress); + // 重新保存进度变更明细 + if (bo.getProgressList() != null && !bo.getProgressList().isEmpty()) { + for (int i = 0; i < bo.getProgressList().size(); i++) { + ErpProjectChangeProgress progress = bo.getProgressList().get(i); + progress.setChangeProgressId(null); // 清空主键,让数据库重新生成 + progress.setProjectChangeId(bo.getProjectChangeId()); + progress.setSortOrder( (i + 1)); + changeProgressMapper.insert(progress); + } } - // 删除前端未提交的旧数据 - Set existingProgressIds = progressList.stream() - .map(ErpProjectChangeProgress::getChangeProgressId) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - MPJLambdaWrapper progressLqw = JoinWrappers.lambda(ErpProjectChangeProgress.class); - progressLqw.eq(ErpProjectChangeProgress::getProjectChangeId, bo.getProjectChangeId()); - List oldProgressList = changeProgressMapper.selectList(progressLqw); - oldProgressList.stream() - .filter(progress -> !existingProgressIds.contains(progress.getChangeProgressId())) - .forEach(progress -> changeProgressMapper.deleteById(progress.getChangeProgressId())); } - - return baseMapper.updateById(update) > 0; + return flag; } /** - * 保存前的数据校验 - */ - private void validEntityBeforeSave(ErpProjectChange entity){ - //TODO 做一些数据校验,如唯一约束 - } - - /** - * 校验并批量删除项目变更申请信息 - * - * @param ids 待删除的主键集合 - * @param isValid 是否进行有效性校验 - * @return 是否删除成功 - */ - @Override - @GlobalTransactional(rollbackFor = Exception.class) - public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if(isValid){ - //TODO 做一些业务上的校验,判断是否需要校验 - } - // 先删除子表 - changeBudgetMapper.delete(Wrappers.lambdaQuery().in(ErpProjectChangeBudget::getProjectChangeId, ids)); - changeProgressMapper.delete(Wrappers.lambdaQuery().in(ErpProjectChangeProgress::getProjectChangeId, ids)); - // 删除主表 - baseMapper.deleteByIds(ids); - //删除工作流 - List businessIds = ids.stream().toList(); - return remoteWorkflowService.deleteInstance(businessIds); - } - - /** - * 根据项目 ID 准备项目变更信息(带出项目信息和项目计划阶段) - * - * @param projectId 项目 ID - * @return 项目变更信息 + * 根据项目ID准备项目变更信息 */ @Override public ErpProjectChangeVo prepareByProjectWithInfo(Long projectId) { @@ -284,124 +294,268 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { throw new ServiceException("项目ID不能为空"); } - // 若存在审批中的项目变更则禁止再次发起 - long approvingCount = baseMapper.selectCount(Wrappers.lambdaQuery() - .eq(ErpProjectChange::getProjectId, projectId) - .eq(ErpProjectChange::getDelFlag, "0") - .eq(ErpProjectChange::getProjectChangeStatus, OAStatusEnum.APPROVING.getStatus()));// "2"审批中,"3"可用 - if (approvingCount > 0) { - throw new ServiceException("该项目存在未完成的变更申请,请等待审批完成后再发起新的变更"); + // 检查是否有未完成的变更(状态1或2) + List existingChanges = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpProjectChange::getProjectId, projectId) + .in(ErpProjectChange::getProjectChangeStatus, Arrays.asList("1", "2")) + .orderByDesc(ErpProjectChange::getCreateTime) + ); + + if (!existingChanges.isEmpty()) { + // 如果有暂存记录,返回该记录供继续编辑 + ErpProjectChange draftChange = existingChanges.stream() + .filter(change -> "1".equals(change.getProjectChangeStatus())) + .findFirst().orElse(null); + if (draftChange != null) { + return queryById(draftChange.getProjectChangeId()); + } + throw new ServiceException("该项目存在未完成的变更申请,请先完成或取消后再发起新的变更"); } - // 如果已有暂存记录,返回该记录供继续编辑 - ErpProjectChange draftChange = baseMapper.selectOne(Wrappers.lambdaQuery() - .eq(ErpProjectChange::getProjectId, projectId) - .eq(ErpProjectChange::getDelFlag, "0") - .eq(ErpProjectChange::getProjectChangeStatus, "1") - .orderByDesc(ErpProjectChange::getCreateTime) - .last("limit 1")); - if (draftChange != null) { - return queryById(draftChange.getProjectChangeId()); - } - - // 通过自定义Mapper方法查询项目信息和用户名称(连表查询sys_user) + // 查询项目信息 ErpProjectChangeVo vo = baseMapper.prepareByProjectId(projectId); if (vo == null) { - throw new ServiceException("项目不存在"); + throw new ServiceException("项目信息不存在"); } - // 查询项目计划信息 - MPJLambdaWrapper planLqw = JoinWrappers.lambda(ErpProjectPlan.class) - .eq(ErpProjectPlan::getProjectId, projectId) - .eq(ErpProjectPlan::getDelFlag, "0"); - ErpProjectPlan projectPlan = projectPlanMapper.selectOne(planLqw); + // 计算变更次数(已完成的变更数量) + Long changeCount = baseMapper.selectCount( + Wrappers.lambdaQuery() + .eq(ErpProjectChange::getProjectId, projectId) + .eq(ErpProjectChange::getProjectChangeStatus, "3") + ); + vo.setChangeNumber(changeCount.intValue() + 1); - if (projectPlan != null) { - // 查询项目计划阶段 - MPJLambdaWrapper stageLqw = JoinWrappers.lambda(ErpProjectPlanStage.class) - .eq(ErpProjectPlanStage::getProjectPlanId, projectPlan.getProjectPlanId()) - .eq(ErpProjectPlanStage::getDelFlag, "0") - .orderByAsc(ErpProjectPlanStage::getSortOrder); - List planStageList = planStageMapper.selectList(stageLqw); + // 如果存在已完成的变更,将最新一次的变更数据作为默认值 + if (changeCount > 0) { + ErpProjectChange lastChange = baseMapper.selectOne( + Wrappers.lambdaQuery() + .eq(ErpProjectChange::getProjectId, projectId) + .eq(ErpProjectChange::getProjectChangeStatus, "3") + .orderByDesc(ErpProjectChange::getCreateTime) + .last("limit 1") + ); - // 将项目阶段转换为进度变更明细 - if (ObjectUtils.isNotEmpty(planStageList)) { - List progressList = planStageList.stream().map(stage -> { - ErpProjectChangeProgress progress = new ErpProjectChangeProgress(); - progress.setPlanStageId(stage.getPlanStageId()); // 绑定项目计划阶段ID - progress.setProjectPhases(stage.getProjectPhases()); // 项目阶段字典值(禁用显示) - progress.setMilestoneName(""); // 里程碑名称由用户手动输入 - progress.setOriginalStart(stage.getPlanStartTime()); // 原计划开始时间 - progress.setOriginalEnd(stage.getPlanEndTime()); // 原计划结束时间 - progress.setChangedStart(stage.getPlanStartTime()); // 初始值设为原计划时间 - progress.setChangedEnd(stage.getPlanEndTime()); // 初始值设为原计划时间 - progress.setSortOrder(stage.getSortOrder()); // 排序顺序 - return progress; - }).collect(Collectors.toList()); + if (lastChange != null) { + // 复制上一次变更的项目当前情况说明等信息作为参考 + vo.setCurrentStatus(lastChange.getCurrentStatus()); + vo.setChangeReason(""); // 变更原因需要重新填写 + vo.setFollowUpWork(""); // 后续工作需要重新填写 + + // 查询上一次变更的预算变更明细作为参考 + List lastBudgetList = changeBudgetMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpProjectChangeBudget::getProjectChangeId, lastChange.getProjectChangeId()) + .orderByAsc(ErpProjectChangeBudget::getSortOrder) + ); + + List budgetList = new ArrayList<>(); + // 将上一次的变更后预算作为本次的变更前预算 + for (ErpProjectChangeBudget lastBudget : lastBudgetList) { + ErpProjectChangeBudget newBudget = new ErpProjectChangeBudget(); + newBudget.setBudgetDetailId(lastBudget.getBudgetDetailId()); + newBudget.setSubjectName(lastBudget.getSubjectName()); + newBudget.setBudgetBefore(lastBudget.getBudgetAfter()); // 上次变更后 = 本次变更前 + newBudget.setBudgetAfter(lastBudget.getBudgetAfter()); // 默认值 + newBudget.setAmountUsed(lastBudget.getAmountUsed()); + newBudget.setSortOrder(lastBudget.getSortOrder()); + budgetList.add(newBudget); + } + vo.setBudgetList(budgetList); + + // 查询上一次变更的进度变更明细作为参考 + List lastProgressList = changeProgressMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpProjectChangeProgress::getProjectChangeId, lastChange.getProjectChangeId()) + .orderByAsc(ErpProjectChangeProgress::getSortOrder) + ); + + List progressList = new ArrayList<>(); + // 将上一次的变更后时间作为本次的原计划时间 + for (ErpProjectChangeProgress lastProgress : lastProgressList) { + ErpProjectChangeProgress newProgress = new ErpProjectChangeProgress(); + newProgress.setPlanStageId(lastProgress.getPlanStageId()); + newProgress.setProjectPhases(lastProgress.getProjectPhases()); + newProgress.setMilestoneName(lastProgress.getMilestoneName()); + newProgress.setOriginalStart(lastProgress.getChangedStart()); // 上次变更后 = 本次原计划 + newProgress.setOriginalEnd(lastProgress.getChangedEnd()); // 上次变更后 = 本次原计划 + newProgress.setChangedStart(lastProgress.getChangedStart()); // 默认值 + newProgress.setChangedEnd(lastProgress.getChangedEnd()); // 默认值 + newProgress.setCompletionDegree(lastProgress.getCompletionDegree()); + newProgress.setSortOrder(lastProgress.getSortOrder()); + progressList.add(newProgress); + } vo.setProgressList(progressList); } } - // 初始化空的预算变更列表(由用户手动添加) - vo.setBudgetList(new ArrayList<>()); + // 设置申请变更日期为当前日期 + vo.setApplyChangeDate(Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant())); - // 计算变更次数:查询该项目已完成的变更次数 - MPJLambdaWrapper changeCountLqw = JoinWrappers.lambda(ErpProjectChange.class) - .eq(ErpProjectChange::getProjectId, projectId) - .eq(ErpProjectChange::getDelFlag, "0") - .eq(ErpProjectChange::getProjectChangeStatus, OAStatusEnum.COMPLETED.getStatus()); - int changeCount = Math.toIntExact(baseMapper.selectCount(changeCountLqw)); + // 如果没有历史变更记录,从项目计划阶段和预算明细中初始化数据 + if (changeCount == 0) { + // 查询项目计划阶段,转换为进度变更明细 + List planStages = planStageMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpProjectPlanStage::getProjectId, projectId) + .orderByAsc(ErpProjectPlanStage::getSortOrder) + ); - vo.setChangeNumber(changeCount + 1); // 当前变更次数 = 已有次数 + 1 + List progressList = new ArrayList<>(); + for (ErpProjectPlanStage stage : planStages) { + ErpProjectChangeProgress progress = new ErpProjectChangeProgress(); + progress.setPlanStageId(stage.getPlanStageId()); + progress.setProjectPhases(stage.getProjectPhases()); + progress.setMilestoneName(""); // 里程碑名称由用户手动输入 + progress.setOriginalStart(stage.getPlanStartTime()); + progress.setOriginalEnd(stage.getPlanEndTime()); + progress.setChangedStart(stage.getPlanStartTime()); // 默认值 + progress.setChangedEnd(stage.getPlanEndTime()); // 默认值 + progress.setCompletionDegree(BigDecimal.ZERO); // 默认完成程度 + progress.setSortOrder(stage.getSortOrder()); + progressList.add(progress); + } + vo.setProgressList(progressList); - // 设置默认值 + // 查询项目预算明细,转换为预算变更明细 + // 首先查询项目的有效预算 + ErpBudgetInfo budgetInfo = budgetInfoMapper.selectOne( + Wrappers.lambdaQuery() + .eq(ErpBudgetInfo::getProjectId, projectId) + .eq(ErpBudgetInfo::getBudgetStatus, "3") // 可用状态 + .orderByDesc(ErpBudgetInfo::getBudgetVersion) + .last("limit 1") + ); + + List budgetList = new ArrayList<>(); + if (budgetInfo != null) { + // 查询预算明细 + List budgetDetails = budgetDetailMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpBudgetDetail::getBudgetId, budgetInfo.getBudgetId()) + .orderByAsc(ErpBudgetDetail::getSortOrder) + ); + + for (ErpBudgetDetail detail : budgetDetails) { + ErpProjectChangeBudget budget = new ErpProjectChangeBudget(); + budget.setBudgetDetailId(detail.getBudgetDetailId()); + budget.setSubjectName(detail.getBudgetItem()); + //xins的预算表数据库是decimal,实体类ErpBudgetDetail却是Long,所以这里需要转换一下 + BigDecimal budgetCost = BigDecimal.valueOf(detail.getBudgetCost()); + budget.setBudgetBefore(budgetCost); + budget.setBudgetAfter(budgetCost); // 默认值 + budget.setAmountUsed(BigDecimal.ZERO); // 默认已使用金额为0 + budget.setSortOrder(Math.toIntExact(detail.getSortOrder())); + budgetList.add(budget); + } + } + vo.setBudgetList(budgetList); + } + + // 设置默认状态 + vo.setProjectChangeStatus("1"); // 暂存 vo.setActiveFlag("1"); - vo.setProjectChangeStatus("1"); // 1-暂存 return vo; } /** * 提交项目变更并发起审批流 - * - * @param bo 项目变更申请 - * @return 是否成功 */ @Override @GlobalTransactional(rollbackFor = Exception.class) - public ErpProjectChangeVo projectChangeSubmitAndFlowStart(ErpProjectChangeBo bo) { - - ErpProjectChange add = MapstructUtils.convert(bo, ErpProjectChange.class); - validEntityBeforeSave(add) ; + public Boolean projectChangeSubmitAndFlowStart(ErpProjectChangeBo bo) { // 权限校验:只有项目经理才能提交 validateProjectManager(bo.getProjectManagerId()); - // 保存或更新项目变更数据 - boolean saveFlag; + // 检查是否有未完成的变更 + List existingChanges = baseMapper.selectList( + Wrappers.lambdaQuery() + .eq(ErpProjectChange::getProjectId, bo.getProjectId()) + .in(ErpProjectChange::getProjectChangeStatus, Arrays.asList("1", "2")) + .ne(bo.getProjectChangeId() != null, ErpProjectChange::getProjectChangeId, bo.getProjectChangeId()) + ); + + if (!existingChanges.isEmpty()) { + throw new ServiceException("该项目存在其他未完成的变更申请,请先完成或取消后再提交"); + } + + // 设置状态为审批中 +// bo.setProjectChangeStatus("2"); +// bo.setFlowStatus("running"); + + Boolean result; if (bo.getProjectChangeId() == null) { - saveFlag = insertByBo(bo); + result = insertByBo(bo); } else { - saveFlag = updateByBo(bo); + result = updateByBo(bo); } - if (!saveFlag) { - throw new ServiceException("保存项目变更失败"); + if (result) { + // 发起审批流程 + // 后端发起需要忽略权限 + bo.getVariables().put("ignore", true); + RemoteStartProcess startProcess = new RemoteStartProcess(); + startProcess.setBusinessId(bo.getProjectChangeId().toString()); + startProcess.setFlowCode(bo.getFlowCode()); + startProcess.setVariables(bo.getVariables()); + startProcess.setBizExt(bo.getBizExt()); + bo.getBizExt().setBusinessId(startProcess.getBusinessId()); + boolean flagOne = remoteWorkflowService.startCompleteTask(startProcess); + if (!flagOne) { + throw new ServiceException("发起审批流程失败"); + } } - // 后端发起需要忽略权限 - bo.getVariables().put("ignore", true); - RemoteStartProcess startProcess = new RemoteStartProcess(); - startProcess.setBusinessId(bo.getProjectChangeId().toString()); - startProcess.setFlowCode(bo.getFlowCode()); - startProcess.setVariables(bo.getVariables()); - startProcess.setBizExt(bo.getBizExt()); - bo.getBizExt().setBusinessId(startProcess.getBusinessId()); - boolean flagOne = remoteWorkflowService.startCompleteTask(startProcess); - if (!flagOne) { - throw new ServiceException("流程发起异常"); + return result; + } + + + /** + * 权限校验:只有项目经理才能提交 + */ + private void validateProjectManager(Long projectManagerId) { + // 超级管理员跳过校验 + if (LoginHelper.isSuperAdmin()) { + return; } - return MapstructUtils.convert(add, ErpProjectChangeVo.class); + Long currentUserId = LoginHelper.getUserId(); + if (!Objects.equals(currentUserId, projectManagerId)) { + throw new ServiceException("只有项目经理才能提交项目计划变更"); + } + } + + /** + * 校验并批量删除项目变更申请信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + // 校验是否可以删除(只能删除暂存状态的记录) + List changes = baseMapper.selectBatchIds(ids); + for (ErpProjectChange change : changes) { + if (!"1".equals(change.getProjectChangeStatus())) { + throw new ServiceException("只能删除暂存状态的变更申请"); + } + } + } + + // 删除主表和子表数据 + for (Long id : ids) { + changeBudgetMapper.delete(Wrappers.lambdaQuery() + .eq(ErpProjectChangeBudget::getProjectChangeId, id)); + changeProgressMapper.delete(Wrappers.lambdaQuery() + .eq(ErpProjectChangeProgress::getProjectChangeId, id)); + } + + return baseMapper.deleteBatchIds(ids) > 0; + } + + private void validEntityBeforeSave(ErpProjectChange entity){ + //TODO 做一些数据校验,如唯一约束 } /** @@ -426,20 +580,4 @@ public class ErpProjectChangeServiceImpl implements IErpProjectChangeService { }); } - /** - * 校验项目经理权限 - * - * @param projectManagerId 项目经理ID - */ - private void validateProjectManager(Long projectManagerId) { - // 超级管理员跳过校验 - if (LoginHelper.isSuperAdmin()) { - return; - } - - Long currentUserId = LoginHelper.getUserId(); - if (!currentUserId.equals(projectManagerId)) { - throw new ServiceException("只有项目经理才能提交项目变更申请"); - } - } } diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeMapper.xml index d9e7e207..9db0fd87 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeMapper.xml +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeMapper.xml @@ -7,35 +7,39 @@ @@ -309,7 +313,7 @@ + diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeProgressMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeProgressMapper.xml index bd47a671..3c308a16 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeProgressMapper.xml +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectChangeProgressMapper.xml @@ -195,5 +195,16 @@ ${ew.getCustomSqlSegment} + + From e8fd53aa4496307d00a2818dbaab3d489ad5902d Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 12 Nov 2025 14:26:47 +0800 Subject: [PATCH 2/3] =?UTF-8?q?refactor(erp):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=AE=A1=E5=88=92=E9=98=B6=E6=AE=B5=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 ErpProjectPlanStage 实体类中的 sortOrder 字段类型从 Long 改为 Integer - 同步修改 Bo 和 Vo 类中对应的 sortOrder 字段类型 - 调整代码顺序,确保业务逻辑清晰执行 --- .../java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java | 2 +- .../org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java | 2 +- .../org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java | 2 +- .../dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java index 2a89b4a1..eec21538 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java @@ -107,7 +107,7 @@ public class ErpProjectPlanStage extends TenantEntity { /** * 排序号 */ - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java index 687f4c7f..5dadcb76 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java @@ -106,7 +106,7 @@ public class ErpProjectPlanStageBo extends BaseEntity { /** * 排序号 */ - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java index 8c915889..6230e098 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java @@ -132,7 +132,7 @@ public class ErpProjectPlanStageVo implements Serializable { * 排序号 */ @ExcelProperty(value = "排序号") - private Long sortOrder; + private Integer sortOrder; /** * 备注 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java index 892c87d3..c7c2c4d5 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java @@ -283,12 +283,12 @@ public class ErpProjectPlanServiceImpl implements IErpProjectPlanService { if(isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } - List businessIds = ids.stream().toList(); // 先删除子表 planStageMapper.delete(Wrappers.lambdaQuery() .in(ErpProjectPlanStage::getProjectPlanId, ids)); baseMapper.deleteByIds(ids); //删除工作流 + List businessIds = ids.stream().toList(); return remoteWorkflowService.deleteInstance(businessIds); } From 9fc158292999d1af57ecfa77e224986045f13354 Mon Sep 17 00:00:00 2001 From: xs Date: Wed, 12 Nov 2025 14:46:48 +0800 Subject: [PATCH 3/3] =?UTF-8?q?1.0.35.00=EF=BC=9A=20=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=E5=B0=81=E8=A3=85=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=9A=E8=A1=A5?= =?UTF-8?q?=E5=85=85pom.xml=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-common/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 6622dd2e..91d833b9 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -44,6 +44,7 @@ ruoyi-common-nacos ruoyi-common-bus ruoyi-common-sse + hwbm-common-workflow ruoyi-common