| | |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.YearMonth; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.util.ArrayList; |
| | |
| | | |
| | | 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_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
| | | private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | private final ApproveProcessMapper approveProcessMapper; |
| | |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | } |
| | | |
| | | @Tool(name = "查询审批待办列表", value = "查询当前登录人相关的审批待办,优先返回自己待处理的审批,支持按状态、类型、关键字过滤。") |
| | | @Tool(name = "查询审批待办列表", value = "查询当前登录人相关的审批待办,优先返回自己待处理的审批,支持按状态、类型、关键字和范围过滤。") |
| | | public String listTodos(@ToolMemoryId String memoryId, |
| | | @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) { |
| | | @P(value = "返回条数,默认10,最大20", required = false) Integer limit, |
| | | @P(value = "查询范围,可选值:related、applicant、approver;related 表示当前用户相关,applicant 表示我发起的,approver 表示待我处理的", required = false) String scope, |
| | | @P(value = "开始日期 yyyy-MM-dd,可不传", required = false) String startDate, |
| | | @P(value = "结束日期 yyyy-MM-dd,可不传", required = false) String endDate, |
| | | @P(value = "时间范围描述,例如 今天、本月、近30天,可不传", required = false) String timeRange) { |
| | | |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | Long userId = loginUser.getUserId(); |
| | | Integer statusCode = parseStatus(status); |
| | | String normalizedScope = normalizeScope(scope); |
| | | boolean hasDateFilter = StringUtils.hasText(startDate) || StringUtils.hasText(endDate) || StringUtils.hasText(timeRange); |
| | | DateRange dateRange = hasDateFilter ? resolveDateRange(startDate, endDate, timeRange) : null; |
| | | |
| | | LambdaQueryWrapper<ApproveProcess> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(ApproveProcess::getApproveDelete, 0) |
| | | .ne(ApproveProcess::getApproveStatus, 2); |
| | | wrapper.eq(ApproveProcess::getApproveDelete, 0); |
| | | if (statusCode == null) { |
| | | wrapper.ne(ApproveProcess::getApproveStatus, 2); |
| | | } |
| | | |
| | | 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)); |
| | | // } |
| | | if (statusCode != null && (statusCode == 0 || statusCode == 1)) { |
| | | 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)); |
| | | } |
| | | if ("applicant".equals(normalizedScope)) { |
| | | wrapper.eq(ApproveProcess::getApproveUser, userId); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } else if ("approver".equals(normalizedScope)) { |
| | | wrapper.eq(ApproveProcess::getApproveUserCurrentId, userId); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } else if (statusCode != null && (statusCode == 0 || statusCode == 1)) { |
| | | wrapper.eq(ApproveProcess::getApproveUserCurrentId, userId); |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } else { |
| | | wrapper.and(w -> w.eq(ApproveProcess::getApproveUser, userId) |
| | | .or().eq(ApproveProcess::getApproveUserCurrentId, userId) |
| | | .or().apply("FIND_IN_SET({0}, approve_user_ids)", userId)); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } |
| | | |
| | | if (dateRange != null) { |
| | | wrapper.ge(ApproveProcess::getCreateTime, dateRange.start().atStartOfDay()) |
| | | .lt(ApproveProcess::getCreateTime, dateRange.end().plusDays(1).atStartOfDay()); |
| | | } |
| | | |
| | | wrapper.orderByDesc(ApproveProcess::getCreateTime) |
| | |
| | | "count", items.size(), |
| | | "statusFilter", StringUtils.hasText(status) ? status : "all", |
| | | "approveType", approveType == null ? "" : approveType, |
| | | "keyword", keyword == null ? "" : keyword |
| | | "keyword", keyword == null ? "" : keyword, |
| | | "scope", normalizedScope, |
| | | "timeRange", dateRange == null ? "all" : dateRange.label(), |
| | | "startDate", dateRange == null ? "" : dateRange.start().toString(), |
| | | "endDate", dateRange == null ? "" : dateRange.end().toString() |
| | | ), |
| | | Map.of("columns", todoColumns(), "items", items), |
| | | Map.of()); |
| | |
| | | }; |
| | | } |
| | | |
| | | private String normalizeScope(String scope) { |
| | | if (!StringUtils.hasText(scope)) { |
| | | return "related"; |
| | | } |
| | | return switch (scope.trim().toLowerCase()) { |
| | | case "applicant", "mine", "created", "initiated" -> "applicant"; |
| | | case "approver", "handler", "todo", "pending" -> "approver"; |
| | | default -> "related"; |
| | | }; |
| | | } |
| | | |
| | | private String approveStatusName(Integer status) { |
| | | if (status == null) { |
| | | return "未知"; |
| | |
| | | case 6 -> "报价审批"; |
| | | case 7 -> "发货审批"; |
| | | case 8 -> "危险作业审批"; |
| | | case 9 -> "办公用品审批"; |
| | | default -> "类型" + type; |
| | | }; |
| | | } |
| | |
| | | } |
| | | |
| | | private String formatDate(Date value) { |
| | | return value == null ? "" : DATE_FORMAT.format(value); |
| | | if (value == null) { |
| | | return ""; |
| | | } |
| | | return value.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DATE_FORMATTER); |
| | | } |
| | | |
| | | private long countByStatus(List<ApproveProcess> processes, int status) { |
| | |
| | | |
| | | private Date parseDate(String dateText) { |
| | | try { |
| | | return DATE_FORMAT.parse(dateText); |
| | | } catch (ParseException e) { |
| | | LocalDate localDate = LocalDate.parse(dateText, DATE_FORMATTER); |
| | | return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | } catch (Exception e) { |
| | | throw new IllegalArgumentException("日期格式必须是 yyyy-MM-dd"); |
| | | } |
| | | } |