Commit b2b67f4754e38687422da44c564ff0372aac0682
1 parent
3be59f71
feat(usr): jjwt deps + login DTO/VO + error codes REQ-USR-004
Showing
6 changed files
with
129 additions
and
0 deletions
backend/pom.xml
| @@ -82,6 +82,25 @@ | @@ -82,6 +82,25 @@ | ||
| 82 | <version>${hutool.version}</version> | 82 | <version>${hutool.version}</version> |
| 83 | </dependency> | 83 | </dependency> |
| 84 | 84 | ||
| 85 | + <!-- REQ-USR-004 JWT (jjwt 0.12.x) --> | ||
| 86 | + <dependency> | ||
| 87 | + <groupId>io.jsonwebtoken</groupId> | ||
| 88 | + <artifactId>jjwt-api</artifactId> | ||
| 89 | + <version>0.12.6</version> | ||
| 90 | + </dependency> | ||
| 91 | + <dependency> | ||
| 92 | + <groupId>io.jsonwebtoken</groupId> | ||
| 93 | + <artifactId>jjwt-impl</artifactId> | ||
| 94 | + <version>0.12.6</version> | ||
| 95 | + <scope>runtime</scope> | ||
| 96 | + </dependency> | ||
| 97 | + <dependency> | ||
| 98 | + <groupId>io.jsonwebtoken</groupId> | ||
| 99 | + <artifactId>jjwt-jackson</artifactId> | ||
| 100 | + <version>0.12.6</version> | ||
| 101 | + <scope>runtime</scope> | ||
| 102 | + </dependency> | ||
| 103 | + | ||
| 85 | <dependency> | 104 | <dependency> |
| 86 | <groupId>org.springframework.boot</groupId> | 105 | <groupId>org.springframework.boot</groupId> |
| 87 | <artifactId>spring-boot-starter-test</artifactId> | 106 | <artifactId>spring-boot-starter-test</artifactId> |
backend/src/main/java/com/xly/erp/common/response/ErrorCode.java
| @@ -6,6 +6,8 @@ import lombok.Getter; | @@ -6,6 +6,8 @@ import lombok.Getter; | ||
| 6 | public enum ErrorCode { | 6 | public enum ErrorCode { |
| 7 | SUCCESS(200, "操作成功"), | 7 | SUCCESS(200, "操作成功"), |
| 8 | PARAM_INVALID(40010, "参数错误"), | 8 | PARAM_INVALID(40010, "参数错误"), |
| 9 | + LOGIN_INVALID_CREDENTIALS(40101, "用户名或密码错误"), | ||
| 10 | + LOGIN_ACCOUNT_LOCKED(40301, "账号已临时锁定"), | ||
| 9 | MOD_PARENT_NOT_FOUND(40411, "父模块不存在或已删除"), | 11 | MOD_PARENT_NOT_FOUND(40411, "父模块不存在或已删除"), |
| 10 | MOD_NOT_FOUND(40421, "模块不存在或已删除"), | 12 | MOD_NOT_FOUND(40421, "模块不存在或已删除"), |
| 11 | STAFF_NOT_FOUND(40421, "职员不存在或已删除"), | 13 | STAFF_NOT_FOUND(40421, "职员不存在或已删除"), |
backend/src/main/java/com/xly/erp/module/usr/dto/LoginDTO.java
0 → 100644
| 1 | +package com.xly.erp.module.usr.dto; | ||
| 2 | + | ||
| 3 | +import jakarta.validation.constraints.NotBlank; | ||
| 4 | +import jakarta.validation.constraints.Pattern; | ||
| 5 | +import jakarta.validation.constraints.Size; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +/** REQ-USR-004 用户登录入参 */ | ||
| 9 | +@Data | ||
| 10 | +public class LoginDTO { | ||
| 11 | + | ||
| 12 | + @NotBlank | ||
| 13 | + @Size(max = 50) | ||
| 14 | + private String sUserName; | ||
| 15 | + | ||
| 16 | + @NotBlank | ||
| 17 | + @Size(max = 100) | ||
| 18 | + private String sPassword; | ||
| 19 | + | ||
| 20 | + @NotBlank | ||
| 21 | + @Pattern(regexp = "^standard$", message = "sVersion 仅支持 standard") | ||
| 22 | + private String sVersion; | ||
| 23 | +} |
backend/src/main/java/com/xly/erp/module/usr/vo/LoginResultVO.java
0 → 100644
| 1 | +package com.xly.erp.module.usr.vo; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.NoArgsConstructor; | ||
| 6 | + | ||
| 7 | +/** REQ-USR-004 登录结果 VO(含 JWT + 用户基本信息) */ | ||
| 8 | +@Data | ||
| 9 | +@NoArgsConstructor | ||
| 10 | +@AllArgsConstructor | ||
| 11 | +public class LoginResultVO { | ||
| 12 | + private String accessToken; | ||
| 13 | + private long expiresIn; | ||
| 14 | + private LoginUserInfo user; | ||
| 15 | + | ||
| 16 | + @Data | ||
| 17 | + @NoArgsConstructor | ||
| 18 | + @AllArgsConstructor | ||
| 19 | + public static class LoginUserInfo { | ||
| 20 | + private Integer iIncrement; | ||
| 21 | + private String sUserNo; | ||
| 22 | + private String sUserName; | ||
| 23 | + private String sUserType; | ||
| 24 | + private String sLanguage; | ||
| 25 | + } | ||
| 26 | +} |
backend/src/test/java/com/xly/erp/common/response/ApiResponseTest.java
| @@ -55,5 +55,7 @@ class ApiResponseTest { | @@ -55,5 +55,7 @@ class ApiResponseTest { | ||
| 55 | assertThat(ErrorCode.PERM_CATEGORY_NOT_FOUND.getCode()).isEqualTo(40422); | 55 | assertThat(ErrorCode.PERM_CATEGORY_NOT_FOUND.getCode()).isEqualTo(40422); |
| 56 | assertThat(ErrorCode.USR_USER_NAME_OR_NO_DUP.getCode()).isEqualTo(40921); | 56 | assertThat(ErrorCode.USR_USER_NAME_OR_NO_DUP.getCode()).isEqualTo(40921); |
| 57 | assertThat(ErrorCode.USR_NOT_FOUND.getCode()).isEqualTo(40431); | 57 | assertThat(ErrorCode.USR_NOT_FOUND.getCode()).isEqualTo(40431); |
| 58 | + assertThat(ErrorCode.LOGIN_INVALID_CREDENTIALS.getCode()).isEqualTo(40101); | ||
| 59 | + assertThat(ErrorCode.LOGIN_ACCOUNT_LOCKED.getCode()).isEqualTo(40301); | ||
| 58 | } | 60 | } |
| 59 | } | 61 | } |
backend/src/test/java/com/xly/erp/module/usr/dto/LoginDTOValidationTest.java
0 → 100644
| 1 | +package com.xly.erp.module.usr.dto; | ||
| 2 | + | ||
| 3 | +import jakarta.validation.ConstraintViolation; | ||
| 4 | +import jakarta.validation.Validation; | ||
| 5 | +import jakarta.validation.Validator; | ||
| 6 | +import jakarta.validation.ValidatorFactory; | ||
| 7 | +import org.junit.jupiter.api.Test; | ||
| 8 | + | ||
| 9 | +import java.util.Set; | ||
| 10 | + | ||
| 11 | +import static org.assertj.core.api.Assertions.assertThat; | ||
| 12 | + | ||
| 13 | +class LoginDTOValidationTest { | ||
| 14 | + | ||
| 15 | + private static final ValidatorFactory FACTORY = Validation.buildDefaultValidatorFactory(); | ||
| 16 | + private final Validator validator = FACTORY.getValidator(); | ||
| 17 | + | ||
| 18 | + private LoginDTO valid() { | ||
| 19 | + LoginDTO d = new LoginDTO(); | ||
| 20 | + d.setSUserName("alice"); | ||
| 21 | + d.setSPassword("666666"); | ||
| 22 | + d.setSVersion("standard"); | ||
| 23 | + return d; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Test | ||
| 27 | + void allValid_yieldsNoViolations() { | ||
| 28 | + Set<ConstraintViolation<LoginDTO>> v = validator.validate(valid()); | ||
| 29 | + assertThat(v).isEmpty(); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Test | ||
| 33 | + void blankRequiredFields_yieldsViolations() { | ||
| 34 | + LoginDTO d = new LoginDTO(); | ||
| 35 | + Set<ConstraintViolation<LoginDTO>> v = validator.validate(d); | ||
| 36 | + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()) | ||
| 37 | + .contains("sUserName", "sPassword", "sVersion"); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Test | ||
| 41 | + void invalidVersion_yieldsViolation() { | ||
| 42 | + LoginDTO d = valid(); | ||
| 43 | + d.setSVersion("experimental"); | ||
| 44 | + Set<ConstraintViolation<LoginDTO>> v = validator.validate(d); | ||
| 45 | + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()).contains("sVersion"); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Test | ||
| 49 | + void overSized_yieldsViolation() { | ||
| 50 | + LoginDTO d = valid(); | ||
| 51 | + d.setSUserName("a".repeat(51)); | ||
| 52 | + d.setSPassword("p".repeat(101)); | ||
| 53 | + Set<ConstraintViolation<LoginDTO>> v = validator.validate(d); | ||
| 54 | + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()) | ||
| 55 | + .contains("sUserName", "sPassword"); | ||
| 56 | + } | ||
| 57 | +} |