diff --git a/ljwx-boot/database/migrations/010_remove_username_unique_constraint.sql b/ljwx-boot/database/migrations/010_remove_username_unique_constraint.sql new file mode 100644 index 0000000..14761d5 --- /dev/null +++ b/ljwx-boot/database/migrations/010_remove_username_unique_constraint.sql @@ -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'; \ No newline at end of file diff --git a/ljwx-boot/database/migrations/010_remove_username_unique_constraint_fixed.sql b/ljwx-boot/database/migrations/010_remove_username_unique_constraint_fixed.sql new file mode 100644 index 0000000..aa5a2d5 --- /dev/null +++ b/ljwx-boot/database/migrations/010_remove_username_unique_constraint_fixed.sql @@ -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; \ No newline at end of file diff --git a/ljwx-boot/ljwx-boot-admin/src/main/java/com/ljwx/admin/controller/system/SysUserController.java b/ljwx-boot/ljwx-boot-admin/src/main/java/com/ljwx/admin/controller/system/SysUserController.java index 25b88b3..f6877fc 100644 --- a/ljwx-boot/ljwx-boot-admin/src/main/java/com/ljwx/admin/controller/system/SysUserController.java +++ b/ljwx-boot/ljwx-boot-admin/src/main/java/com/ljwx/admin/controller/system/SysUserController.java @@ -209,5 +209,25 @@ public class SysUserController { return Result.data(sysUserFacade.batchImportUsers(file, orgIds)); } + @GetMapping("/check_phone") + @Operation(operationId = "11", summary = "检查手机号是否已存在(仅检查未删除用户)") + public Result 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 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); + } + } diff --git a/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/ISysUserService.java b/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/ISysUserService.java index 7031e57..98c6de9 100644 --- a/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/ISysUserService.java +++ b/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/ISysUserService.java @@ -506,4 +506,30 @@ public interface ISysUserService extends IService { * @CreateTime 2025-09-12 */ List getUsersByType(Integer userType, Long orgId, Long customerId); + + /** + * 检查手机号是否已存在(仅检查未删除用户) + *

用于用户创建/更新时的手机号唯一性验证

+ * + * @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); + + /** + * 检查设备序列号是否已存在(仅检查未删除用户) + *

用于用户创建/更新时的设备唯一性验证

+ * + * @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); } diff --git a/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/impl/SysUserServiceImpl.java b/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/impl/SysUserServiceImpl.java index b79ad67..d34b88d 100644 --- a/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/impl/SysUserServiceImpl.java +++ b/ljwx-boot/ljwx-boot-modules/src/main/java/com/ljwx/modules/system/service/impl/SysUserServiceImpl.java @@ -179,11 +179,20 @@ public class SysUserServiceImpl extends ServiceImpl 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() .eq(SysUserOrg::getOrgId, orgUnit.getId())); @@ -191,10 +200,14 @@ public class SysUserServiceImpl extends ServiceImpl 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 impl throw new RuntimeException("保存用户部门关系失败"); } } + + // 根据用户角色设置用户类型和管理级别 + Integer userType = 0; // 默认普通用户 + Integer adminLevel = 0; // 默认非管理员 + + // 获取用户角色信息 + List 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 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 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 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 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 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; + } }