Commit 81a572e65ebc5788208e462747e91f797fea2536

Authored by qianbao
1 parent dcf98766

1111

... ... @@ -74,6 +74,7 @@
74 74 <groupId>org.springframework.boot</groupId>
75 75 <artifactId>spring-boot-starter-webflux</artifactId>
76 76 </dependency>
  77 +
77 78 <dependency>
78 79 <groupId>org.springframework.boot</groupId>
79 80 <artifactId>spring-boot-starter-thymeleaf</artifactId>
... ...
src/main/java/com/xly/agent/DynamicTableNl2SqlAiAgent.java
... ... @@ -23,10 +23,11 @@ public interface DynamicTableNl2SqlAiAgent {
23 23 2. 输出格式:仅返回SQL语句本身,无任何解释、换行、```sql/```包裹、备注、多余空格,直接输出可执行SQL;
24 24 3. 编写规范:
25 25 3.1 多表关联必须使用 表名+字段名(如表名.字段名),严格按下面[涉及表名]中的表次序关联,聚合函数(SUM/COUNT/AVG/MIN/MAX)必须加业务化别名,日期过滤使用标准DATE格式(yyyy-MM-dd);
26   - 3.2 SQL所有字段均采用 表名.字段名 方式生成,务必确保 字段名 在相应的 表名 描述的字段中存在,如果不存在重试其它方式,直到满足条件
27   - 3.3 SQL所有字段涉及的所有表名,都要**严格**按下面[涉及表名]中的表次序关联,没有关联不允许使用
28   - 3.4 SQL所有的查询条件,如果是字符类型的字段,均需要加不为空判断,用示例格式判断,示例:ifnull(customername,'')<>''
29   - 3.5 SQL所有的显示字段的别名中,不能出现空格,如: tCreateDate as earliest 订单日期,正确的应是 tCreateDate as earliest订单日期
  26 + 3.2 SQL所有字段均采用 表名.字段名 方式生成,务必确保 字段名 在相应的 表名 描述的字段中存在,如果不存在重试其它方式,直到满足条件;
  27 + 3.3 SQL所有字段涉及的所有表名,都要**严格**按下面[涉及表名]中的表次序关联,没有关联不允许使用;
  28 + 3.4 SQL所有的查询条件,如果是字符类型的字段,均需要加不为空判断,用示例格式判断,示例:ifnull(customername,'')<>'';
  29 + 3.5 SQL所有的查询条件,如果是日期类型的字段,均需要加不为空判断,用示例格式判断,示例:tmakedate is not Null;
  30 + 3.6 SQL所有的显示字段的别名中,不能出现空格,如: tCreateDate as earliest 订单日期,正确的应是 tCreateDate as earliest订单日期
30 31 4. 安全约束:禁止生成任何DDL/DML语句(DROP/ALTER/INSERT/UPDATE/DELETE等),禁止使用子查询、存储过程、自定义函数、临时表;
31 32 5. 精准性:
32 33 5.1 严格按用户需求+传入的表结构生成,仅使用指定字段/表,无多余字段、无无效表关联、无冗余过滤条件;
... ...
src/main/java/com/xly/entity/UserSceneSession.java
... ... @@ -41,6 +41,12 @@ public class UserSceneSession {
41 41 private SceneDto currentScene;
42 42  
43 43 private ToolMeta currentTool;
  44 +
  45 + /***
  46 + * 当前未清返回的数据集
  47 + **/
  48 + private Map<Integer,Map<String,Object>> currentRowData;
  49 +
44 50 /***
45 51 * @Author 钱豹
46 52 * @Date 10:07 2026/1/31
... ...
src/main/java/com/xly/service/XlyErpService.java
... ... @@ -108,6 +108,7 @@ public class XlyErpService {
108 108 //5.执行工具方法后,清除记忆
109 109 if(session.getBCleanMemory()){
110 110 operableChatMemoryProvider.clearSpecifiedMemory(userId);
  111 + session.setCurrentTool(null);
111 112 session.setBCleanMemory(false);
112 113 }
113 114 // 6.找到方法并且本方法带表结构描述时,需要调用 自然语言转SQL智能体
... ... @@ -318,6 +319,7 @@ public class XlyErpService {
318 319 session.setBCleanMemory(false);
319 320 session.setCurrentTool(null);
320 321 session.setCurrentScene(null);
  322 + session.setCurrentRowData(null);
321 323 UserSceneSessionService.USER_SCENE_SESSION_CACHE.put(userId, session);
322 324 // 清空Agent缓存
323 325 UserSceneSessionService.ERP_AGENT_CACHE.remove(userId);
... ...
src/main/java/com/xly/tool/DynamicToolProvider.java
... ... @@ -20,10 +20,7 @@ import com.xly.mapper.ParamRuleMapper;
20 20 import com.xly.mapper.ToolMetaMapper;
21 21 import com.xly.service.DynamicExeDbService;
22 22 import com.xly.service.UserSceneSessionService;
23   -import com.xly.util.DeepCopyUtils;
24   -import com.xly.util.HttpsRequestUtil;
25   -import com.xly.util.JsonUtils;
26   -import com.xly.util.OkHttpUtil;
  23 +import com.xly.util.*;
27 24 import dev.langchain4j.agent.tool.*;
28 25  
29 26 import dev.langchain4j.data.message.ChatMessage;
... ... @@ -233,7 +230,15 @@ public class DynamicToolProvider implements ToolProvider {
233 230 StringBuffer sl = new StringBuffer();
234 231  
235 232 if(ObjectUtil.isNotEmpty(meta.getStoolDesc())){
236   - stoolDesc.append("MethodNo:").append(meta.getSMethodNo()).append(",核心工作内容:【").append(meta.getSMethodName()).append("】").append(meta.getStoolDesc());
  233 + stoolDesc.append("MethodNo:").append(meta.getSMethodNo()).append(",核心工作内容:【").append(meta.getSMethodName());
  234 +// if (meta.getIBizType()==4){
  235 +// stoolDesc.append(",").append("并选择数据后执行["+meta.getSControlName()+"]操作");
  236 +// }
  237 + stoolDesc.append("】").append(meta.getStoolDesc());
  238 + if (meta.getIBizType()==4){
  239 + stoolDesc.append(",").append("并选择数据后执行 "+meta.getSControlName()+" 操作");
  240 +// .append("1.全部数据生成多个单据 回复【全部确认】;2.全部数据生成一个单据 回复【合并确认】;3.按自然语义描述生成一个单据 如"1,3行确认"");
  241 + }
237 242 }
238 243 if("boxQuote".equals(meta.getSMethodNo())){
239 244 log.info(meta.getSParamRules());
... ... @@ -558,6 +563,14 @@ public class DynamicToolProvider implements ToolProvider {
558 563  
559 564 // {"0":"查询","1":"执行"} 查询不需要确认
560 565 Boolean isConfirmed = isConfirmed(input) || input.contains("生成") || input.contains("确认");
  566 + //判断是否生成数据
  567 + List<Map<String,Object>> sRowData = new ArrayList<>();
  568 + String sHandleType = "merge";
  569 + if(4== meta.getIBizType() && ObjectUtil.isNotEmpty(session.getCurrentRowData())){
  570 + Map<String,Object> sRowDataMap = UserChoseIntentParser.getSelectedRows( input, session.getCurrentRowData());
  571 + sRowData = (List<Map<String, Object>>) sRowDataMap.get("sRowData");
  572 + sHandleType = sRowDataMap.get("sHandleType").toString();
  573 + }
561 574 if((isConfirmed || 0== meta.getIActionType()) && 5!= meta.getIBizType()){
562 575 // 确认后必填项校验
563 576 List<String> missingAfter = checkConfirmAfterParam(args, paramRuleData);
... ... @@ -575,9 +588,9 @@ public class DynamicToolProvider implements ToolProvider {
575 588 askconfirmMsg = buildConfirmUserMessage(meta, args);
576 589 }else if(4== meta.getIBizType() || meta.getIBizType()==5){
577 590 askconfirmMsg = doGetFromData( meta,args,session);
578   - session.setSFunPrompts(askconfirmMsg);
579   - operableChatMemoryProvider.get(memoryId).add(UserMessage.from("SYSTEM: 等待用户确认或选择部分数据操作"));
580   - return String.valueOf(successResult(toolExecutionRequest,askconfirmMsg));
  591 +// session.setSFunPrompts(askconfirmMsg);
  592 +// operableChatMemoryProvider.get(memoryId).add(UserMessage.from("SYSTEM: 等待用户确认或选择部分数据操作"));
  593 + return executeWithConfirmation(toolExecutionRequest,askconfirmMsg,operableChatMemoryProvider.get(memoryId), session, meta).text();
581 594 }else{
582 595 askconfirmMsg =getDefMessage(args,meta.getSControlName(),meta);
583 596 }
... ... @@ -863,64 +876,64 @@ public class DynamicToolProvider implements ToolProvider {
863 876 **/
864 877 private String executeToolAfter(ToolMeta meta, Map<String, Object> args,ToolExecutionRequest toolExecutionRequest,List<ParamRule> paramDefs,UserSceneSession session) {
865 878 // {"1":"存储过程","2":"SQL查询","3":"第三方API","4":"窗体查询","5":"按钮执行","6":"其它"}
866   - String sBizContent = meta.getSBizContent();
867   - Integer iBizType = meta.getIBizType();
868   - args.put("sUserId",session.getUserId());
869   - args.put("sLoginId",session.getUserName());
870   - args.put("sMakePerson",session.getUserName());
871   - args.put("sBrId",session.getSBrandsId());
872   - args.put("sBrandsId",session.getSBrandsId());
873   - args.put("sSuId",session.getSSubsidiaryId());
874   - args.put("sSubsidiaryId",session.getSSubsidiaryId());
875   - if(iBizType==1 || iBizType==4){
876   - Map<String,Object> data = new HashMap<>(args);
877   - data.put("sData", JSONObject.toJSONString(data));
878   - Map<String, Object> searMap = this.dynamicExeDbService.getDoProMap(sBizContent, data);
879   - Map<String,Object> sReturn = this.dynamicExeDbService.getCallPro(searMap,sBizContent);
880   - Integer sCode = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SCODE))? Integer.valueOf(sReturn.get(ProcedureConstant.SCODE).toString()):0;
881   - String sMsgText = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SRETURN))? sReturn.get(ProcedureConstant.SRETURN).toString():"操作成功";
882   - if(sCode< 0){
883   - String msg = ObjectUtil.isEmpty(sMsgText) ?"调用过程sCode:"+Integer.valueOf(searMap.get(ProcedureConstant.SCODE).toString()):sMsgText;
884   - return String.valueOf(askUserResult(toolExecutionRequest, msg));
885   - }
886   - session.setSFunPrompts(sMsgText);
887   - return sMsgText;
888   - }else if(iBizType==2 && ObjectUtil.isNotEmpty(sBizContent)){
889   - //SQL查询
890   - if(sBizContent.toLowerCase().startsWith("update")){
891   - this.dynamicExeDbService.updateSql(args,sBizContent);
892   - }else if(sBizContent.toLowerCase().startsWith("delete")){
893   - this.dynamicExeDbService.delSql(args,sBizContent);
894   - }else if(sBizContent.toLowerCase().startsWith("insert")){
895   - this.dynamicExeDbService.addSql(args,sBizContent);
896   - }else{
897   - List<Map<String,Object>> retData = this.dynamicExeDbService.findSql(args,sBizContent);
898   - if(ObjectUtil.isNotEmpty(retData)){
899   - StringBuffer sb = new StringBuffer();
900   - retData.forEach(one->{
901   - one.forEach((k,v)->{
902   - sb.append(v).append(" ");
903   - });
904   - sb.append("<br/>");
905   - });
906   - if(ObjectUtil.isNotEmpty(retData)){
907   - sb.append("请根据这些信息安排今天的工作吧!如果有具体任务需要进一步处理,请告诉我");
908   - }
909   - session.setSFunPrompts(sb.toString());
910   - if("queryTodayTask".equals(meta.getSMethodNo())){
911   - session.setBCleanMemory(true);
912   - }
913   - return String.valueOf(successResult(toolExecutionRequest, sb.toString()));
914   - }else{
915   - session.setSFunPrompts("未找到对应的数据");
916   - return "未找到对应的数据";
917   - }
918   - }
919   - }else if(iBizType==3){
920   - return HttpsRequestUtil.me().doRequestHttp(sBizContent,JSONUtil.toJsonStr(args),
921   - new HashMap<>(),"POST","JSON");
922   - }
923   - return String.valueOf(successResult(toolExecutionRequest, "操作成功"));
  879 + String sBizContent = meta.getSBizContent();
  880 + Integer iBizType = meta.getIBizType();
  881 + args.put("sUserId", session.getUserId());
  882 + args.put("sLoginId", session.getUserName());
  883 + args.put("sMakePerson", session.getUserName());
  884 + args.put("sBrId", session.getSBrandsId());
  885 + args.put("sBrandsId", session.getSBrandsId());
  886 + args.put("sSuId", session.getSSubsidiaryId());
  887 + args.put("sSubsidiaryId", session.getSSubsidiaryId());
  888 + if (iBizType == 1 || iBizType == 4) {
  889 + Map<String, Object> data = new HashMap<>(args);
  890 + data.put("sData", JSONObject.toJSONString(data));
  891 + Map<String, Object> searMap = this.dynamicExeDbService.getDoProMap(sBizContent, data);
  892 + Map<String, Object> sReturn = this.dynamicExeDbService.getCallPro(searMap, sBizContent);
  893 + Integer sCode = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SCODE)) ? Integer.valueOf(sReturn.get(ProcedureConstant.SCODE).toString()) : 0;
  894 + String sMsgText = ObjectUtil.isNotEmpty(sReturn.get(ProcedureConstant.SRETURN)) ? sReturn.get(ProcedureConstant.SRETURN).toString() : "操作成功";
  895 + if (sCode < 0) {
  896 + String msg = ObjectUtil.isEmpty(sMsgText) ? "调用过程sCode:" + Integer.valueOf(searMap.get(ProcedureConstant.SCODE).toString()) : sMsgText;
  897 + return String.valueOf(askUserResult(toolExecutionRequest, msg));
  898 + }
  899 + session.setSFunPrompts(sMsgText);
  900 + return sMsgText;
  901 + } else if (iBizType == 2 && ObjectUtil.isNotEmpty(sBizContent)) {
  902 + //SQL查询
  903 + if (sBizContent.toLowerCase().startsWith("update")) {
  904 + this.dynamicExeDbService.updateSql(args, sBizContent);
  905 + } else if (sBizContent.toLowerCase().startsWith("delete")) {
  906 + this.dynamicExeDbService.delSql(args, sBizContent);
  907 + } else if (sBizContent.toLowerCase().startsWith("insert")) {
  908 + this.dynamicExeDbService.addSql(args, sBizContent);
  909 + } else {
  910 + List<Map<String, Object>> retData = this.dynamicExeDbService.findSql(args, sBizContent);
  911 + if (ObjectUtil.isNotEmpty(retData)) {
  912 + StringBuffer sb = new StringBuffer();
  913 + retData.forEach(one -> {
  914 + one.forEach((k, v) -> {
  915 + sb.append(v).append(" ");
  916 + });
  917 + sb.append("<br/>");
  918 + });
  919 + if (ObjectUtil.isNotEmpty(retData)) {
  920 + sb.append("请根据这些信息安排今天的工作吧!如果有具体任务需要进一步处理,请告诉我");
  921 + }
  922 + session.setSFunPrompts(sb.toString());
  923 + if ("queryTodayTask".equals(meta.getSMethodNo())) {
  924 + session.setBCleanMemory(true);
  925 + }
  926 + return String.valueOf(successResult(toolExecutionRequest, sb.toString()));
  927 + } else {
  928 + session.setSFunPrompts("未找到对应的数据");
  929 + return "未找到对应的数据";
  930 + }
  931 + }
  932 + } else if (iBizType == 3) {
  933 + return HttpsRequestUtil.me().doRequestHttp(sBizContent, JSONUtil.toJsonStr(args),
  934 + new HashMap<>(), "POST", "JSON");
  935 + }
  936 + return String.valueOf(successResult(toolExecutionRequest, "操作成功"));
924 937 }
925 938  
926 939  
... ... @@ -983,7 +996,7 @@ public class DynamicToolProvider implements ToolProvider {
983 996 log.info("headers=============================={}", JSONObject.toJSONString(headers));
984 997 log.info("请求URL,JSON,headers=={},{},{}",sUrl,JSONObject.toJSONString(sBody),JSONObject.toJSONString(headers));
985 998 ErpResult erpResult = JsonUtils.toObject(result,ErpResult.class);
986   - result = buildResultMessageWithTable( meta, erpResult);
  999 + result = buildResultMessageWithTable( meta, erpResult, session);
987 1000 }catch (Exception e){
988 1001 result ="执行异常:"+e.getMessage();
989 1002 }
... ... @@ -993,8 +1006,8 @@ public class DynamicToolProvider implements ToolProvider {
993 1006 /**
994 1007 * 构建 窗体获取数据方法 未清或者明细
995 1008 */
996   - public String buildResultMessageWithTable(ToolMeta meta,ErpResult erpResult){
997   -
  1009 + public String buildResultMessageWithTable(ToolMeta meta,ErpResult erpResult,UserSceneSession session){
  1010 + Map<Integer,Map<String,Object>> currentRowData = new HashMap<>();
998 1011 ErpDataset dataset = erpResult.getDataset();
999 1012 //返回错误信息
1000 1013 if(erpResult.getCode()<0 && ObjectUtil.isNotEmpty(erpResult.getMsg())){
... ... @@ -1037,24 +1050,36 @@ public class DynamicToolProvider implements ToolProvider {
1037 1050 headers.forEach(header -> markdown.append(header).append(" | "));
1038 1051 markdown.append("\n|").append("---|".repeat(headers.size() + 1)).append("\n");
1039 1052 // 填充表格数据
  1053 + List<Map<String,Object>> machineData = new LinkedList<>();
1040 1054 for (int i = 0; i < recordData.size(); i++) {
1041 1055 // 保存隐藏列的值(如"唯一"字段)
1042 1056 String uniqueValue = recordData.get(i).get("sSlaveId") != null ? recordData.get(i).get("sSlaveId").toString() : "";
1043 1057 markdown.append("| ").append(i + 1).append(" | ");
  1058 + Map<String,Object> rMap = new HashMap<>();
1044 1059 for (String header : headers) {
1045 1060 // 这里需要根据你的数据结构来获取对应的值
1046 1061 Object value = recordData.get(i)!= null ? recordData.get(i).get(header) : null;
1047 1062 markdown.append(value != null ? value : "—").append(" | ");
  1063 + rMap.put(header,value);
1048 1064 }
  1065 + rMap.put("sSlaveId",uniqueValue);
  1066 + rMap.put("唯一",uniqueValue);
1049 1067 // 在行末添加隐藏数据的特殊标记(AI可以解析)
1050   - markdown.append(" <!-- HIDDEN_DATA:{\"sSlaveId\":\"").append(uniqueValue).append("\"} -->");
  1068 + markdown.append(" <!-- HIDDEN_DATA:").append(JSONUtil.toJsonStr(rMap)).append("-->");
1051 1069 markdown.append("\n");
  1070 + machineData.add(rMap);
  1071 + currentRowData.put(i + 1,recordData.get(i));
1052 1072 }
1053 1073 markdown.append(">");
  1074 +// // 4. 机器可读的结构化数据(只出现一次!)
  1075 +// markdown.append("<!-- MACHINE_DATA_START -->\n");
  1076 +// markdown.append(JSONUtil.toJsonStr(machineData));
  1077 +// markdown.append("\n<!-- MACHINE_DATA_END -->\n\n");
1054 1078 if(meta.getIBizType()==4){
1055 1079 markdown.append("\n---\n");
1056 1080 appendConfirmAll(markdown,meta.getSControlName());
1057 1081 }
  1082 + session.setCurrentRowData(currentRowData);
1058 1083 return markdown.toString();
1059 1084 }
1060 1085  
... ...
src/main/java/com/xly/tts/service/PythonTtsProxyService.java
... ... @@ -77,7 +77,6 @@ public class PythonTtsProxyService {
77 77 String sUserType = request.getUsertype();
78 78 String authorization = request.getAuthorization();
79 79 //校验登录token 是否有效
80   -
81 80 AiResponseDTO voiceText = xlyErpService.erpUserInput(userInput,sUserId,sUserName,sBrandsId,sSubsidiaryId,sUserType, authorization);
82 81 return synthesizeStreamAi(request,voiceText);
83 82 }
... ...
src/main/java/com/xly/util/AIModelDataFormatter.java 0 → 100644
  1 +package com.xly.util;
  2 +import cn.hutool.json.JSONUtil;
  3 +import com.xly.entity.ToolMeta;
  4 +
  5 +import java.util.*;
  6 +
  7 +public class AIModelDataFormatter {
  8 + public static String formatDataForAI(ToolMeta meta, Integer TotalCount,
  9 + List<Map<String, Object>> rows,
  10 + Set<String> headers) {
  11 + StringBuilder response = new StringBuilder();
  12 + // 3. **关键:先显示用户友好的表格**
  13 + response.append("## 查询结果(共").append(TotalCount).append("条)\n\n");
  14 + response.append(buildMarkdownTable(rows, headers));
  15 + response.append("\n");
  16 + // 4. 机器可读的结构化数据(只出现一次!)
  17 + response.append("<!-- MACHINE_DATA_START -->\n");
  18 + response.append(buildMachineReadableData(rows,headers));
  19 + response.append("\n<!-- MACHINE_DATA_END -->\n\n");
  20 + return response.toString();
  21 + }
  22 +
  23 + /**
  24 + * 构建Markdown表格
  25 + */
  26 + public static String buildMarkdownTable(List<Map<String, Object>> rows, Set<String> headers) {
  27 + StringBuilder table = new StringBuilder();
  28 + // 表头
  29 + table.append("| 序号 | ");
  30 + for (String header : headers) {
  31 + table.append(header).append(" | ");
  32 + }
  33 + table.append("\n|");
  34 + table.append("---|".repeat(headers.size() + 1));
  35 + table.append("\n");
  36 + // 表格数据
  37 + for (int i = 0; i < rows.size(); i++) {
  38 + Map<String, Object> row = rows.get(i);
  39 + table.append("| ").append(i + 1).append(" | ");
  40 + for (String header : headers) {
  41 + Object value = row.get(header);
  42 + table.append(value != null ? value : "—").append(" | ");
  43 + }
  44 + table.append("\n");
  45 + }
  46 +
  47 + return table.toString();
  48 + }
  49 +
  50 + /**
  51 + * 构建机器可读数据
  52 + */
  53 + public static String buildMachineReadableData(List<Map<String, Object>> rows, Set<String> headers) {
  54 + List<Map<String, Object>> dataList = new ArrayList<>();
  55 + for (int i = 0; i < rows.size(); i++) {
  56 + Map<String, Object> row = rows.get(i);
  57 + Map<String, Object> item = new LinkedHashMap<>();
  58 + item.put("序号", i + 1);
  59 + for (String header : headers) {
  60 + Object value = row.get(header);
  61 + item.put(header,value);
  62 + }
  63 + dataList.add(item);
  64 + }
  65 + return JSONUtil.toJsonStr(dataList);
  66 + }
  67 +
  68 +}
0 69 \ No newline at end of file
... ...
src/main/java/com/xly/util/UserChoseIntentParser.java 0 → 100644
  1 +package com.xly.util;
  2 +
  3 +import java.util.*;
  4 +import java.util.stream.Collectors;
  5 +
  6 +public class UserChoseIntentParser {
  7 +
  8 + /**
  9 + * @param input 用户输入:全部确认 / 合并确认 / 1,3行确认
  10 + * @param rowMap 上面parse出来的全量数据
  11 + * @return 最终要生成订单的明细列表
  12 + */
  13 + public static Map<String,Object> getSelectedRows(String input, Map<Integer, Map<String,Object>> rowMap) {
  14 + List<Map<String,Object>> sRowData = new ArrayList<>();
  15 + Map<String,Object> rMap = new HashMap<>();
  16 + String sHandleType = "merge";//单个的 就是全部确认 否则就是合并确认
  17 + if (input.contains("全部确认") || input.contains("合并确认")) {
  18 + if(input.contains("全部确认")){
  19 + sHandleType = "single";
  20 + }
  21 + sRowData = new ArrayList<>(rowMap.values());
  22 + rMap.put("sRowData",sRowData);
  23 + rMap.put("sHandleType",sHandleType);
  24 + return rMap;
  25 + }
  26 +
  27 + // 解析 1,3,5-7 这类行号
  28 + Set<Integer> selected = new HashSet<>();
  29 + String[] parts = input.replaceAll("[^0-9,-]", "").split("[,,]");
  30 + for (String part : parts) {
  31 + try {
  32 + if (part.contains("-")) {
  33 + String[] range = part.split("-");
  34 + int start = Integer.parseInt(range[0]);
  35 + int end = Integer.parseInt(range[1]);
  36 + for (int i = start; i <= end; i++) {
  37 + selected.add(i);
  38 + }
  39 + } else {
  40 + selected.add(Integer.parseInt(part));
  41 + }
  42 + } catch (Exception ignored) {}
  43 + }
  44 + rMap.put("sRowData",selected.stream()
  45 + .filter(rowMap::containsKey)
  46 + .map(rowMap::get)
  47 + .collect(Collectors.toList()));
  48 + rMap.put("sHandleType",sHandleType);
  49 + return rMap;
  50 + }
  51 +}
... ...
src/main/resources/templates/chat.html
... ... @@ -462,13 +462,13 @@
462 462 <script>
463 463 let sessionId ="";
464 464 // let userid= "17706006510007934913359242990000";
465   - let userid= "17502321750004978169421209637000";
466   - let username= "admin";
  465 + let userid= "17522967560005776104370282597000";
  466 + let username= "钱豹";
467 467 let brandsid= "1111111111";
468 468 let subsidiaryid= "1111111111";
469 469 let usertype= "sysadmin";
470 470 // let usertype= "General";
471   - let authorization="1EDB99C9BF070115F7A57AC43D8CB09F0B8C49F979DAB63A2AEA84B372B2B42BF3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1D2D86C92E4DCE571A8ECF0767494BBDE2495FD8E662F2065F9430347C7E4472B5538155B7ADAEE71E899235DC1122F426";
  471 + let authorization="CE444885A9BCFDDE1FD793F8A0931301E9D7DE6CEDD9DE4B83ECE2219C7829A8F3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1DD20808D80550B390B6BE6ECDCFF597F4D8668502224FF2D5675E74B8CDD2ABFB538155B7ADAEE71E899235DC1122F426";
472 472 //"1EDB99C9BF070115F7A57AC43D8CB09F0B8C49F979DAB63A2AEA84B372B2B42BF3419238942A93E9AD666629E18D159AF7FE144A6407DE745BA0AEC8B235FC1DC16A7526DEC4395CF09285C2BD330B9FD8668502224FF2D5675E74B8CDD2ABFB538155B7ADAEE71E899235DC1122F426";
473 473 let hrefLock = window.location.origin+"/xlyAi";
474 474 // ==================== 配置部分 ====================
... ...