package com.vincent.rsf.server.ai.tool;
|
|
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;
|
import org.springframework.ai.tool.annotation.ToolParam;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.StringUtils;
|
|
import java.util.ArrayList;
|
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 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,}");
|
|
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;空字符串或 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)) {
|
if (fuzzyTaskCode) {
|
queryWrapper.like(Task::getTaskCode, taskCode);
|
} else {
|
queryWrapper.eq(Task::getTaskCode, taskCode);
|
}
|
}
|
if (taskStatus != null) {
|
queryWrapper.eq(Task::getTaskStatus, taskStatus);
|
}
|
if (taskType != null) {
|
queryWrapper.eq(Task::getTaskType, taskType);
|
}
|
if (StringUtils.hasText(orgSite)) {
|
queryWrapper.eq(Task::getOrgSite, orgSite);
|
}
|
if (StringUtils.hasText(targSite)) {
|
queryWrapper.eq(Task::getTargSite, targSite);
|
}
|
}
|
|
private String normalizeTaskCode(String taskCode) {
|
String normalized = BuiltinToolGovernanceSupport.sanitizeQueryText(taskCode, "任务号", 128);
|
if (!StringUtils.hasText(normalized)) {
|
return null;
|
}
|
Matcher taskCodeMatcher = TASK_CODE_PATTERN.matcher(normalized);
|
if (taskCodeMatcher.find()) {
|
return taskCodeMatcher.group().toUpperCase();
|
}
|
Matcher digitMatcher = TASK_CODE_DIGIT_PATTERN.matcher(normalized);
|
if (digitMatcher.find()) {
|
return digitMatcher.group();
|
}
|
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());
|
item.put("taskStatus", task.getTaskStatus());
|
item.put("taskStatusLabel", task.getTaskStatus$());
|
item.put("taskType", task.getTaskType());
|
item.put("taskTypeLabel", task.getTaskType$());
|
item.put("orgSite", task.getOrgSite());
|
item.put("orgSiteLabel", task.getOrgSite$());
|
item.put("targSite", task.getTargSite());
|
item.put("targSiteLabel", task.getTargSite$());
|
item.put("status", task.getStatus());
|
item.put("statusLabel", task.getStatus$());
|
item.put("createTime", task.getCreateTime$());
|
item.put("updateTime", task.getUpdateTime$());
|
return item;
|
}
|
|
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));
|
}
|
|
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;
|
}
|
|
}
|