Commit 93a189675f0c8c9b1fa1f8100ad79ce3cffb3c0f

Authored by zichun
1 parent 32dcaa25

feat(usr): selectUserList XML + getUserList service + GET /api/usr/users REQ-USR-003

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);