zhou zhou
12 小时以前 e1a6b6a5507c101d58c1ae6183361df18daef1da
rsf-server/src/main/java/com/vincent/rsf/server/ai/tool/RsfWmsTaskTools.java
@@ -2,7 +2,10 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.common.utils.FieldsUtils;
import com.vincent.rsf.server.manager.entity.Task;
import com.vincent.rsf.server.manager.entity.TaskItem;
import com.vincent.rsf.server.manager.service.TaskItemService;
import com.vincent.rsf.server.manager.service.TaskService;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.tool.annotation.Tool;
@@ -14,25 +17,104 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
@RequiredArgsConstructor
public class RsfWmsTaskTools {
    private final TaskService taskService;
    private static final Pattern TASK_CODE_PATTERN = Pattern.compile("[A-Za-z]{1,8}\\d{6,}");
    private static final Pattern TASK_CODE_DIGIT_PATTERN = Pattern.compile("\\d{8,}");
    @Tool(name = "rsf_query_task_list", description = "按任务号、状态、任务类型、源站点、目标站点等条件查询任务列表。")
    public List<Map<String, Object>> queryTaskList(
            @ToolParam(description = "任务号,可模糊查询") String taskCode,
            @ToolParam(description = "任务状态,可选") Integer taskStatus,
            @ToolParam(description = "任务类型,可选") Integer taskType,
    private final TaskService taskService;
    private final TaskItemService taskItemService;
    /**
     * 统一任务查询。
     * 传精确 taskCode 时返回任务汇总和明细;否则按条件返回任务列表。
     */
    @Tool(name = "rsf_query_task", description = "只读查询工具。支持按任务号、状态、类型、源站点、目标站点查询任务;传精确任务号时返回任务明细,不传过滤条件时返回最近任务。")
    public Map<String, Object> queryTask(
            @ToolParam(description = "任务号,可模糊查询;支持直接传入自然语言,工具会自动提取任务号") String taskCode,
            @ToolParam(description = "任务状态,可选;空字符串或 0 视为未传") String taskStatus,
            @ToolParam(description = "任务类型,可选;空字符串或 0 视为未传") String taskType,
            @ToolParam(description = "源站点,可选") String orgSite,
            @ToolParam(description = "目标站点,可选") String targSite,
            @ToolParam(description = "返回条数,默认 10,最大 50") Integer limit) {
        LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<>();
            @ToolParam(description = "返回条数,默认 10,最大 50;空字符串或 0 视为默认值") String limit) {
        String normalizedTaskCode = normalizeTaskCode(taskCode);
        Integer normalizedTaskStatus = normalizeOptionalFilterNumber(taskStatus, "任务状态");
        Integer normalizedTaskType = normalizeOptionalFilterNumber(taskType, "任务类型");
        String normalizedOrgSite = BuiltinToolGovernanceSupport.sanitizeQueryText(orgSite, "源站点", 64);
        String normalizedTargSite = BuiltinToolGovernanceSupport.sanitizeQueryText(targSite, "目标站点", 64);
        int finalLimit = normalizeLimit(limit, 10, 50);
        Task exactMatchedTask = findExactMatchedTask(normalizedTaskCode, normalizedTaskStatus, normalizedTaskType,
                normalizedOrgSite, normalizedTargSite);
        if (exactMatchedTask != null) {
            Map<String, Object> response = new LinkedHashMap<>();
            response.put("queryMode", "detail");
            response.put("taskCount", 1);
            response.put("limit", finalLimit);
            response.put("resolvedTaskCode", exactMatchedTask.getTaskCode());
            response.put("tasks", List.of(buildTaskDetail(exactMatchedTask)));
            return response;
        }
        LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<>();
        applyCommonFilters(queryWrapper, normalizedTaskCode, normalizedTaskStatus, normalizedTaskType,
                normalizedOrgSite, normalizedTargSite, true);
        queryWrapper.orderByDesc(Task::getCreateTime).last("LIMIT " + finalLimit);
        List<Task> tasks = taskService.list(queryWrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (Task task : tasks) {
            result.add(buildTaskSummary(task));
        }
        Map<String, Object> response = new LinkedHashMap<>();
        response.put("queryMode", "list");
        response.put("taskCount", result.size());
        response.put("limit", finalLimit);
        response.put("resolvedTaskCode", normalizedTaskCode);
        response.put("tasks", result);
        return response;
    }
    private Task findExactMatchedTask(String taskCode, Integer taskStatus, Integer taskType, String orgSite, String targSite) {
        if (!StringUtils.hasText(taskCode)) {
            return null;
        }
        Task exactTask = findUniqueTask(taskCode, taskStatus, taskType, orgSite, targSite, false);
        if (exactTask != null) {
            return exactTask;
        }
        String digitCandidate = extractDigitCandidate(taskCode);
        if (!StringUtils.hasText(digitCandidate) || digitCandidate.equals(taskCode)) {
            return null;
        }
        return findUniqueTask(digitCandidate, taskStatus, taskType, orgSite, targSite, true);
    }
    private Task findUniqueTask(String taskCode, Integer taskStatus, Integer taskType, String orgSite,
                                String targSite, boolean fuzzyTaskCode) {
        LambdaQueryWrapper<Task> queryWrapper = new LambdaQueryWrapper<>();
        applyCommonFilters(queryWrapper, taskCode, taskStatus, taskType, orgSite, targSite, fuzzyTaskCode);
        queryWrapper.orderByDesc(Task::getCreateTime).last("LIMIT 2");
        List<Task> tasks = taskService.list(queryWrapper);
        if (tasks.size() == 1) {
            return tasks.get(0);
        }
        return null;
    }
    private void applyCommonFilters(LambdaQueryWrapper<Task> queryWrapper, String taskCode, Integer taskStatus,
                                    Integer taskType, String orgSite, String targSite, boolean fuzzyTaskCode) {
        if (StringUtils.hasText(taskCode)) {
            queryWrapper.like(Task::getTaskCode, taskCode);
            if (fuzzyTaskCode) {
                queryWrapper.like(Task::getTaskCode, taskCode);
            } else {
                queryWrapper.eq(Task::getTaskCode, taskCode);
            }
        }
        if (taskStatus != null) {
            queryWrapper.eq(Task::getTaskStatus, taskStatus);
@@ -46,53 +128,66 @@
        if (StringUtils.hasText(targSite)) {
            queryWrapper.eq(Task::getTargSite, targSite);
        }
        queryWrapper.orderByDesc(Task::getCreateTime).last("LIMIT " + finalLimit);
        List<Task> tasks = taskService.list(queryWrapper);
        List<Map<String, Object>> result = new ArrayList<>();
        for (Task task : tasks) {
            result.add(buildTaskSummary(task));
        }
        return result;
    }
    @Tool(name = "rsf_query_task_detail", description = "根据任务 ID 或任务号查询任务详情。")
    public Map<String, Object> queryTaskDetail(
            @ToolParam(description = "任务 ID") Long taskId,
            @ToolParam(description = "任务号") String taskCode) {
        if (taskId == null && !StringUtils.hasText(taskCode)) {
            throw new CoolException("任务 ID 和任务号至少需要提供一个");
    private String normalizeTaskCode(String taskCode) {
        String normalized = BuiltinToolGovernanceSupport.sanitizeQueryText(taskCode, "任务号", 128);
        if (!StringUtils.hasText(normalized)) {
            return null;
        }
        Task task;
        if (taskId != null) {
            task = taskService.getById(taskId);
        } else {
            task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, taskCode));
        Matcher taskCodeMatcher = TASK_CODE_PATTERN.matcher(normalized);
        if (taskCodeMatcher.find()) {
            return taskCodeMatcher.group().toUpperCase();
        }
        if (task == null) {
            throw new CoolException("未查询到任务");
        Matcher digitMatcher = TASK_CODE_DIGIT_PATTERN.matcher(normalized);
        if (digitMatcher.find()) {
            return digitMatcher.group();
        }
        Map<String, Object> result = buildTaskSummary(task);
        result.put("resource", task.getResource());
        result.put("exceStatus", task.getExceStatus());
        result.put("orgLoc", task.getOrgLoc());
        result.put("targLoc", task.getTargLoc());
        result.put("orgSite", task.getOrgSite());
        result.put("orgSiteLabel", task.getOrgSite$());
        result.put("targSite", task.getTargSite());
        result.put("targSiteLabel", task.getTargSite$());
        result.put("barcode", task.getBarcode());
        result.put("robotCode", task.getRobotCode());
        result.put("memo", task.getMemo());
        result.put("expCode", task.getExpCode());
        result.put("expDesc", task.getExpDesc());
        result.put("startTime", task.getStartTime$());
        result.put("endTime", task.getEndTime$());
        result.put("createTime", task.getCreateTime$());
        result.put("updateTime", task.getUpdateTime$());
        return result;
        return normalized.replace("*", "").trim();
    }
    private String extractDigitCandidate(String taskCode) {
        if (!StringUtils.hasText(taskCode)) {
            return null;
        }
        Matcher digitMatcher = TASK_CODE_DIGIT_PATTERN.matcher(taskCode);
        if (digitMatcher.find()) {
            return digitMatcher.group();
        }
        return null;
    }
    private Integer normalizeOptionalFilterNumber(String value, String fieldLabel) {
        String normalized = BuiltinToolGovernanceSupport.sanitizeQueryText(value, fieldLabel, 32);
        if (!StringUtils.hasText(normalized)) {
            return null;
        }
        try {
            int parsed = Integer.parseInt(normalized);
            return parsed <= 0 ? null : parsed;
        } catch (NumberFormatException e) {
            throw new CoolException(fieldLabel + "必须是整数");
        }
    }
    private int normalizeLimit(String value, int defaultValue, int maxValue) {
        String normalized = BuiltinToolGovernanceSupport.sanitizeQueryText(value, "limit", 16);
        if (!StringUtils.hasText(normalized)) {
            return defaultValue;
        }
        try {
            int parsed = Integer.parseInt(normalized);
            if (parsed <= 0) {
                return defaultValue;
            }
            return BuiltinToolGovernanceSupport.normalizeLimit(parsed, defaultValue, maxValue);
        } catch (NumberFormatException e) {
            throw new CoolException("limit 必须是整数");
        }
    }
    private Map<String, Object> buildTaskSummary(Task task) {
        /** 把任务实体收敛为适合模型阅读和前端展示的摘要结构。 */
        Map<String, Object> item = new LinkedHashMap<>();
        item.put("id", task.getId());
        item.put("taskCode", task.getTaskCode());
@@ -111,13 +206,73 @@
        return item;
    }
    private int normalizeLimit(Integer limit, int defaultValue, int maxValue) {
        if (limit == null) {
            return defaultValue;
    private Map<String, Object> buildTaskDetail(Task task) {
        Map<String, Object> result = buildTaskSummary(task);
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>()
                .eq(TaskItem::getTaskId, task.getId()));
        result.put("itemCount", taskItems.size());
        double totalAnfme = 0D;
        double totalWorkQty = 0D;
        double totalQty = 0D;
        List<Map<String, Object>> items = new ArrayList<>();
        for (TaskItem taskItem : taskItems) {
            totalAnfme += taskItem.getAnfme() == null ? 0D : taskItem.getAnfme();
            totalWorkQty += taskItem.getWorkQty() == null ? 0D : taskItem.getWorkQty();
            totalQty += taskItem.getQty() == null ? 0D : taskItem.getQty();
            items.add(buildTaskItemRow(taskItem));
        }
        if (limit < 1 || limit > maxValue) {
            throw new CoolException("limit 必须在 1 到 " + maxValue + " 之间");
        }
        return limit;
        result.put("totalAnfme", totalAnfme);
        result.put("totalWorkQty", totalWorkQty);
        result.put("totalQty", totalQty);
        result.put("items", items);
        return result;
    }
    private Map<String, Object> buildTaskItemRow(TaskItem taskItem) {
        if (!Objects.isNull(taskItem.getFieldsIndex())) {
            taskItem.setExtendFields(FieldsUtils.getFields(taskItem.getFieldsIndex()));
        }
        Map<String, Object> item = new LinkedHashMap<>();
        item.put("id", taskItem.getId());
        item.put("taskId", taskItem.getTaskId());
        item.put("matnrId", taskItem.getMatnrId());
        item.put("matnrCode", taskItem.getMatnrCode());
        item.put("maktx", taskItem.getMaktx());
        item.put("trackCode", taskItem.getTrackCode());
        item.put("splrBatch", taskItem.getSplrBatch());
        item.put("batch", taskItem.getBatch());
        item.put("spec", taskItem.getSpec());
        item.put("model", taskItem.getModel());
        item.put("unit", taskItem.getUnit());
        item.put("anfme", taskItem.getAnfme());
        item.put("workQty", taskItem.getWorkQty());
        item.put("qty", taskItem.getQty());
        item.put("ableQty", taskItem.getAbleQty());
        item.put("source", taskItem.getSource());
        item.put("sourceId", taskItem.getSourceId());
        item.put("sourceCode", taskItem.getSourceCode());
        item.put("orderId", taskItem.getOrderId());
        item.put("orderItemId", taskItem.getOrderItemId());
        item.put("platItemId", taskItem.getPlatItemId());
        item.put("platOrderCode", taskItem.getPlatOrderCode());
        item.put("platWorkCode", taskItem.getPlatWorkCode());
        item.put("projectCode", taskItem.getProjectCode());
        item.put("orderType", taskItem.getOrderType());
        item.put("orderTypeLabel", taskItem.getOrderType$());
        item.put("wkType", taskItem.getWkType());
        item.put("wkTypeLabel", taskItem.getWkType$());
        item.put("isptResult", taskItem.getIsptResult());
        item.put("isptResultLabel", taskItem.getIsptResult$());
        item.put("fieldsIndex", taskItem.getFieldsIndex());
        item.put("extendFields", taskItem.getExtendFields());
        item.put("status", taskItem.getStatus());
        item.put("statusLabel", taskItem.getStatus$());
        item.put("memo", taskItem.getMemo());
        item.put("createTime", taskItem.getCreateTime$());
        item.put("updateTime", taskItem.getUpdateTime$());
        return item;
    }
}