diff --git a/src/main/java/com/xdap/self_development/controller/ActivitiProcessController.java b/src/main/java/com/xdap/self_development/controller/ActivitiProcessController.java index e22a86c..566ac89 100644 --- a/src/main/java/com/xdap/self_development/controller/ActivitiProcessController.java +++ b/src/main/java/com/xdap/self_development/controller/ActivitiProcessController.java @@ -99,10 +99,10 @@ public class ActivitiProcessController { // 直送回退回的节点 @GetMapping("/directSendingApproval") public Response directSendingApproval( - @RequestParam @NotBlank String templateId, + @RequestParam @NotBlank String flowId, @RequestParam @NotBlank String userId ) { - activitiProcessService.directSendingApproval(templateId, userId); + activitiProcessService.directSendingApproval(flowId, userId); return Response.ok(); } // 获取可退回节点 diff --git a/src/main/java/com/xdap/self_development/controller/BenchmarkingReportController.java b/src/main/java/com/xdap/self_development/controller/BenchmarkingReportController.java index 13bbb28..17d1759 100644 --- a/src/main/java/com/xdap/self_development/controller/BenchmarkingReportController.java +++ b/src/main/java/com/xdap/self_development/controller/BenchmarkingReportController.java @@ -5,6 +5,7 @@ import com.definesys.mpaas.query.db.PageQueryResult; import com.xdap.self_development.controller.form.*; import com.xdap.self_development.exception.CommonException; import com.xdap.self_development.pojo.BenchmarkingReport; +import com.xdap.self_development.pojo.dto.ApprovalNodesDTO; import com.xdap.self_development.pojo.view.ApprovalFlowNodeView; import com.xdap.self_development.pojo.view.TodoAndReportView; import com.xdap.self_development.service.BenchmarkingReportService; @@ -64,6 +65,21 @@ public class BenchmarkingReportController { return Response.ok().setMessage("保存成功"); } + // 发起人重新发起流程 + @PostMapping("/restartReportApproval") + public Response restartReportApproval( + @Valid @RequestBody RestartReportApprovalForm form, + BindingResult bindingResult + ) { + if (bindingResult.hasErrors()) { + bindingResult.getFieldErrors().forEach(error -> { + throw new CommonException(error.getDefaultMessage()); + }); + } + benchmarkingReportService.restartReportApproval(form); + return Response.ok().setMessage("重新提交成功"); + } + // 直送回退回的节点 @GetMapping("/directSendingApproval") public Response directSendingApproval( @@ -191,4 +207,13 @@ public class BenchmarkingReportController { ApprovalFlowNodeView approvalFlowView = benchmarkingReportService.getReportProcess(flowId); return Response.ok().data(approvalFlowView); } + + // 获取流程审批人 + @GetMapping("/getApprovalUsers") + public Response getApprovalUsers( + @NotBlank @RequestParam String flowId + ) { + ApprovalNodesDTO nodes = benchmarkingReportService.getApprovalUsers(flowId); + return Response.ok().data(nodes); + } } diff --git a/src/main/java/com/xdap/self_development/controller/DeleteController.java b/src/main/java/com/xdap/self_development/controller/DeleteController.java index 8103579..01b2a99 100644 --- a/src/main/java/com/xdap/self_development/controller/DeleteController.java +++ b/src/main/java/com/xdap/self_development/controller/DeleteController.java @@ -9,7 +9,10 @@ import com.xdap.self_development.common.UserPermissionGetter; import com.xdap.self_development.config.BusinessDatabase; import com.xdap.self_development.controller.request.ModelComparisonExportRequest; import com.xdap.self_development.feign.ApaasMyTokenFeign; +import com.xdap.self_development.pojo.ModelDepUserRolePojo; +import com.xdap.self_development.pojo.view.MenuParamView; import com.xdap.self_development.schedule.ParameterValueCalculationTimer; +import com.xdap.self_development.service.ModelRoleService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -25,112 +28,5 @@ import java.util.List; @RestController @RequestMapping("/custom/delete") public class DeleteController { - @Resource - private BusinessDatabase bd; - @Resource - private UserPermissionGetter getter; - @Resource - private MpaasQueryFactory queryFactory; - @Autowired - private ApaasMyTokenFeign apaasMyTokenFeign; - @Resource - private RuntimeAppContextService runtimeAppContextService; - @Resource - private SnowflakeIdWorker snowflakeIdWorker; - @Resource - private ParameterValueCalculationTimer timer; - @Value("${apaas.token.clientId}") - private String clientId; - @Value("${apaas.token.clientSecred}") - private String clientSecret; - @Autowired - private StringRedisTemplate redisTemplate; - - @GetMapping("/getUserId") - public Response getUserId() { - return Response.ok().data(runtimeAppContextService.getCurrentUserId()); - } - - @PostMapping("/timer") - public Response timer( - @RequestBody ModelComparisonExportRequest request - ) { - StringBuilder sb = new StringBuilder(); - for (String modelId : request.getModelIds()) { - try { - String redisKey = request.getUserID() + ":" + modelId + ":" + "ComparisonExport"; - log.info("尝试从Redis获取,KEY: {}", redisKey); - sb.append(redisKey).append(","); - - String cachedData = redisTemplate.opsForValue().get(redisKey); - - if (cachedData == null) { - log.warn("Redis中未找到数据,KEY: {}", redisKey); - sb.append(modelId).append("未找到数据").append(","); - } else { - log.info("成功获取到数据,长度: {}", cachedData.length()); - sb.append(cachedData).append("\n"); - } - } catch (Exception e) { - log.error("从Redis获取数据失败", e); // 修正日志信息 - sb.append(e.getMessage()).append("\n"); - } - } - return Response.ok().data(sb); - } - - @PostMapping("/exportCompare") - public void exportTemplateCompare( -// @RequestBody CompareRequest request, -// HttpServletResponse response - ) { -// try { -// // 1. 查询对比数据 -// List data = compareService.compareMultipleTemplates( -// request.getTemplateNames(), -// request.getQueryParams() -// ); -// -// // 2. 设置响应头 -// String fileName = "模板参数对比_" + System.currentTimeMillis() + ".xlsx"; -// response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); -// response.setCharacterEncoding("utf-8"); -// response.setHeader("Content-disposition", -// "attachment;filename*=utf-8''" + URLEncoder.encode(fileName, "UTF-8")); -// -// // 3. 动态生成表头 -// List> head = generateDynamicHeader(request.getTemplateNames()); -// -// // 4. 使用EasyExcel写入数据 -// EasyExcel.write(response.getOutputStream()) -// .head(head) // 动态表头 -// .sheet("参数对比") -// .doWrite(data); -// -// } catch (Exception e) { -// throw new RuntimeException("导出失败", e); -// } - } - - /** - * 动态生成表头 - */ - private List> generateDynamicHeader(List templateNames) { - List> head = new ArrayList<>(); - - // 固定列 - head.add(Collections.singletonList("参数名称")); - head.add(Collections.singletonList("参数编码")); - - // 动态模板列 - for (String templateName : templateNames) { - head.add(Collections.singletonList(templateName)); - } - - // 差异标识列 - head.add(Collections.singletonList("差异标识")); - - return head; - } } diff --git a/src/main/java/com/xdap/self_development/controller/form/RestartReportApprovalForm.java b/src/main/java/com/xdap/self_development/controller/form/RestartReportApprovalForm.java new file mode 100644 index 0000000..227302b --- /dev/null +++ b/src/main/java/com/xdap/self_development/controller/form/RestartReportApprovalForm.java @@ -0,0 +1,38 @@ +package com.xdap.self_development.controller.form; + +import com.xdap.self_development.pojo.dto.ApprovalNodeDTO; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +@Data +public class RestartReportApprovalForm { + private String title; + private String manufacturerName; + private String modelName; + private String manufacturerAbbreviation; + private String modelAbbreviation; + private String productNumber; + private String module; + private String plate; + private String platform; + private String series; + private String marketSegment; + private String projectName; + private String projectNumber; + private String fileUrl; + + @NotBlank(message = "报告不能为空") + private String reportId; + @NotBlank(message = "流程标题不能为空") + private String processTitle; + // 审批环节配置列表 + @NotEmpty(message = "环节不能为空") + private List nodeList; + private String explanation; + + @NotBlank(message = "发起人不能为空") + private String userId; +} diff --git a/src/main/java/com/xdap/self_development/controller/request/ApprovalNodeRequest.java b/src/main/java/com/xdap/self_development/controller/request/ApprovalNodeRequest.java index d0fe8db..ac82a07 100644 --- a/src/main/java/com/xdap/self_development/controller/request/ApprovalNodeRequest.java +++ b/src/main/java/com/xdap/self_development/controller/request/ApprovalNodeRequest.java @@ -11,6 +11,9 @@ public class ApprovalNodeRequest { // 机型基础信息 private String modelId; + // 流程ID + private String flowId; + // 参数列表(批量参数) private List paramList; diff --git a/src/main/java/com/xdap/self_development/controller/request/ModelComparisonExportRequest.java b/src/main/java/com/xdap/self_development/controller/request/ModelComparisonExportRequest.java index 18ffa8e..05547b4 100644 --- a/src/main/java/com/xdap/self_development/controller/request/ModelComparisonExportRequest.java +++ b/src/main/java/com/xdap/self_development/controller/request/ModelComparisonExportRequest.java @@ -7,6 +7,6 @@ import java.util.List; @Data public class ModelComparisonExportRequest { String userID; - List modelIds; + List models; List subsystemParts; } diff --git a/src/main/java/com/xdap/self_development/controller/request/ModelItem.java b/src/main/java/com/xdap/self_development/controller/request/ModelItem.java new file mode 100644 index 0000000..eb3e6fa --- /dev/null +++ b/src/main/java/com/xdap/self_development/controller/request/ModelItem.java @@ -0,0 +1,9 @@ +package com.xdap.self_development.controller.request; + +import lombok.Data; + +@Data +public class ModelItem { + private String modelId; + private String parameterVersion; +} diff --git a/src/main/java/com/xdap/self_development/pojo/dto/ActivitiDataAndRecordDTO.java b/src/main/java/com/xdap/self_development/pojo/dto/ActivitiDataAndRecordDTO.java index b6432ec..c633b4d 100644 --- a/src/main/java/com/xdap/self_development/pojo/dto/ActivitiDataAndRecordDTO.java +++ b/src/main/java/com/xdap/self_development/pojo/dto/ActivitiDataAndRecordDTO.java @@ -1,9 +1,11 @@ package com.xdap.self_development.pojo.dto; +import com.fasterxml.jackson.annotation.JsonFormat; import com.xdap.self_development.pojo.view.FlowNode; import lombok.Data; import java.util.ArrayList; +import java.util.Date; import java.util.List; @Data @@ -11,6 +13,11 @@ public class ActivitiDataAndRecordDTO { private String approvalStatus; private String processTitle; private String modelName; + private String createBy; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + private String currentNode; + private String processor; private ArrayList activitiDataDTOS; private List flowNodes; diff --git a/src/main/java/com/xdap/self_development/pojo/view/ApprovalFlowNodeView.java b/src/main/java/com/xdap/self_development/pojo/view/ApprovalFlowNodeView.java index fc62b88..b3e177f 100644 --- a/src/main/java/com/xdap/self_development/pojo/view/ApprovalFlowNodeView.java +++ b/src/main/java/com/xdap/self_development/pojo/view/ApprovalFlowNodeView.java @@ -1,7 +1,9 @@ package com.xdap.self_development.pojo.view; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; +import java.sql.Timestamp; import java.util.List; @Data @@ -12,6 +14,11 @@ public class ApprovalFlowNodeView { private String progressTitle; private String ContentName; private Integer templateVersion; + private String createBy; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Timestamp createTime; + private String currentNode; + private String processor; private List flowNodes; } diff --git a/src/main/java/com/xdap/self_development/pojo/view/TodoAndReportListView.java b/src/main/java/com/xdap/self_development/pojo/view/TodoAndReportListView.java index 93046ff..2c0bd94 100644 --- a/src/main/java/com/xdap/self_development/pojo/view/TodoAndReportListView.java +++ b/src/main/java/com/xdap/self_development/pojo/view/TodoAndReportListView.java @@ -11,6 +11,7 @@ import java.util.List; // CASE af.flow_status WHEN 'COMPLETE' THEN 1 WHEN 'APPROVING' THEN 2 WHEN 'DRAFT' THEN 3 WHEN 'REJECTED' THEN 4 ELSE 5 END ASC, @SQLQuery(value = { + // 查询每个报告最新审批流程 @SQL(view = "getAllReportApprovalList", sql = "WITH RankedFlows AS (SELECT af.id AS flowId, r.id AS reportId, af.process_title AS title, af.flow_status AS status, af.create_by AS createBy, xu.username AS username, af.created_time AS createdTime, ac.assignees AS current_users, ROW_NUMBER() OVER (PARTITION BY af.template_id ORDER BY af.created_time DESC) AS rn FROM yfsjglpt_model_approval_flow af LEFT JOIN approval_config ac ON af.id = ac.approval_id AND af.current_node = ac.node_order LEFT JOIN yfsjglpt_model_benchmarking_report r ON af.template_id = r.id LEFT JOIN xdap_users xu ON xu.id = af.create_by WHERE af.flow_type = 2 and af.flow_status <> 'DRAFT') SELECT flowId, reportId, title, status, createBy, username, createdTime, current_users FROM RankedFlows WHERE rn = 1 AND title LIKE CONCAT('%', #title, '%') AND status LIKE CONCAT('%', #status, '%') AND createdTime LIKE CONCAT('%', #createdTime, '%') AND username LIKE CONCAT('%', #username, '%') ORDER BY createdTime DESC") }) diff --git a/src/main/java/com/xdap/self_development/service/BenchmarkingReportService.java b/src/main/java/com/xdap/self_development/service/BenchmarkingReportService.java index 6a27daa..6db9fc5 100644 --- a/src/main/java/com/xdap/self_development/service/BenchmarkingReportService.java +++ b/src/main/java/com/xdap/self_development/service/BenchmarkingReportService.java @@ -6,6 +6,7 @@ import com.xdap.self_development.pojo.ApprovalConfigPojo; import com.xdap.self_development.pojo.ApprovalFlow; import com.xdap.self_development.pojo.ApprovalRecord; import com.xdap.self_development.pojo.BenchmarkingReport; +import com.xdap.self_development.pojo.dto.ApprovalNodesDTO; import com.xdap.self_development.pojo.view.ApprovalFlowNodeView; import com.xdap.self_development.pojo.view.TodoAndReportListView; import com.xdap.self_development.pojo.view.TodoAndReportView; @@ -23,6 +24,8 @@ public interface BenchmarkingReportService { void startReportApproval(ApprovalPersonForm form); + void restartReportApproval(@Valid RestartReportApprovalForm form); + void directSendingApproval(@NotBlank String reportId, @NotBlank String userId); void handleReturnToNode(@Valid HandleReturnNodeForm form); @@ -53,4 +56,6 @@ public interface BenchmarkingReportService { void updateTasks(ApprovalFlow flow, String status, Integer enableReturn); + ApprovalNodesDTO getApprovalUsers(@NotBlank String flowId); + } diff --git a/src/main/java/com/xdap/self_development/service/TodoTaskService.java b/src/main/java/com/xdap/self_development/service/TodoTaskService.java index 1eca3cf..0d7dc65 100644 --- a/src/main/java/com/xdap/self_development/service/TodoTaskService.java +++ b/src/main/java/com/xdap/self_development/service/TodoTaskService.java @@ -33,6 +33,10 @@ public interface TodoTaskService { void todoReturn(TodoReturnRequest todoReturnRequest); + void batchInsertTodoTask(List todoTaskPojos); + + void batchInsertInitiatedTask(List taskPojos); + //---------------------------------玉柴调用接口-------------------------------- List returnTodoTask(YcGetTodoTaskRequest ycGetTodoTaskRequest); diff --git a/src/main/java/com/xdap/self_development/service/impl/ActivitiProcessServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/ActivitiProcessServiceImpl.java index c4dd16b..81f78e8 100644 --- a/src/main/java/com/xdap/self_development/service/impl/ActivitiProcessServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/ActivitiProcessServiceImpl.java @@ -1,7 +1,6 @@ package com.xdap.self_development.service.impl; import com.alibaba.fastjson2.JSON; -import com.definesys.mpaas.query.MpaasQueryFactory; import com.xdap.api.moudle.user.pojo.XdapUsers; import com.xdap.self_development.config.BusinessDatabase; import com.xdap.self_development.controller.form.ApprovalUserNodeDTO; @@ -23,7 +22,6 @@ import groovy.util.logging.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -173,6 +171,16 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { XdapUsers XdapUsers = xdapDeptUsersService.selectUserByID(userId); String createBy = ObjectUtils.isNotEmpty(XdapUsers) ? XdapUsers.getUsername() : null; String approvalId = UUID.randomUUID().toString(); + + if (approvalNodeRequest.getFlowId() != null && !approvalNodeRequest.getFlowId().isEmpty()) { + // 清除拒绝提醒待办 + bd.getBusinessDatabase() + .eq("document_no", approvalNodeRequest.getFlowId()) + .eq("data_type", "数据拒绝") + .update("is_delete", 1) + .doUpdate(TodoTaskPojo.class); + } + // 创建发动机审核列表POJO对象 EngineReviewListPojo engineReviewListPojo = new EngineReviewListPojo(); engineReviewListPojo.setId(approvalId); @@ -413,7 +421,7 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { myself.cancelProcess( flow, currentNode, - ParamValueVersionEnum.DRAFT, + ParamValueVersionEnum.REFUSE, request.getUserId() ); return; @@ -431,12 +439,12 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { // 检查节点是否完成 public boolean checkNodeComplete(EngineReviewListPojo flow, ApprovalConfigPojo node) { ParamApprovalRecordPojo lastedRecord = bd.getBusinessDatabase() - .eq("flow_id", flow.getId()) - .orderBy("approval_time", "desc") + .eq("approval_id", flow.getId()) + .orderBy("operate_time", "desc") .doQueryFirst(ParamApprovalRecordPojo.class); Integer processedCount = bd.getBusinessDatabase() - .eq("flow_id", flow.getId()) - .eq("node", node.getNodeName()) + .eq("approval_id", flow.getId()) + .eq("node_name", node.getNodeName()) .eq("approval_level", lastedRecord.getApprovalLevel()) .doQuery(ParamApprovalRecordPojo.class).size(); if ("会签".equals(node.getNodeName())) { @@ -461,13 +469,13 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { .doUpdate(TodoTaskPojo.class); // 更改参数状态为草稿 bd.getBusinessDatabase() - .update("version_status", status.getCode())// 改为已拒绝 + .update("version_status", ParamValueVersionEnum.DRAFT.getCode()) .update("operation", "")// 清空操作状态 .update("minor_version", "")// 小版本清空 .update("last_update_date", new Date()) .eq("approval_id", flow.getId()) .doUpdate(EngineParamDetailPojo.class); - // 审批流改为已拒绝 + // 审批流改为已拒绝或已撤回 bd.getBusinessDatabase() .update("status_code", status.getCode()) .update("status_description", status.getDesc()) @@ -475,6 +483,7 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { .update("last_update_date", new Date()) .update("return_num", flow.getReturnNum() + 1) .update("pre_node", node.getNodeOrder()) + .update("node_name", "") .eq("id", flow.getId()) .doUpdate(EngineReviewListPojo.class); // 生成拒绝提醒 @@ -495,9 +504,9 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { todoTask.setOwnerId(flow.getCreatedBy()); todoTask.setCurrentProcessor(JSON.toJSONString(Collections.singletonList(flow.getCreatedBy()))); bd.getBusinessDatabase().doInsert(todoTask); - // 更新发起和已办 - myself.updateTasks(flow, status.getDesc(), 0); } + // 更新发起和已办 + myself.updateTasks(flow, status.getDesc(), 0); } public void updateTasks( EngineReviewListPojo flow, @@ -599,9 +608,6 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { .eq("id", flow.getId()).doUpdate(EngineReviewListPojo.class); } } - else { - return; - } } public void addNodeTodo( @@ -745,14 +751,12 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { .doQueryFirst(ApprovalConfigPojo.class); // 删除当前节点待办 - if (currentNode != null) { - bd.getBusinessDatabase() - .eq("document_no", flow.getId()) - .eq("current_node", currentNode.getNodeName()) - .update("is_delete", 1) - .update("last_update_date", Timestamp.valueOf(LocalDateTime.now())) - .doUpdate(TodoTaskPojo.class); - } + bd.getBusinessDatabase() + .eq("document_no", flow.getId()) + .eq("current_node", currentNode.getNodeName()) + .update("is_delete", 1) + .update("last_update_date", Timestamp.valueOf(LocalDateTime.now())) + .doUpdate(TodoTaskPojo.class); // 根据状态不同获取节点和节点层级 ApprovalConfigPojo preNode = bd.getBusinessDatabase() @@ -783,6 +787,7 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { bd.getBusinessDatabase() .update("status_code", ParamValueVersionEnum.UNDER.getCode()) .update("status_description", ParamValueVersionEnum.UNDER.getDesc()) + .update("node_name", currentNode.getNodeName()) .update("approver_persons", "") .update("last_update_date", new Date()) .eq("id", flow.getId()).doUpdate(EngineReviewListPojo.class); @@ -790,7 +795,7 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { // 清除拒绝提醒待办 bd.getBusinessDatabase() .eq("document_no", flow.getId()) - .eq("data_type", "模板拒绝") + .eq("data_type", "数据拒绝") .update("is_delete", 1) .doUpdate(TodoTaskPojo.class); @@ -990,7 +995,6 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { @Override public ActivitiDataAndRecordDTO showActivitiData(ShowActivitiDTO showActivitiDTO) { String approvalId = showActivitiDTO.getApprovalId(); - String userID = showActivitiDTO.getUserID(); if (approvalId == null) { throw new RuntimeException("无效的审批记录"); } @@ -1031,7 +1035,7 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { // 查询所有流程记录 List allRecord = bd.getBusinessDatabase() .eq("approval_id", approvalId) - .orderBy("creation_date", "desc") + .orderBy("operate_time", "desc") .doQuery(ParamApprovalRecordPojo.class); //获取配置 List nodes = bd.getBusinessDatabase() @@ -1079,9 +1083,9 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { } else { FlowNode flowNode = new FlowNode(); flowNode.setNode(record.getNodeName()); - flowNode.setTodoUserName(record.getCreatedBy()); + flowNode.setTodoUserName(record.getAssignee()); flowNode.setResult(record.getOperator()); - flowNode.setApprovalTime((Timestamp) record.getOperateTime()); + flowNode.setApprovalTime(new Timestamp(record.getOperateTime().getTime())); flowNodes.add(flowNode); } }); @@ -1098,6 +1102,20 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { EngineReviewListPojo engineReviewListPojos = bd.getBusinessDatabase() .eq("id", approvalId).doQueryFirst(EngineReviewListPojo.class); + // 查询基础数据 + activitiDataAndRecordDTO.setCurrentNode(flow.getNodeName()); + activitiDataAndRecordDTO.setCreateBy( + getUserNames( + Arrays.asList(flow.getApplicant().split(",")) + ) + ); + activitiDataAndRecordDTO.setCreateTime(flow.getCreationDate()); + activitiDataAndRecordDTO.setProcessor( + !Objects.equals(flow.getStatusCode(), ParamValueVersionEnum.PASSED.getCode()) ? + getUserNames( + Arrays.asList(flow.getApproverPersons().split(",")) + ) : "无" + ); activitiDataAndRecordDTO.setModelName(engineReviewListPojos.getModelName()); activitiDataAndRecordDTO.setApprovalStatus(engineReviewListPojos.getStatusDescription()); activitiDataAndRecordDTO.setProcessTitle(engineReviewListPojos.getProcessTopic()); @@ -1138,9 +1156,9 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { nodeView.setNode(node.getNodeName()); nodeView.setUndoUserName(getUserNames(Arrays.asList(node.getAssignees().split(",")))); nodeView.setTodoUserName(getUserNames(Arrays.asList(node.getAssignees().split(",")))); - nodeView.setApprovalTime((Timestamp) reversedTime.get(0).getOperateTime()); + nodeView.setApprovalTime(new Timestamp(reversedTime.get(0).getOperateTime().getTime())); nodeView.setOpinions(records.stream().map(approvalRecord -> - getUserNames(Collections.singletonList(approvalRecord.getCreatedBy())) + ":" + approvalRecord.getApprovalComment()).collect(Collectors.toList())); + getUserNames(Collections.singletonList(approvalRecord.getAssignee())) + ":" + approvalRecord.getApprovalComment()).collect(Collectors.toList())); } else { // 节点未完成 nodeView.setNodeOrder(node.getNodeOrder()); @@ -1153,7 +1171,7 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { } else { ParamApprovalRecordPojo record = bd.getBusinessDatabase() .eq("approval_id", flowId) - .eq("node_Name", node.getNodeName()) + .eq("node_name", node.getNodeName()) .eq("approval_level", level) .doQueryFirst(ParamApprovalRecordPojo.class); nodeView.setNodeOrder(node.getNodeOrder()); @@ -1161,9 +1179,9 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { if (record != null) { // 节点已完成 nodeView.setResult(record.getOperator()); - nodeView.setOpinions(Collections.singletonList(getUserNames(Collections.singletonList(record.getCreatedBy())) + ":" + record.getApprovalComment())); - nodeView.setApprovalTime((Timestamp) record.getOperateTime()); - nodeView.setTodoUserName(getUserNames(Collections.singletonList(record.getCreatedBy()))); + nodeView.setOpinions(Collections.singletonList(getUserNames(Collections.singletonList(record.getAssignee())) + ":" + record.getApprovalComment())); + nodeView.setApprovalTime(new Timestamp(record.getOperateTime().getTime())); + nodeView.setTodoUserName(getUserNames(Collections.singletonList(record.getAssignee()))); } else { // 节点未完成 nodeView.setResult("待审批"); @@ -1177,7 +1195,10 @@ public class ActivitiProcessServiceImpl implements ActivitiProcessService { private String getUserNames(List userIds) { StringJoiner joiner = new StringJoiner(" "); for (String userId : userIds) { - String username = bd.getBusinessDatabase().eq("id", userId).doQueryFirst(XdapUsers.class).getUsername(); + String username = bd.getBusinessDatabase() + .eq("id", userId) + .doQueryFirst(XdapUsers.class) + .getUsername(); if (username != null) { joiner.add(username); } else { diff --git a/src/main/java/com/xdap/self_development/service/impl/BenchmarkingReportServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/BenchmarkingReportServiceImpl.java index f673b3e..a4404f9 100644 --- a/src/main/java/com/xdap/self_development/service/impl/BenchmarkingReportServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/BenchmarkingReportServiceImpl.java @@ -15,6 +15,7 @@ import com.xdap.self_development.feign.dto.FileUploadDTO; import com.xdap.self_development.feign.dto.UserTokenDT0; import com.xdap.self_development.pojo.*; import com.xdap.self_development.pojo.dto.ApprovalNodeDTO; +import com.xdap.self_development.pojo.dto.ApprovalNodesDTO; import com.xdap.self_development.pojo.view.*; import com.xdap.self_development.service.BenchmarkingReportService; import com.xdap.self_development.service.XdapDeptUsersService; @@ -142,7 +143,7 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService .stream().map(ApprovalConfigPojo::getId) .collect(Collectors.toList()); bd.getBusinessDatabase() - .in("ids", ids) + .in("id", ids) .doDelete(ApprovalConfigPojo.class); List approvalConfigs = new ArrayList<>(); for (ApprovalNodeDTO node : form.getNodeList()) { @@ -228,6 +229,27 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService throw new CommonException("该报告正在审批中"); } + // 清除拒绝提醒待办 + ApprovalFlow historyFlow = bd.getBusinessDatabase() + .eq("template_id", report.getId()) + .in("flow_status", "REJECTED", "RETURNED") + .doQueryFirst(ApprovalFlow.class); + if (historyFlow != null) { + bd.getBusinessDatabase() + .eq("document_no", historyFlow.getId()) + .eq("data_type", "对标报告拒绝") + .update("is_delete", 1) + .doUpdate(TodoTaskPojo.class); + } + + // 更新流程状态 + bd.getBusinessDatabase() + .eq("id", approvalFlow.getId()) + .update("flow_status", "APPROVING") + .update("current_node", 1) + .update("pre_node", -1) + .doUpdate(ApprovalFlow.class); + // 获取首个节点 ApprovalConfigPojo firstNode = bd.getBusinessDatabase() .eq("approval_id", approvalFlow.getId()) @@ -252,7 +274,7 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService todoTask.setStatusCode(approvalFlow.getFlowStatus()); todoTask.setVersionNumber(1); todoTask.setOwnerId(person); - todoTask.setCurrentProcessor(JSON.toJSONString(firstNode.getAssignees())); + todoTask.setCurrentProcessor(firstNode.getAssignees()); list.add(todoTask); } bd.getBusinessDatabase().doBatchInsert(list); @@ -263,7 +285,7 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService initiatedTask.setCreatedById(form.getUserId()); initiatedTask.setCreatedBy(xdapDeptUsersService.selectUserByID(form.getUserId()).getUsername()); initiatedTask.setCurrentNode(firstNode.getNodeName()); - initiatedTask.setCurrentProcessor(JSON.toJSONString(firstNode.getAssignees())); + initiatedTask.setCurrentProcessor(firstNode.getAssignees()); initiatedTask.setDataType("对标报告审核"); initiatedTask.setProcessTitle(approvalFlow.getProcessTitle()); initiatedTask.setCreationDate(Timestamp.valueOf(LocalDateTime.now())); @@ -292,10 +314,153 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService .doUpdate(BenchmarkingReport.class); } + @Override + @Transactional(rollbackFor = Exception.class) + public void restartReportApproval(RestartReportApprovalForm form) { + // 获取报告 + BenchmarkingReport report = bd.getBusinessDatabase() + .eq("id", form.getReportId()) + .doQueryFirst(BenchmarkingReport.class); + + if (report == null) { + throw new CommonException("报告不存在"); + } + + ApprovalFlow historyFlow = bd.getBusinessDatabase() + .eq("template_id", report.getId()) + .eq("flow_status", "APPROVING") + .doQueryFirst(ApprovalFlow.class); + if (historyFlow != null) { + throw new CommonException("报告正在审批中"); + } + + // 修改报告 + BeanUtils.copyProperties(form, report); + report.setFilePath(form.getFileUrl() == null ? "" : form.getFileUrl()); + report.setUploadTime(Timestamp.valueOf(LocalDateTime.now())); + report.setStatus("DRAFT"); + bd.getBusinessDatabase() + .eq("id", report.getId()) + .doUpdate(report); + + // 清除拒绝提醒待办 + ApprovalFlow approvalFlow = bd.getBusinessDatabase() + .eq("template_id", report.getId()) + .in("flow_status", "REJECTED", "RETURNED") + .doQueryFirst(ApprovalFlow.class); + bd.getBusinessDatabase() + .eq("document_no", approvalFlow.getId()) + .eq("data_type", "对标报告拒绝") + .update("is_delete", 1) + .doUpdate(TodoTaskPojo.class); + + // 新增流程 + ApprovalFlow flow = new ApprovalFlow(); + flow.setTemplateId(report.getId()); + flow.setCurrentNode(1); + flow.setFlowStatus("APPROVING"); + flow.setProcessTitle(form.getProcessTitle()); + flow.setCreateBy(form.getUserId()); + flow.setCreatedTime(Timestamp.valueOf(LocalDateTime.now())); + flow.setFlowType(2); // 设置为报告审批流程 + flow.setExplanation(form.getExplanation()); + flow.setReturnNum(0); + flow.setPreNode(-1); + bd.getBusinessDatabase().doInsert(flow); + + // 创建审批配置 + List approvalConfigs = new ArrayList<>(); + for (ApprovalNodeDTO node : form.getNodeList()) { + ApprovalConfigPojo config = new ApprovalConfigPojo(); + config.setNodeName(node.getNodeName()); + config.setNodeOrder(node.getNodeOrder()); + config.setAssignees(JSON.toJSONString(node.getAssignees())); + config.setApprovalId(flow.getId()); + approvalConfigs.add(config); + } + bd.getBusinessDatabase().doBatchInsert(approvalConfigs); + + // 发起流程 + ApprovalPersonForm newForm = new ApprovalPersonForm(); + newForm.setUserId(form.getUserId()); + newForm.setFlowId(flow.getId()); + newForm.setReportId(report.getId()); + // 更新流程状态 + bd.getBusinessDatabase() + .eq("id", newForm.getFlowId()) + .update("flow_status", "APPROVING") + .update("current_node", 1) + .update("pre_node", -1) + .doUpdate(ApprovalFlow.class); + + // 获取首个节点 + ApprovalConfigPojo firstNode = bd.getBusinessDatabase() + .eq("approval_id", newForm.getFlowId()) + .eq("node_order", 1) + .doQueryFirst(ApprovalConfigPojo.class); + + + // 新增我的待办 + List list = new ArrayList<>(); + for (String person : JSON.parseArray(firstNode.getAssignees()).toJavaList(String.class)) { + TodoTaskPojo todoTask = new TodoTaskPojo(); + todoTask.setCreatedById(newForm.getUserId()); + todoTask.setCreatedBy(xdapDeptUsersService.selectUserByID(newForm.getUserId()).getUsername()); + todoTask.setCurrentNode(firstNode.getNodeName()); + todoTask.setDataType("对标报告审核"); + todoTask.setDocumentNo(newForm.getFlowId()); + todoTask.setCreationDate(Timestamp.valueOf(LocalDateTime.now())); + todoTask.setIsDelete(0); + todoTask.setModelId(report.getId()); + todoTask.setModelName(report.getProjectName() + "对标报告"); + todoTask.setProcessTitle(approvalFlow.getProcessTitle()); + todoTask.setStatusCode(approvalFlow.getFlowStatus()); + todoTask.setVersionNumber(1); + todoTask.setOwnerId(person); + todoTask.setCurrentProcessor(firstNode.getAssignees()); + list.add(todoTask); + } + bd.getBusinessDatabase().doBatchInsert(list); + + // 新增我创建的 + InitiatedTaskPojo initiatedTask = new InitiatedTaskPojo(); + initiatedTask.setDocumentNo(approvalFlow.getId()); + initiatedTask.setCreatedById(newForm.getUserId()); + initiatedTask.setCreatedBy(xdapDeptUsersService.selectUserByID(newForm.getUserId()).getUsername()); + initiatedTask.setCurrentNode(firstNode.getNodeName()); + initiatedTask.setCurrentProcessor(firstNode.getAssignees()); + initiatedTask.setDataType("对标报告审核"); + initiatedTask.setProcessTitle(approvalFlow.getProcessTitle()); + initiatedTask.setCreationDate(Timestamp.valueOf(LocalDateTime.now())); + initiatedTask.setIsDelete(0); + initiatedTask.setModelId(approvalFlow.getTemplateId()); + initiatedTask.setEnableReturn(1); + bd.getBusinessDatabase().doInsert(initiatedTask); + + // 新增发起记录 + bd.getBusinessDatabase().doInsert( + new ApprovalRecord( + approvalFlow.getId(), + "发起审批", + xdapDeptUsersService.selectUserByID(newForm.getUserId()).getUsername(), + "发起审批", + "MSG", + Timestamp.valueOf(LocalDateTime.now()), + 1 + ) + ); + + // 更新报告状态为校对中 + bd.getBusinessDatabase() + .eq("id", report.getId()) + .update("status", "APPROVING") + .doUpdate(BenchmarkingReport.class); + } + @Override @Transactional(rollbackFor = Exception.class) public void directSendingApproval(String reportId, String userId) { - // 获取模板 + // 获取报告 BenchmarkingReport report = bd.getBusinessDatabase() .eq("id", reportId) .doQueryFirst(BenchmarkingReport.class); @@ -307,7 +472,7 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService ApprovalFlow flow = bd.getBusinessDatabase() .eq("template_id", reportId) .ne("pre_node", -1) - .eq("flow_status", "APPROVING") + .ne("flow_status", "COMPLETE") .orderBy("created_time", "desc") .doQueryFirst(ApprovalFlow.class); if (flow == null) { @@ -364,7 +529,7 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService // 清除拒绝提醒待办 bd.getBusinessDatabase() .eq("document_no", flow.getId()) - .eq("data_type", "模板拒绝") + .eq("data_type", "对标报告拒绝") .update("is_delete", 1) .doUpdate(TodoTaskPojo.class); @@ -823,6 +988,45 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService .doUpdate(InitiatedTaskPojo.class); } + @Override + public ApprovalNodesDTO getApprovalUsers(String flowId) { + ApprovalNodesDTO nodes = new ApprovalNodesDTO(); + nodes.setProcessTitle(""); + nodes.setExplanation(""); + List list = new ArrayList<>(); + + // 获取到模板最近一次未完成的流程 + ApprovalFlow flow = bd.getBusinessDatabase() + .eq("id", flowId) + .doQueryFirst(ApprovalFlow.class); + if (flow == null) { + nodes.setNodeList(list); + return nodes; + } + + // 获取流程节点 + List approvalConfigs = bd.getBusinessDatabase() + .eq("approval_id", flow.getId()) + .doQuery(ApprovalConfigPojo.class); + + approvalConfigs.forEach(node -> { + ApprovalUserNodeDTO dto = new ApprovalUserNodeDTO(); + List userIds = JSON.parseArray(node.getAssignees()).toJavaList(String.class); + dto.setNodeName(node.getNodeName()); + dto.setNodeOrder(node.getNodeOrder()); + dto.setAssignees( + bd.getBusinessDatabase() + .in("id", userIds) + .doQuery(XdapUsers.class) + ); + list.add(dto); + }); + nodes.setProcessTitle(flow.getProcessTitle()); + nodes.setExplanation(flow.getExplanation()); + nodes.setNodeList(list); + return nodes; + } + @Override public BenchmarkingReport getReportDetail(String reportId) { return bd.getBusinessDatabase() @@ -854,12 +1058,29 @@ public class BenchmarkingReportServiceImpl implements BenchmarkingReportService .eq("id", approvalFlow.getTemplateId()) .doQueryFirst(BenchmarkingReport.class); + // 获取节点信息 + if (approvalFlow.getCurrentNode() != null) { + ApprovalConfigPojo currentNode = bd.getBusinessDatabase() + .eq("approval_id", approvalFlow.getId()) + .eq("node_order", approvalFlow.getCurrentNode()) + .doQueryFirst(ApprovalConfigPojo.class); + flowNodeView.setCurrentNode(currentNode.getNodeName()); + flowNodeView.setProcessor(getUserNames( + JSON.parseArray(currentNode.getAssignees()).toJavaList(String.class) + )); + } else { + flowNodeView.setCurrentNode("无"); + flowNodeView.setProcessor("无"); + } + // 获取流程信息 flowNodeView.setProgressTitle(approvalFlow.getProcessTitle()); flowNodeView.setContentName(report.getTitle()); flowNodeView.setTemplateVersion(1); flowNodeView.setExplanation(approvalFlow.getExplanation()); flowNodeView.setFlowStatus(approvalFlow.getFlowStatus()); + flowNodeView.setCreateBy(xdapDeptUsersService.selectUserByID(approvalFlow.getCreateBy()).getUsername()); + flowNodeView.setCreateTime(approvalFlow.getCreatedTime()); // 获取节点信息 List nodes = bd.getBusinessDatabase() diff --git a/src/main/java/com/xdap/self_development/service/impl/EngineParamDetailServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/EngineParamDetailServiceImpl.java index 21783eb..cf45f4a 100644 --- a/src/main/java/com/xdap/self_development/service/impl/EngineParamDetailServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/EngineParamDetailServiceImpl.java @@ -18,6 +18,7 @@ import com.xdap.self_development.controller.request.EngineParamDetailRequest; import com.xdap.self_development.enums.CurrentNodeEnum; import com.xdap.self_development.enums.ParamOperation; import com.xdap.self_development.enums.ParamValueVersionEnum; +import com.xdap.self_development.exception.CommonException; import com.xdap.self_development.exception.ExcelImportException; import com.xdap.self_development.listener.EngineParamDetailListener; import com.xdap.self_development.pojo.*; @@ -381,9 +382,9 @@ public class EngineParamDetailServiceImpl implements EngineParamDetailService { for (EngineParamDetailPojo detailPojo : detailPojos) { String filledBy = null; if (ObjectUtils.isNotEmpty(detailPojo.getFilledBy())) { - XdapUsers XdapUsers = + XdapUsers user = xdapDeptUsersService.selectUserByID(detailPojo.getFilledBy()); - filledBy = ObjectUtils.isEmpty(XdapUsers) ? "" : XdapUsers.getUsername(); + filledBy = ObjectUtils.isEmpty(user) ? "" : user.getUsername(); } EngineParamDetailDTO engineParamDetailDTO = new EngineParamDetailDTO(); BeanUtils.copyProperties(detailPojo, engineParamDetailDTO); @@ -553,6 +554,17 @@ public class EngineParamDetailServiceImpl implements EngineParamDetailService { return; } String engineModelId = engineParamDetailPojos.get(0).getEngineModelId(); + TodoTaskPojo todoTaskPojo = sw.buildFromDatasource("xdap_app_223770822127386625") + .eq("owner_id", userID) + .eq("model_id", engineModelId) + .eq("data_type", "任务分解") + .eq("is_delete", 0) + .doQueryFirst(TodoTaskPojo.class); + + if (todoTaskPojo == null) { + throw new CommonException("没有分解任务,请刷新重试"); + } + DataEntryEngineModelPojo modelPojo = dataEntryEngineModelService.selectBymodelId(engineModelId); XdapUsers XdapUsers = xdapDeptUsersService.selectUserByID(userID); @@ -568,26 +580,26 @@ public class EngineParamDetailServiceImpl implements EngineParamDetailService { .doUpdate(engineParamDetailPojo); } //为每个填写人分配任务 - Set filledBys = engineParamDetailPojos.stream().map(x -> x.getFilledBy()).collect(Collectors.toSet()); + Set filledBys = engineParamDetailPojos.stream().map(EngineParamDetailPojo::getFilledBy).collect(Collectors.toSet()); for (String filledBy : filledBys) { - TodoTaskPojo todoTaskPojo = new TodoTaskPojo(); - todoTaskPojo.setCreatedById(userID); - todoTaskPojo.setDocumentNo(UUID.randomUUID().toString()); - todoTaskPojo.setProcessTitle("填写参数"); - todoTaskPojo.setCurrentNode("待维护"); - todoTaskPojo.setStatusCode(null); - todoTaskPojo.setModelName(modelPojo.getModelName()); - todoTaskPojo.setModelId(modelPojo.getId()); - todoTaskPojo.setCreationDate(Timestamp.valueOf(LocalDateTime.now())); - todoTaskPojo.setDataType(CurrentNodeEnum.REPAIR.getDesc()); - todoTaskPojo.setVersionNumber(modelPojo.getVersionNumber()); - todoTaskPojo.setOwnerId(filledBy); - todoTaskPojo.setCurrentProcessor(JSON.toJSONString(Arrays.asList(filledBy))); - todoTaskPojo.setIsDelete(0); - todoTaskPojo.setCreatedBy(ObjectUtils.isNotEmpty(XdapUsers) ? XdapUsers.getUsername() : ""); - todoTaskPojos.add(todoTaskPojo); + TodoTaskPojo todoTask = new TodoTaskPojo(); + todoTask.setCreatedById(userID); + todoTask.setDocumentNo(UUID.randomUUID().toString()); + todoTask.setProcessTitle("填写参数"); + todoTask.setCurrentNode("待维护"); + todoTask.setStatusCode(null); + todoTask.setModelName(modelPojo.getModelName()); + todoTask.setModelId(modelPojo.getId()); + todoTask.setCreationDate(Timestamp.valueOf(LocalDateTime.now())); + todoTask.setDataType(CurrentNodeEnum.REPAIR.getDesc()); + todoTask.setVersionNumber(modelPojo.getVersionNumber()); + todoTask.setOwnerId(filledBy); + todoTask.setCurrentProcessor(JSON.toJSONString(Arrays.asList(filledBy))); + todoTask.setIsDelete(0); + todoTask.setCreatedBy(ObjectUtils.isNotEmpty(XdapUsers) ? XdapUsers.getUsername() : ""); + todoTaskPojos.add(todoTask); InitiatedTaskPojo initiatedTaskPojo = new InitiatedTaskPojo(); - BeanUtils.copyProperties(todoTaskPojo, initiatedTaskPojo); + BeanUtils.copyProperties(todoTask, initiatedTaskPojo); initiatedTaskPojo.setLastUpdatedBy(filledBy); taskPojos.add(initiatedTaskPojo); } @@ -600,10 +612,6 @@ public class EngineParamDetailServiceImpl implements EngineParamDetailService { .filter(x -> x.getEngineModelId().equals(engineModelId) && ObjectUtils.isEmpty(x.getFilledBy())).collect(Collectors.toList()); if (ObjectUtils.isEmpty(paramDetailPojos) || paramDetailPojos.isEmpty()) { //软删除这个代办人的任务 - TodoTaskPojo todoTaskPojo = sw.buildFromDatasource("xdap_app_223770822127386625") - .eq("owner_id", userID) - .eq("model_id", engineModelId) - .eq("data_type", "任务分解").doQueryFirst(TodoTaskPojo.class); //更新待办和发起的任务状态 sw.buildFromDatasource("xdap_app_223770822127386625") .update("is_delete", 1) @@ -685,7 +693,9 @@ public class EngineParamDetailServiceImpl implements EngineParamDetailService { } if ("TC分类表".equals(x.getParameterSource())) { return false; - } else { + } + // TODO:还需排除公式计算的 + else { return true; } }).collect(Collectors.toList()); diff --git a/src/main/java/com/xdap/self_development/service/impl/ModelRoleServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/ModelRoleServiceImpl.java index 0d97bd2..7636fbf 100644 --- a/src/main/java/com/xdap/self_development/service/impl/ModelRoleServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/ModelRoleServiceImpl.java @@ -189,12 +189,13 @@ public class ModelRoleServiceImpl implements ModelRoleService { log.warn("该角色没有权限"); return null; } - List perids = role.stream().map(x -> x.getPerId()).distinct().collect(Collectors.toList()); - List menuParameters = businessDatabase.getBusinessDatabase() - .in("id", perids) - .doQuery(ModelMenuParameterPojo.class); - //获取权限 - for (ModelMenuParameterPojo menuParameter : menuParameters) { + try { + List perids = role.stream().map(x -> x.getPerId()).distinct().collect(Collectors.toList()); + List menuParameters = businessDatabase.getBusinessDatabase() + .in("id", perids) + .doQuery(ModelMenuParameterPojo.class); + //获取权限 + for (ModelMenuParameterPojo menuParameter : menuParameters) { /* 权限变更不再管理菜单且不再存参数 改为子系统+零部件 ModelMenuDictionaryPojo modelMenuDictionaryPojo = modelMenuPermissionService.selectMenuById(menuParameter.getMenuId()); String parameterId = menuParameter.getParameterId(); @@ -211,17 +212,20 @@ public class ModelRoleServiceImpl implements ModelRoleService { Parameter parameter = parameters.get(0);*/ - ParamPermissionEnum byCode = ParamPermissionEnum.getByCode(menuParameter.getParameterPer()); - if (byCode == null) { - throw new RuntimeException("权限异常"); - } - MenuParamView menuParamView = new MenuParamView(); - menuParamView.setId(menuParameter.getId()); + ParamPermissionEnum byCode = ParamPermissionEnum.getByCode(menuParameter.getParameterPer()); + if (byCode == null) { + throw new RuntimeException("权限异常"); + } + MenuParamView menuParamView = new MenuParamView(); + menuParamView.setId(menuParameter.getId()); // menuParamView.setMenuPath(modelMenuDictionaryPojo.getMenuDesc()); // menuParamView.setPartsName(menuParameter.getPartsName()); - menuParamView.setPermissionPath(menuParameter.getPermissionPath()); - menuParamView.setParameterPer(byCode.getDesc()); - menuParamViews.add(menuParamView); + menuParamView.setPermissionPath(menuParameter.getPermissionPath()); + menuParamView.setParameterPer(byCode.getDesc()); + menuParamViews.add(menuParamView); + } + } catch (Exception e) { + return null; } return menuParamViews; diff --git a/src/main/java/com/xdap/self_development/service/impl/TcDataQueryServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/TcDataQueryServiceImpl.java index 5d7c9b5..550133b 100644 --- a/src/main/java/com/xdap/self_development/service/impl/TcDataQueryServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/TcDataQueryServiceImpl.java @@ -9,6 +9,7 @@ import com.xdap.self_development.config.BusinessDatabase; import com.xdap.self_development.controller.request.AnalysisRequest; import com.xdap.self_development.controller.request.AnalysisVerRequest; import com.xdap.self_development.controller.request.ModelComparisonExportRequest; +import com.xdap.self_development.controller.request.ModelItem; import com.xdap.self_development.pojo.*; import com.xdap.self_development.service.DataEntryEngineModelService; import com.xdap.self_development.service.EngineParamDetailService; @@ -19,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; @@ -31,7 +33,7 @@ import java.util.stream.Collectors; @Slf4j public class TcDataQueryServiceImpl implements TcDataQueryService { - @Autowired + @Resource private BusinessDatabase bd; @Autowired private EngineParamDetailService engineParamDetailService; @@ -412,7 +414,7 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { @Override public void modelComparisonExport(ModelComparisonExportRequest request, HttpServletResponse response) { - List modelIds = request.getModelIds(); + List models = request.getModels(); String userID = request.getUserID(); List subsystemParts = request.getSubsystemParts(); @@ -422,10 +424,10 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { // 获取机型名称映射 Map modelNames = new HashMap<>(); - for (String modelId : modelIds) { - DataEntryEngineModelPojo model = engineModelService.selectBymodelId(modelId); - if (model != null) { - modelNames.put(modelId, model.getModelName()); + for (ModelItem model : models) { + DataEntryEngineModelPojo tempModel = engineModelService.selectBymodelId(model.getModelId()); + if (tempModel != null) { + modelNames.put(model.getModelId(), tempModel.getModelName()); } } @@ -435,9 +437,9 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { Map parameterSubsystemParts = new HashMap<>(); // 从Redis获取每个机型的数据 - for (String modelId : modelIds) { + for (ModelItem model : models) { try { - String redisKey = userID + ":" + modelId + ":" + "ComparisonExport"; + String redisKey = userID + ":" + model.getModelId() + ":" + model.getParameterVersion() + ":" + "ComparisonExport"; String cachedData = redisTemplate.opsForValue().get(redisKey); if (ObjectUtils.isNotEmpty(cachedData)) { @@ -475,11 +477,11 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { } // 设置该机型的参数值 - parameterValues.get(paramName).put(modelId, paramValue); + parameterValues.get(paramName).put(model.getModelId(), paramValue); } } } catch (Exception e) { - log.error("从Redis读取机型{}数据失败: {}", modelId, e.getMessage()); + log.error("从Redis读取机型{}数据失败: {}", model.getModelId(), e.getMessage()); } } @@ -491,8 +493,8 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { header.add("子系统"); header.add("零部件"); header.add("参数"); - for (String modelId : modelIds) { - header.add(modelNames.getOrDefault(modelId, "未知机型")); + for (ModelItem model : models) { + header.add(modelNames.getOrDefault(model.getModelId(), "未知机型")); } excelData.add(header); @@ -515,8 +517,8 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { // 每个机型的参数值 Map values = entry.getValue(); - for (String modelId : modelIds) { - row.add(values.getOrDefault(modelId, "")); + for (ModelItem model : models) { + row.add(values.getOrDefault(model.getModelId(), "")); } excelData.add(row); @@ -654,7 +656,7 @@ public class TcDataQueryServiceImpl implements TcDataQueryService { } //将allPojos存到Redis,KEY为userID+engineModelID+预操作,过期时间24小时 try { - String redisKey = analysisRequest.getUserID() + ":" + analysisRequest.getEngineModelID() + ":" + "ComparisonExport"; + String redisKey = analysisRequest.getUserID() + ":" + analysisRequest.getEngineModelID() + ":" + analysisRequest.getParameterVersion() + ":" + "ComparisonExport"; String jsonValue = objectMapper.writeValueAsString(systemDataList); redisTemplate.opsForValue().set(redisKey, jsonValue, 24, java.util.concurrent.TimeUnit.HOURS); log.info("成功将数据存储到Redis,KEY: {}", redisKey); diff --git a/src/main/java/com/xdap/self_development/service/impl/TemplateApprovalServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/TemplateApprovalServiceImpl.java index a73cc98..6505a45 100644 --- a/src/main/java/com/xdap/self_development/service/impl/TemplateApprovalServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/TemplateApprovalServiceImpl.java @@ -76,6 +76,19 @@ public class TemplateApprovalServiceImpl implements TemplateApprovalService { throw new CommonException("空参数模板不允许发起审批"); } + // 清除拒绝提醒待办 + ApprovalFlow historyFlow = bd.getBusinessDatabase() + .eq("template_id", template.getId()) + .in("flow_status", "REJECTED", "RETURNED") + .doQueryFirst(ApprovalFlow.class); + if (historyFlow != null) { + bd.getBusinessDatabase() + .eq("document_no", historyFlow.getId()) + .eq("data_type", "模板拒绝") + .update("is_delete", 1) + .doUpdate(TodoTaskPojo.class); + } + // 新增流程 ApprovalFlow flow = new ApprovalFlow(); flow.setTemplateId(template.getId()); @@ -195,7 +208,7 @@ public class TemplateApprovalServiceImpl implements TemplateApprovalService { ApprovalFlow flow = bd.getBusinessDatabase() .eq("template_id", templateId) .ne("pre_node", -1) - .eq("flow_status", "APPROVING") + .ne("flow_status", "COMPLETE") .orderBy("created_time", "desc") .doQueryFirst(ApprovalFlow.class); if (flow == null) { @@ -727,12 +740,29 @@ public class TemplateApprovalServiceImpl implements TemplateApprovalService { .eq("id", approvalFlow.getTemplateId()) .doQueryFirst(Template.class); + // 获取节点信息 + if (approvalFlow.getCurrentNode() != null) { + ApprovalConfigPojo currentNode = bd.getBusinessDatabase() + .eq("approval_id", approvalFlow.getId()) + .eq("node_order", approvalFlow.getCurrentNode()) + .doQueryFirst(ApprovalConfigPojo.class); + flowNodeView.setCurrentNode(currentNode.getNodeName()); + flowNodeView.setProcessor(getUserNames( + JSON.parseArray(currentNode.getAssignees()).toJavaList(String.class) + )); + } else { + flowNodeView.setCurrentNode("无"); + flowNodeView.setProcessor("无"); + } + // 获取流程信息 flowNodeView.setProgressTitle(approvalFlow.getProcessTitle()); flowNodeView.setContentName(template.getTemplateName()); flowNodeView.setTemplateVersion(template.getVersion()); flowNodeView.setExplanation(approvalFlow.getExplanation()); flowNodeView.setFlowStatus(approvalFlow.getFlowStatus()); + flowNodeView.setCreateBy(xdapDeptUsersService.selectUserByID(approvalFlow.getCreateBy()).getUsername()); + flowNodeView.setCreateTime(approvalFlow.getCreatedTime()); // 获取节点信息 List nodes = bd.getBusinessDatabase() diff --git a/src/main/java/com/xdap/self_development/service/impl/TodoTaskServiceImpl.java b/src/main/java/com/xdap/self_development/service/impl/TodoTaskServiceImpl.java index 998f11b..a2ad141 100644 --- a/src/main/java/com/xdap/self_development/service/impl/TodoTaskServiceImpl.java +++ b/src/main/java/com/xdap/self_development/service/impl/TodoTaskServiceImpl.java @@ -1,10 +1,10 @@ package com.xdap.self_development.service.impl; - import com.alibaba.fastjson2.JSON; import com.definesys.mpaas.query.MpaasQueryFactory; import com.definesys.mpaas.query.db.PageQueryResult; import com.xdap.api.moudle.user.pojo.XdapUsers; +import com.xdap.self_development.config.BusinessDatabase; import com.xdap.self_development.controller.request.TodoReturnRequest; import com.xdap.self_development.controller.request.TodoTaskRequest; import com.xdap.self_development.controller.request.YcGetTodoTaskRequest; @@ -17,14 +17,12 @@ import com.xdap.self_development.service.EngineParamDetailService; import com.xdap.self_development.service.TodoTaskService; import com.xdap.self_development.service.XdapDeptUsersService; import org.apache.commons.lang3.ObjectUtils; -import org.apache.poi.ss.formula.functions.T; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Resource; import java.sql.Timestamp; -import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; @@ -44,14 +42,18 @@ import java.util.stream.Collectors; @Service public class TodoTaskServiceImpl implements TodoTaskService { - @Autowired + @Resource private MpaasQueryFactory sw; - @Autowired + @Resource private EngineParamDetailService engineParamDetailService; - @Autowired + @Resource private XdapDeptUsersService xdapDeptUsersService; - @Autowired + @Resource private DataEntryEngineModelService dataEntryEngineModelService; + @Resource + private BusinessDatabase bd; + @Resource + private TodoTaskService myself; private final String SYSTEM = "研发数据管理平台"; @@ -103,7 +105,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { } }).collect(Collectors.toList()); } catch (Exception e) { - strings = Arrays.asList(currentProcessor); + strings = Collections.singletonList(currentProcessor); } } taskPojo1.setCurrentProcessor(String.join(",", strings)); @@ -117,7 +119,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { @Override public PageResultDTO selectByresPersonTodoPage(TodoTaskRequest todoTaskRequest) { - PageResultDTO pageResultDTO = new PageResultDTO(); + PageResultDTO pageResultDTO = new PageResultDTO<>(); PageQueryResult queryResult = sw.buildFromDatasource("xdap_app_223770822127386625") .eq("owner_id", todoTaskRequest.getPersonId()) .eq("is_delete", 0) @@ -143,7 +145,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { } }).collect(Collectors.toList()); } catch (Exception e) { - strings = Arrays.asList(currentProcessor); + strings = Collections.singletonList(currentProcessor); } } todoTaskPojo1.setCurrentProcessor(String.join(",", strings)); @@ -157,7 +159,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { @Override public PageResultDTO selectByresPersonCompletedPage(TodoTaskRequest taskRequest) { - PageResultDTO pageResultDTO = new PageResultDTO(); + PageResultDTO pageResultDTO = new PageResultDTO<>(); PageQueryResult queryResult = sw.buildFromDatasource("xdap_app_223770822127386625") .eq("owner_id", taskRequest.getPersonId()) .eq("is_delete", 0) @@ -183,7 +185,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { } }).collect(Collectors.toList()); } catch (Exception e) { - strings = Arrays.asList(currentProcessor); + strings = Collections.singletonList(currentProcessor); } } taskPojo1.setCurrentProcessor(String.join(",", strings)); @@ -217,20 +219,23 @@ public class TodoTaskServiceImpl implements TodoTaskService { .doUpdate(EngineParamDetailPojo.class); } //生成分解任务给责任人 参数根据机型+责任人分组 生成数据 - List dtoList = paramDetailPojosAll.stream() + List dtoList = new ArrayList<>(paramDetailPojosAll.stream() // 以responsiblePersonId组合作为key,元素本身作为value - .collect(Collectors.toMap(dto -> dto.getResPersonId(), + .collect(Collectors.toMap(EngineParamDetailDTO::getResPersonId, pojo -> pojo, (existing, replacement) -> existing )) // 提取去重后的value集合 - .values() - .stream() - .collect(Collectors.toList()); + .values()); ArrayList todoTaskPojos = new ArrayList<>(); ArrayList taskPojos = new ArrayList<>(); - // TODO:清除待办 + // 清除待办 + bd.getBusinessDatabase() + .eq("model_id", modelId) + .eq("data_type", CurrentNodeEnum.JOBSPLIT.getDesc()) + .update("is_delete", 1) + .doUpdate(TodoTaskPojo.class); XdapUsers xdapUsersPojo = xdapDeptUsersService.selectUserByID(userID); DataEntryEngineModelPojo modelPojo = dataEntryEngineModelService.selectBymodelId(modelId); @@ -241,7 +246,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { todoTaskPojo.setDocumentNo(UUID.randomUUID().toString()); todoTaskPojo.setProcessTitle("分发参数填写人"); todoTaskPojo.setCurrentNode("待分解"); - todoTaskPojo.setCurrentProcessor(JSON.toJSONString(Arrays.asList(engineParamDetailDTO.getResPersonId()))); + todoTaskPojo.setCurrentProcessor(JSON.toJSONString(Collections.singletonList(engineParamDetailDTO.getResPersonId()))); todoTaskPojo.setStatusCode(null/*String.valueOf(ParamValueVersionEnum.DRAFT.getCode())*/); todoTaskPojo.setModelName(ObjectUtils.isNotEmpty(modelPojo) ? modelPojo.getModelName() : ""); todoTaskPojo.setModelId(modelId); @@ -257,8 +262,8 @@ public class TodoTaskServiceImpl implements TodoTaskService { initiatedTaskPojo.setLastUpdatedBy(engineParamDetailDTO.getResPersonId()); taskPojos.add(initiatedTaskPojo); } - batchInsertTodoTask(todoTaskPojos); - batchInsertInitiatedTask(taskPojos); + myself.batchInsertTodoTask(todoTaskPojos); + myself.batchInsertInitiatedTask(taskPojos); List detailPojos = engineParamDetailService.selectByModelNewVersion(modelId); List paramDetailPojos = detailPojos.stream() .filter(x -> ObjectUtils.isEmpty(x.getResponsiblePersonId())).collect(Collectors.toList()); @@ -318,7 +323,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { todoTaskPojo.setDataType(CurrentNodeEnum.JOBSPLIT.getDesc()); todoTaskPojo.setVersionNumber(todoTaskPojo1.getVersionNumber()); todoTaskPojo.setIsDelete(0); - todoTaskPojo.setCurrentProcessor(JSON.toJSONString(Arrays.asList(todoTaskPojo1.getCreatedById()))); + todoTaskPojo.setCurrentProcessor(JSON.toJSONString(Collections.singletonList(todoTaskPojo1.getCreatedById()))); todoTaskPojo.setOwnerId(todoTaskPojo1.getCreatedById()); todoTaskPojo.setCreatedBy(ObjectUtils.isNotEmpty(xdapUsersPojo) ? xdapUsersPojo.getUsername() : ""); sw.buildFromDatasource("xdap_app_223770822127386625").doInsert(todoTaskPojo); @@ -353,7 +358,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { if (ObjectUtils.isEmpty(username)) { return null; } - ArrayList dtos = new ArrayList(); + ArrayList dtos = new ArrayList<>(); XdapUsers xdapUsers = xdapDeptUsersService.selectUserByCode(username); if (ObjectUtils.isNotEmpty(xdapUsers)) { String id = xdapUsers.getId(); @@ -393,7 +398,7 @@ public class TodoTaskServiceImpl implements TodoTaskService { } String userCode = ObjectUtils.isEmpty(ycGetTodoTaskRequest.getUsername()) ? "" : ycGetTodoTaskRequest.getUsername(); String userid = ObjectUtils.isEmpty(xdapDeptUsersService.selectUserByCode(userCode)) ? "" : xdapDeptUsersService.selectUserByCode(userCode).getId(); - List todoTaskPojos = new ArrayList<>(); + List todoTaskPojos; PageResultDTO ycCompletedResultDTOPageResultDTO = new PageResultDTO<>(); ArrayList ycCompletedResultDTOS = new ArrayList<>(); String str = "owner_id"; @@ -429,8 +434,8 @@ public class TodoTaskServiceImpl implements TodoTaskService { ) { String page = ycGetTodoTaskRequest.getPage(); String pageSize = ycGetTodoTaskRequest.getPageSize(); - int pageint = 0; - int pageSizeint = 999; + int pageint; + int pageSizeint; try { pageint = Integer.parseInt(page); pageSizeint = Integer.parseInt(pageSize); @@ -477,10 +482,12 @@ public class TodoTaskServiceImpl implements TodoTaskService { return ycCompletedResultDTOPageResultDTO; } + @Override @Transactional(rollbackFor = Exception.class) // 发生任何异常都回滚 public void batchInsertTodoTask(List todoTaskPojos) { sw.buildFromDatasource("xdap_app_223770822127386625").doBatchInsert(todoTaskPojos); } + @Override @Transactional(rollbackFor = Exception.class) // 发生任何异常都回滚 public void batchInsertInitiatedTask(List taskPojos) { sw.buildFromDatasource("xdap_app_223770822127386625").doBatchInsert(taskPojos); diff --git a/src/main/java/com/xdap/self_development/utils/FormulaValidator.java b/src/main/java/com/xdap/self_development/utils/FormulaValidator.java index 83bb841..1738b2f 100644 --- a/src/main/java/com/xdap/self_development/utils/FormulaValidator.java +++ b/src/main/java/com/xdap/self_development/utils/FormulaValidator.java @@ -1,76 +1,289 @@ package com.xdap.self_development.utils; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class FormulaValidator { + // 定义支持的数学运算符 + private static final Set OPERATORS = new HashSet<>(Arrays.asList('+', '-', '*', '/')); + // 定义支持的常量 + private static final Set CONSTANTS = new HashSet<>(Arrays.asList("π")); + + /** + * 详细验证公式合法性(基于运算逻辑) + * @param formula 待验证的公式 + * @return 验证结果(是否合法、错误信息) + */ public static Map validateFormulaDetailed(String formula) { Map result = new HashMap<>(); + String originalFormula = formula; - // 检查公式是否为空 + // 1. 基础空值检查 if (formula == null || formula.trim().isEmpty()) { result.put("isValid", false); - result.put("errorMessage", "公式不能为空"); + result.put("errorMessage", String.format("公式不能为空 [原始公式:%s]", originalFormula)); return result; } - // 定义允许的字符:中文、数字、英文括号、运算符和空格 - Pattern allowedPattern = Pattern.compile("^[\\u4e00-\\u9fa50-9()*/+\\-\\s.π]+$"); - Matcher matcher = allowedPattern.matcher(formula); + // 清理公式:移除所有空格,统一格式 + formula = formula.replaceAll("\\s+", ""); - if (!matcher.matches()) { + // 2. 仅对英文括号进行匹配检查(中文括号视为参数字符) + if (!isEnglishParenthesesBalanced(formula)) { result.put("isValid", false); - - // 找出非法字符 - StringBuilder illegalChars = new StringBuilder(); - Pattern illegalPattern = Pattern.compile("[^\\u4e00-\\u9fa50-9()*/+\\-\\s.π]"); - Matcher illegalMatcher = illegalPattern.matcher(formula); - - while (illegalMatcher.find()) { - String illegalChar = illegalMatcher.group(); - if (!illegalChars.toString().contains(illegalChar)) { - if (illegalChars.length() > 0) { - illegalChars.append(", "); - } - illegalChars.append("'").append(illegalChar).append("'"); - } - } - - result.put("errorMessage", "公式包含非法字符: " + illegalChars.toString()); + result.put("errorMessage", String.format("英文括号不匹配 [原始公式:%s]", originalFormula)); return result; } - // 检查括号是否匹配 - if (!isParenthesesBalanced(formula)) { + // 3. 语法结构校验(核心) + try { + // 解析公式并验证运算逻辑(新增顶层括号处理) + validateTopLevelFormula(formula, originalFormula); + } catch (IllegalArgumentException e) { result.put("isValid", false); - result.put("errorMessage", "括号不匹配"); + result.put("errorMessage", e.getMessage()); return result; } - // 检查运算符使用是否合理 - if (hasInvalidOperatorUsage(formula)) { + // 4. 数字格式校验(适配参数+数字混合场景) + String numberFormatError = checkNumberFormatDetailed(formula, originalFormula); + if (numberFormatError != null) { result.put("isValid", false); - result.put("errorMessage", "运算符使用不合理"); - return result; - } - - // 检查数字格式是否正确(避免多个小数点等) - if (hasInvalidNumberFormat(formula)) { - result.put("isValid", false); - result.put("errorMessage", "数字格式不正确"); + result.put("errorMessage", numberFormatError); return result; } + // 验证通过 result.put("isValid", true); - result.put("errorMessage", "公式格式正确"); + result.put("errorMessage", String.format("公式格式正确 [原始公式:%s]", originalFormula)); + // 可选:返回提取的参数信息 + result.put("parameters", extractParameters(originalFormula)); return result; } - // 检查括号是否匹配 - private static boolean isParenthesesBalanced(String formula) { + /** + * 验证顶层公式语法(处理以括号开头的场景) + */ + private static void validateTopLevelFormula(String formula, String originalFormula) { + // 移除所有空格(已提前清理,此处双重保障) + String cleaned = formula.replaceAll("\\s+", ""); + + // 处理空公式 + if (cleaned.isEmpty()) { + throw new IllegalArgumentException(String.format("公式不能为空 [原始公式:%s]", originalFormula)); + } + + // 递归解析括号表达式,将括号块视为整体操作数 + String parsedFormula = parseParenthesesBlocks(cleaned, originalFormula); + + // 验证解析后的公式(括号块已替换为占位符,变为普通运算式) + validateSubFormula(parsedFormula, cleaned, originalFormula); + } + + /** + * 递归解析括号块,将每对匹配的括号内内容视为整体操作数 + * @param formula 待解析公式 + * @param originalFormula 原始公式(用于错误提示) + * @return 替换括号块为占位符的公式 + */ + private static String parseParenthesesBlocks(String formula, String originalFormula) { + // 匹配最外层括号对(非嵌套) + Pattern outerBracketPattern = Pattern.compile("\\(([^()]*)\\)"); + Matcher matcher = outerBracketPattern.matcher(formula); + + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String innerContent = matcher.group(1); + + // 检查括号内是否为空 + if (innerContent.trim().isEmpty()) { + throw new IllegalArgumentException(String.format("英文括号内不能为空 [错误片段:()][原始公式:%s]", originalFormula)); + } + + // 递归解析括号内的子内容(处理嵌套括号) + String parsedInner = parseParenthesesBlocks(innerContent, originalFormula); + + // 验证括号内的子公式 + validateSubFormula(parsedInner, innerContent, originalFormula); + + // 替换括号块为占位符(使用特殊标记,避免与正常内容冲突) + matcher.appendReplacement(sb, "OPERAND_PLACEHOLDER"); + } + matcher.appendTail(sb); + + return sb.toString(); + } + + /** + * 验证子公式的运算逻辑(操作数+运算符交替规则) + * @param subFormula 无嵌套括号的子公式(或括号已替换为占位符) + * @param currentFormula 当前处理的公式片段 + * @param originalFormula 原始公式(用于错误提示) + * @throws IllegalArgumentException 语法错误时抛出异常 + */ + private static void validateSubFormula(String subFormula, String currentFormula, String originalFormula) { + if (subFormula.isEmpty()) { + return; + } + + // 拆分运算单元(参数/数字/常量/占位符 + 运算符) + List tokens = tokenizeFormula(subFormula); + + // 验证运算单元的合法性 + for (int i = 0; i < tokens.size(); i++) { + String token = tokens.get(i); + + // 占位符直接视为合法操作数 + if (token.equals("OPERAND_PLACEHOLDER")) { + continue; + } + + // 第一个token:可以是参数/数字/常量/负号/占位符 + if (i == 0) { + if (token.equals("-")) { + // 负号开头,检查下一个token是否为有效值 + if (tokens.size() < 2 || !isValidOperand(tokens.get(1)) && !tokens.get(1).equals("OPERAND_PLACEHOLDER")) { + throw new IllegalArgumentException(String.format("负号后必须跟有效参数/数字/常量 [错误片段:-%s][原始公式:%s]", + tokens.size() >= 2 ? tokens.get(1) : "", originalFormula)); + } + } else if (!isValidOperand(token)) { + throw new IllegalArgumentException(String.format("公式开头必须是参数/数字/常量,当前为无效字符:%s [错误片段:%s][原始公式:%s]", + token, currentFormula, originalFormula)); + } + } else { + // 偶数位必须是运算符,奇数位必须是操作数/占位符 + if (i % 2 == 0) { // 操作数位置 + if (!isValidOperand(token)) { + throw new IllegalArgumentException(String.format("运算符后必须跟有效参数/数字/常量,当前为无效字符:%s [错误片段:%s][原始公式:%s]", + token, currentFormula, originalFormula)); + } + } else { // 运算符位置 + if (!isValidOperator(token)) { + throw new IllegalArgumentException(String.format("非法运算符或连续运算符:%s [错误片段:%s][原始公式:%s]", + token, currentFormula, originalFormula)); + } + // 检查除法分母不能为0 + if (token.equals("/") && i + 1 < tokens.size()) { + String nextToken = tokens.get(i + 1); + if (!nextToken.equals("OPERAND_PLACEHOLDER") && nextToken.matches("^-?0+(\\.0+)?$")) { + throw new IllegalArgumentException(String.format("除法运算分母不能为0 [错误片段:/%s][原始公式:%s]", + nextToken, originalFormula)); + } + } + } + } + } + + // 验证公式不能以运算符结尾 + if (!tokens.isEmpty() && isValidOperator(tokens.get(tokens.size() - 1))) { + throw new IllegalArgumentException(String.format("公式不能以运算符[%s]结尾 [错误片段:%s][原始公式:%s]", + tokens.get(tokens.size() - 1), currentFormula, originalFormula)); + } + } + + /** + * 将公式拆分为运算单元(操作数+运算符) + * 优化:确保带字母/数字的中文参数名被完整识别 + * @param formula 无嵌套括号的子公式(或含占位符) + * @return 拆分后的token列表 + */ + private static List tokenizeFormula(String formula) { + List tokens = new ArrayList<>(); + StringBuilder currentOperand = new StringBuilder(); + + for (int i = 0; i < formula.length(); i++) { + char c = formula.charAt(i); + + // 处理占位符(整体作为操作数) + if (c == 'O' && formula.startsWith("OPERAND_PLACEHOLDER", i)) { + if (currentOperand.length() > 0) { + tokens.add(currentOperand.toString()); + currentOperand.setLength(0); + } + tokens.add("OPERAND_PLACEHOLDER"); + i += "OPERAND_PLACEHOLDER".length() - 1; + continue; + } + + // 处理运算符(特殊处理开头/括号后的负号) + if (OPERATORS.contains(c)) { + // 负号不是运算符的情况:开头、运算符后、英文括号后 + boolean isNegativeSign = (c == '-') && (i == 0 || + OPERATORS.contains(formula.charAt(i - 1)) || + formula.charAt(i - 1) == '('); + + if (isNegativeSign) { + currentOperand.append(c); + } else { + // 运算符,先保存当前操作数 + if (currentOperand.length() > 0) { + tokens.add(currentOperand.toString()); + currentOperand.setLength(0); + } + tokens.add(String.valueOf(c)); + } + } else { + // 操作数字符(中文、中文括号、数字、字母、小数点、π) + currentOperand.append(c); + } + } + + // 添加最后一个操作数 + if (currentOperand.length() > 0) { + tokens.add(currentOperand.toString()); + } + + return tokens; + } + + /** + * 检查是否为合法操作数(核心优化:支持中文+数字+字母+中文括号) + * @param operand 待检查的操作数 + * @return 是否合法 + */ + private static boolean isValidOperand(String operand) { + if (operand == null || operand.isEmpty()) { + return false; + } + + // 处理负号开头的情况 + String checkOperand = operand.startsWith("-") ? operand.substring(1) : operand; + + // 1. 常量π + if (CONSTANTS.contains(checkOperand)) { + return true; + } + + // 2. 纯数字(整数/小数) + if (checkOperand.matches("\\d+(\\.\\d+)?")) { + return true; + } + + // 3. 中文参数名(核心优化:支持中文、数字、字母、中文括号的任意组合) + // 正则说明:[\u4e00-\u9fa5] 中文 | [0-9] 数字 | [a-zA-Z] 字母 | [()] 中文括号 + if (checkOperand.matches("[\\u4e00-\\u9fa50-9a-zA-Z()]+")) { + // 确保参数名至少包含一个中文(避免纯字母/数字被误判为参数) + return checkOperand.matches(".*[\\u4e00-\\u9fa5]+.*"); + } + + return false; + } + + /** + * 检查是否为合法运算符 + * @param operator 待检查的运算符 + * @return 是否合法 + */ + private static boolean isValidOperator(String operator) { + return operator != null && operator.length() == 1 && OPERATORS.contains(operator.charAt(0)); + } + + /** + * 仅检查英文括号是否匹配 + */ + private static boolean isEnglishParenthesesBalanced(String formula) { int balance = 0; for (char c : formula.toCharArray()) { if (c == '(') { @@ -81,62 +294,66 @@ public class FormulaValidator { return false; // 闭括号多于开括号 } } + // 忽略中文括号 } return balance == 0; } - // 检查运算符使用是否合理 - private static boolean hasInvalidOperatorUsage(String formula) { - // 移除所有空格以便检查 - String cleaned = formula.replaceAll("\\s+", ""); + /** + * 检查数字格式是否正确(优化错误提示) + * @return 错误信息,无错误返回null + */ + private static String checkNumberFormatDetailed(String formula, String originalFormula) { + // 先移除中文括号,避免干扰数字格式检查 + String cleanFormula = formula.replaceAll("[()]", ""); - // 检查开头和结尾的运算符(除了开头的负号) - if (cleaned.matches("^[*/+].*") || cleaned.matches(".*[*/+\\-]$")) { - return true; + // 1. 检查多个小数点 + Pattern multiDotPattern = Pattern.compile("\\d+\\.\\d*\\."); + Matcher multiDotMatcher = multiDotPattern.matcher(cleanFormula); + if (multiDotMatcher.find()) { + String errorPart = multiDotMatcher.group(); + return String.format("数字格式错误:存在多个小数点 [%s] [错误片段:%s][原始公式:%s]", + errorPart, formula, originalFormula); } - // 检查连续的运算符(允许负号的情况) - Pattern operatorPattern = Pattern.compile("[*/+\\-]{2,}"); - Matcher matcher = operatorPattern.matcher(cleaned); + // 2. 检查小数点前后是否有非法字符(排除英文括号) + Pattern invalidDotPattern = Pattern.compile("\\.[\\u4e00-\\u9fa5()]|([^0-9.])\\.([^0-9.])"); + Matcher invalidDotMatcher = invalidDotPattern.matcher(cleanFormula); + if (invalidDotMatcher.find()) { + String errorPart = invalidDotMatcher.group(); + return String.format("数字格式错误:小数点位置非法 [%s] [错误片段:%s][原始公式:%s]", + errorPart, formula, originalFormula); + } + + // 3. 检查以小数点开头/结尾的孤立小数点 + if (cleanFormula.matches("^\\..*|.*[^0-9]\\.$")) { + return String.format("数字格式错误:不能以孤立小数点开头或结尾 [错误片段:%s][原始公式:%s]", + formula, originalFormula); + } + + return null; + } + + /** + * 提取公式中的所有参数(支持带字母/数字/中文括号的参数名) + */ + public static List extractParameters(String formula) { + List parameters = new ArrayList<>(); + if (formula == null || formula.trim().isEmpty()) { + return parameters; + } + + // 匹配中文参数名(核心优化:支持中文+数字+字母+中文括号) + Pattern paramPattern = Pattern.compile("[\\u4e00-\\u9fa5][\\u4e00-\\u9fa50-9a-zA-Z()]*"); + Matcher matcher = paramPattern.matcher(formula); while (matcher.find()) { - String operators = matcher.group(); - // 允许的运算符组合:*- /- +- -- (负号情况) - if (!operators.matches("\\*\\-|/\\-|\\+\\-|\\-\\-")) { - return true; + String param = matcher.group(); + if (!parameters.contains(param)) { + parameters.add(param); } } - return false; - } - - // 检查数字格式是否正确 - private static boolean hasInvalidNumberFormat(String formula) { - // 匹配数字模式(包括小数) - Pattern numberPattern = Pattern.compile("\\d+\\.\\d*\\.|\\.[\\u4e00-\\u9fa5]|\\.\\.[\\d\\u4e00-\\u9fa5]"); - Matcher matcher = numberPattern.matcher(formula); - - if (matcher.find()) { - return true; // 找到无效的数字格式 - } - - // 检查以小数点开头或结尾的数字 - if (formula.matches("^\\..*|.*[^0-9]\\.$")) { - return true; - } - - return false; - } - - // 提取公式中的所有参数(中文参数名和数字) - public static void extractParameters(String formula) { - // 匹配中文参数名和数字 - Pattern paramPattern = Pattern.compile("[\\u4e00-\\u9fa5]+|\\d+(?:\\.\\d+)?"); - Matcher matcher = paramPattern.matcher(formula); - - System.out.println("公式中的参数:"); - while (matcher.find()) { - System.out.println(" - " + matcher.group()); - } + return parameters; } } diff --git a/src/main/resources/sql.sql b/src/main/resources/sql.sql index 45cbafb..30058e5 100644 --- a/src/main/resources/sql.sql +++ b/src/main/resources/sql.sql @@ -402,6 +402,10 @@ from engine_param_detail; delete from todo_task; delete +from initiated_task; +delete +from completed_task; +delete from data_change_comment; delete from approval_config;