🐛 后台改手机号同步登录账号与司机档案电话
- transport 新增 feign /feign/client/driver/sync-mobile:按 user_id+tenant_id 找档案, 档案 mobile 等于账号旧号时只改 mobile 一列(带 driving_vehicle 原值),已是新号幂等成功, 找不到或判据不满足返回 synced=false 与原因 - blade-system updateUserWithPhoneSync:账号等于旧号时跟随新号、补账号唯一性校验, 本地写库后调 feign 同步档案;同步失败只记日志,不回滚账号更新。登录链路仍走 updateUser - App 内改号改调 updateUserWithPhoneSync - bindOpenId 改为覆盖写:不再拦截已绑定其他 openid 或 openid 属于其它账号
This commit is contained in:
+12
@@ -30,7 +30,9 @@ import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.pojo.dto.DriverEnsureProfileDTO;
|
||||
import org.springblade.transport.pojo.dto.DriverSyncMobileDTO;
|
||||
import org.springblade.transport.pojo.vo.DriverEnsureProfileVO;
|
||||
import org.springblade.transport.pojo.vo.DriverSyncMobileVO;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -58,4 +60,14 @@ public class DriverClient implements IDriverClient {
|
||||
return R.data(driverService.ensureProfile(param.getTenantId(), param.getMpUserId(), param.getPhone()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(SYNC_MOBILE)
|
||||
public R<DriverSyncMobileVO> syncMobile(@RequestBody DriverSyncMobileDTO param) {
|
||||
if (param == null) {
|
||||
return R.fail("司机档案参数不能为空");
|
||||
}
|
||||
return R.data(driverService.syncMobile(param.getTenantId(), param.getMpUserId(),
|
||||
param.getOldPhone(), param.getNewPhone()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
@@ -31,6 +31,7 @@ import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.transport.excel.DriverExcel;
|
||||
import org.springblade.transport.pojo.entity.Driver;
|
||||
import org.springblade.transport.pojo.vo.DriverEnsureProfileVO;
|
||||
import org.springblade.transport.pojo.vo.DriverSyncMobileVO;
|
||||
import org.springblade.transport.pojo.vo.DriverExpiryStatVO;
|
||||
import org.springblade.transport.pojo.vo.DriverVO;
|
||||
|
||||
@@ -70,6 +71,21 @@ public interface IDriverService extends BaseService<Driver> {
|
||||
*/
|
||||
DriverEnsureProfileVO ensureProfile(String tenantId, Long mpUserId, String phone);
|
||||
|
||||
/**
|
||||
* 账号改号后同步司机档案手机号
|
||||
* <p>
|
||||
* 幂等入口:只在档案属于该账号(user_id 命中)且档案 mobile 等于旧号时改这一列,
|
||||
* 档案已是新号时返回成功且不写库;档案找不到或判据不满足时不动档案,通过 message 说明原因。
|
||||
* 参数为空、手机号为脱敏值时抛 ServiceException。
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param mpUserId 小程序用户ID
|
||||
* @param oldPhone 账号改号前的手机号
|
||||
* @param newPhone 账号改号后的手机号
|
||||
* @return 档案ID与本次是否同步
|
||||
*/
|
||||
DriverSyncMobileVO syncMobile(String tenantId, Long mpUserId, String oldPhone, String newPhone);
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
*
|
||||
|
||||
+60
@@ -48,8 +48,11 @@ import org.springblade.transport.pojo.entity.Driver;
|
||||
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||
import org.springblade.transport.pojo.vo.DriverEnsureProfileVO;
|
||||
import org.springblade.transport.pojo.vo.DriverExpiryStatVO;
|
||||
import org.springblade.transport.pojo.vo.DriverSyncMobileVO;
|
||||
import org.springblade.transport.pojo.vo.DriverVO;
|
||||
import org.springblade.transport.service.IDriverService;
|
||||
import org.springblade.transport.support.DriverPhoneSyncDecision;
|
||||
import org.springblade.transport.support.DriverPhoneSyncPlanner;
|
||||
import org.springblade.transport.support.VehiclePlateSupport;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -168,6 +171,63 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
|
||||
return bindExistingProfile(existing.getId(), mpUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public DriverSyncMobileVO syncMobile(String tenantId, Long mpUserId, String oldPhone, String newPhone) {
|
||||
String resolvedTenantId = trimToNull(tenantId);
|
||||
if (resolvedTenantId == null) {
|
||||
resolvedTenantId = trimToNull(AuthUtil.getTenantId());
|
||||
}
|
||||
if (resolvedTenantId == null) {
|
||||
throw new ServiceException("租户信息为空,无法同步司机档案手机号");
|
||||
}
|
||||
if (Func.isEmpty(mpUserId)) {
|
||||
throw new ServiceException("小程序用户ID不能为空");
|
||||
}
|
||||
// 旧号是档案当前值的判据基准;含 * 的脱敏串与空串都不是可用手机号
|
||||
String oldMobile = trimToNull(oldPhone);
|
||||
if (oldMobile == null || oldMobile.contains("*")) {
|
||||
throw new ServiceException("账号原手机号无效,无法同步司机档案");
|
||||
}
|
||||
String newMobile = trimToNull(newPhone);
|
||||
if (newMobile == null || newMobile.contains("*")) {
|
||||
throw new ServiceException("手机号无效,不能写入司机档案");
|
||||
}
|
||||
Driver profile = findDriverByUserId(mpUserId);
|
||||
DriverPhoneSyncDecision decision = DriverPhoneSyncPlanner.decide(
|
||||
resolvedTenantId, mpUserId, profile, oldMobile, newMobile);
|
||||
Long driverId = profile == null ? null : profile.getId();
|
||||
if (decision == DriverPhoneSyncDecision.INVALID_PHONE) {
|
||||
throw new ServiceException("手机号无效,不能写入司机档案");
|
||||
}
|
||||
if (decision == DriverPhoneSyncDecision.NO_PROFILE) {
|
||||
return DriverSyncMobileVO.notSynced(null, "未找到该账号的司机档案");
|
||||
}
|
||||
if (decision == DriverPhoneSyncDecision.TENANT_MISMATCH) {
|
||||
return DriverSyncMobileVO.notSynced(driverId, "司机档案不属于该账号所在租户,未同步");
|
||||
}
|
||||
if (decision == DriverPhoneSyncDecision.USER_MISMATCH) {
|
||||
return DriverSyncMobileVO.notSynced(driverId, "司机档案未绑定该账号,未同步");
|
||||
}
|
||||
if (decision == DriverPhoneSyncDecision.ALREADY_SYNCED) {
|
||||
// 档案已是新号:重复调用(前端重试、日志重放)不应再写 update_time
|
||||
return DriverSyncMobileVO.synced(driverId);
|
||||
}
|
||||
if (decision == DriverPhoneSyncDecision.MOBILE_MISMATCH) {
|
||||
return DriverSyncMobileVO.notSynced(driverId, "司机档案手机号与账号原手机号不一致,未同步");
|
||||
}
|
||||
Driver patch = new Driver();
|
||||
patch.setId(driverId);
|
||||
patch.setMobile(newMobile);
|
||||
// 只改 mobile 一列,但 driving_vehicle 是 FieldStrategy.ALWAYS:不带原值会被写成 NULL
|
||||
carryDrivingVehicle(driverId, patch);
|
||||
if (!updateById(patch)) {
|
||||
// 0 行:档案被逻辑删除或租户条件不命中
|
||||
return DriverSyncMobileVO.notSynced(driverId, "司机档案更新未命中记录,未同步");
|
||||
}
|
||||
return DriverSyncMobileVO.synced(driverId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按小程序账号反查档案;user_id 有全局唯一索引,故不叠加租户条件
|
||||
*/
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support;
|
||||
|
||||
/**
|
||||
* 司机档案手机号同步的判定结果
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public enum DriverPhoneSyncDecision {
|
||||
|
||||
/**
|
||||
* 入参不可用:账号ID为空,或旧号/新号为空、为脱敏值
|
||||
*/
|
||||
INVALID_PHONE,
|
||||
|
||||
/**
|
||||
* 该账号没有司机档案
|
||||
*/
|
||||
NO_PROFILE,
|
||||
|
||||
/**
|
||||
* 档案的 tenant_id 与账号租户不一致
|
||||
*/
|
||||
TENANT_MISMATCH,
|
||||
|
||||
/**
|
||||
* 档案的 user_id 不是该账号
|
||||
*/
|
||||
USER_MISMATCH,
|
||||
|
||||
/**
|
||||
* 档案手机号已是新号:幂等,不写库
|
||||
*/
|
||||
ALREADY_SYNCED,
|
||||
|
||||
/**
|
||||
* 档案手机号与账号旧号不一致:不动档案
|
||||
*/
|
||||
MOBILE_MISMATCH,
|
||||
|
||||
/**
|
||||
* 判据全满足:只改 mobile 一列
|
||||
*/
|
||||
NEEDS_UPDATE
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support;
|
||||
|
||||
import org.springblade.transport.pojo.entity.Driver;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 「账号改号后是否同步司机档案 mobile」的判据
|
||||
* <p>
|
||||
* 纯函数:不读写库、不依赖 Spring 容器。Service 只负责「查档案 → decide → 按结果写库」,
|
||||
* 判据本身可以直接列真值表测试。
|
||||
* <p>
|
||||
* 全部以 trim 后比较;null 与 null 视为相等只用于「都没有值」的情形,不参与业务判据。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public final class DriverPhoneSyncPlanner {
|
||||
|
||||
private DriverPhoneSyncPlanner() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判定本次改号是否该改档案的 mobile
|
||||
*
|
||||
* @param tenantId 账号所属租户
|
||||
* @param mpUserId 小程序账号ID
|
||||
* @param profile 按 user_id 查到的司机档案,未查到为 null
|
||||
* @param oldPhone 账号改号前的手机号
|
||||
* @param newPhone 账号改号后的手机号
|
||||
* @return 判定结果
|
||||
*/
|
||||
public static DriverPhoneSyncDecision decide(String tenantId, Long mpUserId, Driver profile,
|
||||
String oldPhone, String newPhone) {
|
||||
String oldMobile = trimToNull(oldPhone);
|
||||
String newMobile = trimToNull(newPhone);
|
||||
// 旧号既是「档案是否仍属于这次改号」的基准,也是唯一能安全替换的值;
|
||||
// 脱敏串与空串写进档案都不可用
|
||||
if (mpUserId == null || oldMobile == null || newMobile == null
|
||||
|| oldMobile.contains("*") || newMobile.contains("*")) {
|
||||
return DriverPhoneSyncDecision.INVALID_PHONE;
|
||||
}
|
||||
if (profile == null) {
|
||||
return DriverPhoneSyncDecision.NO_PROFILE;
|
||||
}
|
||||
String resolvedTenantId = trimToNull(tenantId);
|
||||
String profileTenantId = trimToNull(profile.getTenantId());
|
||||
if (resolvedTenantId != null && profileTenantId != null && !resolvedTenantId.equals(profileTenantId)) {
|
||||
return DriverPhoneSyncDecision.TENANT_MISMATCH;
|
||||
}
|
||||
if (!Objects.equals(profile.getUserId(), mpUserId)) {
|
||||
return DriverPhoneSyncDecision.USER_MISMATCH;
|
||||
}
|
||||
String profileMobile = trimToNull(profile.getMobile());
|
||||
if (Objects.equals(profileMobile, newMobile)) {
|
||||
return DriverPhoneSyncDecision.ALREADY_SYNCED;
|
||||
}
|
||||
if (!Objects.equals(profileMobile, oldMobile)) {
|
||||
return DriverPhoneSyncDecision.MOBILE_MISMATCH;
|
||||
}
|
||||
return DriverPhoneSyncDecision.NEEDS_UPDATE;
|
||||
}
|
||||
|
||||
private static String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user