Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gk
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
micro_app
gk
Commits
92f20b9f
Commit
92f20b9f
authored
Oct 17, 2025
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
班车模块增加接websocket
parent
c1d85650
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
499 additions
and
1 deletion
+499
-1
pom.xml
pom.xml
+4
-0
SecurityInterceptor.java
...dlbridge/jadp/common/interceptor/SecurityInterceptor.java
+7
-0
WebSocketConfig.java
...in/java/com/dlbridge/jadp/config/web/WebSocketConfig.java
+33
-0
BusService.java
...va/com/dlbridge/jadp/plugins/cmpt/service/BusService.java
+51
-1
WebSocketController.java
...java/com/dlbridge/jadp/websocket/WebSocketController.java
+71
-0
WebSocketEventListener.java
...a/com/dlbridge/jadp/websocket/WebSocketEventListener.java
+65
-0
WebSocketMessage.java
...in/java/com/dlbridge/jadp/websocket/WebSocketMessage.java
+30
-0
WebSocketSessionService.java
.../com/dlbridge/jadp/websocket/WebSocketSessionService.java
+238
-0
No files found.
pom.xml
View file @
92f20b9f
...
...
@@ -414,6 +414,10 @@
<artifactId>
aliyun-sdk-oss
</artifactId>
<version>
3.10.2
</version>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-websocket
</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
...
...
src/main/java/com/dlbridge/jadp/common/interceptor/SecurityInterceptor.java
View file @
92f20b9f
...
...
@@ -46,6 +46,13 @@ public class SecurityInterceptor extends JService implements HandlerInterceptor
@Override
public
boolean
preHandle
(
HttpServletRequest
request
,
HttpServletResponse
response
,
Object
handler
)
throws
Exception
{
// 检查请求头中是否有Upgrade: websocket
String
upgradeHeader
=
request
.
getHeader
(
"Upgrade"
);
if
(
"websocket"
.
equalsIgnoreCase
(
upgradeHeader
))
{
// 如果是WebSocket升级请求,可以选择不处理或特殊处理
// 然后返回true,继续执行
return
true
;
}
Map
<
String
,
Object
>
queryParams
=
getQueryParams
(
request
);
logger
.
info
(
"请求URL:"
+
request
.
getRequestURI
()+
queryParams
);
...
...
src/main/java/com/dlbridge/jadp/config/web/WebSocketConfig.java
0 → 100644
View file @
92f20b9f
package
com
.
dlbridge
.
jadp
.
config
.
web
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.messaging.simp.config.MessageBrokerRegistry
;
import
org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
;
import
org.springframework.web.socket.config.annotation.StompEndpointRegistry
;
import
org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
;
@Configuration
@EnableWebSocketMessageBroker
public
class
WebSocketConfig
implements
WebSocketMessageBrokerConfigurer
{
@Override
public
void
configureMessageBroker
(
MessageBrokerRegistry
config
)
{
// 启用简单的内存消息代理,指定消息代理目的地前缀
config
.
enableSimpleBroker
(
"/topic"
,
"/queue"
);
// 设置应用程序目的地前缀
config
.
setApplicationDestinationPrefixes
(
"/app"
);
// 设置用户目的地前缀(点对点消息)
config
.
setUserDestinationPrefix
(
"/user"
);
}
@Override
public
void
registerStompEndpoints
(
StompEndpointRegistry
registry
)
{
// 注册 STOMP 端点,指定使用 SockJS
registry
.
addEndpoint
(
"/ws"
).
setAllowedOrigins
(
"*"
)
// 允许所有来源,生产环境应指定具体域名
.
withSockJS
();
// 同时支持原生 WebSocket
registry
.
addEndpoint
(
"/ws"
)
.
setAllowedOrigins
(
"*"
);
}
}
src/main/java/com/dlbridge/jadp/plugins/cmpt/service/BusService.java
View file @
92f20b9f
...
...
@@ -7,12 +7,15 @@ import com.dlbridge.jadp.plugins.cmpt.dao.model.param.BusVerifyRecordParam;
import
com.dlbridge.jadp.plugins.cmpt.dao.model.vo.BusVerifyStatistics
;
import
com.dlbridge.jadp.plugins.cmpt.dto.*
;
import
com.dlbridge.jadp.plugins.cmpt.utils.Page
;
import
com.dlbridge.jadp.websocket.WebSocketSessionService
;
import
com.github.pagehelper.PageHelper
;
import
com.github.pagehelper.PageInfo
;
import
com.google.zxing.BarcodeFormat
;
import
com.google.zxing.EncodeHintType
;
import
com.google.zxing.client.j2se.MatrixToImageWriter
;
import
com.google.zxing.common.BitMatrix
;
import
com.google.zxing.qrcode.QRCodeWriter
;
import
com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
;
import
lombok.extern.log4j.Log4j2
;
import
org.apache.commons.lang.StringUtils
;
import
org.springframework.stereotype.Service
;
...
...
@@ -21,6 +24,8 @@ import com.dlbridge.jadp.plugins.cmpt.utils.PageConvertUtil;
import
javax.annotation.Resource
;
import
java.io.ByteArrayOutputStream
;
import
java.time.LocalDateTime
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
...
...
@@ -33,6 +38,9 @@ public class BusService {
@Resource
private
BusMapper
busMapper
;
@Resource
WebSocketSessionService
webSocketSessionService
;
public
List
<
BusTicketVerification
>
selectTicketByIdNumber
(
Integer
cmptId
,
String
idNumber
)
{
// 获取我购买的车票列表
List
<
BusTicketVerification
>
tickets
=
busMapper
.
selectTicketByIdNumber
(
cmptId
,
idNumber
);
...
...
@@ -74,8 +82,17 @@ public class BusService {
long
timestamp
=
System
.
currentTimeMillis
();
String
qrContent
=
String
.
format
(
"ticketNo=%s×tamp=%d"
,
ticket
.
getTicketNo
(),
timestamp
);
// 设置二维码参数,去除白边
Map
<
EncodeHintType
,
Object
>
hints
=
new
HashMap
<>();
hints
.
put
(
EncodeHintType
.
CHARACTER_SET
,
"UTF-8"
);
hints
.
put
(
EncodeHintType
.
MARGIN
,
0
);
// 关键:设置边距为0
hints
.
put
(
EncodeHintType
.
ERROR_CORRECTION
,
ErrorCorrectionLevel
.
M
);
// 错误纠正级别
QRCodeWriter
qrCodeWriter
=
new
QRCodeWriter
();
BitMatrix
bitMatrix
=
qrCodeWriter
.
encode
(
qrContent
,
BarcodeFormat
.
QR_CODE
,
250
,
250
);
BitMatrix
bitMatrix
=
qrCodeWriter
.
encode
(
qrContent
,
BarcodeFormat
.
QR_CODE
,
200
,
200
,
hints
);
// 进一步处理去除白边
bitMatrix
=
removeWhiteBorder
(
bitMatrix
);
ByteArrayOutputStream
outputStream
=
new
ByteArrayOutputStream
();
MatrixToImageWriter
.
writeToStream
(
bitMatrix
,
"PNG"
,
outputStream
);
...
...
@@ -87,6 +104,31 @@ public class BusService {
}
}
/**
* 去除二维码白边的辅助方法
*/
private
BitMatrix
removeWhiteBorder
(
BitMatrix
matrix
)
{
int
[]
enclosingRectangle
=
matrix
.
getEnclosingRectangle
();
if
(
enclosingRectangle
==
null
)
{
return
matrix
;
}
int
left
=
enclosingRectangle
[
0
];
int
top
=
enclosingRectangle
[
1
];
int
width
=
enclosingRectangle
[
2
];
int
height
=
enclosingRectangle
[
3
];
BitMatrix
newMatrix
=
new
BitMatrix
(
width
,
height
);
for
(
int
x
=
0
;
x
<
width
;
x
++)
{
for
(
int
y
=
0
;
y
<
height
;
y
++)
{
if
(
matrix
.
get
(
x
+
left
,
y
+
top
))
{
newMatrix
.
set
(
x
,
y
);
}
}
}
return
newMatrix
;
}
/**
* 车票核销方法
* @param param 核销参数
...
...
@@ -132,6 +174,14 @@ public class BusService {
record
.
setVerifierId
(
param
.
getVerifierId
());
record
.
setVerificationStationId
(
ticket
.
getBoardingStationId
());
busMapper
.
insertVerificationRecord
(
record
);
// 发送核销成功通知
LocalDateTime
now
=
LocalDateTime
.
now
();
String
successMessage
=
String
.
format
(
"车票核销成功!班次:%s,核销时间:%s"
,
ticket
.
getScheduleName
(),
now
.
format
(
java
.
time
.
format
.
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
)));
webSocketSessionService
.
sendVerificationSuccess
(
ticket
.
getTicketNo
(),
successMessage
);
log
.
info
(
"车票核销成功并已发送通知 - 票号: {}, 用户: {}"
,
ticket
.
getTicketNo
(),
ticket
.
getIdNumber
());
return
Result
.
success
();
}
...
...
src/main/java/com/dlbridge/jadp/websocket/WebSocketController.java
0 → 100644
View file @
92f20b9f
package
com
.
dlbridge
.
jadp
.
websocket
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.messaging.handler.annotation.MessageMapping
;
import
org.springframework.messaging.handler.annotation.Payload
;
import
org.springframework.messaging.simp.annotation.SubscribeMapping
;
import
org.springframework.stereotype.Controller
;
import
java.util.Map
;
@Slf4j
@Controller
@RequiredArgsConstructor
public
class
WebSocketController
{
private
final
WebSocketSessionService
sessionService
;
/**
* 处理客户端发送的心跳消息
*/
@MessageMapping
(
"/heartbeat"
)
public
void
handleHeartbeat
(
@Payload
Map
<
String
,
Object
>
message
)
{
String
sessionId
=
(
String
)
message
.
get
(
"sessionId"
);
String
userId
=
(
String
)
message
.
get
(
"userId"
);
log
.
info
(
"收到心跳消息 - 会话ID: {}, 用户ID: {}"
,
sessionId
,
userId
);
}
/**
* 处理客户端订阅特定票号
*/
@MessageMapping
(
"/ticket/subscribe"
)
public
void
handleTicketSubscribe
(
@Payload
Map
<
String
,
Object
>
message
)
{
String
sessionId
=
(
String
)
message
.
get
(
"sessionId"
);
String
userId
=
(
String
)
message
.
get
(
"userId"
);
String
ticketNo
=
(
String
)
message
.
get
(
"ticketNo"
);
log
.
info
(
"客户端订阅票号 - 会话ID: {}, 用户ID: {}, 票号: {}"
,
sessionId
,
userId
,
ticketNo
);
if
(
userId
!=
null
&&
ticketNo
!=
null
)
{
sessionService
.
registerUserSession
(
userId
,
ticketNo
,
sessionId
);
// 发送订阅成功确认
WebSocketMessage
response
=
WebSocketMessage
.
info
(
ticketNo
,
"票号订阅成功,开始接收核销通知"
);
sessionService
.
sendToUser
(
userId
,
response
);
}
}
/**
* 处理客户端取消订阅票号
*/
@MessageMapping
(
"/ticket/unsubscribe"
)
public
void
handleTicketUnsubscribe
(
@Payload
Map
<
String
,
Object
>
message
)
{
String
sessionId
=
(
String
)
message
.
get
(
"sessionId"
);
String
ticketNo
=
(
String
)
message
.
get
(
"ticketNo"
);
log
.
info
(
"客户端取消订阅票号 - 会话ID: {}, 票号: {}"
,
sessionId
,
ticketNo
);
sessionService
.
removeTicketSession
(
sessionId
,
ticketNo
);
}
/**
* 处理获取会话状态请求
*/
@SubscribeMapping
(
"/user/queue/status"
)
public
Map
<
String
,
Object
>
handleStatusRequest
()
{
return
sessionService
.
getSessionStats
();
}
}
\ No newline at end of file
src/main/java/com/dlbridge/jadp/websocket/WebSocketEventListener.java
0 → 100644
View file @
92f20b9f
package
com
.
dlbridge
.
jadp
.
websocket
;
import
lombok.RequiredArgsConstructor
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.context.event.EventListener
;
import
org.springframework.messaging.simp.stomp.StompHeaderAccessor
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.socket.messaging.SessionConnectEvent
;
import
org.springframework.web.socket.messaging.SessionDisconnectEvent
;
import
org.springframework.web.socket.messaging.SessionSubscribeEvent
;
@Slf4j
@Component
@RequiredArgsConstructor
public
class
WebSocketEventListener
{
private
final
WebSocketSessionService
sessionService
;
/**
* 处理 WebSocket 连接建立事件
*/
@EventListener
public
void
handleWebSocketConnectListener
(
SessionConnectEvent
event
)
{
StompHeaderAccessor
headerAccessor
=
StompHeaderAccessor
.
wrap
(
event
.
getMessage
());
String
sessionId
=
headerAccessor
.
getSessionId
();
String
userId
=
headerAccessor
.
getFirstNativeHeader
(
"userId"
);
log
.
info
(
"WebSocket 连接建立 - 会话ID: {}, 用户ID: {}"
,
sessionId
,
userId
);
}
/**
* 处理 WebSocket 订阅事件
*/
@EventListener
public
void
handleWebSocketSubscribeListener
(
SessionSubscribeEvent
event
)
{
StompHeaderAccessor
headerAccessor
=
StompHeaderAccessor
.
wrap
(
event
.
getMessage
());
String
sessionId
=
headerAccessor
.
getSessionId
();
String
destination
=
headerAccessor
.
getDestination
();
String
userId
=
headerAccessor
.
getFirstNativeHeader
(
"userId"
);
String
ticketNo
=
headerAccessor
.
getFirstNativeHeader
(
"ticketNo"
);
log
.
info
(
"WebSocket 订阅 - 会话ID: {}, 目的地: {}, 用户ID: {}, 票号: {}"
,
sessionId
,
destination
,
userId
,
ticketNo
);
if
(
userId
!=
null
&&
ticketNo
!=
null
)
{
sessionService
.
registerUserSession
(
userId
,
ticketNo
,
sessionId
);
}
}
/**
* 处理 WebSocket 连接断开事件
*/
@EventListener
public
void
handleWebSocketDisconnectListener
(
SessionDisconnectEvent
event
)
{
StompHeaderAccessor
headerAccessor
=
StompHeaderAccessor
.
wrap
(
event
.
getMessage
());
String
sessionId
=
headerAccessor
.
getSessionId
();
log
.
info
(
"WebSocket 连接断开 - 会话ID: {}"
,
sessionId
);
sessionService
.
removeUserSession
(
sessionId
);
}
}
src/main/java/com/dlbridge/jadp/websocket/WebSocketMessage.java
0 → 100644
View file @
92f20b9f
package
com
.
dlbridge
.
jadp
.
websocket
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
java.time.LocalDateTime
;
@Data
@NoArgsConstructor
@AllArgsConstructor
public
class
WebSocketMessage
{
private
String
type
;
// 消息类型:VERIFICATION_SUCCESS, ERROR, etc.
private
String
ticketNo
;
// 票号
private
String
message
;
// 消息内容
private
LocalDateTime
timestamp
;
// 时间戳
private
Object
data
;
// 附加数据
public
static
WebSocketMessage
success
(
String
ticketNo
,
String
message
)
{
return
new
WebSocketMessage
(
"VERIFICATION_SUCCESS"
,
ticketNo
,
message
,
LocalDateTime
.
now
(),
null
);
}
public
static
WebSocketMessage
error
(
String
ticketNo
,
String
message
)
{
return
new
WebSocketMessage
(
"ERROR"
,
ticketNo
,
message
,
LocalDateTime
.
now
(),
null
);
}
public
static
WebSocketMessage
info
(
String
ticketNo
,
String
message
)
{
return
new
WebSocketMessage
(
"INFO"
,
ticketNo
,
message
,
LocalDateTime
.
now
(),
null
);
}
}
src/main/java/com/dlbridge/jadp/websocket/WebSocketSessionService.java
0 → 100644
View file @
92f20b9f
package
com
.
dlbridge
.
jadp
.
websocket
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.messaging.simp.SimpMessagingTemplate
;
import
org.springframework.stereotype.Service
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.*
;
@Slf4j
@Service
public
class
WebSocketSessionService
{
private
final
SimpMessagingTemplate
messagingTemplate
;
// 存储用户与票号的映射关系(一个用户对应多个票号)
private
final
Map
<
String
,
Set
<
String
>>
userTicketsMap
=
new
ConcurrentHashMap
<>();
// 存储票号与用户的映射关系
private
final
Map
<
String
,
String
>
ticketUserMap
=
new
ConcurrentHashMap
<>();
// 存储会话与用户的映射关系
private
final
Map
<
String
,
String
>
sessionUserMap
=
new
ConcurrentHashMap
<>();
// 存储会话与票号的映射关系(一个会话可能监听多个票号)
private
final
Map
<
String
,
Set
<
String
>>
sessionTicketsMap
=
new
ConcurrentHashMap
<>();
public
WebSocketSessionService
(
SimpMessagingTemplate
messagingTemplate
)
{
this
.
messagingTemplate
=
messagingTemplate
;
}
/**
* 注册用户会话
* @param userId 用户ID(赛事ID+证件号,例:29643-HK002949527)
* @param ticketNo 票号
* @param sessionId 会话ID
*/
public
void
registerUserSession
(
String
userId
,
String
ticketNo
,
String
sessionId
)
{
// 更新用户-票号映射
userTicketsMap
.
computeIfAbsent
(
userId
,
k
->
ConcurrentHashMap
.
newKeySet
()).
add
(
ticketNo
);
// 更新票号-用户映射
ticketUserMap
.
put
(
ticketNo
,
userId
);
// 更新会话-用户映射
sessionUserMap
.
put
(
sessionId
,
userId
);
// 更新会话-票号映射
sessionTicketsMap
.
computeIfAbsent
(
sessionId
,
k
->
ConcurrentHashMap
.
newKeySet
()).
add
(
ticketNo
);
log
.
info
(
"用户会话注册成功 - 用户: {}, 票号: {}, 会话ID: {}"
,
userId
,
ticketNo
,
sessionId
);
log
.
debug
(
"当前会话状态 - 用户数: {}, 票号数: {}, 会话数: {}"
,
userTicketsMap
.
size
(),
ticketUserMap
.
size
(),
sessionUserMap
.
size
());
}
/**
* 移除用户会话
* @param sessionId 会话ID
*/
public
void
removeUserSession
(
String
sessionId
)
{
String
userId
=
sessionUserMap
.
remove
(
sessionId
);
if
(
userId
!=
null
)
{
// 移除会话相关的票号
Set
<
String
>
ticketNos
=
sessionTicketsMap
.
remove
(
sessionId
);
if
(
ticketNos
!=
null
)
{
// 从用户-票号映射中移除这些票号
Set
<
String
>
userTickets
=
userTicketsMap
.
get
(
userId
);
if
(
userTickets
!=
null
)
{
userTickets
.
removeAll
(
ticketNos
);
// 如果用户没有票号了,移除用户
if
(
userTickets
.
isEmpty
())
{
userTicketsMap
.
remove
(
userId
);
}
}
// 从票号-用户映射中移除这些票号
ticketNos
.
forEach
(
ticketUserMap:
:
remove
);
}
log
.
info
(
"用户会话移除成功 - 会话ID: {}, 用户: {}"
,
sessionId
,
userId
);
}
else
{
log
.
warn
(
"移除不存在的会话 - 会话ID: {}"
,
sessionId
);
}
}
/**
* 移除特定票号的会话
* @param sessionId 会话ID
* @param ticketNo 票号
*/
public
void
removeTicketSession
(
String
sessionId
,
String
ticketNo
)
{
// 从会话-票号映射中移除
Set
<
String
>
sessionTickets
=
sessionTicketsMap
.
get
(
sessionId
);
if
(
sessionTickets
!=
null
)
{
sessionTickets
.
remove
(
ticketNo
);
if
(
sessionTickets
.
isEmpty
())
{
sessionTicketsMap
.
remove
(
sessionId
);
}
}
// 从用户-票号映射中移除
String
userId
=
sessionUserMap
.
get
(
sessionId
);
if
(
userId
!=
null
)
{
Set
<
String
>
userTickets
=
userTicketsMap
.
get
(
userId
);
if
(
userTickets
!=
null
)
{
userTickets
.
remove
(
ticketNo
);
if
(
userTickets
.
isEmpty
())
{
userTicketsMap
.
remove
(
userId
);
}
}
}
// 从票号-用户映射中移除
ticketUserMap
.
remove
(
ticketNo
);
log
.
info
(
"票号会话移除成功 - 会话ID: {}, 票号: {}"
,
sessionId
,
ticketNo
);
}
/**
* 根据票号获取用户ID
*/
public
String
getUserIdByTicketNo
(
String
ticketNo
)
{
return
ticketUserMap
.
get
(
ticketNo
);
}
/**
* 根据用户ID获取所有票号
*/
public
Set
<
String
>
getTicketNosByUserId
(
String
userId
)
{
return
userTicketsMap
.
getOrDefault
(
userId
,
Collections
.
emptySet
());
}
/**
* 根据用户ID获取所有会话ID
*/
public
Set
<
String
>
getSessionIdsByUserId
(
String
userId
)
{
Set
<
String
>
sessionIds
=
new
HashSet
<>();
sessionUserMap
.
forEach
((
sessionId
,
uid
)
->
{
if
(
uid
.
equals
(
userId
))
{
sessionIds
.
add
(
sessionId
);
}
});
return
sessionIds
;
}
/**
* 发送核销成功通知给指定票号对应的用户
*/
public
void
sendVerificationSuccess
(
String
ticketNo
,
String
message
)
{
String
userId
=
getUserIdByTicketNo
(
ticketNo
);
if
(
userId
!=
null
)
{
String
destination
=
"/topic/verification/"
+
userId
;
WebSocketMessage
wsMessage
=
WebSocketMessage
.
success
(
ticketNo
,
message
);
messagingTemplate
.
convertAndSend
(
destination
,
wsMessage
);
log
.
info
(
"核销成功通知已发送 - 用户: {}, 票号: {}, 消息: {}"
,
userId
,
ticketNo
,
message
);
// 核销成功后,移除该票号的会话映射
removeTicketMappings
(
ticketNo
);
}
else
{
log
.
warn
(
"未找到对应的用户会话,无法发送核销通知 - 票号: {}"
,
ticketNo
);
}
}
/**
* 发送错误通知给指定票号对应的用户
*/
public
void
sendError
(
String
ticketNo
,
String
errorMessage
)
{
String
userId
=
getUserIdByTicketNo
(
ticketNo
);
if
(
userId
!=
null
)
{
String
destination
=
"/topic/errors/"
+
userId
;
WebSocketMessage
wsMessage
=
WebSocketMessage
.
error
(
ticketNo
,
errorMessage
);
messagingTemplate
.
convertAndSend
(
destination
,
wsMessage
);
log
.
info
(
"错误通知已发送 - 用户: {}, 票号: {}, 错误: {}"
,
userId
,
ticketNo
,
errorMessage
);
}
}
/**
* 发送通知给指定用户的所有会话
*/
public
void
sendToUser
(
String
userId
,
WebSocketMessage
message
)
{
String
destination
=
"/topic/user/"
+
userId
;
messagingTemplate
.
convertAndSend
(
destination
,
message
);
log
.
info
(
"用户通知已发送 - 用户: {}, 类型: {}"
,
userId
,
message
.
getType
());
}
/**
* 移除票号的所有映射关系
*/
private
void
removeTicketMappings
(
String
ticketNo
)
{
String
userId
=
ticketUserMap
.
remove
(
ticketNo
);
if
(
userId
!=
null
)
{
// 从用户-票号映射中移除
Set
<
String
>
userTickets
=
userTicketsMap
.
get
(
userId
);
if
(
userTickets
!=
null
)
{
userTickets
.
remove
(
ticketNo
);
if
(
userTickets
.
isEmpty
())
{
userTicketsMap
.
remove
(
userId
);
}
}
// 从所有会话的票号集合中移除
sessionTicketsMap
.
values
().
forEach
(
tickets
->
tickets
.
remove
(
ticketNo
));
log
.
info
(
"票号映射关系已移除 - 票号: {}"
,
ticketNo
);
}
}
/**
* 获取当前会话统计信息(用于监控)
*/
public
Map
<
String
,
Object
>
getSessionStats
()
{
Map
<
String
,
Object
>
stats
=
new
HashMap
<>();
stats
.
put
(
"userCount"
,
userTicketsMap
.
size
());
stats
.
put
(
"ticketCount"
,
ticketUserMap
.
size
());
stats
.
put
(
"sessionCount"
,
sessionUserMap
.
size
());
stats
.
put
(
"activeSessions"
,
sessionTicketsMap
.
size
());
// 计算平均每个用户的票号数
double
avgTicketsPerUser
=
userTicketsMap
.
values
().
stream
()
.
mapToInt
(
Set:
:
size
)
.
average
()
.
orElse
(
0.0
);
stats
.
put
(
"avgTicketsPerUser"
,
avgTicketsPerUser
);
return
stats
;
}
/**
* 清理过期会话(可定时调用)
*/
public
void
cleanupExpiredSessions
()
{
// 这里可以实现会话过期清理逻辑
// 比如移除长时间没有活动的会话
log
.
debug
(
"清理过期会话 - 当前会话数: {}"
,
sessionUserMap
.
size
());
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment