This commit is contained in:
2025-12-23 15:57:11 +08:00
parent 33a16342d3
commit 68daf391af
23 changed files with 608 additions and 523 deletions

View File

@@ -9,7 +9,9 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.xyzh.api.ai.dto.TbAgent;
import org.xyzh.api.ai.dto.TbKnowledge;
import org.xyzh.api.ai.service.AgentService;
import org.xyzh.api.ai.service.KnowledgeService;
import org.xyzh.api.system.service.SysConfigService;
import org.xyzh.common.core.domain.ResultDomain;
@@ -23,8 +25,8 @@ import org.xyzh.common.utils.id.IdUtil;
* @since 2025-12-18
*/
@Configuration
public class KnowledgeInit {
private static final Logger logger = LoggerFactory.getLogger(KnowledgeInit.class);
public class AiInit {
private static final Logger logger = LoggerFactory.getLogger(AiInit.class);
private static final String SERVICE_WORKCASE = "workcase";
private static final String CATEGORY_INTERNAL = "internal";
@@ -36,6 +38,9 @@ public class KnowledgeInit {
@DubboReference(version = "1.0.0", group = "system", timeout = 30000, retries = 0)
private SysConfigService sysConfigService;
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0)
private AgentService agentService;
@Bean
public CommandLineRunner knowledgeInitRunner() {
return args -> {
@@ -58,6 +63,31 @@ public class KnowledgeInit {
};
}
@Bean
public CommandLineRunner agentInitRunner(){
return args -> {
logger.info("开始初始化客服系统智能体...");
TbAgent agent = new TbAgent();
agent.setIsOuter(true);
agent.setName("泰豪小电");
ResultDomain<TbAgent> listDomain = agentService.getAgentList(agent);
if (listDomain.getSuccess()&&!listDomain.getDataList().isEmpty()) {
logger.info("泰豪小电智能体已经存在");
return;
}
agent.setApiKey("app-CDKy0wYkPnl6dA6G7eu113Vw");
agent.setIntroduce("您好,我是泰豪小电智能客服。请描述您的问题,我会尽力协助。");
agent.setCategory("客服智能体");
agent.setCategory("user_admin");
ResultDomain<TbAgent> resultDomain = agentService.addAgent(agent);
if(resultDomain.getSuccess()){
logger.info("泰豪小电智能体初始化成功");
}else{
logger.error("泰豪小电智能体初始化失败"+resultDomain.getMessage());
}
};
}
/**
* 构建8个知识库配置
*/

View File

@@ -2,6 +2,7 @@ package org.xyzh.workcase.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -11,16 +12,11 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.xyzh.api.ai.dto.ChatPrepareData;
import org.xyzh.api.ai.dto.TbChat;
import org.xyzh.api.ai.dto.TbChatMessage;
import org.xyzh.api.workcase.dto.TbChatRoomDTO;
import org.xyzh.api.workcase.dto.TbChatRoomMemberDTO;
import org.xyzh.api.workcase.dto.TbChatRoomMessageDTO;
import org.xyzh.api.workcase.dto.TbCustomerServiceDTO;
import org.xyzh.api.workcase.dto.TbWordCloudDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
import org.xyzh.api.workcase.service.ChatRoomService;
import org.xyzh.api.workcase.service.WorkcaseChatService;
import org.xyzh.api.workcase.vo.ChatMemberVO;
@@ -49,6 +45,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
* @since 2025-12-19
*/
@Tag(name = "工单对话")
@Validated
@RestController
@RequestMapping("/workcase/chat")
public class WorkcaseChatContorller {
@@ -59,106 +56,6 @@ public class WorkcaseChatContorller {
@Autowired
private ChatRoomService chatRoomService;
// ========================= AI对话管理 =========================
@Operation(summary = "创建对话")
@PreAuthorize("hasAuthority('workcase:chat:create')")
@PostMapping
public ResultDomain<TbChat> createChat(@RequestBody TbChat chat) {
ValidationResult vr = ValidationUtils.validate(chat, Arrays.asList(
ValidationUtils.requiredString("userId", "用户ID")
));
if (!vr.isValid()) {
return ResultDomain.failure(vr.getAllErrors());
}
return workcaseChatService.createChat(chat);
}
@Operation(summary = "更新对话")
@PreAuthorize("hasAuthority('workcase:chat:update')")
@PutMapping
public ResultDomain<TbChat> updateChat(@RequestBody TbChat chat) {
ValidationResult vr = ValidationUtils.validate(chat, Arrays.asList(
ValidationUtils.requiredString("chatId", "对话ID")
));
if (!vr.isValid()) {
return ResultDomain.failure(vr.getAllErrors());
}
return workcaseChatService.updateChat(chat);
}
@Operation(summary = "查询对话列表")
@PreAuthorize("hasAuthority('workcase:chat:list')")
@PostMapping("/list")
public ResultDomain<TbChat> getChatList(@RequestBody TbChat filter) {
return workcaseChatService.getChatList(filter);
}
@Operation(summary = "获取对话消息列表")
@PreAuthorize("hasAuthority('workcase:chat:message')")
@PostMapping("/message/list")
public ResultDomain<TbChatMessage> getChatMessageList(@RequestBody TbChat filter) {
ValidationResult vr = ValidationUtils.validate(filter, Arrays.asList(
ValidationUtils.requiredString("chatId", "对话ID")
));
if (!vr.isValid()) {
return ResultDomain.failure(vr.getAllErrors());
}
return workcaseChatService.getChatMessageList(filter);
}
@Operation(summary = "准备对话会话")
@PreAuthorize("hasAuthority('workcase:chat:stream')")
@PostMapping("/prepare")
public ResultDomain<String> prepareChatMessageSession(@RequestBody ChatPrepareData prepareData) {
ValidationResult vr = ValidationUtils.validate(prepareData, Arrays.asList(
ValidationUtils.requiredString("chatId", "对话ID"),
ValidationUtils.requiredString("query", "用户问题")
));
if (!vr.isValid()) {
return ResultDomain.failure(vr.getAllErrors());
}
return workcaseChatService.prepareChatMessageSession(prepareData);
}
@Operation(summary = "流式对话SSE")
@PreAuthorize("hasAuthority('workcase:chat:stream')")
@GetMapping(value = "/stream/{sessionId}", produces = "text/event-stream")
public SseEmitter streamChatMessage(@PathVariable String sessionId) {
return workcaseChatService.streamChatMessageWithSse(sessionId);
}
@Operation(summary = "停止对话")
@PreAuthorize("hasAuthority('workcase:chat:stream')")
@PostMapping("/stop/{taskId}")
public ResultDomain<Boolean> stopChat(@RequestBody TbChat filter, @PathVariable String taskId) {
return workcaseChatService.stopChatMessageByTaskId(filter, taskId);
}
@Operation(summary = "评论对话消息")
@PreAuthorize("hasAuthority('workcase:chat:message')")
@PostMapping("/comment")
public ResultDomain<Boolean> commentChatMessage(@RequestBody TbChat filter,
@RequestParam String messageId, @RequestParam String comment) {
return workcaseChatService.commentChatMessage(filter, messageId, comment);
}
// ========================= 对话分析 =========================
@Operation(summary = "分析对话AI预填工单信息")
@PreAuthorize("hasAuthority('workcase:chat:analyze')")
@GetMapping("/analyze/{chatId}")
public ResultDomain<TbWorkcaseDTO> analyzeChat(@PathVariable String chatId) {
return workcaseChatService.analyzeChat(chatId);
}
@Operation(summary = "总结对话")
@PreAuthorize("hasAuthority('workcase:chat:analyze')")
@PostMapping("/summary/{chatId}")
public ResultDomain<TbWorkcaseDTO> summaryChat(@PathVariable String chatId) {
return workcaseChatService.summaryChat(chatId);
}
// ========================= ChatRoom聊天室管理实时IM =========================
@Operation(summary = "创建聊天室(转人工时调用)")

View File

@@ -4,24 +4,16 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.xyzh.api.ai.dto.ChatPrepareData;
import org.xyzh.api.ai.dto.TbChat;
import org.xyzh.api.ai.dto.TbChatMessage;
import org.xyzh.api.ai.service.AgentChatService;
import org.xyzh.api.workcase.dto.TbWordCloudDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
import org.xyzh.api.workcase.service.WorkcaseChatService;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.redis.service.RedisService;
import org.xyzh.common.utils.id.IdUtil;
import org.xyzh.workcase.mapper.TbWordCloudMapper;
@@ -30,201 +22,9 @@ public class WorkcaseChatServiceImpl implements WorkcaseChatService{
private static final Logger logger = LoggerFactory.getLogger(WorkcaseChatServiceImpl.class);
private static final String CHAT_COUNT_KEY_PREFIX = "workcase:chat:count:";
private static final int TRANSFER_HUMAN_THRESHOLD = 3;
@DubboReference(version = "1.0.0", group = "ai", check = false, retries = 0)
private AgentChatService agentChatService;
@Autowired
private TbWordCloudMapper wordCloudMapper;
@Autowired
private RedisService redisService;
// ========================= 聊天管理 ==========================
@Override
public ResultDomain<TbChat> createChat(TbChat chat) {
logger.info("创建对话: userId={}", chat.getUserId());
ResultDomain<TbChat> result = agentChatService.createChat(chat);
if (result.getSuccess() && result.getData() != null) {
redisService.set(CHAT_COUNT_KEY_PREFIX + result.getData().getChatId(), 0);
}
return result;
}
@Override
public ResultDomain<TbChat> updateChat(TbChat chat) {
logger.info("更新对话: chatId={}", chat.getChatId());
return agentChatService.updateChat(chat);
}
@Override
public ResultDomain<TbChat> getChatList(TbChat filter) {
return agentChatService.getChatList(filter);
}
// ========================= 聊天信息管理 ======================
@Override
public ResultDomain<TbChatMessage> getChatMessageList(TbChat filter) {
return agentChatService.getChatMessageList(filter);
}
@Override
public ResultDomain<String> prepareChatMessageSession(ChatPrepareData prepareData) {
logger.info("准备对话会话: chatId={}, query={}", prepareData.getChatId(), prepareData.getQuery());
String chatId = prepareData.getChatId();
Object countObj = redisService.get(CHAT_COUNT_KEY_PREFIX + chatId);
int chatCount = (countObj != null) ? (Integer) countObj : 0;
chatCount++;
redisService.set(CHAT_COUNT_KEY_PREFIX + chatId, chatCount);
ResultDomain<String> result = agentChatService.prepareChatMessageSession(prepareData);
if (result.getSuccess() && chatCount >= TRANSFER_HUMAN_THRESHOLD) {
logger.info("已达到{}次AI对话建议转人工: chatId={}", TRANSFER_HUMAN_THRESHOLD, chatId);
}
return result;
}
@Override
public SseEmitter streamChatMessageWithSse(String sessionId) {
return agentChatService.streamChatMessageWithSse(sessionId);
}
@Override
public ResultDomain<Boolean> stopChatMessageByTaskId(TbChat filter, String taskId) {
return agentChatService.stopChatMessageByTaskId(filter, taskId);
}
@Override
public ResultDomain<Boolean> commentChatMessage(TbChat filter, String messageId, String comment) {
return agentChatService.commentChatMessage(filter, messageId, comment);
}
// =============================== 对话分析 ==========================
@Override
public ResultDomain<TbWorkcaseDTO> analyzeChat(String chatId) {
logger.info("分析对话内容,生成工单预填信息: chatId={}", chatId);
TbChat filter = new TbChat();
filter.setChatId(chatId);
ResultDomain<TbChatMessage> msgResult = agentChatService.getChatMessageList(filter);
if (!msgResult.getSuccess() || msgResult.getDataList() == null || msgResult.getDataList().isEmpty()) {
return ResultDomain.failure("获取对话消息失败或消息为空");
}
List<TbChatMessage> messages = msgResult.getDataList();
// ============== 伪代码调用AI分析对话自动生成工单预填信息 ==============
// 步骤5AI根据聊天对话自动生成部分工单信息预填入小程序的工单创建表单
//
// 1. 构建对话上下文
// StringBuilder conversationContext = new StringBuilder();
// for (TbChatMessage msg : messages) {
// conversationContext.append(msg.getRole()).append(": ").append(msg.getContent()).append("\n");
// }
//
// 2. 调用Dify工作流或Agent进行对话分析
// DifyWorkflowRequest request = new DifyWorkflowRequest();
// request.setWorkflowId("workcase-analysis-workflow");
// request.setInputs(Map.of("conversation", conversationContext.toString()));
// DifyWorkflowResponse response = difyService.runWorkflow(request);
//
// 3. 解析AI分析结果提取工单预填信息
// JSONObject analysisResult = JSON.parseObject(response.getOutputs());
// TbWorkcaseDTO workcase = new TbWorkcaseDTO();
// workcase.setType(analysisResult.getString("type")); // 问题类型:如"设备故障"、"维修申请"
// workcase.setDevice(analysisResult.getString("device")); // 设备名称:如"燃气管道"、"电梯"
// workcase.setDeviceCode(analysisResult.getString("deviceCode")); // 设备编号(如果用户提到)
// workcase.setEmergency(analysisResult.getString("emergency")); // 紧急程度normal/urgent/critical
// workcase.setRemark(analysisResult.getString("description")); // 问题描述摘要
// ============== 伪代码结束 ==============
// 模拟AI分析结果实际应由AI返回
TbWorkcaseDTO workcase = new TbWorkcaseDTO();
workcase.setType("设备故障");
workcase.setDevice("待用户确认");
workcase.setDeviceCode("");
workcase.setEmergency("normal");
workcase.setRemark("对话ID:" + chatId + " | " + buildConversationSummary(messages));
logger.info("对话分析完成,工单预填信息已生成: chatId={}", chatId);
return ResultDomain.success("对话分析完成", workcase);
}
private String buildConversationSummary(List<TbChatMessage> messages) {
StringBuilder summary = new StringBuilder();
for (TbChatMessage msg : messages) {
if ("user".equals(msg.getRole())) {
summary.append(msg.getContent()).append(" ");
}
}
String result = summary.toString().trim();
return result.length() > 200 ? result.substring(0, 200) + "..." : result;
}
@Override
public ResultDomain<TbWorkcaseDTO> summaryChat(String chatId) {
logger.info("总结对话: chatId={}", chatId);
TbChat filter = new TbChat();
filter.setChatId(chatId);
ResultDomain<TbChatMessage> msgResult = agentChatService.getChatMessageList(filter);
if (!msgResult.getSuccess() || msgResult.getDataList() == null) {
return ResultDomain.failure("获取对话消息失败");
}
List<TbChatMessage> messages = msgResult.getDataList();
// TODO: 调用AI进行对话总结提取关键词更新词云
// 伪代码:
// String summary = aiService.summarize(messages);
// List<String> keywords = aiService.extractKeywords(messages);
// updateWordCloud(keywords);
extractAndUpdateWordCloud(messages);
TbWorkcaseDTO summary = new TbWorkcaseDTO();
logger.info("对话总结完成: chatId={}", chatId);
return ResultDomain.success("对话总结完成", summary);
}
private void extractAndUpdateWordCloud(List<TbChatMessage> messages) {
// TODO: 调用AI提取关键词
// 伪代码List<String> keywords = aiService.extractKeywords(messages);
// 模拟提取的关键词
String[] mockKeywords = {"故障", "维修", "设备"};
String today = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
for (String keyword : mockKeywords) {
TbWordCloudDTO queryFilter = new TbWordCloudDTO();
queryFilter.setWord(keyword);
queryFilter.setCategory("fault");
queryFilter.setStatDate(today);
TbWordCloudDTO existing = wordCloudMapper.selectWordCloudOne(queryFilter);
if (existing != null) {
wordCloudMapper.incrementFrequency(existing.getWordId(), 1);
} else {
TbWordCloudDTO wordCloud = new TbWordCloudDTO();
wordCloud.setWordId(IdUtil.generateUUID());
wordCloud.setWord(keyword);
wordCloud.setCategory("fault");
wordCloud.setStatDate(today);
wordCloud.setFrequency("1");
wordCloudMapper.insertWordCloud(wordCloud);
}
}
}
// =============================== 词云管理 ==========================
@Override

View File

@@ -9,7 +9,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseDeviceDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseProcessDTO;
import org.xyzh.api.workcase.service.WorkcaseChatService;
import org.xyzh.api.workcase.service.WorkcaseService;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageDomain;
@@ -21,7 +20,6 @@ import org.xyzh.workcase.mapper.TbWorkcaseDeviceMapper;
import org.xyzh.workcase.mapper.TbWorkcaseMapper;
import org.xyzh.workcase.mapper.TbWorkcaseProcessMapper;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@DubboService(version = "1.0.0",group = "workcase",timeout = 30000,retries = 0)
@@ -37,9 +35,6 @@ public class WorkcaseServiceImpl implements WorkcaseService {
@Autowired
private TbWorkcaseDeviceMapper workcaseDeviceMapper;
@Autowired
private WorkcaseChatService workcaseChatService;
// ====================== 工单管理 ======================
@Override
@@ -103,11 +98,9 @@ public class WorkcaseServiceImpl implements WorkcaseService {
if ("done".equals(workcase.getStatus())) {
process.setAction(WorkcaseProcessAction.FINISH.getName());
process.setMessage("工单完成");
workcaseChatService.summaryChat(existing.getWorkcaseId());
} else if ("cancelled".equals(workcase.getStatus())) {
process.setAction(WorkcaseProcessAction.REPEAL.getName());
process.setMessage("工单撤销");
workcaseChatService.summaryChat(existing.getWorkcaseId());
} else {
process.setAction(WorkcaseProcessAction.INFO.getName());
process.setMessage("状态变更: " + oldStatus + " -> " + workcase.getStatus());
@@ -309,13 +302,11 @@ public class WorkcaseServiceImpl implements WorkcaseService {
workcase.setWorkcaseId(workcaseProcess.getWorkcaseId());
workcase.setStatus("done");
workcaseMapper.updateWorkcase(workcase);
workcaseChatService.summaryChat(workcaseProcess.getWorkcaseId());
} else if (WorkcaseProcessAction.REPEAL.getName().equals(action)) {
TbWorkcaseDTO workcase = new TbWorkcaseDTO();
workcase.setWorkcaseId(workcaseProcess.getWorkcaseId());
workcase.setStatus("cancelled");
workcaseMapper.updateWorkcase(workcase);
workcaseChatService.summaryChat(workcaseProcess.getWorkcaseId());
}
int rows = workcaseProcessMapper.insertWorkcaseProcess(workcaseProcess);