diff --git a/backend/src/main/java/com/xly/erp/module/usr/dto/UserCreateDTO.java b/backend/src/main/java/com/xly/erp/module/usr/dto/UserCreateDTO.java new file mode 100644 index 0000000..9bf763b --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/dto/UserCreateDTO.java @@ -0,0 +1,38 @@ +package com.xly.erp.module.usr.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +/** REQ-USR-001 用户新增入参(密码不在 DTO 里,service 层固定哈希 666666 落库)。 */ +@Data +public class UserCreateDTO { + + @NotBlank + @Size(max = 50) + private String sUserNo; + + @NotBlank + @Size(max = 50) + private String sUserName; + + /** 可空:关联职员 id */ + private Integer iStaffId; + + @NotBlank + @Pattern(regexp = "^(普通用户|超级管理员)$", message = "sUserType 必须是 普通用户/超级管理员 之一") + private String sUserType; + + @NotBlank + @Pattern(regexp = "^(zh|en|zh-TW)$", message = "sLanguage 必须是 zh/en/zh-TW 之一") + private String sLanguage; + + /** 可空:默认 false */ + private Boolean bCanModifyDocs; + + /** 可空:每个 id 必须指向未删除的 tPermissionCategory.iIncrement */ + private List permissionCategoryIds; +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/vo/UserVO.java b/backend/src/main/java/com/xly/erp/module/usr/vo/UserVO.java new file mode 100644 index 0000000..e1d7ee2 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/vo/UserVO.java @@ -0,0 +1,38 @@ +package com.xly.erp.module.usr.vo; + +import com.xly.erp.module.usr.entity.UserEntity; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +/** REQ-USR-001 用户 VO(不含 sPasswordHash 等内部字段)。 */ +@Data +public class UserVO { + private Integer iIncrement; + private String sUserNo; + private String sUserName; + private Integer iStaffId; + private String sUserType; + private String sLanguage; + private Boolean bCanModifyDocs; + private LocalDateTime tCreateDate; + private Boolean bDeleted; + private List permissionCategoryIds = new ArrayList<>(); + + public static UserVO from(UserEntity e, List permissionCategoryIds) { + UserVO v = new UserVO(); + v.setIIncrement(e.getIIncrement()); + v.setSUserNo(e.getSUserNo()); + v.setSUserName(e.getSUserName()); + v.setIStaffId(e.getIStaffId()); + v.setSUserType(e.getSUserType()); + v.setSLanguage(e.getSLanguage()); + v.setBCanModifyDocs(e.getBCanModifyDocs()); + v.setTCreateDate(e.getTCreateDate()); + v.setBDeleted(e.getBDeleted()); + v.setPermissionCategoryIds(permissionCategoryIds == null ? new ArrayList<>() : permissionCategoryIds); + return v; + } +} diff --git a/backend/src/test/java/com/xly/erp/module/usr/dto/UserCreateDTOValidationTest.java b/backend/src/test/java/com/xly/erp/module/usr/dto/UserCreateDTOValidationTest.java new file mode 100644 index 0000000..179aab3 --- /dev/null +++ b/backend/src/test/java/com/xly/erp/module/usr/dto/UserCreateDTOValidationTest.java @@ -0,0 +1,69 @@ +package com.xly.erp.module.usr.dto; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class UserCreateDTOValidationTest { + + private static final ValidatorFactory FACTORY = Validation.buildDefaultValidatorFactory(); + private final Validator validator = FACTORY.getValidator(); + + private UserCreateDTO valid() { + UserCreateDTO d = new UserCreateDTO(); + d.setSUserNo("u001"); + d.setSUserName("alice"); + d.setSUserType("普通用户"); + d.setSLanguage("zh"); + d.setBCanModifyDocs(false); + d.setPermissionCategoryIds(List.of()); + return d; + } + + @Test + void allValidFields_yieldsNoViolations() { + Set> v = validator.validate(valid()); + assertThat(v).isEmpty(); + } + + @Test + void blankRequiredFields_yieldsViolations() { + UserCreateDTO d = new UserCreateDTO(); + Set> v = validator.validate(d); + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()) + .contains("sUserNo", "sUserName", "sUserType", "sLanguage"); + } + + @Test + void invalidUserTypeEnum_yieldsViolation() { + UserCreateDTO d = valid(); + d.setSUserType("非法值"); + Set> v = validator.validate(d); + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()).contains("sUserType"); + } + + @Test + void invalidLanguageEnum_yieldsViolation() { + UserCreateDTO d = valid(); + d.setSLanguage("fr"); + Set> v = validator.validate(d); + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()).contains("sLanguage"); + } + + @Test + void overSizedFields_yieldsViolations() { + UserCreateDTO d = valid(); + d.setSUserNo("a".repeat(51)); + d.setSUserName("a".repeat(51)); + Set> v = validator.validate(d); + assertThat(v).extracting(cv -> cv.getPropertyPath().toString()) + .contains("sUserNo", "sUserName"); + } +}