本文記錄了如何實現用獲取戶信息,用戶信息更新,用戶頭像上傳三大基礎功能
先上接口實現截圖:
一、項目結構概覽
先介紹一下
個人博客系統采用了標準的 Spring Boot 項目結構,用戶功能相關的文件主要分布在以下幾個目錄:
WeblogSystem/
├── src/main/java/com/zxy/weblogsystem/
│ ├── controller/ # 控制器層,處理HTTP請求
│ ├── service/ # 服務層,實現業務邏輯
│ │ └── impl/ # 服務實現類
│ ├── repository/ # 數據訪問層,與數據庫交互
│ ├── entity/ # 實體類,映射數據庫表
│ ├── dto/ # 數據傳輸對象,用于API交互
│ ├── exception/ # 自定義異常類
│ └── config/ # 配置類
├── src/main/resources/
│ ├── static/ # 靜態資源
│ ├── templates/ # 模板文件
│ ├── application.properties # 應用配置
│ ├── schema.sql # 數據庫表結構
│ └── data.sql # 初始數據
└── docs/ # 項目文檔
二、用戶信息功能實現
- 實體類定義
文件位置:src/main/java/com/zxy/weblogsystem/entity/User.java
功能說明:定義用戶實體類,映射數據庫中的 users 表。
主要內容:
@Data // Lombok注解,自動生成getter/setter等方法
@NoArgsConstructor // 無參構造函數
@AllArgsConstructor // 全參構造函數
@Entity // JPA實體類注解
@Table(name = "users") // 指定表名
public class User {@Id // 主鍵@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增策略private Long id;@Column(nullable = false, unique = true, length = 50)private String username;@Column(nullable = false, length = 100)private String password;@Column(nullable = false, unique = true, length = 100)private String email;@Column(length = 50)private String nickname;@Column(length = 255)private String avatarUrl; // 頭像URL@Column(nullable = false, length = 20)private String role = "USER";@Column(nullable = false)private Integer status = 1;@Column(name = "created_at", nullable = false, updatable = false)private LocalDateTime createdAt;@Column(name = "updated_at", nullable = false)private LocalDateTime updatedAt;@PrePersistprotected void onCreate() {createdAt = LocalDateTime.now();updatedAt = LocalDateTime.now();}@PreUpdateprotected void onUpdate() {updatedAt = LocalDateTime.now();}
}
- 數據傳輸對象(DTO)
2.1 用戶信息DTO
文件位置:src/main/java/com/zxy/weblogsystem/dto/UserInfoDto.java
功能說明:用于返回用戶信息的數據傳輸對象。
主要內容:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfoDto {private Long id;private String username;private String nickname;private String email;private String avatarUrl;private String role;private Integer status;private Integer followersCount; // 粉絲數private Integer followingCount; // 關注數private Integer articleCount; // 文章數private LocalDateTime createdAt;
}
2.2 用戶更新DTO
文件位置:src/main/java/com/zxy/weblogsystem/dto/UserUpdateDto.java
功能說明:用于接收用戶信息更新請求的數據傳輸對象。
主要內容:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserUpdateDto {@Size(max = 50, message = "昵稱長度不能超過50個字符")private String nickname;@URL(message = "頭像URL格式不正確")private String avatarUrl;
}
2.3 API響應DTO
文件位置:src/main/java/com/zxy/weblogsystem/dto/ApiResponse.java
功能說明:統一的API響應格式。
主要內容:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {private Integer code;private String message;private T data;public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(200, "OK", data);}public static <T> ApiResponse<T> success(String message, T data) {return new ApiResponse<>(200, message, data);}public static <T> ApiResponse<T> error(Integer code, String message) {return new ApiResponse<>(code, message, null);}
}
- 數據訪問層
文件位置:src/main/java/com/zxy/weblogsystem/repository/UserRepository.java
功能說明:用戶數據訪問接口,提供數據庫操作方法。
主要內容:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {Optional<User> findByUsername(String username);Optional<User> findByEmail(String email);boolean existsByUsername(String username);boolean existsByEmail(String email);
}
- 服務層
4.1 用戶服務接口
文件位置:src/main/java/com/zxy/weblogsystem/service/UserService.java
功能說明:定義用戶相關的業務邏輯接口。
主要內容:
public interface UserService {/*** 根據用戶ID獲取用戶信息* @param userId 用戶ID* @return 用戶詳細信息DTO*/UserInfoDto getUserInfo(Long userId);/*** 根據用戶ID更新用戶信息* @param userId 用戶ID* @param userUpdateDto 用戶更新信息DTO* @return 更新后的用戶信息DTO*/UserInfoDto updateUserInfo(Long userId, UserUpdateDto userUpdateDto);/*** 更新用戶頭像* @param userId 用戶ID* @param avatarUrl 頭像URL* @return 更新后的用戶信息DTO*/UserInfoDto updateUserAvatar(Long userId, String avatarUrl);
}
4.2 用戶服務實現類
文件位置:src/main/java/com/zxy/weblogsystem/service/impl/UserServiceImpl.java
功能說明:實現用戶服務接口中定義的業務邏輯。
主要內容:
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {private final UserRepository userRepository;private final UserFollowRepository userFollowRepository;private final ArticleRepository articleRepository;@Override@Transactional(readOnly = true)public UserInfoDto getUserInfo(Long userId) {// 1. 查詢用戶基本信息User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("用戶不存在,ID: " + userId));// 2. 查詢統計數據Integer followersCount = userFollowRepository.countByFollowedId(userId);Integer followingCount = userFollowRepository.countByFollowerId(userId);Integer articleCount = articleRepository.countByAuthorId(userId);// 3. 構建并返回DTOreturn UserInfoDto.builder().id(user.getId()).username(user.getUsername()).nickname(user.getNickname()).email(user.getEmail()).avatarUrl(user.getAvatarUrl()).role(user.getRole()).status(user.getStatus()).followersCount(followersCount).followingCount(followingCount).articleCount(articleCount).createdAt(user.getCreatedAt()).build();}@Override@Transactionalpublic UserInfoDto updateUserInfo(Long userId, UserUpdateDto userUpdateDto) {// 1. 查詢用戶是否存在User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("用戶不存在,ID: " + userId));// 2. 更新用戶信息boolean isUpdated = false;// 更新昵稱if (userUpdateDto.getNickname() != null && !userUpdateDto.getNickname().isEmpty()) {user.setNickname(userUpdateDto.getNickname());isUpdated = true;}// 3. 保存更新后的用戶信息if (isUpdated) {user = userRepository.save(user);}// 4. 查詢統計數據并構建返回DTOreturn getUserInfo(userId);}@Override@Transactionalpublic UserInfoDto updateUserAvatar(Long userId, String avatarUrl) {// 1. 查詢用戶是否存在User user = userRepository.findById(userId).orElseThrow(() -> new ResourceNotFoundException("用戶不存在,ID: " + userId));// 2. 更新用戶頭像URLuser.setAvatarUrl(avatarUrl);// 3. 保存更新后的用戶信息userRepository.save(user);// 4. 查詢統計數據并構建返回DTOreturn getUserInfo(userId);}
}
- 控制器層
文件位置:src/main/java/com/zxy/weblogsystem/controller/UserController.java
功能說明:處理用戶相關的HTTP請求。
主要內容:
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {private final UserService userService;private final FileService fileService;/*** 獲取用戶信息*/@GetMapping("/{id}")public ApiResponse<UserInfoDto> getUserInfo(@PathVariable Long id) {UserInfoDto userInfo = userService.getUserInfo(id);return ApiResponse.success(userInfo);}/*** 更新用戶信息*/@PutMapping("/{id}")public ApiResponse<UserInfoDto> updateUserInfo(@PathVariable Long id, @Valid @RequestBody UserUpdateDto userUpdateDto) {UserInfoDto updatedUserInfo = userService.updateUserInfo(id, userUpdateDto);return ApiResponse.success("更新成功", updatedUserInfo);}/*** 上傳用戶頭像*/@PostMapping("/{id}/avatar")public ApiResponse<Map<String, String>> uploadAvatar(@PathVariable Long id, @RequestParam("file") MultipartFile file) {// 1. 調用文件服務上傳頭像String avatarUrl = fileService.uploadAvatar(id, file);// 2. 更新用戶頭像信息userService.updateUserAvatar(id, avatarUrl);// 3. 返回頭像URLMap<String, String> result = new HashMap<>();result.put("avatarUrl", avatarUrl);return ApiResponse.success("上傳成功", result);}
}
三、文件上傳功能實現
- 文件上傳配置
文件位置:src/main/java/com/zxy/weblogsystem/config/FileUploadConfig.java
功能說明:配置文件上傳相關參數和靜態資源訪問。
主要內容:
@Configuration
public class FileUploadConfig implements WebMvcConfigurer {@Value("${file.upload.path:uploads}")private String uploadPath;@Value("${file.access.path:/uploads/}")private String accessPath;@Beanpublic MultipartResolver multipartResolver() {return new StandardServletMultipartResolver();}@Overridepublic void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {// 確保上傳目錄存在File uploadDir = new File(uploadPath);if (!uploadDir.exists()) {uploadDir.mkdirs();}// 獲取上傳目錄的絕對路徑String absolutePath = uploadDir.getAbsolutePath();// 添加資源處理器,將上傳路徑映射到訪問路徑registry.addResourceHandler(accessPath + "**").addResourceLocations("file:" + absolutePath + "/");}
}
- 文件上傳異常
文件位置:src/main/java/com/zxy/weblogsystem/exception/FileUploadException.java
功能說明:自定義文件上傳異常類。
主要內容:
public class FileUploadException extends RuntimeException {public FileUploadException(String message) {super(message);}public FileUploadException(String message, Throwable cause) {super(message, cause);}
}
- 文件服務接口
文件位置:src/main/java/com/zxy/weblogsystem/service/FileService.java
功能說明:定義文件上傳相關的業務邏輯接口。
主要內容:
public interface FileService {/*** 上傳頭像文件* * @param userId 用戶ID* @param file 頭像文件* @return 頭像訪問URL*/String uploadAvatar(Long userId, MultipartFile file);
}
- 文件服務實現類
文件位置:src/main/java/com/zxy/weblogsystem/service/impl/FileServiceImpl.java
功能說明:實現文件上傳相關的業務邏輯。
主要內容:
@Service
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {@Value("${file.upload.path:uploads}")private String uploadPath;@Value("${file.access.path:/uploads/}")private String accessPath;private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList("image/jpeg", "image/png");@Overridepublic String uploadAvatar(Long userId, MultipartFile file) {// 1. 校驗文件是否為空if (file == null || file.isEmpty()) {throw new FileUploadException("上傳文件不能為空");}// 2. 校驗文件類型String contentType = file.getContentType();if (contentType == null || !ALLOWED_IMAGE_TYPES.contains(contentType)) {throw new FileUploadException("只支持JPG和PNG格式的圖片");}// 3. 校驗文件大小if (file.getSize() > 2 * 1024 * 1024) { // 2MBthrow new FileUploadException("文件大小不能超過2MB");}try {// 4. 確保上傳目錄存在File uploadDir = new File(uploadPath);if (!uploadDir.exists()) {uploadDir.mkdirs();}// 5. 創建用戶頭像目錄String userAvatarDir = uploadPath + "/avatars/" + userId;File userDir = new File(userAvatarDir);if (!userDir.exists()) {userDir.mkdirs();}// 6. 生成唯一文件名String originalFilename = file.getOriginalFilename();String fileExtension = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf(".")) : ".jpg";String newFilename = UUID.randomUUID().toString() + fileExtension;// 7. 保存文件Path targetPath = Paths.get(userAvatarDir, newFilename);Files.copy(file.getInputStream(), targetPath);// 8. 返回文件訪問URLreturn accessPath + "avatars/" + userId + "/" + newFilename;} catch (IOException e) {throw new FileUploadException("文件上傳失敗: " + e.getMessage());}}
}
四、配置文件
- 應用配置
文件位置:src/main/resources/application.properties
功能說明:配置應用參數,包括數據庫連接、文件上傳等。
主要內容:
# 數據庫配置
spring.datasource.url=jdbc:mysql://localhost:3306/weblog?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# JPA配置
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect# 文件上傳配置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.file-size-threshold=0# 文件存儲路徑配置
file.upload.path=e:/個人博客系統/WeblogSystem/uploads
file.access.path=/uploads/
- 數據庫表結構
文件位置:src/main/resources/schema.sql
功能說明:定義數據庫表結構。
主要內容:
-- 用戶表
CREATE TABLE IF NOT EXISTS users (id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用戶ID',username VARCHAR(50) NOT NULL UNIQUE COMMENT '用戶名',password VARCHAR(100) NOT NULL COMMENT '密碼(加密)',email VARCHAR(100) NOT NULL UNIQUE COMMENT '郵箱',nickname VARCHAR(50) COMMENT '昵稱',avatar_url VARCHAR(255) COMMENT '頭像URL',role VARCHAR(20) NOT NULL DEFAULT 'USER' COMMENT '角色',status INT NOT NULL DEFAULT 1 COMMENT '狀態(0=禁用,1=啟用)',created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',INDEX idx_email(email),INDEX idx_username(username)
) COMMENT '用戶表';
五、功能測試
- 獲取用戶信息
請求方法:GET
URL:/users/{id}
示例:GET http://localhost:8080/users/1
響應示例:
{"code": 200,"message": "OK","data": {"id": 1,"username": "admin","nickname": "管理員","email": "admin@example.com","avatarUrl": "/uploads/avatars/1/63675700-ad83-4e05-a64f-2e59f8a16eeb.jpg","role": "ADMIN","status": 1,"followersCount": 0,"followingCount": 0,"articleCount": 0,"createdAt": "2025-04-13T12:00:00"}
}
- 更新用戶信息
請求方法:PUT
URL:/users/{id}
請求體:
{"nickname": "新昵稱"
}
示例:PUT http://localhost:8080/users/1
響應示例:
{"code": 200,"message": "更新成功","data": {"id": 1,"username": "admin","nickname": "新昵稱","email": "admin@example.com","avatarUrl": "/uploads/avatars/1/63675700-ad83-4e05-a64f-2e59f8a16eeb.jpg","role": "ADMIN","status": 1,"followersCount": 0,"followingCount": 0,"articleCount": 0,"createdAt": "2025-04-13T12:00:00"}
}
- 上傳用戶頭像
請求方法:POST
URL:/users/{id}/avatar
Content-Type:multipart/form-data
請求參數:
file: 圖片文件(支持jpg、png,最大2MB)
示例:POST http://localhost:8080/users/1/avatar
響應示例:
{"code": 200,"message": "上傳成功","data": {"avatarUrl": "/uploads/avatars/1/63675700-ad83-4e05-a64f-2e59f8a16eeb.jpg"}
}
六、總結
通過以上實現,完成了用戶信息管理的三個主要功能:
獲取用戶信息:通過用戶ID獲取用戶詳細信息,包括基本資料和統計數據
更新用戶信息:支持更新用戶昵稱等基本信息
上傳用戶頭像:支持上傳JPG、PNG格式的頭像圖片,并自動更新用戶頭像URL