Commit 3ba4dd16713378014482af5311db50e2f21bbb8e
1 parent
9cc28448
添加向量库
Showing
3 changed files
with
136 additions
and
41 deletions
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 | */ | ... | ... |