stomp + websocket 业务代码实现
This commit is contained in:
parent
4c19ee2627
commit
c265d3a19a
|
@ -0,0 +1,30 @@
|
|||
# 忽略 IDE 和编译生成的文件
|
||||
.idea/
|
||||
target/
|
||||
*.iml
|
||||
*.class
|
||||
|
||||
# 忽略日志文件
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# 忽略配置文件
|
||||
application-local.yml
|
||||
bootstrap-local.yml
|
||||
|
||||
# 忽略临时文件
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
|
||||
# 忽略依赖缓存
|
||||
.m2/
|
||||
node_modules/
|
||||
|
||||
# 忽略敏感信息
|
||||
.env
|
||||
credentials.json
|
||||
|
||||
# 忽略系统文件
|
||||
.DS_Store
|
||||
Thumbs.db
|
|
@ -0,0 +1,64 @@
|
|||
#Tomcat
|
||||
server:
|
||||
port: 9200
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
#邮箱基本配置
|
||||
mail:
|
||||
# 配置在limit_time内,用户可以发送limit次验证码
|
||||
limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
limitTime: 10 这个是我额外的配置
|
||||
#配置smtp服务主机地址
|
||||
# sina smtp.sina.cn
|
||||
# aliyun smtp.aliyun.com
|
||||
# 163 smtp.163.com 端口号465或994
|
||||
host: mail.privateemail.com
|
||||
#发送者邮箱
|
||||
username: do.not.reply@m2pool.com
|
||||
#配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# password:
|
||||
# password: M2pool2024@!
|
||||
# password: axvm-zfgx-cgcg-qhhu
|
||||
password: M2202401!
|
||||
|
||||
|
||||
#端口号
|
||||
port: 587
|
||||
#默认的邮件编码为UTF-8
|
||||
default-encoding: UTF-8
|
||||
#其他参数
|
||||
properties:
|
||||
mail:
|
||||
#配置SSL 加密工厂
|
||||
smtp:
|
||||
ssl:
|
||||
#本地测试,先放开ssl
|
||||
enable: false
|
||||
required: false
|
||||
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
debug: false
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-auth
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
server:
|
||||
port: 9500
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
#邮箱基本配置
|
||||
mail:
|
||||
# 配置在limit_time内,用户可以发送limit次验证码
|
||||
limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
limitTime: 10 这个是我额外的配置
|
||||
#配置smtp服务主机地址
|
||||
# sina smtp.sina.cn
|
||||
# aliyun smtp.aliyun.com
|
||||
# 163 smtp.163.com 端口号465或994
|
||||
host: mail.privateemail.com
|
||||
#发送者邮箱
|
||||
username: do.not.reply@m2pool.com
|
||||
#配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# password:
|
||||
# password: M2202401!
|
||||
# password: axvm-zfgx-cgcg-qhhu
|
||||
password: M2202401!
|
||||
|
||||
#端口号
|
||||
port: 587
|
||||
# port: 465
|
||||
#默认的邮件编码为UTF-8
|
||||
default-encoding: UTF-8
|
||||
#其他参数
|
||||
properties:
|
||||
mail:
|
||||
#配置SSL 加密工厂
|
||||
smtp:
|
||||
ssl:
|
||||
#本地测试,先放开ssl
|
||||
enable: false
|
||||
required: false
|
||||
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
debug: false
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
#
|
||||
# host: smtp.qq.com
|
||||
# #发送者邮箱
|
||||
# username: 1328642438@qq.com
|
||||
# #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# # password:
|
||||
# # password: M2pool2024@!
|
||||
# # password: axvm-zfgx-cgcg-qhhu
|
||||
# password: eqrzqxeqzlshhedh
|
||||
#
|
||||
# #端口号
|
||||
# port: 465
|
||||
# #默认的邮件编码为UTF-8
|
||||
# default-encoding: UTF-8
|
||||
# #其他参数
|
||||
# properties:
|
||||
# mail:
|
||||
# #配置SSL 加密工厂
|
||||
# smtp:
|
||||
# ssl:
|
||||
# #本地测试,先放开ssl
|
||||
# enable: true
|
||||
# required: true
|
||||
# #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
# debug: false
|
||||
# socketFactory:
|
||||
# class: javax.net.ssl.SSLSocketFactory
|
||||
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-auth
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
|
@ -1,159 +1,3 @@
|
|||
#Tomcat
|
||||
server:
|
||||
port: 9200
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
#邮箱基本配置
|
||||
mail:
|
||||
# 配置在limit_time内,用户可以发送limit次验证码
|
||||
limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
limitTime: 10 这个是我额外的配置
|
||||
#配置smtp服务主机地址
|
||||
# sina smtp.sina.cn
|
||||
# aliyun smtp.aliyun.com
|
||||
# 163 smtp.163.com 端口号465或994
|
||||
host: mail.privateemail.com
|
||||
#发送者邮箱
|
||||
username: do.not.reply@m2pool.com
|
||||
#配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# password:
|
||||
# password: M2pool2024@!
|
||||
# password: axvm-zfgx-cgcg-qhhu
|
||||
password: M2202401!
|
||||
|
||||
|
||||
#端口号
|
||||
port: 587
|
||||
#默认的邮件编码为UTF-8
|
||||
default-encoding: UTF-8
|
||||
#其他参数
|
||||
properties:
|
||||
mail:
|
||||
#配置SSL 加密工厂
|
||||
smtp:
|
||||
ssl:
|
||||
#本地测试,先放开ssl
|
||||
enable: false
|
||||
required: false
|
||||
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
debug: false
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-auth
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: prod
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
|
||||
#server:
|
||||
# port: 9500
|
||||
#
|
||||
## Spring
|
||||
#spring:
|
||||
# #邮箱基本配置
|
||||
# mail:
|
||||
# # 配置在limit_time内,用户可以发送limit次验证码
|
||||
# limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
# limitTime: 10 这个是我额外的配置
|
||||
# #配置smtp服务主机地址
|
||||
# # sina smtp.sina.cn
|
||||
# # aliyun smtp.aliyun.com
|
||||
# # 163 smtp.163.com 端口号465或994
|
||||
# host: mail.privateemail.com
|
||||
# #发送者邮箱
|
||||
# username: do.not.reply@m2pool.com
|
||||
# #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# # password:
|
||||
# # password: M2202401!
|
||||
# # password: axvm-zfgx-cgcg-qhhu
|
||||
# password: M2202401!
|
||||
#
|
||||
# #端口号
|
||||
# port: 587
|
||||
## port: 465
|
||||
# #默认的邮件编码为UTF-8
|
||||
# default-encoding: UTF-8
|
||||
# #其他参数
|
||||
# properties:
|
||||
# mail:
|
||||
# #配置SSL 加密工厂
|
||||
# smtp:
|
||||
# ssl:
|
||||
# #本地测试,先放开ssl
|
||||
# enable: false
|
||||
# required: false
|
||||
# #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
# debug: false
|
||||
# socketFactory:
|
||||
# class: javax.net.ssl.SSLSocketFactory
|
||||
##
|
||||
## host: smtp.qq.com
|
||||
## #发送者邮箱
|
||||
## username: 1328642438@qq.com
|
||||
## #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
## # password:
|
||||
## # password: M2pool2024@!
|
||||
## # password: axvm-zfgx-cgcg-qhhu
|
||||
## password: eqrzqxeqzlshhedh
|
||||
##
|
||||
## #端口号
|
||||
## port: 465
|
||||
## #默认的邮件编码为UTF-8
|
||||
## default-encoding: UTF-8
|
||||
## #其他参数
|
||||
## properties:
|
||||
## mail:
|
||||
## #配置SSL 加密工厂
|
||||
## smtp:
|
||||
## ssl:
|
||||
## #本地测试,先放开ssl
|
||||
## enable: true
|
||||
## required: true
|
||||
## #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
## debug: false
|
||||
## socketFactory:
|
||||
## class: javax.net.ssl.SSLSocketFactory
|
||||
#
|
||||
# application:
|
||||
# # 应用名称
|
||||
# name: m2pool-auth
|
||||
# profiles:
|
||||
# # 环境配置
|
||||
# active: test
|
||||
# cloud:
|
||||
# nacos:
|
||||
# discovery:
|
||||
# # 服务注册地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# namespace: m2_test
|
||||
# group: m2_test_group
|
||||
# config:
|
||||
# # 配置中心地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# # 配置文件格式
|
||||
# file-extension: yml
|
||||
# # 共享配置
|
||||
# shared-configs:
|
||||
# - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
# namespace: m2_test
|
||||
# group: m2_test_group
|
||||
active: prod
|
|
@ -1,5 +1,6 @@
|
|||
package com.m2pool.gateway;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
|
|
@ -0,0 +1,247 @@
|
|||
package com.m2pool.gateway.config;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
import org.springframework.web.reactive.socket.client.WebSocketClient;
|
||||
import org.springframework.web.reactive.socket.server.WebSocketService;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 解决gateway网关 websocket关闭异常 问题
|
||||
* @author yyb
|
||||
* @Desc websocket客户端主动断开连接,
|
||||
* @date 2025/4/14 11:21
|
||||
*/
|
||||
@Component
|
||||
public class CustomWebsocketRoutingFilter implements GlobalFilter, Ordered {
|
||||
|
||||
public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
|
||||
private static final Log log = LogFactory.getLog(CustomWebsocketRoutingFilter.class);
|
||||
private final WebSocketClient webSocketClient;
|
||||
private final WebSocketService webSocketService;
|
||||
private final ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider;
|
||||
private volatile List<HttpHeadersFilter> headersFilters;
|
||||
|
||||
public CustomWebsocketRoutingFilter(WebSocketClient webSocketClient, WebSocketService webSocketService, ObjectProvider<List<HttpHeadersFilter>> headersFiltersProvider) {
|
||||
this.webSocketClient = webSocketClient;
|
||||
this.webSocketService = webSocketService;
|
||||
this.headersFiltersProvider = headersFiltersProvider;
|
||||
}
|
||||
|
||||
static String convertHttpToWs(String scheme) {
|
||||
scheme = scheme.toLowerCase();
|
||||
return "http".equals(scheme) ? "ws" : ("https".equals(scheme) ? "wss" : scheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 2147483645;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
changeSchemeIfIsWebSocketUpgrade(exchange);
|
||||
URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
|
||||
String scheme = requestUrl.getScheme();
|
||||
if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && ("ws".equals(scheme) || "wss".equals(scheme))) {
|
||||
ServerWebExchangeUtils.setAlreadyRouted(exchange);
|
||||
HttpHeaders headers = exchange.getRequest().getHeaders();
|
||||
HttpHeaders filtered = HttpHeadersFilter.filterRequest(this.getHeadersFilters(), exchange);
|
||||
List<String> protocols = this.getProtocols(headers);
|
||||
return this.webSocketService.handleRequest(exchange, new CustomWebsocketRoutingFilter.ProxyWebSocketHandler(requestUrl, this.webSocketClient, filtered, protocols));
|
||||
} else {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> getProtocols(HttpHeaders headers) {
|
||||
List<String> protocols = headers.get("Sec-WebSocket-Protocol");
|
||||
if (protocols != null) {
|
||||
ArrayList<String> updatedProtocols = new ArrayList();
|
||||
|
||||
for(int i = 0; i < ((List)protocols).size(); ++i) {
|
||||
String protocol = (String)((List)protocols).get(i);
|
||||
updatedProtocols.addAll(Arrays.asList(StringUtils.tokenizeToStringArray(protocol, ",")));
|
||||
}
|
||||
|
||||
protocols = updatedProtocols;
|
||||
}
|
||||
|
||||
return (List)protocols;
|
||||
}
|
||||
|
||||
List<HttpHeadersFilter> getHeadersFilters() {
|
||||
if (this.headersFilters == null) {
|
||||
this.headersFilters = (List)this.headersFiltersProvider.getIfAvailable(ArrayList::new);
|
||||
this.headersFilters.add((headers, exchange) -> {
|
||||
HttpHeaders filtered = new HttpHeaders();
|
||||
filtered.addAll(headers);
|
||||
filtered.remove("Host");
|
||||
boolean preserveHost = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);
|
||||
if (preserveHost) {
|
||||
String host = exchange.getRequest().getHeaders().getFirst("Host");
|
||||
filtered.add("Host", host);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
this.headersFilters.add((headers, exchange) -> {
|
||||
HttpHeaders filtered = new HttpHeaders();
|
||||
Iterator var3 = headers.entrySet().iterator();
|
||||
|
||||
while(var3.hasNext()) {
|
||||
Map.Entry<String, List<String>> entry = (Map.Entry)var3.next();
|
||||
if (!((String)entry.getKey()).toLowerCase().startsWith("sec-websocket")) {
|
||||
filtered.addAll((String)entry.getKey(), (List)entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
return this.headersFilters;
|
||||
}
|
||||
|
||||
static void changeSchemeIfIsWebSocketUpgrade(ServerWebExchange exchange) {
|
||||
URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
|
||||
String scheme = requestUrl.getScheme().toLowerCase();
|
||||
String upgrade = exchange.getRequest().getHeaders().getUpgrade();
|
||||
if ("WebSocket".equalsIgnoreCase(upgrade) && ("http".equals(scheme) || "https".equals(scheme))) {
|
||||
String wsScheme = convertHttpToWs(scheme);
|
||||
boolean encoded = ServerWebExchangeUtils.containsEncodedParts(requestUrl);
|
||||
URI wsRequestUrl = UriComponentsBuilder.fromUri(requestUrl).scheme(wsScheme).build(encoded).toUri();
|
||||
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, wsRequestUrl);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("changeSchemeTo:[" + wsRequestUrl + "]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ProxyWebSocketHandler implements WebSocketHandler {
|
||||
private final WebSocketClient client;
|
||||
private final URI url;
|
||||
private final HttpHeaders headers;
|
||||
private final List<String> subProtocols;
|
||||
|
||||
ProxyWebSocketHandler(URI url, WebSocketClient client, HttpHeaders headers, List<String> protocols) {
|
||||
this.client = client;
|
||||
this.url = url;
|
||||
this.headers = headers;
|
||||
if (protocols != null) {
|
||||
this.subProtocols = protocols;
|
||||
} else {
|
||||
this.subProtocols = Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSubProtocols() {
|
||||
return this.subProtocols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession session) {
|
||||
return this.client.execute(this.url, this.headers, new WebSocketHandler() {
|
||||
|
||||
private CloseStatus adaptCloseStatus(CloseStatus closeStatus) {
|
||||
int code = closeStatus.getCode();
|
||||
if (code > 2999 && code < 5000) {
|
||||
return closeStatus;
|
||||
}
|
||||
switch (code) {
|
||||
case 1000:
|
||||
return closeStatus;
|
||||
case 1001:
|
||||
return closeStatus;
|
||||
case 1002:
|
||||
return closeStatus;
|
||||
case 1003:
|
||||
return closeStatus;
|
||||
case 1004:
|
||||
// Should not be used in a close frame
|
||||
// RESERVED;
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
case 1005:
|
||||
// Should not be used in a close frame
|
||||
// return CloseStatus.NO_STATUS_CODE;
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
case 1006:
|
||||
// Should not be used in a close frame
|
||||
// return CloseStatus.NO_CLOSE_FRAME;
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
case 1007:
|
||||
return closeStatus;
|
||||
case 1008:
|
||||
return closeStatus;
|
||||
case 1009:
|
||||
return closeStatus;
|
||||
case 1010:
|
||||
return closeStatus;
|
||||
case 1011:
|
||||
return closeStatus;
|
||||
case 1012:
|
||||
// Not in RFC6455
|
||||
// return CloseStatus.SERVICE_RESTARTED;
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
case 1013:
|
||||
// Not in RFC6455
|
||||
// return CloseStatus.SERVICE_OVERLOAD;
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
case 1015:
|
||||
// Should not be used in a close frame
|
||||
// return CloseStatus.TLS_HANDSHAKE_FAILURE;
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
default:
|
||||
return CloseStatus.PROTOCOL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession proxySession) {
|
||||
Mono<Void> serverClose = proxySession.closeStatus().filter(__ -> session.isOpen())
|
||||
.map(this::adaptCloseStatus)
|
||||
.flatMap(session::close);
|
||||
Mono<Void> proxyClose = session.closeStatus().filter(__ -> proxySession.isOpen())
|
||||
.map(this::adaptCloseStatus)
|
||||
.flatMap(proxySession::close);
|
||||
// Use retain() for Reactor Netty
|
||||
Mono<Void> proxySessionSend = proxySession
|
||||
.send(session.receive().doOnNext(WebSocketMessage::retain));
|
||||
Mono<Void> serverSessionSend = session
|
||||
.send(proxySession.receive().doOnNext(WebSocketMessage::retain));
|
||||
// Ensure closeStatus from one propagates to the other
|
||||
Mono.when(serverClose, proxyClose).subscribe();
|
||||
// Complete when both sessions are done
|
||||
return Mono.zip(proxySessionSend, serverSessionSend).then();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSubProtocols() {
|
||||
return CustomWebsocketRoutingFilter.ProxyWebSocketHandler.this.subProtocols;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 8201
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-gateway
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
sentinel:
|
||||
# 取消控制台懒加载
|
||||
eager: true
|
||||
transport:
|
||||
# 控制台地址
|
||||
dashboard: 127.0.0.1:8718
|
||||
# nacos配置持久化
|
||||
datasource:
|
||||
ds1:
|
||||
nacos:
|
||||
server-addr: 127.0.0.1:8808
|
||||
dataId: sentinel-m2pool-gateway
|
||||
groupId: m2_prod_group
|
||||
data-type: json
|
||||
rule-type: flow
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 2MB
|
||||
max-request-size: 8MB
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
server:
|
||||
port: 8101
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-gateway
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
sentinel:
|
||||
# 取消控制台懒加载
|
||||
eager: true
|
||||
transport:
|
||||
# 控制台地址
|
||||
dashboard: 127.0.0.1:8718
|
||||
# nacos配置持久化
|
||||
datasource:
|
||||
ds1:
|
||||
nacos:
|
||||
server-addr: 127.0.0.1:8848
|
||||
dataId: sentinel-m2pool-gateway
|
||||
groupId: m2_test_group
|
||||
data-type: json
|
||||
rule-type: flow
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 2MB
|
||||
max-request-size: 8MB
|
|
@ -1,100 +1,3 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 8201
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-gateway
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: prod
|
||||
main:
|
||||
allow-circular-references: true
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
sentinel:
|
||||
# 取消控制台懒加载
|
||||
eager: true
|
||||
transport:
|
||||
# 控制台地址
|
||||
dashboard: 127.0.0.1:8718
|
||||
# nacos配置持久化
|
||||
datasource:
|
||||
ds1:
|
||||
nacos:
|
||||
server-addr: 127.0.0.1:8808
|
||||
dataId: sentinel-m2pool-gateway
|
||||
groupId: m2_prod_group
|
||||
data-type: json
|
||||
rule-type: flow
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 2MB
|
||||
max-request-size: 8MB
|
||||
|
||||
#server:
|
||||
# port: 8101
|
||||
## Spring
|
||||
#spring:
|
||||
# application:
|
||||
# # 应用名称
|
||||
# name: m2pool-gateway
|
||||
# profiles:
|
||||
# # 环境配置
|
||||
# active: test
|
||||
# main:
|
||||
# allow-circular-references: true
|
||||
# allow-bean-definition-overriding: true
|
||||
#
|
||||
# cloud:
|
||||
# nacos:
|
||||
# discovery:
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# namespace: m2_test
|
||||
# group: m2_test_group
|
||||
# config:
|
||||
# # 配置中心地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# # 配置文件格式
|
||||
# file-extension: yml
|
||||
# # 共享配置
|
||||
# shared-configs:
|
||||
# - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
# namespace: m2_test
|
||||
# group: m2_test_group
|
||||
# sentinel:
|
||||
# # 取消控制台懒加载
|
||||
# eager: true
|
||||
# transport:
|
||||
# # 控制台地址
|
||||
# dashboard: 127.0.0.1:8718
|
||||
# # nacos配置持久化
|
||||
# datasource:
|
||||
# ds1:
|
||||
# nacos:
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# dataId: sentinel-m2pool-gateway
|
||||
# groupId: m2_test_group
|
||||
# data-type: json
|
||||
# rule-type: flow
|
||||
# servlet:
|
||||
# multipart:
|
||||
# max-file-size: 2MB
|
||||
# max-request-size: 8MB
|
||||
active: prod
|
|
@ -135,6 +135,12 @@
|
|||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
|
||||
<!--google两步认证相关-->
|
||||
<dependency>
|
||||
<groupId>de.taimos</groupId>
|
||||
|
|
|
@ -28,7 +28,7 @@ public class M2ChatApplication {
|
|||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(M2ChatApplication.class,args);
|
||||
System.out.println("m2pool矿池 开放api微服务启动成功");
|
||||
System.out.println("m2pool矿池 聊天室微服务启动成功");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.m2pool.chat.config;
|
||||
|
||||
import com.m2pool.chat.constant.Destination;
|
||||
import com.m2pool.chat.coverter.CommonMessageConvert;
|
||||
import com.m2pool.chat.interceptor.WebsocketChannelInterceptor;
|
||||
import com.m2pool.chat.interceptor.WebsocketHandshakeInterceptor;
|
||||
|
@ -30,12 +31,17 @@ public class WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer {
|
|||
*/
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/chat")
|
||||
|
||||
//sockjs 形式端点
|
||||
registry.addEndpoint("/sockjs")
|
||||
.addInterceptors(new WebsocketHandshakeInterceptor())
|
||||
//.setHandshakeHandler(webSocketHandshakeHandler)
|
||||
//允许跨域访问
|
||||
.setAllowedOrigins("*")
|
||||
.withSockJS();
|
||||
|
||||
// websocket 形式端点
|
||||
registry.addEndpoint("/ws")
|
||||
.addInterceptors(new WebsocketHandshakeInterceptor())
|
||||
.setAllowedOrigins("*");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,13 +52,13 @@ public class WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer {
|
|||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
|
||||
config.enableSimpleBroker("/topic", "/queue")
|
||||
config.enableSimpleBroker(Destination.TOPIC, Destination.QUEUE)
|
||||
.setHeartbeatValue(new long[] {10000, 10000})
|
||||
.setTaskScheduler(new DefaultManagedTaskScheduler());
|
||||
|
||||
config.setApplicationDestinationPrefixes("/message");
|
||||
config.setApplicationDestinationPrefixes(Destination.SEND_PREFIX);
|
||||
//服务端通知客户端的前缀,可以不设置,默认为user
|
||||
config.setUserDestinationPrefix("/user");
|
||||
config.setUserDestinationPrefix(Destination.USER_PREFIX);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
package com.m2pool.chat.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/25 14:43
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter(){
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.m2pool.chat.constant;
|
||||
|
||||
/**
|
||||
* @ClassName Destination
|
||||
* @Description 消息发送目的地
|
||||
* @Author yyb
|
||||
* @Date 2025/4/14 14:54
|
||||
*/
|
||||
public class Destination {
|
||||
/**
|
||||
* stomp 默认单对单 发送目的地
|
||||
*/
|
||||
public static final String QUEUE = "/queue";
|
||||
|
||||
/**
|
||||
* stomp 默认群发 目的地
|
||||
*/
|
||||
public static final String TOPIC = "/topic";
|
||||
|
||||
/**
|
||||
* 前端订阅消息所需前缀。 stomp 默认user前缀。
|
||||
*/
|
||||
public static final String USER_PREFIX = "/user";
|
||||
|
||||
/**
|
||||
* stomp 默认发送消息前缀。
|
||||
*/
|
||||
public static final String SEND_PREFIX = "/message";
|
||||
|
||||
/**
|
||||
* 游客标识
|
||||
*/
|
||||
public static final String VISITOR_PREFIX = "visitor";
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.m2pool.chat.constant;
|
||||
|
||||
/**
|
||||
* @ClassName messageType
|
||||
* @Description 消息类型常量类
|
||||
* @Author yyb
|
||||
* @Date 2025/4/14 14:51
|
||||
*/
|
||||
public class MessageType {
|
||||
/**
|
||||
* 文本消息
|
||||
*/
|
||||
public static final Integer TYPE_TEXT = 0;
|
||||
|
||||
/**
|
||||
* 图片信息
|
||||
*/
|
||||
public static final Integer TYPE_IMAGE = 1;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.m2pool.chat.constant;
|
||||
|
||||
/**
|
||||
* @ClassName UserType
|
||||
* @Description TODO
|
||||
* @Author yyb
|
||||
* @Date 2025/4/15 11:25
|
||||
*/
|
||||
public class UserType {
|
||||
/**
|
||||
* 游客
|
||||
*/
|
||||
public static final Integer TOURIST = 0;
|
||||
|
||||
/**
|
||||
* 登录用户
|
||||
*/
|
||||
public static final Integer LOGIN_USER = 1;
|
||||
|
||||
|
||||
/**
|
||||
* 客服
|
||||
*/
|
||||
public static final Integer CUSTOMER = 2;
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package com.m2pool.chat.constant;
|
||||
|
||||
/**
|
||||
* @ClassName ExceptionCode
|
||||
* @Description websocket 异常码
|
||||
* @Author yyb
|
||||
* @Date 2025/4/10 16:37
|
||||
*/
|
||||
public class WebsocketExceptionCode {
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package com.m2pool.chat.controller;
|
||||
|
||||
import com.m2pool.chat.dto.WebsocketMessageDto;
|
||||
import com.m2pool.chat.service.ChatService;
|
||||
import com.m2pool.chat.vo.ChatHistoryVo;
|
||||
import com.m2pool.chat.vo.UserMessageVo;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.core.web.controller.BaseController;
|
||||
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.annotation.SendToUser;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/24 15:05
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Controller
|
||||
public class ChatController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private ChatService chatService;
|
||||
|
||||
//@Resource
|
||||
// //private WebSocketServer webSocketServer;
|
||||
|
||||
@GetMapping("/history")
|
||||
@ResponseBody
|
||||
public AjaxResult getChatHistory(@RequestBody ChatHistoryVo vo) {
|
||||
|
||||
if(StringUtils.isNull(vo)){
|
||||
return AjaxResult.error("请求参数为空");
|
||||
}
|
||||
|
||||
String identifier = StringUtils.isNotBlank(vo.getEmail()) ? vo.getEmail() : vo.getBrowserId();
|
||||
return chatService.getHistory(identifier);
|
||||
}
|
||||
|
||||
//spring提供的推送方式
|
||||
@Resource
|
||||
private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* 发送消息到对应的用户
|
||||
* @param userId 用户id,消息接受者
|
||||
* @param userMessageVo 消息体
|
||||
* @return 返回值通过CommonMessageConvert消息转换器转换
|
||||
*/
|
||||
@MessageMapping("/message/{userId}")
|
||||
@SendToUser("/queue/{userId}")
|
||||
public WebsocketMessageDto sendMessageToUser(@DestinationVariable String userId, UserMessageVo userMessageVo) {
|
||||
WebsocketMessageDto websocketMessageDto = new WebsocketMessageDto();
|
||||
websocketMessageDto.setType("message");
|
||||
websocketMessageDto.setMessage(userMessageVo.getMessage());
|
||||
|
||||
return websocketMessageDto;
|
||||
}
|
||||
|
||||
|
||||
//TODO 前端打开聊天框,获取用户信息,建立一对一链接
|
||||
|
||||
|
||||
//TODO 用户登录后,保存历史信息到数据库表,分表存储(7天)。
|
||||
|
||||
|
||||
//TODO 用户注销,需删除历史信息
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.m2pool.chat.controller;
|
||||
|
||||
import com.m2pool.chat.service.ChatMessageService;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/chat/message")
|
||||
@Api(tags = "聊天消息相关接口")
|
||||
public class ChatMessageController {
|
||||
|
||||
@Autowired
|
||||
private ChatMessageService chatMessageService;
|
||||
|
||||
/**
|
||||
* 查询7天前的历史记录
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/find/history/message")
|
||||
@ApiOperation(value = "查询7天前的历史记录", notes = "根据ID查询7天前的历史记录")
|
||||
public AjaxResult findHistoryMessage(
|
||||
@ApiParam(value = "游标:最后一条消息id", required = true, example = "1")
|
||||
@RequestParam(defaultValue = "1") Long id,
|
||||
@ApiParam(value = "分页页码,默认为10条/页", example = "10")
|
||||
@RequestParam(required = false,defaultValue = "10")Integer pageNum,
|
||||
@ApiParam(value = "聊天室id", required = true, example = "1")
|
||||
@RequestParam Long roomId
|
||||
) {
|
||||
return chatMessageService.findHistoryMessage(id,pageNum,roomId);
|
||||
}
|
||||
|
||||
@GetMapping("/find/recently/message")
|
||||
@ApiOperation(value = "查询七天内的记录", notes = "根据ID查询七天内的记录")
|
||||
public AjaxResult findRecentlyMessage(
|
||||
@ApiParam(value = "游标:最后一条消息id", required = true, example = "1")
|
||||
@RequestParam(defaultValue = "1") Long id,
|
||||
@ApiParam(value = "分页页码,默认为10条/页", example = "10")
|
||||
@RequestParam(required = false,defaultValue = "10")Integer pageNum,
|
||||
@ApiParam(value = "聊天室id", required = true, example = "1")
|
||||
@RequestParam Long roomId
|
||||
) {
|
||||
return chatMessageService.findRecentlyMessage(id,pageNum,roomId);
|
||||
}
|
||||
|
||||
@GetMapping("/delete/message")
|
||||
@ApiOperation(value = "消息撤回或删除")
|
||||
public AjaxResult deleteMessage(
|
||||
@ApiParam(value = "消息id", required = true, example = "1")
|
||||
@RequestParam(defaultValue = "1") Long id,
|
||||
@ApiParam(value = "消息类型 0 文本 1 图片", example = "0")
|
||||
@RequestParam(required = false,defaultValue = "0")Integer type
|
||||
) {
|
||||
return chatMessageService.deleteMessage(id,type);
|
||||
}
|
||||
|
||||
@GetMapping("/read/message")
|
||||
@ApiOperation(value = "聊天室消息改已读")
|
||||
public AjaxResult readMessage(
|
||||
@ApiParam(value = "聊天室id", required = true, example = "1")
|
||||
@RequestParam Long roomId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.m2pool.chat.controller;
|
||||
|
||||
import com.m2pool.chat.service.ChatRoomService;
|
||||
import com.m2pool.chat.vo.CharRoomVo;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.core.web.page.TableDataInfo;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Api(tags = "聊天室相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/chat/rooms")
|
||||
public class ChatRoomController {
|
||||
|
||||
@Autowired
|
||||
private ChatRoomService chatRoomService;
|
||||
|
||||
|
||||
@GetMapping("/find/room/list")
|
||||
@ApiOperation(value = "查询客服人员的聊天室列表")
|
||||
public TableDataInfo findRoomList() {
|
||||
return chatRoomService.findRoomList();
|
||||
}
|
||||
|
||||
@GetMapping("/find/room/by/userid")
|
||||
@ApiOperation(value = "根据当前用户邮箱查询聊天室")
|
||||
public AjaxResult findRoomByUserid(@ApiParam(value = "当前用户聊天对象的userId", example = "1")
|
||||
@RequestParam(required = false) String email) {
|
||||
return chatRoomService.findRoomByUserid(email);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/update/room")
|
||||
@ApiOperation(value = "修改room相关信息")
|
||||
public AjaxResult updateRoom(@RequestBody CharRoomVo charRoomVo) {
|
||||
return chatRoomService.updateRoom(charRoomVo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.m2pool.chat.controller;
|
||||
|
||||
import com.m2pool.chat.service.StompService;
|
||||
import com.m2pool.chat.vo.UserMessageVo;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.core.web.controller.BaseController;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.messaging.simp.annotation.SendToUser;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/24 15:05
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Controller
|
||||
public class StompController extends BaseController {
|
||||
|
||||
|
||||
|
||||
|
||||
@Resource
|
||||
private StompService stompService;
|
||||
|
||||
/**
|
||||
* 发送消息到对应的用户
|
||||
* @param userMessageVo 消息体
|
||||
* @return 返回值通过CommonMessageConvert消息转换器转换
|
||||
*/
|
||||
@MessageMapping("/send/message")
|
||||
@SendToUser("/queue")
|
||||
public AjaxResult sendMessageToUser(@Payload UserMessageVo userMessageVo) {
|
||||
return stompService.sendMessageToUser(userMessageVo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.m2pool.chat.dto;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @ClassName ChatMessageDto
|
||||
* @Description 聊天消息返回对象
|
||||
* @Author yyb
|
||||
* @Date 2025/4/14 14:28
|
||||
*/
|
||||
|
||||
@ApiModel(description = "聊天消息返回对象")
|
||||
@Data
|
||||
public class ChatMessageDto {
|
||||
|
||||
@ApiModelProperty(value = "消息ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@ApiModelProperty(value = "消息类型", example = "0 文本 1 图片")
|
||||
private Integer type;
|
||||
|
||||
@ApiModelProperty(value = "发送者邮箱", example = "54544@qq.com")
|
||||
private String sendEmail;
|
||||
|
||||
@ApiModelProperty(value = "消息内容", example = "消息内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty(value = "发送时间", example = "")
|
||||
private DateTime createTime;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.m2pool.chat.dto;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @ClassName ChatRoomDto
|
||||
* @Description 客服 聊天室列表返回对象
|
||||
* @Author yyb
|
||||
* @Date 2025/4/14 14:28
|
||||
*/
|
||||
@Data
|
||||
public class ChatRoomDto {
|
||||
|
||||
/**
|
||||
* 聊天室id
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 聊天对象id
|
||||
*/
|
||||
private String userEmail;
|
||||
/**
|
||||
* 聊天室重要程度
|
||||
*/
|
||||
private Integer flag;
|
||||
|
||||
/**
|
||||
* 未读消息条数
|
||||
*/
|
||||
private Integer unReadNumber;
|
||||
|
||||
|
||||
/**
|
||||
* 用户回复时间
|
||||
*/
|
||||
private DateTime lastUserSendTime;
|
||||
|
||||
/**
|
||||
* 客服回复时间
|
||||
*/
|
||||
private DateTime lastCustomerSendTime;
|
||||
}
|
|
@ -10,6 +10,23 @@ import lombok.Data;
|
|||
*/
|
||||
@Data
|
||||
public class WebsocketMessageDto {
|
||||
private String type;
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 消息id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 是否是登录用户
|
||||
*/
|
||||
private boolean isLoginUser;
|
||||
}
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
package com.m2pool.chat.entity;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/3/7 15:53
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
public class ChatMessage {
|
||||
private Long id;
|
||||
private String senderType; // USER 0/CUSTOMER_SERVICE 1
|
||||
private String senderId; // 邮箱或客服ID
|
||||
private String receiverId;
|
||||
private Integer type;
|
||||
private Integer read;
|
||||
private String sendEmail;
|
||||
private String content;
|
||||
private String userIdentifier; // 邮箱或浏览器指纹
|
||||
private String browserFingerprint;
|
||||
private Date createdTime;
|
||||
private Long roomId;
|
||||
private DateTime createTime;
|
||||
private DateTime updateTime;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.m2pool.chat.entity;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ChatMessageHistory {
|
||||
private Long id;
|
||||
private Integer type;
|
||||
private String sendEmail;
|
||||
private String content;
|
||||
private Long roomId;
|
||||
private DateTime createTime;
|
||||
private DateTime updateTime;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package com.m2pool.chat.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/28 18:11
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Data
|
||||
public class ChatMsg {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy_MM_dd_HH_mm_ss")
|
||||
private Date createTime=new Date();
|
||||
|
||||
/**
|
||||
* 通信key(sendId_receiveId)
|
||||
*/
|
||||
private String msgKey;
|
||||
|
||||
/**
|
||||
* 通信消息
|
||||
*/
|
||||
private String chatMsg;
|
||||
|
||||
/**
|
||||
* 发送id
|
||||
*/
|
||||
private String sendId;
|
||||
|
||||
/**
|
||||
* 接收id
|
||||
*/
|
||||
private String receiveId;
|
||||
|
||||
/**
|
||||
* 查看状态 0未看 1已看
|
||||
*/
|
||||
private String readState;
|
||||
|
||||
/**
|
||||
*1为我 0 为他
|
||||
*/
|
||||
@Transient
|
||||
private Integer flag;
|
||||
|
||||
@Transient
|
||||
private String name;
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.m2pool.chat.entity;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class ChatRoom {
|
||||
private Long id;
|
||||
private String userOneEmail;
|
||||
private String userTwoEmail;
|
||||
private DateTime lastUserSendTime;
|
||||
private DateTime lastCustomerSendTime;
|
||||
private Boolean flag;
|
||||
private DateTime createTime;
|
||||
private DateTime updateTime;
|
||||
private Boolean del;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.m2pool.chat.entity;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* stomp用户身份信息
|
||||
*/
|
||||
public class StompPrincipal implements Principal {
|
||||
String name;
|
||||
|
||||
public StompPrincipal(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -4,23 +4,16 @@ import com.m2pool.common.core.exception.base.BaseException;
|
|||
|
||||
public class WebSocketException extends BaseException {
|
||||
|
||||
private static String websocket_module = "chat-websocket";
|
||||
|
||||
|
||||
|
||||
public WebSocketException(String module, String code, Object[] args, String defaultMessage) {
|
||||
super(module, code, args, defaultMessage);
|
||||
module = websocket_module;
|
||||
}
|
||||
|
||||
public WebSocketException(String module, String code, Object[] args) {
|
||||
super(module, code, args);
|
||||
module = websocket_module;
|
||||
}
|
||||
|
||||
public WebSocketException(String module, String defaultMessage) {
|
||||
super(module, defaultMessage);
|
||||
module = websocket_module;
|
||||
}
|
||||
|
||||
public WebSocketException(String code, Object[] args) {
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
package com.m2pool.chat.interceptor;
|
||||
|
||||
|
||||
import com.m2pool.chat.entity.StompPrincipal;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.stomp.StompCommand;
|
||||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.m2pool.chat.constant.Destination.VISITOR_PREFIX;
|
||||
|
||||
/**
|
||||
* @ClassName WebsocketChannelInterceptor
|
||||
* @Description websocket channel 通道拦截器
|
||||
* @Description websocket channel 通道拦截器 适用于前端@stomp/stompjs 实现.
|
||||
* @Author yyb
|
||||
* @Date 2025/4/10 15:44
|
||||
*/
|
||||
|
@ -27,26 +35,41 @@ public class WebsocketChannelInterceptor implements ChannelInterceptor {
|
|||
*/
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
//获取链接建立时的请求头信息
|
||||
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
|
||||
|
||||
//TODO 前端通过@stomp/stompjs、基于stomp-client+webscoket等插件实现,可以在该方法获取自定义请求头,做一些校验
|
||||
|
||||
if (accessor.getCommand() == StompCommand.CONNECT) {
|
||||
String visitor = (String) accessor.getSessionAttributes().get(VISITOR_PREFIX);
|
||||
LOGGER.info("------------收到websocket的连接消息");
|
||||
}
|
||||
//获取channel 中的请求头信息
|
||||
StompHeaderAccessor mha = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
|
||||
Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
|
||||
System.out.println("raw:"+raw);
|
||||
if (raw instanceof Map) {
|
||||
Object userInfo = ((Map) raw).get("userId");
|
||||
// 游客或登录用户
|
||||
if (userInfo == null){
|
||||
mha.setUser(new StompPrincipal(visitor));
|
||||
}else if(userInfo instanceof ArrayList) {
|
||||
mha.setUser(new StompPrincipal((String) ((ArrayList) userInfo).get(0)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (accessor.getCommand() == StompCommand.SEND) {
|
||||
LOGGER.info("------------收到websocket的数据发送消息");
|
||||
LOGGER.info("------------websocket send message");
|
||||
}
|
||||
|
||||
if (accessor.getCommand() == StompCommand.SUBSCRIBE) {
|
||||
LOGGER.info("------------收到websocket的订阅消息");
|
||||
LOGGER.info("------------websocket subscribe message");
|
||||
}
|
||||
|
||||
if (accessor.getCommand() == StompCommand.UNSUBSCRIBE) {
|
||||
LOGGER.info("------------收到websocket的取消订阅消息");
|
||||
LOGGER.info("------------websocket unsubscribe");
|
||||
}
|
||||
|
||||
if (accessor.getCommand() == StompCommand.DISCONNECT){
|
||||
LOGGER.info("------------websocket disconnect");
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,12 @@ import org.springframework.web.socket.server.HandshakeInterceptor;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.m2pool.chat.constant.Destination.VISITOR_PREFIX;
|
||||
|
||||
|
||||
/**
|
||||
* @ClassName WebsocketHandshakeInterceptor
|
||||
* @Description websocket 握手处理器类
|
||||
* @Description websocket 握手处理器类 适用于前端原生websocket实现.建立websocket链接时调用
|
||||
* @Author yyb
|
||||
* @Date 2025/4/10 15:39
|
||||
*/
|
||||
|
@ -20,15 +22,32 @@ public class WebsocketHandshakeInterceptor implements HandshakeInterceptor {
|
|||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketHandshakeInterceptor.class);
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
|
||||
LOGGER.info("------------------WebsocketHandshakeInterceptor:beforeHandshake");
|
||||
//TODO 前端如果是webscoket原生实现, 获取一些自定义请求头,做一些校验
|
||||
// 为游客生成一个唯一标识
|
||||
// String s = generateVisitorId(request);
|
||||
// attributes.put(VISITOR_PREFIX, s);
|
||||
// response.getHeaders().add(VISITOR_PREFIX, s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
|
||||
LOGGER.info("-----------------WebsocketHandshakeInterceptor:afterHandshake");
|
||||
LOGGER.info("------------------WebsocketHandshakeInterceptor:afterHandshake");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个唯一标识
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private String generateVisitorId(ServerHttpRequest request) {
|
||||
String first = request.getHeaders().getFirst("sec-websocket-key");
|
||||
String prefix = VISITOR_PREFIX;
|
||||
return prefix + first;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package com.m2pool.chat.mapper;
|
||||
|
||||
import com.m2pool.chat.entity.ChatMessage;
|
||||
import com.m2pool.common.datasource.annotation.Master;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import javax.validation.constraints.Pattern;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description 角色数据层
|
||||
* @Date 2024/6/12 17:37
|
||||
* @Author dy
|
||||
*/
|
||||
@Master
|
||||
public interface ChatMapper {
|
||||
|
||||
public boolean insert(@Param("vo") ChatMessage vo);
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.m2pool.chat.mapper;
|
||||
|
||||
|
||||
import com.m2pool.chat.dto.ChatMessageDto;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ChatMessageHistoryMapper {
|
||||
|
||||
/**
|
||||
* 根据游标查询十条数据
|
||||
* @param id
|
||||
* @param pageNum
|
||||
* @param roomId
|
||||
* @return
|
||||
*/
|
||||
List<ChatMessageDto> findHistoryMessage(@Param("id") Long id,@Param("pageNum") Integer pageNum,@Param("roomId") Long roomId);
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.m2pool.chat.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.m2pool.chat.dto.ChatMessageDto;
|
||||
import com.m2pool.chat.entity.ChatMessage;
|
||||
import org.apache.ibatis.annotations.MapKey;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface ChatMessageMapper extends BaseMapper<ChatMessage> {
|
||||
|
||||
/**
|
||||
* 根据游标查询十条数据
|
||||
* @param id
|
||||
* @param pageNum
|
||||
* @param roomId
|
||||
* @return
|
||||
*/
|
||||
List<ChatMessageDto> findRecentlyMessage(@Param("id") Long id,@Param("pageNum") Integer pageNum,@Param("roomId") Long roomId);
|
||||
|
||||
|
||||
/**
|
||||
* 查询未读消息条数
|
||||
* @param userEmails
|
||||
* @return Map<String, Integer>,键为用户邮箱,值为未读消息条数
|
||||
*/
|
||||
@MapKey("userEmail")
|
||||
Map<String, Integer> findUnReadNums(@Param("userEmails") List<String> userEmails);
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.m2pool.chat.mapper;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.m2pool.chat.dto.ChatRoomDto;
|
||||
import com.m2pool.chat.entity.ChatRoom;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ChatRoomMapper extends BaseMapper<ChatRoom> {
|
||||
|
||||
|
||||
/**
|
||||
* 查询客服的聊天室列表
|
||||
* @param userEmail 客服邮箱
|
||||
* @return
|
||||
*/
|
||||
List<ChatRoomDto> findRoomList(@Param("userEmail") String userEmail);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 查找当前用户与对应用户是否已存在创建的聊天室
|
||||
* @param userEmail 用户邮箱
|
||||
* @param email 客服邮箱
|
||||
* @return
|
||||
*/
|
||||
String findRoomByUserEmail(@Param("userEmail") String userEmail,@Param("email") String email);
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.m2pool.chat.service;
|
||||
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
|
||||
public interface ChatMessageService{
|
||||
|
||||
|
||||
/**
|
||||
* 游标分页 查询7天前的历史记录
|
||||
* @param id 游标
|
||||
* @param pageNum 每页条数
|
||||
* @param roomId 聊天室id
|
||||
* @return
|
||||
*/
|
||||
AjaxResult findHistoryMessage(Long id, Integer pageNum,Long roomId);
|
||||
|
||||
/**
|
||||
* 游标分页 查询7天内数据
|
||||
* @param id 游标
|
||||
* @param pageNum 每页条数
|
||||
* @param roomId 聊天室id
|
||||
* @return
|
||||
*/
|
||||
AjaxResult findRecentlyMessage(Long id,Integer pageNum,Long roomId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据消息id 删除
|
||||
* @param id 消息id
|
||||
* @param type 消息类型 0 文本 1 图片
|
||||
* @return
|
||||
*/
|
||||
AjaxResult deleteMessage(Long id,Integer type);
|
||||
|
||||
|
||||
/**
|
||||
* 根据聊天室修改用户未读信息
|
||||
* @param roomId
|
||||
* @return
|
||||
*/
|
||||
AjaxResult readMessage(Long roomId);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.m2pool.chat.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.m2pool.chat.entity.ChatRoom;
|
||||
import com.m2pool.chat.vo.CharRoomVo;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.core.web.page.TableDataInfo;
|
||||
|
||||
public interface ChatRoomService extends IService<ChatRoom> {
|
||||
|
||||
/**
|
||||
* 查询客服的聊天室列表
|
||||
* @return
|
||||
*/
|
||||
TableDataInfo findRoomList();
|
||||
|
||||
/**
|
||||
* 根据当前用户邮箱查询聊天室
|
||||
* @return
|
||||
*/
|
||||
AjaxResult findRoomByUserid(String email);
|
||||
|
||||
/**
|
||||
* 修改聊天室信息
|
||||
* @param charRoomVo
|
||||
* @return
|
||||
*/
|
||||
AjaxResult updateRoom( CharRoomVo charRoomVo);
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package com.m2pool.chat.service;
|
||||
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.system.api.entity.SysUser;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/28 18:06
|
||||
* @Author 杜懿
|
||||
*/
|
||||
public interface ChatService {
|
||||
|
||||
//public AjaxResult userList(UserItem userItem, SysUser data);
|
||||
|
||||
public AjaxResult getHistory(String identifier);
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.m2pool.chat.service;
|
||||
|
||||
import com.m2pool.chat.vo.UserMessageVo;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
|
||||
public interface StompService {
|
||||
|
||||
/**
|
||||
* 发送消息给用户
|
||||
* @param userMessageVo
|
||||
* @return
|
||||
*/
|
||||
AjaxResult sendMessageToUser(UserMessageVo userMessageVo);
|
||||
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
package com.m2pool.chat.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.m2pool.chat.entity.ChatMsg;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/28 18:02
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@ServerEndpoint(value = "/ws/asset")
|
||||
@Component
|
||||
public class WebSocketServer {
|
||||
|
||||
private static ChatService chatService;
|
||||
@Autowired
|
||||
public void setChatService(ChatService chatService) {
|
||||
WebSocketServer.chatService = chatService;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
System.out.println("websocket 加载");
|
||||
}
|
||||
private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
|
||||
private static final AtomicInteger OnlineCount = new AtomicInteger(0);
|
||||
// concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
|
||||
private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
|
||||
/**
|
||||
* 连接建立成功调用的方法
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(Session session) {
|
||||
SessionSet.add(session);
|
||||
int cnt = OnlineCount.incrementAndGet();
|
||||
log.info("有连接加入,当前连接数为:{}", cnt);
|
||||
SendMessage(session, "连接成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接关闭调用的方法
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(Session session) {
|
||||
SessionSet.remove(session);
|
||||
int cnt = OnlineCount.decrementAndGet();
|
||||
log.info("有连接关闭,当前连接数为:{}", cnt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到客户端消息后调用的方法
|
||||
* @param message 客户端发送过来的消息
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(String message, Session session) {
|
||||
log.info("来自客户端的消息:{}",message);
|
||||
try{
|
||||
JSONObject jsonObject = JSONObject.parseObject(message);
|
||||
String linkType = jsonObject.getString("linkType");
|
||||
String sendId = jsonObject.getString("sendId");
|
||||
if (linkType.equals("bind")){
|
||||
//CacheManager.set("bind_"+sendId,session);
|
||||
SendMessage(session, "连接成功");
|
||||
}else if (linkType.equals("msg")){
|
||||
//聊天
|
||||
ChatMsg msg = new ChatMsg();
|
||||
//发消息
|
||||
String chatMsg = jsonObject.getString("chatMsg");
|
||||
String receiveId = jsonObject.getString("receiveId");
|
||||
msg.setChatMsg(chatMsg);
|
||||
msg.setSendId(sendId);
|
||||
msg.setMsgKey(sendId+"_"+receiveId);
|
||||
msg.setReceiveId(receiveId);
|
||||
msg.setReadState("0");
|
||||
//chatService.sendOne(msg);
|
||||
}
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 出现错误
|
||||
* @param session
|
||||
* @param error
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId());
|
||||
error.printStackTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息,实践表明,每次浏览器刷新,session会发生变化。
|
||||
* @param session
|
||||
* @param message
|
||||
*/
|
||||
public static void SendMessage(Session session, String message) {
|
||||
try {
|
||||
//session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
|
||||
log.info("sessionID="+session.getId());
|
||||
session.getBasicRemote().sendText(message);
|
||||
} catch (IOException e) {
|
||||
log.error("发送消息出错:{}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 指定Session发送消息
|
||||
* @param sessionId
|
||||
* @param message
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void SendMessageById(String message,String sessionId) throws IOException {
|
||||
Session session = null;
|
||||
for (Session s : SessionSet) {
|
||||
if(s.getId().equals(sessionId)){
|
||||
session = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(session!=null){
|
||||
SendMessage(session, message);
|
||||
}
|
||||
else{
|
||||
log.warn("没有找到你指定ID的会话:{}",sessionId);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @description: 指定发送
|
||||
* @author: lvyq
|
||||
* @date: 2021/9/24 11:30
|
||||
* @version 1.0
|
||||
*/
|
||||
public static void SendMessageByRecId(String message,String receiveId) throws IOException {
|
||||
//Session session= CacheManager.get("bind_"+receiveId);
|
||||
Session session= null;
|
||||
String sessionId = "";
|
||||
if (session!=null){
|
||||
sessionId=session.getId();
|
||||
}
|
||||
for (Session s : SessionSet) {
|
||||
if(s.getId().equals(sessionId)){
|
||||
session = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(session!=null){
|
||||
SendMessage(session, message);
|
||||
}
|
||||
else{
|
||||
System.out.println("没有找到你指定ID的会话:"+sessionId);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.m2pool.chat.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.m2pool.chat.dto.ChatMessageDto;
|
||||
import com.m2pool.chat.entity.ChatMessage;
|
||||
import com.m2pool.chat.entity.ChatRoom;
|
||||
import com.m2pool.chat.mapper.ChatMessageHistoryMapper;
|
||||
import com.m2pool.chat.mapper.ChatMessageMapper;
|
||||
import com.m2pool.chat.mapper.ChatRoomMapper;
|
||||
import com.m2pool.chat.service.ChatMessageService;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ChatMessageServiceImpl implements ChatMessageService {
|
||||
|
||||
@Resource
|
||||
private ChatMessageMapper chatMessageMapper;
|
||||
|
||||
@Resource
|
||||
private ChatMessageHistoryMapper chatMessageHistoryMapper;
|
||||
|
||||
@Resource
|
||||
private ChatRoomMapper chatRoomMapper;
|
||||
|
||||
@Override
|
||||
public AjaxResult findHistoryMessage(Long id,Integer pageNum,Long roomId) {
|
||||
List<ChatMessageDto> historyMessage = chatMessageHistoryMapper.findHistoryMessage(id, pageNum, roomId);
|
||||
return AjaxResult.success(historyMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AjaxResult findRecentlyMessage(Long id,Integer pageNum,Long roomId) {
|
||||
List<ChatMessageDto> recentlyMessage = chatMessageMapper.findRecentlyMessage(id, pageNum, roomId);
|
||||
return AjaxResult.success(recentlyMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AjaxResult deleteMessage(Long id,Integer type) {
|
||||
if(chatMessageMapper.deleteById(id) > 0){
|
||||
//TODO 对象图片存储删除
|
||||
return AjaxResult.success("删除成功");
|
||||
}
|
||||
return AjaxResult.error("删除失败");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AjaxResult readMessage(Long roomId) {
|
||||
ChatRoom chatRoom = chatRoomMapper.selectById(roomId);
|
||||
String username = SecurityUtils.getUsername();
|
||||
|
||||
String updateUserEmail = "";
|
||||
if(username.equals(chatRoom.getUserOneEmail())){
|
||||
updateUserEmail = chatRoom.getUserTwoEmail();
|
||||
}else if(username.equals(chatRoom.getUserTwoEmail())){
|
||||
updateUserEmail = chatRoom.getUserOneEmail();
|
||||
}
|
||||
int update = chatMessageMapper.update(ChatMessage.builder().read(1).build(),
|
||||
new LambdaUpdateWrapper<ChatMessage>().eq(ChatMessage::getSendEmail, updateUserEmail).eq(ChatMessage::getRoomId, roomId));
|
||||
return update > 0 ? AjaxResult.success("已读") : AjaxResult.error("消息读取失败");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.m2pool.chat.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.m2pool.chat.dto.ChatRoomDto;
|
||||
import com.m2pool.chat.entity.ChatRoom;
|
||||
import com.m2pool.chat.mapper.ChatMessageMapper;
|
||||
import com.m2pool.chat.mapper.ChatRoomMapper;
|
||||
import com.m2pool.chat.service.ChatRoomService;
|
||||
import com.m2pool.chat.vo.CharRoomVo;
|
||||
import com.m2pool.common.core.constant.HttpStatus;
|
||||
import com.m2pool.common.core.utils.PageUtils;
|
||||
import com.m2pool.common.core.utils.StringUtils;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.core.web.page.TableDataInfo;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class ChatRoomServiceImpl extends ServiceImpl<ChatRoomMapper, ChatRoom> implements ChatRoomService {
|
||||
|
||||
@Autowired
|
||||
private ChatRoomMapper chatRoomMapper;
|
||||
|
||||
@Resource
|
||||
private ChatMessageMapper chatMessageMapper;
|
||||
|
||||
@Override
|
||||
public TableDataInfo findRoomList() {
|
||||
PageUtils.startPage();
|
||||
//1.查找当前客服对应的聊天室
|
||||
List<ChatRoomDto> roomList = chatRoomMapper.findRoomList(SecurityUtils.getUsername());
|
||||
List<String> userEmails = roomList.stream().map(ChatRoomDto::getUserEmail).collect(Collectors.toList());
|
||||
//2.查询未读数量
|
||||
Map<String, Integer> unReadNums = chatMessageMapper.findUnReadNums(userEmails);
|
||||
|
||||
for (ChatRoomDto room : roomList) {
|
||||
Integer i = unReadNums.get(room.getUserEmail());
|
||||
room.setUnReadNumber(i == null ? 0 : i);
|
||||
}
|
||||
return getDataTable(roomList);
|
||||
}
|
||||
|
||||
private TableDataInfo getDataTable(List<?> list)
|
||||
{
|
||||
TableDataInfo rspData = new TableDataInfo();
|
||||
rspData.setCode(HttpStatus.SUCCESS);
|
||||
rspData.setRows(list);
|
||||
rspData.setMsg("查询成功");
|
||||
PageInfo pageInfo = new PageInfo(list);
|
||||
rspData.setTotal(pageInfo.getTotal());
|
||||
rspData.setTotalPage(pageInfo.getPages());
|
||||
return rspData;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public AjaxResult findRoomByUserid(String email) {
|
||||
//1.查询当前用户与对应用户是否已存在创建的聊天室
|
||||
String userEmail = SecurityUtils.getUsername();
|
||||
String roomByUserEmail = chatRoomMapper.findRoomByUserEmail(userEmail, email);
|
||||
if(StringUtils.isNotEmpty(roomByUserEmail)){
|
||||
return AjaxResult.success(roomByUserEmail);
|
||||
}
|
||||
//2.不存在创建一个聊天室
|
||||
int insert = chatRoomMapper.insert(ChatRoom.builder().userOneEmail(userEmail).userTwoEmail(email).build());
|
||||
if (insert > 0){
|
||||
return AjaxResult.success(roomByUserEmail);
|
||||
}
|
||||
return AjaxResult.error("聊天室不存在,并且创建聊天室失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AjaxResult updateRoom(CharRoomVo charRoomVo) {
|
||||
int i = chatRoomMapper.updateById(ChatRoom.builder().id(charRoomVo.getId()).flag(charRoomVo.getFlag()).build());
|
||||
if(i > 0){
|
||||
return AjaxResult.success("修改成功");
|
||||
}
|
||||
return AjaxResult.error("修改失败");
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package com.m2pool.chat.service.impl;
|
||||
|
||||
import com.m2pool.chat.entity.ChatMessage;
|
||||
import com.m2pool.chat.mapper.ChatMapper;
|
||||
import com.m2pool.chat.service.ChatService;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/2/28 18:08
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Service
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
@Autowired
|
||||
private ChatMapper chatMapper;
|
||||
|
||||
@Async("messageExecutor")
|
||||
public void saveMessageAsync(ChatMessage message) {
|
||||
// 批量插入优化
|
||||
chatMapper.insert(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AjaxResult getHistory(String identifier) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package com.m2pool.chat.service.impl;
|
||||
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import com.m2pool.chat.constant.Destination;
|
||||
import com.m2pool.chat.dto.WebsocketMessageDto;
|
||||
import com.m2pool.chat.entity.ChatMessage;
|
||||
import com.m2pool.chat.entity.ChatRoom;
|
||||
import com.m2pool.chat.mapper.ChatMessageMapper;
|
||||
import com.m2pool.chat.mapper.ChatRoomMapper;
|
||||
import com.m2pool.chat.service.StompService;
|
||||
import com.m2pool.chat.vo.UserMessageVo;
|
||||
import com.m2pool.common.core.web.Result.AjaxResult;
|
||||
import com.m2pool.common.security.utils.SecurityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.user.SimpUserRegistry;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static com.m2pool.chat.constant.UserType.CUSTOMER;
|
||||
import static com.m2pool.chat.constant.UserType.TOURIST;
|
||||
|
||||
@Service
|
||||
public class StompServiceImpl implements StompService {
|
||||
|
||||
@Autowired
|
||||
private SimpUserRegistry userRegistry;
|
||||
|
||||
@Autowired
|
||||
private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
@Resource
|
||||
private ChatMessageMapper chatMessageMapper;
|
||||
|
||||
@Resource
|
||||
private ChatRoomMapper chatRoomMapper;
|
||||
|
||||
@Override
|
||||
public AjaxResult sendMessageToUser(UserMessageVo userMessageVo) {
|
||||
WebsocketMessageDto websocketMessageDto = new WebsocketMessageDto();
|
||||
websocketMessageDto.setType(userMessageVo.getType());
|
||||
websocketMessageDto.setContent(userMessageVo.getContent());
|
||||
// 检查目标用户是否在线
|
||||
if (!checkOnline(userMessageVo)) {
|
||||
return AjaxResult.error("目标用户不在线");
|
||||
}
|
||||
//当前用户发送其他人, 发送给指定用户. 否则默认发送给当前发送者
|
||||
messagingTemplate.convertAndSendToUser(userMessageVo.getEmail(), Destination.QUEUE + "/" + userMessageVo.getEmail(), userMessageVo.getContent());
|
||||
// 接收者和发送者 都不是游客才能存储消息 和修改聊天室信息
|
||||
if (!TOURIST.equals(userMessageVo.getReceiveUserType()) && TOURIST.equals(userMessageVo.getSendUserType())){
|
||||
Long id = insertMessage(userMessageVo);
|
||||
websocketMessageDto.setId(id);
|
||||
updateRoom(userMessageVo);
|
||||
}
|
||||
return AjaxResult.success(websocketMessageDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否在线
|
||||
* @return
|
||||
*/
|
||||
private boolean checkOnline(UserMessageVo userMessageVo){
|
||||
return userRegistry.getUsers().stream()
|
||||
.anyMatch(user -> user.getName().equals(userMessageVo.getEmail()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增消息
|
||||
* @param userMessageVo
|
||||
* @return
|
||||
*/
|
||||
private Long insertMessage(UserMessageVo userMessageVo){
|
||||
ChatMessage message = ChatMessage.builder()
|
||||
.sendEmail(SecurityUtils.getUsername())
|
||||
.content(userMessageVo.getContent())
|
||||
.type(userMessageVo.getType())
|
||||
.roomId(userMessageVo.getRoomId())
|
||||
.build();
|
||||
chatMessageMapper.insert(message);
|
||||
return message.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改聊天信息
|
||||
* @param userMessageVo
|
||||
*/
|
||||
private void updateRoom(UserMessageVo userMessageVo){
|
||||
ChatRoom room = ChatRoom.builder().id(userMessageVo.getRoomId()).build();
|
||||
if (CUSTOMER.equals(userMessageVo.getSendUserType())) {
|
||||
room.setLastCustomerSendTime(DateTime.now());
|
||||
} else {
|
||||
room.setLastUserSendTime(DateTime.now());
|
||||
}
|
||||
chatRoomMapper.insert(room);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.m2pool.chat.task;
|
||||
|
||||
/**
|
||||
* @ClassName ChatTask
|
||||
* @Description chat 聊天室和消息定时任务
|
||||
* @Author yyb
|
||||
* @Date 2025/4/15 11:51
|
||||
*/
|
||||
public class ChatTask {
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.m2pool.chat.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @ClassName CharRoomVo
|
||||
* @Description 聊天室请求对象
|
||||
* @Author yyb
|
||||
* @Date 2025/4/15 10:42
|
||||
*/
|
||||
@Data
|
||||
public class CharRoomVo {
|
||||
private Long id;
|
||||
private Boolean flag;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package com.m2pool.chat.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Description TODO
|
||||
* @Date 2025/3/7 15:55
|
||||
* @Author 杜懿
|
||||
*/
|
||||
@Data
|
||||
public class ChatHistoryVo {
|
||||
private String email;
|
||||
private String browserId;
|
||||
}
|
|
@ -14,10 +14,32 @@ public class UserMessageVo {
|
|||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
private String type;
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 消息体
|
||||
* 消息内容
|
||||
*/
|
||||
private String message;
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 接收者
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 接受用户类型 0 游客 1 登录用户 2 客服人员
|
||||
*/
|
||||
private Integer receiveUserType;
|
||||
|
||||
/**
|
||||
* 发送者类型 0 游客 1 登录用户 2 客服人员
|
||||
*/
|
||||
private Integer sendUserType;
|
||||
|
||||
/**
|
||||
* 聊天室id
|
||||
*/
|
||||
private Long roomId;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
server:
|
||||
port: 9209
|
||||
compression:
|
||||
enabled: true
|
||||
mime-types: application/json
|
||||
|
||||
spring:
|
||||
#邮箱基本配置
|
||||
mail:
|
||||
# 配置在limit_time内,用户可以发送limit次验证码
|
||||
limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
limitTime: 10 这个是我额外的配置
|
||||
#配置smtp服务主机地址
|
||||
# sina smtp.sina.cn
|
||||
# aliyun smtp.aliyun.com
|
||||
# 163 smtp.163.com 端口号465或994
|
||||
host: mail.privateemail.com
|
||||
#发送者邮箱
|
||||
username: support@m2pool.cc
|
||||
#配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# password:
|
||||
# password: m2pool2024@!
|
||||
# password: axvm-zfgx-cgcg-qhhu
|
||||
password: m2pool2024@!
|
||||
#端口号
|
||||
port: 587
|
||||
#默认的邮件编码为UTF-8
|
||||
default-encoding: UTF-8
|
||||
#其他参数
|
||||
properties:
|
||||
mail:
|
||||
#配置SSL 加密工厂
|
||||
smtp:
|
||||
ssl:
|
||||
#本地测试,先放开ssl
|
||||
enable: false
|
||||
required: false
|
||||
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
debug: false
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-chat
|
||||
# profiles:
|
||||
# # 环境配置
|
||||
# active: prod
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
# server-addr: 127.0.0.1:8808
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 2MB
|
||||
max-request-size: 8MB
|
||||
|
||||
myenv:
|
||||
domain: https://www.m2pool.com
|
||||
path: /var/www/html/web
|
||||
img: /img
|
|
@ -0,0 +1,43 @@
|
|||
#测试环境
|
||||
server:
|
||||
port: 9509
|
||||
compression:
|
||||
enabled: true
|
||||
mime-types: application/json
|
||||
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-chat
|
||||
# profiles:
|
||||
# # 环境配置
|
||||
# active: test
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
# server-addr: 127.0.0.1:8808
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 2MB
|
||||
max-request-size: 8MB
|
||||
|
||||
myenv:
|
||||
domain: https://test.m2pool.com
|
||||
path: /var/www/html/web_test
|
||||
img: /imgpp
|
||||
|
|
@ -1,156 +1,3 @@
|
|||
server:
|
||||
port: 9209
|
||||
compression:
|
||||
enabled: true
|
||||
mime-types: application/json
|
||||
|
||||
spring:
|
||||
#邮箱基本配置
|
||||
mail:
|
||||
# 配置在limit_time内,用户可以发送limit次验证码
|
||||
limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
limitTime: 10 这个是我额外的配置
|
||||
#配置smtp服务主机地址
|
||||
# sina smtp.sina.cn
|
||||
# aliyun smtp.aliyun.com
|
||||
# 163 smtp.163.com 端口号465或994
|
||||
host: mail.privateemail.com
|
||||
#发送者邮箱
|
||||
username: support@m2pool.cc
|
||||
#配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# password:
|
||||
# password: m2pool2024@!
|
||||
# password: axvm-zfgx-cgcg-qhhu
|
||||
password: m2pool2024@!
|
||||
#端口号
|
||||
port: 587
|
||||
#默认的邮件编码为UTF-8
|
||||
default-encoding: UTF-8
|
||||
#其他参数
|
||||
properties:
|
||||
mail:
|
||||
#配置SSL 加密工厂
|
||||
smtp:
|
||||
ssl:
|
||||
#本地测试,先放开ssl
|
||||
enable: false
|
||||
required: false
|
||||
#开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
debug: false
|
||||
socketFactory:
|
||||
class: javax.net.ssl.SSLSocketFactory
|
||||
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-chat
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: prod
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
# server-addr: 127.0.0.1:8808
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 2MB
|
||||
max-request-size: 8MB
|
||||
|
||||
myenv:
|
||||
domain: https://www.m2pool.com
|
||||
path: /var/www/html/web
|
||||
img: /img
|
||||
|
||||
|
||||
#测试环境
|
||||
#server:
|
||||
# port: 9509
|
||||
# compression:
|
||||
# enabled: true
|
||||
# mime-types: application/json
|
||||
#
|
||||
#spring:
|
||||
# #邮箱基本配置
|
||||
# mail:
|
||||
# # 配置在limit_time内,用户可以发送limit次验证码
|
||||
# limit: 2 这个是我额外的配置,结合邮箱服务用的
|
||||
# limitTime: 10 这个是我额外的配置
|
||||
# #配置smtp服务主机地址
|
||||
# # sina smtp.sina.cn
|
||||
# # aliyun smtp.aliyun.com
|
||||
# # 163 smtp.163.com 端口号465或994
|
||||
# host: mail.privateemail.com
|
||||
# #发送者邮箱
|
||||
# username: support@m2pool.cc
|
||||
# #配置密码,注意不是真正的密码,而是刚刚申请到的授权码
|
||||
# # password:
|
||||
# # password: m2pool2024@!
|
||||
# # password: axvm-zfgx-cgcg-qhhu
|
||||
# password: m2pool2024@!
|
||||
# #端口号
|
||||
# port: 587
|
||||
# #默认的邮件编码为UTF-8
|
||||
# default-encoding: UTF-8
|
||||
# #其他参数
|
||||
# properties:
|
||||
# mail:
|
||||
# #配置SSL 加密工厂
|
||||
# smtp:
|
||||
# ssl:
|
||||
# #本地测试,先放开ssl
|
||||
# enable: false
|
||||
# required: false
|
||||
# #开启debug模式,这样邮件发送过程的日志会在控制台打印出来,方便排查错误
|
||||
# debug: false
|
||||
# socketFactory:
|
||||
# class: javax.net.ssl.SSLSocketFactory
|
||||
#
|
||||
# application:
|
||||
# # 应用名称
|
||||
# name: m2pool-chat
|
||||
# profiles:
|
||||
# # 环境配置
|
||||
# active: test
|
||||
# cloud:
|
||||
# nacos:
|
||||
# discovery:
|
||||
# # 服务注册地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# namespace: m2_test
|
||||
# group: m2_test_group
|
||||
# # server-addr: 127.0.0.1:8808
|
||||
# config:
|
||||
# # 配置中心地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# # server-addr: 127.0.0.1:8808
|
||||
# # 配置文件格式
|
||||
# file-extension: yml
|
||||
# # 共享配置
|
||||
# shared-configs:
|
||||
# - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
# namespace: m2_test
|
||||
# group: m2_test_group
|
||||
# servlet:
|
||||
# multipart:
|
||||
# max-file-size: 2MB
|
||||
# max-request-size: 8MB
|
||||
#
|
||||
#myenv:
|
||||
# domain: https://test.m2pool.com
|
||||
# path: /var/www/html/web_test
|
||||
# img: /imgpp
|
||||
#
|
||||
active: prod
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.m2pool.chat.mapper.ChatMessageHistoryMapper">
|
||||
|
||||
|
||||
<select id="findHistoryMessage" resultType="com.m2pool.chat.dto.ChatMessageDto">
|
||||
SELECT id,
|
||||
type,
|
||||
send_email as sendEmail,
|
||||
content,
|
||||
create_time as createTime
|
||||
FROM chat_message_history
|
||||
WHERE id <![CDATA[ <= ]]> #{id} AND room_id = #{roomId}
|
||||
ORDER BY create_time DESC
|
||||
LIMIT #{pageNum}
|
||||
</select>
|
||||
</mapper>
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.m2pool.chat.mapper.ChatMessageMapper">
|
||||
|
||||
|
||||
<select id="findRecentlyMessage" resultType="com.m2pool.chat.dto.ChatMessageDto">
|
||||
SELECT id,
|
||||
type,
|
||||
send_email as sendEmail,
|
||||
content,
|
||||
create_time as createTime
|
||||
FROM chat_message
|
||||
WHERE id <![CDATA[ <= ]]> #{id} AND room_id = #{roomId}
|
||||
ORDER BY create_time DESC
|
||||
LIMIT #{pageNum}
|
||||
</select>
|
||||
<select id="findUnReadNums" resultType="java.util.Map">
|
||||
SELECT user_email as userEmail,
|
||||
count(*) as unReadNums
|
||||
FROM chat_message
|
||||
WHERE send_email IN
|
||||
<foreach collection="userEmails" item="userEmail" open="(" separator="," close=")">
|
||||
#{userEmail}
|
||||
</foreach>
|
||||
AND read_flag = false
|
||||
GROUP BY user_email
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.m2pool.chat.mapper.ChatRoomMapper">
|
||||
|
||||
<select id="findRoomByUserEmail" resultType="java.lang.String">
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
chat_room
|
||||
WHERE
|
||||
user_one_email = #{userEmail} AND user_two_email = #{email} OR
|
||||
(user_one_email = #{email} AND user_two_email = #{userEmail})
|
||||
</select>
|
||||
<select id="findRoomList" resultType="com.m2pool.chat.dto.ChatRoomDto">
|
||||
SELECT
|
||||
id,
|
||||
user_one_email AS userEmail,
|
||||
flag,
|
||||
last_user_send_time as lastUserSendTime,
|
||||
last_customer_send_time as lastCustomerSendTime
|
||||
FROM
|
||||
chat_room
|
||||
WHERE
|
||||
user_two_email = #{userEmail}
|
||||
AND del = FALSE
|
||||
GROUP BY
|
||||
flag DESC,
|
||||
last_user_send_time DESC,
|
||||
last_customer_send_time DESC
|
||||
</select>
|
||||
</mapper>
|
|
@ -3,6 +3,7 @@ package com.m2pool.system;
|
|||
import com.m2pool.common.security.annotation.EnableCustomConfig;
|
||||
import com.m2pool.common.security.annotation.EnableM2PoolFeignClients;
|
||||
import com.m2pool.common.swagger.annotation.EnableCustomSwagger2;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
@ -16,6 +17,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||
@EnableCustomSwagger2
|
||||
@EnableM2PoolFeignClients
|
||||
@SpringBootApplication
|
||||
@MapperScan({"com.m2pool.system.mapper"})
|
||||
public class M2PoolSystemApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 9201
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-system
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_prod
|
||||
group: m2_prod_group
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
server:
|
||||
port: 9501
|
||||
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-system
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8848
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
|
@ -1,58 +1,3 @@
|
|||
## Tomcat
|
||||
#server:
|
||||
# port: 9201
|
||||
#
|
||||
## Spring
|
||||
#spring:
|
||||
# application:
|
||||
# # 应用名称
|
||||
# name: m2pool-system
|
||||
# profiles:
|
||||
# # 环境配置
|
||||
# active: prod
|
||||
# cloud:
|
||||
# nacos:
|
||||
# discovery:
|
||||
# # 服务注册地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# namespace: m2_prod
|
||||
# group: m2_prod_group
|
||||
# config:
|
||||
# # 配置中心地址
|
||||
# server-addr: 127.0.0.1:8808
|
||||
# # 配置文件格式
|
||||
# file-extension: yml
|
||||
# # 共享配置
|
||||
# shared-configs:
|
||||
# - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
# namespace: m2_prod
|
||||
# group: m2_prod_group
|
||||
|
||||
|
||||
server:
|
||||
port: 9501
|
||||
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: m2pool-system
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: test
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 127.0.0.1:8808
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||
namespace: m2_test
|
||||
group: m2_test_group
|
||||
active: prod
|
Loading…
Reference in New Issue