package com.xly.tool;
import com.fasterxml.jackson.databind.JsonNode;
import com.xly.service.ErpClient;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Read 工具(Phase A):读取指定 ERP 表单的真实业务数据。
*
*
走 ERP 后端通用表单读接口({@link ErpClient})——即架构里"Read = 薄 HTTP 客户端、
* 业务逻辑在 ERP 后端、得到租户+行级范围"。列名用字段字典({@code viw_kg_field_dict})
* 渲染成中文,只展示前几列/前几行,避免吐爆上下文。
*
*
安全:只传分页参数、不传任何写参数(如 bUpdate),读不会变写。
*/
@Component
public class ErpReadTool {
private static final int MAX_ROWS = 10;
private static final int MAX_COLS = 6;
private final ErpClient erp;
private final JdbcTemplate jdbc;
public ErpReadTool(ErpClient erp, JdbcTemplate jdbc) {
this.erp = erp;
this.jdbc = jdbc;
}
@Tool("读取指定 ERP 表单的真实业务数据(返回前若干行 + 总条数)。可选 keyword 用于按名称模糊过滤"
+ "(如查某个客户 / 物料)。必须先用 findForms 得到目标表单的 formId 与 moduleId。")
public String readFormData(
@P("表单id(findForms 返回的 formId)") String formId,
@P("菜单id(findForms 返回的 moduleId)") String moduleId,
@P(value = "可选:仅当要查找某个具体名称的记录时才填(如某个客户名/物料名);"
+ "问数量 / 全部 / 概况时必须留空", required = false) String keyword) {
if (formId == null || formId.isBlank() || moduleId == null || moduleId.isBlank()) {
return "缺少 formId 或 moduleId,请先用 findForms 检索到具体表单再调用本工具。";
}
String kw = (keyword == null) ? "" : keyword.trim();
String nameField = kw.isEmpty() ? null : resolveNameField(formId.trim());
JsonNode root;
try {
root = erp.readForm(formId.trim(), moduleId.trim(), 1, MAX_ROWS, nameField, kw.isEmpty() ? null : kw);
} catch (Exception e) {
return "读取失败:" + e.getMessage();
}
int code = root.path("code").asInt(0);
if (code < 0) {
return "读取失败:" + root.path("msg").asText("未知错误");
}
JsonNode ds = root.path("dataset");
int total = ds.path("totalCount").asInt(0);
JsonNode rows = ds.path("rows");
JsonNode data = (rows.isArray() && rows.size() > 0) ? rows.get(0).path("dataSet") : null;
if (data == null || !data.isArray() || data.isEmpty()) {
return "该表单当前没有数据(共 " + total + " 条)。";
}
Map labels = loadLabels(formId.trim());
List cols = pickColumns(data.get(0), labels);
String scope;
if (!kw.isEmpty() && nameField != null) {
scope = "(按名称含「" + kw + "」筛选)";
} else if (!kw.isEmpty()) {
scope = "(未识别到该表单的名称字段,无法按「" + kw + "」过滤,返回前几条)";
} else {
scope = "";
}
StringBuilder sb = new StringBuilder();
sb.append(scope).append("共 ").append(total).append(" 条,前 ").append(data.size()).append(" 条:\n\n");
sb.append("| ")
.append(cols.stream().map(c -> labels.getOrDefault(c, c)).collect(Collectors.joining(" | ")))
.append(" |\n");
sb.append("|").append(" --- |".repeat(cols.size())).append("\n");
for (JsonNode r : data) {
List vals = new ArrayList<>(cols.size());
for (String c : cols) {
vals.add(cell(r.path(c)));
}
sb.append("| ").append(String.join(" | ", vals)).append(" |\n");
}
return sb.toString();
}
/** 表单数据源表 -> 字段中文名(字段字典),用于把技术列名渲染为中文表头。 */
private Map loadLabels(String formId) {
Map m = new HashMap<>();
try {
List