Commit 3ba4dd16713378014482af5311db50e2f21bbb8e

Authored by qianbao
1 parent 9cc28448

添加向量库

src/main/java/com/xly/service/XlyErpService.java
... ... @@ -37,6 +37,7 @@ import dev.langchain4j.service.MemoryId;
37 37 import dev.langchain4j.service.V;
38 38 import io.milvus.v2.common.DataType;
39 39 import io.milvus.v2.service.collection.request.CreateCollectionReq;
  40 +import jnr.ffi.annotations.In;
40 41 import lombok.RequiredArgsConstructor;
41 42 import lombok.extern.slf4j.Slf4j;
42 43 import org.apache.commons.lang3.time.DateFormatUtils;
... ... @@ -227,6 +228,25 @@ public class XlyErpService {
227 228 }
228 229 }
229 230  
  231 + /****
  232 + * @Author 钱豹
  233 + * @Date 8:54 2026/4/7
  234 + * @Param [userInput, userId, sUserName, sBrandsId, sSubsidiaryId, sUserType, authorization]
  235 + * @return com.xly.entity.AiResponseDTO
  236 + * @Description 换一换
  237 + **/
  238 + public AiResponseDTO change(String userId ,
  239 + String sUserName ,
  240 + String sBrandsId ,
  241 + String sSubsidiaryId,
  242 + String sUserType,
  243 + String authorization,
  244 + Integer iPage) {
  245 + // 1. 初始化用户场景会话(权限内场景)
  246 + UserSceneSession session = userSceneSessionService.getUserSceneSession(userId,sUserName,sBrandsId,sSubsidiaryId,sUserType,authorization);
  247 + return getChange(session, iPage);
  248 + }
  249 +
230 250 /***
231 251 * @Author 钱豹
232 252 * @Date 19:18 2026/1/27
... ... @@ -270,7 +290,7 @@ public class XlyErpService {
270 290 // 3. 未选场景:先展示场景选择界面,处理用户序号选择
271 291 if (!session.isSceneSelected() && ValiDataUtil.me().isPureNumber(input)){
272 292 // 3.1 尝试处理场景选择(输入序号则匹配,否则展示选择提示)
273   - return handleSceneSelect(userId, input, session);
  293 + return handleSceneSelect(userId, input, session,1);
274 294 }
275 295 // 4. 构建Agent,执行业务交互,如果返回为null,说明大模型没有判段出场景,必判断出后才能继续
276 296 ErpAiAgent aiAgent = createErpAiAgent(userId, input, session);
... ... @@ -942,7 +962,7 @@ public class XlyErpService {
942 962 /**
943 963 * 处理用户场景选择:输入序号→匹配场景→更新会话状态
944 964 */
945   - private AiResponseDTO handleSceneSelect(String userId, String userInput, UserSceneSession session) {
  965 + private AiResponseDTO handleSceneSelect(String userId, String userInput, UserSceneSession session,Integer page) {
946 966 // 1. 尝试根据序号匹配场景
947 967 boolean selectSuccess = session.selectSceneByInput(userInput);
948 968 String sceneName = StrUtil.EMPTY;
... ... @@ -954,20 +974,34 @@ public class XlyErpService {
954 974 UserSceneSessionService.ERP_AGENT_CACHE.remove(userId);
955 975 //清除记忆缓存
956 976 operableChatMemoryProvider.clearSpecifiedMemory(userId);
957   - StringBuffer aiText = new StringBuffer().append(" <div style='line-height:1.8;width:100%;'>")
958   - .append("<div style=\"color: #333;\">")
959   - .append("智能体选择成功! 现在可以问她相关问题(如" + String.join("、", session.getCurrentScene().getSSceneContext()) + ")")
960   - .append("</div>");
961   - //插入用户常用问题
962   - aiText.append(getSelectAgent(session,1));
963   - sceneName = ObjectUtil.isNotEmpty(session.getCurrentScene())?session.getCurrentScene().getSSceneName():StrUtil.EMPTY;
964   - methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSControlName():StrUtil.EMPTY;
965   - return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(aiText.toString()).sSceneName(session.getCurrentScene().getSSceneName()).build();
  977 + return getChange( session, page);
966 978 } else {
967 979 // 3. 选择失败:重新展示场景选择提示
968 980 return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(session.buildSceneSelectHint()).build();
969 981 }
970 982 }
  983 +
  984 + /***
  985 + * @Author 钱豹
  986 + * @Date 9:00 2026/4/7
  987 + * @Param [session, page]
  988 + * @return com.xly.entity.AiResponseDTO
  989 + * @Description 用户输入数字换一换
  990 + **/
  991 + private AiResponseDTO getChange(UserSceneSession session,Integer page){
  992 + StringBuffer aiText = new StringBuffer().append(" <div style='line-height:1.8;width:100%;'>")
  993 + .append("<div style=\"color: #333;\">")
  994 + .append("智能体选择成功! 现在可以问她相关问题(如" + String.join("、", session.getCurrentScene().getSSceneContext()) + ")")
  995 + .append("</div>");
  996 + //插入用户常用问题
  997 + aiText.append(getSelectAgent(session,page));
  998 + String sceneName = ObjectUtil.isNotEmpty(session.getCurrentScene())?session.getCurrentScene().getSSceneName():StrUtil.EMPTY;
  999 + String methodName = ObjectUtil.isNotEmpty(session.getCurrentTool())?session.getCurrentTool().getSControlName():StrUtil.EMPTY;
  1000 + return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(aiText.toString()).sSceneName(session.getCurrentScene().getSSceneName()).build();
  1001 + }
  1002 +
  1003 +
  1004 +
971 1005 /***
972 1006 * @Author 钱豹
973 1007 * @Date 11:45 2026/3/13
... ... @@ -978,18 +1012,24 @@ public class XlyErpService {
978 1012 private String getSelectAgent(UserSceneSession session,Integer page){
979 1013 List<ToolMeta> toolMetaAll = session.getAuthTool();
980 1014 String sSceneId = session.getCurrentScene().getSId();
  1015 + String sToolId = session.getCurrentTool().getSId();
981 1016 toolMetaAll = toolMetaAll.stream().filter(to-> to.getSSceneId().equals(session.getCurrentScene().getSId())).collect(Collectors.toUnmodifiableList());
982 1017 StringBuffer sb = new StringBuffer();
983   - List<List<ToolMeta>> firstFive = ListUtil.split(toolMetaAll,5);
984   - List<ToolMeta> showList;
985   - if(ObjectUtil.isNotEmpty(firstFive.get(page-1))){
986   - showList = firstFive.get(page-1);
987   - page = page + 1;
988   - }else{
989   - showList = firstFive.get(0);
990   - page = 1;
  1018 + //获取用户最近五次问题
  1019 + List<Map<String,Object>> data = getAiUserAgentQuestion(session.getUserId(),sSceneId, sToolId, page,3);
  1020 + Integer iPageCount = 0;
  1021 + if(page==1){
  1022 + iPageCount =getAiUserAgentQuestionCount(session.getUserId(), sSceneId, sToolId);
  1023 + }
  1024 + List<ToolMeta> showListAll = new ArrayList<>();
  1025 + if(ObjectUtil.isNotEmpty(data)){
  1026 + data.forEach(one->{
  1027 + ToolMeta tm = new ToolMeta();
  1028 + tm.setSMethodName(one.get("sUserInput").toString());
  1029 + });
991 1030 }
992   - showList.forEach(one->{
  1031 + showListAll.addAll(toolMetaAll);
  1032 + showListAll.forEach(one->{
993 1033 sb.append("<div style=\"color: #4096ff; margin-top: 5px;display:flex;align-items:center;font-size:12px;\" data-action=\"reset\" data-text=\"")
994 1034 .append(one.getSMethodName()).append("\" onclick=\"reset(").append(one.getSMethodName()).append("\">")
995 1035 .append("<span style=\"display:inline-block; width:5px; height:5px; background-color:#ccc; border-radius:50%; margin-right:5px;\"></span>")
... ... @@ -997,11 +1037,53 @@ public class XlyErpService {
997 1037 .append(" </div>");
998 1038 });
999 1039 sb.append("</div>");
1000   - sb.append(" <div style=\"color: #4096ff; margin-top: 5px;width:100%;text-align:right;margin-right:10px;font-size:12px;\" data-action=\"resetTag\" data-text=\"").append(sSceneId).append(",").append(page).append("\" onclick=\"reset(换一换)\">");
  1040 + sb.append(" <div style=\"color: #4096ff; margin-top: 5px;width:100%;text-align:right;margin-right:10px;font-size:12px;\" data-action=\"resetTag\" data-text=\"").append(sSceneId).append(",").append(sToolId).append(",").append(page+1).append(",").append(iPageCount).append("\" onclick=\"reset(换一换)\">");
1001 1041 sb.append(" 换一换").append("</div>");
1002 1042 return sb.toString();
1003 1043 }
1004 1044  
  1045 + /***
  1046 + * @Author 钱豹
  1047 + * @Date 10:02 2026/4/7
  1048 + * @Param [sLoginId, sSceneId, sMethodId, iPageNum, iPageSize]
  1049 + * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
  1050 + * @Description 换一换获取最近数据
  1051 + **/
  1052 + private List<Map<String,Object>> getAiUserAgentQuestion(String sLoginId, String sSceneId, String sMethodId, Integer iPageNum,Integer iPageSize){
  1053 + StringBuffer sb = new StringBuffer().append("SELECT sId,iUpdate,sUserInput FROM ai_user_agent_question ")
  1054 + .append("WHERE sLoginId = #{sLoginId} ")
  1055 + .append("AND sSceneId = #{sSceneId} ")
  1056 + .append("AND sMethodId = #{sMethodId} ")
  1057 + .append("AND IFNULL(sUserInput,'') <> '' ")
  1058 + .append("ORDER BY iUpdate DESC,tUpdateDate DESC ")
  1059 + .append("LIMIT #{iPageNum},#{iPageSize} ");
  1060 + Map<String,Object> serMap = new HashMap<>();
  1061 + serMap.put("sLoginId",sLoginId);
  1062 + serMap.put("sSceneId",sSceneId);
  1063 + serMap.put("sMethodId",sMethodId);
  1064 + serMap.put("iPageNum",iPageNum);
  1065 + serMap.put("iPageSize",iPageSize);
  1066 + return dynamicExeDbService.findSql(serMap,sb.toString());
  1067 + }
  1068 +
  1069 + private Integer getAiUserAgentQuestionCount(String sLoginId, String sSceneId, String sMethodId){
  1070 + StringBuffer sb = new StringBuffer().append("SELECT COUNT(1) AS iCount FROM ai_user_agent_question ")
  1071 + .append("WHERE sLoginId = #{sLoginId} ")
  1072 + .append("AND sSceneId = #{sSceneId} ")
  1073 + .append("AND sMethodId = #{sMethodId} ")
  1074 + .append("AND IFNULL(sUserInput,'') <> '' ");
  1075 + Map<String,Object> serMap = new HashMap<>();
  1076 + serMap.put("sLoginId",sLoginId);
  1077 + serMap.put("sSceneId",sSceneId);
  1078 + serMap.put("sMethodId",sMethodId);
  1079 + List<Map<String,Object>> data = dynamicExeDbService.findSql(serMap,sb.toString());
  1080 + if(ObjectUtil.isNotEmpty(data)){
  1081 + return (Integer) data.get(0).get("iCount");
  1082 + }
  1083 + return 0;
  1084 + }
  1085 +
  1086 +
1005 1087 /**
1006 1088 * @Author 钱豹
1007 1089 * @Date 13:32 2026/2/6
... ... @@ -1056,25 +1138,5 @@ public class XlyErpService {
1056 1138 String sChatMessage = chatiAgent.chat(session.getUserId(), input);
1057 1139 return AiResponseDTO.builder().sSceneName(sceneName).sMethodName(methodName).aiText(sChatMessage).systemText(StrUtil.EMPTY).sReturnType(ReturnTypeCode.HTML.getCode()).build();
1058 1140 }
1059   - /***
1060   - * @Author 钱豹
1061   - * @Date 12:14 2026/3/8
1062   - * @Param [chatMessage, memoryId]
1063   - * @return void
1064   - * @Description 随便聊聊移除最后一个AI 返回信息 跟系统询问的随便聊聊
1065   - **/
1066   - private void removeMssageSbll(List<ChatMessage> chatMessage,String memoryId){
1067   - if(chatMessage!=null){
1068   -// operableChatMemoryProvider.deleteSingleMessage(memoryId,chatMessage.get(chatMessage.size()-1));
1069   - for(int i=chatMessage.size()-1;i>0;i--){
1070   - ChatMessage data = chatMessage.get(i);
1071   - ChatMessageType sType = data.type();
1072   - if(ChatMessageType.SYSTEM.equals(sType)){
1073   - operableChatMemoryProvider.deleteSingleMessage(memoryId,data);
1074   - return;
1075   - }
1076   - }
1077   - }
1078   - }
1079 1141  
1080 1142 }
1081 1143 \ No newline at end of file
... ...
src/main/java/com/xly/tts/service/PythonTtsProxyService.java
... ... @@ -68,6 +68,26 @@ public class PythonTtsProxyService {
68 68 }
69 69  
70 70 /**
  71 + * @Author 钱豹
  72 + * @Date 8:54 2026/4/7
  73 + * @Param [request]
  74 + * @return org.springframework.http.ResponseEntity<com.xly.tts.bean.TTSResponseDTO>
  75 + * @Description 换一换
  76 + **/
  77 + public ResponseEntity<TTSResponseDTO> change(TTSRequestDTO request) {
  78 + String userInput = request.getText();
  79 + String sUserId = request.getUserid();
  80 + String sUserName = request.getUsername();
  81 + String sBrandsId = request.getBrandsid();
  82 + String sSubsidiaryId = request.getSubsidiaryid();
  83 + String sUserType = request.getUsertype();
  84 + String authorization = request.getAuthorization();
  85 + Integer iPage = Integer.valueOf(userInput);
  86 + AiResponseDTO voiceText = xlyErpService.change(sUserId, sUserName , sBrandsId , sSubsidiaryId, sUserType, authorization,iPage);
  87 + return synthesizeStreamAi(request, voiceText);
  88 + }
  89 +
  90 + /**
71 91 * 【保持原有返回类型】AI对话 + 流式TTS
72 92 */
73 93 public ResponseEntity<TTSResponseDTO> synthesizeStreamAi(TTSRequestDTO request) {
... ...
src/main/java/com/xly/web/TTSStreamController.java
... ... @@ -77,6 +77,19 @@ public class TTSStreamController {
77 77 pythonTtsProxyService.shutdown();
78 78 }
79 79  
  80 + /***
  81 + * @Author 钱豹
  82 + * @Date 8:53 2026/4/7
  83 + * @Param [request]
  84 + * @return org.springframework.http.ResponseEntity<com.xly.tts.bean.TTSResponseDTO>
  85 + * @Description 换一换
  86 + **/
  87 + @PostMapping(value = "/stream/change", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.ALL_VALUE})
  88 + public ResponseEntity<TTSResponseDTO> change(@RequestBody TTSRequestDTO request) {
  89 + return pythonTtsProxyService.change(request);
  90 + }
  91 +
  92 +
80 93 /**
81 94 * 流式合成语音(代理到Python服务)
82 95 */
... ...