|
|
@@ -80,6 +80,81 @@ public class ArtifactService {
|
|
|
"execute_sql", "SQL查询",
|
|
|
"query_table_data", "基础数据");
|
|
|
|
|
|
+ /**
|
|
|
+ * 具体 {@code @Tool} 名 → 中文工具名。
|
|
|
+ *
|
|
|
+ * <p>文件名格式:{@code 工具组中文名_工具中文名_时间戳_随机串.csv}。
|
|
|
+ * 本表与前端 {@code ai/utils/constants.ts#TOOL_LABELS} 保持同步;新工具忘记登记时
|
|
|
+ * 回退原始 {@code @Tool} 名,仍能区分是哪个工具,只是显示为英文。
|
|
|
+ */
|
|
|
+ private static final Map<String, String> ARTIFACT_ACTION_LABELS = Map.ofEntries(
|
|
|
+ Map.entry("execute_sql", "执行 SQL 查询"),
|
|
|
+ Map.entry("query_table_data", "查询表格数据"),
|
|
|
+
|
|
|
+ // 人员画像与亲密度
|
|
|
+ Map.entry("list_persons", "列出案件人员"),
|
|
|
+ Map.entry("get_person_call_profile", "通话数据画像"),
|
|
|
+ Map.entry("stat_person_call_station", "通话基站分布"),
|
|
|
+ Map.entry("stat_person_call_top10", "最常联系人 TOP10"),
|
|
|
+ Map.entry("stat_call_record_summary", "通话记录汇总"),
|
|
|
+ Map.entry("get_person_trans_profile", "交易数据画像"),
|
|
|
+ Map.entry("stat_person_trans_top10", "最常交易对手 TOP10"),
|
|
|
+ Map.entry("stat_trans_record_summary", "交易记录汇总"),
|
|
|
+ Map.entry("get_trans_bank_card_info", "银行卡交易汇总"),
|
|
|
+ Map.entry("stat_trans_amount_trend_month", "交易金额月度趋势"),
|
|
|
+ Map.entry("stat_trans_amount_trend_year", "交易金额年度趋势"),
|
|
|
+ Map.entry("stat_intimacy_summary", "亲密度总览"),
|
|
|
+
|
|
|
+ // 通话明细
|
|
|
+ Map.entry("get_call_records", "查询通话明细"),
|
|
|
+ Map.entry("stat_call_night_summary", "夜间通话汇总"),
|
|
|
+ Map.entry("stat_call_night_detail", "夜间通话明细"),
|
|
|
+ Map.entry("stat_call_continuous_summary", "持续通话汇总"),
|
|
|
+ Map.entry("stat_call_continuous_detail", "持续通话明细"),
|
|
|
+ Map.entry("stat_call_sensitive_summary", "敏感日期通话汇总"),
|
|
|
+ Map.entry("stat_call_sensitive_detail", "敏感日期通话明细"),
|
|
|
+
|
|
|
+ // 交易与资金
|
|
|
+ Map.entry("get_trans_records", "查询交易明细"),
|
|
|
+ Map.entry("stat_trans_big_summary", "大额交易汇总"),
|
|
|
+ Map.entry("stat_trans_big_by_date", "大额交易按日统计"),
|
|
|
+ Map.entry("stat_trans_big_detail", "大额交易明细"),
|
|
|
+ Map.entry("stat_trans_together", "共同交易分析"),
|
|
|
+ Map.entry("stat_trans_card_hold", "代持卡分析"),
|
|
|
+ Map.entry("stat_trans_cash_flow", "交易现金流汇总"),
|
|
|
+ Map.entry("stat_trans_continuous_summary", "持续交易汇总"),
|
|
|
+ Map.entry("stat_trans_continuous_detail", "持续交易明细"),
|
|
|
+ Map.entry("stat_trans_fast_fund_flow", "资金快转分析"),
|
|
|
+ Map.entry("stat_trans_financial", "金融交易筛选"),
|
|
|
+ Map.entry("stat_trans_fixed_deposit", "定期存款汇总"),
|
|
|
+ Map.entry("stat_trans_fixed_deposit_detail", "定期存款明细"),
|
|
|
+ Map.entry("stat_trans_frequency", "交易频次统计"),
|
|
|
+ Map.entry("stat_trans_sensitive_summary", "敏感日期交易汇总"),
|
|
|
+ Map.entry("stat_trans_sensitive_detail", "敏感日期交易明细"),
|
|
|
+
|
|
|
+ // 轨迹 / 出行 / 快递
|
|
|
+ Map.entry("stat_track_cell_tower_summary", "基站轨迹汇总"),
|
|
|
+ Map.entry("stat_track_cell_tower_detail", "基站轨迹明细"),
|
|
|
+ Map.entry("stat_track_en_local_summary", "境外昼夜活动汇总"),
|
|
|
+ Map.entry("stat_track_en_local_detail", "境外昼夜活动明细"),
|
|
|
+ Map.entry("stat_express_summary", "快递收寄汇总"),
|
|
|
+ Map.entry("stat_express_by_phone", "按号码查快递"),
|
|
|
+ Map.entry("get_express_records", "查询快递明细"),
|
|
|
+ Map.entry("stat_track_meet_summary", "人员碰面汇总"),
|
|
|
+ Map.entry("stat_track_meet_detail", "人员碰面明细"),
|
|
|
+ Map.entry("stat_together_live_summary", "同住关系汇总"),
|
|
|
+ Map.entry("stat_together_live_detail", "同住关系明细"),
|
|
|
+ Map.entry("get_hotel_stay_detail", "旅馆住宿明细"),
|
|
|
+ Map.entry("get_together_live_info_detail", "同住服务记录明细"),
|
|
|
+ Map.entry("stat_together_travel_summary", "同行出行汇总"),
|
|
|
+ Map.entry("stat_together_travel_detail", "同行出行明细"),
|
|
|
+ Map.entry("get_travel_record_detail", "出行票务明细"),
|
|
|
+
|
|
|
+ // 非表格材料档案
|
|
|
+ Map.entry("list_file_profiles", "检索材料清单"),
|
|
|
+ Map.entry("stat_file_profiles_by_category", "材料分类统计"),
|
|
|
+ Map.entry("get_file_sample", "读取材料正文片段"));
|
|
|
+
|
|
|
private final ArtifactProperties props;
|
|
|
|
|
|
/** 随工具结果回传的预览行数(模型与前端都不看全量,全量在 CSV 里) */
|
|
|
@@ -367,21 +442,25 @@ public class ArtifactService {
|
|
|
private record Names(String id, String fileName) {
|
|
|
}
|
|
|
|
|
|
- /** 文件名 = {工具域}_{具体工具名}_{时间戳}_{8位随机}.csv */
|
|
|
+ /** 文件名 = {工具组中文名}_{工具中文名}_{时间戳}_{8位随机}.csv */
|
|
|
private Names buildNames(String toolName) {
|
|
|
String domain = safeToolPart(toolName, "query");
|
|
|
String specific = currentToolName();
|
|
|
String specificSafe = safeToolPart(specific, null);
|
|
|
- // execute_sql / query_table_data 这类工具:域标识本身就是具体工具名,避免重复
|
|
|
+ // execute_sql / query_table_data 这类工具:域标识本身就是具体工具名,磁盘 id 避免重复
|
|
|
boolean appendSpecific = specificSafe != null && !specificSafe.equalsIgnoreCase(domain);
|
|
|
|
|
|
String uuid = UUID.randomUUID().toString().replace("-", "");
|
|
|
String suffix = "_" + LocalDateTime.now().format(ID_TS) + "_"
|
|
|
+ uuid.substring(0, 8) + ".csv";
|
|
|
String idBase = appendSpecific ? domain + "_" + specificSafe : domain;
|
|
|
- String displayBase = artifactLabel(toolName);
|
|
|
- if (appendSpecific) {
|
|
|
- displayBase = displayBase + "_" + specificSafe;
|
|
|
+
|
|
|
+ // 展示名:工具组中文名 + 工具中文名(未登记的工具回退原始 @Tool 名)
|
|
|
+ String domainLabel = artifactLabel(toolName);
|
|
|
+ String actionLabel = displayToolPart(artifactActionLabel(specific));
|
|
|
+ String displayBase = domainLabel;
|
|
|
+ if (!actionLabel.isEmpty() && !actionLabel.equals(domainLabel)) {
|
|
|
+ displayBase = displayBase + "_" + actionLabel;
|
|
|
}
|
|
|
return new Names(idBase + suffix, displayBase + suffix);
|
|
|
}
|
|
|
@@ -443,6 +522,27 @@ public class ArtifactService {
|
|
|
return safe.isEmpty() ? fallback : safe.toString();
|
|
|
}
|
|
|
|
|
|
+ /** 具体工具的中文名;未登记时回退原始 @Tool 名(至少还能区分) */
|
|
|
+ private static String artifactActionLabel(String toolName) {
|
|
|
+ if (toolName == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ String trimmed = toolName.trim();
|
|
|
+ return ARTIFACT_ACTION_LABELS.getOrDefault(trimmed, trimmed);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 展示文件名片段清洗:去掉 Windows 非法字符与空白,最长 40 字符 */
|
|
|
+ private static String displayToolPart(String value) {
|
|
|
+ if (value == null || value.isBlank()) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ String cleaned = value.trim()
|
|
|
+ .replaceAll("[\\\\/:*?\"<>|\\r\\n\\t]", "_")
|
|
|
+ .replaceAll("\\s+", "")
|
|
|
+ .replaceAll("_+", "_");
|
|
|
+ return cleaned.length() > 40 ? cleaned.substring(0, 40) : cleaned;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 工具标识 → 产物文件名的中文前缀。
|
|
|
*
|