diff --git a/backend/src/main/java/com/xly/erp/module/usr/entity/SysCompany.java b/backend/src/main/java/com/xly/erp/module/usr/entity/SysCompany.java new file mode 100644 index 0000000..f68005e --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/entity/SysCompany.java @@ -0,0 +1,29 @@ +package com.xly.erp.module.usr.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 公司表实体(只需登录用到的字段)。docs/03 § sys_company。 + */ +@Data +@TableName("sys_company") +public class SysCompany { + + @TableId(value = "iIncrement", type = IdType.AUTO) + private Integer iIncrement; + + private String sId; + private String sBrandsId; + private String sSubsidiaryId; + private LocalDateTime tCreateDate; + + private String sCompanyName; + private String sCompanyCode; + private Integer iSortOrder; + private Integer iIsDeleted; +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/entity/SysEmployee.java b/backend/src/main/java/com/xly/erp/module/usr/entity/SysEmployee.java new file mode 100644 index 0000000..07d1179 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/entity/SysEmployee.java @@ -0,0 +1,32 @@ +package com.xly.erp.module.usr.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 职员表实体(只读 join,含登录返回 employeeName 所需的最小字段)。 + * docs/03 § sys_employee。 + */ +@Data +@TableName("sys_employee") +public class SysEmployee { + + @TableId(value = "iIncrement", type = IdType.AUTO) + private Integer iIncrement; + + private String sId; + private String sBrandsId; + private String sSubsidiaryId; + private LocalDateTime tCreateDate; + + private String sEmployeeName; + private String sEmployeeCode; + private Integer iDepartmentId; + private String sPhone; + private String sEmail; + private Integer iIsDeleted; +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/entity/SysUser.java b/backend/src/main/java/com/xly/erp/module/usr/entity/SysUser.java new file mode 100644 index 0000000..07b408f --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/entity/SysUser.java @@ -0,0 +1,40 @@ +package com.xly.erp.module.usr.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 用户表实体。docs/03 § sys_user。 + */ +@Data +@TableName("sys_user") +public class SysUser { + + @TableId(value = "iIncrement", type = IdType.AUTO) + private Integer iIncrement; + + private String sId; + private String sBrandsId; + private String sSubsidiaryId; + private LocalDateTime tCreateDate; + + private String sUsername; + private String sUserCode; + private String sPasswordHash; + + private Integer iEmployeeId; + private String sUserType; + private String sLanguage; + private Integer iCanEditDocument; + private Integer iIsDeleted; + private Integer iFailedLoginCount; + private LocalDateTime tLockUntil; + private LocalDateTime tLastLoginDate; + private String sCreatedBy; + private String sUpdatedBy; + private LocalDateTime tUpdatedDate; +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/mapper/SysCompanyMapper.java b/backend/src/main/java/com/xly/erp/module/usr/mapper/SysCompanyMapper.java new file mode 100644 index 0000000..6705b51 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/mapper/SysCompanyMapper.java @@ -0,0 +1,13 @@ +package com.xly.erp.module.usr.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xly.erp.module.usr.entity.SysCompany; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface SysCompanyMapper extends BaseMapper { + + @Select("SELECT * FROM sys_company WHERE sCompanyCode = #{code} LIMIT 1") + SysCompany selectByCode(String code); +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/mapper/SysEmployeeMapper.java b/backend/src/main/java/com/xly/erp/module/usr/mapper/SysEmployeeMapper.java new file mode 100644 index 0000000..3e0b638 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/mapper/SysEmployeeMapper.java @@ -0,0 +1,9 @@ +package com.xly.erp.module.usr.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xly.erp.module.usr.entity.SysEmployee; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SysEmployeeMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/mapper/SysUserMapper.java b/backend/src/main/java/com/xly/erp/module/usr/mapper/SysUserMapper.java new file mode 100644 index 0000000..daca0f9 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/mapper/SysUserMapper.java @@ -0,0 +1,13 @@ +package com.xly.erp.module.usr.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.xly.erp.module.usr.entity.SysUser; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface SysUserMapper extends BaseMapper { + + @Select("SELECT * FROM sys_user WHERE sUsername = #{username} LIMIT 1") + SysUser selectByUsername(String username); +} diff --git a/backend/src/test/java/com/xly/erp/module/usr/mapper/SysUserMapperTest.java b/backend/src/test/java/com/xly/erp/module/usr/mapper/SysUserMapperTest.java new file mode 100644 index 0000000..76c8cb4 --- /dev/null +++ b/backend/src/test/java/com/xly/erp/module/usr/mapper/SysUserMapperTest.java @@ -0,0 +1,46 @@ +package com.xly.erp.module.usr.mapper; + +import com.xly.erp.module.usr.entity.SysUser; +import com.xly.erp.module.usr.support.LoginTestSeeder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@ActiveProfiles("test") +class SysUserMapperTest { + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private LoginTestSeeder seeder; + + @BeforeEach + void setUp() { + seeder.reset(); + } + + @Test + void selectByUsername_returnsUserWithAllFields() { + SysUser user = userMapper.selectByUsername(LoginTestSeeder.USER_OK); + assertNotNull(user); + assertEquals(LoginTestSeeder.USER_OK, user.getSUsername()); + assertEquals("U001", user.getSUserCode()); + assertNotNull(user.getSPasswordHash()); + assertEquals("NORMAL", user.getSUserType()); + assertEquals("zh-CN", user.getSLanguage()); + assertEquals(0, user.getIIsDeleted()); + assertEquals(0, user.getIFailedLoginCount()); + } + + @Test + void selectByUsername_returnsNullWhenNotFound() { + SysUser user = userMapper.selectByUsername("nobody"); + assertNull(user); + } +} diff --git a/backend/src/test/java/com/xly/erp/module/usr/support/LoginTestSeeder.java b/backend/src/test/java/com/xly/erp/module/usr/support/LoginTestSeeder.java new file mode 100644 index 0000000..3fe8cb1 --- /dev/null +++ b/backend/src/test/java/com/xly/erp/module/usr/support/LoginTestSeeder.java @@ -0,0 +1,116 @@ +package com.xly.erp.module.usr.support; + +import com.xly.erp.module.usr.entity.SysCompany; +import com.xly.erp.module.usr.entity.SysEmployee; +import com.xly.erp.module.usr.entity.SysUser; +import com.xly.erp.module.usr.mapper.SysCompanyMapper; +import com.xly.erp.module.usr.mapper.SysEmployeeMapper; +import com.xly.erp.module.usr.mapper.SysUserMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * 测试数据种子。在每个集成测试 @BeforeEach 调用 reset()。 + * 复用 BCryptPasswordEncoder 生成密码哈希,避免在 SQL 中硬编码。 + * + * 默认账号: + * - alice / Password1! / 启用 / 关联员工"张三" + * - bob_deleted / Password1! / 作废(iIsDeleted=1) + * 默认公司:HQ(启用)/ DEL_CO(软删)。 + */ +@Component +public class LoginTestSeeder { + + public static final String DEFAULT_PASSWORD = "Password1!"; + public static final String COMPANY_OK = "HQ"; + public static final String COMPANY_DELETED = "DEL_CO"; + public static final String USER_OK = "alice"; + public static final String USER_DELETED = "bob_deleted"; + + private final JdbcTemplate jdbc; + private final SysCompanyMapper companyMapper; + private final SysEmployeeMapper employeeMapper; + private final SysUserMapper userMapper; + private final BCryptPasswordEncoder encoder; + + @Autowired + public LoginTestSeeder(JdbcTemplate jdbc, + SysCompanyMapper companyMapper, + SysEmployeeMapper employeeMapper, + SysUserMapper userMapper, + BCryptPasswordEncoder encoder) { + this.jdbc = jdbc; + this.companyMapper = companyMapper; + this.employeeMapper = employeeMapper; + this.userMapper = userMapper; + this.encoder = encoder; + } + + @Transactional + public Fixture reset() { + jdbc.update("DELETE FROM sys_user_permission_category"); + jdbc.update("DELETE FROM sys_user"); + jdbc.update("DELETE FROM sys_employee"); + jdbc.update("DELETE FROM sys_department"); + jdbc.update("DELETE FROM sys_company"); + + SysCompany hq = new SysCompany(); + hq.setSCompanyCode(COMPANY_OK); + hq.setSCompanyName("总部"); + hq.setIIsDeleted(0); + companyMapper.insert(hq); + + SysCompany del = new SysCompany(); + del.setSCompanyCode(COMPANY_DELETED); + del.setSCompanyName("已删公司"); + del.setIIsDeleted(1); + companyMapper.insert(del); + + jdbc.update("INSERT INTO sys_department (sDepartmentName, sDepartmentCode, iIsDeleted) VALUES (?,?,0)", + "技术部", "TECH"); + Integer deptId = jdbc.queryForObject( + "SELECT iIncrement FROM sys_department WHERE sDepartmentCode='TECH'", + Integer.class); + + SysEmployee emp = new SysEmployee(); + emp.setSEmployeeName("张三"); + emp.setSEmployeeCode("E001"); + emp.setIDepartmentId(deptId); + emp.setIIsDeleted(0); + employeeMapper.insert(emp); + + String hash = encoder.encode(DEFAULT_PASSWORD); + + SysUser alice = new SysUser(); + alice.setSUsername(USER_OK); + alice.setSUserCode("U001"); + alice.setSPasswordHash(hash); + alice.setIEmployeeId(emp.getIIncrement()); + alice.setSUserType("NORMAL"); + alice.setSLanguage("zh-CN"); + alice.setICanEditDocument(0); + alice.setIIsDeleted(0); + alice.setIFailedLoginCount(0); + alice.setSCreatedBy("system"); + userMapper.insert(alice); + + SysUser bob = new SysUser(); + bob.setSUsername(USER_DELETED); + bob.setSUserCode("U002"); + bob.setSPasswordHash(hash); + bob.setSUserType("NORMAL"); + bob.setSLanguage("zh-CN"); + bob.setICanEditDocument(0); + bob.setIIsDeleted(1); + bob.setIFailedLoginCount(0); + bob.setSCreatedBy("system"); + userMapper.insert(bob); + + return new Fixture(alice.getIIncrement(), bob.getIIncrement(), emp.getIIncrement()); + } + + public record Fixture(Integer aliceId, Integer bobDeletedId, Integer employeeId) {} +}