Commit fe2e63069a6353b94bf39b50dfb7311f16f78391
1 parent
c6c415dc
feat(usr): UserMapper.xml searchUsers REQ-USR-003
Showing
3 changed files
with
128 additions
and
0 deletions
backend/src/main/java/com/xly/erp/module/usr/mapper/UserMapper.java
| 1 | package com.xly.erp.module.usr.mapper; | 1 | package com.xly.erp.module.usr.mapper; |
| 2 | 2 | ||
| 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| 4 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 5 | +import com.xly.erp.module.usr.dto.UserQueryDTO; | ||
| 4 | import com.xly.erp.module.usr.entity.UserEntity; | 6 | import com.xly.erp.module.usr.entity.UserEntity; |
| 7 | +import com.xly.erp.module.usr.vo.UserListItemVO; | ||
| 8 | +import org.apache.ibatis.annotations.Param; | ||
| 5 | 9 | ||
| 6 | public interface UserMapper extends BaseMapper<UserEntity> { | 10 | public interface UserMapper extends BaseMapper<UserEntity> { |
| 11 | + | ||
| 12 | + /** REQ-USR-003 用户列表查询:跨表 JOIN tStaff,按 query 过滤 + 分页。XML 实现。 */ | ||
| 13 | + IPage<UserListItemVO> searchUsers(IPage<UserListItemVO> page, @Param("query") UserQueryDTO query); | ||
| 7 | } | 14 | } |
backend/src/main/resources/mapper/usr/UserMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||
| 3 | +<mapper namespace="com.xly.erp.module.usr.mapper.UserMapper"> | ||
| 4 | + | ||
| 5 | + <!-- REQ-USR-003 用户列表查询:LEFT JOIN tStaff + 动态 WHERE。 | ||
| 6 | + query.column 由 service 层白名单映射,绝不接受用户原始输入。 --> | ||
| 7 | + <select id="searchUsers" resultType="com.xly.erp.module.usr.vo.UserListItemVO"> | ||
| 8 | + SELECT | ||
| 9 | + u.iIncrement, u.sUserName, s.sStaffName, u.sUserNo, | ||
| 10 | + s.sDepartment, u.sUserType, u.sLanguage, u.bDeleted, | ||
| 11 | + u.tLastLoginDate, u.sCreatedBy, u.tCreateDate | ||
| 12 | + FROM tUser u | ||
| 13 | + LEFT JOIN tStaff s ON u.iStaffId = s.iIncrement AND s.bDeleted = 0 | ||
| 14 | + <where> | ||
| 15 | + <if test="query.queryField != 'deleted'"> | ||
| 16 | + u.bDeleted = 0 | ||
| 17 | + </if> | ||
| 18 | + <if test="query.column != null and query.column != '' and query.queryValue != null and query.queryValue != ''"> | ||
| 19 | + AND | ||
| 20 | + <choose> | ||
| 21 | + <when test="query.matchType == 'equals'"> | ||
| 22 | + ${query.column} = #{query.queryValue} | ||
| 23 | + </when> | ||
| 24 | + <when test="query.matchType == 'notContains'"> | ||
| 25 | + ${query.column} NOT LIKE CONCAT('%', #{query.queryValue}, '%') | ||
| 26 | + </when> | ||
| 27 | + <otherwise> | ||
| 28 | + ${query.column} LIKE CONCAT('%', #{query.queryValue}, '%') | ||
| 29 | + </otherwise> | ||
| 30 | + </choose> | ||
| 31 | + </if> | ||
| 32 | + </where> | ||
| 33 | + ORDER BY u.tCreateDate DESC, u.iIncrement DESC | ||
| 34 | + </select> | ||
| 35 | +</mapper> |
backend/src/test/java/com/xly/erp/module/usr/mapper/UserMapperSearchIT.java
0 → 100644
| 1 | +package com.xly.erp.module.usr.mapper; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 4 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 5 | +import com.xly.erp.module.usr.dto.UserQueryDTO; | ||
| 6 | +import com.xly.erp.module.usr.entity.StaffEntity; | ||
| 7 | +import com.xly.erp.module.usr.entity.UserEntity; | ||
| 8 | +import com.xly.erp.module.usr.vo.UserListItemVO; | ||
| 9 | +import org.junit.jupiter.api.Test; | ||
| 10 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 11 | +import org.springframework.boot.test.context.SpringBootTest; | ||
| 12 | +import org.springframework.test.annotation.Rollback; | ||
| 13 | +import org.springframework.test.context.ActiveProfiles; | ||
| 14 | +import org.springframework.transaction.annotation.Transactional; | ||
| 15 | + | ||
| 16 | +import java.time.LocalDateTime; | ||
| 17 | + | ||
| 18 | +import static org.assertj.core.api.Assertions.assertThat; | ||
| 19 | + | ||
| 20 | +@SpringBootTest | ||
| 21 | +@ActiveProfiles("test") | ||
| 22 | +@Transactional | ||
| 23 | +@Rollback | ||
| 24 | +class UserMapperSearchIT { | ||
| 25 | + | ||
| 26 | + @Autowired UserMapper userMapper; | ||
| 27 | + @Autowired StaffMapper staffMapper; | ||
| 28 | + | ||
| 29 | + private Integer insertStaff(String name) { | ||
| 30 | + StaffEntity s = new StaffEntity(); | ||
| 31 | + s.setSStaffNo("st_" + System.nanoTime()); | ||
| 32 | + s.setSStaffName(name); | ||
| 33 | + s.setSDepartment("研发部"); | ||
| 34 | + s.setBDeleted(false); | ||
| 35 | + s.setTCreateDate(LocalDateTime.now()); | ||
| 36 | + staffMapper.insert(s); | ||
| 37 | + return s.getIIncrement(); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + private Integer insertUser(String userName, Integer staffId) { | ||
| 41 | + UserEntity u = new UserEntity(); | ||
| 42 | + u.setSUserNo("uno_" + System.nanoTime()); | ||
| 43 | + u.setSUserName(userName); | ||
| 44 | + u.setIStaffId(staffId); | ||
| 45 | + u.setSUserType("普通用户"); | ||
| 46 | + u.setSLanguage("zh"); | ||
| 47 | + u.setBCanModifyDocs(false); | ||
| 48 | + u.setSPasswordHash("$2a$10$x"); | ||
| 49 | + u.setBDeleted(false); | ||
| 50 | + u.setTCreateDate(LocalDateTime.now()); | ||
| 51 | + userMapper.insert(u); | ||
| 52 | + return u.getIIncrement(); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Test | ||
| 56 | + void searchUsers_emptyFilter_returnsAllUndeletedAsPage() { | ||
| 57 | + Integer staffId = insertStaff("张三"); | ||
| 58 | + insertUser("alice_" + System.nanoTime(), staffId); | ||
| 59 | + insertUser("bob_" + System.nanoTime(), null); | ||
| 60 | + | ||
| 61 | + UserQueryDTO query = new UserQueryDTO(); | ||
| 62 | + IPage<UserListItemVO> result = userMapper.searchUsers(new Page<>(1, 50), query); | ||
| 63 | + | ||
| 64 | + assertThat(result.getTotal()).isGreaterThanOrEqualTo(2L); | ||
| 65 | + assertThat(result.getRecords()).extracting(UserListItemVO::getSUserName) | ||
| 66 | + .anyMatch(n -> n.startsWith("alice_") || n.startsWith("bob_")); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Test | ||
| 70 | + void searchUsers_filterByUserName_filtersCorrectly() { | ||
| 71 | + String alicePrefix = "ali_" + System.nanoTime(); | ||
| 72 | + insertUser(alicePrefix + "_alice", null); | ||
| 73 | + insertUser("bob_unmatch_" + System.nanoTime(), null); | ||
| 74 | + | ||
| 75 | + UserQueryDTO query = new UserQueryDTO(); | ||
| 76 | + query.setQueryField("username"); | ||
| 77 | + query.setColumn("u.sUserName"); | ||
| 78 | + query.setMatchType("contains"); | ||
| 79 | + query.setQueryValue(alicePrefix); | ||
| 80 | + | ||
| 81 | + IPage<UserListItemVO> result = userMapper.searchUsers(new Page<>(1, 50), query); | ||
| 82 | + | ||
| 83 | + assertThat(result.getRecords()).hasSize(1); | ||
| 84 | + assertThat(result.getRecords().get(0).getSUserName()).startsWith(alicePrefix); | ||
| 85 | + } | ||
| 86 | +} |