From a9a2fb5d688526690903349dad983f98a474ed7a Mon Sep 17 00:00:00 2001
From: yang <2119157836@qq.com>
Date: Mon, 3 Mar 2025 17:21:50 +0800
Subject: [PATCH] =?UTF-8?q?feat(communityCenter):=20ai=E7=AA=97=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../communityCenter/ChatController.java | 25 ++
.../src/main/resources/application-dev.yml | 271 ++++++++---------
.../src/main/resources/application-druid.yml | 276 +++++++++---------
mcwl-common/pom.xml | 6 +
.../com/mcwl/common/config/JacksonConfig.java | 18 ++
mcwl-communityCenter/pom.xml | 20 ++
.../config/WebSocketConfig.java | 25 ++
.../domain/DeepSeekRequest.java | 68 +++++
.../communityCenter/service/AIService.java | 10 +
.../service/CustomerService.java | 11 +
.../service/impl/AIServiceImpl.java | 84 ++++++
.../service/impl/CustomerServiceImpl.java | 32 ++
.../webSocket/ChatWebSocketPoint.java | 92 ++++++
.../framework/config/ResourcesConfig.java | 23 ++
14 files changed, 692 insertions(+), 269 deletions(-)
create mode 100644 mcwl-admin/src/main/java/com/mcwl/web/controller/communityCenter/ChatController.java
create mode 100644 mcwl-common/src/main/java/com/mcwl/common/config/JacksonConfig.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/config/WebSocketConfig.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/domain/DeepSeekRequest.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/AIService.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/CustomerService.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/AIServiceImpl.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/CustomerServiceImpl.java
create mode 100644 mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/webSocket/ChatWebSocketPoint.java
diff --git a/mcwl-admin/src/main/java/com/mcwl/web/controller/communityCenter/ChatController.java b/mcwl-admin/src/main/java/com/mcwl/web/controller/communityCenter/ChatController.java
new file mode 100644
index 0000000..13a63b0
--- /dev/null
+++ b/mcwl-admin/src/main/java/com/mcwl/web/controller/communityCenter/ChatController.java
@@ -0,0 +1,25 @@
+package com.mcwl.web.controller.communityCenter;
+
+import com.mcwl.communityCenter.webSocket.ChatWebSocketPoint;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/chat")
+public class ChatController {
+
+ private final ChatWebSocketPoint chatWebSocketPoint;
+
+ @GetMapping("/switchUserMode")
+ public void switchUserMode(String sessionId, Boolean isCustomer) throws Exception {
+ if (isCustomer == null) {
+ chatWebSocketPoint.switchUserMode(sessionId, false);
+ } else {
+ chatWebSocketPoint.switchUserMode(sessionId, isCustomer);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/mcwl-admin/src/main/resources/application-dev.yml b/mcwl-admin/src/main/resources/application-dev.yml
index 36c3a82..64759e3 100644
--- a/mcwl-admin/src/main/resources/application-dev.yml
+++ b/mcwl-admin/src/main/resources/application-dev.yml
@@ -1,103 +1,112 @@
# 数据源配置
spring:
- #mq
- rabbitmq:
- host: 1.13.246.108
- port: 5672
- username: guest
- password: guest
- virtualHost: /
- listener:
- simple:
- prefetch: 1 # 每次之能获取一条
- acknowledge-mode: manual # 设置消费端手动ack确认
- retry:
- enabled: true # 是否支持重试
- # 生产者配置
- publisher-confirm-type: correlated #确认消息已发送到交换机(Exchange)
- publisher-returns: true #确认消息已发送到队列(Queue)
+ #mq
+ rabbitmq:
+ host: 1.13.246.108
+ port: 5672
+ username: guest
+ password: guest
+ virtualHost: /
+ listener:
+ simple:
+ prefetch: 1 # 每次之能获取一条
+ acknowledge-mode: manual # 设置消费端手动ack确认
+ retry:
+ enabled: true # 是否支持重试
+ # 生产者配置
+ publisher-confirm-type: correlated #确认消息已发送到交换机(Exchange)
+ publisher-returns: true #确认消息已发送到队列(Queue)
- datasource:
- type: com.alibaba.druid.pool.DruidDataSource
- driverClassName: com.mysql.cj.jdbc.Driver
- druid:
- # 主库数据源
- master:
- url: jdbc:mysql://1.13.246.108:3306/mcwl?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
- username: root
- password: ybl123456@
- # 从库数据源
- slave:
- # 从数据源开关/默认关闭
- enabled: false
- url:
- username:
- password:
- # 初始连接数
- initialSize: 5
- # 最小连接池数量
- minIdle: 10
- # 最大连接池数量
- maxActive: 20
- # 配置获取连接等待超时的时间
- maxWait: 60000
- # 配置连接超时时间
- connectTimeout: 30000
- # 配置网络超时时间
- socketTimeout: 60000
- # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
- timeBetweenEvictionRunsMillis: 60000
- # 配置一个连接在池中最小生存的时间,单位是毫秒
- minEvictableIdleTimeMillis: 300000
- # 配置一个连接在池中最大生存的时间,单位是毫秒
- maxEvictableIdleTimeMillis: 900000
- # 配置检测连接是否有效
- validationQuery: SELECT 1 FROM DUAL
- testWhileIdle: true
- testOnBorrow: false
- testOnReturn: false
- webStatFilter:
- enabled: true
- statViewServlet:
- enabled: true
- # 设置白名单,不填则允许所有访问
- allow:
- url-pattern: /druid/*
- # 控制台管理用户名和密码
- login-username: mcwl
- login-password: 123456
- filter:
- stat:
- enabled: true
- # 慢SQL记录
- log-slow-sql: true
- slow-sql-millis: 1000
- merge-sql: true
- wall:
- config:
- multi-statement-allow: true
- # redis 配置
- redis:
- # 地址
- host: 1.13.246.108
- # 端口,默认为6379
- port: 6370
- # 数据库索引
- database: 0
- # 密码
- password: MuYu_Cloud@Redis
- # 连接超时时间
- timeout: 10s
- lettuce:
- pool:
- # 连接池中的最小空闲连接
- min-idle: 0
- # 连接池中的最大空闲连接
- max-idle: 8
- # 连接池的最大数据库连接数
- max-active: 8
- # #连接池最大阻塞等待时间(使用负值表示没有限制)
- max-wait: -1ms
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ druid:
+ # 主库数据源
+ master:
+ url: jdbc:mysql://1.13.246.108:3306/mcwl?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ username: root
+ password: ybl123456@
+ # 从库数据源
+ slave:
+ # 从数据源开关/默认关闭
+ enabled: false
+ url:
+ username:
+ password:
+ # 初始连接数
+ initialSize: 5
+ # 最小连接池数量
+ minIdle: 10
+ # 最大连接池数量
+ maxActive: 20
+ # 配置获取连接等待超时的时间
+ maxWait: 60000
+ # 配置连接超时时间
+ connectTimeout: 30000
+ # 配置网络超时时间
+ socketTimeout: 60000
+ # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+ timeBetweenEvictionRunsMillis: 60000
+ # 配置一个连接在池中最小生存的时间,单位是毫秒
+ minEvictableIdleTimeMillis: 300000
+ # 配置一个连接在池中最大生存的时间,单位是毫秒
+ maxEvictableIdleTimeMillis: 900000
+ # 配置检测连接是否有效
+ validationQuery: SELECT 1 FROM DUAL
+ testWhileIdle: true
+ testOnBorrow: false
+ testOnReturn: false
+ webStatFilter:
+ enabled: true
+ statViewServlet:
+ enabled: true
+ # 设置白名单,不填则允许所有访问
+ allow:
+ url-pattern: /druid/*
+ # 控制台管理用户名和密码
+ login-username: mcwl
+ login-password: 123456
+ filter:
+ stat:
+ enabled: true
+ # 慢SQL记录
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ # redis 配置
+ redis:
+ # 地址
+ host: 1.13.246.108
+ # 端口,默认为6379
+ port: 6370
+ # 数据库索引
+ database: 0
+ # 密码
+ password: MuYu_Cloud@Redis
+ # 连接超时时间
+ timeout: 10s
+ lettuce:
+ pool:
+ # 连接池中的最小空闲连接
+ min-idle: 0
+ # 连接池中的最大空闲连接
+ max-idle: 8
+ # 连接池的最大数据库连接数
+ max-active: 8
+ # #连接池最大阻塞等待时间(使用负值表示没有限制)
+ max-wait: -1ms
+
+ #ai配置
+ ai:
+ dashscope:
+ base-url: https://api.deepseek.com/chat/completions
+ api-key: sk-5d1f611b6ba74b90ae9e3dff5aaa508a
+ chat:
+ options:
+ model: deepseek-chat
# token配置
@@ -111,47 +120,47 @@ token:
# 公众号配置
wechat:
- # 应用ID
- appid: wx82d4c3c96f0ffa5b
- # 应用密钥
- secret: abbabcf1da711a3bbd95387ec83edcac
+ # 应用ID
+ appid: wx82d4c3c96f0ffa5b
+ # 应用密钥
+ secret: abbabcf1da711a3bbd95387ec83edcac
# yml版(application.yml)
aliyun:
- oss:
- bucketName: ybl2112
- endpoint: oss-cn-beijing.aliyuncs.com
- accessKeyId: LTAI5tSHZZ8wHJRP8X4r9TXT
- accessKeySecret: F82IVNx0IGJ3AnP6gSIfcyql1HCXIH
- accessPre: https://ybl2112.oss-cn-beijing.aliyuncs.com/
+ oss:
+ bucketName: ybl2112
+ endpoint: oss-cn-beijing.aliyuncs.com
+ accessKeyId: LTAI5tSHZZ8wHJRP8X4r9TXT
+ accessKeySecret: F82IVNx0IGJ3AnP6gSIfcyql1HCXIH
+ accessPre: https://ybl2112.oss-cn-beijing.aliyuncs.com/
## 沙箱环境
mall:
- mgt:
- aliPayConfig:
- protocol: https
- gatewayHost: openapi-sandbox.dl.alipaydev.com
- signType: RSA2
- # 沙箱应用id
- appId: 9021000135682614
- # 沙箱应用私钥
- privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQxmQGcaiKjOhayWi+zNTvpp8B5YT8jFFkjLzrD+W+T2Dwf2GfFR4p95zsCJxYeoLWdghMPA6/GMFrLbuVFpaEjuTm4icqA9N8n5d3W0j7gh+wMjZoqyJclAIeb09ut7rY6mWzilA9kWmZnUG7MOWIU70RVRYrfJectCFw/odM9lG4XIVe13X2h+1ecTQyQzLWmnvKFCfo7dQjE7fIYiWfud1ZGUneNs3u73pNWMB6ThGTTCbs0atcgM3fYOg3q7fTxIu9VcaUCJiJ/kNbL9sVEyOrSyx2f2o6w06zdEaOiQFsuDeS8QPYGMg7pf42wAfqCO6hqxQiQT5vp1hvB0o1AgMBAAECggEAIhaEYLwMSispXo8D2cES9iaOU/z91hUX6Qv2Q4anuqqoEZh8nN91Db6etTjFz1NxURvTklelxTsH97t56n26DRY0MWTYgd0Kw9Iz8MeOpKGb4nnAM97vpUq4QQBGfLRIC2ENdzu+7vA5JBFR88hsky/cWaNmJ/EbJauIIDneE7GigMR2HF7kfzdZzOBN4ZEh/ef5NKeCnEieRJJhWRgrgNXVZ44Tqi67AM7ey9pyUtBe7fgzxXtrWXBN9yKaVxxSXm3KJXFQqA6mcilFVZaxMNlAySc4MPTW8lq0ozOCOCunoeIphNz/OVIxGu3/voXFXlBfOKqOkYMVZxMY6OrvtQKBgQD0nIlXK4VW72VaGpz9kxQkRNzJV/yqaqet1GOSlPM2l0RCRFOVVdnvbQdHGPe6+HxHL1dh5MP8T/aHoP+4UXkkQCc8moS2FZxJvFH2QTSZBcSSdGL7GMpROqs38J+XlJzrhNcB20lrW6D7yMeQa4YEcXwdbD8Er/YaIqODBWYYewKBgQCXg+16RLDArciwwhf0TBWZPor2iYFDdwU5UPu7CKOhU1MLfQhG85gGpXHjB6G8cMUi/ezxh/FEl+sWOZegpkPwL5/BQS9tNYWIaC4kipPF/a5Up4DMYUHVAuuPwNqqXpvgU+rGjCns0wtPRnjrkghLkc3oTSID7o7pzUwIk2whDwKBgAys3+EIfExY82OL5X6uVGjcuKQmTw11oWK8krxRw5iclgjpCXu/ix+BAtOIU634mlgF9/02oYE9k4TLrvSaJDDgsifNyfq1e/fGLmkYT+VuCxWbulVQn4s+AwlPCrYMGWWK6KlL9638fYcOjGjLaZJpXwkXRtyzUYlhKh/r87JpAoGBAIavRp2mi/xrPvgpQQPv0k9L8llfOCHRnjoqC+thrZsNp8eRmJcBmMVnskofEZ2iHQuS71pw/n58EQTLo0ayJbhPjVJL8K3CovXzrfjbmqqoa5xi3bJQTiXdF6rMw1QpD6Uk05E1LVuQ6v/IZFr7kBYlAQWb8z3NhQq+bPU+nyLvAoGAGpBbSM8gPzdWQqkHoos0icu3cj0GhN3MU7+1Eb/rsXyh/lk5wtZTEnHjwhdUOUtwVNjvrv7CzA7unhOoaM6YcE/Zpd4zt8pjqH1Mhds7UHf4Xg+A+J4G6meYnhSwfBpOub02ncsqfBlXE0qhFv6AvcMewWndyLb8EYaUUXTYkG0=
- # 沙箱应用公钥证书
- appCertPath: cert/appPublicCert.crt
- # 沙箱支付宝公钥证书路径
- alipayCertPath: cert/alipayPublicCert.crt
- # 沙箱支付宝根证书路径
- alipayRootCertPath: cert/alipayRootCert.crt
- notifyUrl: https://53a65908.r27.cpolar.top/ali/pay/notify
- # 沙箱支付宝网关
- gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
+ mgt:
+ aliPayConfig:
+ protocol: https
+ gatewayHost: openapi-sandbox.dl.alipaydev.com
+ signType: RSA2
+ # 沙箱应用id
+ appId: 9021000135682614
+ # 沙箱应用私钥
+ privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQxmQGcaiKjOhayWi+zNTvpp8B5YT8jFFkjLzrD+W+T2Dwf2GfFR4p95zsCJxYeoLWdghMPA6/GMFrLbuVFpaEjuTm4icqA9N8n5d3W0j7gh+wMjZoqyJclAIeb09ut7rY6mWzilA9kWmZnUG7MOWIU70RVRYrfJectCFw/odM9lG4XIVe13X2h+1ecTQyQzLWmnvKFCfo7dQjE7fIYiWfud1ZGUneNs3u73pNWMB6ThGTTCbs0atcgM3fYOg3q7fTxIu9VcaUCJiJ/kNbL9sVEyOrSyx2f2o6w06zdEaOiQFsuDeS8QPYGMg7pf42wAfqCO6hqxQiQT5vp1hvB0o1AgMBAAECggEAIhaEYLwMSispXo8D2cES9iaOU/z91hUX6Qv2Q4anuqqoEZh8nN91Db6etTjFz1NxURvTklelxTsH97t56n26DRY0MWTYgd0Kw9Iz8MeOpKGb4nnAM97vpUq4QQBGfLRIC2ENdzu+7vA5JBFR88hsky/cWaNmJ/EbJauIIDneE7GigMR2HF7kfzdZzOBN4ZEh/ef5NKeCnEieRJJhWRgrgNXVZ44Tqi67AM7ey9pyUtBe7fgzxXtrWXBN9yKaVxxSXm3KJXFQqA6mcilFVZaxMNlAySc4MPTW8lq0ozOCOCunoeIphNz/OVIxGu3/voXFXlBfOKqOkYMVZxMY6OrvtQKBgQD0nIlXK4VW72VaGpz9kxQkRNzJV/yqaqet1GOSlPM2l0RCRFOVVdnvbQdHGPe6+HxHL1dh5MP8T/aHoP+4UXkkQCc8moS2FZxJvFH2QTSZBcSSdGL7GMpROqs38J+XlJzrhNcB20lrW6D7yMeQa4YEcXwdbD8Er/YaIqODBWYYewKBgQCXg+16RLDArciwwhf0TBWZPor2iYFDdwU5UPu7CKOhU1MLfQhG85gGpXHjB6G8cMUi/ezxh/FEl+sWOZegpkPwL5/BQS9tNYWIaC4kipPF/a5Up4DMYUHVAuuPwNqqXpvgU+rGjCns0wtPRnjrkghLkc3oTSID7o7pzUwIk2whDwKBgAys3+EIfExY82OL5X6uVGjcuKQmTw11oWK8krxRw5iclgjpCXu/ix+BAtOIU634mlgF9/02oYE9k4TLrvSaJDDgsifNyfq1e/fGLmkYT+VuCxWbulVQn4s+AwlPCrYMGWWK6KlL9638fYcOjGjLaZJpXwkXRtyzUYlhKh/r87JpAoGBAIavRp2mi/xrPvgpQQPv0k9L8llfOCHRnjoqC+thrZsNp8eRmJcBmMVnskofEZ2iHQuS71pw/n58EQTLo0ayJbhPjVJL8K3CovXzrfjbmqqoa5xi3bJQTiXdF6rMw1QpD6Uk05E1LVuQ6v/IZFr7kBYlAQWb8z3NhQq+bPU+nyLvAoGAGpBbSM8gPzdWQqkHoos0icu3cj0GhN3MU7+1Eb/rsXyh/lk5wtZTEnHjwhdUOUtwVNjvrv7CzA7unhOoaM6YcE/Zpd4zt8pjqH1Mhds7UHf4Xg+A+J4G6meYnhSwfBpOub02ncsqfBlXE0qhFv6AvcMewWndyLb8EYaUUXTYkG0=
+ # 沙箱应用公钥证书
+ appCertPath: cert/appPublicCert.crt
+ # 沙箱支付宝公钥证书路径
+ alipayCertPath: cert/alipayPublicCert.crt
+ # 沙箱支付宝根证书路径
+ alipayRootCertPath: cert/alipayRootCert.crt
+ notifyUrl: https://53a65908.r27.cpolar.top/ali/pay/notify
+ # 沙箱支付宝网关
+ gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
huawei:
- obs:
- ak: NWXJ93POA9NCDVV1WQYJ
- sk: 3aBuzYdUgSsxdNnycD2mESnVwtcyjAKPxqewE79N
- bucketName: mcwl
- upload:
- endPoint: obs.cn-south-1.myhuaweicloud.com
+ obs:
+ ak: NWXJ93POA9NCDVV1WQYJ
+ sk: 3aBuzYdUgSsxdNnycD2mESnVwtcyjAKPxqewE79N
+ bucketName: mcwl
+ upload:
+ endPoint: obs.cn-south-1.myhuaweicloud.com
diff --git a/mcwl-admin/src/main/resources/application-druid.yml b/mcwl-admin/src/main/resources/application-druid.yml
index 2d1d4d6..897e6ec 100644
--- a/mcwl-admin/src/main/resources/application-druid.yml
+++ b/mcwl-admin/src/main/resources/application-druid.yml
@@ -1,158 +1,158 @@
# 数据源配置
spring:
- #mq
- rabbitmq:
- host: 113.45.74.175
- port: 5672
- username: guest
- password: guest
- virtualHost: /
- listener:
- simple:
- prefetch: 1 # 每次之能获取一条
- acknowledge-mode: manual # 设置消费端手动ack确认
- retry:
- enabled: true # 是否支持重试
- # 生产者配置
- publisher-confirm-type: correlated #确认消息已发送到交换机(Exchange)
- publisher-returns: true #确认消息已发送到队列(Queue)
+ #mq
+ rabbitmq:
+ host: 113.45.74.175
+ port: 5672
+ username: guest
+ password: guest
+ virtualHost: /
+ listener:
+ simple:
+ prefetch: 1 # 每次之能获取一条
+ acknowledge-mode: manual # 设置消费端手动ack确认
+ retry:
+ enabled: true # 是否支持重试
+ # 生产者配置
+ publisher-confirm-type: correlated #确认消息已发送到交换机(Exchange)
+ publisher-returns: true #确认消息已发送到队列(Queue)
- datasource:
- type: com.alibaba.druid.pool.DruidDataSource
- driverClassName: com.mysql.cj.jdbc.Driver
- druid:
- # 主库数据源
- master:
- url: jdbc:mysql://113.45.74.175:3306/mcwl?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
- username: root
- password: Leng1128
- # 从库数据源
- slave:
- # 从数据源开关/默认关闭
- enabled: false
- url:
- username:
- password:
- # 初始连接数
- initialSize: 5
- # 最小连接池数量
- minIdle: 10
- # 最大连接池数量
- maxActive: 20
- # 配置获取连接等待超时的时间
- maxWait: 60000
- # 配置连接超时时间
- connectTimeout: 30000
- # 配置网络超时时间
- socketTimeout: 60000
- # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
- timeBetweenEvictionRunsMillis: 60000
- # 配置一个连接在池中最小生存的时间,单位是毫秒
- minEvictableIdleTimeMillis: 300000
- # 配置一个连接在池中最大生存的时间,单位是毫秒
- maxEvictableIdleTimeMillis: 900000
- # 配置检测连接是否有效
- validationQuery: SELECT 1 FROM DUAL
- testWhileIdle: true
- testOnBorrow: false
- testOnReturn: false
- webStatFilter:
- enabled: true
- statViewServlet:
- enabled: true
- # 设置白名单,不填则允许所有访问
- allow:
- url-pattern: /druid/*
- # 控制台管理用户名和密码
- login-username: mcwl
- login-password: 123456
- filter:
- stat:
- enabled: true
- # 慢SQL记录
- log-slow-sql: true
- slow-sql-millis: 1000
- merge-sql: true
- wall:
- config:
- multi-statement-allow: true
- # redis 配置
- redis:
- # 地址
- host: 113.45.74.175
- # 端口,默认为6379
- port: 6370
- # 数据库索引
- database: 0
- # 密码
- password: MuYu_Cloud@Redis
- # 连接超时时间
- timeout: 10s
- lettuce:
- pool:
- # 连接池中的最小空闲连接
- min-idle: 0
- # 连接池中的最大空闲连接
- max-idle: 8
- # 连接池的最大数据库连接数
- max-active: 8
- # #连接池最大阻塞等待时间(使用负值表示没有限制)
- max-wait: -1ms
+ datasource:
+ type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ druid:
+ # 主库数据源
+ master:
+ url: jdbc:mysql://113.45.74.175:3306/mcwl?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+ username: root
+ password: Leng1128
+ # 从库数据源
+ slave:
+ # 从数据源开关/默认关闭
+ enabled: false
+ url:
+ username:
+ password:
+ # 初始连接数
+ initialSize: 5
+ # 最小连接池数量
+ minIdle: 10
+ # 最大连接池数量
+ maxActive: 20
+ # 配置获取连接等待超时的时间
+ maxWait: 60000
+ # 配置连接超时时间
+ connectTimeout: 30000
+ # 配置网络超时时间
+ socketTimeout: 60000
+ # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+ timeBetweenEvictionRunsMillis: 60000
+ # 配置一个连接在池中最小生存的时间,单位是毫秒
+ minEvictableIdleTimeMillis: 300000
+ # 配置一个连接在池中最大生存的时间,单位是毫秒
+ maxEvictableIdleTimeMillis: 900000
+ # 配置检测连接是否有效
+ validationQuery: SELECT 1 FROM DUAL
+ testWhileIdle: true
+ testOnBorrow: false
+ testOnReturn: false
+ webStatFilter:
+ enabled: true
+ statViewServlet:
+ enabled: true
+ # 设置白名单,不填则允许所有访问
+ allow:
+ url-pattern: /druid/*
+ # 控制台管理用户名和密码
+ login-username: mcwl
+ login-password: 123456
+ filter:
+ stat:
+ enabled: true
+ # 慢SQL记录
+ log-slow-sql: true
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ # redis 配置
+ redis:
+ # 地址
+ host: 113.45.74.175
+ # 端口,默认为6379
+ port: 6370
+ # 数据库索引
+ database: 0
+ # 密码
+ password: MuYu_Cloud@Redis
+ # 连接超时时间
+ timeout: 10s
+ lettuce:
+ pool:
+ # 连接池中的最小空闲连接
+ min-idle: 0
+ # 连接池中的最大空闲连接
+ max-idle: 8
+ # 连接池的最大数据库连接数
+ max-active: 8
+ # #连接池最大阻塞等待时间(使用负值表示没有限制)
+ max-wait: -1ms
# token配置
token:
- # 令牌自定义标识
- header: Authorization
- # 令牌密钥
- secret: abcdefghijklmnopqrstuvwxyz
- # 令牌有效期(默认30分钟)
- expireTime: 30
+ # 令牌自定义标识
+ header: Authorization
+ # 令牌密钥
+ secret: abcdefghijklmnopqrstuvwxyz
+ # 令牌有效期(默认30分钟)
+ expireTime: 30
# 公众号配置
wechat:
- # 应用ID
- appid: wx82d4c3c96f0ffa5b
- # 应用密钥
- secret: abbabcf1da711a3bbd95387ec83edcac
+ # 应用ID
+ appid: wx82d4c3c96f0ffa5b
+ # 应用密钥
+ secret: abbabcf1da711a3bbd95387ec83edcac
# yml版(application.yml)
aliyun:
- oss:
- bucketName: ybl2112
- endpoint: oss-cn-beijing.aliyuncs.com
- accessKeyId: LTAI5tSHZZ8wHJRP8X4r9TXT
- accessKeySecret: F82IVNx0IGJ3AnP6gSIfcyql1HCXIH
- accessPre: https://ybl2112.oss-cn-beijing.aliyuncs.com/
+ oss:
+ bucketName: ybl2112
+ endpoint: oss-cn-beijing.aliyuncs.com
+ accessKeyId: LTAI5tSHZZ8wHJRP8X4r9TXT
+ accessKeySecret: F82IVNx0IGJ3AnP6gSIfcyql1HCXIH
+ accessPre: https://ybl2112.oss-cn-beijing.aliyuncs.com/
# 线上环境
mall:
- mgt:
- aliPayConfig:
- protocol: https
- gatewayHost: openapi-sandbox.dl.alipaydev.com
- signType: RSA2
- # 线上应用id
- appId: 2021005119630093
- # 线上应用私钥
- privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQuhRU5iHoabfzJS40A+moMKzb5rmi5XbL59K98CW65Oq/rnzQnj6sCYggIXOLCLKUykZ1XIVa+4V7bGFkMDF0236ncPumnVY/tfCSebLyKl+up1mGeH4mAR6ily7nXAhRnXL+A0015bpCa5FCg2ReN68MEmquB/mHfeLqJfRupwzDWnIWxYSKkFCJ0i47KTFYhM93LHHQ8GrZNNaeiJ6q59MruQI83z0HCxN/DzK4Z2rBWGilwPuYcSEa0STPXYhz8XMXUX/Mlp7dd5zyWeNaxkNRWlhKHvhLVPGvteaEcchs476DIWLCRVXEC1372wMKNrKblzRp7Wi5+l+W14k5AgMBAAECggEAHahkPjmivTPc5FC8NSCQI01GPxH6/Ky1OXfaMd4ifTgn+vvQzBeBlFOnt53jRZyUq/T/l1FMaqacZfyBwLw3hlDslXeLuksHv6qTEBEsYH/ad7oHmIzcnQEhvAPrMrIjakYvqGoYynC3SKEudUjjqoMSthCYF+2+bsuXUpBQlNQLqcJ03U+YhfLswwVVujKvAujoLRqDAWeaa0k0y/1Ua4fr5GJq5M+P8TTBRgZlSK5A7Ee6yxMUncZ9W3lTqZtyG0OQLbz9gkr0c63KPisigaOx9eKlCPWFWUWo+zGvQMVijHDTeoJ3dqNmsPtiIE3rDt8anx4a1R7YK6MQ520hAQKBgQDPFMNl4fRmLSOLNShJm/0mBMoIjPmS3+tPBtCiG4oH9F8vYMJWDA1Im0FdgRa2HlCv6hpiVvvQt824RaUYa7XR6aefXKVEWddHvl3utnghMohjK8o8fTmIxADtU5Q2OlWmPdp1v7yDg8yE5P1GlY+AZ3oggxXOIRVtQrNeL7E3iQKBgQCy6nEaUQUCDDts4o7Pn+N402g3/T9Vz/5JtKmDI01EndK5l/yE8CdgGwdiwN1k3la/Nv8P5kaaX2qVcCGoOIQmMNGOxuNfkKCi+bJKdxmFOI4E3iVYT2H6x27QJwNB6bik1i6b2OWT7ckxeB6xMjefNp1u8Qif+vab60OQZSqoMQKBgESRF3Hwsz/xykcZvtFAuT2RcGQMacbcJcnw87v5ambf33SMkUx0iSF1ZttTFvYOa6ET3tCZBKBDe/Z7+QJxB68NstbtkjtjlAjJW8ji2jwDw20y3q/QtvA2Fih++CBMiHeXG3LJnd3eEiYevE5Wz0ExAhspzFqIUdPvtfyFxiQhAoGBAKgc+iGMN3RxIfVx/FbDGe6SVr8lSrnLMlj3VMBQD26GDVcupKwVLCp1uz7jkiQRdtk9R/UcnwK23WOFdVqnoCRygrXx0/wb3ZTFou2tc2Fmfqu8QML19E67zjfwMHNitYjNaAYwi6ewKvg8sjo1wWXs34k7GquYGNjw+w9Wv/pBAoGAUlVHfZ4RncAiiH/x3EDgqmmVikFpLoM2xXxM16nplrjdBE6IzkpgWb/x2ZI6JCMDyoV2kvF6owW2+QEQ81MrHQMtBRpAvd8nI03gXA5VF3uwp4UjfGpoIAGDBrqQv9DM8vx6C6VJNTT5esZWZSw/+PZ4D19l0/n7gICGx3ugw0Q=
- # 线上应用公钥证书
- appCertPath: appCertPublicKey_2021005119630093.crt
- # 线上支付宝公钥证书路径
- alipayCertPath: cert/alipayCertPublicKey_RSA2.crt
- # 线上支付宝根证书路径
- alipayRootCertPath: cert/alipayRootCert.crt
- # 线上支付宝公钥
- notifyUrl: https://253d7236.r27.cpolar.top/ali/pay/notify
- # 线上支付宝网关
- gatewayUrl: https://openapi.alipay.com/gateway.do
+ mgt:
+ aliPayConfig:
+ protocol: https
+ gatewayHost: openapi-sandbox.dl.alipaydev.com
+ signType: RSA2
+ # 线上应用id
+ appId: 2021005119630093
+ # 线上应用私钥
+ privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCQuhRU5iHoabfzJS40A+moMKzb5rmi5XbL59K98CW65Oq/rnzQnj6sCYggIXOLCLKUykZ1XIVa+4V7bGFkMDF0236ncPumnVY/tfCSebLyKl+up1mGeH4mAR6ily7nXAhRnXL+A0015bpCa5FCg2ReN68MEmquB/mHfeLqJfRupwzDWnIWxYSKkFCJ0i47KTFYhM93LHHQ8GrZNNaeiJ6q59MruQI83z0HCxN/DzK4Z2rBWGilwPuYcSEa0STPXYhz8XMXUX/Mlp7dd5zyWeNaxkNRWlhKHvhLVPGvteaEcchs476DIWLCRVXEC1372wMKNrKblzRp7Wi5+l+W14k5AgMBAAECggEAHahkPjmivTPc5FC8NSCQI01GPxH6/Ky1OXfaMd4ifTgn+vvQzBeBlFOnt53jRZyUq/T/l1FMaqacZfyBwLw3hlDslXeLuksHv6qTEBEsYH/ad7oHmIzcnQEhvAPrMrIjakYvqGoYynC3SKEudUjjqoMSthCYF+2+bsuXUpBQlNQLqcJ03U+YhfLswwVVujKvAujoLRqDAWeaa0k0y/1Ua4fr5GJq5M+P8TTBRgZlSK5A7Ee6yxMUncZ9W3lTqZtyG0OQLbz9gkr0c63KPisigaOx9eKlCPWFWUWo+zGvQMVijHDTeoJ3dqNmsPtiIE3rDt8anx4a1R7YK6MQ520hAQKBgQDPFMNl4fRmLSOLNShJm/0mBMoIjPmS3+tPBtCiG4oH9F8vYMJWDA1Im0FdgRa2HlCv6hpiVvvQt824RaUYa7XR6aefXKVEWddHvl3utnghMohjK8o8fTmIxADtU5Q2OlWmPdp1v7yDg8yE5P1GlY+AZ3oggxXOIRVtQrNeL7E3iQKBgQCy6nEaUQUCDDts4o7Pn+N402g3/T9Vz/5JtKmDI01EndK5l/yE8CdgGwdiwN1k3la/Nv8P5kaaX2qVcCGoOIQmMNGOxuNfkKCi+bJKdxmFOI4E3iVYT2H6x27QJwNB6bik1i6b2OWT7ckxeB6xMjefNp1u8Qif+vab60OQZSqoMQKBgESRF3Hwsz/xykcZvtFAuT2RcGQMacbcJcnw87v5ambf33SMkUx0iSF1ZttTFvYOa6ET3tCZBKBDe/Z7+QJxB68NstbtkjtjlAjJW8ji2jwDw20y3q/QtvA2Fih++CBMiHeXG3LJnd3eEiYevE5Wz0ExAhspzFqIUdPvtfyFxiQhAoGBAKgc+iGMN3RxIfVx/FbDGe6SVr8lSrnLMlj3VMBQD26GDVcupKwVLCp1uz7jkiQRdtk9R/UcnwK23WOFdVqnoCRygrXx0/wb3ZTFou2tc2Fmfqu8QML19E67zjfwMHNitYjNaAYwi6ewKvg8sjo1wWXs34k7GquYGNjw+w9Wv/pBAoGAUlVHfZ4RncAiiH/x3EDgqmmVikFpLoM2xXxM16nplrjdBE6IzkpgWb/x2ZI6JCMDyoV2kvF6owW2+QEQ81MrHQMtBRpAvd8nI03gXA5VF3uwp4UjfGpoIAGDBrqQv9DM8vx6C6VJNTT5esZWZSw/+PZ4D19l0/n7gICGx3ugw0Q=
+ # 线上应用公钥证书
+ appCertPath: appCertPublicKey_2021005119630093.crt
+ # 线上支付宝公钥证书路径
+ alipayCertPath: cert/alipayCertPublicKey_RSA2.crt
+ # 线上支付宝根证书路径
+ alipayRootCertPath: cert/alipayRootCert.crt
+ # 线上支付宝公钥
+ notifyUrl: https://253d7236.r27.cpolar.top/ali/pay/notify
+ # 线上支付宝网关
+ gatewayUrl: https://openapi.alipay.com/gateway.do
huawei:
- obs:
- ak: NWXJ93POA9NCDVV1WQYJ
- sk: 3aBuzYdUgSsxdNnycD2mESnVwtcyjAKPxqewE79N
- bucketName: mcwl
- upload:
- endPoint: obs.cn-south-1.myhuaweicloud.com
+ obs:
+ ak: NWXJ93POA9NCDVV1WQYJ
+ sk: 3aBuzYdUgSsxdNnycD2mESnVwtcyjAKPxqewE79N
+ bucketName: mcwl
+ upload:
+ endPoint: obs.cn-south-1.myhuaweicloud.com
diff --git a/mcwl-common/pom.xml b/mcwl-common/pom.xml
index 0d16b32..a953e81 100644
--- a/mcwl-common/pom.xml
+++ b/mcwl-common/pom.xml
@@ -402,6 +402,12 @@
3.19.7
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
diff --git a/mcwl-common/src/main/java/com/mcwl/common/config/JacksonConfig.java b/mcwl-common/src/main/java/com/mcwl/common/config/JacksonConfig.java
new file mode 100644
index 0000000..3bedc10
--- /dev/null
+++ b/mcwl-common/src/main/java/com/mcwl/common/config/JacksonConfig.java
@@ -0,0 +1,18 @@
+package com.mcwl.common.config;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class JacksonConfig {
+
+ @Bean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ }
+}
\ No newline at end of file
diff --git a/mcwl-communityCenter/pom.xml b/mcwl-communityCenter/pom.xml
index a630525..9a80243 100644
--- a/mcwl-communityCenter/pom.xml
+++ b/mcwl-communityCenter/pom.xml
@@ -31,6 +31,26 @@
com.mcwl
mcwl-system
+
+
+
+
+
+
+
+
+ com.alibaba.cloud.ai
+ spring-ai-alibaba-starter
+ 1.0.0-M5.1
+
+
+
+ io.projectreactor.netty
+ reactor-netty-http
+ 1.1.6
+
+
+
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/config/WebSocketConfig.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/config/WebSocketConfig.java
new file mode 100644
index 0000000..192b4a1
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/config/WebSocketConfig.java
@@ -0,0 +1,25 @@
+package com.mcwl.communityCenter.config;
+
+import com.mcwl.communityCenter.webSocket.ChatWebSocketPoint;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+@Configuration
+@EnableWebSocket
+public class WebSocketConfig implements WebSocketConfigurer {
+
+ @Override
+ public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
+ registry.addHandler(chatWebSocketPoint(), "/chat")
+ .setAllowedOrigins("*");
+ }
+
+ @Bean
+ public ChatWebSocketPoint chatWebSocketPoint() {
+ return new ChatWebSocketPoint();
+ }
+
+}
\ No newline at end of file
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/domain/DeepSeekRequest.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/domain/DeepSeekRequest.java
new file mode 100644
index 0000000..15b8152
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/domain/DeepSeekRequest.java
@@ -0,0 +1,68 @@
+package com.mcwl.communityCenter.domain;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DeepSeek API请求参数封装类
+ */
+public class DeepSeekRequest {
+ private String model;
+ private List messages;
+ @JsonProperty("max_tokens")
+ private Integer maxTokens;
+ private Double temperature;
+ private Boolean stream;
+
+ // 构造方法
+ public DeepSeekRequest() {
+ this.messages = new ArrayList<>();
+ }
+
+ // 添加消息的便捷方法
+ public void addMessage(String role, String content) {
+ if (this.messages == null) {
+ this.messages = new ArrayList<>();
+ }
+ this.messages.add(new Message(role, content));
+ }
+
+
+
+ // Getters and Setters
+ // 注意:必须包含所有需要序列化的字段的getter方法
+
+ public static class Message {
+ private String role;
+ private String content;
+
+ public Message(String role, String content) {
+ this.role = role;
+ this.content = content;
+ }
+
+ // Getters
+ public String getRole() { return role; }
+ public String getContent() { return content; }
+ }
+
+ // 以下是各字段的getter/setter
+ public String getModel() { return model; }
+ public void setModel(String model) { this.model = model; }
+
+ public List getMessages() { return messages; }
+ public void setMessages(List messages) { this.messages = messages; }
+
+ public Integer getMaxTokens() { return maxTokens; }
+ public void setMaxTokens(Integer maxTokens) { this.maxTokens = maxTokens; }
+
+ public Double getTemperature() { return temperature; }
+ public void setTemperature(Double temperature) { this.temperature = temperature; }
+ public Boolean getStream() {
+ return this.stream;
+ }
+ public void setStream(boolean stream) {
+ this.stream = stream;
+ }
+}
\ No newline at end of file
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/AIService.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/AIService.java
new file mode 100644
index 0000000..4069449
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/AIService.java
@@ -0,0 +1,10 @@
+package com.mcwl.communityCenter.service;
+
+import reactor.core.publisher.Flux;
+
+public interface AIService {
+
+ Flux getDeepSeekResponseStream(String message);
+
+
+}
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/CustomerService.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/CustomerService.java
new file mode 100644
index 0000000..5115fab
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/CustomerService.java
@@ -0,0 +1,11 @@
+package com.mcwl.communityCenter.service;
+
+import org.springframework.web.socket.WebSocketSession;
+
+import java.io.IOException;
+
+public interface CustomerService {
+ void transferToHuman(String userId, WebSocketSession session);
+
+ void handleCustomerMessage(String userId, String message) throws IOException;
+}
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/AIServiceImpl.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/AIServiceImpl.java
new file mode 100644
index 0000000..594e430
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/AIServiceImpl.java
@@ -0,0 +1,84 @@
+package com.mcwl.communityCenter.service.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.mcwl.common.utils.StringUtils;
+import com.mcwl.communityCenter.domain.DeepSeekRequest;
+import com.mcwl.communityCenter.service.AIService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.core.publisher.SignalType;
+
+import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+@Service
+@RequiredArgsConstructor
+public class AIServiceImpl implements AIService {
+
+ @Value("${spring.ai.dashscope.base-url}")
+ private String DEEPSEEK_API_URL;
+ @Value("${spring.ai.dashscope.api-key}")
+ private String API_KEY;
+ @Value("${spring.ai.dashscope.chat.options.model}")
+ private String apiModel;
+
+
+ private final ObjectMapper objectMapper;
+
+ @Override
+ public Flux getDeepSeekResponseStream(String message) {
+ WebClient client = WebClient.builder()
+ .baseUrl(DEEPSEEK_API_URL)
+ .defaultHeader("Authorization", "Bearer " + API_KEY)
+ .build();
+
+ // 构建请求体(推荐使用对象映射)
+ DeepSeekRequest request = new DeepSeekRequest();
+ request.setModel(apiModel);
+ // 添加对话历史
+ request.addMessage("user", message);
+ request.setMaxTokens(500);
+ request.setTemperature(0.7);
+ request.setStream(true);
+
+ return client.post()
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(request)
+ .retrieve()
+ .bodyToFlux(String.class) // 原始数据流
+ .takeUntil(data -> data.contains("[DONE]")) // 遇到结束标记停止
+ .flatMap(json -> parseContentFromJson(json)) // 解析内容
+ .onErrorResume(e -> Flux.just("服务暂时不可用"));// 错误处理
+
+ }
+
+
+ // 辅助方法:从 JSON 中提取 content
+ private Mono parseContentFromJson(String json) {
+ try {
+ JsonNode root = objectMapper.readTree(json);
+ String reasoning_content = root.path("choices")
+ .get(0)
+ .path("delta")
+ .path("reasoning_content")
+ .asText("");
+ String content = root.path("choices")
+ .get(0)
+ .path("delta")
+ .path("content")
+ .asText("");
+ System.out.print(StringUtils.isNotEmpty(reasoning_content) ? reasoning_content : content);
+ return Mono.just(StringUtils.isNotEmpty(reasoning_content) ? reasoning_content : content);
+ } catch (JsonProcessingException e) {
+ return Mono.error(e);
+ }
+ }
+
+}
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/CustomerServiceImpl.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/CustomerServiceImpl.java
new file mode 100644
index 0000000..49fa65c
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/service/impl/CustomerServiceImpl.java
@@ -0,0 +1,32 @@
+package com.mcwl.communityCenter.service.impl;
+
+import com.mcwl.communityCenter.service.CustomerService;
+import org.springframework.stereotype.Service;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+public class CustomerServiceImpl implements CustomerService {
+
+ private static final Map activeSessions = new ConcurrentHashMap<>();
+
+ @Override
+ public void transferToHuman(String userId, WebSocketSession session) {
+ activeSessions.put(userId, session);
+ // 这里可以添加通知客服人员的逻辑
+ }
+
+ @Override
+ public void handleCustomerMessage(String userId, String message) throws IOException {
+ // 应实现消息队列或持久化存储
+ // 先返回固定响应
+ WebSocketSession session = activeSessions.get(userId);
+ if (session != null && session.isOpen()) {
+ session.sendMessage(new TextMessage("[客服] 您好,当前为人工服务模式"));
+ }
+ }
+}
diff --git a/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/webSocket/ChatWebSocketPoint.java b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/webSocket/ChatWebSocketPoint.java
new file mode 100644
index 0000000..adfd9bb
--- /dev/null
+++ b/mcwl-communityCenter/src/main/java/com/mcwl/communityCenter/webSocket/ChatWebSocketPoint.java
@@ -0,0 +1,92 @@
+package com.mcwl.communityCenter.webSocket;
+
+import com.mcwl.communityCenter.service.AIService;
+import com.mcwl.communityCenter.service.CustomerService;
+import lombok.NoArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.AbstractWebSocketHandler;
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@ServerEndpoint("/chat")
+@NoArgsConstructor
+public class ChatWebSocketPoint extends AbstractWebSocketHandler {
+ private final Map userModes = new ConcurrentHashMap<>();
+
+ // 存储会话与订阅的映射关系
+ private final Map sessionSubscriptions = new ConcurrentHashMap<>();
+
+ @Autowired
+ private AIService aiService;
+
+ @Autowired
+ private CustomerService customerService;
+
+ // 构造函数注入服务...
+
+ @Override
+ public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+ String userId = session.getId();
+ String userMessage = message.getPayload();
+ // 判断当前模式
+ if (userModes.getOrDefault(userId, false)) {
+ // 人工模式
+ customerService.handleCustomerMessage(userId, userMessage);
+ } else {
+ // AI 流式响应模式
+ Flux responseStream = aiService.getDeepSeekResponseStream(userMessage);
+
+ // 订阅响应流并存储 Disposable
+ Disposable disposable = responseStream
+ .doOnNext(chunk -> sendText(session, chunk))
+ .doOnComplete(() -> sendText(session, "[END]"))
+ .doOnError(e -> sendText(session, "[ERROR] " + e.getMessage()))
+ .subscribe();
+
+ sessionSubscriptions.put(userId, disposable);
+ }
+ }
+
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) throws Exception {
+ super.afterConnectionEstablished(session);
+ userModes.put(session.getId(), false);
+ }
+
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
+ // 清理订阅资源
+ String sessionId = session.getId();
+ Disposable disposable = sessionSubscriptions.remove(sessionId);
+ if (disposable != null && !disposable.isDisposed()) {
+ disposable.dispose();
+ System.out.println("已清理会话资源: " + sessionId);
+ }
+ }
+
+ // 添加模式切换方法(根据业务需求)
+ public void switchUserMode(String sessionId, boolean isHumanMode) {
+ userModes.put(sessionId, isHumanMode);
+ }
+
+ // 线程安全的发送方法
+ private void sendText(WebSocketSession session, String text) {
+ try {
+ if (session.isOpen()) {
+ synchronized (session) { // WebSocketSession 非线程安全
+ session.sendMessage(new TextMessage(text));
+ }
+ }
+ } catch (IOException e) {
+ System.out.println("WebSocket 发送失败: " + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/mcwl-framework/src/main/java/com/mcwl/framework/config/ResourcesConfig.java b/mcwl-framework/src/main/java/com/mcwl/framework/config/ResourcesConfig.java
index cc23821..d5af248 100644
--- a/mcwl-framework/src/main/java/com/mcwl/framework/config/ResourcesConfig.java
+++ b/mcwl-framework/src/main/java/com/mcwl/framework/config/ResourcesConfig.java
@@ -1,13 +1,16 @@
package com.mcwl.framework.config;
+import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -48,6 +51,26 @@ public class ResourcesConfig implements WebMvcConfigurer
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
}
+ @Override
+ public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ // 核心线程数(根据服务器CPU核心调整)
+ executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
+ // 最大线程数
+ executor.setMaxPoolSize(50);
+ // 队列容量
+ executor.setQueueCapacity(1000);
+ // 线程名前缀
+ executor.setThreadNamePrefix("mvc-async-");
+ // 拒绝策略(CallerRunsPolicy:由调用线程处理该任务)
+ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+ executor.initialize();
+
+ configurer.setTaskExecutor(executor);
+ // 设置异步超时时间(毫秒)
+ configurer.setDefaultTimeout(30_000);
+ }
+
/**
* 跨域配置
*/