Commit 93a189675f0c8c9b1fa1f8100ad79ce3cffb3c0f
1 parent
32dcaa25
feat(usr): selectUserList XML + getUserList service + GET /api/usr/users REQ-USR-003
Showing
7 changed files
with
180 additions
and
0 deletions
backend/src/main/java/com/example/erp/module/usr/controller/UserController.java
| 1 | package com.example.erp.module.usr.controller; | 1 | package com.example.erp.module.usr.controller; |
| 2 | 2 | ||
| 3 | import com.example.erp.common.response.Result; | 3 | import com.example.erp.common.response.Result; |
| 4 | +import com.example.erp.common.vo.PageVO; | ||
| 4 | import com.example.erp.config.UserPrincipal; | 5 | import com.example.erp.config.UserPrincipal; |
| 5 | import com.example.erp.module.usr.dto.UserCreateReqDTO; | 6 | import com.example.erp.module.usr.dto.UserCreateReqDTO; |
| 7 | +import com.example.erp.module.usr.dto.UserListQueryDTO; | ||
| 6 | import com.example.erp.module.usr.service.UserService; | 8 | import com.example.erp.module.usr.service.UserService; |
| 7 | import com.example.erp.module.usr.vo.PermissionGroupVO; | 9 | import com.example.erp.module.usr.vo.PermissionGroupVO; |
| 8 | import com.example.erp.module.usr.vo.StaffVO; | 10 | import com.example.erp.module.usr.vo.StaffVO; |
| 9 | import com.example.erp.module.usr.vo.UserCreateRespVO; | 11 | import com.example.erp.module.usr.vo.UserCreateRespVO; |
| 12 | +import com.example.erp.module.usr.vo.UserListItemVO; | ||
| 10 | import jakarta.validation.Valid; | 13 | import jakarta.validation.Valid; |
| 11 | import lombok.RequiredArgsConstructor; | 14 | import lombok.RequiredArgsConstructor; |
| 12 | import org.springframework.security.core.annotation.AuthenticationPrincipal; | 15 | import org.springframework.security.core.annotation.AuthenticationPrincipal; |
| @@ -28,6 +31,23 @@ public class UserController { | @@ -28,6 +31,23 @@ public class UserController { | ||
| 28 | return Result.ok(userService.createUser(req, principal)); | 31 | return Result.ok(userService.createUser(req, principal)); |
| 29 | } | 32 | } |
| 30 | 33 | ||
| 34 | + @GetMapping("/users") | ||
| 35 | + public Result<PageVO<UserListItemVO>> getUsers( | ||
| 36 | + @RequestParam(defaultValue = "username") String queryField, | ||
| 37 | + @RequestParam(defaultValue = "contains") String matchType, | ||
| 38 | + @RequestParam(defaultValue = "") String queryValue, | ||
| 39 | + @RequestParam(defaultValue = "1") int page, | ||
| 40 | + @RequestParam(defaultValue = "20") int pageSize, | ||
| 41 | + @AuthenticationPrincipal UserPrincipal principal) { | ||
| 42 | + UserListQueryDTO q = new UserListQueryDTO(); | ||
| 43 | + q.setQueryField(queryField); | ||
| 44 | + q.setMatchType(matchType); | ||
| 45 | + q.setQueryValue(queryValue); | ||
| 46 | + q.setPage(page); | ||
| 47 | + q.setPageSize(pageSize); | ||
| 48 | + return Result.ok(userService.getUserList(q, principal.brandId())); | ||
| 49 | + } | ||
| 50 | + | ||
| 31 | @GetMapping("/users/staffs") | 51 | @GetMapping("/users/staffs") |
| 32 | public Result<List<StaffVO>> getStaffs(@AuthenticationPrincipal UserPrincipal principal) { | 52 | public Result<List<StaffVO>> getStaffs(@AuthenticationPrincipal UserPrincipal principal) { |
| 33 | return Result.ok(userService.getStaffs(principal.brandId())); | 53 | return Result.ok(userService.getStaffs(principal.brandId())); |
backend/src/main/java/com/example/erp/module/usr/mapper/UsrUserMapper.java
| 1 | package com.example.erp.module.usr.mapper; | 1 | package com.example.erp.module.usr.mapper; |
| 2 | 2 | ||
| 3 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| 4 | import com.example.erp.module.usr.entity.UsrUserEntity; | 5 | import com.example.erp.module.usr.entity.UsrUserEntity; |
| 6 | +import com.example.erp.module.usr.vo.UserListItemVO; | ||
| 7 | +import org.apache.ibatis.annotations.Param; | ||
| 5 | 8 | ||
| 6 | public interface UsrUserMapper extends BaseMapper<UsrUserEntity> { | 9 | public interface UsrUserMapper extends BaseMapper<UsrUserEntity> { |
| 10 | + | ||
| 11 | + IPage<UserListItemVO> selectUserList( | ||
| 12 | + IPage<UserListItemVO> page, | ||
| 13 | + @Param("brandId") String brandId, | ||
| 14 | + @Param("queryField") String queryField, | ||
| 15 | + @Param("matchType") String matchType, | ||
| 16 | + @Param("queryValue") String queryValue | ||
| 17 | + ); | ||
| 7 | } | 18 | } |
backend/src/main/java/com/example/erp/module/usr/service/UserService.java
| 1 | package com.example.erp.module.usr.service; | 1 | package com.example.erp.module.usr.service; |
| 2 | 2 | ||
| 3 | +import com.example.erp.common.vo.PageVO; | ||
| 3 | import com.example.erp.config.UserPrincipal; | 4 | import com.example.erp.config.UserPrincipal; |
| 4 | import com.example.erp.module.usr.dto.UserCreateReqDTO; | 5 | import com.example.erp.module.usr.dto.UserCreateReqDTO; |
| 6 | +import com.example.erp.module.usr.dto.UserListQueryDTO; | ||
| 5 | import com.example.erp.module.usr.vo.PermissionGroupVO; | 7 | import com.example.erp.module.usr.vo.PermissionGroupVO; |
| 6 | import com.example.erp.module.usr.vo.StaffVO; | 8 | import com.example.erp.module.usr.vo.StaffVO; |
| 7 | import com.example.erp.module.usr.vo.UserCreateRespVO; | 9 | import com.example.erp.module.usr.vo.UserCreateRespVO; |
| 10 | +import com.example.erp.module.usr.vo.UserListItemVO; | ||
| 8 | 11 | ||
| 9 | import java.util.List; | 12 | import java.util.List; |
| 10 | 13 | ||
| @@ -15,4 +18,6 @@ public interface UserService { | @@ -15,4 +18,6 @@ public interface UserService { | ||
| 15 | List<StaffVO> getStaffs(String brandId); | 18 | List<StaffVO> getStaffs(String brandId); |
| 16 | 19 | ||
| 17 | List<PermissionGroupVO> getPermissionGroups(String brandId); | 20 | List<PermissionGroupVO> getPermissionGroups(String brandId); |
| 21 | + | ||
| 22 | + PageVO<UserListItemVO> getUserList(UserListQueryDTO query, String brandId); | ||
| 18 | } | 23 | } |
backend/src/main/java/com/example/erp/module/usr/service/impl/UserServiceImpl.java
| 1 | package com.example.erp.module.usr.service.impl; | 1 | package com.example.erp.module.usr.service.impl; |
| 2 | 2 | ||
| 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 4 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 5 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 4 | import com.example.erp.common.constants.UsrErrorCode; | 6 | import com.example.erp.common.constants.UsrErrorCode; |
| 5 | import com.example.erp.common.exception.BizException; | 7 | import com.example.erp.common.exception.BizException; |
| 8 | +import com.example.erp.common.vo.PageVO; | ||
| 6 | import com.example.erp.config.UserPrincipal; | 9 | import com.example.erp.config.UserPrincipal; |
| 7 | import com.example.erp.module.usr.dto.UserCreateReqDTO; | 10 | import com.example.erp.module.usr.dto.UserCreateReqDTO; |
| 11 | +import com.example.erp.module.usr.dto.UserListQueryDTO; | ||
| 8 | import com.example.erp.module.usr.entity.PermissionGroupEntity; | 12 | import com.example.erp.module.usr.entity.PermissionGroupEntity; |
| 9 | import com.example.erp.module.usr.entity.StaffEntity; | 13 | import com.example.erp.module.usr.entity.StaffEntity; |
| 10 | import com.example.erp.module.usr.entity.UserPermissionEntity; | 14 | import com.example.erp.module.usr.entity.UserPermissionEntity; |
| @@ -17,6 +21,7 @@ import com.example.erp.module.usr.service.UserService; | @@ -17,6 +21,7 @@ import com.example.erp.module.usr.service.UserService; | ||
| 17 | import com.example.erp.module.usr.vo.PermissionGroupVO; | 21 | import com.example.erp.module.usr.vo.PermissionGroupVO; |
| 18 | import com.example.erp.module.usr.vo.StaffVO; | 22 | import com.example.erp.module.usr.vo.StaffVO; |
| 19 | import com.example.erp.module.usr.vo.UserCreateRespVO; | 23 | import com.example.erp.module.usr.vo.UserCreateRespVO; |
| 24 | +import com.example.erp.module.usr.vo.UserListItemVO; | ||
| 20 | import lombok.RequiredArgsConstructor; | 25 | import lombok.RequiredArgsConstructor; |
| 21 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | 26 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| 22 | import org.springframework.stereotype.Service; | 27 | import org.springframework.stereotype.Service; |
| @@ -118,6 +123,16 @@ public class UserServiceImpl implements UserService { | @@ -118,6 +123,16 @@ public class UserServiceImpl implements UserService { | ||
| 118 | 123 | ||
| 119 | @Override | 124 | @Override |
| 120 | @Transactional(readOnly = true) | 125 | @Transactional(readOnly = true) |
| 126 | + public PageVO<UserListItemVO> getUserList(UserListQueryDTO query, String brandId) { | ||
| 127 | + int cappedSize = Math.min(query.getPageSize(), 100); | ||
| 128 | + IPage<UserListItemVO> iPage = new Page<>(query.getPage(), cappedSize); | ||
| 129 | + String queryValue = query.getQueryValue() == null ? "" : query.getQueryValue(); | ||
| 130 | + IPage<UserListItemVO> result = userMapper.selectUserList(iPage, brandId, query.getQueryField(), query.getMatchType(), queryValue); | ||
| 131 | + return PageVO.of(result); | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + @Override | ||
| 135 | + @Transactional(readOnly = true) | ||
| 121 | public List<PermissionGroupVO> getPermissionGroups(String brandId) { | 136 | public List<PermissionGroupVO> getPermissionGroups(String brandId) { |
| 122 | List<PermissionGroupEntity> groups = permGroupMapper.selectList(new LambdaQueryWrapper<PermissionGroupEntity>() | 137 | List<PermissionGroupEntity> groups = permGroupMapper.selectList(new LambdaQueryWrapper<PermissionGroupEntity>() |
| 123 | .eq(PermissionGroupEntity::getSBrandsId, brandId)); | 138 | .eq(PermissionGroupEntity::getSBrandsId, brandId)); |
backend/src/main/resources/mapper/UsrUserMapper.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | ||
| 3 | + "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||
| 4 | +<mapper namespace="com.example.erp.module.usr.mapper.UsrUserMapper"> | ||
| 5 | + | ||
| 6 | + <select id="selectUserList" resultType="com.example.erp.module.usr.vo.UserListItemVO"> | ||
| 7 | + SELECT u.sId, u.sUsername, u.sUserCode, u.sUserType, u.sLanguage, | ||
| 8 | + u.bIsDisabled, u.tLastLoginDate, u.sCreatorUsername, u.tCreateDate, | ||
| 9 | + s.sStaffName, s.sDepartment | ||
| 10 | + FROM usr_user u | ||
| 11 | + LEFT JOIN tStaff s ON u.sEmployeeId = s.sId | ||
| 12 | + AND s.sBrandsId = u.sBrandsId | ||
| 13 | + AND s.bDeleted = 0 | ||
| 14 | + WHERE u.sBrandsId = #{brandId} | ||
| 15 | + <if test="queryValue != null and queryValue != ''"> | ||
| 16 | + <choose> | ||
| 17 | + <when test="queryField == 'username'"> | ||
| 18 | + <choose> | ||
| 19 | + <when test="matchType == 'contains'">AND u.sUsername LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 20 | + <when test="matchType == 'notContains'">AND u.sUsername NOT LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 21 | + <otherwise>AND u.sUsername = #{queryValue}</otherwise> | ||
| 22 | + </choose> | ||
| 23 | + </when> | ||
| 24 | + <when test="queryField == 'staffName'"> | ||
| 25 | + <choose> | ||
| 26 | + <when test="matchType == 'contains'">AND s.sStaffName LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 27 | + <when test="matchType == 'notContains'">AND s.sStaffName NOT LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 28 | + <otherwise>AND s.sStaffName = #{queryValue}</otherwise> | ||
| 29 | + </choose> | ||
| 30 | + </when> | ||
| 31 | + <when test="queryField == 'userCode'"> | ||
| 32 | + <choose> | ||
| 33 | + <when test="matchType == 'contains'">AND u.sUserCode LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 34 | + <when test="matchType == 'notContains'">AND u.sUserCode NOT LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 35 | + <otherwise>AND u.sUserCode = #{queryValue}</otherwise> | ||
| 36 | + </choose> | ||
| 37 | + </when> | ||
| 38 | + <when test="queryField == 'department'"> | ||
| 39 | + <choose> | ||
| 40 | + <when test="matchType == 'contains'">AND s.sDepartment LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 41 | + <when test="matchType == 'notContains'">AND s.sDepartment NOT LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 42 | + <otherwise>AND s.sDepartment = #{queryValue}</otherwise> | ||
| 43 | + </choose> | ||
| 44 | + </when> | ||
| 45 | + <when test="queryField == 'userType'"> | ||
| 46 | + <choose> | ||
| 47 | + <when test="matchType == 'contains'">AND u.sUserType LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 48 | + <when test="matchType == 'notContains'">AND u.sUserType NOT LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 49 | + <otherwise>AND u.sUserType = #{queryValue}</otherwise> | ||
| 50 | + </choose> | ||
| 51 | + </when> | ||
| 52 | + <when test="queryField == 'creator'"> | ||
| 53 | + <choose> | ||
| 54 | + <when test="matchType == 'contains'">AND u.sCreatorUsername LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 55 | + <when test="matchType == 'notContains'">AND u.sCreatorUsername NOT LIKE CONCAT('%', #{queryValue}, '%')</when> | ||
| 56 | + <otherwise>AND u.sCreatorUsername = #{queryValue}</otherwise> | ||
| 57 | + </choose> | ||
| 58 | + </when> | ||
| 59 | + <when test="queryField == 'disabled'"> | ||
| 60 | + <choose> | ||
| 61 | + <when test="queryValue == '是'">AND u.bIsDisabled = 1</when> | ||
| 62 | + <when test="queryValue == '否'">AND u.bIsDisabled = 0</when> | ||
| 63 | + </choose> | ||
| 64 | + </when> | ||
| 65 | + <when test="queryField == 'lastLoginDate'"> | ||
| 66 | + AND DATE(u.tLastLoginDate) = #{queryValue} | ||
| 67 | + </when> | ||
| 68 | + </choose> | ||
| 69 | + </if> | ||
| 70 | + ORDER BY u.tCreateDate DESC | ||
| 71 | + </select> | ||
| 72 | + | ||
| 73 | +</mapper> |
backend/src/test/java/com/example/erp/module/usr/UserControllerTest.java
| @@ -7,7 +7,10 @@ import com.example.erp.config.SecurityConfig; | @@ -7,7 +7,10 @@ import com.example.erp.config.SecurityConfig; | ||
| 7 | import com.example.erp.common.util.JwtUtil; | 7 | import com.example.erp.common.util.JwtUtil; |
| 8 | import com.example.erp.module.usr.controller.UserController; | 8 | import com.example.erp.module.usr.controller.UserController; |
| 9 | import com.example.erp.module.usr.service.UserService; | 9 | import com.example.erp.module.usr.service.UserService; |
| 10 | +import com.example.erp.common.vo.PageVO; | ||
| 11 | +import com.example.erp.module.usr.dto.UserListQueryDTO; | ||
| 10 | import com.example.erp.module.usr.vo.UserCreateRespVO; | 12 | import com.example.erp.module.usr.vo.UserCreateRespVO; |
| 13 | +import com.example.erp.module.usr.vo.UserListItemVO; | ||
| 11 | import com.example.erp.module.usr.vo.StaffVO; | 14 | import com.example.erp.module.usr.vo.StaffVO; |
| 12 | import com.example.erp.module.usr.vo.PermissionGroupVO; | 15 | import com.example.erp.module.usr.vo.PermissionGroupVO; |
| 13 | import com.fasterxml.jackson.databind.ObjectMapper; | 16 | import com.fasterxml.jackson.databind.ObjectMapper; |
| @@ -103,6 +106,28 @@ class UserControllerTest { | @@ -103,6 +106,28 @@ class UserControllerTest { | ||
| 103 | } | 106 | } |
| 104 | 107 | ||
| 105 | @Test | 108 | @Test |
| 109 | + void getUsers_withToken_returns200() throws Exception { | ||
| 110 | + PageVO<UserListItemVO> page = new PageVO<>(); | ||
| 111 | + page.setTotal(0); | ||
| 112 | + page.setPage(1); | ||
| 113 | + page.setPageSize(20); | ||
| 114 | + page.setList(List.of()); | ||
| 115 | + when(userService.getUserList(any(UserListQueryDTO.class), anyString())).thenReturn(page); | ||
| 116 | + | ||
| 117 | + mockMvc.perform(get("/api/usr/users").with(superAdmin())) | ||
| 118 | + .andExpect(status().isOk()) | ||
| 119 | + .andExpect(jsonPath("$.code").value(200)) | ||
| 120 | + .andExpect(jsonPath("$.data.total").value(0)) | ||
| 121 | + .andExpect(jsonPath("$.data.list").isArray()); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @Test | ||
| 125 | + void getUsers_noAuth_returns401() throws Exception { | ||
| 126 | + mockMvc.perform(get("/api/usr/users")) | ||
| 127 | + .andExpect(status().isUnauthorized()); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @Test | ||
| 106 | void getPermissionGroups_returns200() throws Exception { | 131 | void getPermissionGroups_returns200() throws Exception { |
| 107 | PermissionGroupVO g = new PermissionGroupVO(); | 132 | PermissionGroupVO g = new PermissionGroupVO(); |
| 108 | g.setSId("g1"); | 133 | g.setSId("g1"); |
backend/src/test/java/com/example/erp/module/usr/UserServiceTest.java
| 1 | package com.example.erp.module.usr; | 1 | package com.example.erp.module.usr; |
| 2 | 2 | ||
| 3 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 4 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 3 | import com.example.erp.common.exception.BizException; | 5 | import com.example.erp.common.exception.BizException; |
| 6 | +import com.example.erp.common.vo.PageVO; | ||
| 4 | import com.example.erp.config.UserPrincipal; | 7 | import com.example.erp.config.UserPrincipal; |
| 5 | import com.example.erp.module.usr.dto.UserCreateReqDTO; | 8 | import com.example.erp.module.usr.dto.UserCreateReqDTO; |
| 6 | import com.example.erp.module.usr.dto.UserListQueryDTO; | 9 | import com.example.erp.module.usr.dto.UserListQueryDTO; |
| 10 | +import com.example.erp.module.usr.vo.UserListItemVO; | ||
| 7 | import com.example.erp.module.usr.entity.StaffEntity; | 11 | import com.example.erp.module.usr.entity.StaffEntity; |
| 8 | import com.example.erp.module.usr.entity.UsrUserEntity; | 12 | import com.example.erp.module.usr.entity.UsrUserEntity; |
| 9 | import com.example.erp.module.usr.entity.UserPermissionEntity; | 13 | import com.example.erp.module.usr.entity.UserPermissionEntity; |
| @@ -118,6 +122,33 @@ class UserServiceTest { | @@ -118,6 +122,33 @@ class UserServiceTest { | ||
| 118 | } | 122 | } |
| 119 | 123 | ||
| 120 | @Test | 124 | @Test |
| 125 | + void getUserList_capsPageSizeAt100_callsMapper() { | ||
| 126 | + IPage<UserListItemVO> mockPage = new Page<>(1, 100, 5); | ||
| 127 | + when(userMapper.selectUserList(argThat(p -> p.getSize() == 100), eq("b1"), any(), any(), any())) | ||
| 128 | + .thenReturn(mockPage); | ||
| 129 | + | ||
| 130 | + UserListQueryDTO q = new UserListQueryDTO(); | ||
| 131 | + q.setPageSize(200); | ||
| 132 | + PageVO<UserListItemVO> result = userService.getUserList(q, "b1"); | ||
| 133 | + | ||
| 134 | + assertEquals(5L, result.getTotal()); | ||
| 135 | + verify(userMapper).selectUserList(argThat(p -> p.getSize() == 100), eq("b1"), any(), any(), any()); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + @Test | ||
| 139 | + void getUserList_emptyQueryValue_doesNotFilter() { | ||
| 140 | + IPage<UserListItemVO> mockPage = new Page<>(1, 20, 2); | ||
| 141 | + when(userMapper.selectUserList(any(), eq("b1"), eq("username"), eq("contains"), eq(""))) | ||
| 142 | + .thenReturn(mockPage); | ||
| 143 | + | ||
| 144 | + UserListQueryDTO q = new UserListQueryDTO(); | ||
| 145 | + q.setQueryValue(null); | ||
| 146 | + PageVO<UserListItemVO> result = userService.getUserList(q, "b1"); | ||
| 147 | + | ||
| 148 | + assertEquals(2L, result.getTotal()); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + @Test | ||
| 121 | void createUser_invalidEmployeeId_throws40001() { | 152 | void createUser_invalidEmployeeId_throws40001() { |
| 122 | req.setEmployeeId("bad-staff-id"); | 153 | req.setEmployeeId("bad-staff-id"); |
| 123 | when(userMapper.selectCount(any())).thenReturn(0L); | 154 | when(userMapper.selectCount(any())).thenReturn(0L); |