| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.tools; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.ruoyi.approve.mapper.ApproveLogMapper; |
| | | import com.ruoyi.approve.mapper.ApproveNodeMapper; |
| | | import com.ruoyi.approve.mapper.ApproveProcessMapper; |
| | | import com.ruoyi.approve.pojo.ApproveLog; |
| | | import com.ruoyi.approve.pojo.ApproveNode; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import dev.langchain4j.agent.tool.P; |
| | | import dev.langchain4j.agent.tool.Tool; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Component |
| | | public class ApproveTodoTools { |
| | | |
| | | private static final int DEFAULT_LIMIT = 10; |
| | | private static final int MAX_LIMIT = 20; |
| | | private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); |
| | | private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | private final ApproveProcessMapper approveProcessMapper; |
| | | private final ApproveNodeMapper approveNodeMapper; |
| | | private final ApproveLogMapper approveLogMapper; |
| | | |
| | | public ApproveTodoTools( |
| | | ApproveProcessMapper approveProcessMapper, |
| | | ApproveNodeMapper approveNodeMapper, |
| | | ApproveLogMapper approveLogMapper) { |
| | | this.approveProcessMapper = approveProcessMapper; |
| | | this.approveNodeMapper = approveNodeMapper; |
| | | this.approveLogMapper = approveLogMapper; |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹å¾
åå表", value = "æ¥è¯¢å®¡æ¹å¾
åï¼æ¯ææç¶æãç±»åãå
³é®åè¿æ»¤ï¼è¿å Markdown è¡¨æ ¼ã") |
| | | public String listTodos( |
| | | @P(value = "审æ¹ç¶æï¼å¯éå¼ï¼allãpendingãprocessingãapprovedãrejectedãresubmitted", required = false) String status, |
| | | @P(value = "审æ¹ç±»åç¼å·ï¼å¯ä¸ä¼ ", required = false) Integer approveType, |
| | | @P(value = "å
³é®åï¼å¯å¹é
æµç¨ç¼å·ãæ é¢ãç³è¯·äººãå½å审æ¹äºº", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§20", required = false) Integer limit) { |
| | | |
| | | LambdaQueryWrapper<ApproveProcess> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(ApproveProcess::getApproveDelete, 0); |
| | | |
| | | Integer statusCode = parseStatus(status); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | if (approveType != null) { |
| | | wrapper.eq(ApproveProcess::getApproveType, approveType); |
| | | } |
| | | if (StringUtils.hasText(keyword)) { |
| | | wrapper.and(w -> w.like(ApproveProcess::getApproveId, keyword) |
| | | .or().like(ApproveProcess::getApproveReason, keyword) |
| | | .or().like(ApproveProcess::getApproveUserName, keyword) |
| | | .or().like(ApproveProcess::getApproveUserCurrentName, keyword)); |
| | | } |
| | | |
| | | wrapper.orderByDesc(ApproveProcess::getCreateTime); |
| | | wrapper.last("limit " + normalizeLimit(limit)); |
| | | |
| | | List<ApproveProcess> processes = approveProcessMapper.selectList(wrapper); |
| | | if (processes == null || processes.isEmpty()) { |
| | | return jsonResponse( |
| | | true, |
| | | "todo_list", |
| | | "æªæ¥è¯¢å°ç¬¦åæ¡ä»¶ç审æ¹å¾
åã", |
| | | Map.of("count", 0), |
| | | Map.of( |
| | | "columns", List.of("approveId", "approveType", "approveUserName", "approveUserCurrentName", "approveReason", "approveStatus", "createTime"), |
| | | "items", List.of() |
| | | ), |
| | | Map.of() |
| | | ); |
| | | } |
| | | |
| | | List<Map<String, Object>> items = processes.stream().map(process -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("approveId", process.getApproveId()); |
| | | item.put("approveType", approveTypeName(process.getApproveType())); |
| | | item.put("approveUserName", process.getApproveUserName()); |
| | | item.put("approveUserCurrentName", process.getApproveUserCurrentName()); |
| | | item.put("approveReason", process.getApproveReason()); |
| | | item.put("approveStatus", approveStatusName(process.getApproveStatus())); |
| | | item.put("createTime", formatDateTime(process.getCreateTime())); |
| | | return item; |
| | | }).collect(Collectors.toList()); |
| | | |
| | | return jsonResponse( |
| | | true, |
| | | "todo_list", |
| | | "å·²è¿å审æ¹å¾
åå表ï¼å¯ç´æ¥æ¸²æè¡¨æ ¼æå¡çã", |
| | | Map.of( |
| | | "count", items.size(), |
| | | "statusFilter", status == null ? "all" : status, |
| | | "approveType", approveType == null ? "" : approveType, |
| | | "keyword", keyword == null ? "" : keyword |
| | | ), |
| | | Map.of( |
| | | "columns", List.of("approveId", "approveType", "approveUserName", "approveUserCurrentName", "approveReason", "approveStatus", "createTime"), |
| | | "items", items |
| | | ), |
| | | Map.of() |
| | | ); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹å¾
å详æ
", value = "æ ¹æ®æµç¨ç¼å·æ¥è¯¢åæ¡å®¡æ¹å¾
å详æ
ï¼è¿åç»æåææ¬ã") |
| | | public String getTodoDetail(@P("æµç¨ç¼å· approveId") String approveId) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return "æªæ¾å°å¯¹åºçå®¡æ¹æµç¨ï¼è¯·ç¡®è®¤æµç¨ç¼å·æ¯å¦æ£ç¡®ã"; |
| | | } |
| | | |
| | | StringJoiner detail = new StringJoiner("\n"); |
| | | detail.add("审æ¹è¯¦æ
"); |
| | | detail.add("æµç¨ç¼å·: " + safe(process.getApproveId())); |
| | | detail.add("审æ¹ç±»å: " + approveTypeName(process.getApproveType())); |
| | | detail.add("ç³è¯·äºº: " + safe(process.getApproveUserName())); |
| | | detail.add("ç³è¯·é¨é¨: " + safe(process.getApproveDeptName())); |
| | | detail.add("å½å审æ¹äºº: " + safe(process.getApproveUserCurrentName())); |
| | | detail.add("æ é¢: " + safe(process.getApproveReason())); |
| | | detail.add("ç¶æ: " + approveStatusName(process.getApproveStatus())); |
| | | detail.add("ç³è¯·æ¥æ: " + formatDate(process.getApproveTime())); |
| | | detail.add("å¼å§æ¥æ: " + formatDate(process.getStartDate())); |
| | | detail.add("ç»ææ¥æ: " + formatDate(process.getEndDate())); |
| | | detail.add("å°ç¹: " + safe(process.getLocation())); |
| | | detail.add("éé¢: " + (process.getPrice() == null ? "" : process.getPrice().toPlainString())); |
| | | detail.add("夿³¨: " + safe(process.getApproveRemark())); |
| | | detail.add("å建æ¶é´: " + formatDateTime(process.getCreateTime())); |
| | | return detail.toString(); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹æµè½¬è®°å½", value = "æ ¹æ®æµç¨ç¼å·æ¥è¯¢å®¡æ¹èç¹åå®¡æ¹æ¥å¿ï¼éååçè¿åº¦ãå¡å¨åªä¸ªèç¹ãè°å¤çè¿ã") |
| | | public String getTodoProgress(@P("æµç¨ç¼å· approveId") String approveId) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return jsonResponse( |
| | | false, |
| | | "todo_progress", |
| | | "æªæ¾å°å¯¹åºçå®¡æ¹æµç¨ï¼è¯·ç¡®è®¤æµç¨ç¼å·æ¯å¦æ£ç¡®ã", |
| | | Map.of("approveId", approveId == null ? "" : approveId), |
| | | Map.of(), |
| | | Map.of() |
| | | ); |
| | | } |
| | | |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | List<ApproveLog> logs = listLogs(process.getId()); |
| | | List<Map<String, Object>> nodeItems = nodes.stream().map(node -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("approveNodeOrder", node.getApproveNodeOrder()); |
| | | item.put("approveNodeUser", node.getApproveNodeUser()); |
| | | item.put("approveNodeStatus", approveNodeStatusName(node.getApproveNodeStatus())); |
| | | item.put("approveNodeTime", formatDate(node.getApproveNodeTime())); |
| | | item.put("approveNodeReason", node.getApproveNodeReason()); |
| | | item.put("approveNodeRemark", node.getApproveNodeRemark()); |
| | | return item; |
| | | }).collect(Collectors.toList()); |
| | | List<Map<String, Object>> logItems = logs.stream().map(log -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("approveNodeOrder", log.getApproveNodeOrder()); |
| | | item.put("approveUser", log.getApproveUser()); |
| | | item.put("approveStatus", approveStatusName(log.getApproveStatus())); |
| | | item.put("approveTime", formatDate(log.getApproveTime())); |
| | | item.put("approveRemark", log.getApproveRemark()); |
| | | return item; |
| | | }).collect(Collectors.toList()); |
| | | return jsonResponse( |
| | | true, |
| | | "todo_progress", |
| | | "å·²è¿åå®¡æ¹æµè½¬è®°å½ï¼å¯æ¸²ææ¶é´çº¿ãæ¥éª¤æ¡åæ¥å¿è¡¨æ ¼ã", |
| | | Map.of( |
| | | "approveId", process.getApproveId(), |
| | | "currentStatus", approveStatusName(process.getApproveStatus()), |
| | | "currentApprover", safe(process.getApproveUserCurrentName()), |
| | | "nodeCount", nodeItems.size(), |
| | | "logCount", logItems.size() |
| | | ), |
| | | Map.of( |
| | | "nodes", nodeItems, |
| | | "logs", logItems |
| | | ), |
| | | Map.of() |
| | | ); |
| | | } |
| | | |
| | | @Tool(name = "ç»è®¡å®¡æ¹å¾
åæ°æ®", value = "è¿åä¸ä¸åç»è®¡ç»æï¼å
å«æè¦ææ åå¯ç´æ¥ç¨äº ECharts çå¾è¡¨ optionã") |
| | | public String getTodoStats() { |
| | | List<ApproveProcess> processes = approveProcessMapper.selectList(new LambdaQueryWrapper<ApproveProcess>() |
| | | .eq(ApproveProcess::getApproveDelete, 0)); |
| | | if (processes == null || processes.isEmpty()) { |
| | | return jsonResponse( |
| | | true, |
| | | "todo_stats", |
| | | "å½å没æå®¡æ¹å¾
åæ°æ®ã", |
| | | Map.of("total", 0), |
| | | Map.of( |
| | | "statusDistribution", Map.of(), |
| | | "typeDistribution", Map.of(), |
| | | "recent7DayTrend", List.of(), |
| | | "tips", List.of() |
| | | ), |
| | | Map.of() |
| | | ); |
| | | } |
| | | |
| | | Map<String, Long> statusStats = processes.stream() |
| | | .collect(Collectors.groupingBy(p -> approveStatusName(p.getApproveStatus()), LinkedHashMap::new, Collectors.counting())); |
| | | Map<String, Long> typeStats = processes.stream() |
| | | .collect(Collectors.groupingBy(p -> approveTypeName(p.getApproveType()), LinkedHashMap::new, Collectors.counting())); |
| | | |
| | | long pendingCount = countByStatus(processes, 0); |
| | | long processingCount = countByStatus(processes, 1); |
| | | long approvedCount = countByStatus(processes, 2); |
| | | long rejectedCount = countByStatus(processes, 3); |
| | | long resubmittedCount = countByStatus(processes, 4); |
| | | |
| | | List<String> recentDates = buildRecentDates(7); |
| | | List<Long> trendValues = recentDates.stream() |
| | | .map(date -> processes.stream() |
| | | .filter(process -> process.getCreateTime() != null) |
| | | .filter(process -> process.getCreateTime().toLocalDate().equals(LocalDate.parse(date))) |
| | | .count()) |
| | | .collect(Collectors.toList()); |
| | | |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("total", processes.size()); |
| | | summary.put("pending", pendingCount); |
| | | summary.put("processing", processingCount); |
| | | summary.put("approved", approvedCount); |
| | | summary.put("rejected", rejectedCount); |
| | | summary.put("resubmitted", resubmittedCount); |
| | | summary.put("approvalCompletionRate", calculateRate(approvedCount, processes.size())); |
| | | summary.put("rejectionRate", calculateRate(rejectedCount, processes.size())); |
| | | |
| | | Map<String, Object> charts = new LinkedHashMap<>(); |
| | | charts.put("statusBarOption", buildStatusBarOption(statusStats)); |
| | | charts.put("typePieOption", buildTypePieOption(typeStats)); |
| | | charts.put("recentTrendLineOption", buildRecentTrendLineOption(recentDates, trendValues)); |
| | | |
| | | return jsonResponse( |
| | | true, |
| | | "todo_stats", |
| | | "å·²è¿å审æ¹ç»è®¡æ¦è§ä¸å¾è¡¨é
ç½®ï¼å端å¯ç´æ¥ä½¿ç¨ charts ä¸ç ECharts option 渲æã", |
| | | summary, |
| | | Map.of( |
| | | "statusDistribution", statusStats, |
| | | "typeDistribution", typeStats, |
| | | "recent7DayTrend", toTrendItems(recentDates, trendValues), |
| | | "tips", List.of( |
| | | "statusBarOption éåå±ç¤ºå审æ¹ç¶ææ°é对æ¯", |
| | | "typePieOption éåå±ç¤ºå审æ¹ç±»åå æ¯", |
| | | "recentTrendLineOption éåå±ç¤ºæè¿7天æ°å¢å®¡æ¹è¶å¿" |
| | | ) |
| | | ), |
| | | charts |
| | | ); |
| | | } |
| | | |
| | | @Transactional |
| | | @Tool(name = "审æ¹å¾
å", value = "æ§è¡å®¡æ¹å¨ä½ãaction åªæ¯æ approve æ rejectãapprove 表示éè¿ï¼reject 表示驳åã") |
| | | public String reviewTodo( |
| | | @P("æµç¨ç¼å· approveId") String approveId, |
| | | @P("å¨ä½ï¼approve=éè¿ï¼reject=驳å") String action, |
| | | @P(value = "审æ¹å¤æ³¨ï¼å¯ä¸ä¼ ", required = false) String remark) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "review_action", "æªæ¾å°å¯¹åºå®¡æ¹æµç¨ã", approveId, null); |
| | | } |
| | | if (process.getApproveDelete() != null && process.getApproveDelete() == 1) { |
| | | return actionResult(false, "review_action", "è¯¥å®¡æ¹æµç¨å·²å é¤ï¼ä¸è½åå®¡æ ¸ã", approveId, null); |
| | | } |
| | | if (process.getApproveStatus() != null && (process.getApproveStatus() == 2 || process.getApproveStatus() == 3)) { |
| | | return actionResult(false, "review_action", "è¯¥å®¡æ¹æµç¨å·²ç»æï¼ä¸è½éå¤å®¡æ ¸ã", approveId, null); |
| | | } |
| | | |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | ApproveNode currentNode = findCurrentNode(nodes); |
| | | if (currentNode == null) { |
| | | return actionResult(false, "review_action", "æªæ¾å°å¯å®¡æ ¸çå½åèç¹ã", approveId, null); |
| | | } |
| | | |
| | | String normalizedAction = action == null ? "" : action.trim().toLowerCase(); |
| | | Date now = new Date(); |
| | | currentNode.setApproveNodeTime(now); |
| | | currentNode.setUpdateTime(LocalDateTime.now()); |
| | | |
| | | ApproveLog log = new ApproveLog(); |
| | | log.setApproveId(process.getId()); |
| | | log.setApproveNodeOrder(currentNode.getApproveNodeOrder()); |
| | | log.setApproveUser(currentNode.getApproveNodeUserId()); |
| | | log.setApproveTime(now); |
| | | log.setApproveRemark(remark); |
| | | |
| | | if ("approve".equals(normalizedAction)) { |
| | | currentNode.setApproveNodeStatus(1); |
| | | currentNode.setApproveNodeReason(null); |
| | | approveNodeMapper.updateById(currentNode); |
| | | |
| | | ApproveNode nextNode = findNextNode(nodes, currentNode.getApproveNodeOrder()); |
| | | if (nextNode == null) { |
| | | process.setApproveStatus(2); |
| | | process.setApproveOverTime(now); |
| | | process.setApproveUserCurrentId(null); |
| | | process.setApproveUserCurrentName(null); |
| | | } else { |
| | | process.setApproveStatus(1); |
| | | process.setApproveUserCurrentId(nextNode.getApproveNodeUserId()); |
| | | process.setApproveUserCurrentName(nextNode.getApproveNodeUser()); |
| | | } |
| | | process.setApproveRemark(remark); |
| | | approveProcessMapper.updateById(process); |
| | | |
| | | log.setApproveStatus(nextNode == null ? 2 : 1); |
| | | approveLogMapper.insert(log); |
| | | return actionResult(true, "review_action", |
| | | nextNode == null ? "审æ¹å·²éè¿ï¼ä¸è¯¥æµç¨å·²å
¨é¨å®æã" : "审æ¹å·²éè¿ï¼æµç¨å·²æµè½¬å°ä¸ä¸å®¡æ¹èç¹ã", |
| | | approveId, |
| | | Map.of( |
| | | "action", "approve", |
| | | "currentStatus", approveStatusName(process.getApproveStatus()), |
| | | "nextApprover", nextNode == null ? "" : safe(nextNode.getApproveNodeUser()), |
| | | "remark", safe(remark) |
| | | )); |
| | | } |
| | | |
| | | if ("reject".equals(normalizedAction)) { |
| | | currentNode.setApproveNodeStatus(2); |
| | | currentNode.setApproveNodeReason(remark); |
| | | currentNode.setApproveNodeRemark(remark); |
| | | approveNodeMapper.updateById(currentNode); |
| | | |
| | | process.setApproveStatus(3); |
| | | process.setApproveOverTime(now); |
| | | process.setApproveUserCurrentId(null); |
| | | process.setApproveUserCurrentName(null); |
| | | process.setApproveRemark(remark); |
| | | approveProcessMapper.updateById(process); |
| | | |
| | | log.setApproveStatus(3); |
| | | approveLogMapper.insert(log); |
| | | return actionResult(true, "review_action", "审æ¹å·²é©³åã", approveId, Map.of( |
| | | "action", "reject", |
| | | "currentStatus", approveStatusName(process.getApproveStatus()), |
| | | "remark", safe(remark) |
| | | )); |
| | | } |
| | | |
| | | return actionResult(false, "review_action", "action åªæ¯æ approve æ rejectã", approveId, null); |
| | | } |
| | | |
| | | @Transactional |
| | | @Tool(name = "忶审æ¹å¾
åå®¡æ ¸", value = "æ¤éæè¿ä¸æ¬¡å®¡æ ¸ç»æï¼å°æè¿å®¡æ ¸èç¹æ¢å¤ä¸ºæªå®¡æ ¸ï¼å¹¶åæ»æµç¨ç¶æã") |
| | | public String cancelReviewTodo( |
| | | @P("æµç¨ç¼å· approveId") String approveId, |
| | | @P(value = "åæ¶åå ï¼å¯ä¸ä¼ ", required = false) String reason) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "cancel_review_action", "æªæ¾å°å¯¹åºå®¡æ¹æµç¨ã", approveId, null); |
| | | } |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | ApproveNode lastReviewedNode = nodes.stream() |
| | | .filter(node -> node.getApproveNodeStatus() != null && node.getApproveNodeStatus() != 0) |
| | | .max(Comparator.comparing(ApproveNode::getApproveNodeOrder)) |
| | | .orElse(null); |
| | | if (lastReviewedNode == null) { |
| | | return actionResult(false, "cancel_review_action", "å½åæµç¨æ²¡æå¯åæ¶çå®¡æ ¸è®°å½ã", approveId, null); |
| | | } |
| | | |
| | | lastReviewedNode.setApproveNodeStatus(0); |
| | | lastReviewedNode.setApproveNodeTime(null); |
| | | lastReviewedNode.setApproveNodeReason(null); |
| | | lastReviewedNode.setApproveNodeRemark(reason); |
| | | lastReviewedNode.setUpdateTime(LocalDateTime.now()); |
| | | approveNodeMapper.updateById(lastReviewedNode); |
| | | |
| | | List<ApproveLog> logs = listLogs(process.getId()); |
| | | ApproveLog latestLog = logs.stream() |
| | | .max(Comparator.comparing(ApproveLog::getApproveNodeOrder) |
| | | .thenComparing(ApproveLog::getApproveTime, Comparator.nullsLast(Date::compareTo))) |
| | | .orElse(null); |
| | | if (latestLog != null) { |
| | | approveLogMapper.deleteById(latestLog.getId()); |
| | | } |
| | | |
| | | ApproveNode previousReviewedNode = nodes.stream() |
| | | .filter(node -> !node.getId().equals(lastReviewedNode.getId())) |
| | | .filter(node -> node.getApproveNodeStatus() != null && node.getApproveNodeStatus() == 1) |
| | | .max(Comparator.comparing(ApproveNode::getApproveNodeOrder)) |
| | | .orElse(null); |
| | | |
| | | process.setApproveOverTime(null); |
| | | process.setApproveRemark(reason); |
| | | process.setApproveStatus(previousReviewedNode == null ? 0 : 1); |
| | | process.setApproveUserCurrentId(lastReviewedNode.getApproveNodeUserId()); |
| | | process.setApproveUserCurrentName(lastReviewedNode.getApproveNodeUser()); |
| | | approveProcessMapper.updateById(process); |
| | | |
| | | return actionResult(true, "cancel_review_action", "æè¿ä¸æ¬¡å®¡æ ¸å·²åæ¶ï¼æµç¨å·²åéå°å¯¹åºå®¡æ¹èç¹ã", approveId, Map.of( |
| | | "rollbackNodeOrder", lastReviewedNode.getApproveNodeOrder(), |
| | | "currentStatus", approveStatusName(process.getApproveStatus()), |
| | | "currentApprover", safe(process.getApproveUserCurrentName()), |
| | | "reason", safe(reason) |
| | | )); |
| | | } |
| | | |
| | | @Transactional |
| | | @Tool(name = "ä¿®æ¹å®¡æ¹å¾
å", value = "ä¿®æ¹å®¡æ¹ååºç¡ä¿¡æ¯ãæ¯æä¿®æ¹æ é¢ãå¼å§æ¥æãç»ææ¥æãéé¢ãå°ç¹ãç±»åå夿³¨ãæ¥ææ ¼å¼å¿
é¡»æ¯ yyyy-MM-ddã") |
| | | public String updateTodo( |
| | | @P("æµç¨ç¼å· approveId") String approveId, |
| | | @P(value = "æ°çæ é¢ï¼å¯ä¸ä¼ ", required = false) String approveReason, |
| | | @P(value = "æ°çå¼å§æ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String startDate, |
| | | @P(value = "æ°çç»ææ¥æ yyyy-MM-ddï¼å¯ä¸ä¼ ", required = false) String endDate, |
| | | @P(value = "æ°çéé¢ï¼å¯ä¸ä¼ ", required = false) BigDecimal price, |
| | | @P(value = "æ°çå°ç¹ï¼å¯ä¸ä¼ ", required = false) String location, |
| | | @P(value = "æ°ç审æ¹ç±»åï¼å¯ä¸ä¼ ", required = false) Integer approveType, |
| | | @P(value = "æ°ç夿³¨ï¼å¯ä¸ä¼ ", required = false) String approveRemark) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "update_action", "æªæ¾å°å¯¹åºå®¡æ¹æµç¨ã", approveId, null); |
| | | } |
| | | if (!StringUtils.hasText(approveReason) |
| | | && !StringUtils.hasText(startDate) |
| | | && !StringUtils.hasText(endDate) |
| | | && price == null |
| | | && !StringUtils.hasText(location) |
| | | && approveType == null |
| | | && !StringUtils.hasText(approveRemark)) { |
| | | return actionResult(false, "update_action", "æ²¡ææ£æµå°å¯æ´æ°çåæ®µã", approveId, null); |
| | | } |
| | | |
| | | if (StringUtils.hasText(approveReason)) { |
| | | process.setApproveReason(approveReason); |
| | | } |
| | | if (StringUtils.hasText(startDate)) { |
| | | process.setStartDate(parseDate(startDate)); |
| | | } |
| | | if (StringUtils.hasText(endDate)) { |
| | | process.setEndDate(parseDate(endDate)); |
| | | } |
| | | if (price != null) { |
| | | process.setPrice(price); |
| | | } |
| | | if (StringUtils.hasText(location)) { |
| | | process.setLocation(location); |
| | | } |
| | | if (approveType != null) { |
| | | process.setApproveType(approveType); |
| | | } |
| | | if (StringUtils.hasText(approveRemark)) { |
| | | process.setApproveRemark(approveRemark); |
| | | } |
| | | |
| | | approveProcessMapper.updateById(process); |
| | | return actionResult(true, "update_action", "审æ¹åå·²æ´æ°ã", approveId, Map.of( |
| | | "approveReason", safe(process.getApproveReason()), |
| | | "startDate", formatDate(process.getStartDate()), |
| | | "endDate", formatDate(process.getEndDate()), |
| | | "price", process.getPrice() == null ? "" : process.getPrice(), |
| | | "location", safe(process.getLocation()), |
| | | "approveType", approveTypeName(process.getApproveType()), |
| | | "approveRemark", safe(process.getApproveRemark()) |
| | | )); |
| | | } |
| | | |
| | | @Transactional |
| | | @Tool(name = "å é¤å®¡æ¹å¾
å", value = "å é¤å®¡æ¹æµç¨ãé»è¾å 餿µç¨è®°å½ï¼å¹¶åæ¥é»è¾å é¤å®¡æ¹èç¹ã") |
| | | public String deleteTodo(@P("æµç¨ç¼å· approveId") String approveId) { |
| | | ApproveProcess process = getProcessByApproveId(approveId); |
| | | if (process == null) { |
| | | return actionResult(false, "delete_action", "æªæ¾å°å¯¹åºå®¡æ¹æµç¨ã", approveId, null); |
| | | } |
| | | if (process.getApproveDelete() != null && process.getApproveDelete() == 1) { |
| | | return actionResult(false, "delete_action", "è¯¥å®¡æ¹æµç¨å·²ç»æ¯å é¤ç¶æã", approveId, null); |
| | | } |
| | | |
| | | process.setApproveDelete(1); |
| | | approveProcessMapper.updateById(process); |
| | | |
| | | List<ApproveNode> nodes = listNodes(process); |
| | | for (ApproveNode node : nodes) { |
| | | node.setDeleteFlag(1); |
| | | node.setUpdateTime(LocalDateTime.now()); |
| | | approveNodeMapper.updateById(node); |
| | | } |
| | | return actionResult(true, "delete_action", "å®¡æ¹æµç¨å·²å é¤ã", approveId, Map.of( |
| | | "deletedNodeCount", nodes.size(), |
| | | "approveStatus", approveStatusName(process.getApproveStatus()) |
| | | )); |
| | | } |
| | | |
| | | private ApproveProcess getProcessByApproveId(String approveId) { |
| | | if (!StringUtils.hasText(approveId)) { |
| | | return null; |
| | | } |
| | | return approveProcessMapper.selectOne(new LambdaQueryWrapper<ApproveProcess>() |
| | | .eq(ApproveProcess::getApproveId, approveId) |
| | | .last("limit 1")); |
| | | } |
| | | |
| | | private List<ApproveNode> listNodes(ApproveProcess process) { |
| | | List<ApproveNode> nodes = approveNodeMapper.selectList(new LambdaQueryWrapper<ApproveNode>() |
| | | .eq(ApproveNode::getDeleteFlag, 0) |
| | | .eq(ApproveNode::getApproveProcessId, process.getApproveId()) |
| | | .orderByAsc(ApproveNode::getApproveNodeOrder)); |
| | | if (nodes != null && !nodes.isEmpty()) { |
| | | return nodes; |
| | | } |
| | | List<ApproveNode> fallbackNodes = approveNodeMapper.selectList(new LambdaQueryWrapper<ApproveNode>() |
| | | .eq(ApproveNode::getDeleteFlag, 0) |
| | | .eq(ApproveNode::getApproveProcessId, String.valueOf(process.getId())) |
| | | .orderByAsc(ApproveNode::getApproveNodeOrder)); |
| | | return fallbackNodes == null ? List.of() : fallbackNodes; |
| | | } |
| | | |
| | | private List<ApproveLog> listLogs(Long processId) { |
| | | List<ApproveLog> logs = approveLogMapper.selectList(new LambdaQueryWrapper<ApproveLog>() |
| | | .eq(ApproveLog::getApproveId, processId) |
| | | .orderByAsc(ApproveLog::getApproveNodeOrder, ApproveLog::getApproveTime)); |
| | | return logs == null ? List.of() : logs; |
| | | } |
| | | |
| | | private ApproveNode findCurrentNode(List<ApproveNode> nodes) { |
| | | return nodes.stream() |
| | | .filter(node -> node.getApproveNodeStatus() != null && node.getApproveNodeStatus() == 0) |
| | | .min(Comparator.comparing(ApproveNode::getApproveNodeOrder)) |
| | | .orElse(null); |
| | | } |
| | | |
| | | private ApproveNode findNextNode(List<ApproveNode> nodes, Integer currentOrder) { |
| | | return nodes.stream() |
| | | .filter(node -> node.getApproveNodeOrder() != null && currentOrder != null) |
| | | .filter(node -> node.getApproveNodeOrder() > currentOrder) |
| | | .min(Comparator.comparing(ApproveNode::getApproveNodeOrder)) |
| | | .orElse(null); |
| | | } |
| | | |
| | | private int normalizeLimit(Integer limit) { |
| | | if (limit == null || limit <= 0) { |
| | | return DEFAULT_LIMIT; |
| | | } |
| | | return Math.min(limit, MAX_LIMIT); |
| | | } |
| | | |
| | | private Integer parseStatus(String status) { |
| | | if (!StringUtils.hasText(status) || "all".equalsIgnoreCase(status)) { |
| | | return null; |
| | | } |
| | | return switch (status.trim().toLowerCase()) { |
| | | case "pending" -> 0; |
| | | case "processing" -> 1; |
| | | case "approved" -> 2; |
| | | case "rejected" -> 3; |
| | | case "resubmitted" -> 4; |
| | | default -> null; |
| | | }; |
| | | } |
| | | |
| | | private String approveStatusName(Integer status) { |
| | | if (status == null) { |
| | | return "æªç¥"; |
| | | } |
| | | return switch (status) { |
| | | case 0 -> "å¾
å®¡æ ¸"; |
| | | case 1 -> "å®¡æ ¸ä¸"; |
| | | case 2 -> "å®¡æ ¸å®æ"; |
| | | case 3 -> "å®¡æ ¸æªéè¿"; |
| | | case 4 -> "已鿰æäº¤"; |
| | | default -> "æªç¥"; |
| | | }; |
| | | } |
| | | |
| | | private String approveNodeStatusName(Integer status) { |
| | | if (status == null) { |
| | | return "æªç¥"; |
| | | } |
| | | return switch (status) { |
| | | case 0 -> "æªå®¡æ ¸"; |
| | | case 1 -> "åæ"; |
| | | case 2 -> "æç»"; |
| | | default -> "æªç¥"; |
| | | }; |
| | | } |
| | | |
| | | private String approveTypeName(Integer type) { |
| | | if (type == null) { |
| | | return "æªç¥"; |
| | | } |
| | | return switch (type) { |
| | | case 1 -> "å
¬åºç®¡ç"; |
| | | case 2 -> "请å管ç"; |
| | | case 3 -> "åºå·®ç®¡ç"; |
| | | case 4 -> "æ¥é管ç"; |
| | | case 5 -> "éè´å®¡æ¹"; |
| | | case 6 -> "æ¥ä»·å®¡æ¹"; |
| | | case 7 -> "å货审æ¹"; |
| | | case 8 -> "å±é©ä½ä¸å®¡æ¹"; |
| | | default -> "ç±»å" + type; |
| | | }; |
| | | } |
| | | |
| | | private String safe(Object value) { |
| | | return value == null ? "" : String.valueOf(value).replace('\n', ' ').replace('\r', ' '); |
| | | } |
| | | |
| | | private String formatDateTime(Object value) { |
| | | if (value == null) { |
| | | return ""; |
| | | } |
| | | if (value instanceof LocalDateTime localDateTime) { |
| | | return localDateTime.format(DATE_TIME_FORMATTER); |
| | | } |
| | | return safe(value); |
| | | } |
| | | |
| | | private String formatDate(Date value) { |
| | | return value == null ? "" : DATE_FORMAT.format(value); |
| | | } |
| | | |
| | | private long countByStatus(List<ApproveProcess> processes, int status) { |
| | | return processes.stream() |
| | | .filter(process -> process.getApproveStatus() != null) |
| | | .filter(process -> process.getApproveStatus() == status) |
| | | .count(); |
| | | } |
| | | |
| | | private String calculateRate(long part, int total) { |
| | | if (total <= 0) { |
| | | return "0.00%"; |
| | | } |
| | | double rate = part * 100.0 / total; |
| | | return String.format("%.2f%%", rate); |
| | | } |
| | | |
| | | private List<String> buildRecentDates(int days) { |
| | | List<String> dates = new ArrayList<>(); |
| | | LocalDate today = LocalDate.now(); |
| | | for (int i = days - 1; i >= 0; i--) { |
| | | dates.add(today.minusDays(i).toString()); |
| | | } |
| | | return dates; |
| | | } |
| | | |
| | | private List<Map<String, Object>> toTrendItems(List<String> dates, List<Long> values) { |
| | | List<Map<String, Object>> items = new ArrayList<>(); |
| | | for (int i = 0; i < dates.size(); i++) { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("date", dates.get(i)); |
| | | item.put("count", values.get(i)); |
| | | items.add(item); |
| | | } |
| | | return items; |
| | | } |
| | | |
| | | private Map<String, Object> buildStatusBarOption(Map<String, Long> statusStats) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "审æ¹ç¶æåå¸", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", new ArrayList<>(statusStats.keySet()))); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of( |
| | | "name", "æ°é", |
| | | "type", "bar", |
| | | "data", new ArrayList<>(statusStats.values()), |
| | | "barWidth", "40%" |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildTypePieOption(Map<String, Long> typeStats) { |
| | | List<Map<String, Object>> data = typeStats.entrySet().stream() |
| | | .map(entry -> { |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("name", entry.getKey()); |
| | | item.put("value", entry.getValue()); |
| | | return item; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "审æ¹ç±»åå æ¯", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "item")); |
| | | option.put("legend", Map.of("orient", "vertical", "left", "left")); |
| | | option.put("series", List.of(Map.of( |
| | | "name", "审æ¹ç±»å", |
| | | "type", "pie", |
| | | "radius", List.of("35%", "65%"), |
| | | "data", data |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Map<String, Object> buildRecentTrendLineOption(List<String> dates, List<Long> values) { |
| | | Map<String, Object> option = new LinkedHashMap<>(); |
| | | option.put("title", Map.of("text", "æè¿7å¤©å®¡æ¹æ°å¢è¶å¿", "left", "center")); |
| | | option.put("tooltip", Map.of("trigger", "axis")); |
| | | option.put("xAxis", Map.of("type", "category", "data", dates)); |
| | | option.put("yAxis", Map.of("type", "value")); |
| | | option.put("series", List.of(Map.of( |
| | | "name", "æ°å¢å®¡æ¹", |
| | | "type", "line", |
| | | "smooth", true, |
| | | "data", values, |
| | | "areaStyle", Map.of() |
| | | ))); |
| | | return option; |
| | | } |
| | | |
| | | private Date parseDate(String dateText) { |
| | | try { |
| | | return DATE_FORMAT.parse(dateText); |
| | | } catch (ParseException e) { |
| | | throw new IllegalArgumentException("æ¥ææ ¼å¼å¿
é¡»æ¯ yyyy-MM-dd"); |
| | | } |
| | | } |
| | | |
| | | private String actionResult(boolean success, String type, String description, String approveId, Map<String, Object> data) { |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("approveId", approveId == null ? "" : approveId); |
| | | return jsonResponse(success, type, description, summary, data == null ? Map.of() : data, Map.of()); |
| | | } |
| | | |
| | | private String jsonResponse( |
| | | boolean success, |
| | | String type, |
| | | String description, |
| | | Map<String, Object> summary, |
| | | Map<String, Object> data, |
| | | Map<String, Object> charts) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("success", success); |
| | | result.put("type", type); |
| | | result.put("description", description); |
| | | result.put("summary", summary == null ? Map.of() : summary); |
| | | result.put("data", data == null ? Map.of() : data); |
| | | result.put("charts", charts == null ? Map.of() : charts); |
| | | return JSON.toJSONString(result); |
| | | } |
| | | } |