本文记录了如何实现用获取户信息,用户信息更新,用户头像上传三大基础功能
先上接口实现截图:
一、项目结构概览
先介绍一下
个人博客系统采用了标准的 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