refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage
This commit is contained in:
+33
@@ -0,0 +1,33 @@
|
||||
package cn.novalon.gym.manage.notify.config;
|
||||
|
||||
import cn.novalon.gym.manage.notify.websocket.SysWebSocketHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
|
||||
@Bean
|
||||
public HandlerMapping webSocketHandlerMapping(SysWebSocketHandler webSocketHandler) {
|
||||
Map<String, WebSocketHandler> map = new HashMap<>();
|
||||
map.put("/ws", webSocketHandler);
|
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
|
||||
handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||
handlerMapping.setUrlMap(map);
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSocketHandlerAdapter webSocketHandlerAdapter() {
|
||||
return new WebSocketHandlerAdapter();
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package cn.novalon.gym.manage.notify.core.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SysNotice {
|
||||
|
||||
private Long id;
|
||||
private String noticeTitle;
|
||||
private String noticeType;
|
||||
private String noticeContent;
|
||||
private String status;
|
||||
private String createBy;
|
||||
private String updateBy;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
private LocalDateTime deletedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getNoticeTitle() {
|
||||
return noticeTitle;
|
||||
}
|
||||
|
||||
public void setNoticeTitle(String noticeTitle) {
|
||||
this.noticeTitle = noticeTitle;
|
||||
}
|
||||
|
||||
public String getNoticeType() {
|
||||
return noticeType;
|
||||
}
|
||||
|
||||
public void setNoticeType(String noticeType) {
|
||||
this.noticeType = noticeType;
|
||||
}
|
||||
|
||||
public String getNoticeContent() {
|
||||
return noticeContent;
|
||||
}
|
||||
|
||||
public void setNoticeContent(String noticeContent) {
|
||||
this.noticeContent = noticeContent;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getCreateBy() {
|
||||
return createBy;
|
||||
}
|
||||
|
||||
public void setCreateBy(String createBy) {
|
||||
this.createBy = createBy;
|
||||
}
|
||||
|
||||
public String getUpdateBy() {
|
||||
return updateBy;
|
||||
}
|
||||
|
||||
public void setUpdateBy(String updateBy) {
|
||||
this.updateBy = updateBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getDeletedAt() {
|
||||
return deletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedAt(LocalDateTime deletedAt) {
|
||||
this.deletedAt = deletedAt;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package cn.novalon.gym.manage.notify.core.domain;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SysUserMessage {
|
||||
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private String title;
|
||||
private String content;
|
||||
private String messageType;
|
||||
private String isRead;
|
||||
private LocalDateTime createTime;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getUserId() { return userId; }
|
||||
public void setUserId(Long userId) { this.userId = userId; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public String getMessageType() { return messageType; }
|
||||
public void setMessageType(String messageType) { this.messageType = messageType; }
|
||||
public String getIsRead() { return isRead; }
|
||||
public void setIsRead(String isRead) { this.isRead = isRead; }
|
||||
public LocalDateTime getCreateTime() { return createTime; }
|
||||
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package cn.novalon.gym.manage.notify.core.query;
|
||||
|
||||
/**
|
||||
* 用户消息查询对象
|
||||
*
|
||||
* @author 张翔
|
||||
* @date 2026-03-13
|
||||
*/
|
||||
public class SysUserMessageQuery {
|
||||
|
||||
private Long userId;
|
||||
private String isRead;
|
||||
private String keyword;
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getIsRead() {
|
||||
return isRead;
|
||||
}
|
||||
|
||||
public void setIsRead(String isRead) {
|
||||
this.isRead = isRead;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package cn.novalon.gym.manage.notify.core.repository;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysNotice;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysNoticeRepository {
|
||||
|
||||
Flux<SysNotice> findByDeletedAtIsNull();
|
||||
|
||||
Flux<SysNotice> findByStatusAndDeletedAtIsNull(String status);
|
||||
|
||||
Mono<SysNotice> findById(Long id);
|
||||
|
||||
Mono<SysNotice> save(SysNotice notice);
|
||||
|
||||
Mono<Void> deleteByIdAndDeletedAtIsNull(Long id);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.novalon.gym.manage.notify.core.repository;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysUserMessage;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysUserMessageRepository {
|
||||
|
||||
Flux<SysUserMessage> findByUserIdOrderByCreateTimeDesc(Long userId);
|
||||
|
||||
Flux<SysUserMessage> findByUserIdAndIsReadOrderByCreateTimeDesc(Long userId, String isRead);
|
||||
|
||||
Mono<Long> countByUserIdAndIsRead(Long userId, String isRead);
|
||||
|
||||
Mono<SysUserMessage> save(SysUserMessage message);
|
||||
|
||||
Mono<SysUserMessage> findById(Long id);
|
||||
|
||||
Mono<Void> deleteById(Long id);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.novalon.gym.manage.notify.core.service;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysNotice;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysNoticeService {
|
||||
|
||||
Flux<SysNotice> getAllNotices();
|
||||
|
||||
Mono<SysNotice> getNoticeById(Long id);
|
||||
|
||||
Flux<SysNotice> getNoticesByStatus(String status);
|
||||
|
||||
Mono<SysNotice> createNotice(SysNotice notice);
|
||||
|
||||
Mono<SysNotice> updateNotice(Long id, SysNotice notice);
|
||||
|
||||
Mono<Void> deleteNotice(Long id);
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cn.novalon.gym.manage.notify.core.service;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysUserMessage;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ISysUserMessageService {
|
||||
|
||||
Flux<SysUserMessage> getMessagesByUser(Long userId);
|
||||
|
||||
Mono<Long> getUnreadCount(Long userId);
|
||||
|
||||
Flux<SysUserMessage> getUnreadMessages(Long userId);
|
||||
|
||||
Mono<SysUserMessage> createMessage(SysUserMessage message);
|
||||
|
||||
Mono<SysUserMessage> markAsRead(Long id);
|
||||
|
||||
Mono<Void> deleteMessage(Long id);
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package cn.novalon.gym.manage.notify.core.service.impl;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysNotice;
|
||||
import cn.novalon.gym.manage.notify.core.repository.ISysNoticeRepository;
|
||||
import cn.novalon.gym.manage.notify.core.service.ISysNoticeService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Service
|
||||
public class SysNoticeServiceImpl implements ISysNoticeService {
|
||||
|
||||
private final ISysNoticeRepository noticeRepository;
|
||||
|
||||
public SysNoticeServiceImpl(ISysNoticeRepository noticeRepository) {
|
||||
this.noticeRepository = noticeRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysNotice> getAllNotices() {
|
||||
return noticeRepository.findByDeletedAtIsNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysNotice> getNoticeById(Long id) {
|
||||
return noticeRepository.findById(id)
|
||||
.filter(notice -> notice.getDeletedAt() == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysNotice> getNoticesByStatus(String status) {
|
||||
return noticeRepository.findByStatusAndDeletedAtIsNull(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysNotice> createNotice(SysNotice notice) {
|
||||
notice.setCreatedAt(LocalDateTime.now());
|
||||
return noticeRepository.save(notice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysNotice> updateNotice(Long id, SysNotice notice) {
|
||||
return noticeRepository.findById(id)
|
||||
.flatMap(existingNotice -> {
|
||||
if (notice.getNoticeTitle() != null) {
|
||||
existingNotice.setNoticeTitle(notice.getNoticeTitle());
|
||||
}
|
||||
if (notice.getNoticeContent() != null) {
|
||||
existingNotice.setNoticeContent(notice.getNoticeContent());
|
||||
}
|
||||
if (notice.getStatus() != null) {
|
||||
existingNotice.setStatus(notice.getStatus());
|
||||
}
|
||||
if (notice.getNoticeType() != null) {
|
||||
existingNotice.setNoticeType(notice.getNoticeType());
|
||||
}
|
||||
existingNotice.setUpdatedAt(LocalDateTime.now());
|
||||
return noticeRepository.save(existingNotice);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteNotice(Long id) {
|
||||
return noticeRepository.findById(id)
|
||||
.filter(notice -> notice.getDeletedAt() == null)
|
||||
.flatMap(notice -> {
|
||||
notice.setDeletedAt(LocalDateTime.now());
|
||||
return noticeRepository.save(notice);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package cn.novalon.gym.manage.notify.core.service.impl;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysUserMessage;
|
||||
import cn.novalon.gym.manage.notify.core.repository.ISysUserMessageRepository;
|
||||
import cn.novalon.gym.manage.notify.core.service.ISysUserMessageService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Service
|
||||
public class SysUserMessageServiceImpl implements ISysUserMessageService {
|
||||
|
||||
private final ISysUserMessageRepository messageRepository;
|
||||
|
||||
public SysUserMessageServiceImpl(ISysUserMessageRepository messageRepository) {
|
||||
this.messageRepository = messageRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysUserMessage> getMessagesByUser(Long userId) {
|
||||
return messageRepository.findByUserIdOrderByCreateTimeDesc(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> getUnreadCount(Long userId) {
|
||||
return messageRepository.countByUserIdAndIsRead(userId, "0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<SysUserMessage> getUnreadMessages(Long userId) {
|
||||
return messageRepository.findByUserIdAndIsReadOrderByCreateTimeDesc(userId, "0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysUserMessage> createMessage(SysUserMessage message) {
|
||||
message.setCreateTime(LocalDateTime.now());
|
||||
message.setIsRead("0");
|
||||
return messageRepository.save(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<SysUserMessage> markAsRead(Long id) {
|
||||
return messageRepository.findById(id)
|
||||
.flatMap(message -> {
|
||||
message.setIsRead("1");
|
||||
return messageRepository.save(message);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteMessage(Long id) {
|
||||
return messageRepository.deleteById(id);
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package cn.novalon.gym.manage.notify.handler;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysNotice;
|
||||
import cn.novalon.gym.manage.notify.core.service.ISysNoticeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@Tag(name = "通知管理", description = "系统通知相关操作")
|
||||
public class SysNoticeHandler {
|
||||
|
||||
private final ISysNoticeService noticeService;
|
||||
private static final List<String> VALID_NOTICE_TYPES = Arrays.asList("1", "2");
|
||||
private static final List<String> VALID_STATUSES = Arrays.asList("0", "1");
|
||||
|
||||
public SysNoticeHandler(ISysNoticeService noticeService) {
|
||||
this.noticeService = noticeService;
|
||||
}
|
||||
|
||||
@Operation(summary = "获取所有通知", description = "获取系统中所有通知列表")
|
||||
public Mono<ServerResponse> getAllNotices(ServerRequest request) {
|
||||
Flux<SysNotice> notices = noticeService.getAllNotices();
|
||||
return ServerResponse.ok().body(notices, SysNotice.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "根据ID获取通知", description = "根据通知ID获取通知详细信息")
|
||||
public Mono<ServerResponse> getNoticeById(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return noticeService.getNoticeById(id)
|
||||
.flatMap(notice -> ServerResponse.ok().bodyValue(notice))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "根据状态获取通知", description = "根据状态获取通知列表")
|
||||
public Mono<ServerResponse> getNoticesByStatus(ServerRequest request) {
|
||||
String status = request.pathVariable("status");
|
||||
Flux<SysNotice> notices = noticeService.getNoticesByStatus(status);
|
||||
return ServerResponse.ok().body(notices, SysNotice.class);
|
||||
}
|
||||
|
||||
@Operation(summary = "创建通知", description = "创建新通知")
|
||||
public Mono<ServerResponse> createNotice(ServerRequest request) {
|
||||
return request.bodyToMono(SysNotice.class)
|
||||
.filter(notice -> notice.getNoticeTitle() != null && !notice.getNoticeTitle().trim().isEmpty())
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("公告标题不能为空")))
|
||||
.filter(notice -> VALID_NOTICE_TYPES.contains(notice.getNoticeType()))
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("公告类型必须是1(通知)或2(公告)")))
|
||||
.filter(notice -> notice.getNoticeContent() != null && !notice.getNoticeContent().trim().isEmpty())
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("公告内容不能为空")))
|
||||
.filter(notice -> notice.getStatus() == null || VALID_STATUSES.contains(notice.getStatus()))
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("状态必须是0(正常)或1(关闭)")))
|
||||
.flatMap(noticeService::createNotice)
|
||||
.flatMap(notice -> ServerResponse.created(request.uriBuilder().path("/{id}").build(notice.getId())).bodyValue(notice))
|
||||
.onErrorResume(IllegalArgumentException.class, ex -> {
|
||||
return ServerResponse.badRequest().bodyValue(Map.of(
|
||||
"code", HttpStatus.BAD_REQUEST.value(),
|
||||
"message", ex.getMessage(),
|
||||
"timestamp", LocalDateTime.now()
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@Operation(summary = "更新通知", description = "更新通知信息")
|
||||
public Mono<ServerResponse> updateNotice(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return request.bodyToMono(SysNotice.class)
|
||||
.flatMap(notice -> noticeService.updateNotice(id, notice))
|
||||
.flatMap(notice -> ServerResponse.ok().bodyValue(notice))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "删除通知", description = "删除指定通知")
|
||||
public Mono<ServerResponse> deleteNotice(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return noticeService.getNoticeById(id)
|
||||
.filter(notice -> notice.getDeletedAt() == null)
|
||||
.flatMap(notice -> noticeService.deleteNotice(id)
|
||||
.then(ServerResponse.noContent().build()))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package cn.novalon.gym.manage.notify.handler;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysUserMessage;
|
||||
import cn.novalon.gym.manage.notify.core.service.ISysUserMessageService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Component
|
||||
public class SysUserMessageHandler {
|
||||
|
||||
private final ISysUserMessageService messageService;
|
||||
|
||||
public SysUserMessageHandler(ISysUserMessageService messageService) {
|
||||
this.messageService = messageService;
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> getMessagesByUser(ServerRequest request) {
|
||||
Long userId = Long.parseLong(request.pathVariable("userId"));
|
||||
Flux<SysUserMessage> messages = messageService.getMessagesByUser(userId);
|
||||
return ServerResponse.ok().body(messages, SysUserMessage.class);
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> getUnreadCount(ServerRequest request) {
|
||||
Long userId = Long.parseLong(request.pathVariable("userId"));
|
||||
return messageService.getUnreadCount(userId)
|
||||
.flatMap(count -> ServerResponse.ok().bodyValue(count));
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> getUnreadList(ServerRequest request) {
|
||||
Long userId = Long.parseLong(request.pathVariable("userId"));
|
||||
Flux<SysUserMessage> messages = messageService.getUnreadMessages(userId);
|
||||
return ServerResponse.ok().body(messages, SysUserMessage.class);
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> createMessage(ServerRequest request) {
|
||||
return request.bodyToMono(SysUserMessage.class)
|
||||
.flatMap(messageService::createMessage)
|
||||
.flatMap(message -> ServerResponse.ok().bodyValue(message));
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> markAsRead(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return messageService.markAsRead(id)
|
||||
.flatMap(message -> ServerResponse.ok().bodyValue(message))
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
|
||||
public Mono<ServerResponse> deleteMessage(ServerRequest request) {
|
||||
Long id = Long.parseLong(request.pathVariable("id"));
|
||||
return messageService.deleteMessage(id)
|
||||
.then(ServerResponse.ok().build())
|
||||
.switchIfEmpty(ServerResponse.notFound().build());
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package cn.novalon.gym.manage.notify.websocket;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Component
|
||||
public class SysWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
|
||||
private final Map<String, LocalDateTime> lastActivityTime = new ConcurrentHashMap<>();
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Value("${websocket.idle-timeout:300s}")
|
||||
private Duration idleTimeout;
|
||||
|
||||
@Value("${websocket.heartbeat-interval:30s}")
|
||||
private Duration heartbeatInterval;
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession session) {
|
||||
String userId = extractUserId(session);
|
||||
lastActivityTime.put(userId, LocalDateTime.now());
|
||||
|
||||
return session.receive()
|
||||
.doOnNext(message -> {
|
||||
String payload = message.getPayloadAsText();
|
||||
handleIncomingMessage(session, userId, payload);
|
||||
lastActivityTime.put(userId, LocalDateTime.now());
|
||||
})
|
||||
.doOnComplete(() -> {
|
||||
sessions.remove(userId);
|
||||
lastActivityTime.remove(userId);
|
||||
System.out.println("WebSocket session closed for user: " + userId);
|
||||
})
|
||||
.doOnError(error -> {
|
||||
sessions.remove(userId);
|
||||
lastActivityTime.remove(userId);
|
||||
System.err.println("WebSocket error for user " + userId + ": " + error.getMessage());
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 60000)
|
||||
public void cleanupIdleConnections() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
lastActivityTime.entrySet().removeIf(entry -> {
|
||||
LocalDateTime lastActivity = entry.getValue();
|
||||
if (Duration.between(lastActivity, now).compareTo(idleTimeout) > 0) {
|
||||
String userId = entry.getKey();
|
||||
WebSocketSession session = sessions.get(userId);
|
||||
if (session != null) {
|
||||
try {
|
||||
session.close();
|
||||
System.out.println("Closed idle WebSocket connection for user: " + userId);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error closing idle connection for user " + userId + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 30000)
|
||||
public void sendHeartbeat() {
|
||||
sessions.forEach((userId, session) -> {
|
||||
try {
|
||||
if (session.isOpen()) {
|
||||
String heartbeatMessage = objectMapper.writeValueAsString(Map.of(
|
||||
"type", "heartbeat",
|
||||
"timestamp", System.currentTimeMillis()
|
||||
));
|
||||
session.send(Mono.just(session.textMessage(heartbeatMessage))).subscribe();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error sending heartbeat to user " + userId + ": " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String extractUserId(WebSocketSession session) {
|
||||
String query = session.getHandshakeInfo().getUri().getQuery();
|
||||
if (query != null && query.contains("userId=")) {
|
||||
return query.split("userId=")[1].split("&")[0];
|
||||
}
|
||||
return session.getId();
|
||||
}
|
||||
|
||||
private void handleIncomingMessage(WebSocketSession session, String userId, String payload) {
|
||||
try {
|
||||
Map<String, Object> message = objectMapper.readValue(payload, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
String type = (String) message.get("type");
|
||||
|
||||
switch (type) {
|
||||
case "ping":
|
||||
sendMessageToUser(userId, Map.of("type", "pong", "timestamp", System.currentTimeMillis()));
|
||||
break;
|
||||
case "pong":
|
||||
lastActivityTime.put(userId, LocalDateTime.now());
|
||||
break;
|
||||
case "subscribe":
|
||||
sessions.put(userId, session);
|
||||
lastActivityTime.put(userId, LocalDateTime.now());
|
||||
System.out.println("User " + userId + " subscribed to WebSocket");
|
||||
break;
|
||||
case "heartbeat":
|
||||
lastActivityTime.put(userId, LocalDateTime.now());
|
||||
break;
|
||||
default:
|
||||
System.out.println("Unknown message type: " + type);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error handling WebSocket message: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessageToUser(String userId, Object message) {
|
||||
WebSocketSession session = sessions.get(userId);
|
||||
if (session != null && session.isOpen()) {
|
||||
try {
|
||||
String json = objectMapper.writeValueAsString(message);
|
||||
session.send(Mono.just(session.textMessage(json))).subscribe();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error sending message to user " + userId + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastMessage(Object message) {
|
||||
String json;
|
||||
try {
|
||||
json = objectMapper.writeValueAsString(message);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error serializing broadcast message: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
sessions.forEach((userId, session) -> {
|
||||
try {
|
||||
if (session.isOpen()) {
|
||||
session.send(Mono.just(session.textMessage(json))).subscribe();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error broadcasting to user " + userId + ": " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
cn.novalon.manage.notify.config.WebSocketConfig
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
package cn.novalon.gym.manage.notify.handler;
|
||||
|
||||
import cn.novalon.gym.manage.notify.core.domain.SysNotice;
|
||||
import cn.novalon.gym.manage.notify.core.service.ISysNoticeService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SysNoticeHandlerTest {
|
||||
|
||||
@Mock
|
||||
private ISysNoticeService noticeService;
|
||||
|
||||
private SysNoticeHandler noticeHandler;
|
||||
private SysNotice testNotice;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
noticeHandler = new SysNoticeHandler(noticeService);
|
||||
|
||||
testNotice = new SysNotice();
|
||||
testNotice.setId(1L);
|
||||
testNotice.setNoticeTitle("系统维护通知");
|
||||
testNotice.setNoticeType("SYSTEM");
|
||||
testNotice.setNoticeContent("系统将于今晚进行维护");
|
||||
testNotice.setStatus("PUBLISHED");
|
||||
testNotice.setCreateBy("admin");
|
||||
testNotice.setCreatedAt(LocalDateTime.now());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAllNotices() {
|
||||
when(noticeService.getAllNotices()).thenReturn(Flux.just(testNotice));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder().build();
|
||||
Mono<ServerResponse> response = noticeHandler.getAllNotices(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getAllNotices();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNoticeById() {
|
||||
when(noticeService.getNoticeById(1L)).thenReturn(Mono.just(testNotice));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", "1")
|
||||
.build();
|
||||
Mono<ServerResponse> response = noticeHandler.getNoticeById(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getNoticeById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNoticeById_NotFound() {
|
||||
when(noticeService.getNoticeById(999L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", "999")
|
||||
.build();
|
||||
Mono<ServerResponse> response = noticeHandler.getNoticeById(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.NOT_FOUND)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getNoticeById(999L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNoticesByStatus() {
|
||||
when(noticeService.getNoticesByStatus("PUBLISHED")).thenReturn(Flux.just(testNotice));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("status", "PUBLISHED")
|
||||
.build();
|
||||
Mono<ServerResponse> response = noticeHandler.getNoticesByStatus(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getNoticesByStatus("PUBLISHED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetNoticesByStatus_Draft() {
|
||||
when(noticeService.getNoticesByStatus("DRAFT")).thenReturn(Flux.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("status", "DRAFT")
|
||||
.build();
|
||||
Mono<ServerResponse> response = noticeHandler.getNoticesByStatus(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getNoticesByStatus("DRAFT");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateNotice() {
|
||||
SysNotice newNotice = new SysNotice();
|
||||
newNotice.setNoticeTitle("新通知");
|
||||
newNotice.setNoticeType("1");
|
||||
newNotice.setNoticeContent("测试内容");
|
||||
newNotice.setStatus("0");
|
||||
|
||||
when(noticeService.createNotice(any(SysNotice.class))).thenReturn(Mono.just(testNotice));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(newNotice));
|
||||
Mono<ServerResponse> response = noticeHandler.createNotice(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.CREATED)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).createNotice(any(SysNotice.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateNotice_WithAllFields() {
|
||||
SysNotice newNotice = new SysNotice();
|
||||
newNotice.setNoticeTitle("完整通知");
|
||||
newNotice.setNoticeType("2");
|
||||
newNotice.setNoticeContent("完整内容");
|
||||
newNotice.setStatus("1");
|
||||
newNotice.setCreateBy("admin");
|
||||
|
||||
when(noticeService.createNotice(any(SysNotice.class))).thenReturn(Mono.just(testNotice));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(newNotice));
|
||||
Mono<ServerResponse> response = noticeHandler.createNotice(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.CREATED)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).createNotice(any(SysNotice.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateNotice() {
|
||||
SysNotice updateNotice = new SysNotice();
|
||||
updateNotice.setNoticeTitle("更新后的通知");
|
||||
updateNotice.setNoticeType("SYSTEM");
|
||||
updateNotice.setNoticeContent("更新后的内容");
|
||||
updateNotice.setStatus("PUBLISHED");
|
||||
|
||||
when(noticeService.updateNotice(anyLong(), any(SysNotice.class))).thenReturn(Mono.just(testNotice));
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", "1")
|
||||
.body(Mono.just(updateNotice));
|
||||
Mono<ServerResponse> response = noticeHandler.updateNotice(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.OK)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).updateNotice(1L, updateNotice);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdateNotice_NotFound() {
|
||||
SysNotice updateNotice = new SysNotice();
|
||||
updateNotice.setNoticeTitle("更新后的通知");
|
||||
|
||||
when(noticeService.updateNotice(anyLong(), any(SysNotice.class))).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", "999")
|
||||
.body(Mono.just(updateNotice));
|
||||
Mono<ServerResponse> response = noticeHandler.updateNotice(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.NOT_FOUND)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).updateNotice(999L, updateNotice);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteNotice() {
|
||||
when(noticeService.getNoticeById(1L)).thenReturn(Mono.just(testNotice));
|
||||
when(noticeService.deleteNotice(1L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", "1")
|
||||
.build();
|
||||
Mono<ServerResponse> response = noticeHandler.deleteNotice(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.NO_CONTENT)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getNoticeById(1L);
|
||||
verify(noticeService).deleteNotice(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeleteNotice_NotFound() {
|
||||
when(noticeService.getNoticeById(999L)).thenReturn(Mono.empty());
|
||||
|
||||
ServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("id", "999")
|
||||
.build();
|
||||
Mono<ServerResponse> response = noticeHandler.deleteNotice(request);
|
||||
|
||||
StepVerifier.create(response)
|
||||
.expectNextMatches(serverResponse ->
|
||||
serverResponse.statusCode() == HttpStatus.NOT_FOUND)
|
||||
.verifyComplete();
|
||||
|
||||
verify(noticeService).getNoticeById(999L);
|
||||
}
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
package cn.novalon.gym.manage.notify.websocket;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SysWebSocketHandlerTest {
|
||||
|
||||
@Mock
|
||||
private WebSocketSession session;
|
||||
|
||||
@Mock
|
||||
private WebSocketMessage message;
|
||||
|
||||
@Mock
|
||||
private HandshakeInfo handshakeInfo;
|
||||
|
||||
private SysWebSocketHandler webSocketHandler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
webSocketHandler = new SysWebSocketHandler();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_NewConnection() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(session.receive()).thenReturn(Flux.empty());
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_WithUserId() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=123"));
|
||||
when(session.receive()).thenReturn(Flux.empty());
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_WithoutUserId() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws"));
|
||||
when(session.getId()).thenReturn("test-session-id");
|
||||
when(session.receive()).thenReturn(Flux.empty());
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_PongMessage() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(message.getPayloadAsText()).thenReturn("{\"type\":\"pong\"}");
|
||||
when(session.receive()).thenReturn(Flux.just(message));
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
verify(message).getPayloadAsText();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_SubscribeMessage() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(message.getPayloadAsText()).thenReturn("{\"type\":\"subscribe\"}");
|
||||
when(session.receive()).thenReturn(Flux.just(message));
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
verify(message).getPayloadAsText();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_HeartbeatMessage() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(message.getPayloadAsText()).thenReturn("{\"type\":\"heartbeat\"}");
|
||||
when(session.receive()).thenReturn(Flux.just(message));
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
verify(message).getPayloadAsText();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_UnknownMessageType() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(message.getPayloadAsText()).thenReturn("{\"type\":\"unknown\"}");
|
||||
when(session.receive()).thenReturn(Flux.just(message));
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
verify(message).getPayloadAsText();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_InvalidJson() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(message.getPayloadAsText()).thenReturn("invalid json");
|
||||
when(session.receive()).thenReturn(Flux.just(message));
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
|
||||
verify(session).receive();
|
||||
verify(message).getPayloadAsText();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandle_SessionError() {
|
||||
when(session.getHandshakeInfo()).thenReturn(handshakeInfo);
|
||||
when(handshakeInfo.getUri()).thenReturn(URI.create("ws://localhost/ws?userId=testuser"));
|
||||
when(session.receive()).thenReturn(Flux.error(new RuntimeException("Connection error")));
|
||||
|
||||
Mono<Void> result = webSocketHandler.handle(session);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyError();
|
||||
|
||||
verify(session).receive();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendMessageToUser_SessionNotFound() {
|
||||
webSocketHandler.sendMessageToUser("nonexistent", java.util.Map.of("type", "notification", "message", "test"));
|
||||
|
||||
verify(session, never()).send(any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user