From 409e33abb6f8b618b8459bf06c95da35a78a2cb9 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Fri, 19 Dec 2025 11:11:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E5=8D=95=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- .../database/postgres/sql/createTableAI.sql | 1 + .../postgres/sql/createTableWorkcase.sql | 74 ++++++- urbanLifelineServ/ai/pom.xml | 4 + .../resources/mapper/TbChatMessageMapper.xml | 4 +- .../xyzh/api/system/service/GuestService.java | 8 + urbanLifelineServ/apis/api-workcase/pom.xml | 8 +- .../src/main/java/org/xyzh/Main.java | 7 - .../xyzh/api/workcase/WorkcaseService.java | 9 - .../api/workcase/dto/TbConversationDTO.java | 71 ------- .../xyzh/api/workcase/dto/TbCustomerDTO.java | 91 --------- .../xyzh/api/workcase/dto/TbTicketDTO.java | 103 ---------- .../xyzh/api/workcase/dto/TbWordCloudDTO.java | 28 +++ .../xyzh/api/workcase/dto/TbWorkcaseDTO.java | 55 +++++ .../api/workcase/dto/TbWorkcaseDeviceDTO.java | 38 ++++ .../workcase/dto/TbWorkcaseProcessDTO.java | 40 ++++ .../workcase/service/WorkcaseChatService.java | 141 +++++++++++++ .../api/workcase/service/WorkcaseService.java | 161 +++++++++++++++ urbanLifelineServ/common/common-all/pom.xml | 4 + .../java/org/xyzh/common/dto/BaseDTO.java | 17 ++ .../java/org/xyzh/common/dto/OrderField.java | 22 ++ .../org/xyzh/common/dto/sys/TbSysUserDTO.java | 3 - urbanLifelineServ/common/common-jdbc/pom.xml | 44 ++++ .../jdbc/config}/TypeHandlerConfig.java | 4 +- .../handler}/EncryptedStringTypeHandler.java | 3 +- .../jdbc/handler/EnumNameTypeHandler.java | 103 ++++++++++ .../jdbc/handler}/FastJson2TypeHandler.java | 5 +- .../jdbc/handler}/StringArrayTypeHandler.java | 4 +- urbanLifelineServ/common/common-utils/pom.xml | 10 - urbanLifelineServ/common/pom.xml | 6 + urbanLifelineServ/pom.xml | 21 ++ .../system/controller/GuestController.java | 7 + .../system/mapper/user/TbGuestMapper.java | 8 + .../system/service/impl/GuestServiceImpl.java | 22 +- .../mapper/config/TbSysConfigMapper.xml | 8 +- .../resources/mapper/user/TbGuestMapper.xml | 9 +- urbanLifelineServ/workcase/pom.xml | 42 ++++ .../xyzh/workcase/config/KnowledgeInit.java | 168 +++++++++++++++ .../workcase/enums/WorkcaseProcessAction.java | 27 +++ .../mapper/TbWorkcaseDeviceMapper.java | 55 +++++ .../workcase/mapper/TbWorkcaseMapper.java | 55 +++++ .../mapper/TbWorkcaseProcessMapper.java | 55 +++++ .../service/WorkcaseChatServiceImpl.java | 113 ++++++++++ .../workcase/service/WorkcaseServiceImpl.java | 128 ++++++++++++ .../mapper/TbWorkcaseDeviceMapper.xml | 131 ++++++++++++ .../resources/mapper/TbWorkcaseMapper.xml | 193 ++++++++++++++++++ .../mapper/TbWorkcaseProcessMapper.xml | 130 ++++++++++++ urbanLifelineServ/workcase/工单流程.md | 12 ++ urbanLifelineServ/知识库逻辑变更.md | 2 - 49 files changed, 1934 insertions(+), 323 deletions(-) delete mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/Main.java delete mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/WorkcaseService.java delete mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbConversationDTO.java delete mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbCustomerDTO.java delete mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbTicketDTO.java create mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWordCloudDTO.java create mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDTO.java create mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDeviceDTO.java create mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseProcessDTO.java create mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseChatService.java create mode 100644 urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseService.java create mode 100644 urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/OrderField.java create mode 100644 urbanLifelineServ/common/common-jdbc/pom.xml rename urbanLifelineServ/common/{common-utils/src/main/java/org/xyzh/common/utils/crypto => common-jdbc/src/main/java/org/xyzh/common/jdbc/config}/TypeHandlerConfig.java (88%) rename urbanLifelineServ/common/{common-utils/src/main/java/org/xyzh/common/utils/crypto => common-jdbc/src/main/java/org/xyzh/common/jdbc/handler}/EncryptedStringTypeHandler.java (96%) create mode 100644 urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EnumNameTypeHandler.java rename urbanLifelineServ/common/{common-utils/src/main/java/org/xyzh/common/utils/json => common-jdbc/src/main/java/org/xyzh/common/jdbc/handler}/FastJson2TypeHandler.java (93%) rename urbanLifelineServ/common/{common-utils/src/main/java/org/xyzh/common/utils/json => common-jdbc/src/main/java/org/xyzh/common/jdbc/handler}/StringArrayTypeHandler.java (97%) create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/KnowledgeInit.java create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/enums/WorkcaseProcessAction.java create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/mapper/TbWorkcaseDeviceMapper.java create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/mapper/TbWorkcaseMapper.java create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/mapper/TbWorkcaseProcessMapper.java create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseChatServiceImpl.java create mode 100644 urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/service/WorkcaseServiceImpl.java create mode 100644 urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseDeviceMapper.xml create mode 100644 urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseMapper.xml create mode 100644 urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseProcessMapper.xml create mode 100644 urbanLifelineServ/workcase/工单流程.md delete mode 100644 urbanLifelineServ/知识库逻辑变更.md diff --git a/.gitignore b/.gitignore index 12ab2e8a..52a3d422 100644 --- a/.gitignore +++ b/.gitignore @@ -199,4 +199,5 @@ cython_debug/ # PyPI configuration file .pypirc -江西城市生命线-可交互原型/frontend/node_modules/* \ No newline at end of file +江西城市生命线-可交互原型/frontend/node_modules/* +THAI-Platform/* \ No newline at end of file diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql index 6e5c68a0..38df93d9 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableAI.sql @@ -33,6 +33,7 @@ CREATE TABLE ai.tb_chat( user_id VARCHAR(50) NOT NULL, -- 用户ID user_type BOOLEAN NOT NULL DEFAULT true, -- 用户类型 true-系统内部人员 false-系统外部人员 title VARCHAR(500) NOT NULL, -- 对话标题 + channel VARCHAR(50) DEFAULT 'agent' -- 对话渠道 agent、wechat create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 diff --git a/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql b/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql index 6c7c7de8..6bc200d3 100644 --- a/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql +++ b/urbanLifelineServ/.bin/database/postgres/sql/createTableWorkcase.sql @@ -20,27 +20,93 @@ CREATE TABLE sys.tb_guest( ); --- 客服对话记录,客服对话消息,包含ai、员工回答和来客提问 --- 直接使用ai.tb_chat和ai.tb_chat_message,user_type为false,表示该对话为来客对话 --- 这里,需要把在微信客服上的聊天同步到ai.tb_chat_message +-- 客服对话记录,客服对话消息,包含ai、员工回答和来客提问,从ai.tb_chat同步ai对话时的数据 +-- 先是ai对话,后转人工 + + + -- 工单表 DROP TABLE IF EXISTS workcase.tb_workcase CASCADE; CREATE TABLE workcase.tb_workcase( optsn VARCHAR(50) NOT NULL, -- 流水号 workcase_id VARCHAR(50) NOT NULL, -- 工单ID + chat_id VARCHAR(50) NOT NULL, -- 对话ID user_id VARCHAR(50) NOT NULL, -- 来客ID username VARCHAR(200) NOT NULL, -- 来客姓名 phone VARCHAR(20) NOT NULL, -- 来客电话 type VARCHAR(50) NOT NULL, -- 故障类型 - device VARCHAR(50) NOT NULL, -- 设备型号 + device VARCHAR(50) NOT NULL, -- 设备名称 + device_code VARCHAR(50) NOT NULL, -- 设备代码 + imgs VARCHAR(50)[] DEFAULT '[]', -- 工单图片id emergency VARCHAR(50) NOT NULL DEFAULT 'normal', -- 紧急程度 normal-普通 emergency-紧急 status VARCHAR(50) NOT NULL DEFAULT 'pending', -- 状态 pending-待处理 processing-处理中 done-已完成 processor VARCHAR(50) DEFAULT NULL, -- 处理人 + creator VARCHAR(50) NOT NULL, -- 创建人 create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 delete_time TIMESTAMPTZ DEFAULT NULL, -- 删除时间 deleted BOOLEAN NOT NULL DEFAULT false, -- 是否删除 PRIMARY KEY (workcase_id), + UNIQUE (chat_id), UNIQUE (optsn) +); + +-- 工单处理过程表(包含工单流转) +DROP TABLE IF EXISTS workcase.tb_workcase_process CASCADE; +CREATE TABLE workcase.tb_workcase_process( + optsn VARCHAR(50) NOT NULL, -- 流水号 + workcase_id VARCHAR(50) NOT NULL, -- 工单ID + process_id VARCHAR(50) NOT NULL, -- 过程id + action VARCHAR(50) NOT NULL, -- 动作 info:记录,assign:指派,redeploy:转派,repeal:撤销,finish:完成 + message VARCHAR(200) DEFAULT NULL, -- 消息 + files VARCHAR(50)[] DEFAULT '[]', -- 携带文件 + processor VARCHAR(50) DEFAULT NULL, -- 处理人(指派、转派专属) + remark VARCHAR(500) DEFAULT NULL, -- 备注 + creator VARCHAR(50) NOT NULL, -- 过程发起人 + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + PRIMARY KEY (process_id) +); + +-- 工单设备涉及的文件表 +DROP TABLE IF EXISTS workcase.tb_workcase_device CASCADE; +CREATE TABLE workcase.tb_workcase_device( + optsn VARCHAR(50) NOT NULL, -- 流水号 + workcase_id VARCHAR(50) NOT NULL, -- 工单ID + device VARCHAR(50) NOT NULL, -- 设备名称 + device_code VARCHAR(50) DEFAULT NULL, -- 设备代码 + file_id VARCHAR(50) NOT NULL, -- 文件id + file_name VARCHAR(50) NOT NULL, -- 文件名 + file_root_id VARCHAR(50) DEFAULT NULL, -- 文件根id + PRIMARY KEY(workcase_id, file_id) +); + +-- 来客对话、工单过程中生成的词云表 +DROP TABLE IF EXISTS workcase.tb_word_cloud CASCADE; +CREATE TABLE workcase.tb_word_cloud( + optsn VARCHAR(50) NOT NULL, -- 流水号 + word_id VARCHAR(50) NOT NULL, -- 词条ID + word VARCHAR(100) NOT NULL, -- 词语 + frequency INTEGER NOT NULL DEFAULT 1, -- 词频 + source_type VARCHAR(20) NOT NULL, -- 来源类型 chat-聊天 workcase-工单 + source_id VARCHAR(50) DEFAULT NULL, -- 来源ID(chat_id/workcase_id,NULL表示全局统计) + category VARCHAR(50) DEFAULT NULL, -- 分类(如:故障类型、设备名称、情绪词等) + stat_date DATE NOT NULL, -- 统计日期(按天聚合) + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + PRIMARY KEY (word_id), + UNIQUE (word, source_type, source_id, stat_date) -- 同一天同一来源的词唯一 +); + +DROP TABLE IF EXISTS workcase.tb_word_cloud CASCADE; +CREATE TABLE workcase.tb_word_cloud( + word_id VARCHAR(50) NOT NULL, -- 词条ID + word VARCHAR(100) NOT NULL, -- 词语 + frequency INTEGER NOT NULL DEFAULT 1, -- 词频 + category VARCHAR(50) DEFAULT NULL, -- 分类:fault-故障类型 device-设备 emotion-情绪词 + stat_date DATE NOT NULL, -- 统计日期(按天聚合) + create_time TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间 + update_time TIMESTAMPTZ DEFAULT NULL, -- 更新时间 + PRIMARY KEY (word_id), + UNIQUE (word, category, stat_date) -- 同一天同一分类的词唯一 ); \ No newline at end of file diff --git a/urbanLifelineServ/ai/pom.xml b/urbanLifelineServ/ai/pom.xml index 370b25af..9dddd762 100644 --- a/urbanLifelineServ/ai/pom.xml +++ b/urbanLifelineServ/ai/pom.xml @@ -45,6 +45,10 @@ org.xyzh.common common-utils + + org.xyzh.common + common-jdbc + org.xyzh.common common-exception diff --git a/urbanLifelineServ/ai/src/main/resources/mapper/TbChatMessageMapper.xml b/urbanLifelineServ/ai/src/main/resources/mapper/TbChatMessageMapper.xml index 1ffdadaf..9edc64e1 100644 --- a/urbanLifelineServ/ai/src/main/resources/mapper/TbChatMessageMapper.xml +++ b/urbanLifelineServ/ai/src/main/resources/mapper/TbChatMessageMapper.xml @@ -9,7 +9,7 @@ - + @@ -31,7 +31,7 @@ ) VALUES ( #{optsn}, #{messageId}, #{chatId}, #{role}, #{content} , #{difyMessageId} - , #{files, typeHandler=org.xyzh.common.utils.json.StringArrayTypeHandler} + , #{files, typeHandler=org.xyzh.common.jdbc.handler.StringArrayTypeHandler} , #{comment} ) diff --git a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/GuestService.java b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/GuestService.java index c5fbf206..d4301160 100644 --- a/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/GuestService.java +++ b/urbanLifelineServ/apis/api-system/src/main/java/org/xyzh/api/system/service/GuestService.java @@ -45,6 +45,14 @@ public interface GuestService { */ ResultDomain selectGuestOne(TbGuestDTO guest); + /** + * @description 根据微信id查询来客 + * @param wechatId + * @author yslg + * @since 2025-12-18 + */ + ResultDomain selectGuestByWechatId(String wechatId); + /** * @description 查询来客列表 * @param guest 来客信息 diff --git a/urbanLifelineServ/apis/api-workcase/pom.xml b/urbanLifelineServ/apis/api-workcase/pom.xml index 5bcd75a9..d8c42550 100644 --- a/urbanLifelineServ/apis/api-workcase/pom.xml +++ b/urbanLifelineServ/apis/api-workcase/pom.xml @@ -9,7 +9,7 @@ 1.0.0 - org.xyzh + org.xyzh.apis api-workcase 1.0.0 @@ -18,4 +18,10 @@ 21 + + + org.xyzh.apis + api-ai + + \ No newline at end of file diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/Main.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/Main.java deleted file mode 100644 index f660b7a0..00000000 --- a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.xyzh; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world!"); - } -} \ No newline at end of file diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/WorkcaseService.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/WorkcaseService.java deleted file mode 100644 index f9dd5fc1..00000000 --- a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/WorkcaseService.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.xyzh.api.workcase; - -/** - * 工单服务接口 - * 用于客服工单管理 - */ -public interface WorkcaseService { - -} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbConversationDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbConversationDTO.java deleted file mode 100644 index c9c5d6e5..00000000 --- a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbConversationDTO.java +++ /dev/null @@ -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 tags; - - @Schema(description = "会话元数据") - private JsonNode metadata; -} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbCustomerDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbCustomerDTO.java deleted file mode 100644 index 76ebd840..00000000 --- a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbCustomerDTO.java +++ /dev/null @@ -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 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; -} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbTicketDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbTicketDTO.java deleted file mode 100644 index cce7ab72..00000000 --- a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbTicketDTO.java +++ /dev/null @@ -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 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 tags; - - @Schema(description = "工单元数据") - private JsonNode metadata; -} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWordCloudDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWordCloudDTO.java new file mode 100644 index 00000000..4e676176 --- /dev/null +++ b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWordCloudDTO.java @@ -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; + +} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDTO.java new file mode 100644 index 00000000..6f554d5a --- /dev/null +++ b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDTO.java @@ -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 imgs; + + @Schema(description = "紧急程度 normal-普通 emergency-紧急") + private String emergency; + + @Schema(description = "状态 pending-待处理 processing-处理中 done-已完成") + private String status; + + @Schema(description = "处理人ID") + private String processor; + +} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDeviceDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDeviceDTO.java new file mode 100644 index 00000000..8ef5930b --- /dev/null +++ b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseDeviceDTO.java @@ -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; + +} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseProcessDTO.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseProcessDTO.java new file mode 100644 index 00000000..c8b960c3 --- /dev/null +++ b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/dto/TbWorkcaseProcessDTO.java @@ -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 files; + + @Schema(description = "处理人(指派、转派专属)") + private String processor; + +} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseChatService.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseChatService.java new file mode 100644 index 00000000..84afce79 --- /dev/null +++ b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseChatService.java @@ -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 createChat(TbChat chat); + + /** + * @description 更新聊天名称 + * @param + * @author yslg + * @since 2025-12-18 + */ + ResultDomain updateChat(TbChat chat); + + /** + * 获取聊天列表 + * @param agentId 智能体ID + * @return 聊天列表 + */ + ResultDomain getChatList(TbChat filter); + + /** + * 获取聊天分页 + * @param pageRequest + * @return 聊天分页 + */ + ResultDomain getChatPage(PageRequest pageRequest); + + // ========================= 聊天信息管理 ====================== + + /** + * 获取会话消息列表 + * @param filter 会话过滤条件(包含agentId, chatId, userId, userType) + * @return 会话消息列表 + */ + ResultDomain getChatMessageList(TbChat filter); + + // 用户转人工后,就不和智能体聊天了,在微信客服里聊天 + /** + * 准备聊天数据(POST传递复杂参数) + * @param prepareData 对话准备数据(包含agentId, chatId, query, files, userId, userType) + * @return ResultDomain 返回sessionId + */ + ResultDomain 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 stopChatMessageByTaskId(TbChat filter, String taskId); + + /** + * 评价 + * @param filter 会话过滤条件(包含agentId, chatId, userId, userType) + * @param messageId 消息ID + * @param comment 评价 + * @return 评价结果 + */ + ResultDomain commentChatMessage(TbChat filter, String messageId, String comment); + + // =============================== 对话分析 ========================== + + /** + * 对话分析, 提取出工单相关的内容,这里有智能体调用等 + * @param chatId 对话ID + * @return 对话分析结果 + */ + ResultDomain analyzeChat(String chatId); + + // 对话总结 + ResultDomain summaryChat(String chatId); + + + + // =============================== 对话、工单等词云管理 ========================== + + /** + * @description 添加词云 + * @param wordCloud 词云对象 + * @author yslg + * @since 2025-12-19 + */ + ResultDomain addWordCloud(TbWordCloudDTO wordCloud); + + /** + * @description 更新词云 + * @param wordCloud 词云对象 + * @author yslg + * @since 2025-12-19 + */ + ResultDomain updateWordCloud(TbWordCloudDTO wordCloud); + + /** + * @description 获取词云列表 + * @param filter 词云过滤条件 + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWordCloudList(TbWordCloudDTO filter); + + /** + * @description 获取词云分页 + * @param pageRequest 分页请求 + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWordCloudPage(PageRequest pageRequest); + +} diff --git a/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseService.java b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseService.java new file mode 100644 index 00000000..868727c2 --- /dev/null +++ b/urbanLifelineServ/apis/api-workcase/src/main/java/org/xyzh/api/workcase/service/WorkcaseService.java @@ -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 createWorkcase(TbWorkcaseDTO workcase); + /** + * @description 更新工单 + * @param workcase + * @author yslg + * @since 2025-12-19 + */ + ResultDomain updateWorkcase(TbWorkcaseDTO workcase); + + /** + * @description 删除工单 + * @param workcase + * @author yslg + * @since 2025-12-19 + */ + ResultDomain deleteWorkcase(TbWorkcaseDTO workcase); + + /** + * @description 获取工单列表 + * @param filter + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcaseList(TbWorkcaseDTO filter); + + /** + * @description 获取工单分页 + * @param pageRequest + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcasePage(PageRequest pageRequest); + + /** + * @description 获取工单详情 + * @param workcaseId + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcaseById(String workcaseId); + + // ====================== 同步到CRM和接收 =================== + /** + * @description 同步工单到CRM + * @param workcase + * @author yslg + * @since 2025-12-19 + */ + ResultDomain syncWorkcaseToCrm(TbWorkcaseDTO workcase); + + /** + * @description 接收CRM的工单处理结果 + * @param json + * @author yslg + * @since 2025-12-19 + */ + ResultDomain receiveWorkcaseFromCrm(JSON json); + + // ====================== 工单处理过程 ====================== + /** + * @description 创建工单处理过程 + * @param workcaseProcess + * @author yslg + * @since 2025-12-19 + */ + ResultDomain createWorkcaseProcess(TbWorkcaseProcessDTO workcaseProcess); + + /** + * @description 更新工单处理过程 + * @param workcaseProcess + * @author yslg + * @since 2025-12-19 + */ + ResultDomain updateWorkcaseProcess(TbWorkcaseProcessDTO workcaseProcess); + + /** + * @description 删除工单处理过程 + * @param workcaseProcess + * @author yslg + * @since 2025-12-19 + */ + ResultDomain deleteWorkcaseProcess(TbWorkcaseProcessDTO workcaseProcess); + + /** + * @description 获取工单处理过程列表 + * @param filter + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcaseProcessList(TbWorkcaseProcessDTO filter); + + /** + * @description 获取工单处理过程分页 + * @param pageRequest + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcaseProcessPage(PageRequest pageRequest); + + // ====================== 工单设备管理 ====================== + /** + * @description 创建工单设备 + * @param workcaseDevice + * @author yslg + * @since 2025-12-19 + */ + ResultDomain createWorkcaseDevice(TbWorkcaseDeviceDTO workcaseDevice); + /** + * @description 更新工单设备 + * @param workcaseDevice + * @author yslg + * @since 2025-12-19 + */ + ResultDomain updateWorkcaseDevice(TbWorkcaseDeviceDTO workcaseDevice); + /** + * @description 删除工单设备 + * @param workcaseDevice + * @author yslg + * @since 2025-12-19 + */ + ResultDomain deleteWorkcaseDevice(TbWorkcaseDeviceDTO workcaseDevice); + /** + * @description 获取工单设备列表 + * @param filter + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcaseDeviceList(TbWorkcaseDeviceDTO filter); + /** + * @description 获取工单设备分页 + * @param pageRequest + * @author yslg + * @since 2025-12-19 + */ + ResultDomain getWorkcaseDevicePage(PageRequest pageRequest); + + +} diff --git a/urbanLifelineServ/common/common-all/pom.xml b/urbanLifelineServ/common/common-all/pom.xml index 1b1138d0..77a04afa 100644 --- a/urbanLifelineServ/common/common-all/pom.xml +++ b/urbanLifelineServ/common/common-all/pom.xml @@ -44,6 +44,10 @@ org.xyzh.common common-utils + + org.xyzh.common + common-jdbc + \ No newline at end of file diff --git a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/BaseDTO.java b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/BaseDTO.java index e8255364..65e207c8 100644 --- a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/BaseDTO.java +++ b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/BaseDTO.java @@ -2,6 +2,8 @@ package org.xyzh.common.dto; import java.io.Serializable; import java.util.Date; +import java.util.List; + import com.alibaba.fastjson2.annotation.JSONField; import io.swagger.v3.oas.annotations.media.Schema; @@ -41,4 +43,19 @@ public class BaseDTO implements Serializable { @Schema(description = "是否已删除", defaultValue = "false") private Boolean deleted = false; + + + // =============== 下方为筛选字段,非数据库字段 ================ + + @Schema(description = "数量限制") + private Integer limit; + + @Schema(description = "开始时间") + private Date startTime; + + @Schema(description = "结束时间") + private Date endTime; + + @Schema(description = "排序字段") + private List orderFields; } \ No newline at end of file diff --git a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/OrderField.java b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/OrderField.java new file mode 100644 index 00000000..a9fc928a --- /dev/null +++ b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/OrderField.java @@ -0,0 +1,22 @@ +package org.xyzh.common.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "排序方式对象") +public class OrderField { + public static final String ASC = "ASC"; + public static final String DESC = "DESC"; + + @Schema(description = "排序字段") + private String field; + + @Schema(description = "排序方式") + private String order; + + public OrderField(String field, String order) { + this.field = field; + this.order = order; + } +} \ No newline at end of file diff --git a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/sys/TbSysUserDTO.java b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/sys/TbSysUserDTO.java index c1d48c22..558621e8 100644 --- a/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/sys/TbSysUserDTO.java +++ b/urbanLifelineServ/common/common-dto/src/main/java/org/xyzh/common/dto/sys/TbSysUserDTO.java @@ -4,8 +4,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.xyzh.common.dto.BaseDTO; import org.xyzh.common.utils.crypto.AesEncryptUtil; -import org.xyzh.common.utils.crypto.EncryptedStringTypeHandler; -import com.baomidou.mybatisplus.annotation.TableField; import io.swagger.v3.oas.annotations.media.Schema; @@ -33,7 +31,6 @@ public class TbSysUserDTO extends BaseDTO { @Schema(description = "邮箱") private String email; - @TableField(typeHandler = EncryptedStringTypeHandler.class) @Schema(description = "手机(加密)") private String phone; diff --git a/urbanLifelineServ/common/common-jdbc/pom.xml b/urbanLifelineServ/common/common-jdbc/pom.xml new file mode 100644 index 00000000..9385ac12 --- /dev/null +++ b/urbanLifelineServ/common/common-jdbc/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + org.xyzh + common + 1.0.0 + + + org.xyzh.common + common-jdbc + ${urban-lifeline.version} + jar + JDBC相关工具:MyBatis类型处理器 + + + 21 + 21 + + + + + + org.mybatis + mybatis + provided + + + + + com.alibaba.fastjson2 + fastjson2-extension-spring6 + + + + + org.xyzh.common + common-utils + provided + + + diff --git a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/TypeHandlerConfig.java b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/config/TypeHandlerConfig.java similarity index 88% rename from urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/TypeHandlerConfig.java rename to urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/config/TypeHandlerConfig.java index e7ea3fc4..35dc0349 100644 --- a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/TypeHandlerConfig.java +++ b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/config/TypeHandlerConfig.java @@ -1,8 +1,10 @@ -package org.xyzh.common.utils.crypto; +package org.xyzh.common.jdbc.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Configuration; +import org.xyzh.common.jdbc.handler.EncryptedStringTypeHandler; +import org.xyzh.common.utils.crypto.AesEncryptUtil; import jakarta.annotation.PostConstruct; diff --git a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/EncryptedStringTypeHandler.java b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EncryptedStringTypeHandler.java similarity index 96% rename from urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/EncryptedStringTypeHandler.java rename to urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EncryptedStringTypeHandler.java index 17b9b4f3..60a9b3ed 100644 --- a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/crypto/EncryptedStringTypeHandler.java +++ b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EncryptedStringTypeHandler.java @@ -1,8 +1,9 @@ -package org.xyzh.common.utils.crypto; +package org.xyzh.common.jdbc.handler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedTypes; +import org.xyzh.common.utils.crypto.AesEncryptUtil; import java.sql.CallableStatement; import java.sql.PreparedStatement; diff --git a/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EnumNameTypeHandler.java b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EnumNameTypeHandler.java new file mode 100644 index 00000000..fd8226ec --- /dev/null +++ b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/EnumNameTypeHandler.java @@ -0,0 +1,103 @@ +package org.xyzh.common.jdbc.handler; + +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * @description 通用枚举类型处理器,支持枚举类中定义 getName() 方法 + * 将枚举的 name 属性(自定义字符串)存储到数据库 + * @filename EnumNameTypeHandler.java + * @author yslg + * @copyright xyzh + * @since 2025-12-18 + */ +public class EnumNameTypeHandler> extends BaseTypeHandler { + + private final Class type; + + public EnumNameTypeHandler(Class type) { + if (type == null) { + throw new IllegalArgumentException("Type argument cannot be null"); + } + this.type = type; + } + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException { + String value = getEnumName(parameter); + ps.setString(i, value); + } + + @Override + public E getNullableResult(ResultSet rs, String columnName) throws SQLException { + String name = rs.getString(columnName); + return fromName(name); + } + + @Override + public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + String name = rs.getString(columnIndex); + return fromName(name); + } + + @Override + public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + String name = cs.getString(columnIndex); + return fromName(name); + } + + /** + * 获取枚举的 name 属性值 + * 优先调用 getName() 方法,如果不存在则使用 name() + */ + private String getEnumName(E enumValue) { + try { + java.lang.reflect.Method getNameMethod = type.getMethod("getName"); + return (String) getNameMethod.invoke(enumValue); + } catch (NoSuchMethodException e) { + return enumValue.name(); + } catch (Exception e) { + return enumValue.name(); + } + } + + /** + * 根据 name 属性值查找枚举 + * 优先匹配 getName(),如果不存在则匹配 name() + */ + private E fromName(String name) { + if (name == null) { + return null; + } + E[] enumConstants = type.getEnumConstants(); + + // 先尝试匹配 getName() + try { + java.lang.reflect.Method getNameMethod = type.getMethod("getName"); + for (E enumConstant : enumConstants) { + String enumName = (String) getNameMethod.invoke(enumConstant); + if (name.equals(enumName)) { + return enumConstant; + } + } + } catch (NoSuchMethodException e) { + // 没有 getName 方法,使用 name() + } catch (Exception e) { + // 其他异常,使用 name() + } + + // 回退到 name() 匹配 + for (E enumConstant : enumConstants) { + if (name.equals(enumConstant.name())) { + return enumConstant; + } + } + + return null; + } +} diff --git a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/json/FastJson2TypeHandler.java b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/FastJson2TypeHandler.java similarity index 93% rename from urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/json/FastJson2TypeHandler.java rename to urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/FastJson2TypeHandler.java index 70801d89..b30ad91c 100644 --- a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/json/FastJson2TypeHandler.java +++ b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/FastJson2TypeHandler.java @@ -1,4 +1,4 @@ -package org.xyzh.common.utils.json; +package org.xyzh.common.jdbc.handler; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; @@ -15,7 +15,7 @@ import java.sql.SQLException; * @description FastJSON2 JSONObject 类型处理器 * @filename FastJson2TypeHandler.java * @author yslg - * @copyright yslg + * @copyright xyzh * @since 2025-12-09 */ @MappedTypes({JSONObject.class}) @@ -51,7 +51,6 @@ public class FastJson2TypeHandler extends BaseTypeHandler { try { return JSON.parseObject(jsonString); } catch (Exception e) { - // 如果解析失败,返回一个空的JSONObject return new JSONObject(); } } diff --git a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/json/StringArrayTypeHandler.java b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/StringArrayTypeHandler.java similarity index 97% rename from urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/json/StringArrayTypeHandler.java rename to urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/StringArrayTypeHandler.java index e868aaa9..f6659759 100644 --- a/urbanLifelineServ/common/common-utils/src/main/java/org/xyzh/common/utils/json/StringArrayTypeHandler.java +++ b/urbanLifelineServ/common/common-jdbc/src/main/java/org/xyzh/common/jdbc/handler/StringArrayTypeHandler.java @@ -1,4 +1,4 @@ -package org.xyzh.common.utils.json; +package org.xyzh.common.jdbc.handler; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; @@ -16,7 +16,7 @@ import java.util.List; * @description PostgreSQL VARCHAR数组类型处理器 * @filename StringArrayTypeHandler.java * @author yslg - * @copyright yslg + * @copyright xyzh * @since 2025-12-15 */ @MappedTypes({List.class}) diff --git a/urbanLifelineServ/common/common-utils/pom.xml b/urbanLifelineServ/common/common-utils/pom.xml index 9536c302..ad51b7d6 100644 --- a/urbanLifelineServ/common/common-utils/pom.xml +++ b/urbanLifelineServ/common/common-utils/pom.xml @@ -30,16 +30,6 @@ poi-ooxml - - - org.mybatis - mybatis - provided - - - com.baomidou - mybatis-plus-boot-starter - org.springframework.boot diff --git a/urbanLifelineServ/common/pom.xml b/urbanLifelineServ/common/pom.xml index 6561405e..8bd0be38 100644 --- a/urbanLifelineServ/common/pom.xml +++ b/urbanLifelineServ/common/pom.xml @@ -19,6 +19,7 @@ common-auth common-redis common-utils + common-jdbc common-all common-exception @@ -65,6 +66,11 @@ common-exception ${urban-lifeline.version} + + org.xyzh.common + common-jdbc + ${urban-lifeline.version} + \ No newline at end of file diff --git a/urbanLifelineServ/pom.xml b/urbanLifelineServ/pom.xml index 1facc96d..cd9ed4da 100644 --- a/urbanLifelineServ/pom.xml +++ b/urbanLifelineServ/pom.xml @@ -195,6 +195,12 @@ + + + com.baomidou + mybatis-plus-annotation + ${mybatis.plus.version} + org.mybatis @@ -247,6 +253,16 @@ api-log ${urban-lifeline.version} + + org.xyzh.apis + api-ai + ${urban-lifeline.version} + + + org.xyzh.apis + api-workcase + ${urban-lifeline.version} + @@ -279,6 +295,11 @@ common-exception ${urban-lifeline.version} + + org.xyzh.common + common-jdbc + ${urban-lifeline.version} + org.xyzh diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java index 17f791d9..d16ad6cd 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/GuestController.java @@ -6,6 +6,7 @@ import org.apache.dubbo.config.annotation.DubboReference; 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; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -16,6 +17,7 @@ import org.xyzh.common.core.page.PageRequest; import org.xyzh.common.dto.sys.TbGuestDTO; import org.xyzh.common.utils.validation.ValidationUtils; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; /** @@ -63,6 +65,11 @@ public class GuestController { return guestService.deleteGuest(userId); } + @GetMapping("/wechat/{wechatId}") + public ResultDomain selectGuestByWechat(@PathVariable("wechatId") @NotBlank String wechatId){ + return guestService.selectGuestByWechatId(wechatId); + } + @GetMapping("/list") public ResultDomain listGuest(TbGuestDTO filter) { diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/mapper/user/TbGuestMapper.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/mapper/user/TbGuestMapper.java index ae979b84..79b36b4c 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/mapper/user/TbGuestMapper.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/mapper/user/TbGuestMapper.java @@ -50,6 +50,14 @@ public interface TbGuestMapper extends BaseMapper{ */ TbGuestDTO selectGuestOne(TbGuestDTO guest); + /** + * @description 根据微信id查询来客 + * @param wechatId + * @author yslg + * @since 2025-12-18 + */ + TbGuestDTO selectGuestByWechatId(String wechatId); + /** * @description 查询来客列表 * @param guest 来客信息 diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/service/impl/GuestServiceImpl.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/service/impl/GuestServiceImpl.java index d62b503e..50fcf4f9 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/service/impl/GuestServiceImpl.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/service/impl/GuestServiceImpl.java @@ -49,21 +49,31 @@ public class GuestServiceImpl implements GuestService{ } @Override + @Transactional public ResultDomain deleteGuest(String userId) { - guestMapper.deleteGuest(userId); - return ResultDomain.success("删除成功"); + int rows = guestMapper.deleteGuest(userId); + if (rows > 0) { + return ResultDomain.success("删除成功"); + } + return ResultDomain.failure("删除失败,来客不存在"); } @Override public ResultDomain selectGuestList(TbGuestDTO guest) { - guestMapper.selectGuestList(guest); - return ResultDomain.success("查询成功",guest); + List list = guestMapper.selectGuestList(guest); + return ResultDomain.success("查询成功", list); } @Override public ResultDomain selectGuestOne(TbGuestDTO guest) { - guestMapper.selectGuestOne(guest); - return ResultDomain.success("查询成功",guest); + TbGuestDTO selected = guestMapper.selectGuestOne(guest); + return ResultDomain.success("查询成功",selected); + } + + @Override + public ResultDomain selectGuestByWechatId(String wechatId){ + TbGuestDTO selected = guestMapper.selectGuestByWechatId(wechatId); + return ResultDomain.success("查询成功", selected); } @Override diff --git a/urbanLifelineServ/system/src/main/resources/mapper/config/TbSysConfigMapper.xml b/urbanLifelineServ/system/src/main/resources/mapper/config/TbSysConfigMapper.xml index c4795284..cd938752 100644 --- a/urbanLifelineServ/system/src/main/resources/mapper/config/TbSysConfigMapper.xml +++ b/urbanLifelineServ/system/src/main/resources/mapper/config/TbSysConfigMapper.xml @@ -11,8 +11,8 @@ - - + + @@ -37,8 +37,8 @@ - - + + diff --git a/urbanLifelineServ/system/src/main/resources/mapper/user/TbGuestMapper.xml b/urbanLifelineServ/system/src/main/resources/mapper/user/TbGuestMapper.xml index 035602b2..b312b249 100644 --- a/urbanLifelineServ/system/src/main/resources/mapper/user/TbGuestMapper.xml +++ b/urbanLifelineServ/system/src/main/resources/mapper/user/TbGuestMapper.xml @@ -74,11 +74,16 @@ AND wechat_id = #{wechatId} - - + LIMIT 1 + + SELECT + FROM workcase.tb_workcase_device + WHERE workcase_id = #{workcaseId} AND file_id = #{fileId} + + + + + + + + + diff --git a/urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseMapper.xml b/urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseMapper.xml new file mode 100644 index 00000000..0c665aef --- /dev/null +++ b/urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseMapper.xml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + workcase_id, optsn, user_id, username, phone, type, device, device_code, imgs, + emergency, status, processor, creator, create_time, update_time, delete_time, deleted + + + + INSERT INTO workcase.tb_workcase ( + optsn, workcase_id, user_id, username, phone, type, device, device_code, creator + , imgs + , emergency + , status + , processor + ) VALUES ( + #{optsn}, #{workcaseId}, #{userId}, #{username}, #{phone}, #{type}, #{device}, #{deviceCode}, #{creator} + , #{imgs, typeHandler=org.xyzh.common.jdbc.handler.StringArrayTypeHandler} + , #{emergency} + , #{status} + , #{processor} + ) + + + + UPDATE workcase.tb_workcase + + user_id = #{userId}, + username = #{username}, + phone = #{phone}, + type = #{type}, + device = #{device}, + device_code = #{deviceCode}, + imgs = #{imgs, typeHandler=org.xyzh.common.jdbc.handler.StringArrayTypeHandler}, + emergency = #{emergency}, + status = #{status}, + processor = #{processor}, + update_time = now() + + WHERE workcase_id = #{workcaseId} AND deleted = false + + + + UPDATE workcase.tb_workcase + SET deleted = true, delete_time = now() + WHERE workcase_id = #{workcaseId} AND deleted = false + + + + + + + + + + + diff --git a/urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseProcessMapper.xml b/urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseProcessMapper.xml new file mode 100644 index 00000000..35016b3e --- /dev/null +++ b/urbanLifelineServ/workcase/src/main/resources/mapper/TbWorkcaseProcessMapper.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + process_id, optsn, workcase_id, action, message, files, processor, remark, creator, create_time + + + + INSERT INTO workcase.tb_workcase_process ( + optsn, workcase_id, process_id, action, creator + , message + , files + , processor + , remark + ) VALUES ( + #{optsn}, #{workcaseId}, #{processId}, #{action}, #{creator} + , #{message} + , #{files, typeHandler=org.xyzh.common.jdbc.handler.StringArrayTypeHandler} + , #{processor} + , #{remark} + ) + + + + UPDATE workcase.tb_workcase_process + + action = #{action}, + message = #{message}, + files = #{files, typeHandler=org.xyzh.common.jdbc.handler.StringArrayTypeHandler}, + processor = #{processor}, + remark = #{remark}, + + WHERE process_id = #{processId} + + + + DELETE FROM workcase.tb_workcase_process + WHERE process_id = #{processId} + + + + + + + + + + + diff --git a/urbanLifelineServ/workcase/工单流程.md b/urbanLifelineServ/workcase/工单流程.md new file mode 100644 index 00000000..83f4382b --- /dev/null +++ b/urbanLifelineServ/workcase/工单流程.md @@ -0,0 +1,12 @@ +# 小程序用户聊天和工单的产生逻辑 +接口实现方式: + 1. WorkcaseChatServiceImpl通过ai接口进行ai回复,对话人员是来客和ai + 2. 当连续3次ai聊天后,询问是否转人工 + 3. 用户触发转人工(可能是一开始,就手动触发,没有聊天记录),跳转到微信客服的功能服务 + 4. 用户跳转前,必须创建工单 + 5. 同步工单到CRM + 6. 将工单信息作为微信客服的欢迎语,进行放送,让来客和员工能看到工单的基本信息,从而实现让员工知道工单是哪一个 + + 7. 同步用户和员工在微信客服上的聊天记录,同步到tb_chat表里面,对话人员是来客和客服。(把ai替换成员工进行对话的续接) + 8. 员工自己更新工单状态,如果在CRM更新工单状态会触发receiveWorkcaseFromCrm,如果在本系统更新工单会触发工单同步到CRM + 9. 在工单是完成、撤销后,工单、对话进行总结,并更新词云 \ No newline at end of file diff --git a/urbanLifelineServ/知识库逻辑变更.md b/urbanLifelineServ/知识库逻辑变更.md deleted file mode 100644 index fe94540e..00000000 --- a/urbanLifelineServ/知识库逻辑变更.md +++ /dev/null @@ -1,2 +0,0 @@ -1. 不再自己构建知识库,使用dify的知识库 -2. 服务的知识库表,只对文件进行重新上传才产生版本,对知识库内文档分段等修改不生成新的版本