🐛 修复小程序重新授权因空账号判空失败报 500 且 openid 无法绑定

system 侧 R.data(null) 被空值序列化写成 {},Feign 侧反序列化出 id 为空的
MpUser 而非 null,被当作「已查到」:换号撞键分支误置 openidOccupied,导致
openid 永不绑定并提示「该微信已绑定其他账号」;手机号查不到账号时还会以
空主键、空租户调用 bindOpenId,系统侧判缺参 400,再被 Feign 降级包成
AssertionError(Error 而非 Exception)逃出 catch (Exception),登录返回 500。

- 新增 presentAccount():以主键判定是否命中,findByOpenid/findByPhone 统一走它
- bindOpenid 入口加守卫:缺主键或租户只记 WARN 后跳过,不再发空参数请求
- 补 2 条单测:空对象 data 视为未命中;不落空主键账号、不发空主键绑定
This commit is contained in:
2026-10-26 13:48:30 +08:00
parent 1b11d3f369
commit ccf3d7adf7
2 changed files with 58 additions and 2 deletions
@@ -229,6 +229,44 @@ class MpLoginSupportTest {
verify(mpUserClient, never()).bindOpenId(anyString(), anyLong(), anyString());
}
@Test
@DisplayName("远端查无账号返回空对象(id 为空):按未命中建号,并用真实主键补绑 openid")
void emptyAccountFromRemoteIsTreatedAsMiss() {
// 服务端 R.data(null) 落 HTTP 是 {},Feign 反序列化成 id 为空的 MpUser 而非 null
MpUser created = mpUser(61L, PHONE, null, "driver");
when(mpUserClient.userByOpenid(TENANT_ID, OPENID)).thenReturn(R.data(new MpUser()));
when(mpUserClient.userByPhone(TENANT_ID, PHONE)).thenReturn(R.data(new MpUser()));
when(mpUserClient.ensureUser(any(MpUserEnsureDTO.class))).thenReturn(R.data(created));
when(mpUserClient.bindOpenId(TENANT_ID, 61L, OPENID)).thenReturn(R.data(Boolean.TRUE));
when(mpUserClient.userInfoById(61L)).thenReturn(R.data(userInfo(created, "driver")));
when(driverClient.ensureProfile(any(DriverEnsureProfileDTO.class))).thenReturn(R.data(profileResult()));
OAuth2User user = mpLoginSupport.ensureLoginAccount(TENANT_ID, PHONE, OPENID);
assertNotNull(user);
assertEquals("61", user.getUserId());
assertNull(user.getDetail().get(MpLoginSupport.DETAIL_LOGIN_NOTICE), "不该出现换号撞键提示");
verify(mpUserClient).ensureUser(any(MpUserEnsureDTO.class));
verify(mpUserClient, never()).updateUser(any(MpUser.class));
verify(mpUserClient).bindOpenId(TENANT_ID, 61L, OPENID);
}
@Test
@DisplayName("两个查询都返回空对象时:不落空账号、不发起带空主键的绑定")
void emptyAccountDoesNotLeakNullIdentityIntoBind() {
MpUser created = mpUser(62L, PHONE, OPENID, "driver");
when(mpUserClient.userByOpenid(TENANT_ID, OPENID)).thenReturn(R.data(new MpUser()));
when(mpUserClient.userByPhone(TENANT_ID, PHONE)).thenReturn(R.data(new MpUser()));
when(mpUserClient.ensureUser(any(MpUserEnsureDTO.class))).thenReturn(R.data(created));
when(mpUserClient.userInfoById(62L)).thenReturn(R.data(userInfo(created, "driver")));
when(driverClient.ensureProfile(any(DriverEnsureProfileDTO.class))).thenReturn(R.data(profileResult()));
OAuth2User user = mpLoginSupport.ensureLoginAccount(TENANT_ID, PHONE, OPENID);
assertEquals("62", user.getUserId());
verify(mpUserClient, never()).bindOpenId(anyString(), anyLong(), anyString());
}
private MpUser mpUser(Long id, String phone, String openid, String identities) {
MpUser user = new MpUser();
user.setId(id);