From 71909ce798da14956b43020a02b1055cb31d5fda Mon Sep 17 00:00:00 2001 From: czhqwer Date: Sun, 30 Mar 2025 02:26:21 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BD=BF=E7=94=A8SSE=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E8=BD=BB=E9=87=8F=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/czh/Main.java | 2 + .../cn/czh/controller/FileController.java | 8 +++ .../java/cn/czh/controller/SseController.java | 34 +++++++++ .../main/java/cn/czh/dto/NotifyMessage.java | 16 +++++ .../main/java/cn/czh/service/ISseService.java | 28 ++++++++ .../cn/czh/service/impl/AuthServiceImpl.java | 11 +++ .../cn/czh/service/impl/FileServiceImpl.java | 8 +++ .../cn/czh/service/impl/SseServiceImpl.java | 72 +++++++++++++++++++ .../src/components/ShareFile/ShareFile.vue | 19 ++++- upload-file-frontend/src/utils/api.js | 57 ++++++++++++++- upload-file-frontend/src/utils/http.js | 1 - 11 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 upload-file-backend/src/main/java/cn/czh/controller/SseController.java create mode 100644 upload-file-backend/src/main/java/cn/czh/dto/NotifyMessage.java create mode 100644 upload-file-backend/src/main/java/cn/czh/service/ISseService.java create mode 100644 upload-file-backend/src/main/java/cn/czh/service/impl/SseServiceImpl.java diff --git a/upload-file-backend/src/main/java/cn/czh/Main.java b/upload-file-backend/src/main/java/cn/czh/Main.java index 10fc13f..45e8975 100644 --- a/upload-file-backend/src/main/java/cn/czh/Main.java +++ b/upload-file-backend/src/main/java/cn/czh/Main.java @@ -3,10 +3,12 @@ package cn.czh; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; +@EnableAsync @SpringBootApplication public class Main { diff --git a/upload-file-backend/src/main/java/cn/czh/controller/FileController.java b/upload-file-backend/src/main/java/cn/czh/controller/FileController.java index 048c171..bbe5497 100644 --- a/upload-file-backend/src/main/java/cn/czh/controller/FileController.java +++ b/upload-file-backend/src/main/java/cn/czh/controller/FileController.java @@ -1,11 +1,14 @@ package cn.czh.controller; import cn.czh.base.Result; +import cn.czh.dto.NotifyMessage; import cn.czh.entity.StorageConfig; import cn.czh.service.IAuthService; import cn.czh.service.IFileService; +import cn.czh.service.ISseService; import cn.czh.service.IStorageConfigService; import cn.czh.utils.FileTypeUtil; +import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ByteArrayResource; @@ -24,6 +27,7 @@ import java.net.NetworkInterface; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Enumeration; +import java.util.Map; @Slf4j @RestController @@ -36,6 +40,8 @@ public class FileController { private IFileService fileService; @Resource private IAuthService authService; + @Resource + private ISseService sseService; @Value("${server.port}") private String serverPort; @@ -147,6 +153,8 @@ public class FileController { return Result.error("非主用户不能修改分享状态"); } enableShare = enable; + Map data = MapUtil.of("enable", enable); + sseService.notifySystemEvent(new NotifyMessage("enableShare", data)); return Result.success(); } diff --git a/upload-file-backend/src/main/java/cn/czh/controller/SseController.java b/upload-file-backend/src/main/java/cn/czh/controller/SseController.java new file mode 100644 index 0000000..02adc44 --- /dev/null +++ b/upload-file-backend/src/main/java/cn/czh/controller/SseController.java @@ -0,0 +1,34 @@ +package cn.czh.controller; + +import cn.czh.service.ISseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +@Slf4j +@RestController +@RequestMapping("/sse") +public class SseController { + + @Resource + private ISseService sseService; + + @GetMapping("/subscribeSharedFiles") + public SseEmitter subscribeSharedFiles(HttpServletRequest request) { + String remoteAddr = request.getRemoteAddr(); + return sseService.subscribeSharedFiles(remoteAddr); + } + + @GetMapping("/subscribeSystemEvent") + public SseEmitter subscribeSystemEvent(HttpServletRequest request) { + String remoteAddr = request.getRemoteAddr(); + return sseService.subscribeSystemEvent(remoteAddr); + } + + +} diff --git a/upload-file-backend/src/main/java/cn/czh/dto/NotifyMessage.java b/upload-file-backend/src/main/java/cn/czh/dto/NotifyMessage.java new file mode 100644 index 0000000..d0e9802 --- /dev/null +++ b/upload-file-backend/src/main/java/cn/czh/dto/NotifyMessage.java @@ -0,0 +1,16 @@ +package cn.czh.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.Map; + +@AllArgsConstructor +@Data +public class NotifyMessage { + + private String type; + + private Map data; + +} diff --git a/upload-file-backend/src/main/java/cn/czh/service/ISseService.java b/upload-file-backend/src/main/java/cn/czh/service/ISseService.java new file mode 100644 index 0000000..e61611c --- /dev/null +++ b/upload-file-backend/src/main/java/cn/czh/service/ISseService.java @@ -0,0 +1,28 @@ +package cn.czh.service; + +import cn.czh.dto.NotifyMessage; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface ISseService { + + /** + * 订阅共享文件更新 + */ + SseEmitter subscribeSharedFiles(String clientId); + + /** + * 订阅系统事件 + */ + SseEmitter subscribeSystemEvent(String clientId); + + /** + * 通知共享文件更新 + */ + void notifySharedFileUpdate(String message); + + /** + * 通知系统事件 + */ + void notifySystemEvent(NotifyMessage message); + +} diff --git a/upload-file-backend/src/main/java/cn/czh/service/impl/AuthServiceImpl.java b/upload-file-backend/src/main/java/cn/czh/service/impl/AuthServiceImpl.java index d0c5970..c09ffe4 100644 --- a/upload-file-backend/src/main/java/cn/czh/service/impl/AuthServiceImpl.java +++ b/upload-file-backend/src/main/java/cn/czh/service/impl/AuthServiceImpl.java @@ -1,20 +1,26 @@ package cn.czh.service.impl; +import cn.czh.dto.NotifyMessage; import cn.czh.entity.UserConfig; import cn.czh.mapper.UserConfigMapper; import cn.czh.service.IAuthService; +import cn.czh.service.ISseService; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; @Service public class AuthServiceImpl implements IAuthService { @Resource private UserConfigMapper userConfigMapper; + @Resource + private ISseService sseService; @Override public String getMainUserPassword() { @@ -48,6 +54,11 @@ public class AuthServiceImpl implements IAuthService { } else { userConfigMapper.updateById(user); } + Map data = new HashMap<>(); + data.put("password", password); + NotifyMessage message = new NotifyMessage("setPassword", data); + + sseService.notifySystemEvent(message); } @Override diff --git a/upload-file-backend/src/main/java/cn/czh/service/impl/FileServiceImpl.java b/upload-file-backend/src/main/java/cn/czh/service/impl/FileServiceImpl.java index 5163aa1..bcaa1e3 100644 --- a/upload-file-backend/src/main/java/cn/czh/service/impl/FileServiceImpl.java +++ b/upload-file-backend/src/main/java/cn/czh/service/impl/FileServiceImpl.java @@ -6,6 +6,10 @@ import cn.czh.entity.UploadFile; import cn.czh.mapper.ShareFileMapper; import cn.czh.mapper.UploadFileMapper; import cn.czh.service.IFileService; +import cn.czh.service.ISseService; +import cn.hutool.json.JSON; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -24,6 +28,8 @@ public class FileServiceImpl implements IFileService { private UploadFileMapper uploadFileMapper; @Resource private ShareFileMapper shareFileMapper; + @Resource + private ISseService sseService; @Override public IPage pageFiles(Integer page, Integer pageSize, String storageType, String fileName) { @@ -70,12 +76,14 @@ public class FileServiceImpl implements IFileService { sharedFile.setFileIdentifier(identifier); sharedFile.setCreateTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); shareFileMapper.insert(sharedFile); + sseService.notifySharedFileUpdate("add"); } } @Override public void removeSharedFile(String fileIdentifier) { shareFileMapper.delete(Wrappers.lambdaQuery(SharedFile.class).eq(SharedFile::getFileIdentifier, fileIdentifier)); + sseService.notifySharedFileUpdate("remove"); } @Override diff --git a/upload-file-backend/src/main/java/cn/czh/service/impl/SseServiceImpl.java b/upload-file-backend/src/main/java/cn/czh/service/impl/SseServiceImpl.java new file mode 100644 index 0000000..0fd3647 --- /dev/null +++ b/upload-file-backend/src/main/java/cn/czh/service/impl/SseServiceImpl.java @@ -0,0 +1,72 @@ +package cn.czh.service.impl; + +import cn.czh.dto.NotifyMessage; +import cn.czh.service.ISseService; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Service +public class SseServiceImpl implements ISseService { + + private final Map sharedFilesEmitters = new ConcurrentHashMap<>(); + private final Map systemEventEmitters = new ConcurrentHashMap<>(); + + @Override + public SseEmitter subscribeSharedFiles(String clientId) { + SseEmitter emitter = new SseEmitter(0L); // 设置超时时间 + sharedFilesEmitters.put(clientId, emitter); + + emitter.onCompletion(() -> sharedFilesEmitters.remove(clientId)); + emitter.onTimeout(() -> sharedFilesEmitters.remove(clientId)); + return emitter; + } + + @Override + public SseEmitter subscribeSystemEvent(String clientId) { + SseEmitter emitter = new SseEmitter(0L); // 设置超时时间 + systemEventEmitters.put(clientId, emitter); + + emitter.onCompletion(() -> systemEventEmitters.remove(clientId)); + emitter.onTimeout(() -> systemEventEmitters.remove(clientId)); + return emitter; + } + + @Async + @Override + public void notifySharedFileUpdate(String message) { + sharedFilesEmitters.forEach((clientId, emitter) -> + sendNotification(clientId, emitter, "sharedFileUpdate", message, sharedFilesEmitters)); + } + + @Async + @Override + public void notifySystemEvent(NotifyMessage message) { + systemEventEmitters.forEach((clientId, emitter) -> + sendNotification(clientId, emitter, "systemEvent", JSONUtil.toJsonStr(message), systemEventEmitters)); + } + + private void sendNotification(String clientId, SseEmitter emitter, String event, String message, Map emittersMap) { + try { + if (emitter != null) { + emitter.send(SseEmitter.event().name(event).data(message)); + } else { + log.warn("Emitter for client {} is disposed or null, skipping notification", clientId); + } + } catch (IOException e) { + log.error("Error sending notification to client: {}", clientId, e); + emittersMap.remove(clientId); // 从对应的Map中移除 + } catch (Exception e) { + log.error("Unexpected error sending notification to client: {}", clientId, e); + emittersMap.remove(clientId); + } + } + +} \ No newline at end of file diff --git a/upload-file-frontend/src/components/ShareFile/ShareFile.vue b/upload-file-frontend/src/components/ShareFile/ShareFile.vue index 8879a7f..df34652 100644 --- a/upload-file-frontend/src/components/ShareFile/ShareFile.vue +++ b/upload-file-frontend/src/components/ShareFile/ShareFile.vue @@ -68,7 +68,7 @@