feat: 完善用户管理系统字段维护和唯一性验证
• 移除用户名唯一约束,允许员工重名 • 新增手机号和设备序列号唯一性验证(前端+后端) • 优化用户操作抽屉UI,更紧凑美观的设计 • 修复新增/修改用户时字段维护逻辑: - org_id, org_name, customer_id 从 sys_org_units 动态获取 - user_type 根据 sys_role.is_admin 动态计算 - admin_level 根据 sys_org_units.level 动态设置 • 员工卡号调整到工作信息栏,布局更合理 • 添加数据库迁移脚本移除用户名唯一约束 • 增强用户创建/编辑的数据完整性和一致性 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
-- 移除 sys_user 表的 user_name 唯一约束
|
||||
-- 原因: 员工名字可以重复,应该通过用户ID区分,而不是用户名
|
||||
-- 作者: bruno.gao
|
||||
-- 时间: 2025-09-22
|
||||
|
||||
-- 检查并删除 user_name 唯一约束
|
||||
-- 注意:不同数据库可能有不同的约束名称
|
||||
|
||||
-- 方法1: 如果约束名为 idx_user_name_unique
|
||||
ALTER TABLE sys_user DROP INDEX IF EXISTS idx_user_name_unique;
|
||||
|
||||
-- 方法2: 如果约束名为 UK_user_name
|
||||
ALTER TABLE sys_user DROP INDEX IF EXISTS UK_user_name;
|
||||
|
||||
-- 方法3: 如果约束名为 ux_sys_user_user_name
|
||||
ALTER TABLE sys_user DROP INDEX IF EXISTS ux_sys_user_user_name;
|
||||
|
||||
-- 方法4: 通用方法 - 查看所有约束后再删除
|
||||
-- 可以先运行以下查询查看所有约束:
|
||||
-- SHOW INDEX FROM sys_user WHERE Column_name = 'user_name';
|
||||
|
||||
-- 添加注释说明
|
||||
ALTER TABLE sys_user COMMENT = '用户表 - user_name字段不再强制唯一,员工可以重名,通过ID区分';
|
||||
|
||||
-- 验证约束删除是否成功的查询
|
||||
-- SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
|
||||
-- WHERE TABLE_NAME = 'sys_user' AND CONSTRAINT_TYPE = 'UNIQUE';
|
||||
@@ -0,0 +1,13 @@
|
||||
-- 移除 sys_user 表的 user_name 唯一约束
|
||||
-- 原因: 员工名字可以重复,应该通过用户ID区分,而不是用户名
|
||||
-- 作者: bruno.gao
|
||||
-- 时间: 2025-09-22
|
||||
|
||||
-- 删除 user_name 唯一约束索引
|
||||
DROP INDEX idx_user_name_unique ON sys_user;
|
||||
|
||||
-- 添加注释说明
|
||||
ALTER TABLE sys_user COMMENT = '用户表 - user_name字段不再强制唯一,员工可以重名,通过ID区分';
|
||||
|
||||
-- 验证约束删除是否成功
|
||||
SELECT 'Username unique constraint removed successfully' as result;
|
||||
+20
@@ -209,5 +209,25 @@ public class SysUserController {
|
||||
return Result.data(sysUserFacade.batchImportUsers(file, orgIds));
|
||||
}
|
||||
|
||||
@GetMapping("/check_phone")
|
||||
@Operation(operationId = "11", summary = "检查手机号是否已存在(仅检查未删除用户)")
|
||||
public Result<Boolean> checkPhoneExists(
|
||||
@Parameter(description = "手机号") @RequestParam("phone") String phone,
|
||||
@Parameter(description = "排除的用户ID", required = false) @RequestParam(value = "excludeUserId", required = false) Long excludeUserId,
|
||||
@Parameter(description = "是否删除标识(0-未删除,1-已删除)", required = false) @RequestParam(value = "isDeleted", defaultValue = "0") Integer isDeleted) {
|
||||
boolean exists = sysUserService.checkPhoneExists(phone, excludeUserId, isDeleted);
|
||||
return Result.data(exists);
|
||||
}
|
||||
|
||||
@GetMapping("/check_device_sn")
|
||||
@Operation(operationId = "12", summary = "检查设备序列号是否已存在(仅检查未删除用户)")
|
||||
public Result<Boolean> checkDeviceSnExists(
|
||||
@Parameter(description = "设备序列号") @RequestParam("deviceSn") String deviceSn,
|
||||
@Parameter(description = "排除的用户ID", required = false) @RequestParam(value = "excludeUserId", required = false) Long excludeUserId,
|
||||
@Parameter(description = "是否删除标识(0-未删除,1-已删除)", required = false) @RequestParam(value = "isDeleted", defaultValue = "0") Integer isDeleted) {
|
||||
boolean exists = sysUserService.checkDeviceSnExists(deviceSn, excludeUserId, isDeleted);
|
||||
return Result.data(exists);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+26
@@ -506,4 +506,30 @@ public interface ISysUserService extends IService<SysUser> {
|
||||
* @CreateTime 2025-09-12
|
||||
*/
|
||||
List<SysUser> getUsersByType(Integer userType, Long orgId, Long customerId);
|
||||
|
||||
/**
|
||||
* 检查手机号是否已存在(仅检查未删除用户)
|
||||
* <p>用于用户创建/更新时的手机号唯一性验证</p>
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @param excludeUserId 排除的用户ID(编辑时用)
|
||||
* @param isDeleted 是否删除标识(0-未删除,1-已删除)
|
||||
* @return true-已存在,false-不存在
|
||||
* @author bruno.gao
|
||||
* @CreateTime 2025-09-22
|
||||
*/
|
||||
boolean checkPhoneExists(String phone, Long excludeUserId, Integer isDeleted);
|
||||
|
||||
/**
|
||||
* 检查设备序列号是否已存在(仅检查未删除用户)
|
||||
* <p>用于用户创建/更新时的设备唯一性验证</p>
|
||||
*
|
||||
* @param deviceSn 设备序列号
|
||||
* @param excludeUserId 排除的用户ID(编辑时用)
|
||||
* @param isDeleted 是否删除标识(0-未删除,1-已删除)
|
||||
* @return true-已存在,false-不存在
|
||||
* @author bruno.gao
|
||||
* @CreateTime 2025-09-22
|
||||
*/
|
||||
boolean checkDeviceSnExists(String deviceSn, Long excludeUserId, Integer isDeleted);
|
||||
}
|
||||
|
||||
+147
-6
@@ -179,11 +179,20 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
String password = DigestUtils.sha256Hex(sha256HexPwd + sysUserBO.getSalt());
|
||||
sysUserBO.setPassword(password);
|
||||
|
||||
// 生成用户卡号
|
||||
// 设置组织信息
|
||||
SysOrgUnits orgUnit = null;
|
||||
if (ObjectUtils.isNotEmpty(sysUserBO.getOrgIds())) {
|
||||
// 获取部门code
|
||||
SysOrgUnits orgUnit = sysOrgUnitsService.getById(sysUserBO.getOrgIds().get(0));
|
||||
// 获取第一个组织作为主要组织
|
||||
orgUnit = sysOrgUnitsService.getById(sysUserBO.getOrgIds().get(0));
|
||||
if (orgUnit != null) {
|
||||
// 设置组织信息(冗余字段,优化查询性能)
|
||||
sysUserBO.setOrgId(orgUnit.getId());
|
||||
sysUserBO.setOrgName(orgUnit.getName());
|
||||
sysUserBO.setCustomerId(orgUnit.getCustomerId());
|
||||
|
||||
log.info("✅ 新增用户设置组织信息: orgId={}, orgName={}, customerId={}",
|
||||
orgUnit.getId(), orgUnit.getName(), orgUnit.getCustomerId());
|
||||
|
||||
// 查询该部门现有用户数
|
||||
Long userCount = sysUserOrgService.count(new LambdaQueryWrapper<SysUserOrg>()
|
||||
.eq(SysUserOrg::getOrgId, orgUnit.getId()));
|
||||
@@ -191,10 +200,14 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
// 生成卡号:部门code-序号
|
||||
String cardNumber = orgUnit.getCode() + "-" + String.format("%03d", userCount + 1);
|
||||
sysUserBO.setUserCardNumber(cardNumber);
|
||||
} else {
|
||||
log.warn("⚠️ 未找到组织信息: orgId={}", sysUserBO.getOrgIds().get(0));
|
||||
}
|
||||
} else {
|
||||
log.warn("⚠️ 新增用户时未指定组织ID");
|
||||
}
|
||||
|
||||
// 保存用户基本信息
|
||||
// 保存用户基本信息(先保存,获取用户ID)
|
||||
boolean saved = super.save(sysUserBO);
|
||||
if (!saved) {
|
||||
throw new RuntimeException("保存用户基本信息失败");
|
||||
@@ -215,6 +228,50 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
throw new RuntimeException("保存用户部门关系失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 根据用户角色设置用户类型和管理级别
|
||||
Integer userType = 0; // 默认普通用户
|
||||
Integer adminLevel = 0; // 默认非管理员
|
||||
|
||||
// 获取用户角色信息
|
||||
List<SysRoleBO> userRoles = sysRoleService.queryRoleListWithUserId(sysUserBO.getId());
|
||||
if (ObjectUtils.isNotEmpty(userRoles)) {
|
||||
// 检查是否有管理员角色
|
||||
boolean hasAdminRole = userRoles.stream()
|
||||
.anyMatch(role -> role.getIsAdmin() != null && role.getIsAdmin() == 1);
|
||||
|
||||
if (hasAdminRole) {
|
||||
userType = 1; // 管理员
|
||||
|
||||
// 根据组织层级设置管理级别
|
||||
if (orgUnit != null && orgUnit.getLevel() != null) {
|
||||
switch (orgUnit.getLevel()) {
|
||||
case 1: // 租户级
|
||||
adminLevel = 2; // 租户级管理员
|
||||
break;
|
||||
case 2: // 部门级
|
||||
adminLevel = 1; // 部门级管理员
|
||||
break;
|
||||
default:
|
||||
adminLevel = 1; // 默认部门级管理员
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
adminLevel = 1; // 默认部门级管理员
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户类型和管理级别
|
||||
sysUserBO.setUserType(userType);
|
||||
sysUserBO.setAdminLevel(adminLevel);
|
||||
boolean typeUpdated = super.updateById(sysUserBO);
|
||||
if (!typeUpdated) {
|
||||
log.warn("⚠️ 更新用户类型和管理级别失败");
|
||||
}
|
||||
|
||||
log.info("✅ 新增用户设置用户类型: userType={}, adminLevel={}, 角色数量={}",
|
||||
userType, adminLevel, userRoles.size());
|
||||
|
||||
// 处理设备绑定
|
||||
if (StringUtils.hasText(sysUserBO.getDeviceSn())) {
|
||||
@@ -280,17 +337,53 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
Long primaryOrgId = sysUserBO.getOrgIds().get(0); // 取第一个作为主要组织
|
||||
SysOrgUnits primaryOrg = sysOrgUnitsService.getById(primaryOrgId);
|
||||
if (primaryOrg != null) {
|
||||
// 根据用户角色和组织层级重新计算user_type和admin_level
|
||||
Integer userType = 0; // 默认普通用户
|
||||
Integer adminLevel = 0; // 默认非管理员
|
||||
|
||||
// 获取用户角色信息
|
||||
List<SysRoleBO> userRoles = sysRoleService.queryRoleListWithUserId(sysUserBO.getId());
|
||||
if (ObjectUtils.isNotEmpty(userRoles)) {
|
||||
// 检查是否有管理员角色
|
||||
boolean hasAdminRole = userRoles.stream()
|
||||
.anyMatch(role -> role.getIsAdmin() != null && role.getIsAdmin() == 1);
|
||||
|
||||
if (hasAdminRole) {
|
||||
userType = 1; // 管理员
|
||||
|
||||
// 根据组织层级设置管理级别
|
||||
if (primaryOrg.getLevel() != null) {
|
||||
switch (primaryOrg.getLevel()) {
|
||||
case 1: // 租户级
|
||||
adminLevel = 2; // 租户级管理员
|
||||
break;
|
||||
case 2: // 部门级
|
||||
adminLevel = 1; // 部门级管理员
|
||||
break;
|
||||
default:
|
||||
adminLevel = 1; // 默认部门级管理员
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
adminLevel = 1; // 默认部门级管理员
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SysUser userToUpdate = new SysUser();
|
||||
userToUpdate.setId(sysUserBO.getId());
|
||||
userToUpdate.setOrgId(primaryOrgId);
|
||||
userToUpdate.setOrgName(primaryOrg.getName());
|
||||
userToUpdate.setCustomerId(primaryOrg.getCustomerId()); // 同时更新租户ID
|
||||
userToUpdate.setUserType(userType); // 更新用户类型
|
||||
userToUpdate.setAdminLevel(adminLevel); // 更新管理级别
|
||||
|
||||
boolean orgInfoUpdated = super.updateById(userToUpdate);
|
||||
if (!orgInfoUpdated) {
|
||||
log.warn("⚠️ 同步更新用户组织信息失败: userId={}, orgId={}", sysUserBO.getId(), primaryOrgId);
|
||||
} else {
|
||||
log.info("✅ 已同步更新用户组织信息: userId={}, orgId={}, orgName={}, customerId={}",
|
||||
sysUserBO.getId(), primaryOrgId, primaryOrg.getName(), primaryOrg.getCustomerId());
|
||||
log.info("✅ 已同步更新用户信息: userId={}, orgId={}, orgName={}, customerId={}, userType={}, adminLevel={}",
|
||||
sysUserBO.getId(), primaryOrgId, primaryOrg.getName(), primaryOrg.getCustomerId(), userType, adminLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1698,4 +1791,52 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
|
||||
return this.list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPhoneExists(String phone, Long excludeUserId, Integer isDeleted) {
|
||||
log.info("🔍 检查手机号唯一性,phone: {}, excludeUserId: {}, isDeleted: {}", phone, excludeUserId, isDeleted);
|
||||
|
||||
if (phone == null || phone.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SysUser::getPhone, phone)
|
||||
.eq(SysUser::getDeleted, isDeleted); // 只检查指定删除状态的用户
|
||||
|
||||
// 排除当前用户(编辑时用)
|
||||
if (excludeUserId != null) {
|
||||
queryWrapper.ne(SysUser::getId, excludeUserId);
|
||||
}
|
||||
|
||||
long count = this.count(queryWrapper);
|
||||
boolean exists = count > 0;
|
||||
|
||||
log.info("📱 手机号 {} 检查结果: {}", phone, exists ? "已存在" : "可用");
|
||||
return exists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkDeviceSnExists(String deviceSn, Long excludeUserId, Integer isDeleted) {
|
||||
log.info("🔍 检查设备序列号唯一性,deviceSn: {}, excludeUserId: {}, isDeleted: {}", deviceSn, excludeUserId, isDeleted);
|
||||
|
||||
if (deviceSn == null || deviceSn.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SysUser::getDeviceSn, deviceSn)
|
||||
.eq(SysUser::getDeleted, isDeleted); // 只检查指定删除状态的用户
|
||||
|
||||
// 排除当前用户(编辑时用)
|
||||
if (excludeUserId != null) {
|
||||
queryWrapper.ne(SysUser::getId, excludeUserId);
|
||||
}
|
||||
|
||||
long count = this.count(queryWrapper);
|
||||
boolean exists = count > 0;
|
||||
|
||||
log.info("📱 设备序列号 {} 检查结果: {}", deviceSn, exists ? "已存在" : "可用");
|
||||
return exists;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user