工单模块

This commit is contained in:
2025-12-19 11:11:51 +08:00
parent 41cbe2bd54
commit 409e33abb6
49 changed files with 1934 additions and 323 deletions

View File

@@ -45,6 +45,14 @@ public interface GuestService {
*/
ResultDomain<TbGuestDTO> selectGuestOne(TbGuestDTO guest);
/**
* @description 根据微信id查询来客
* @param wechatId
* @author yslg
* @since 2025-12-18
*/
ResultDomain<TbGuestDTO> selectGuestByWechatId(String wechatId);
/**
* @description 查询来客列表
* @param guest 来客信息

View File

@@ -9,7 +9,7 @@
<version>1.0.0</version>
</parent>
<groupId>org.xyzh</groupId>
<groupId>org.xyzh.apis</groupId>
<artifactId>api-workcase</artifactId>
<version>1.0.0</version>
@@ -18,4 +18,10 @@
<maven.compiler.target>21</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.xyzh.apis</groupId>
<artifactId>api-ai</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,7 +0,0 @@
package org.xyzh;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@@ -1,9 +0,0 @@
package org.xyzh.api.workcase;
/**
* 工单服务接口
* 用于客服工单管理
*/
public interface WorkcaseService {
}

View File

@@ -1,71 +0,0 @@
package org.xyzh.api.workcase.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Date;
import java.util.List;
/**
* 会话DTO
* 用于创建和更新会话
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "会话DTO")
public class TbConversationDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "会话ID更新时需要")
private String conversationId;
@Schema(description = "客户ID")
private String customerId;
@Schema(description = "会话类型ai-AI客服/human-人工客服/transfer-转接", defaultValue = "ai")
private String conversationType;
@Schema(description = "渠道wechat-微信/web-网页/app-应用/phone-电话", defaultValue = "wechat")
private String channel;
@Schema(description = "智能体ID或客服人员ID")
private String agentId;
@Schema(description = "座席类型ai-AI/human-人工", defaultValue = "ai")
private String agentType;
@Schema(description = "会话开始时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date sessionStartTime;
@Schema(description = "会话结束时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date sessionEndTime;
@Schema(description = "会话时长(秒)")
private Integer durationSeconds;
@Schema(description = "消息数量", defaultValue = "0")
private Integer messageCount;
@Schema(description = "会话状态active-进行中/closed-已结束/transferred-已转接/timeout-超时", defaultValue = "active")
private String conversationStatus;
@Schema(description = "满意度评分1-5星")
private Integer satisfactionRating;
@Schema(description = "满意度反馈")
private String satisfactionFeedback;
@Schema(description = "会话摘要AI生成")
private String summary;
@Schema(description = "会话标签")
private List<String> tags;
@Schema(description = "会话元数据")
private JsonNode metadata;
}

View File

@@ -1,91 +0,0 @@
package org.xyzh.api.workcase.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.alibaba.fastjson2.annotation.JSONField;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 客户信息DTO
* 用于创建和更新客户信息
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "客户信息DTO")
public class TbCustomerDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "客户ID更新时需要")
private String customerId;
@Schema(description = "客户编号")
private String customerNo;
@Schema(description = "客户姓名")
private String customerName;
@Schema(description = "客户类型individual-个人/enterprise-企业", defaultValue = "individual")
private String customerType;
@Schema(description = "公司名称")
private String companyName;
@Schema(description = "电话")
private String phone;
@Schema(description = "邮箱")
private String email;
@Schema(description = "微信OpenID")
private String wechatOpenid;
@Schema(description = "微信UnionID")
private String wechatUnionid;
@Schema(description = "头像URL")
private String avatar;
@Schema(description = "性别0-未知/1-男/2-女", defaultValue = "0")
private Integer gender;
@Schema(description = "地址")
private String address;
@Schema(description = "客户等级vip/important/normal/potential", defaultValue = "normal")
private String customerLevel;
@Schema(description = "客户来源wechat-微信/web-网站/phone-电话/referral-推荐")
private String customerSource;
@Schema(description = "客户标签数组")
private List<String> tags;
@Schema(description = "备注")
private String notes;
@Schema(description = "CRM系统客户ID")
private String crmCustomerId;
@Schema(description = "最后联系时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date lastContactTime;
@Schema(description = "咨询总次数", defaultValue = "0")
private Integer totalConsultations;
@Schema(description = "订单总数", defaultValue = "0")
private Integer totalOrders;
@Schema(description = "总消费金额", defaultValue = "0")
private BigDecimal totalAmount;
@Schema(description = "满意度评分1-5")
private BigDecimal satisfactionScore;
@Schema(description = "状态active-活跃/inactive-非活跃/blacklist-黑名单", defaultValue = "active")
private String status;
}

View File

@@ -1,103 +0,0 @@
package org.xyzh.api.workcase.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.Date;
import java.util.List;
/**
* 工单DTO
* 用于创建和更新工单
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "工单DTO")
public class TbTicketDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "工单ID更新时需要")
private String ticketId;
@Schema(description = "工单编号")
private String ticketNo;
@Schema(description = "客户ID")
private String customerId;
@Schema(description = "关联会话ID")
private String conversationId;
@Schema(description = "工单类型consultation-咨询/complaint-投诉/suggestion-建议/repair-维修/installation-安装/other-其他")
private String ticketType;
@Schema(description = "工单分类")
private String ticketCategory;
@Schema(description = "优先级urgent-紧急/high-高/normal-普通/low-低", defaultValue = "normal")
private String priority;
@Schema(description = "工单标题")
private String title;
@Schema(description = "问题描述")
private String description;
@Schema(description = "附件ID数组")
private List<String> attachments;
@Schema(description = "工单来源ai-AI生成/manual-人工创建/system-系统自动", defaultValue = "ai")
private String ticketSource;
@Schema(description = "分配给(处理人)")
private String assignedTo;
@Schema(description = "分配部门")
private String assignedDept;
@Schema(description = "工单状态pending-待处理/processing-处理中/resolved-已解决/closed-已关闭/cancelled-已取消", defaultValue = "pending")
private String ticketStatus;
@Schema(description = "解决方案")
private String resolution;
@Schema(description = "解决时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date resolutionTime;
@Schema(description = "关闭时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date closeTime;
@Schema(description = "首次响应时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date responseTime;
@Schema(description = "SLA截止时间", format = "date-time")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date slaDeadline;
@Schema(description = "是否逾期", defaultValue = "false")
private Boolean isOverdue;
@Schema(description = "客户评分1-5星")
private Integer customerRating;
@Schema(description = "客户反馈")
private String customerFeedback;
@Schema(description = "CRM系统工单ID")
private String crmTicketId;
@Schema(description = "同步状态pending-待同步/synced-已同步/failed-失败", defaultValue = "pending")
private String syncStatus;
@Schema(description = "工单标签")
private List<String> tags;
@Schema(description = "工单元数据")
private JsonNode metadata;
}

View File

@@ -0,0 +1,28 @@
package org.xyzh.api.workcase.dto;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "词云表对象")
public class TbWordCloudDTO extends BaseDTO{
private static final long serialVersionUID = 1L;
@Schema(description = "词条ID")
private String wordId;
@Schema(description = "词语")
private String word;
@Schema(description = "词频")
private String frequency;
@Schema(description = "分类")
private String category;
@Schema(description = "统计日期")
private String statDate;
}

View File

@@ -0,0 +1,55 @@
package org.xyzh.api.workcase.dto;
import java.util.List;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @description 工单表数据对象DTO
* @filename TbWorkcaseDTO.java
* @author yslg
* @copyright xyzh
* @since 2025-12-18
*/
@Data
@Schema(description = "工单表对象")
public class TbWorkcaseDTO extends BaseDTO{
private static final long serialVersionUID = 1L;
@Schema(description = "工单ID")
private String workcaseId;
@Schema(description = "来客ID")
private String userId;
@Schema(description = "来客姓名")
private String username;
@Schema(description = "来客电话")
private String phone;
@Schema(description = "故障类型")
private String type;
@Schema(description = "设备名称")
private String device;
@Schema(description = "设备代码")
private String deviceCode;
@Schema(description = "工单图片列表")
private List<String> imgs;
@Schema(description = "紧急程度 normal-普通 emergency-紧急")
private String emergency;
@Schema(description = "状态 pending-待处理 processing-处理中 done-已完成")
private String status;
@Schema(description = "处理人ID")
private String processor;
}

View File

@@ -0,0 +1,38 @@
package org.xyzh.api.workcase.dto;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @description 工单设备涉及的文件DTO
* @filename TbWorkcaseDeviceDTO.java
* @author yslg
* @copyright xyzh
* @since 2025-12-18
*/
@Data
@Schema(description = "工单设备涉及的文件DTO")
public class TbWorkcaseDeviceDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "工单ID")
private String workcaseId;
@Schema(description = "设备名称")
private String device;
@Schema(description = "设备代码")
private String deviceCode;
@Schema(description = "文件ID")
private String fileId;
@Schema(description = "文件名")
private String fileName;
@Schema(description = "文件根ID")
private String fileRootId;
}

View File

@@ -0,0 +1,40 @@
package org.xyzh.api.workcase.dto;
import java.util.List;
import org.xyzh.common.dto.BaseDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @description 工单过程表DTO
* @filename TbWorkcaseProcessDTO.java
* @author yslg
* @copyright xyzh
* @since 2025-12-18
*/
@Data
@Schema(description = "工单过程表DTO")
public class TbWorkcaseProcessDTO extends BaseDTO {
private static final long serialVersionUID = 1L;
@Schema(description = "工单ID")
private String workcaseId;
@Schema(description = "过程ID")
private String processId;
@Schema(description = "动作 info记录assign指派redeploy转派repeal撤销finish完成")
private String action;
@Schema(description = "消息")
private String message;
@Schema(description = "携带文件列表")
private List<String> files;
@Schema(description = "处理人(指派、转派专属)")
private String processor;
}

View File

@@ -0,0 +1,141 @@
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;
/**
* @description 客服聊天服务涉及agent回答客户和微信客服回答客户
* @filename WorkcaseChatService.java
* @author yslg
* @copyright xyzh
* @since 2025-12-18
*/
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 pageRequest
* @return 聊天分页
*/
ResultDomain<TbChat> getChatPage(PageRequest<TbChat> pageRequest);
// ========================= 聊天信息管理 ======================
/**
* 获取会话消息列表
* @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);
// =============================== 对话、工单等词云管理 ==========================
/**
* @description 添加词云
* @param wordCloud 词云对象
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWordCloudDTO> addWordCloud(TbWordCloudDTO wordCloud);
/**
* @description 更新词云
* @param wordCloud 词云对象
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWordCloudDTO> updateWordCloud(TbWordCloudDTO wordCloud);
/**
* @description 获取词云列表
* @param filter 词云过滤条件
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWordCloudDTO> getWordCloudList(TbWordCloudDTO filter);
/**
* @description 获取词云分页
* @param pageRequest 分页请求
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWordCloudDTO> getWordCloudPage(PageRequest<TbWordCloudDTO> pageRequest);
}

View File

@@ -0,0 +1,161 @@
package org.xyzh.api.workcase.service;
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseDeviceDTO;
import org.xyzh.api.workcase.dto.TbWorkcaseProcessDTO;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
import com.alibaba.fastjson2.JSON;
/**
* 工单服务接口
* 用于客服工单管理
*/
public interface WorkcaseService {
// ====================== 工单管理 ======================
/**
* @description 创建工单
* @param workcase
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDTO> createWorkcase(TbWorkcaseDTO workcase);
/**
* @description 更新工单
* @param workcase
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDTO> updateWorkcase(TbWorkcaseDTO workcase);
/**
* @description 删除工单
* @param workcase
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDTO> deleteWorkcase(TbWorkcaseDTO workcase);
/**
* @description 获取工单列表
* @param filter
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDTO> getWorkcaseList(TbWorkcaseDTO filter);
/**
* @description 获取工单分页
* @param pageRequest
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDTO> getWorkcasePage(PageRequest<TbWorkcaseDTO> pageRequest);
/**
* @description 获取工单详情
* @param workcaseId
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDTO> getWorkcaseById(String workcaseId);
// ====================== 同步到CRM和接收 ===================
/**
* @description 同步工单到CRM
* @param workcase
* @author yslg
* @since 2025-12-19
*/
ResultDomain<Void> syncWorkcaseToCrm(TbWorkcaseDTO workcase);
/**
* @description 接收CRM的工单处理结果
* @param json
* @author yslg
* @since 2025-12-19
*/
ResultDomain<Void> receiveWorkcaseFromCrm(JSON json);
// ====================== 工单处理过程 ======================
/**
* @description 创建工单处理过程
* @param workcaseProcess
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseProcessDTO> createWorkcaseProcess(TbWorkcaseProcessDTO workcaseProcess);
/**
* @description 更新工单处理过程
* @param workcaseProcess
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseProcessDTO> updateWorkcaseProcess(TbWorkcaseProcessDTO workcaseProcess);
/**
* @description 删除工单处理过程
* @param workcaseProcess
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseProcessDTO> deleteWorkcaseProcess(TbWorkcaseProcessDTO workcaseProcess);
/**
* @description 获取工单处理过程列表
* @param filter
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseProcessDTO> getWorkcaseProcessList(TbWorkcaseProcessDTO filter);
/**
* @description 获取工单处理过程分页
* @param pageRequest
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseProcessDTO> getWorkcaseProcessPage(PageRequest<TbWorkcaseProcessDTO> pageRequest);
// ====================== 工单设备管理 ======================
/**
* @description 创建工单设备
* @param workcaseDevice
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDeviceDTO> createWorkcaseDevice(TbWorkcaseDeviceDTO workcaseDevice);
/**
* @description 更新工单设备
* @param workcaseDevice
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDeviceDTO> updateWorkcaseDevice(TbWorkcaseDeviceDTO workcaseDevice);
/**
* @description 删除工单设备
* @param workcaseDevice
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDeviceDTO> deleteWorkcaseDevice(TbWorkcaseDeviceDTO workcaseDevice);
/**
* @description 获取工单设备列表
* @param filter
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDeviceDTO> getWorkcaseDeviceList(TbWorkcaseDeviceDTO filter);
/**
* @description 获取工单设备分页
* @param pageRequest
* @author yslg
* @since 2025-12-19
*/
ResultDomain<TbWorkcaseDeviceDTO> getWorkcaseDevicePage(PageRequest<TbWorkcaseDeviceDTO> pageRequest);
}