重构云文件管理系统
This commit is contained in:
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -7,6 +7,7 @@
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="system-pro" />
|
||||
<module name="file-system-backend" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
@@ -14,6 +15,7 @@
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="file-system-backend" options="-parameters" />
|
||||
<module name="system-pro" options="-parameters" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -8,5 +8,5 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="java-17" project-jdk-type="JavaSDK" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="JDK17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
@@ -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() : "未知错误");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, String> request) {
|
||||
public ApiResult<Map<String, Object>> login(@RequestBody Map<String, String> 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<String, Object> 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<String, String> request) {
|
||||
public ApiResult<Map<String, Object>> register(@RequestBody Map<String, String> 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<Void> 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<Map<String, Object>> 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<String, Object> body = new HashMap<>();
|
||||
body.put("data", userData);
|
||||
|
||||
return ResponseEntity.ok(body);
|
||||
return ApiResult.success(userData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Map<String, Object>> test() {
|
||||
return ApiResult.success(Map.of("message", "Backend is running", "timestamp", System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getFiles(
|
||||
public ApiResult<List<FileEntity>> getFiles(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestParam(required = false) Long folderId,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
List<FileEntity> 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<List<FileEntity>> 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<List<FileEntity>> getSharedByMe(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
return ApiResult.success(fileService.getSharedByMe(principal.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/sharedByMe/folder")
|
||||
public ResponseEntity<?> getSharedByMeFolderFiles(
|
||||
public ApiResult<List<FileEntity>> getSharedByMeFolderFiles(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestParam Long folderId) {
|
||||
List<FileEntity> 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<List<FileEntity>> getSharedToMe(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
return ApiResult.success(fileService.getSharedToMe(principal.getUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/sharedToMe/folder")
|
||||
public ResponseEntity<?> getSharedFolderFiles(
|
||||
public ApiResult<List<FileEntity>> getSharedFolderFiles(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestParam Long folderId) {
|
||||
List<FileEntity> files = fileService.getSharedFolderFiles(principal.getUserId(), folderId);
|
||||
return ResponseEntity.ok(Map.of("data", files));
|
||||
return ApiResult.success(files);
|
||||
}
|
||||
|
||||
@PostMapping("/uploadBatch")
|
||||
public ResponseEntity<?> uploadFiles(
|
||||
public ApiResult<List<FileEntity>> uploadFiles(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestParam("files") List<MultipartFile> files,
|
||||
@RequestParam(required = false) Long folderId) throws IOException {
|
||||
List<FileEntity> 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<FileEntity> createFolder(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestBody Map<String, Object> 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<Void> 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<Void> 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<Void> 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<Void> 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<byte[]> 用于二进制响应)
|
||||
*/
|
||||
@GetMapping("/{id}/download")
|
||||
public ResponseEntity<byte[]> downloadFile(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@@ -170,6 +169,9 @@ public class FileController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览文件(返回 ResponseEntity<byte[]> 用于二进制响应)
|
||||
*/
|
||||
@GetMapping("/{id}/preview")
|
||||
public ResponseEntity<byte[]> previewFile(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@@ -190,7 +192,7 @@ public class FileController {
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/shareFile")
|
||||
public ResponseEntity<?> shareFile(
|
||||
public ApiResult<FileShare> shareFile(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@PathVariable Long id,
|
||||
@RequestBody Map<String, Object> 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<Void> 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<byte[]> 用于二进制响应)
|
||||
*/
|
||||
@GetMapping("/avatar/**")
|
||||
public ResponseEntity<byte[]> getAvatar(jakarta.servlet.http.HttpServletRequest request) throws IOException {
|
||||
@@ -257,24 +259,24 @@ public class FileController {
|
||||
}
|
||||
|
||||
@PutMapping("/{id}/rename")
|
||||
public ResponseEntity<?> renameFile(
|
||||
public ApiResult<Void> renameFile(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@PathVariable Long id,
|
||||
@RequestBody Map<String, String> 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<Void> moveFile(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@PathVariable Long id,
|
||||
@RequestBody Map<String, Object> 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<String, Object> 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<Long> 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<List<FileEntity>> getMovableFolders(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestParam(required = false) List<Long> excludeIds,
|
||||
@RequestParam(required = false) Long currentFolderId) {
|
||||
List<FileEntity> 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<Void> batchCancelShare(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
Object idsObj = request.get("ids");
|
||||
if (idsObj == null) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "请选择要取消共享的文件"));
|
||||
return ApiResult.error("请选择要取消共享的文件");
|
||||
}
|
||||
List<Long> 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<Void> batchRestore(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestBody Map<String, Object> request) {
|
||||
Object idsObj = request.get("ids");
|
||||
if (idsObj == null) {
|
||||
return ResponseEntity.badRequest().body(Map.of("message", "请选择要还原的文件"));
|
||||
return ApiResult.error("请选择要还原的文件");
|
||||
}
|
||||
List<Long> 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("批量还原成功");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> index() {
|
||||
return ApiResult.success("File System API is running");
|
||||
}
|
||||
}
|
||||
@@ -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<Map<String, String>> 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<byte[]> 用于二进制响应)====================
|
||||
|
||||
@GetMapping("/file/**")
|
||||
public ResponseEntity<byte[]> getChatFile(HttpServletRequest request) throws IOException {
|
||||
@@ -122,7 +123,7 @@ public class MessageController {
|
||||
// ==================== 消息收发 ====================
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getMessages(
|
||||
public ApiResult<List<Map<String, Object>>> getMessages(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestParam Long userId) {
|
||||
List<Message> 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<List<Map<String, Object>>> getUsers(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
List<User> users = userService.getAllUsersExcept(principal.getUserId());
|
||||
List<Map<String, Object>> 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<Map<String, Object>> sendMessage(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestBody Map<String, Object> 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<Map<String, Integer>> 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<List<Map<String, Object>>> getUnreadList(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
List<Message> 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<Void> markAsRead(@PathVariable Long id) {
|
||||
messageService.markAsRead(id);
|
||||
return ResponseEntity.ok(Map.of("message", "已标记已读"));
|
||||
return ApiResult.success("已标记已读");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<List<Map<String, Object>>> getAllUsers(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
List<User> users = userService.getAllUsersExcept(principal.getUserId());
|
||||
List<Map<String, Object>> result = users.stream()
|
||||
.filter(u -> u.getStatus() == 1) // 只返回启用状态的用户
|
||||
.map(u -> {
|
||||
Map<String, Object> m = new java.util.HashMap<>();
|
||||
Map<String, Object> 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<Map<String, String>> 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<Map<String, Object>> getCurrentUser(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
User user = userService.findById(principal.getUserId());
|
||||
Map<String, Object> result = new java.util.HashMap<>();
|
||||
Map<String, Object> 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<Void> updateProfile(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestBody Map<String, String> 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<Void> changePassword(
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
@RequestBody Map<String, String> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
78
src/main/java/com/filesystem/utils/ApiResult.java
Normal file
78
src/main/java/com/filesystem/utils/ApiResult.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package com.filesystem.utils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 统一API响应实体类
|
||||
*/
|
||||
@Data
|
||||
public class ApiResult<T> {
|
||||
|
||||
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 <T> ApiResult<T> success() {
|
||||
return new ApiResult<>(200, "success", null);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> success(T data) {
|
||||
return new ApiResult<>(200, "success", data);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> success(String message, T data) {
|
||||
return new ApiResult<>(200, message, data);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> success(String message) {
|
||||
return new ApiResult<>(200, message, null);
|
||||
}
|
||||
|
||||
// ========== 错误 ==========
|
||||
|
||||
public static <T> ApiResult<T> error(String message) {
|
||||
return new ApiResult<>(400, message, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> error(int code, String message) {
|
||||
return new ApiResult<>(code, message, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResult<T> error(int code, String message, T data) {
|
||||
return new ApiResult<>(code, message, data);
|
||||
}
|
||||
|
||||
// ========== 未授权 ==========
|
||||
|
||||
public static <T> ApiResult<T> unauthorized(String message) {
|
||||
return new ApiResult<>(401, message, null);
|
||||
}
|
||||
|
||||
// ========== 禁止 ==========
|
||||
|
||||
public static <T> ApiResult<T> forbidden(String message) {
|
||||
return new ApiResult<>(403, message, null);
|
||||
}
|
||||
|
||||
// ========== 未找到 ==========
|
||||
|
||||
public static <T> ApiResult<T> notFound(String message) {
|
||||
return new ApiResult<>(404, message, null);
|
||||
}
|
||||
|
||||
// ========== 服务器错误 ==========
|
||||
|
||||
public static <T> ApiResult<T> serverError(String message) {
|
||||
return new ApiResult<>(500, message, null);
|
||||
}
|
||||
}
|
||||
34
src/main/java/com/filesystem/utils/PasswordUtil.java
Normal file
34
src/main/java/com/filesystem/utils/PasswordUtil.java
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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 也不跳转
|
||||
|
||||
Reference in New Issue
Block a user