diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java index 22cddc8..2f3dc6b 100644 --- a/web-api/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -126,4 +126,18 @@ public interface SysUserMapper extends BaseMapper * @return 结果 */ public SysUser checkEmailUnique(String email); + + /** + * 统计部门下启用中的用户数量(del_flag=0 且 status=0) + * + * @param deptId 部门 id + * @param excludeUserId 排除的用户(修改/启用时排除自身再 +1 槽位) + * @return 数量 + */ + int countNormalUsersByDeptId(@Param("deptId") Long deptId, @Param("excludeUserId") Long excludeUserId); + + /** + * 部门下后台用户数量(含停用,不含已删除) + */ + int countSysUsersByDeptId(@Param("deptId") Long deptId); } diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java index 26c7048..1081756 100644 --- a/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import com.ruoyi.ai.domain.AiGroupBalanceChangeRecord; @@ -29,6 +30,7 @@ import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.spring.SpringUtils; import com.ruoyi.system.mapper.SysDeptMapper; import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysUserMapper; import com.ruoyi.system.service.ISysDeptService; import javax.annotation.Resource; @@ -46,6 +48,10 @@ public class SysDeptServiceImpl implements ISysDeptService @Autowired private SysRoleMapper roleMapper; + + @Autowired + private SysUserMapper userMapper; + @Resource private EncryptionService encryptionService; @@ -236,6 +242,10 @@ public class SysDeptServiceImpl implements ISysDeptService public int insertDept(SysDept dept) { SysDept info = deptMapper.selectDeptById(dept.getParentId()); + if (!isFirstLevelDept(info)) + { + throw new ServiceException("仅允许在一级部门下创建二级部门"); + } // 如果父节点不为正常状态,则不允许新增子节点 if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) { @@ -262,6 +272,12 @@ public class SysDeptServiceImpl implements ISysDeptService { SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNull(oldDept)) + { + throw new ServiceException("部门不存在"); + } + validateDeptUpdateConstraint(dept, oldDept, newParentDept); + validateDeptMaxUserCountFloor(dept, oldDept); if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) { String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); @@ -297,6 +313,23 @@ public class SysDeptServiceImpl implements ISysDeptService deptMapper.updateDeptStatusNormal(deptIds); } + /** + * 账号上限调低时:不能小于当前启用中用户数(max_user_count 为空或≤0 表示不限制) + */ + private void validateDeptMaxUserCountFloor(SysDept incoming, SysDept oldDept) + { + Integer newMax = incoming.getMaxUserCount() != null ? incoming.getMaxUserCount() : oldDept.getMaxUserCount(); + if (newMax == null || newMax <= 0) + { + return; + } + int enabledCnt = userMapper.countNormalUsersByDeptId(incoming.getDeptId(), null); + if (enabledCnt > newMax) + { + throw new ServiceException(String.format("该团队当前启用中账号共 %d 个,账号上限不能小于该数量", enabledCnt)); + } + } + /** * 修改子元素关系 * @@ -439,4 +472,82 @@ public class SysDeptServiceImpl implements ISysDeptService { return getChildList(list, t).size() > 0; } + + /** + * 约束:仅允许创建/更新二级部门;一级部门仅允许修改名称。 + * + * @param newDept 前端提交的新部门参数 + * @param oldDept 数据库中的原部门数据 + * @param newParentDept 修改后的父部门 + */ + private void validateDeptUpdateConstraint(SysDept newDept, SysDept oldDept, SysDept newParentDept) + { + if (isFirstLevelDept(oldDept)) + { + validateFirstLevelDeptOnlyRename(newDept, oldDept); + return; + } + if (isSecondLevelDept(oldDept)) + { + if (!isFirstLevelDept(newParentDept)) + { + throw new ServiceException("仅允许更新二级部门,且其上级必须为一级部门"); + } + return; + } + throw new ServiceException("仅允许更新二级部门,或更新一级部门名称"); + } + + /** + * 一级部门仅允许修改部门名称,其他字段必须保持不变。 + * + * @param newDept 前端提交的新部门参数 + * @param oldDept 数据库中的原部门数据 + */ + private void validateFirstLevelDeptOnlyRename(SysDept newDept, SysDept oldDept) + { + if (!Objects.equals(newDept.getParentId(), oldDept.getParentId()) + || !Objects.equals(newDept.getOrderNum(), oldDept.getOrderNum()) + || !Objects.equals(newDept.getLeader(), oldDept.getLeader()) + || !Objects.equals(newDept.getPhone(), oldDept.getPhone()) + || !Objects.equals(newDept.getEmail(), oldDept.getEmail()) + || !Objects.equals(newDept.getStatus(), oldDept.getStatus()) + || !Objects.equals(newDept.getMaxUserCount(), oldDept.getMaxUserCount()) + || !Objects.equals(newDept.getBalance(), oldDept.getBalance())) + { + throw new ServiceException("一级部门仅允许修改名称"); + } + newDept.setAncestors(oldDept.getAncestors()); + newDept.setByteApiKey(oldDept.getByteApiKey()); + newDept.setProject(oldDept.getProject()); + newDept.setModelParm(oldDept.getModelParm()); + } + + /** + * 判断是否为一级部门(根节点下的直接子部门)。 + * + * @param dept 部门信息 + * @return true-一级部门,false-非一级部门 + */ + private boolean isFirstLevelDept(SysDept dept) + { + return StringUtils.isNotNull(dept) + && Objects.equals(0L, dept.getParentId()) + && "0".equals(dept.getAncestors()); + } + + /** + * 判断是否为二级部门(一级部门的直接子部门)。 + * + * @param dept 部门信息 + * @return true-二级部门,false-非二级部门 + */ + private boolean isSecondLevelDept(SysDept dept) + { + if (StringUtils.isNull(dept) || StringUtils.isBlank(dept.getAncestors()) || StringUtils.isNull(dept.getParentId())) + { + return false; + } + return Objects.equals(2, StringUtils.split(dept.getAncestors(), ",").length); + } } diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java index 9daf5b3..db56d0b 100644 --- a/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import com.ruoyi.common.annotation.DataScope; import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.exception.ServiceException; @@ -65,6 +66,32 @@ public class SysUserServiceImpl implements ISysUserService @Autowired protected Validator validator; + /** + * 校验部门启用账号未超过上限(max_user_count 为空或≤0 表示不限制) + */ + private void assertDeptEnabledUserWithinLimit(Long deptId, Long excludeUserId) + { + if (deptId == null) + { + return; + } + SysDept dept = deptService.selectDeptById(deptId); + if (dept == null) + { + return; + } + Integer max = dept.getMaxUserCount(); + if (max == null || max <= 0) + { + return; + } + int others = userMapper.countNormalUsersByDeptId(deptId, excludeUserId); + if (others + 1 > max) + { + throw new ServiceException(String.format("该团队启用中账号已达上限(%d),请先停用部分账号或联系管理员调高上限", max)); + } + } + /** * 根据条件分页查询用户列表 * @@ -260,6 +287,11 @@ public class SysUserServiceImpl implements ISysUserService @Transactional public int insertUser(SysUser user) { + String st = StringUtils.isEmpty(user.getStatus()) ? UserConstants.NORMAL : user.getStatus(); + if (UserConstants.NORMAL.equals(st)) + { + assertDeptEnabledUserWithinLimit(user.getDeptId(), null); + } // 新增用户信息 int rows = userMapper.insertUser(user); // 新增用户岗位关联 @@ -278,6 +310,11 @@ public class SysUserServiceImpl implements ISysUserService @Override public boolean registerUser(SysUser user) { + String st = StringUtils.isEmpty(user.getStatus()) ? UserConstants.NORMAL : user.getStatus(); + if (UserConstants.NORMAL.equals(st)) + { + assertDeptEnabledUserWithinLimit(user.getDeptId(), null); + } return userMapper.insertUser(user) > 0; } @@ -291,6 +328,13 @@ public class SysUserServiceImpl implements ISysUserService @Transactional public int updateUser(SysUser user) { + SysUser oldUser = userMapper.selectUserById(user.getUserId()); + Long newDeptId = user.getDeptId() != null ? user.getDeptId() : oldUser.getDeptId(); + String newStatus = user.getStatus() != null ? user.getStatus() : oldUser.getStatus(); + if (UserConstants.NORMAL.equals(newStatus)) + { + assertDeptEnabledUserWithinLimit(newDeptId, user.getUserId()); + } Long userId = user.getUserId(); // 删除用户与角色关联 userRoleMapper.deleteUserRoleByUserId(userId); @@ -326,6 +370,12 @@ public class SysUserServiceImpl implements ISysUserService @Override public int updateUserStatus(SysUser user) { + SysUser oldUser = userMapper.selectUserById(user.getUserId()); + String newStatus = user.getStatus() != null ? user.getStatus() : oldUser.getStatus(); + if (UserConstants.NORMAL.equals(newStatus)) + { + assertDeptEnabledUserWithinLimit(oldUser.getDeptId(), user.getUserId()); + } return userMapper.updateUser(user); } @@ -503,6 +553,11 @@ public class SysUserServiceImpl implements ISysUserService { BeanValidators.validateWithException(validator, user); deptService.checkDeptDataScope(user.getDeptId()); + String stAdd = StringUtils.isEmpty(user.getStatus()) ? UserConstants.NORMAL : user.getStatus(); + if (UserConstants.NORMAL.equals(stAdd)) + { + assertDeptEnabledUserWithinLimit(user.getDeptId(), null); + } String password = configService.selectConfigByKey("sys.user.initPassword"); user.setPassword(SecurityUtils.encryptPassword(password)); user.setCreateBy(operName); @@ -516,6 +571,12 @@ public class SysUserServiceImpl implements ISysUserService checkUserAllowed(u); checkUserDataScope(u.getUserId()); deptService.checkDeptDataScope(user.getDeptId()); + Long newDeptId = user.getDeptId() != null ? user.getDeptId() : u.getDeptId(); + String newStatus = user.getStatus() != null ? user.getStatus() : u.getStatus(); + if (UserConstants.NORMAL.equals(newStatus)) + { + assertDeptEnabledUserWithinLimit(newDeptId, u.getUserId()); + } user.setUserId(u.getUserId()); user.setUpdateBy(operName); userMapper.updateUser(user);