From ceb6c8258c38e28f07e8b90408ffc47490cae056 Mon Sep 17 00:00:00 2001 From: gaoxq <376340421@qq.com> Date: Thu, 2 Apr 2026 23:35:19 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BA=91=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 2 + .idea/misc.xml | 2 +- .../config/GlobalExceptionHandler.java | 31 ++-- .../filesystem/controller/AuthController.java | 33 ++--- .../filesystem/controller/FileController.java | 136 +++++++++--------- .../controller/IndexController.java | 16 +-- .../controller/MessageController.java | 33 ++--- .../filesystem/controller/UserController.java | 46 +++--- .../java/com/filesystem/utils/ApiResult.java | 78 ++++++++++ .../com/filesystem/utils/PasswordUtil.java | 34 +++++ web-vue/src/api/request.js | 30 +++- 11 files changed, 294 insertions(+), 147 deletions(-) create mode 100644 src/main/java/com/filesystem/utils/ApiResult.java create mode 100644 src/main/java/com/filesystem/utils/PasswordUtil.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml index a891a69..e2a5e51 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -7,6 +7,7 @@ + @@ -14,6 +15,7 @@ \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 1f93b88..1e86ad3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/src/main/java/com/filesystem/config/GlobalExceptionHandler.java b/src/main/java/com/filesystem/config/GlobalExceptionHandler.java index a44b76b..2ee1e8a 100644 --- a/src/main/java/com/filesystem/config/GlobalExceptionHandler.java +++ b/src/main/java/com/filesystem/config/GlobalExceptionHandler.java @@ -1,13 +1,11 @@ package com.filesystem.config; +import com.filesystem.utils.ApiResult; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import java.io.IOException; -import java.util.Map; - @RestControllerAdvice public class GlobalExceptionHandler { @@ -15,29 +13,34 @@ public class GlobalExceptionHandler { * 处理所有运行时异常 */ @ExceptionHandler(RuntimeException.class) - public ResponseEntity handleRuntimeException(RuntimeException e) { + public ApiResult handleRuntimeException(RuntimeException e) { e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("message", e.getMessage() != null ? e.getMessage() : "服务器内部错误")); + return ApiResult.error(e.getMessage() != null ? e.getMessage() : "服务器内部错误"); } /** - * 处理 IO 异常(如 ZIP 压缩失败) + * 处理参数异常 */ - @ExceptionHandler(IOException.class) - public ResponseEntity handleIOException(IOException e) { + @ExceptionHandler(IllegalArgumentException.class) + public ApiResult handleIllegalArgumentException(IllegalArgumentException e) { + return ApiResult.error(e.getMessage() != null ? e.getMessage() : "请求参数错误"); + } + + /** + * 处理 IO 异常(如文件操作失败) + */ + @ExceptionHandler(java.io.IOException.class) + public ApiResult handleIOException(java.io.IOException e) { e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("message", "文件操作失败: " + (e.getMessage() != null ? e.getMessage() : "未知错误"))); + return ApiResult.error("文件操作失败: " + (e.getMessage() != null ? e.getMessage() : "未知错误")); } /** * 处理所有其他异常 */ @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception e) { + public ApiResult handleException(Exception e) { e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(Map.of("message", "服务器错误: " + (e.getMessage() != null ? e.getMessage() : "未知错误"))); + return ApiResult.serverError(e.getMessage() != null ? e.getMessage() : "未知错误"); } } diff --git a/src/main/java/com/filesystem/controller/AuthController.java b/src/main/java/com/filesystem/controller/AuthController.java index 7dffca2..a7f3eb2 100644 --- a/src/main/java/com/filesystem/controller/AuthController.java +++ b/src/main/java/com/filesystem/controller/AuthController.java @@ -3,8 +3,8 @@ package com.filesystem.controller; import com.filesystem.entity.User; import com.filesystem.security.UserPrincipal; import com.filesystem.service.UserService; +import com.filesystem.utils.ApiResult; import jakarta.annotation.Resource; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; @@ -19,7 +19,7 @@ public class AuthController { private UserService userService; @PostMapping("/login") - public ResponseEntity login(@RequestBody Map request) { + public ApiResult> login(@RequestBody Map request) { String username = request.get("username"); String password = request.get("password"); @@ -46,47 +46,43 @@ public class AuthController { result.put("token", token); result.put("user", userData); - Map body = new HashMap<>(); - body.put("data", result); - body.put("message", "登录成功"); - - return ResponseEntity.ok(body); + return ApiResult.success("登录成功", result); } catch (Exception e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @PostMapping("/register") - public ResponseEntity register(@RequestBody Map request) { + public ApiResult> register(@RequestBody Map request) { String username = request.get("username"); String password = request.get("password"); String nickname = request.get("nickname"); if (userService.findByUsername(username) != null) { - return ResponseEntity.badRequest().body(Map.of("message", "用户名已存在")); + return ApiResult.error("用户名已存在"); } User user = userService.createUser(username, password, nickname != null ? nickname : username); - return ResponseEntity.ok(Map.of("message", "注册成功", "data", Map.of("id", user.getId()))); + return ApiResult.success("注册成功", Map.of("id", user.getId())); } @PostMapping("/logout") - public ResponseEntity logout(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult logout(@AuthenticationPrincipal UserPrincipal principal) { if (principal != null) { userService.logout(principal.getUserId()); } - return ResponseEntity.ok(Map.of("message", "退出成功")); + return ApiResult.success("退出成功"); } @GetMapping("/info") - public ResponseEntity getUserInfo(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult> getUserInfo(@AuthenticationPrincipal UserPrincipal principal) { if (principal == null) { - return ResponseEntity.status(401).body(Map.of("message", "未登录")); + return ApiResult.unauthorized("未登录"); } User user = userService.findById(principal.getUserId()); if (user == null) { - return ResponseEntity.status(401).body(Map.of("message", "用户不存在")); + return ApiResult.unauthorized("用户不存在"); } // 精确重算存储空间 @@ -104,9 +100,6 @@ public class AuthController { userData.put("storageUsed", storageUsed); userData.put("storageLimit", storageLimit); - Map body = new HashMap<>(); - body.put("data", userData); - - return ResponseEntity.ok(body); + return ApiResult.success(userData); } } diff --git a/src/main/java/com/filesystem/controller/FileController.java b/src/main/java/com/filesystem/controller/FileController.java index 5f4fa5b..4fd85ba 100644 --- a/src/main/java/com/filesystem/controller/FileController.java +++ b/src/main/java/com/filesystem/controller/FileController.java @@ -4,6 +4,7 @@ import com.filesystem.entity.FileEntity; import com.filesystem.entity.FileShare; import com.filesystem.security.UserPrincipal; import com.filesystem.service.FileService; +import com.filesystem.utils.ApiResult; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; @@ -19,10 +20,8 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -36,121 +35,121 @@ public class FileController { @Value("${file.storage.path:./uploads}") private String storagePath; - public FileController() { - } - @GetMapping("/test") - public ResponseEntity test() { - return ResponseEntity.ok(Map.of("message", "Backend is running", "timestamp", System.currentTimeMillis())); + public ApiResult> test() { + return ApiResult.success(Map.of("message", "Backend is running", "timestamp", System.currentTimeMillis())); } @GetMapping - public ResponseEntity getFiles( + public ApiResult> getFiles( @AuthenticationPrincipal UserPrincipal principal, @RequestParam(required = false) Long folderId, @RequestParam(required = false) String keyword) { List files = fileService.getFiles(principal.getUserId(), folderId, keyword); - return ResponseEntity.ok(Map.of("data", files)); + return ApiResult.success(files); } @GetMapping("/trashFiles") - public ResponseEntity getTrashFiles( + public ApiResult> getTrashFiles( @AuthenticationPrincipal UserPrincipal principal, @RequestParam(required = false) Long folderId) { - return ResponseEntity.ok(Map.of("data", fileService.getTrashFiles(principal.getUserId(), folderId))); + return ApiResult.success(fileService.getTrashFiles(principal.getUserId(), folderId)); } @GetMapping("/sharedByMe") - public ResponseEntity getSharedByMe(@AuthenticationPrincipal UserPrincipal principal) { - return ResponseEntity.ok(Map.of("data", fileService.getSharedByMe(principal.getUserId()))); + public ApiResult> getSharedByMe(@AuthenticationPrincipal UserPrincipal principal) { + return ApiResult.success(fileService.getSharedByMe(principal.getUserId())); } @GetMapping("/sharedByMe/folder") - public ResponseEntity getSharedByMeFolderFiles( + public ApiResult> getSharedByMeFolderFiles( @AuthenticationPrincipal UserPrincipal principal, @RequestParam Long folderId) { List files = fileService.getSharedByMeFolderFiles(principal.getUserId(), folderId); - return ResponseEntity.ok(Map.of("data", files)); + return ApiResult.success(files); } @GetMapping("/sharedToMe") - public ResponseEntity getSharedToMe(@AuthenticationPrincipal UserPrincipal principal) { - return ResponseEntity.ok(Map.of("data", fileService.getSharedToMe(principal.getUserId()))); + public ApiResult> getSharedToMe(@AuthenticationPrincipal UserPrincipal principal) { + return ApiResult.success(fileService.getSharedToMe(principal.getUserId())); } @GetMapping("/sharedToMe/folder") - public ResponseEntity getSharedFolderFiles( + public ApiResult> getSharedFolderFiles( @AuthenticationPrincipal UserPrincipal principal, @RequestParam Long folderId) { List files = fileService.getSharedFolderFiles(principal.getUserId(), folderId); - return ResponseEntity.ok(Map.of("data", files)); + return ApiResult.success(files); } @PostMapping("/uploadBatch") - public ResponseEntity uploadFiles( + public ApiResult> uploadFiles( @AuthenticationPrincipal UserPrincipal principal, @RequestParam("files") List files, @RequestParam(required = false) Long folderId) throws IOException { List uploaded = fileService.uploadFiles(files, principal.getUserId(), folderId); - return ResponseEntity.ok(Map.of("data", uploaded, "message", "上传成功")); + return ApiResult.success("上传成功", uploaded); } @PostMapping("/createFolder") - public ResponseEntity createFolder( + public ApiResult createFolder( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request) { String name = (String) request.get("name"); Long parentId = request.get("parentId") != null ? Long.valueOf(request.get("parentId").toString()) : null; FileEntity folder = fileService.createFolder(name, principal.getUserId(), parentId); - return ResponseEntity.ok(Map.of("data", folder, "message", "创建成功")); + return ApiResult.success("创建成功", folder); } @DeleteMapping("/{id}") - public ResponseEntity deleteFile( + public ApiResult deleteFile( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id) { try { fileService.moveToTrash(id, principal.getUserId()); - return ResponseEntity.ok(Map.of("message", "已移至回收站")); + return ApiResult.success("已移至回收站"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @PostMapping("/{id}/restore") - public ResponseEntity restoreFile( + public ApiResult restoreFile( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id) { try { fileService.restoreFile(id, principal.getUserId()); - return ResponseEntity.ok(Map.of("message", "已还原")); + return ApiResult.success("已还原"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @DeleteMapping("/{id}/deletePermanent") - public ResponseEntity deletePermanently( + public ApiResult deletePermanently( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id) { try { fileService.deletePermanently(id, principal.getUserId()); - return ResponseEntity.ok(Map.of("message", "已彻底删除")); + return ApiResult.success("已彻底删除"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @DeleteMapping("/emptyTrash") - public ResponseEntity emptyTrash(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult emptyTrash(@AuthenticationPrincipal UserPrincipal principal) { try { fileService.emptyTrash(principal.getUserId()); - return ResponseEntity.ok(Map.of("message", "已清空回收站")); + return ApiResult.success("已清空回收站"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } + /** + * 下载文件(返回 ResponseEntity 用于二进制响应) + */ @GetMapping("/{id}/download") public ResponseEntity downloadFile( @AuthenticationPrincipal UserPrincipal principal, @@ -170,6 +169,9 @@ public class FileController { } } + /** + * 预览文件(返回 ResponseEntity 用于二进制响应) + */ @GetMapping("/{id}/preview") public ResponseEntity previewFile( @AuthenticationPrincipal UserPrincipal principal, @@ -190,7 +192,7 @@ public class FileController { } @PostMapping("/{id}/shareFile") - public ResponseEntity shareFile( + public ApiResult shareFile( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id, @RequestBody Map request) { @@ -198,26 +200,26 @@ public class FileController { Long shareToUserId = Long.valueOf(request.get("userId").toString()); String permission = (String) request.getOrDefault("permission", "view"); FileShare share = fileService.shareFile(id, principal.getUserId(), shareToUserId, permission); - return ResponseEntity.ok(Map.of("data", share, "message", "共享成功")); + return ApiResult.success("共享成功", share); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @DeleteMapping("/{id}/cancelShare") - public ResponseEntity cancelShare( + public ApiResult cancelShare( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id) { try { fileService.cancelShare(id, principal.getUserId()); - return ResponseEntity.ok(Map.of("message", "已取消共享")); + return ApiResult.success("已取消共享"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } /** - * 获取头像图片 + * 获取头像图片(返回 ResponseEntity 用于二进制响应) */ @GetMapping("/avatar/**") public ResponseEntity getAvatar(jakarta.servlet.http.HttpServletRequest request) throws IOException { @@ -257,24 +259,24 @@ public class FileController { } @PutMapping("/{id}/rename") - public ResponseEntity renameFile( + public ApiResult renameFile( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id, @RequestBody Map request) { String newName = request.get("name"); if (newName == null || newName.trim().isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "名称不能为空")); + return ApiResult.error("名称不能为空"); } try { fileService.renameFile(id, principal.getUserId(), newName.trim()); - return ResponseEntity.ok(Map.of("message", "重命名成功")); + return ApiResult.success("重命名成功"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @PutMapping("/{id}/move") - public ResponseEntity moveFile( + public ApiResult moveFile( @AuthenticationPrincipal UserPrincipal principal, @PathVariable Long id, @RequestBody Map request) { @@ -285,20 +287,25 @@ public class FileController { } try { fileService.moveFile(id, principal.getUserId(), folderId); - return ResponseEntity.ok(Map.of("message", "移动成功")); + return ApiResult.success("移动成功"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } + /** + * 批量下载(返回 ZIP 文件,使用 HttpServletResponse 直接写入) + */ @PostMapping("/batchDownload") - public ResponseEntity batchDownload( + public void batchDownload( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request, HttpServletResponse response) throws IOException { Object idsObj = request.get("ids"); if (idsObj == null) { - return ResponseEntity.badRequest().body(Map.of("message", "请选择要下载的文件")); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":400,\"message\":\"请选择要下载的文件\"}"); + return; } List ids = new ArrayList<>(); @@ -313,7 +320,9 @@ public class FileController { } if (ids.isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "请选择要下载的文件")); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":400,\"message\":\"请选择要下载的文件\"}"); + return; } try { @@ -324,29 +333,28 @@ public class FileController { response.setContentLength(zipBytes.length); response.getOutputStream().write(zipBytes); response.getOutputStream().flush(); - - return ResponseEntity.ok().build(); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"code\":400,\"message\":\"" + e.getMessage().replace("\"", "\\\"") + "\"}"); } } @GetMapping("/movableFolders") - public ResponseEntity getMovableFolders( + public ApiResult> getMovableFolders( @AuthenticationPrincipal UserPrincipal principal, @RequestParam(required = false) List excludeIds, @RequestParam(required = false) Long currentFolderId) { List folders = fileService.getMovableFolders(principal.getUserId(), excludeIds, currentFolderId); - return ResponseEntity.ok(Map.of("data", folders)); + return ApiResult.success(folders); } @PostMapping("/batchCancelShare") - public ResponseEntity batchCancelShare( + public ApiResult batchCancelShare( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request) { Object idsObj = request.get("ids"); if (idsObj == null) { - return ResponseEntity.badRequest().body(Map.of("message", "请选择要取消共享的文件")); + return ApiResult.error("请选择要取消共享的文件"); } List ids = new ArrayList<>(); if (idsObj instanceof List) { @@ -361,16 +369,16 @@ public class FileController { for (Long fileId : ids) { fileService.cancelShare(fileId, principal.getUserId()); } - return ResponseEntity.ok(Map.of("message", "批量取消共享成功")); + return ApiResult.success("批量取消共享成功"); } @PostMapping("/batchRestore") - public ResponseEntity batchRestore( + public ApiResult batchRestore( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request) { Object idsObj = request.get("ids"); if (idsObj == null) { - return ResponseEntity.badRequest().body(Map.of("message", "请选择要还原的文件")); + return ApiResult.error("请选择要还原的文件"); } List ids = new ArrayList<>(); if (idsObj instanceof List) { @@ -385,6 +393,6 @@ public class FileController { for (Long fileId : ids) { fileService.restoreFile(fileId, principal.getUserId()); } - return ResponseEntity.ok(Map.of("message", "批量还原成功")); + return ApiResult.success("批量还原成功"); } } diff --git a/src/main/java/com/filesystem/controller/IndexController.java b/src/main/java/com/filesystem/controller/IndexController.java index 7a84cdb..f32c09b 100644 --- a/src/main/java/com/filesystem/controller/IndexController.java +++ b/src/main/java/com/filesystem/controller/IndexController.java @@ -1,14 +1,14 @@ package com.filesystem.controller; -import org.springframework.stereotype.Controller; +import com.filesystem.utils.ApiResult; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; -@Controller +@RestController public class IndexController { - - // 所有非 /api/**、非 /ws/** 的前端路由,统一返回 index.html - @GetMapping({"/", "/login", "/register", "/desktop", "/desktop/**"}) - public String forward() { - return "forward:/webapp/index.html"; + + @GetMapping("/") + public ApiResult index() { + return ApiResult.success("File System API is running"); } -} \ No newline at end of file +} diff --git a/src/main/java/com/filesystem/controller/MessageController.java b/src/main/java/com/filesystem/controller/MessageController.java index aa1e1da..83dfc77 100644 --- a/src/main/java/com/filesystem/controller/MessageController.java +++ b/src/main/java/com/filesystem/controller/MessageController.java @@ -5,6 +5,7 @@ import com.filesystem.entity.User; import com.filesystem.security.UserPrincipal; import com.filesystem.service.MessageService; import com.filesystem.service.UserService; +import com.filesystem.utils.ApiResult; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; @@ -45,12 +46,12 @@ public class MessageController { // ==================== 聊天文件上传 ==================== @PostMapping("/upload") - public ResponseEntity uploadChatFile( + public ApiResult> uploadChatFile( @AuthenticationPrincipal UserPrincipal principal, @RequestParam("file") MultipartFile file) throws IOException { if (file.isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "请选择文件")); + return ApiResult.error("请选择文件"); } String originalName = file.getOriginalFilename(); @@ -68,10 +69,10 @@ public class MessageController { file.transferTo(filePath.toFile()); String fileUrl = "/api/messages/file/" + datePath + "/" + storedName; - return ResponseEntity.ok(Map.of("url", fileUrl, "message", "上传成功")); + return ApiResult.success("上传成功", Map.of("url", fileUrl)); } - // ==================== 聊天文件访问 ==================== + // ==================== 聊天文件访问(返回 ResponseEntity 用于二进制响应)==================== @GetMapping("/file/**") public ResponseEntity getChatFile(HttpServletRequest request) throws IOException { @@ -122,7 +123,7 @@ public class MessageController { // ==================== 消息收发 ==================== @GetMapping - public ResponseEntity getMessages( + public ApiResult>> getMessages( @AuthenticationPrincipal UserPrincipal principal, @RequestParam Long userId) { List messages = messageService.getMessages(principal.getUserId(), userId); @@ -169,11 +170,11 @@ public class MessageController { return m; }).collect(Collectors.toList()); - return ResponseEntity.ok(Map.of("data", result)); + return ApiResult.success(result); } @GetMapping("/users") - public ResponseEntity getUsers(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult>> getUsers(@AuthenticationPrincipal UserPrincipal principal) { List users = userService.getAllUsersExcept(principal.getUserId()); List> result = users.stream() .map(u -> { @@ -188,11 +189,11 @@ public class MessageController { return m; }) .collect(Collectors.toList()); - return ResponseEntity.ok(Map.of("data", result)); + return ApiResult.success(result); } @PostMapping - public ResponseEntity sendMessage( + public ApiResult> sendMessage( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request) { Long toUserId = Long.valueOf(request.get("toUserId").toString()); @@ -227,17 +228,17 @@ public class MessageController { result.put("fromSignature", fromUser.getSignature()); } - return ResponseEntity.ok(Map.of("data", result, "message", "发送成功")); + return ApiResult.success("发送成功", result); } @GetMapping("/unreadCount") - public ResponseEntity getUnreadCount(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult> getUnreadCount(@AuthenticationPrincipal UserPrincipal principal) { int count = messageService.getUnreadCount(principal.getUserId()); - return ResponseEntity.ok(Map.of("data", Map.of("count", count))); + return ApiResult.success(Map.of("count", count)); } @GetMapping("/unreadList") - public ResponseEntity getUnreadList(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult>> getUnreadList(@AuthenticationPrincipal UserPrincipal principal) { List unreadMessages = messageService.getUnreadMessages(principal.getUserId()); // 按发送人分组 @@ -268,12 +269,12 @@ public class MessageController { result.add(item); } - return ResponseEntity.ok(Map.of("data", result)); + return ApiResult.success(result); } @PostMapping("/{id}/read") - public ResponseEntity markAsRead(@PathVariable Long id) { + public ApiResult markAsRead(@PathVariable Long id) { messageService.markAsRead(id); - return ResponseEntity.ok(Map.of("message", "已标记已读")); + return ApiResult.success("已标记已读"); } } diff --git a/src/main/java/com/filesystem/controller/UserController.java b/src/main/java/com/filesystem/controller/UserController.java index 1cbafff..f585c3f 100644 --- a/src/main/java/com/filesystem/controller/UserController.java +++ b/src/main/java/com/filesystem/controller/UserController.java @@ -3,9 +3,9 @@ package com.filesystem.controller; import com.filesystem.entity.User; import com.filesystem.security.UserPrincipal; import com.filesystem.service.UserService; +import com.filesystem.utils.ApiResult; import jakarta.annotation.Resource; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -14,9 +14,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; import java.util.stream.Collectors; @RestController @@ -29,16 +29,18 @@ public class UserController { @Value("${file.storage.path:./uploads}") private String storagePath; + private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy/MM"); + /** * 获取所有可用用户(用于文件共享等场景) */ @GetMapping - public ResponseEntity getAllUsers(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult>> getAllUsers(@AuthenticationPrincipal UserPrincipal principal) { List users = userService.getAllUsersExcept(principal.getUserId()); List> result = users.stream() .filter(u -> u.getStatus() == 1) // 只返回启用状态的用户 .map(u -> { - Map m = new java.util.HashMap<>(); + Map m = new HashMap<>(); m.put("id", u.getId()); m.put("username", u.getUsername()); m.put("nickname", u.getNickname() != null ? u.getNickname() : u.getUsername()); @@ -47,24 +49,24 @@ public class UserController { return m; }) .collect(Collectors.toList()); - return ResponseEntity.ok(Map.of("data", result)); + return ApiResult.success(result); } /** * 上传头像 */ @PostMapping("/avatar") - public ResponseEntity uploadAvatar( + public ApiResult> uploadAvatar( @AuthenticationPrincipal UserPrincipal principal, @RequestParam("avatar") MultipartFile file) throws IOException { if (file.isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "请选择图片")); + return ApiResult.error("请选择图片"); } // 限制文件大小 2MB if (file.getSize() > 2 * 1024 * 1024) { - return ResponseEntity.badRequest().body(Map.of("message", "图片大小不能超过2MB")); + return ApiResult.error("图片大小不能超过2MB"); } // 保存文件 @@ -73,7 +75,7 @@ public class UserController { ? originalFilename.substring(originalFilename.lastIndexOf(".")) : ".jpg"; String fileName = UUID.randomUUID().toString() + ext; - String datePath = java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy/MM")); + String datePath = LocalDateTime.now().format(DATE_FMT); // 使用配置文件中的路径 + 日期目录 Path uploadPath = Paths.get(storagePath).toAbsolutePath().resolve("avatars").resolve(datePath); @@ -88,16 +90,16 @@ public class UserController { String avatarUrl = "/api/files/avatar/" + datePath + "/" + fileName; userService.updateAvatar(principal.getUserId(), avatarUrl); - return ResponseEntity.ok(Map.of("data", Map.of("url", avatarUrl), "message", "上传成功")); + return ApiResult.success("上传成功", Map.of("url", avatarUrl)); } /** * 获取当前用户信息 */ @GetMapping("/me") - public ResponseEntity getCurrentUser(@AuthenticationPrincipal UserPrincipal principal) { + public ApiResult> getCurrentUser(@AuthenticationPrincipal UserPrincipal principal) { User user = userService.findById(principal.getUserId()); - Map result = new java.util.HashMap<>(); + Map result = new HashMap<>(); result.put("id", user.getId()); result.put("username", user.getUsername()); result.put("nickname", user.getNickname()); @@ -105,14 +107,14 @@ public class UserController { result.put("signature", user.getSignature()); result.put("email", user.getEmail()); result.put("phone", user.getPhone()); - return ResponseEntity.ok(Map.of("data", result)); + return ApiResult.success(result); } /** * 更新个人信息 */ @PutMapping("/profile") - public ResponseEntity updateProfile( + public ApiResult updateProfile( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request) { try { @@ -121,9 +123,9 @@ public class UserController { String phone = request.get("phone"); String email = request.get("email"); userService.updateProfile(principal.getUserId(), nickname, signature, phone, email); - return ResponseEntity.ok(Map.of("message", "更新成功")); + return ApiResult.success("更新成功"); } catch (RuntimeException e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } @@ -131,21 +133,21 @@ public class UserController { * 修改密码 */ @PutMapping("/password") - public ResponseEntity changePassword( + public ApiResult changePassword( @AuthenticationPrincipal UserPrincipal principal, @RequestBody Map request) { String oldPassword = request.get("oldPassword"); String newPassword = request.get("newPassword"); if (oldPassword == null || oldPassword.isEmpty() || newPassword == null || newPassword.isEmpty()) { - return ResponseEntity.badRequest().body(Map.of("message", "请填写完整信息")); + return ApiResult.error("请填写完整信息"); } try { userService.changePassword(principal.getUserId(), oldPassword, newPassword); - return ResponseEntity.ok(Map.of("message", "密码修改成功")); + return ApiResult.success("密码修改成功"); } catch (Exception e) { - return ResponseEntity.badRequest().body(Map.of("message", e.getMessage())); + return ApiResult.error(e.getMessage()); } } } diff --git a/src/main/java/com/filesystem/utils/ApiResult.java b/src/main/java/com/filesystem/utils/ApiResult.java new file mode 100644 index 0000000..2972f7e --- /dev/null +++ b/src/main/java/com/filesystem/utils/ApiResult.java @@ -0,0 +1,78 @@ +package com.filesystem.utils; + +import lombok.Data; + +/** + * 统一API响应实体类 + */ +@Data +public class ApiResult { + + private int code; + private String message; + private T data; + + public ApiResult() {} + + public ApiResult(int code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + // ========== 成功 ========== + + public static ApiResult success() { + return new ApiResult<>(200, "success", null); + } + + public static ApiResult success(T data) { + return new ApiResult<>(200, "success", data); + } + + public static ApiResult success(String message, T data) { + return new ApiResult<>(200, message, data); + } + + public static ApiResult success(String message) { + return new ApiResult<>(200, message, null); + } + + // ========== 错误 ========== + + public static ApiResult error(String message) { + return new ApiResult<>(400, message, null); + } + + public static ApiResult error(int code, String message) { + return new ApiResult<>(code, message, null); + } + + public static ApiResult error(int code, String message, T data) { + return new ApiResult<>(code, message, data); + } + + // ========== 未授权 ========== + + public static ApiResult unauthorized(String message) { + return new ApiResult<>(401, message, null); + } + + // ========== 禁止 ========== + + public static ApiResult forbidden(String message) { + return new ApiResult<>(403, message, null); + } + + // ========== 未找到 ========== + + public static ApiResult notFound(String message) { + return new ApiResult<>(404, message, null); + } + + // ========== 服务器错误 ========== + + public static ApiResult serverError(String message) { + return new ApiResult<>(500, message, null); + } +} \ No newline at end of file diff --git a/src/main/java/com/filesystem/utils/PasswordUtil.java b/src/main/java/com/filesystem/utils/PasswordUtil.java new file mode 100644 index 0000000..925a0e6 --- /dev/null +++ b/src/main/java/com/filesystem/utils/PasswordUtil.java @@ -0,0 +1,34 @@ +package com.filesystem.utils; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +/** + * 密码工具类 + */ +public class PasswordUtil { + + private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + + /** + * 加密密码 + */ + public static String encode(String rawPassword) { + return encoder.encode(rawPassword); + } + + /** + * 验证密码 + */ + public static boolean matches(String rawPassword, String encodedPassword) { + return encoder.matches(rawPassword, encodedPassword); + } + + public static void main(String[] args) { + // 生成 admin123 的密码哈希 + String password = "system"; + String hash = encode(password); + System.out.println("Password: " + password); + System.out.println("BCrypt Hash: " + hash); + System.out.println("Matches: " + matches(password, hash)); + } +} diff --git a/web-vue/src/api/request.js b/web-vue/src/api/request.js index 5170953..10eb4f0 100644 --- a/web-vue/src/api/request.js +++ b/web-vue/src/api/request.js @@ -1,4 +1,4 @@ -import axios from 'axios' +import axios from 'axios' const request = axios.create({ baseURL: '/api', @@ -17,9 +17,35 @@ request.interceptors.request.use( ) request.interceptors.response.use( - response => response.data, + response => { + const res = response.data + + // 如果是二进制数据(blob),直接返回 + if (response.config.responseType === 'blob') { + return res + } + + // 统一响应格式: { code, message, data } + // code === 200 表示成功,返回 data + if (res.code === 200) { + return res.data + } + + // 业务错误,抛出异常 + const error = new Error(res.message || '请求失败') + error.code = res.code + error.response = response + return Promise.reject(error) + }, error => { const status = error.response?.status + const data = error.response?.data + + // 如果响应体中有错误信息,优先使用 + if (data && typeof data === 'object' && data.message) { + error.message = data.message + error.code = data.code + } // 只有 401/403 才清理 token 并跳转登录页 // 但在登录页时不跳转(避免死循环),登录接口的 401 也不跳转