From a75b8d36742b9d287da3694f542e937beb2c1089 Mon Sep 17 00:00:00 2001 From: zichun Date: Thu, 30 Apr 2026 14:27:32 +0800 Subject: [PATCH] feat(usr): user update dto + service happy path REQ-USR-002 --- backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ backend/src/main/java/com/xly/erp/module/usr/service/UserService.java | 3 +++ backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java diff --git a/backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java b/backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java new file mode 100644 index 0000000..4f537d0 --- /dev/null +++ b/backend/src/main/java/com/xly/erp/module/usr/dto/UpdateUserDTO.java @@ -0,0 +1,52 @@ +package com.xly.erp.module.usr.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +import java.util.List; + +public class UpdateUserDTO { + + @JsonProperty("sUserNo") + @NotBlank + @Size(max = 50) + private String sUserNo; + + @JsonProperty("sUserName") + @NotBlank + @Size(max = 50) + private String sUserName; + + @JsonProperty("iStaffId") + private Integer iStaffId; + + @JsonProperty("sUserType") + @NotBlank + private String sUserType; + + @JsonProperty("sLanguage") + @NotBlank + private String sLanguage; + + @JsonProperty("bCanModifyDocs") + private Boolean bCanModifyDocs; + + @JsonProperty("permissionCategoryIds") + private List permissionCategoryIds; + + public String getSUserNo() { return sUserNo; } + public void setSUserNo(String sUserNo) { this.sUserNo = sUserNo; } + public String getSUserName() { return sUserName; } + public void setSUserName(String sUserName) { this.sUserName = sUserName; } + public Integer getIStaffId() { return iStaffId; } + public void setIStaffId(Integer iStaffId) { this.iStaffId = iStaffId; } + public String getSUserType() { return sUserType; } + public void setSUserType(String sUserType) { this.sUserType = sUserType; } + public String getSLanguage() { return sLanguage; } + public void setSLanguage(String sLanguage) { this.sLanguage = sLanguage; } + public Boolean getBCanModifyDocs() { return bCanModifyDocs; } + public void setBCanModifyDocs(Boolean bCanModifyDocs) { this.bCanModifyDocs = bCanModifyDocs; } + public List getPermissionCategoryIds() { return permissionCategoryIds; } + public void setPermissionCategoryIds(List permissionCategoryIds) { this.permissionCategoryIds = permissionCategoryIds; } +} diff --git a/backend/src/main/java/com/xly/erp/module/usr/service/UserService.java b/backend/src/main/java/com/xly/erp/module/usr/service/UserService.java index ec396f7..924a3f3 100644 --- a/backend/src/main/java/com/xly/erp/module/usr/service/UserService.java +++ b/backend/src/main/java/com/xly/erp/module/usr/service/UserService.java @@ -1,9 +1,12 @@ package com.xly.erp.module.usr.service; import com.xly.erp.module.usr.dto.CreateUserDTO; +import com.xly.erp.module.usr.dto.UpdateUserDTO; import java.util.Map; public interface UserService { Map create(CreateUserDTO dto); + + Integer update(Integer id, UpdateUserDTO dto); } diff --git a/backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java b/backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java index b453fc1..b2860d3 100644 --- a/backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java +++ b/backend/src/main/java/com/xly/erp/module/usr/service/impl/UserServiceImpl.java @@ -5,6 +5,7 @@ import com.xly.erp.common.config.TenantProperties; import com.xly.erp.common.exception.BizException; import com.xly.erp.common.security.SecurityContextHelper; import com.xly.erp.module.usr.dto.CreateUserDTO; +import com.xly.erp.module.usr.dto.UpdateUserDTO; import com.xly.erp.module.usr.entity.User; import com.xly.erp.module.usr.entity.UserPermission; import com.xly.erp.module.usr.mapper.PermissionCategoryMapper; @@ -114,4 +115,60 @@ public class UserServiceImpl implements UserService { result.put("sUserNo", entity.getSUserNo()); return result; } + + @Override + public Integer update(Integer id, UpdateUserDTO dto) { + User original = userMapper.selectById(id); + if (original == null || Boolean.TRUE.equals(original.getBDeleted())) { + throw new BizException(40400, "用户不存在或已删除"); + } + if (!USER_TYPES.contains(dto.getSUserType())) { + throw new BizException(40001, "sUserType: 取值非法"); + } + if (!LANGUAGES.contains(dto.getSLanguage())) { + throw new BizException(40001, "sLanguage: 取值非法"); + } + if (dto.getIStaffId() != null && !staffMapper.existsActiveById(dto.getIStaffId())) { + throw new BizException(40022, "职员不存在或已删除"); + } + List ids = dto.getPermissionCategoryIds(); + if (ids != null && !ids.isEmpty()) { + int found = permissionCategoryMapper.countActiveByIds(ids); + if (found != ids.size()) { + throw new BizException(40023, "权限分类含无效 id"); + } + } + + User entity = new User(); + entity.setIIncrement(id); + entity.setSUserNo(dto.getSUserNo()); + entity.setSUserName(dto.getSUserName()); + entity.setIStaffId(dto.getIStaffId()); + entity.setSUserType(dto.getSUserType()); + entity.setSLanguage(dto.getSLanguage()); + entity.setBCanModifyDocs(dto.getBCanModifyDocs() != null ? dto.getBCanModifyDocs() : false); + try { + userMapper.updateById(entity); + } catch (DuplicateKeyException e) { + throw new BizException(40020, "用户号或用户名已存在"); + } + + userPermissionMapper.deleteByUserId(id); + if (ids != null && !ids.isEmpty()) { + String authedUserNo = SecurityContextHelper.currentUserNo(); + String createdBy = authedUserNo != null ? authedUserNo : stub.getStubUserNo(); + LocalDateTime now = LocalDateTime.now(); + for (Integer cid : ids) { + UserPermission rel = new UserPermission(); + rel.setSBrandsId(tenant.getBrandsId()); + rel.setSSubsidiaryId(tenant.getSubsidiaryId()); + rel.setTCreateDate(now); + rel.setIUserId(id); + rel.setICategoryId(cid); + rel.setSCreatedBy(createdBy); + userPermissionMapper.insert(rel); + } + } + return id; + } } diff --git a/backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java b/backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java index ea4f9fa..071b2eb 100644 --- a/backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java +++ b/backend/src/test/java/com/xly/erp/module/usr/service/UserServiceImplTest.java @@ -4,6 +4,7 @@ import com.xly.erp.common.config.StubSecurityProperties; import com.xly.erp.common.config.TenantProperties; import com.xly.erp.common.exception.BizException; import com.xly.erp.module.usr.dto.CreateUserDTO; +import com.xly.erp.module.usr.dto.UpdateUserDTO; import com.xly.erp.module.usr.entity.User; import com.xly.erp.module.usr.entity.UserPermission; import com.xly.erp.module.usr.mapper.PermissionCategoryMapper; @@ -180,6 +181,92 @@ class UserServiceImplTest { assertThat(cap.getValue().getSCreatedBy()).isEqualTo("ALICE"); } + @Test + void updateWithValidDto_invokesUpdateById_andRebuildsPermissions() { + when(userMapper.selectById(10)).thenReturn(stubExistingUser(10)); + when(staffMapper.existsActiveById(7)).thenReturn(true); + when(permissionCategoryMapper.countActiveByIds(List.of(11, 22))).thenReturn(2); + when(userMapper.updateById(any(User.class))).thenReturn(1); + when(userPermissionMapper.deleteByUserId(10)).thenReturn(3); + + UpdateUserDTO dto = baseUpdateDto(); + dto.setIStaffId(7); + dto.setPermissionCategoryIds(List.of(11, 22)); + + Integer result = service.update(10, dto); + + assertThat(result).isEqualTo(10); + ArgumentCaptor uc = ArgumentCaptor.forClass(User.class); + verify(userMapper).updateById(uc.capture()); + User passed = uc.getValue(); + assertThat(passed.getIIncrement()).isEqualTo(10); + assertThat(passed.getSUserNo()).isEqualTo("u_new"); + assertThat(passed.getSPasswordHash()).isNull(); + assertThat(passed.getSCreatedBy()).isNull(); + assertThat(passed.getTCreateDate()).isNull(); + assertThat(passed.getSBrandsId()).isNull(); + + verify(userPermissionMapper, times(1)).deleteByUserId(10); + verify(userPermissionMapper, times(2)).insert(any(UserPermission.class)); + } + + @Test + void updateWithTargetNotFound_throws40400() { + when(userMapper.selectById(99)).thenReturn(null); + assertThatThrownBy(() -> service.update(99, baseUpdateDto())) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 40400); + verify(userMapper, never()).updateById(any(User.class)); + } + + @Test + void updateWithTargetAlreadyDeleted_throws40400() { + User deleted = stubExistingUser(10); + deleted.setBDeleted(true); + when(userMapper.selectById(10)).thenReturn(deleted); + assertThatThrownBy(() -> service.update(10, baseUpdateDto())) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 40400); + verify(userMapper, never()).updateById(any(User.class)); + } + + @Test + void updateWithBCanModifyDocsNull_setsFalseInEntity() { + when(userMapper.selectById(10)).thenReturn(stubExistingUser(10)); + when(userMapper.updateById(any(User.class))).thenReturn(1); + UpdateUserDTO dto = baseUpdateDto(); + dto.setBCanModifyDocs(null); + + service.update(10, dto); + + ArgumentCaptor uc = ArgumentCaptor.forClass(User.class); + verify(userMapper).updateById(uc.capture()); + assertThat(uc.getValue().getBCanModifyDocs()).isFalse(); + } + + private UpdateUserDTO baseUpdateDto() { + UpdateUserDTO dto = new UpdateUserDTO(); + dto.setSUserNo("u_new"); + dto.setSUserName("用户新"); + dto.setSUserType("普通用户"); + dto.setSLanguage("zh"); + dto.setBCanModifyDocs(false); + return dto; + } + + private User stubExistingUser(Integer id) { + User u = new User(); + u.setIIncrement(id); + u.setSUserNo("u_orig"); + u.setSUserName("原用户"); + u.setSUserType("普通用户"); + u.setSLanguage("zh"); + u.setSPasswordHash("$2a$10$origHash"); + u.setSCreatedBy("ORIG_USER"); + u.setBDeleted(false); + return u; + } + private CreateUserDTO baseDto() { CreateUserDTO dto = new CreateUserDTO(); dto.setSUserNo("u001"); -- libgit2 0.22.2