|
|
@@ -0,0 +1,259 @@
|
|
|
+package cn.iocoder.yudao.module.rd.service.researchProject;
|
|
|
+
|
|
|
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
+import cn.iocoder.yudao.module.rd.controller.admin.researchProject.vo.ProjectPageReqVO;
|
|
|
+import cn.iocoder.yudao.module.rd.controller.admin.researchProject.vo.ProjectRespVO;
|
|
|
+import cn.iocoder.yudao.module.rd.controller.admin.researchProject.vo.ProjectSaveReqVO;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.dataobject.researchProject.ProjectBudgetDO;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.dataobject.researchProject.ProjectDO;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.dataobject.researchProject.ProjectScheduleDO;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.mysql.researchProject.ProjectApprovalRecordMapper;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.mysql.researchProject.ProjectBudgetMapper;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.mysql.researchProject.ProjectMapper;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.mysql.researchProject.ProjectScheduleMapper;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.validation.annotation.Validated;
|
|
|
+
|
|
|
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static cn.iocoder.yudao.module.rd.enums.ErrorCodeConstant.*;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+
|
|
|
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static cn.iocoder.yudao.module.rd.enums.ErrorCodeConstant.PROJECT_NOT_EXISTS;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * 项目立项 Service 实现类
|
|
|
+ *
|
|
|
+ * @author ruiqi
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Validated
|
|
|
+public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProjectMapper projectMapper;
|
|
|
+ @Resource
|
|
|
+ private ProjectScheduleMapper projectScheduleMapper;
|
|
|
+ @Resource
|
|
|
+ private ProjectBudgetMapper projectBudgetMapper;
|
|
|
+ @Resource
|
|
|
+ private ProjectApprovalRecordMapper approvalRecordMapper;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增项目
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public Long createProject(ProjectSaveReqVO createReqVO) {
|
|
|
+ ProjectDO project = BeanUtils.toBean(createReqVO, ProjectDO.class);
|
|
|
+
|
|
|
+ // 排序
|
|
|
+ project.setSort(getMaxSort() + 1);
|
|
|
+
|
|
|
+ // ⭐ 关键:初始化审批状态为"草稿"
|
|
|
+ project.setPreAuditStatus(AUDIT_STATUS_DRAFT); // 0 预审未提交
|
|
|
+ project.setPreCurrentNode(null);
|
|
|
+ project.setPreCurrentApproverId(null);
|
|
|
+ project.setPreSubmitTime(null);
|
|
|
+
|
|
|
+ project.setFormalAuditStatus(AUDIT_STATUS_DRAFT); // 0 正式未提交
|
|
|
+ project.setFormalCurrentNode(null);
|
|
|
+ project.setFormalCurrentApproverId(null);
|
|
|
+ project.setFormalSubmitTime(null);
|
|
|
+
|
|
|
+ // 是否涉及预算:由前端传 or 后端根据预算合计判断
|
|
|
+ if (project.getInvolveBudget() == null) {
|
|
|
+ project.setInvolveBudget(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ projectMapper.insert(project);
|
|
|
+
|
|
|
+ // 子表
|
|
|
+ insertScheduleList(createReqVO.getScheduleList(), project.getId());
|
|
|
+ insertBudgetList(createReqVO.getBudgetList(), project.getId());
|
|
|
+
|
|
|
+ return project.getId();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除项目
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void deleteProject(Long id) {
|
|
|
+ ProjectDO exist = projectMapper.selectById(id);
|
|
|
+ if (exist == null) {
|
|
|
+ throw exception(PROJECT_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ // ⚠️ 已进入审批的项目禁止删除(安全)
|
|
|
+ if (exist.getPreAuditStatus() != null && exist.getPreAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_IN_APPROVAL_CANNOT_DELETE);
|
|
|
+ }
|
|
|
+ if (exist.getFormalAuditStatus() != null && exist.getFormalAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_IN_APPROVAL_CANNOT_DELETE);
|
|
|
+ }
|
|
|
+
|
|
|
+ projectMapper.deleteById(id);
|
|
|
+ projectScheduleMapper.deleteByProjectId(id);
|
|
|
+ projectBudgetMapper.deleteByProjectId(id);
|
|
|
+ // 审批记录保留(审计需求),如需级联删,放开下一行
|
|
|
+ // approvalRecordMapper.deleteByProjectId(id);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前最大的 sort 值,没有记录时返回 0
|
|
|
+ */
|
|
|
+ private int getMaxSort() {
|
|
|
+ Integer max = projectMapper.selectMaxSort();
|
|
|
+ return max == null ? 0 : max;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新项目
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void updateProject(ProjectSaveReqVO updateReqVO) {
|
|
|
+ ProjectDO exist = projectMapper.selectById(updateReqVO.getId());
|
|
|
+ if (exist == null) {
|
|
|
+ throw exception(PROJECT_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ⚠️ 已提交审批的项目不允许随意修改(否则审批内容会被篡改)
|
|
|
+ if (exist.getPreAuditStatus() != null && exist.getPreAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_IN_APPROVAL_CANNOT_UPDATE);
|
|
|
+ }
|
|
|
+ if (exist.getFormalAuditStatus() != null && exist.getFormalAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_IN_APPROVAL_CANNOT_UPDATE);
|
|
|
+ }
|
|
|
+
|
|
|
+ ProjectDO updateObj = BeanUtils.toBean(updateReqVO, ProjectDO.class);
|
|
|
+ // ⚠️ 不允许前端改审批字段(安全)
|
|
|
+ updateObj.setPreAuditStatus(null);
|
|
|
+ updateObj.setPreCurrentNode(null);
|
|
|
+ updateObj.setPreCurrentApproverId(null);
|
|
|
+ updateObj.setFormalAuditStatus(null);
|
|
|
+ updateObj.setFormalCurrentNode(null);
|
|
|
+ updateObj.setFormalCurrentApproverId(null);
|
|
|
+
|
|
|
+ projectMapper.updateById(updateObj);
|
|
|
+
|
|
|
+ // 子表全删全插
|
|
|
+ projectScheduleMapper.deleteByProjectId(updateReqVO.getId());
|
|
|
+ projectBudgetMapper.deleteByProjectId(updateReqVO.getId());
|
|
|
+ insertScheduleList(updateReqVO.getScheduleList(), updateReqVO.getId());
|
|
|
+ insertBudgetList(updateReqVO.getBudgetList(), updateReqVO.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 删除项目
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void deleteProject(Long id) {
|
|
|
+ ProjectDO exist = projectMapper.selectById(id);
|
|
|
+ if (exist == null) {
|
|
|
+ throw exception(PROJECT_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ // ⚠️ 已进入审批的项目禁止删除(安全)
|
|
|
+ if (exist.getPreAuditStatus() != null && exist.getPreAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_IN_APPROVAL_CANNOT_DELETE);
|
|
|
+ }
|
|
|
+ if (exist.getFormalAuditStatus() != null && exist.getFormalAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_IN_APPROVAL_CANNOT_DELETE);
|
|
|
+ }
|
|
|
+
|
|
|
+ projectMapper.deleteById(id);
|
|
|
+ projectScheduleMapper.deleteByProjectId(id);
|
|
|
+ projectBudgetMapper.deleteByProjectId(id);
|
|
|
+ // 审批记录保留(审计需求),如需级联删,放开下一行
|
|
|
+ // approvalRecordMapper.deleteByProjectId(id);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void validateProjectExists(Long id) {
|
|
|
+ if (projectMapper.selectById(id) == null) {
|
|
|
+ throw exception(PROJECT_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ProjectRespVO getProject(Long id) {
|
|
|
+ ProjectDO project = projectMapper.selectById(id);
|
|
|
+ if (project == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ ProjectRespVO respVO = BeanUtils.toBean(project, ProjectRespVO.class);
|
|
|
+ // 子表:进度安排
|
|
|
+ List<ProjectScheduleDO> scheduleList = projectScheduleMapper.selectListByProjectId(id);
|
|
|
+ respVO.setScheduleList(BeanUtils.toBean(scheduleList, ProjectRespVO.ScheduleVO.class));
|
|
|
+ // 子表:费用预算
|
|
|
+ List<ProjectBudgetDO> budgetList = projectBudgetMapper.selectListByProjectId(id);
|
|
|
+ respVO.setBudgetList(BeanUtils.toBean(budgetList, ProjectRespVO.BudgetVO.class));
|
|
|
+ return respVO;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PageResult<ProjectRespVO> getProjectPage(ProjectPageReqVO pageReqVO) {
|
|
|
+ PageResult<ProjectDO> pageResult = projectMapper.selectPage(pageReqVO);
|
|
|
+ // 列表页不带子表,只转主表字段
|
|
|
+ return BeanUtils.toBean(pageResult, ProjectRespVO.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ProjectDO> getProjects(ProjectPageReqVO reqVO) {
|
|
|
+ return projectMapper.selectList(reqVO);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // ==================== 私有方法 ====================
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量插入进度安排
|
|
|
+ */
|
|
|
+ private void insertScheduleList(List<ProjectSaveReqVO.ScheduleVO> list, Long projectId) {
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ List<ProjectScheduleDO> scheduleList = BeanUtils.toBean(list, ProjectScheduleDO.class);
|
|
|
+ scheduleList.forEach(item -> {
|
|
|
+ item.setProjectId(projectId);
|
|
|
+ item.setSort(item.getSort() == null ? 0 : item.getSort());
|
|
|
+ });
|
|
|
+ scheduleList.forEach(projectScheduleMapper::insert);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量插入费用预算
|
|
|
+ */
|
|
|
+ private void insertBudgetList(List<ProjectSaveReqVO.BudgetVO> list, Long projectId) {
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ List<ProjectBudgetDO> budgetList = BeanUtils.toBean(list, ProjectBudgetDO.class);
|
|
|
+ budgetList.forEach(item -> {
|
|
|
+ item.setProjectId(projectId);
|
|
|
+ item.setSort(item.getSort() == null ? 0 : item.getSort());
|
|
|
+ });
|
|
|
+ budgetList.forEach(projectBudgetMapper::insert);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<ProjectDO> getProjectList(ProjectPageReqVO reqVO) {
|
|
|
+ return projectMapper.selectList(reqVO);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|