fix(微信登录): 修复重复注册产生同手机号多账号,导致审核误报「手机号已存在」
- loginByMpWxPhone 在手机号/openid 缺失时不再落库建匿名账号(旧逻辑先建无手机号账号再回填,授权失败或并发双击会堆出多个账号) - 登录改为先按 openid 认账号、再按手机号兜底,并补齐缺失的 shop_user_oauth 绑定,避免老账号被当成新用户再建号 - getByOauthId 按 id 升序取最早一条,避免历史重复授权记录抛 TooManyResultsException - 附生产数据修复脚本 fix_duplicate_user_phone_17307866134_2026-10-20.sql(软删同手机号空壳账号 972)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
-- ============================================================================
|
||||
-- 用途:清理手机号 17307866134 的重复用户(同一微信 openid 的并发注册副本),
|
||||
-- 解除后台"认证审核通过"时报"手机号已存在"的拦截。
|
||||
-- 日期:2026-10-20
|
||||
-- 背景:
|
||||
-- 小程序登录 /wx-login/loginByMpWxPhone 是"先按手机号查、查不到再注册"的逻辑,
|
||||
-- 且注册分支只校验 username(addUser 里 phone 为空,saveUser 拦不住)。
|
||||
-- 2026-10-11 17:20:33 同一 openid 的两个并发请求各自走完"查不到"→插入,
|
||||
-- 于是在 sys_user 生成两条同 openid、同手机号的账号:
|
||||
-- user_id=971 闭炎丽 auth_status=1(待审核) 已传身份证 有 yxe_visit_list 记录
|
||||
-- user_id=972 (空壳) auth_status=0(未提交) 无任何业务数据
|
||||
-- shop_user_oauth 同样生成两条 oauth_id 相同的记录。
|
||||
-- 后台审核"通过"调用 PUT /api/system/user → UserServiceImpl.updateUser,
|
||||
-- 手机号唯一性校验 ne(user_id, 971) 命中 972 → 抛"手机号已存在",审核永远失败。
|
||||
-- 另外 getByPhone 用 limit 1,可能随机取到空壳 972,导致该用户登录后仍是未认证状态。
|
||||
-- 影响行数:sys_user 1 行、shop_user_oauth 1 行(均为软删除)
|
||||
-- 是否幂等:是,带 WHERE 限定,可重复执行
|
||||
-- 配套代码改动:无(本次仅数据清理);接口幂等改造与唯一索引见后续 issue
|
||||
--
|
||||
-- 说明:972 无订单/余额/积分/任务/填报/地址/子级等任何引用,软删不影响业务;
|
||||
-- 971 保留全部资料与业务数据,软删后即成为该手机号的唯一有效账号。
|
||||
-- ============================================================================
|
||||
|
||||
-- ---------- 1. 执行前核对 ----------
|
||||
SELECT user_id, username, phone, open_id, auth_status, real_name, deleted, create_time
|
||||
FROM sys_user
|
||||
WHERE user_id IN (971, 972);
|
||||
|
||||
SELECT id, user_id, oauth_type, oauth_id, deleted, create_time
|
||||
FROM shop_user_oauth
|
||||
WHERE user_id IN (971, 972) OR oauth_id = 'ocS6D110zS65-AdOafsUz7QnkiMM';
|
||||
|
||||
-- 期望:971 为有资料、待审核的有效账号;972 为同 openid/同手机号的空壳副本
|
||||
|
||||
-- ---------- 2. 变更 ----------
|
||||
-- 2.1 软删除空壳用户 972
|
||||
UPDATE sys_user
|
||||
SET deleted = 1,
|
||||
update_time = NOW()
|
||||
WHERE user_id = 972
|
||||
AND deleted = 0
|
||||
AND phone = '17307866134';
|
||||
|
||||
-- 2.2 软删除 972 对应的第三方授权记录,避免 shop_user_oauth 同 oauth_id 两条
|
||||
UPDATE shop_user_oauth
|
||||
SET deleted = 1,
|
||||
update_time = NOW()
|
||||
WHERE user_id = 972
|
||||
AND oauth_id = 'ocS6D110zS65-AdOafsUz7QnkiMM'
|
||||
AND deleted = 0;
|
||||
|
||||
-- ---------- 3. 执行后核对 ----------
|
||||
-- 3.1 该手机号的有效账号应只剩 971 一条
|
||||
SELECT user_id, username, phone, auth_status, deleted
|
||||
FROM sys_user
|
||||
WHERE phone = '17307866134'
|
||||
AND deleted = 0;
|
||||
-- 期望:1 行,user_id=971
|
||||
|
||||
-- 3.2 该 openid 的有效授权记录应只剩 1 条
|
||||
SELECT id, user_id, oauth_id, deleted
|
||||
FROM shop_user_oauth
|
||||
WHERE oauth_id = 'ocS6D110zS65-AdOafsUz7QnkiMM'
|
||||
AND deleted = 0;
|
||||
-- 期望:1 行,user_id=971
|
||||
|
||||
-- 3.3 模拟 updateUser 的手机号校验:结果应为 0,即审核可正常提交
|
||||
SELECT COUNT(*) AS conflict_count
|
||||
FROM sys_user
|
||||
WHERE phone = '17307866134'
|
||||
AND user_id <> 971
|
||||
AND deleted = 0;
|
||||
-- 期望:0
|
||||
@@ -9,6 +9,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
|
||||
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
|
||||
import cn.binarywang.wx.miniapp.util.WxMaConfigHolder;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
@@ -81,7 +82,7 @@ public class WxLoginController extends BaseController {
|
||||
// 1.获取openid
|
||||
JSONObject result = getOpenIdByCode(userParam);
|
||||
String openid = result.getString("openid");
|
||||
if (openid == null) {
|
||||
if (StrUtil.isBlank(openid)) {
|
||||
return fail("获取openid失败", null);
|
||||
}
|
||||
// 2.通过openid查询用户是否已存在
|
||||
@@ -112,12 +113,19 @@ public class WxLoginController extends BaseController {
|
||||
JSONObject result = getOpenIdByCode(userParam);
|
||||
String openid = result.getString("openid");
|
||||
String phone = getPhoneByCode(userParam);
|
||||
System.out.println("phone:" + phone);
|
||||
// 拿不到手机号/ openid 时不再落库:旧逻辑先建一条无手机号的账号再回填,
|
||||
// 手机号授权失败,或在页面上并发点两次,都会堆出同一 openid 的多个账号
|
||||
// (生产已出现同一 openid 13 个账号、同一手机号 2 个账号,审核时互撞"手机号已存在")
|
||||
if (StrUtil.isBlank(openid) || StrUtil.isBlank(phone)) {
|
||||
return fail("获取微信手机号失败,请重试", null);
|
||||
}
|
||||
|
||||
// 查询是否存在
|
||||
User user = userService.getByPhone(phone);
|
||||
System.out.println("用户:" + user);
|
||||
// 不存在
|
||||
// 先按 openid 认账号(同一微信换手机号也能回到原账号),再按手机号兜底
|
||||
User user = userOauthService.getByOauthId(openid);
|
||||
if (user == null) {
|
||||
user = userService.getByPhone(phone);
|
||||
}
|
||||
// 不存在则注册
|
||||
if (user == null) {
|
||||
user = addUser(userParam);
|
||||
user.setPhone(phone);
|
||||
@@ -126,7 +134,10 @@ public class WxLoginController extends BaseController {
|
||||
user.setParentId(userParam.getParentId());
|
||||
}
|
||||
userService.updateById(user);
|
||||
// 添加第三方用户信息
|
||||
}
|
||||
// 补齐第三方绑定:老账号可能是后台或手机号注册的,还没有 openid 授权记录,
|
||||
// 缺这条记录会让 /getOpenId 判成新用户又建一个账号
|
||||
if (userOauthService.getByOauthId(openid) == null) {
|
||||
UserOauth uo = new UserOauth();
|
||||
uo.setUserId(user.getUserId());
|
||||
uo.setTenantId(user.getTenantId());
|
||||
|
||||
@@ -64,7 +64,9 @@ public class UserOauthServiceImpl extends ServiceImpl<UserOauthMapper, UserOauth
|
||||
|
||||
@Override
|
||||
public User getByOauthId(String openid) {
|
||||
UserOauth userOauth = query().eq("oauth_id", openid).one();
|
||||
// 历史上并发注册给同一 openid 留下过多条授权记录,用 one() 会抛 TooManyResultsException,
|
||||
// 这里按 id 升序取最早的一条,保证登录链路不再 500
|
||||
UserOauth userOauth = query().eq("oauth_id", openid).orderByAsc("id").last("limit 1").one();
|
||||
if(userOauth == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user