feat(auth): 实现跨租户管理员手机号短信验证码发送功能

- 新增根据手机号查询跨租户管理员账号功能,支持超级管理员和普通管理员
- 新增后台管理员跨租户发送短信验证码接口,允许管理员身份验证后发送
- 将短信发送核心逻辑抽取到私有方法,支持传入租户ID读取对应配置
- 优化短信配置读取逻辑,兼容历史配置Key
- 移除MainController中硬编码的短信配置,改为通过配置和租户动态获取
- UserMapper及UserService相关接口和实现新增跨租户管理员查询方法
This commit is contained in:
2026-07-20 23:30:35 +08:00
parent 592345018e
commit b2487572a6
5 changed files with 72 additions and 10 deletions

View File

@@ -616,13 +616,6 @@ public class MainController extends BaseController {
if (param == null) {
return fail("参数不能为空");
}
// 默认配置(当租户未配置短信服务时使用)
String accessKeyId = "LTAI5t7jGTFTbpSLzzXY8HzP";
String accessKeySecret = "Z22EPJyUhQaIZfEEmZ4Hdbw6xZibCb";
String templateCode = "SMS_481670203";
String signName = "网宿信息";
String regionId = "cn-hangzhou";
if (!CommonUtil.isValidPhoneNumber(param.getPhone())) {
return fail("请输入有效的手机号码");
}
@@ -655,13 +648,48 @@ public class MainController extends BaseController {
return fail("租户ID格式不正确");
}
}
return sendSmsCaptchaInternal(param, tenantId);
}
@Operation(summary = "发送短信验证码(后台管理员, 跨租户)")
@PostMapping("/sendSmsCaptchaByAdmin")
public ApiResult<?> sendSmsCaptchaByAdmin(@RequestBody SmsCaptchaParam param) {
if (param == null) {
return fail("参数不能为空");
}
if (!CommonUtil.isValidPhoneNumber(param.getPhone())) {
return fail("请输入有效的手机号码");
}
// 跨租户校验:只要某个租户下该手机号是超级管理员或管理员即可发送验证码
final User admin = userService.getByPhoneAndAdmin(param.getPhone());
if (ObjectUtil.isEmpty(admin)) {
return fail("该手机号码未注册管理员账号!");
}
// 使用管理员所在租户的短信配置发送
return sendSmsCaptchaInternal(param, admin.getTenantId());
}
/**
* 短信发送核心逻辑
*
* @param param 参数
* @param configTenantId 用于读取租户短信配置的租户ID为 null 时使用默认配置
*/
private ApiResult<?> sendSmsCaptchaInternal(SmsCaptchaParam param, Integer configTenantId) {
// 默认配置(当租户未配置短信服务时使用)
String accessKeyId = "LTAI5t7jGTFTbpSLzzXY8HzP";
String accessKeySecret = "Z22EPJyUhQaIZfEEmZ4Hdbw6xZibCb";
String templateCode = "SMS_481670203";
String signName = "网宿信息";
String regionId = "cn-hangzhou";
// 读取租户的短信配置SettingController写入的key格式为sms:{tenantId}
if (tenantId != null) {
String settingJson = redisUtil.get("sms:" + tenantId);
if (configTenantId != null) {
String settingJson = redisUtil.get("sms:" + configTenantId);
// 兼容历史key
if (StrUtil.isBlank(settingJson)) {
settingJson = redisUtil.get("setting:sms:" + tenantId);
settingJson = redisUtil.get("setting:sms:" + configTenantId);
}
if (StrUtil.isNotBlank(settingJson)) {
JSONObject jsonObject = JSONObject.parseObject(settingJson);

View File

@@ -117,4 +117,13 @@ public interface UserMapper extends BaseMapper<User> {
@InterceptorIgnore(tenantLine = "true")
User selectLastLoginDeveloperByPhone(@Param("phone") String phone);
/**
* 根据手机号查询管理员账号(跨租户:超级管理员或普通管理员均可,忽略租户隔离)
*
* @param phone 手机号
* @return User
*/
@InterceptorIgnore(tenantLine = "true")
User selectAdminByPhoneCrossTenant(@Param("phone") String phone);
}

View File

@@ -448,4 +448,16 @@
LIMIT 1
</select>
<!-- 根据手机号查询管理员账号(跨租户:超级管理员或普通管理员均可,忽略租户隔离) -->
<select id="selectAdminByPhoneCrossTenant" resultType="com.gxwebsoft.common.system.entity.User">
SELECT u.*
FROM sys_user u
WHERE u.deleted = 0
AND u.phone = #{phone}
AND (u.is_super_admin = 1 OR u.is_admin = 1)
ORDER BY u.update_time DESC,
u.user_id DESC
LIMIT 1
</select>
</mapper>

View File

@@ -144,6 +144,14 @@ public interface UserService extends IService<User>, UserDetailsService {
*/
User getLastLoginDeveloperByPhone(String phone);
/**
* 根据手机号查询管理员账号(跨租户:超级管理员或普通管理员均可,忽略租户隔离)
*
* @param phone 手机号
* @return 用户信息
*/
User getByPhoneAndAdmin(String phone);
List<User> pageAll(UserParam param);
User getByUserId(String userId);

View File

@@ -410,6 +410,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return baseMapper.selectLastLoginDeveloperByPhone(phone);
}
@Override
public User getByPhoneAndAdmin(String phone) {
return baseMapper.selectAdminByPhoneCrossTenant(phone);
}
@Override
public List<User> pageAll(UserParam param) {
return baseMapper.pageRelAll(param);