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

@@ -17,9 +17,10 @@ import org.xyzh.api.ai.dto.DifyFileInfo;
public class ChatRequest {
/**
* 输入变量
* 输入变量Dify API 必需字段)
*/
private Map<String, Object> inputs;
@JSONField(serializeFeatures = com.alibaba.fastjson2.JSONWriter.Feature.WriteMapNullValue)
private Map<String, Object> inputs = new java.util.HashMap<>();
/**
* 用户问题

View File

@@ -66,7 +66,7 @@ public class ChatController {
chat.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
chat.setUserType(true);
}
}
@@ -95,7 +95,7 @@ public class ChatController {
chat.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
chat.setUserType(true);
}
}
@@ -114,7 +114,7 @@ public class ChatController {
chat.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
chat.setUserType(true);
}
}
@@ -135,7 +135,7 @@ public class ChatController {
filter.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
filter.setUserType(true);
}
}
@@ -161,7 +161,7 @@ public class ChatController {
filter.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
filter.setUserType(true);
}
}
@@ -192,7 +192,7 @@ public class ChatController {
chatPrepareData.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
chatPrepareData.setUserType(true);
}
}
@@ -245,7 +245,7 @@ public class ChatController {
filter.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
filter.setUserType(true);
}
}
@@ -278,7 +278,7 @@ public class ChatController {
filter.setUserType(false);
if(NonUtils.isNotEmpty(token)){
LoginDomain loginDomain = LoginUtil.getCurrentLogin();
if (NonUtils.isNotEmpty(loginDomain)) {
if (NonUtils.isNotEmpty(loginDomain) && loginDomain.getUser().getStatus()!="guest") {
filter.setUserType(true);
}
}

View File

@@ -326,6 +326,7 @@ public class AgentChatServiceImpl implements AgentChatService {
chatRequest.setQuery(query);
chatRequest.setUser(userId);
chatRequest.setResponseMode("streaming");
chatRequest.setInputs(new HashMap<>()); // Dify API 要求 inputs 必传
if (filesData != null && !filesData.isEmpty()) {
chatRequest.setFiles(filesData);

View File

@@ -17,7 +17,7 @@ auth:
- /error
- /actuator/health
- /actuator/info
- /ai/chat/* # AI对话有非系统用户对话的接口无登录状态
- /ai/chat/** # AI对话有非系统用户对话的接口无登录状态
security:
aes:

View File

@@ -17,7 +17,7 @@ auth:
- /error
- /actuator/health
- /actuator/info
- /ai/chat/* # AI对话有非系统用户对话的接口无登录状态
- /ai/chat/** # AI对话有非系统用户对话的接口无登录状态
security:
aes:

View File

@@ -1,11 +1,6 @@
package org.xyzh.api.workcase.service;
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.TbWordCloudDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
@@ -18,85 +13,6 @@ import org.xyzh.common.core.page.PageRequest;
*/
public interface WorkcaseChatService {
// ========================= 聊天管理 ==========================
/**
* @description 来客创建聊天对话
* @param
* @author yslg
* @since 2025-12-18
*/
ResultDomain<TbChat> createChat(TbChat chat);
/**
* @description 更新聊天名称
* @param
* @author yslg
* @since 2025-12-18
*/
ResultDomain<TbChat> updateChat(TbChat chat);
/**
* 获取聊天列表
* @param agentId 智能体ID
* @return 聊天列表
*/
ResultDomain<TbChat> getChatList(TbChat filter);
// ========================= 聊天信息管理 ======================
/**
* 获取会话消息列表
* @param filter 会话过滤条件包含agentId, chatId, userId, userType
* @return 会话消息列表
*/
ResultDomain<TbChatMessage> getChatMessageList(TbChat filter);
// 用户转人工后,就不和智能体聊天了,在微信客服里聊天
/**
* 准备聊天数据POST传递复杂参数
* @param prepareData 对话准备数据包含agentId, chatId, query, files, userId, userType
* @return ResultDomain<String> 返回sessionId
*/
ResultDomain<String> prepareChatMessageSession(ChatPrepareData prepareData);
/**
* 流式对话SSE- 使用sessionId建立SSE连接 产生chatMessage
* @param sessionId 会话标识
* @return SseEmitter 流式推送对象
*/
SseEmitter streamChatMessageWithSse(String sessionId);
/**
* 停止对话生成通过Dify TaskID
* @param filter 会话过滤条件包含agentId, userId, userType
* @param taskId Dify任务ID
* @return 停止结果
*/
ResultDomain<Boolean> stopChatMessageByTaskId(TbChat filter, String taskId);
/**
* 评价
* @param filter 会话过滤条件包含agentId, chatId, userId, userType
* @param messageId 消息ID
* @param comment 评价
* @return 评价结果
*/
ResultDomain<Boolean> commentChatMessage(TbChat filter, String messageId, String comment);
// =============================== 对话分析 ==========================
/**
* 对话分析, 提取出工单相关的内容,这里有智能体调用等
* @param chatId 对话ID
* @return 对话分析结果
*/
ResultDomain<TbWorkcaseDTO> analyzeChat(String chatId);
// 对话总结
ResultDomain<TbWorkcaseDTO> summaryChat(String chatId);
// =============================== 对话、工单等词云管理 ==========================
/**

View File

@@ -178,6 +178,7 @@ public class GuestController {
// 3. 来客不存在,创建新来客
if (guest == null) {
TbGuestDTO newGuest = new TbGuestDTO();
newGuest.setOptsn(IdUtil.getOptsn());
newGuest.setUserId(IdUtil.generateID());
newGuest.setWechatId(loginParam.getWechatId());
newGuest.setPhone(loginParam.getPhone());

View File

@@ -13,6 +13,7 @@ import org.xyzh.common.core.page.PageDomain;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.dto.sys.TbGuestDTO;
import org.xyzh.common.dto.sys.TbSysUserRoleDTO;
import org.xyzh.common.utils.id.IdUtil;
import org.xyzh.system.mapper.user.TbGuestMapper;
import org.xyzh.system.mapper.user.TbSysUserRoleMapper;
@@ -48,6 +49,7 @@ public class GuestServiceImpl implements GuestService{
// 绑定访客角色(role_guest)
TbSysUserRoleDTO userRole = new TbSysUserRoleDTO();
userRole.setOptsn(IdUtil.getOptsn());
userRole.setUserId(guest.getUserId());
userRole.setRoleId("role_guest");
userRole.setDeptId("dept_root");

View File

@@ -224,12 +224,15 @@ public class SysUserServiceImpl implements SysUserService {
@Override
public ResultDomain<SysUserVO> getLoginUser(SysUserVO filter) {
// 登录查询语义与 getUser 相同(可根据用户名/手机号/邮箱查询)
// 登录查询语义与 getUser 相同(可根据用户名/手机号/邮箱/wechatId查询)
if(NonUtils.isNotNull(filter.getPhone())){
filter.setPhone(filter.getPhone());
}
SysUserVO userVO = userMapper.getUserByFilter(filter).get(0);
return ResultDomain.success("查询成功", userVO);
List<SysUserVO> list = userMapper.getUserByFilter(filter);
if (list == null || list.isEmpty()) {
return ResultDomain.failure("用户不存在");
}
return ResultDomain.success("查询成功", list.get(0));
}
@Override

View File

@@ -172,6 +172,9 @@
<if test="filter.username !=null and filter.username !=''">
AND ui.username = #{filter.username}
</if>
<if test="filter.wechatId !=null and filter.wechatId !=''">
AND u.wechat_id = #{filter.wechatId}
</if>
<!-- username / userType / deptPath 在表中不存在,按 SQL 为准移除相关条件 -->
AND (u.deleted IS NULL OR u.deleted = false)
</where>

View File

@@ -7,6 +7,7 @@
<!-- 用户角色关系字段 -->
<id column="user_id" property="userId" jdbcType="VARCHAR"/>
<id column="role_id" property="roleId" jdbcType="VARCHAR"/>
<id column="dept_id" property="deptId" jdbcType="VARCHAR"/>
<!-- 基础字段 -->
<result column="optsn" property="optsn" jdbcType="VARCHAR"/>
<result column="creator" property="creator" jdbcType="VARCHAR"/>
@@ -62,7 +63,7 @@
<!-- 基础列 -->
<sql id="Base_Column_List">
user_id, role_id,
user_id, role_id, dept_id,
optsn, creator, updater, dept_path, remark, create_time, update_time, delete_time, deleted
</sql>
@@ -73,6 +74,7 @@
<!-- 必填字段user_id, role_id, optsn -->
user_id,
role_id,
dept_id,
optsn,
<!-- 可选字段:基础字段按是否有值动态拼接 -->
<if test="creator != null and creator != ''">creator,</if>
@@ -88,6 +90,7 @@
<!-- 必填字段值 -->
#{userId},
#{roleId},
#{deptId},
#{optsn},
<!-- 可选字段值 -->
<if test="creator != null and creator != ''">#{creator},</if>

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);