✨ 新增小程序账号查找或创建与按openid查询接口

This commit is contained in:
2026-10-23 15:48:31 +08:00
parent 0c72a72f13
commit 3475ec1f87
5 changed files with 179 additions and 0 deletions
@@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import org.springblade.core.tenant.annotation.NonDS;
import org.springblade.core.tool.api.R;
import org.springblade.system.pojo.dto.MpUserEnsureDTO;
import org.springblade.system.pojo.entity.MpUser;
import org.springblade.system.pojo.entity.MpUserInfo;
import org.springblade.system.service.IMpUserService;
@@ -65,6 +66,18 @@ public class MpUserClient implements IMpUserClient {
return R.data(mpUserService.getByAccount(tenantId, account));
}
@Override
@GetMapping(USER_BY_OPENID)
public R<MpUser> userByOpenid(String tenantId, String openid) {
return R.data(mpUserService.getByOpenid(tenantId, openid));
}
@Override
@PostMapping(ENSURE_USER)
public R<MpUser> ensureUser(@RequestBody MpUserEnsureDTO param) {
return R.data(mpUserService.ensureUser(param));
}
@Override
@GetMapping(USER_INFO_BY_ID)
public R<MpUserInfo> userInfoById(Long userId) {
@@ -24,6 +24,7 @@ package org.springblade.system.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseService;
import org.springblade.system.pojo.dto.MpUserEnsureDTO;
import org.springblade.system.pojo.entity.MpUser;
import org.springblade.system.pojo.entity.MpUserInfo;
@@ -47,6 +48,18 @@ public interface IMpUserService extends BaseService<MpUser> {
*/
MpUser getByAccount(String tenantId, String account);
/**
* 按租户 + 微信小程序 openid 查询
*/
MpUser getByOpenid(String tenantId, String openid);
/**
* 查找或创建小程序用户(登录路径专用)
* <p>
* 与 {@link #submit} 的提交式语义相反:手机号已存在时直接返回已有账号,且不改其身份。
*/
MpUser ensureUser(MpUserEnsureDTO param);
/**
* 按主键组装登录信息
*/
@@ -25,6 +25,7 @@ package org.springblade.system.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.utils.PasswordRuleUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
@@ -36,6 +37,7 @@ import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.system.cache.MpUserCache;
import org.springblade.system.mapper.MpUserMapper;
import org.springblade.system.pojo.dto.MpUserEnsureDTO;
import org.springblade.system.pojo.entity.MpUser;
import org.springblade.system.pojo.entity.MpUserInfo;
import org.springblade.system.pojo.enums.MpUserIdentity;
@@ -53,10 +55,16 @@ import java.util.Objects;
*
* @author Chill
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> implements IMpUserService {
/**
* 兜底租户:与 blade_mp_user.tenant_id 的建表默认值一致
*/
private static final String DEFAULT_TENANT_ID = "000000";
@Override
public MpUser getByPhone(String tenantId, String phone) {
if (Func.isBlank(tenantId) || Func.isBlank(phone)) {
@@ -81,6 +89,81 @@ public class MpUserServiceImpl extends BaseServiceImpl<MpUserMapper, MpUser> imp
.last("LIMIT 1"));
}
@Override
public MpUser getByOpenid(String tenantId, String openid) {
if (Func.isBlank(tenantId) || Func.isBlank(openid)) {
return null;
}
return getOne(Wrappers.<MpUser>lambdaQuery()
.eq(MpUser::getTenantId, tenantId)
.eq(MpUser::getOpenid, openid)
.orderByDesc(MpUser::getCreateTime)
.last("LIMIT 1"));
}
@Override
@Transactional(rollbackFor = Exception.class)
public MpUser ensureUser(MpUserEnsureDTO param) {
if (param == null) {
throw new ServiceException("小程序用户参数不能为空");
}
String tenantId = resolveEnsureTenantId(param.getTenantId());
String phone = trimToNull(param.getPhone());
if (Func.isBlank(phone)) {
throw new ServiceException("手机号不能为空");
}
// 先查后插:命中即返回,不新建也不改身份。租户为空时唯一键对 NULL 不去重,
// 故 resolveEnsureTenantId 已在写库前把租户兜到非空。
MpUser existed = getByPhone(tenantId, phone);
if (existed != null && Func.isNotEmpty(existed.getId())) {
return existed;
}
MpUser user = new MpUser();
user.setTenantId(tenantId);
user.setPhone(phone);
user.setAccount(phone);
user.setIdentities(MpUserIdentity.DRIVER.getCode());
user.setOpenid(trimToNull(param.getOpenid()));
user.setStatus(1);
prepare(user);
validate(user);
// 登录路径不设密码:密码留空,避免调用方误传弱口令
user.setPassword(null);
try {
save(user);
} catch (Exception exception) {
// 并发建号撞 (tenant_id, phone) 唯一键:重读一次返回,登录方不该因重试失败
MpUser concurrent = getByPhone(tenantId, phone);
if (concurrent != null && Func.isNotEmpty(concurrent.getId())) {
return concurrent;
}
log.warn("小程序用户建号失败,租户={} 手机号={}", tenantId, phone, exception);
throw new ServiceException("创建小程序用户失败");
}
if (Func.isEmpty(user.getId())) {
throw new ServiceException("创建小程序用户失败");
}
MpUserCache.clear();
return user;
}
/**
* 登录路径的租户解析:入参 → 当前登录租户 → 默认租户
* <p>
* 写库前必须得到非空值:唯一键 (tenant_id, phone) 对 NULL 不去重,租户为 NULL
* 会为同一手机号造出重复账号。
*/
private String resolveEnsureTenantId(String tenantId) {
String resolved = trimToNull(tenantId);
if (resolved == null) {
resolved = trimToNull(AuthUtil.getTenantId());
}
if (resolved == null) {
resolved = DEFAULT_TENANT_ID;
}
return resolved;
}
@Override
public MpUserInfo userInfo(Long userId) {
if (Func.isEmpty(userId)) {