diff --git a/chaos_api_common/pom.xml b/chaos_api_common/pom.xml index 7f56791..155cd5d 100644 --- a/chaos_api_common/pom.xml +++ b/chaos_api_common/pom.xml @@ -29,5 +29,21 @@ com.alibaba.fastjson2 fastjson2 + + org.springframework + spring-context + compile + + + cn.nopj + chaos_api_domain + 1.0-SNAPSHOT + compile + + + + org.springframework.boot + spring-boot-starter-validation + \ No newline at end of file diff --git a/chaos_api_common/src/main/java/cn/nopj/chaos_api/common/constants/ErrorCode.java b/chaos_api_common/src/main/java/cn/nopj/chaos_api/common/constants/ErrorCode.java new file mode 100644 index 0000000..1c2b0fd --- /dev/null +++ b/chaos_api_common/src/main/java/cn/nopj/chaos_api/common/constants/ErrorCode.java @@ -0,0 +1,54 @@ +package cn.nopj.chaos_api.common.constants; + +import lombok.Getter; + +/** + * 全局错误码枚举(HTTP状态码 + 业务错误码) + * 规范: + * - 4xx: 客户端错误 + * - 5xx: 服务端错误 + * - 业务码分段: + * 100-199: 用户相关 + * 200-299: 帖子/评论相关 + * 300-399: 系统/第三方服务 + */ +@Getter +public enum ErrorCode { + // ================== 用户相关 (100-199) ================== + USERNAME_EXISTS(409, "USER-101", "用户名已存在"), + USERNAME_FORMAT_ERROR(400, "USER-102", "用户名需5-20位字母数字组合"), + USERNAME_PATTERN_INVALID( 400, "USER-103" , "用户名格式无效,必须由字母、数字、下划线" ), + PASSWORD_FORMAT_ERROR(400, "USER-104", "密码需6-20位字符组合"), + USER_NOT_EXISTS(404, "USER-104", "用户不存在"), + USER_ID_INVALID(400, "USER-105", "用户ID无效"), + USER_NOT_LOGIN(401, "USER-105", "请先登录"), + USER_BANNED(403, "USER-106", "账号已被封禁"), + EMAIL_EXISTS(409, "USER-107", "邮箱已注册"), + EMAIL_FORMAT_ERROR(400, "USER-108", "邮箱格式无效"), + PHONE_EXISTS(409, "USER-109", "手机号已注册"), + PHONE_FORMAT_ERROR(400, "USER-110", "手机号格式无效"), + + // ================== 论坛内容相关 (200-299) ================== + POST_NOT_FOUND(404, "POST-201", "帖子不存在"), + POST_DELETED(410, "POST-202", "帖子已被删除"), + POST_TITLE_EMPTY(400, "POST-203", "标题不能为空"), + POST_CONTENT_EMPTY(400, "POST-204", "内容不能为空"), + COMMENT_NOT_FOUND(404, "POST-205", "评论不存在"), + COMMENT_TOO_LONG(400, "POST-206", "评论超过500字限制"), + + // ================== 系统/第三方 (300-399) ================== + CAPTCHA_ERROR(400, "SYS-301", "验证码错误"), + SMS_SEND_FAILED(500, "SYS-302", "短信发送失败"), + FILE_UPLOAD_FAILED(500, "SYS-303", "文件上传失败"), + RATE_LIMIT_EXCEEDED(429, "SYS-304", "操作过于频繁"), ; + + private final int httpStatus; + private final String code; // 业务错误码(领域-编号) + private final String message; + + ErrorCode(int httpStatus, String code, String message) { + this.httpStatus = httpStatus; + this.code = code; + this.message = message; + } +} \ No newline at end of file diff --git a/chaos_api_common/src/main/java/cn/nopj/chaos_api/common/exceotion/BizException.java b/chaos_api_common/src/main/java/cn/nopj/chaos_api/common/exceotion/BizException.java new file mode 100644 index 0000000..3528d24 --- /dev/null +++ b/chaos_api_common/src/main/java/cn/nopj/chaos_api/common/exceotion/BizException.java @@ -0,0 +1,14 @@ +package cn.nopj.chaos_api.common.exceotion; + +import cn.nopj.chaos_api.common.constants.ErrorCode; +import lombok.Getter; + +@Getter +public class BizException extends RuntimeException{ + public final ErrorCode errorCode; + + public BizException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } +} diff --git a/chaos_api_common/src/main/java/cn/nopj/chaos_api/converter/UserConverter.java b/chaos_api_common/src/main/java/cn/nopj/chaos_api/converter/UserConverter.java new file mode 100644 index 0000000..99deed4 --- /dev/null +++ b/chaos_api_common/src/main/java/cn/nopj/chaos_api/converter/UserConverter.java @@ -0,0 +1,22 @@ +package cn.nopj.chaos_api.converter; + + +import cn.nopj.chaos_api.domain.entity.User; +import cn.nopj.chaos_api.dto.request.AuthRegisterRequest; +import org.springframework.stereotype.Component; + +@Component +public class UserConverter { + public User convert(AuthRegisterRequest request){ + User user = new User(); + user.setUsername(request.getUsername()); + user.setPassword(request.getPassword()); + user.setEnabled(true); + user.setAccountNonExpired(true); + user.setCredentialsNonExpired(true); + user.setAccountNonLocked(true); + user.setCreateTime(java.time.LocalDateTime.now()); + user.setUpdateTime(java.time.LocalDateTime.now()); + return user; + } +} diff --git a/chaos_api_data/src/main/java/cn/nopj/chaos_api/mapper/UserMapper.java b/chaos_api_data/src/main/java/cn/nopj/chaos_api/mapper/UserMapper.java index 3322d0e..da780ae 100644 --- a/chaos_api_data/src/main/java/cn/nopj/chaos_api/mapper/UserMapper.java +++ b/chaos_api_data/src/main/java/cn/nopj/chaos_api/mapper/UserMapper.java @@ -1,10 +1,9 @@ package cn.nopj.chaos_api.mapper; +import cn.nopj.chaos_api.domain.entity.Role; import cn.nopj.chaos_api.domain.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.*; import java.util.List; @@ -37,4 +36,51 @@ public interface UserMapper extends BaseMapper { */ @Select("INSERT INTO t_user_role (user_id, role_id) VALUES (#{userId}, #{roleId})") void insertUserRole(@Param("userId") Long id, @Param("roleId") Long i); + + /** + * 根据用户名查询用户信息 + * @param username 用户名 + */ + @Select("SELECT * FROM t_user WHERE username = #{username}") + User selectByUsername(String username); + + + + /** + * 根据用户ID查询用户信息和角色信息 + */ + @Select("SELECT * FROM t_user WHERE id = #{id}") + @Results({ + @Result(property = "roles", column = "id", + many = @Many(select = "findRolesByUserId")) + }) + User findUserWithRoles(Long id); + + + /** + * 查询所有用户信息 + * @return 所有用户信息 + **/ + @Select(""" + SELECT * FROM t_user + """) + @Results({ + @Result(id = true, property = "id", column = "id"), + @Result(property = "username", column = "username"), + @Result(property = "roles", column = "id", + many = @Many(select = "findRolesByUserId")) + }) + List findAllUserWithRoles(); + + + /** + * 根据用户ID查询角色信息 + */ + @Select(""" + SELECT r.id, r.name ,r.code + FROM t_role r + JOIN t_user_role ur ON r.id = ur.role_id + WHERE ur.user_id = #{userId} + """) + List findRolesByUserId(@Param("userId") Long userId); } diff --git a/chaos_api_domain/pom.xml b/chaos_api_domain/pom.xml index b503960..9b837d9 100644 --- a/chaos_api_domain/pom.xml +++ b/chaos_api_domain/pom.xml @@ -20,6 +20,10 @@ UTF-8 + + org.springframework.boot + spring-boot-starter-validation + com.baomidou mybatis-plus @@ -29,5 +33,11 @@ lombok provided + + jakarta.validation + jakarta.validation-api + 3.0.2 + compile + \ No newline at end of file diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/domain/entity/User.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/domain/entity/User.java index 4bf12d8..fe4a36a 100644 --- a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/domain/entity/User.java +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/domain/entity/User.java @@ -1,21 +1,57 @@ package cn.nopj.chaos_api.domain.entity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.io.Serializable; import java.time.LocalDateTime; +import java.util.List; + @Data @TableName("t_user") public class User implements Serializable { + /** + * 用户id + */ + @TableId private Long id; + /** + * 用户名称 + */ private String username; + /** + * 密码 + */ private String password; + /** + * 账号是否启用 + */ private Boolean enabled; + /** + * 账号是否未过期 + */ private Boolean accountNonExpired; + /** + * 密码是否未过期 + */ private Boolean credentialsNonExpired; + /** + * 账号是否未锁定 + */ private Boolean accountNonLocked; + /** + * 创建时间 + */ private LocalDateTime createTime; + /** + * 更新时间 + */ private LocalDateTime updateTime; + + @TableField(exist = false) + private List roles; } + diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/LoginRequest.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/LoginRequest.java deleted file mode 100644 index 76aa1ea..0000000 --- a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/LoginRequest.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.nopj.chaos_api.dto; - -import lombok.Data; - -/** - * 登录请求参数 - */ -@Data -public class LoginRequest { - private String username; - private String password; -} diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/RegisterRequest.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/RegisterRequest.java deleted file mode 100644 index b1f401d..0000000 --- a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/RegisterRequest.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.nopj.chaos_api.dto; - -import lombok.Data; - -/** - * 注册请求参数 - */ -@Data -public class RegisterRequest { - private String username; - private String password; -} \ No newline at end of file diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/AuthLoginRequest.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/AuthLoginRequest.java new file mode 100644 index 0000000..f5bd646 --- /dev/null +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/AuthLoginRequest.java @@ -0,0 +1,15 @@ +package cn.nopj.chaos_api.dto.request; + +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +/** + * 登录请求参数 + */ +@Data +public class AuthLoginRequest { + @Pattern(regexp = "^[a-zA-Z0-9_-]{5,16}$", message = "用户名需为5-16位字母、数字、_或-") + private String username; + @Pattern(regexp = "^.{8,16}$", message = "密码长度需为8-16位") + private String password; +} diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/AuthRegisterRequest.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/AuthRegisterRequest.java new file mode 100644 index 0000000..0c323d1 --- /dev/null +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/AuthRegisterRequest.java @@ -0,0 +1,17 @@ +package cn.nopj.chaos_api.dto.request; + + +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +/** + * 注册请求参数 + */ +@Data +public class AuthRegisterRequest { + @Pattern(regexp = "^[a-zA-Z0-9_-]{5,16}$", message = "用户名需为5-16位字母、数字、_或-") + private String username; + //密码不低于8位 不高于16位不限制字符 + @Pattern(regexp = "^.{8,16}$", message = "密码长度需为8-16位") + private String password; +} \ No newline at end of file diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/SetUserPasswordRequest.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/SetUserPasswordRequest.java new file mode 100644 index 0000000..78f7413 --- /dev/null +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/request/SetUserPasswordRequest.java @@ -0,0 +1,13 @@ +package cn.nopj.chaos_api.dto.request; + +import jakarta.validation.constraints.Pattern; +import lombok.Data; + +@Data +public class SetUserPasswordRequest { + //用户id为数字Long类型 + @Pattern(regexp = "^[0-9]+$", message = "用户id格式错误") + private String userId; + @Pattern(regexp = "^.{8,16}$", message = "密码长度需为8-16位") + private String password; +} diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/FileUploadResponse.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/FileUploadResponse.java similarity index 85% rename from chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/FileUploadResponse.java rename to chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/FileUploadResponse.java index 501a252..af2c4a4 100644 --- a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/FileUploadResponse.java +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/FileUploadResponse.java @@ -1,4 +1,4 @@ -package cn.nopj.chaos_api.dto; +package cn.nopj.chaos_api.dto.response; import lombok.Data; diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/RoleResponse.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/RoleResponse.java new file mode 100644 index 0000000..27c5738 --- /dev/null +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/RoleResponse.java @@ -0,0 +1,8 @@ +package cn.nopj.chaos_api.dto.response; + +import lombok.Data; +@Data +public class RoleResponse { + private Long id; + private String name; +} diff --git a/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/UserinfoResponse.java b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/UserinfoResponse.java new file mode 100644 index 0000000..2660584 --- /dev/null +++ b/chaos_api_domain/src/main/java/cn/nopj/chaos_api/dto/response/UserinfoResponse.java @@ -0,0 +1,12 @@ +package cn.nopj.chaos_api.dto.response; + +import lombok.Data; + +import java.util.List; + +@Data +public class UserinfoResponse { + private Long id; + private String username; + private List roles; +} diff --git a/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/ImageService.java b/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/ImageService.java index 192405e..cbd6e7e 100644 --- a/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/ImageService.java +++ b/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/ImageService.java @@ -1,6 +1,8 @@ package cn.nopj.chaos_api.service; +import cn.nopj.chaos_api.dto.response.FileUploadResponse; + import java.io.InputStream; public interface ImageService { @@ -11,5 +13,5 @@ public interface ImageService { * @param content 文件内容 * @return 文件路径 */ - String uploadImage(String fileName, InputStream content); + FileUploadResponse uploadImage(String fileName, InputStream content); } diff --git a/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/UserInfoService.java b/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/UserInfoService.java new file mode 100644 index 0000000..4b6ebc2 --- /dev/null +++ b/chaos_api_interface/src/main/java/cn/nopj/chaos_api/service/UserInfoService.java @@ -0,0 +1,37 @@ +package cn.nopj.chaos_api.service; + +import cn.nopj.chaos_api.dto.response.UserinfoResponse; +import jakarta.validation.constraints.Pattern; + +import java.util.List; + +/** + * 用户信息服务 + * + * @author nopj + */ +public interface UserInfoService { + + /** + * 设置用户密码 + * + * @param id 用户id + * @param password 密码 + */ + void setUserPassword(Long id, String password); + + /** + * 获取所有用户信息 + * + * @return 所有用户信息 + */ + List getAllUsers(); + + /** + * 设置用户密码 + * + * @param userId 用户id + * @param password 密码 + */ + void setUserPassword(@Pattern(regexp = "^[0-9]+$", message = "用户id格式错误") String userId, @Pattern(regexp = "^.{8,16}$", message = "密码长度需为8-16位") String password); +} diff --git a/chaos_api_service/pom.xml b/chaos_api_service/pom.xml index 3f228a8..3643d62 100644 --- a/chaos_api_service/pom.xml +++ b/chaos_api_service/pom.xml @@ -50,5 +50,12 @@ spring-web compile + + org.springframework.security + spring-security-crypto + 6.5.6 + compile + + \ No newline at end of file diff --git a/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/ImageServiceImpl.java b/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/ImageServiceImpl.java index abf5924..d15fd0b 100644 --- a/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/ImageServiceImpl.java +++ b/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/ImageServiceImpl.java @@ -1,5 +1,6 @@ package cn.nopj.chaos_api.service.impl; +import cn.nopj.chaos_api.dto.response.FileUploadResponse; import cn.nopj.chaos_api.service.ImageService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -10,8 +11,8 @@ import java.io.InputStream; @Service public class ImageServiceImpl implements ImageService { @Override - public String uploadImage(String fileName, InputStream content) { + public FileUploadResponse uploadImage(String fileName, InputStream content) { //todo 完成上传图片功能 - return ""; + return new FileUploadResponse(); } } diff --git a/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/UserInfoServiceImpl.java b/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/UserInfoServiceImpl.java new file mode 100644 index 0000000..e0058ad --- /dev/null +++ b/chaos_api_service/src/main/java/cn/nopj/chaos_api/service/impl/UserInfoServiceImpl.java @@ -0,0 +1,75 @@ +package cn.nopj.chaos_api.service.impl; + +import cn.nopj.chaos_api.common.constants.ErrorCode; +import cn.nopj.chaos_api.common.exceotion.BizException; +import cn.nopj.chaos_api.domain.entity.User; +import cn.nopj.chaos_api.dto.response.RoleResponse; +import cn.nopj.chaos_api.dto.response.UserinfoResponse; +import cn.nopj.chaos_api.mapper.UserMapper; +import cn.nopj.chaos_api.service.UserInfoService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class UserInfoServiceImpl implements UserInfoService { + @Autowired + private PasswordEncoder passwordEncoder; + @Autowired + UserMapper userMapper; + @Override + public void setUserPassword(Long Id, String password) { + + log.info("重置用户id: {}的密码", Id); + User user = userMapper.selectById(Id); + //查询用户是否存在 + + if (user == null) { + throw new BizException(ErrorCode.USER_NOT_EXISTS); + } + user.setPassword(passwordEncoder.encode(password)); + int i = userMapper.updateById(user); + log.info("重置用户id: {}的密码结果: {}", Id, i); + + } + + @Override + public List getAllUsers() { + List allUserWithRoles = userMapper.findAllUserWithRoles(); + + if (allUserWithRoles == null){ + throw new RuntimeException("获取所有用户信息失败"); + } + + return allUserWithRoles.stream().map(user -> { + UserinfoResponse userinfoResponse = new UserinfoResponse(); + userinfoResponse.setId(user.getId()); + userinfoResponse.setUsername(user.getUsername()); + userinfoResponse.setRoles( + user.getRoles().stream().map(role -> { + RoleResponse roleResponse = new RoleResponse(); + roleResponse.setId(role.getId()); + roleResponse.setName(role.getName()); + return roleResponse; + }).toList() + ); + return userinfoResponse; + }).toList(); + } + + @Override + public void setUserPassword(String userId, String password) { + try{ + Long Id = Long.parseLong(userId); + setUserPassword(Id, password); + }catch (NumberFormatException e) { + throw new BizException(ErrorCode.USER_ID_INVALID); + } + } + + +} diff --git a/chaos_api_web/pom.xml b/chaos_api_web/pom.xml index 9eefc1e..5bae1e0 100644 --- a/chaos_api_web/pom.xml +++ b/chaos_api_web/pom.xml @@ -44,7 +44,6 @@ org.springframework.boot spring-boot-starter-web - org.springframework.boot @@ -69,7 +68,6 @@ provided - diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/ChaosApiWebApplication.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/ChaosApiWebApplication.java index d600772..371539c 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/ChaosApiWebApplication.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/ChaosApiWebApplication.java @@ -1,9 +1,11 @@ package cn.nopj.chaos_api; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication(scanBasePackages = "cn.nopj.chaos_api") +@Slf4j public class ChaosApiWebApplication { public static void main(String[] args) { SpringApplication.run(ChaosApiWebApplication.class, args); diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java index 363bcbe..b82542c 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java @@ -38,10 +38,8 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { - log.info("JWT 登录授权过滤器"); String authHeader = request.getHeader(this.tokenHeader); - log.info("authHeader: {}", authHeader); if (authHeader != null && authHeader.startsWith(this.tokenHead)) { String authToken = authHeader.substring(this.tokenHead.length()+1); log.info("authToken={}", authToken); diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/AuthController.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/AuthController.java index 975ec55..83d6118 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/AuthController.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/AuthController.java @@ -1,12 +1,13 @@ package cn.nopj.chaos_api.controller; -import cn.nopj.chaos_api.dto.LoginRequest; -import cn.nopj.chaos_api.dto.RegisterRequest; +import cn.nopj.chaos_api.converter.UserConverter; +import cn.nopj.chaos_api.dto.request.AuthLoginRequest; +import cn.nopj.chaos_api.dto.request.AuthRegisterRequest; import cn.nopj.chaos_api.model.ApiResult; import cn.nopj.chaos_api.service.AuthService; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -15,6 +16,11 @@ import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; + +/** + * 认证控管理 + * + */ @RestController @RequestMapping("/api/auth") public class AuthController { @@ -24,38 +30,34 @@ public class AuthController { @Value("${jwt.tokenHead}") private String tokenHead; + @Autowired + private UserConverter userConverter; /** * 注册 * - * @param registerRequest 注册信息 + * @param authRegisterRequest 注册信息 * @return 注册结果 */ @PostMapping("/register") - public ApiResult register(@RequestBody RegisterRequest registerRequest) { - if (authService.register(registerRequest) != null) { - return ApiResult.success("注册成功"); - } - return ApiResult.failed("用户名已存在"); + public ApiResult register(@Valid @RequestBody AuthRegisterRequest authRegisterRequest) { + return ApiResult.success(authService.register(userConverter.convert(authRegisterRequest))); } /** * 登录 * - * @param loginRequest 登录信息 + * @param authLoginRequest 登录信息 * @return 登录结果 */ @PostMapping("/login") - public ApiResult login(@RequestBody LoginRequest loginRequest) { - try { - String token = authService.login(loginRequest.getUsername(), loginRequest.getPassword()); + public ApiResult login(@RequestBody AuthLoginRequest authLoginRequest) { - Map tokenMap = new HashMap<>(); - tokenMap.put("token", token); - tokenMap.put("tokenHead", tokenHead); - return ApiResult.success(tokenMap); + String token = authService.login(authLoginRequest.getUsername(), authLoginRequest.getPassword()); + + Map tokenMap = new HashMap<>(); + tokenMap.put("token", token); + tokenMap.put("tokenHead", tokenHead); + return ApiResult.success(tokenMap); - }catch (BadCredentialsException e){ - return ApiResult.failed(e.getMessage()); - } } } diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java deleted file mode 100644 index 8515fd9..0000000 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.nopj.chaos_api.controller; - -import cn.nopj.chaos_api.model.ApiResult; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Slf4j -@RestController -@RequestMapping("/api/hls") -public class HLSController { - - @PreAuthorize("hasAuthority('admin')") - @GetMapping("/") - ApiResult getHLS(){ - return ApiResult.success("HLS is radar"); - } -} \ No newline at end of file diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/ImageController.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/ImageController.java index bb06729..eaa84d8 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/ImageController.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/ImageController.java @@ -1,5 +1,6 @@ package cn.nopj.chaos_api.controller; +import cn.nopj.chaos_api.dto.response.FileUploadResponse; import cn.nopj.chaos_api.model.ApiResult; import cn.nopj.chaos_api.service.ImageService; import lombok.extern.slf4j.Slf4j; @@ -9,6 +10,12 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.io.InputStream; + +/** + * 图片管理 + */ @Slf4j @RestController @RequestMapping("/api/image") @@ -16,9 +23,24 @@ public class ImageController { @Autowired private ImageService imageService; + /** + * 上传图片 + * @param file 文件 + * @return 上传结果 + */ @RequestMapping("/upload") ApiResult uploadImage(@RequestParam("file") MultipartFile file) { - log.info("上传图片"); - return ApiResult.success("上传成功"); + if (file.isEmpty()){ + return ApiResult.failed("上传文件不能为空"); + } + try { + String fileName = file.getName(); + InputStream inputStream = file.getInputStream(); + FileUploadResponse response = imageService.uploadImage(fileName, inputStream); + return ApiResult.success(response.getFileDownloadUri()); + }catch (IOException e){ + log.error("上传文件失败", e); + return ApiResult.failed("上传文件失败"); + } } } diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/RoleController.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/RoleController.java index 728e8af..969414b 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/RoleController.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/RoleController.java @@ -5,6 +5,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RestController; +/** + * 角色管理 + */ @Slf4j @RestController public class RoleController { diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/UserController.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/UserController.java index 85a62ab..61ab936 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/UserController.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/UserController.java @@ -1,13 +1,52 @@ package cn.nopj.chaos_api.controller; - +import cn.nopj.chaos_api.dto.request.SetUserPasswordRequest; +import cn.nopj.chaos_api.dto.response.UserinfoResponse; +import cn.nopj.chaos_api.model.ApiResult; +import cn.nopj.chaos_api.service.UserInfoService; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; +/** + * 用户管理 + * + * @author nopj + */ @Slf4j @RestController +@RequestMapping("/api/user") public class UserController { + @Autowired + UserInfoService userInfoService; + + /** + * 获取所有用户信息 + * @return 所有用户信息 + */ + @PreAuthorize("hasAuthority('admin')") + @GetMapping("/all") + ApiResult> getAllUsers(){ + return ApiResult.success(userInfoService.getAllUsers()); + } + + /** + * 设置用户密码 + * @param request 设置用户密码请求 + * @return 设置用户密码结果 + */ + @PreAuthorize("hasAuthority('admin')") + @RequestMapping("/setUserPassword") + ApiResult setUserPassword(@RequestBody SetUserPasswordRequest request){ + userInfoService.setUserPassword(request.getUserId(), request.getPassword()); + return ApiResult.success("用户密码修改成功"); + } } diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/exception/GlobalExceptionHandler.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..085b00e --- /dev/null +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/exception/GlobalExceptionHandler.java @@ -0,0 +1,43 @@ +package cn.nopj.chaos_api.exception; + +import cn.nopj.chaos_api.common.exceotion.BizException; +import cn.nopj.chaos_api.model.ApiResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Objects; + +@Slf4j +@RestControllerAdvice +@Order(Ordered.HIGHEST_PRECEDENCE) +public class GlobalExceptionHandler { + @ExceptionHandler(MethodArgumentNotValidException.class) + public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + // 提取第一条错误提示 + String errorMsg = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage(); + // 返回统一的错误格式 + return ApiResult.failed(errorMsg); + } + + /** + * 处理自定义业务异常 + */ + @ExceptionHandler(BizException.class) + public ApiResult handleBizException(BizException ex) { + return ApiResult.failed(ex.getErrorCode().getHttpStatus(),ex.getMessage()); + } + + /** + * 兜底处理:处理所有未知的 RuntimeException + * 防止出现 500 错误页面,给前端返回友好的提示 + */ + @ExceptionHandler(Exception.class) + public ApiResult handleAllExceptions(Exception ex) { + log.error("服务器内部错误", ex); + return ApiResult.failed("服务器内部错误,请联系管理员"); + } +} diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/AuthService.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/AuthService.java index d94c502..4f4119d 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/AuthService.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/AuthService.java @@ -1,13 +1,12 @@ package cn.nopj.chaos_api.service; import cn.nopj.chaos_api.domain.entity.User; -import cn.nopj.chaos_api.dto.RegisterRequest; public interface AuthService { /** * 注册 */ - User register(RegisterRequest registerRequest); + User register(User user); /** * 登录 diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/AuthServiceImpl.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/AuthServiceImpl.java index a66b6bf..d3e00b4 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/AuthServiceImpl.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/AuthServiceImpl.java @@ -1,13 +1,13 @@ package cn.nopj.chaos_api.service.impl; - +import cn.nopj.chaos_api.common.constants.ErrorCode; +import cn.nopj.chaos_api.common.exceotion.BizException; import cn.nopj.chaos_api.domain.entity.User; -import cn.nopj.chaos_api.dto.RegisterRequest; import cn.nopj.chaos_api.mapper.UserMapper; import cn.nopj.chaos_api.service.AuthService; import cn.nopj.chaos_api.util.JwtTokenUtil; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -18,8 +18,9 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service +@Slf4j public class AuthServiceImpl implements AuthService { - + @Autowired private UserMapper userMapper; @@ -31,35 +32,45 @@ public class AuthServiceImpl implements AuthService { private AuthenticationManager authenticationManager; @Override - public User register(RegisterRequest registerRequest) { + public User register(User user) { + // 检查用户名是否已存在 - if (userMapper.selectOne(new QueryWrapper().eq("username", registerRequest.getUsername())) != null) { + if (userMapper.selectByUsername(user.getUsername()) != null) { // 在实际项目中,这里应该抛出自定义异常 - return null; + throw new BizException(ErrorCode.USERNAME_EXISTS); } - User user = new User(); - user.setUsername(registerRequest.getUsername()); // 使用 BCrypt 加密密码 - user.setPassword(passwordEncoder.encode(registerRequest.getPassword())); + user.setPassword(passwordEncoder.encode(user.getPassword())); userMapper.insert(user); // 你可以在这里为新用户分配默认角色 userMapper.insertUserRole(user.getId(), 2L); + return user; } @Override public String login(String username, String password) { + // 尝试进行身份验证 + try{ Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(username, password) ); + + // 将认证结果保存在 SecurityContextHolder 中 + SecurityContextHolder.getContext().setAuthentication(authentication); // 获取用户详情 UserDetails userDetails = (UserDetails) authentication.getPrincipal(); - if (!userDetails.isEnabled()){ + if (!userDetails.isEnabled()) { return null; } // 生成 JWT return jwtTokenUtil.generateToken(userDetails); + }catch (Exception e){ + + throw new BizException(ErrorCode.USER_NOT_EXISTS); + } + } } \ No newline at end of file diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java index aba8d0d..c7ab58d 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java @@ -1,15 +1,16 @@ package cn.nopj.chaos_api.service.impl; +import cn.nopj.chaos_api.common.constants.ErrorCode; +import cn.nopj.chaos_api.common.exceotion.BizException; import cn.nopj.chaos_api.domain.entity.User; import cn.nopj.chaos_api.mapper.UserMapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Service; import java.util.List; @@ -22,11 +23,11 @@ public class UserDetailsServiceImpl implements UserDetailsService { @Autowired UserMapper userMapper; @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + public UserDetails loadUserByUsername(String username) { // 1. 从数据库查询用户 User user = userMapper.selectOne(new QueryWrapper().eq("username", username)); if (user == null) { - throw new UsernameNotFoundException("用户不存在"); + throw new BizException(ErrorCode.USER_NOT_EXISTS); } // 2. 查询该用户的权限信息 (角色 + 权限) @@ -38,6 +39,7 @@ public class UserDetailsServiceImpl implements UserDetailsService { .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); + // 4. 构建并返回 Spring Security 的 User 对象 return new org.springframework.security.core.userdetails.User( user.getUsername(), diff --git a/pom.xml b/pom.xml index 5522f51..6a7bf69 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,7 @@ logback-classic 1.5.18 + org.springframework.boot spring-boot-starter-amqp @@ -130,6 +131,18 @@ spring-web 6.2.13 + + + org.springframework.boot + spring-boot-starter-validation + 3.5.7 + + + + org.springframework + spring-context + 6.2.13 + \ No newline at end of file