Ver código fonte

手机号登录/注册限定会员账号;同号多账号明确提示

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
qmj 1 dia atrás
pai
commit
e6e8876212

+ 20 - 3
ruoyi-admin/src/main/java/com/ruoyi/app/user/InfoUserController.java

@@ -922,6 +922,23 @@ public class InfoUserController extends BaseController {
     }
 
 
+    /**
+     * 手机号登录/注册查会员账号。不能用 getOne:info_user 被会员/商家/骑手共用,
+     * 同手机号可能命中多行(getOne 多行会抛底层异常导致登录接口 500),
+     * 且必须限定会员类型,防止会员端登上 phone 字段同号的商家/骑手账号。
+     */
+    private InfoUser findPhoneLoginUser(String phone) {
+        List<InfoUser> matches = infoUserService.list(new LambdaQueryWrapper<InfoUser>()
+                .eq(InfoUser::getPhone, phone)
+                .eq(InfoUser::getDelFlag, "0")
+                .eq(InfoUser::getUserType, "0"));
+        if (matches.size() > 1) {
+            log.warn("[Login] 手机号关联多个账号 phone={}", maskPhone(phone));
+            throw new ServiceException(MessageUtils.message("no.user.phone.duplicate"));
+        }
+        return matches.isEmpty() ? null : matches.get(0);
+    }
+
     /**
      * 用户登录或注册
      *
@@ -935,7 +952,7 @@ public class InfoUserController extends BaseController {
         String xcode = redisCache.getCacheObject(userDTO.getPhone().trim().replaceAll("\\+", ""));
         if (xcode == null) {
             if (userDTO.getCode().equals("8888")) {
-                InfoUser infoUser = infoUserService.getuser(userDTO.getPhone());
+                InfoUser infoUser = findPhoneLoginUser(userDTO.getPhone());
                 if (infoUser == null) {
                     return createUser(userDTO);
                 } else {
@@ -946,7 +963,7 @@ public class InfoUserController extends BaseController {
             }
         } else {
             if (xcode.equals(userDTO.getCode()) || userDTO.getCode().equals("8888")) {
-                InfoUser infoUser = infoUserService.getuser(userDTO.getPhone());
+                InfoUser infoUser = findPhoneLoginUser(userDTO.getPhone());
                 if (infoUser == null) {
                     return createUser(userDTO);
                 } else {
@@ -976,7 +993,7 @@ public class InfoUserController extends BaseController {
         info.setUserType("0");
         info.setMycode(uuid.get8UUID());
         infoUserService.saveOrUpdate(info);
-        InfoUser inus = infoUserService.getuser(userDTO.getPhone());
+        InfoUser inus = findPhoneLoginUser(userDTO.getPhone());
         createUserWallet(inus.getUserId());
         redisCache.deleteKeys(CacheConstants.USER_TOKEN_KEY + inus.getUserId() + ":" + "*");
         // 补充登录用户信息

+ 1 - 0
ruoyi-admin/src/main/resources/i18n/messages.properties

@@ -76,6 +76,7 @@ no.user.phone.blank=手机号不能为空
 no.user.password.not.null=Mật khẩu không được để trống
 no.user.jcaptcha.error=CAPTCHA không đúng
 no.user.not.exist=Người dùng không tồn tại
+no.user.phone.duplicate=该手机号关联多个账号,请联系平台
 no.user.login.success=Đăng nhập thành công
 no.user.password.error=Mật khẩu sai
 no.system.error=Lỗi hệ thống

+ 1 - 0
ruoyi-admin/src/main/resources/i18n/messages_en_US.properties

@@ -76,6 +76,7 @@ no.user.phone.blank=Phone number is required
 no.user.password.not.null=Password cannot be empty
 no.user.jcaptcha.error=Incorrect verification code
 no.user.not.exist=User does not exist
+no.user.phone.duplicate=This phone number is linked to multiple accounts. Please contact the platform.
 no.user.login.success=Login successful
 no.user.password.error=Password error
 no.system.error=System error

+ 1 - 0
ruoyi-admin/src/main/resources/i18n/messages_th_TH.properties

@@ -75,6 +75,7 @@ no.user.mobile.exist=ไม่สำเร็จ หมายเลขโทร
 no.user.password.not.null=รหัสผ่านต้องไม่ว่าง
 no.user.jcaptcha.error=รหัสยืนยันไม่ถูกต้อง
 no.user.not.exist=ไม่มีผู้ใช้นี้
+no.user.phone.duplicate=หมายเลขโทรศัพท์นี้เชื่อมโยงกับบัญชีหลายบัญชี โปรดติดต่อแพลตฟอร์ม
 no.user.login.success=เข้าสู่ระบบสำเร็จ
 no.user.password.error=รหัสผ่านไม่ถูกต้อง
 no.system.error=เกิดข้อผิดพลาดของระบบ

+ 1 - 0
ruoyi-admin/src/main/resources/i18n/messages_vi.properties

@@ -76,6 +76,7 @@ no.user.phone.blank=Số điện thoại không được để trống
 no.user.password.not.null=Mật khẩu không được để trống
 no.user.jcaptcha.error=CAPTCHA không đúng
 no.user.not.exist=Người dùng không tồn tại
+no.user.phone.duplicate=Số điện thoại này được liên kết với nhiều tài khoản. Vui lòng liên hệ nền tảng.
 no.user.login.success=Đăng nhập thành công
 no.user.password.error=Mật khẩu sai
 no.system.error=Lỗi hệ thống

+ 1 - 0
ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties

@@ -76,6 +76,7 @@ no.user.phone.blank=手机号不能为空
 no.user.password.not.null=密码不能为空
 no.user.jcaptcha.error=验证码不正确
 no.user.not.exist=用户不存在
+no.user.phone.duplicate=该手机号关联多个账号,请联系平台
 no.user.login.success=登录成功
 no.user.password.error=密码错误
 no.system.error=系统错误

+ 1 - 0
ruoyi-admin/src/main/resources/i18n/messages_zh_TW.properties

@@ -76,6 +76,7 @@ no.user.phone.blank=手機號碼不可為空
 no.user.password.not.null=密碼不能為空
 no.user.jcaptcha.error=驗證碼不正確
 no.user.not.exist=用戶不存在
+no.user.phone.duplicate=該手機號關聯多個帳號,請聯繫平台
 no.user.login.success=登入成功
 no.user.password.error=密碼錯誤
 no.system.error=系統錯誤

+ 39 - 1
ruoyi-admin/src/test/java/com/ruoyi/app/user/InfoUserControllerTest.java

@@ -100,6 +100,7 @@ class InfoUserControllerTest {
         messageSource.addMessage("no.user.jcaptcha.error", Locale.getDefault(), "验证码错误");
         messageSource.addMessage("no.oauth.tempkey.expired", Locale.getDefault(), "登录凭证已过期");
         messageSource.addMessage("no.oauth.phone.duplicate", Locale.getDefault(), "该手机号关联多个账号,请联系平台");
+        messageSource.addMessage("no.user.phone.duplicate", Locale.getDefault(), "该手机号关联多个账号,请联系平台");
         beanFactory.registerSingleton("messageSource", messageSource);
         beanFactory.registerSingleton("redisCache", mock(RedisCache.class));
         new SpringUtils().postProcessBeanFactory(beanFactory);
@@ -325,7 +326,7 @@ class InfoUserControllerTest {
         created.setUserId(42L);
         created.setPhone(request.getPhone());
         when(infoUserService.saveOrUpdate(any(InfoUser.class))).thenReturn(true);
-        when(infoUserService.getuser(request.getPhone())).thenReturn(created);
+        when(infoUserService.list(any(Wrapper.class))).thenReturn(java.util.List.of(created));
 
         AjaxResult result = controller.createUser(request);
 
@@ -333,6 +334,43 @@ class InfoUserControllerTest {
         assertEquals("phone", JWT.decode(token).getClaim("provider").asString());
     }
 
+    @Test
+    void phoneLoginRejectsPhoneLinkedToMultipleAccounts() {
+        UserDTO request = new UserDTO();
+        request.setPhone("0912345678");
+        request.setCode("8888");
+        when(redisCache.getCacheObject("0912345678")).thenReturn(null);
+        when(infoUserService.list(any(Wrapper.class))).thenReturn(java.util.List.of(
+                activeUser(71L, "0"), activeUser(72L, "0")));
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> controller.lodeing(request));
+
+        assertEquals("该手机号关联多个账号,请联系平台", exception.getMessage());
+        verify(infoUserService, never()).saveOrUpdate(any(InfoUser.class));
+    }
+
+    @Test
+    void phoneLoginOnlyMatchesMemberAccounts() {
+        UserDTO request = new UserDTO();
+        request.setPhone("0912345678");
+        request.setCode("8888");
+        when(redisCache.getCacheObject("0912345678")).thenReturn(null);
+        InfoUser created = activeUser(42L, "0");
+        created.setPhone("0912345678");
+        // 查询限定 user_type=0:同号商家行不命中,未注册则新建会员(首查空 + 新建回查)
+        when(infoUserService.list(any(Wrapper.class)))
+                .thenReturn(java.util.List.of(), java.util.List.of(created));
+        when(infoUserService.saveOrUpdate(any(InfoUser.class))).thenReturn(true);
+
+        AjaxResult result = controller.lodeing(request);
+
+        assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG));
+        ArgumentCaptor<InfoUser> createdCaptor = ArgumentCaptor.forClass(InfoUser.class);
+        verify(infoUserService).saveOrUpdate(createdCaptor.capture());
+        assertEquals("0", createdCaptor.getValue().getUserType());
+    }
+
     @Test
     void riderRegistrationRejectsDuplicateBusinessPhone() {
         com.ruoyi.system.domain.vo.UserDTO request = new com.ruoyi.system.domain.vo.UserDTO();