✨ 新增小程序账号查找或创建与按openid查询接口
This commit is contained in:
+15
@@ -24,6 +24,7 @@ package org.springblade.system.feign;
|
||||
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
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.springframework.cloud.openfeign.FeignClient;
|
||||
@@ -46,6 +47,7 @@ public interface IMpUserClient {
|
||||
String USER_BY_ID = API_PREFIX + "/user-by-id";
|
||||
String USER_BY_PHONE = API_PREFIX + "/user-by-phone";
|
||||
String USER_BY_ACCOUNT = API_PREFIX + "/user-by-account";
|
||||
String USER_BY_OPENID = API_PREFIX + "/user-by-openid";
|
||||
String USER_INFO_BY_ID = API_PREFIX + "/info-by-id";
|
||||
String USER_INFO_BY_PHONE = API_PREFIX + "/info-by-phone";
|
||||
String USER_INFO_BY_ACCOUNT = API_PREFIX + "/info-by-account";
|
||||
@@ -53,6 +55,7 @@ public interface IMpUserClient {
|
||||
String UPDATE_USER = API_PREFIX + "/update-user";
|
||||
String BIND_OPENID = API_PREFIX + "/bind-openid";
|
||||
String SET_PASSWORD = API_PREFIX + "/set-password";
|
||||
String ENSURE_USER = API_PREFIX + "/ensure-user";
|
||||
|
||||
/**
|
||||
* 按主键查询
|
||||
@@ -72,6 +75,18 @@ public interface IMpUserClient {
|
||||
@GetMapping(USER_BY_ACCOUNT)
|
||||
R<MpUser> userByAccount(@RequestParam("tenantId") String tenantId, @RequestParam("account") String account);
|
||||
|
||||
/**
|
||||
* 按租户 + openid 查询
|
||||
*/
|
||||
@GetMapping(USER_BY_OPENID)
|
||||
R<MpUser> userByOpenid(@RequestParam("tenantId") String tenantId, @RequestParam("openid") String openid);
|
||||
|
||||
/**
|
||||
* 查找或创建小程序用户(登录路径专用,已存在时不抛异常、不改身份)
|
||||
*/
|
||||
@PostMapping(ENSURE_USER)
|
||||
R<MpUser> ensureUser(@RequestBody MpUserEnsureDTO param);
|
||||
|
||||
/**
|
||||
* 按主键查询登录信息
|
||||
*/
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.system.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 小程序用户「查找或创建」参数
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "小程序用户查找或创建参数")
|
||||
public class MpUserEnsureDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "租户ID,为空时依次取当前登录租户与默认租户")
|
||||
private String tenantId;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "微信小程序openid,非空时随首次建号一并写入")
|
||||
private String openid;
|
||||
|
||||
}
|
||||
+13
@@ -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) {
|
||||
|
||||
+13
@@ -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);
|
||||
|
||||
/**
|
||||
* 按主键组装登录信息
|
||||
*/
|
||||
|
||||
+83
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user