From 453e4f6db8caeb838941f7ce82c2a4edd4aee8ac Mon Sep 17 00:00:00 2001 From: weicw1996 Date: Tue, 20 Oct 2026 12:24:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=BE=AE=E4=BF=A1=E7=99=BB=E5=BD=95):=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=87=8D=E5=A4=8D=E6=B3=A8=E5=86=8C=E4=BA=A7?= =?UTF-8?q?=E7=94=9F=E5=90=8C=E6=89=8B=E6=9C=BA=E5=8F=B7=E5=A4=9A=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=EF=BC=8C=E5=AF=BC=E8=87=B4=E5=AE=A1=E6=A0=B8=E8=AF=AF?= =?UTF-8?q?=E6=8A=A5=E3=80=8C=E6=89=8B=E6=9C=BA=E5=8F=B7=E5=B7=B2=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - loginByMpWxPhone 在手机号/openid 缺失时不再落库建匿名账号(旧逻辑先建无手机号账号再回填,授权失败或并发双击会堆出多个账号) - 登录改为先按 openid 认账号、再按手机号兜底,并补齐缺失的 shop_user_oauth 绑定,避免老账号被当成新用户再建号 - getByOauthId 按 id 升序取最早一条,避免历史重复授权记录抛 TooManyResultsException - 附生产数据修复脚本 fix_duplicate_user_phone_17307866134_2026-10-20.sql(软删同手机号空壳账号 972) --- ...cate_user_phone_17307866134_2026-10-20.sql | 73 +++++++++++++++++++ .../shop/controller/WxLoginController.java | 25 +++++-- .../service/impl/UserOauthServiceImpl.java | 4 +- 3 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 fix_duplicate_user_phone_17307866134_2026-10-20.sql diff --git a/fix_duplicate_user_phone_17307866134_2026-10-20.sql b/fix_duplicate_user_phone_17307866134_2026-10-20.sql new file mode 100644 index 0000000..9e3efba --- /dev/null +++ b/fix_duplicate_user_phone_17307866134_2026-10-20.sql @@ -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 diff --git a/src/main/java/com/gxwebsoft/shop/controller/WxLoginController.java b/src/main/java/com/gxwebsoft/shop/controller/WxLoginController.java index d24acf7..ed1afdd 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/WxLoginController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/WxLoginController.java @@ -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()); diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/UserOauthServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/UserOauthServiceImpl.java index e32cec3..00a8282 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/UserOauthServiceImpl.java +++ b/src/main/java/com/gxwebsoft/shop/service/impl/UserOauthServiceImpl.java @@ -64,7 +64,9 @@ public class UserOauthServiceImpl extends ServiceImpl