|
|
@@ -1,38 +1,25 @@
|
|
|
package cn.iocoder.yudao.module.rd.service.researchProject;
|
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
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 cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
|
|
+import cn.iocoder.yudao.module.rd.controller.admin.researchProject.vo.*;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.dataobject.researchProject.*;
|
|
|
+import cn.iocoder.yudao.module.rd.dal.mysql.researchProject.*;
|
|
|
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.time.LocalDateTime;
|
|
|
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;
|
|
|
-
|
|
|
+import static cn.iocoder.yudao.module.rd.enums.ErrorCodeConstant.*;
|
|
|
+import static cn.iocoder.yudao.module.rd.enums.ProjectApprovalConstants.*;
|
|
|
|
|
|
-/**
|
|
|
- * 项目立项 Service 实现类
|
|
|
- *
|
|
|
- * @author ruiqi
|
|
|
- */
|
|
|
@Service
|
|
|
@Validated
|
|
|
public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
@@ -45,11 +32,11 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
private ProjectBudgetMapper projectBudgetMapper;
|
|
|
@Resource
|
|
|
private ProjectApprovalRecordMapper approvalRecordMapper;
|
|
|
+ @Resource
|
|
|
+ private ApproverQueryMapper approverQueryMapper;
|
|
|
|
|
|
+ // ==================== 新增 ====================
|
|
|
|
|
|
- /**
|
|
|
- * 新增项目
|
|
|
- */
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public Long createProject(ProjectSaveReqVO createReqVO) {
|
|
|
@@ -83,46 +70,6 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
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) {
|
|
|
@@ -157,10 +104,6 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
insertBudgetList(updateReqVO.getBudgetList(), updateReqVO.getId());
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- /**
|
|
|
- * 删除项目
|
|
|
- */
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void deleteProject(Long id) {
|
|
|
@@ -183,11 +126,7 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
// approvalRecordMapper.deleteByProjectId(id);
|
|
|
}
|
|
|
|
|
|
- private void validateProjectExists(Long id) {
|
|
|
- if (projectMapper.selectById(id) == null) {
|
|
|
- throw exception(PROJECT_NOT_EXISTS);
|
|
|
- }
|
|
|
- }
|
|
|
+ // ==================== 详情 & 分页 ====================
|
|
|
|
|
|
@Override
|
|
|
public ProjectRespVO getProject(Long id) {
|
|
|
@@ -195,39 +134,224 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
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;
|
|
|
+ ProjectRespVO vo = BeanUtils.toBean(project, ProjectRespVO.class);
|
|
|
+
|
|
|
+ // 子表
|
|
|
+ vo.setScheduleList(BeanUtils.toBean(
|
|
|
+ projectScheduleMapper.selectListByProjectId(id), ProjectRespVO.ScheduleVO.class));
|
|
|
+ vo.setBudgetList(BeanUtils.toBean(
|
|
|
+ projectBudgetMapper.selectListByProjectId(id), ProjectRespVO.BudgetVO.class));
|
|
|
+
|
|
|
+ // 审批记录
|
|
|
+ vo.setApprovalRecords(BeanUtils.toBean(
|
|
|
+ approvalRecordMapper.selectListByProjectId(id), ProjectRespVO.ApprovalRecordVO.class));
|
|
|
+
|
|
|
+ return vo;
|
|
|
}
|
|
|
|
|
|
@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) {
|
|
|
+ public List<ProjectDO> getProjectList(ProjectPageReqVO reqVO) {
|
|
|
return projectMapper.selectList(reqVO);
|
|
|
}
|
|
|
|
|
|
+ // ==================== 提交预审 ====================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void submitPreApproval(Long projectId) {
|
|
|
+ ProjectDO project = getAndValidate(projectId);
|
|
|
+
|
|
|
+ // 只有"未提交"或"被驳回"可以提交
|
|
|
+ Integer status = project.getPreAuditStatus();
|
|
|
+ if (status != AUDIT_STATUS_DRAFT && status != AUDIT_STATUS_REJECTED) {
|
|
|
+ throw exception(PROJECT_PRE_STATUS_ILLEGAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ⭐ 校验必填项(比如附件必须上传),防止用户草稿直接提交
|
|
|
+ validateBeforeSubmitPre(project);
|
|
|
+
|
|
|
+ // 找一级审批人(预审只有二级)
|
|
|
+ Long approverId = findApprover(NODE_PRE_SECOND, project.getDeptId());
|
|
|
+ if (approverId == null) {
|
|
|
+ throw exception(PROJECT_APPROVER_NOT_FOUND);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 写一条"提交"记录
|
|
|
+ insertRecord(projectId, STAGE_PRE, NODE_PRE_SECOND, "提交预审", RESULT_SUBMIT, "提交预审材料");
|
|
|
+
|
|
|
+ // 更新主表
|
|
|
+ ProjectDO update = new ProjectDO();
|
|
|
+ update.setId(projectId);
|
|
|
+ update.setPreAuditStatus(AUDIT_STATUS_PROCESSING);
|
|
|
+ update.setPreCurrentNode(NODE_PRE_SECOND);
|
|
|
+ update.setPreCurrentApproverId(approverId);
|
|
|
+ update.setPreSubmitTime(LocalDateTime.now());
|
|
|
+ projectMapper.updateById(update);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 预审审批 ====================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void approvePre(ProjectApproveReqVO reqVO) {
|
|
|
+ ProjectDO project = getAndValidate(reqVO.getProjectId());
|
|
|
+
|
|
|
+ if (project.getPreAuditStatus() != AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_NOT_IN_APPROVAL);
|
|
|
+ }
|
|
|
+ if (!Objects.equals(project.getPreCurrentApproverId(), SecurityFrameworkUtils.getLoginUserId())) {
|
|
|
+ throw exception(PROJECT_NOT_YOUR_TURN);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 写记录
|
|
|
+ insertRecord(project.getId(), STAGE_PRE, project.getPreCurrentNode(),
|
|
|
+ getNodeName(project.getPreCurrentNode()), reqVO.getResult(), reqVO.getOpinion());
|
|
|
+
|
|
|
+ // 更新主表
|
|
|
+ ProjectDO update = new ProjectDO();
|
|
|
+ update.setId(project.getId());
|
|
|
+ if (reqVO.getResult() == RESULT_REJECT) {
|
|
|
+ update.setPreAuditStatus(AUDIT_STATUS_REJECTED);
|
|
|
+ } else {
|
|
|
+ // 预审只有二级,通过就结束
|
|
|
+ update.setPreAuditStatus(AUDIT_STATUS_PASSED);
|
|
|
+ }
|
|
|
+ update.setPreCurrentNode(null);
|
|
|
+ update.setPreCurrentApproverId(null);
|
|
|
+ projectMapper.updateById(update);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 提交正式立项 ====================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void submitFormalApproval(Long projectId) {
|
|
|
+ ProjectDO project = getAndValidate(projectId);
|
|
|
+
|
|
|
+ // ⭐ 必须先预审通过
|
|
|
+ if (project.getPreAuditStatus() == null || project.getPreAuditStatus() != AUDIT_STATUS_PASSED) {
|
|
|
+ throw exception(PROJECT_PRE_NOT_PASSED);
|
|
|
+ }
|
|
|
+ Integer status = project.getFormalAuditStatus();
|
|
|
+ if (status != AUDIT_STATUS_DRAFT && status != AUDIT_STATUS_REJECTED) {
|
|
|
+ throw exception(PROJECT_FORMAL_STATUS_ILLEGAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ validateBeforeSubmitFormal(project);
|
|
|
+
|
|
|
+ Long approverId = findApprover(NODE_FORMAL_FIRST, project.getDeptId());
|
|
|
+ if (approverId == null) {
|
|
|
+ throw exception(PROJECT_APPROVER_NOT_FOUND);
|
|
|
+ }
|
|
|
+
|
|
|
+ insertRecord(projectId, STAGE_FORMAL, NODE_FORMAL_FIRST, "提交正式立项",
|
|
|
+ RESULT_SUBMIT, "提交立项汇报");
|
|
|
+
|
|
|
+ ProjectDO update = new ProjectDO();
|
|
|
+ update.setId(projectId);
|
|
|
+ update.setFormalAuditStatus(AUDIT_STATUS_PROCESSING);
|
|
|
+ update.setFormalCurrentNode(NODE_FORMAL_FIRST);
|
|
|
+ update.setFormalCurrentApproverId(approverId);
|
|
|
+ update.setFormalSubmitTime(LocalDateTime.now());
|
|
|
+ projectMapper.updateById(update);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 正式立项审批(含条件分支) ====================
|
|
|
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void approveFormal(ProjectApproveReqVO reqVO) {
|
|
|
+ ProjectDO project = getAndValidate(reqVO.getProjectId());
|
|
|
+
|
|
|
+ if (project.getFormalAuditStatus() != AUDIT_STATUS_PROCESSING) {
|
|
|
+ throw exception(PROJECT_NOT_IN_APPROVAL);
|
|
|
+ }
|
|
|
+ if (!Objects.equals(project.getFormalCurrentApproverId(), SecurityFrameworkUtils.getLoginUserId())) {
|
|
|
+ throw exception(PROJECT_NOT_YOUR_TURN);
|
|
|
+ }
|
|
|
+
|
|
|
+ insertRecord(project.getId(), STAGE_FORMAL, project.getFormalCurrentNode(),
|
|
|
+ getNodeName(project.getFormalCurrentNode()), reqVO.getResult(), reqVO.getOpinion());
|
|
|
+
|
|
|
+ ProjectDO update = new ProjectDO();
|
|
|
+ update.setId(project.getId());
|
|
|
+
|
|
|
+ if (reqVO.getResult() == RESULT_REJECT) {
|
|
|
+ update.setFormalAuditStatus(AUDIT_STATUS_REJECTED);
|
|
|
+ update.setFormalCurrentNode(null);
|
|
|
+ update.setFormalCurrentApproverId(null);
|
|
|
+ } else {
|
|
|
+ Integer nextNode = getNextFormalNode(project);
|
|
|
+ if (nextNode == null) {
|
|
|
+ update.setFormalAuditStatus(AUDIT_STATUS_PASSED);
|
|
|
+ update.setFormalCurrentNode(null);
|
|
|
+ update.setFormalCurrentApproverId(null);
|
|
|
+ } else {
|
|
|
+ Long nextApproverId = findApprover(nextNode, project.getDeptId());
|
|
|
+ if (nextApproverId == null) {
|
|
|
+ throw exception(PROJECT_APPROVER_NOT_FOUND);
|
|
|
+ }
|
|
|
+ update.setFormalCurrentNode(nextNode);
|
|
|
+ update.setFormalCurrentApproverId(nextApproverId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ projectMapper.updateById(update);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 撤回 ====================
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void cancelApproval(Long projectId) {
|
|
|
+ ProjectDO project = getAndValidate(projectId);
|
|
|
+
|
|
|
+ // 判断在哪个阶段
|
|
|
+ if (project.getPreAuditStatus() != null && project.getPreAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ insertRecord(projectId, STAGE_PRE, project.getPreCurrentNode(),
|
|
|
+ getNodeName(project.getPreCurrentNode()), RESULT_CANCEL, "撤回申请");
|
|
|
+ ProjectDO update = new ProjectDO();
|
|
|
+ update.setId(projectId);
|
|
|
+ update.setPreAuditStatus(AUDIT_STATUS_DRAFT);
|
|
|
+ update.setPreCurrentNode(null);
|
|
|
+ update.setPreCurrentApproverId(null);
|
|
|
+ projectMapper.updateById(update);
|
|
|
+ } else if (project.getFormalAuditStatus() != null && project.getFormalAuditStatus() == AUDIT_STATUS_PROCESSING) {
|
|
|
+ insertRecord(projectId, STAGE_FORMAL, project.getFormalCurrentNode(),
|
|
|
+ getNodeName(project.getFormalCurrentNode()), RESULT_CANCEL, "撤回申请");
|
|
|
+ ProjectDO update = new ProjectDO();
|
|
|
+ update.setId(projectId);
|
|
|
+ update.setFormalAuditStatus(AUDIT_STATUS_DRAFT);
|
|
|
+ update.setFormalCurrentNode(null);
|
|
|
+ update.setFormalCurrentApproverId(null);
|
|
|
+ projectMapper.updateById(update);
|
|
|
+ } else {
|
|
|
+ throw exception(PROJECT_NOT_IN_APPROVAL);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
// ==================== 私有方法 ====================
|
|
|
|
|
|
- /**
|
|
|
- * 批量插入进度安排
|
|
|
- */
|
|
|
- private void insertScheduleList(List<ProjectSaveReqVO.ScheduleVO> list, Long projectId) {
|
|
|
- if (list == null || list.isEmpty()) {
|
|
|
- return;
|
|
|
+ private ProjectDO getAndValidate(Long id) {
|
|
|
+ ProjectDO project = projectMapper.selectById(id);
|
|
|
+ if (project == null) {
|
|
|
+ throw exception(PROJECT_NOT_EXISTS);
|
|
|
}
|
|
|
+ return project;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int getMaxSort() {
|
|
|
+ Integer max = projectMapper.selectMaxSort();
|
|
|
+ return max == null ? 0 : max;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
@@ -236,13 +360,8 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
scheduleList.forEach(projectScheduleMapper::insert);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 批量插入费用预算
|
|
|
- */
|
|
|
private void insertBudgetList(List<ProjectSaveReqVO.BudgetVO> list, Long projectId) {
|
|
|
- if (list == null || list.isEmpty()) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (list == null || list.isEmpty()) return;
|
|
|
List<ProjectBudgetDO> budgetList = BeanUtils.toBean(list, ProjectBudgetDO.class);
|
|
|
budgetList.forEach(item -> {
|
|
|
item.setProjectId(projectId);
|
|
|
@@ -251,9 +370,106 @@ public class ResearchProjectServiceImpl implements ResearchProjectService {
|
|
|
budgetList.forEach(projectBudgetMapper::insert);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public List<ProjectDO> getProjectList(ProjectPageReqVO reqVO) {
|
|
|
- return projectMapper.selectList(reqVO);
|
|
|
+ /** 写一条审批记录 */
|
|
|
+ private void insertRecord(Long projectId, Integer stage, Integer node, String nodeName,
|
|
|
+ Integer result, String opinion) {
|
|
|
+ ProjectApprovalRecordDO record = new ProjectApprovalRecordDO();
|
|
|
+ record.setProjectId(projectId);
|
|
|
+ record.setStage(stage);
|
|
|
+ record.setNode(node);
|
|
|
+ record.setNodeName(nodeName);
|
|
|
+ record.setApproverId(SecurityFrameworkUtils.getLoginUserId());
|
|
|
+ record.setApproverName(SecurityFrameworkUtils.getLoginUserNickname());
|
|
|
+ record.setResult(result);
|
|
|
+ record.setOpinion(opinion);
|
|
|
+ record.setApproveTime(LocalDateTime.now());
|
|
|
+ approvalRecordMapper.insert(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 计算正式立项的下一节点(含"涉及预算"条件分支) */
|
|
|
+ private Integer getNextFormalNode(ProjectDO project) {
|
|
|
+ Integer currentNode = project.getFormalCurrentNode();
|
|
|
+ if (currentNode == null) return null;
|
|
|
+ switch (currentNode) {
|
|
|
+ case NODE_FORMAL_FIRST:
|
|
|
+ return NODE_FORMAL_SECOND;
|
|
|
+ case NODE_FORMAL_SECOND:
|
|
|
+ // ⭐ 条件分支:涉及预算 → 财务
|
|
|
+ if (project.getInvolveBudget() != null && project.getInvolveBudget() == 1) {
|
|
|
+ return NODE_FORMAL_FINANCE;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ case NODE_FORMAL_FINANCE:
|
|
|
+ return null;
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按节点 + 部门查审批人
|
|
|
+ */
|
|
|
+ private Long findApprover(Integer node, Long deptId) {
|
|
|
+ if (node == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String roleCode = getRoleCodeByNode(node);
|
|
|
+ if (StrUtil.isBlank(roleCode)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ⭐ 二级审批是全局角色,不按部门过滤
|
|
|
+ if (node == NODE_PRE_SECOND || node == NODE_FORMAL_SECOND) {
|
|
|
+ return approverQueryMapper.selectUserIdByRoleCode(roleCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ⭐ 一级、财务审批按部门过滤
|
|
|
+ if (deptId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return approverQueryMapper.selectUserIdByRoleCodeAndDept(roleCode, deptId);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getRoleCodeByNode(Integer node) {
|
|
|
+ if (node == null) return null;
|
|
|
+ switch (node) {
|
|
|
+ case NODE_PRE_SECOND:
|
|
|
+ case NODE_FORMAL_SECOND:
|
|
|
+ return "科技发展部负责人";
|
|
|
+ case NODE_FORMAL_FIRST:
|
|
|
+ return "技术副总";
|
|
|
+ case NODE_FORMAL_FINANCE:
|
|
|
+ return "财务负责人";
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getNodeName(Integer node) {
|
|
|
+ if (node == null) return "";
|
|
|
+ switch (node) {
|
|
|
+ case NODE_PRE_SECOND: return "预审-科技发展部二级审批";
|
|
|
+ case NODE_FORMAL_FIRST: return "正式-专业公司一级审批";
|
|
|
+ case NODE_FORMAL_SECOND: return "正式-科技发展部二级审批";
|
|
|
+ case NODE_FORMAL_FINANCE:return "正式-财务审批";
|
|
|
+ default: return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 提交预审前的必填校验 */
|
|
|
+ private void validateBeforeSubmitPre(ProjectDO project) {
|
|
|
+ // 例:附件必填
|
|
|
+ if (project.getApplicationFile() == null || project.getApplicationFile().isEmpty()) {
|
|
|
+ throw exception(PROJECT_APPLICATION_FILE_REQUIRED);
|
|
|
+ }
|
|
|
+ if (project.getOpeningReportFile() == null || project.getOpeningReportFile().isEmpty()) {
|
|
|
+ throw exception(PROJECT_OPENING_REPORT_REQUIRED);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void validateBeforeSubmitFormal(ProjectDO project) {
|
|
|
+ // 例:正式立项前必须有预算
|
|
|
+ // 按需扩展
|
|
|
}
|
|
|
|
|
|
-}
|
|
|
+}
|