在支持手机号登录后,手机号成为了用户登录的重要凭证。但经过检查发现,虽然**用户添加/修改**功能已有手机号唯一性校验,但在**用户导入**和**用户同步**功能中缺少该校验,可能导致手机号重复,造成登录混乱。
文件: ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
方法: importUser
// 新增用户时
if (StringUtils.isNotEmpty(user.getPhonenumber()))
{
SysUser phoneCheck = userMapper.checkPhoneUnique(user.getPhonenumber());
if (StringUtils.isNotNull(phoneCheck))
{
failureNum++;
failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName()
+ " 导入失败:手机号码 " + user.getPhonenumber()
+ " 已被用户 " + phoneCheck.getUserName() + " 使用");
continue;
}
}
// 更新用户时
if (StringUtils.isNotEmpty(user.getPhonenumber()))
{
SysUser phoneCheck = userMapper.checkPhoneUnique(user.getPhonenumber());
if (StringUtils.isNotNull(phoneCheck) && !phoneCheck.getUserId().equals(u.getUserId()))
{
failureNum++;
failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName()
+ " 更新失败:手机号码 " + user.getPhonenumber()
+ " 已被用户 " + phoneCheck.getUserName() + " 使用");
continue;
}
}
文件: ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserSyncServiceImpl.java
createNewUser)增强点:
- ✅ 创建前校验手机号唯一性
- ✅ 如手机号重复,记录警告日志并跳过创建
- ✅ 避免因手机号冲突导致同步失败
if (StringUtils.isNotEmpty(dto.getPhonenumber()))
{
// 校验手机号是否已被其他用户使用
SysUser phoneCheck = sysUserMapper.checkPhoneUnique(dto.getPhonenumber());
if (StringUtils.isNotNull(phoneCheck))
{
log.warn("创建用户失败,手机号 {} 已被用户 {} 使用,跳过用户 {}",
dto.getPhonenumber(), phoneCheck.getUserName(), dto.getUserName());
return; // 跳过创建
}
newUser.setPhonenumber(dto.getPhonenumber());
}
updateExistingUser)增强点:
- ✅ 更新前校验手机号唯一性(排除自己)
- ✅ 如手机号被占用,记录警告日志并跳过手机号更新
- ✅ 继续更新其他字段,不影响整体同步流程
if (StringUtils.isNotEmpty(dto.getPhonenumber()))
{
// 校验手机号是否已被其他用户使用(排除自己)
SysUser phoneCheck = sysUserMapper.checkPhoneUnique(dto.getPhonenumber());
if (StringUtils.isNotNull(phoneCheck) && !phoneCheck.getUserId().equals(existingUser.getUserId()))
{
log.warn("更新用户 {} 失败,手机号 {} 已被用户 {} 使用,跳过手机号更新",
existingUser.getUserName(), dto.getPhonenumber(), phoneCheck.getUserName());
}
else
{
existingUser.setPhonenumber(dto.getPhonenumber());
}
}
| 功能模块 | 原状态 | 完善后状态 | 说明 |
|---|---|---|---|
| 用户添加 | ✅ 已有校验 | ✅ 保持 | Controller层已实现 |
| 用户修改 | ✅ 已有校验 | ✅ 保持 | Controller层已实现 |
| 用户导入 | ❌ 缺少校验 | ✅ 已完善 | 新增+更新均校验 |
| 用户同步(新增) | ❌ 缺少校验 | ✅ 已完善 | 创建前校验 |
| 用户同步(更新) | ❌ 缺少校验 | ✅ 已完善 | 更新前校验 |
检查手机号 → 已存在? → 是 → 拒绝创建,记录错误
→ 否 → 允许创建
检查手机号 → 已存在? → 是 → 是自己? → 是 → 允许更新
→ 否 → 拒绝更新,记录错误
→ 否 → 允许更新
| # | 文件 | 修改内容 | 行数变化 |
|---|---|---|---|
| 1 | SysUserServiceImpl.java |
用户导入增加手机号/邮箱唯一性校验 | +50 |
| 2 | UserSyncServiceImpl.java |
用户同步增加手机号唯一性校验 | +19 |
总计: +69 行
很抱歉,导入失败!共 2 条数据格式不正确,错误如下:
1、账号 zhangsan 导入失败:手机号码 13800138000 已被用户 lisi 使用
2、账号 wangwu 导入失败:邮箱 test@example.com 已被用户 zhaoliu 使用
2025-10-23 10:30:45 WARN 创建用户失败,手机号 13800138000 已被用户 lisi 使用,跳过用户 zhangsan
2025-10-23 10:30:46 WARN 更新用户 wangwu 失败,手机号 13900139000 已被用户 zhaoliu 使用,跳过手机号更新
输入: 用户名=test001, 手机号=13800138000(已被admin使用)
预期: 导入失败,提示"手机号码 13800138000 已被用户 admin 使用"
输入: 用户名=test001, 手机号=13900139999(未使用)
预期: 导入成功
输入: 用户名=test001(已存在), 手机号=13800138000(被admin使用)
预期: 更新失败,提示"手机号码 13800138000 已被用户 admin 使用"
输入: 用户名=test001(已存在,手机号=13900139999), 手机号=13900139999
预期: 更新成功
输入: OA用户(手机号=13800138000,已被系统用户使用)
预期: 跳过创建,记录警告日志
输入: 更新用户手机号为已被占用的号码
预期: 跳过手机号更新,其他字段正常更新,记录警告日志
虽然代码层面已完善校验,但建议在数据库层面也添加约束:
-- 为手机号字段添加唯一索引
CREATE UNIQUE INDEX idx_sys_user_phonenumber
ON sys_user(phonenumber)
WHERE phonenumber IS NOT NULL AND phonenumber != '' AND del_flag = '0';
注意:
- 仅对非空、非空字符串、未删除的记录建立唯一约束
- 允许多条记录的手机号为NULL或空字符串
通过本次完善:
1. ✅ 全面覆盖了所有用户创建/更新入口的手机号唯一性校验
2. ✅ 避免了手机号重复导致的登录混乱
3. ✅ 提升了数据一致性和系统安全性
4. ✅ 提供了清晰的错误提示和日志记录
5. ✅ 采用了合理的容错策略,保证自动化同步的稳定性
现在系统可以安全地使用手机号作为登录凭证!🎊