fix gao user invite

This commit is contained in:
Timi
2026-08-21 21:54:09 +08:00
parent 1b8936f197
commit 3a3809b661
5 changed files with 43 additions and 13 deletions
@@ -65,6 +65,9 @@ public class SmsServiceImplement implements SmsService {
NotifyQueueConfig.Sms.Captcha captchaConf = config.getSms().getCaptcha();
NotifyDetail detail = notifyDetailService.get(detailId);
TimiException.required(detail, "短信验证码无效");
TimiException.required(detail.getSendAt(), "短信验证码无效");
TimiException.requiredTrue(NotifyDetail.MsgType.SMS == detail.getMsgType(), "短信验证码无效");
if (Time.M * captchaConf.getTtlMinute() < Time.now() - detail.getSendAt()) {
throw new TimiException(TimiCode.ARG_EXPIRED, "验证码过期");
}
@@ -27,7 +27,6 @@ import com.imyeyu.api.modules.user.service.UserService;
import com.imyeyu.api.modules.user.vo.LoginResponse;
import com.imyeyu.java.TimiJava;
import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.CaptchaValid;
import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult;
@@ -80,12 +79,11 @@ public class GaoUserController {
@AOPLog
@JsonView(ResponseView.Public.class)
@CaptchaValid
@RequestRateLimit(value = 5, inSeconds = 60)
@PostMapping("/invite/register")
public LoginResponse registerByInvite(@RequestBody @Valid GaoUserInviteRegisterRequest req) {
GaoUserInviteRegisterData data = req.getData();
String userId = inviteService.register(data);
String userId = inviteService.register(data, req.getDetailId(), req.getCaptcha());
return userLoginService.login(UUID.randomUUID().toString(), userId);
}
@@ -26,6 +26,8 @@ public interface GaoUserInviteService {
/// 使用邀请注册 GAO 用户
///
/// @param data 注册数据
/// @param smsDetailId 短信通知详情 ID
/// @param smsCaptcha 短信验证码
/// @return 注册成功的用户 ID
String register(GaoUserInviteRegisterData data);
String register(GaoUserInviteRegisterData data, String smsDetailId, String smsCaptcha);
}
@@ -3,6 +3,8 @@ package com.imyeyu.api.modules.gao.service.implement;
import com.imyeyu.api.bean.CoreRoleCode;
import com.imyeyu.api.bean.ModuleCode;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.common.entity.NotifyDetail;
import com.imyeyu.api.modules.common.service.SmsService;
import com.imyeyu.api.modules.gao.bean.GaoUserInviteCache;
import com.imyeyu.api.modules.gao.bean.GaoPermissionCode;
import com.imyeyu.api.modules.gao.bean.GaoRoleCode;
@@ -69,6 +71,7 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
private final GaoStoreService gaoStoreService;
private final GaoUserService gaoUserService;
private final SmsService smsService;
private final UserLoginService userLoginService;
private final UserRoleService userRoleService;
private final UserService userService;
@@ -98,9 +101,11 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
logger.setUserId(inviterUserId);
permissionChecker.checkAny(ModuleCode.GAO, GaoPermissionCode.USER_CREATE.getValue());
Role requestedRole = roleService.get(req.getRoleId());
boolean adminInviteGlobalManager = roleChecker.hasAny(ModuleCode.CORE, CoreRoleCode.ADMIN.name())
boolean coreAdmin = isCoreAdmin();
boolean adminInviteGlobalManager = coreAdmin
&& isGlobalManagerRole(requestedRole);
boolean globalManager = authorizationScopeService.isSystemAdmin(inviterUserId)
|| coreAdmin
|| roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name());
boolean storeManager = !globalManager;
GaoStore loginStore = storeManager ? gaoStoreService.getByUserId(inviterUserId) : null;
@@ -154,7 +159,7 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public String register(GaoUserInviteRegisterData data) {
public String register(GaoUserInviteRegisterData data, String smsDetailId, String smsCaptcha) {
TimiException.required(data, "not found register data");
String code = data.getCode();
TimiException.required(code, "not found code");
@@ -170,6 +175,9 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
cache = requireValidInvite(code);
logger.setContent(buildLogContent(cache));
validateInviteTarget(cache);
NotifyDetail smsDetail = smsService.captchaVerify(smsDetailId, smsCaptcha);
TimiException.requiredTrue(NotifyDetail.MsgType.SMS == smsDetail.getMsgType()
&& data.getPhoneNo().equals(smsDetail.getSendTo()), "短信验证码与手机号不匹配");
TimiException.requiredNull(userService.getByName(data.getName()), "existed user name");
TimiException.requiredNull(userService.getByPhoneNo(data.getPhoneNo()), "existed phone number");
if (!redisInvite.destroy(getInviteKey(code))) {
@@ -264,10 +272,11 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
.stream()
.map(Role::getId)
.toList();
boolean descendant = authorizationScopeService.listManageableRole(inviterUserId, ModuleCode.GAO)
boolean adminInviteGlobalManager = isCoreAdmin() && isGlobalManagerRole(role);
boolean descendant = adminInviteGlobalManager || (authorizationScopeService.listManageableRole(inviterUserId, ModuleCode.GAO)
.stream()
.anyMatch(item -> role.getId().equals(item.getId()))
&& !directRoleIdList.contains(role.getId());
&& !directRoleIdList.contains(role.getId()));
if (!descendant) {
throw new TimiException(TimiCode.PERMISSION_ERROR, "角色不在可邀请范围内");
}
@@ -302,7 +311,8 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
}
private void checkStoreScope(String userId, String storeId) {
if (authorizationScopeService.isSystemAdmin(userId) || roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
if (authorizationScopeService.isSystemAdmin(userId) || isCoreAdmin()
|| roleChecker.hasAny(ModuleCode.GAO, GaoRoleCode.GLOBAL_MANAGER.name())) {
return;
}
GaoStore userStore = gaoStoreService.getByUserId(userId);
@@ -346,4 +356,8 @@ public class GaoUserInviteServiceImplement implements GaoUserInviteService {
private TimiException invalidInvite() {
return new TimiException(TimiCode.ARG_BAD, "邀请码无效或已过期");
}
private boolean isCoreAdmin() {
return roleChecker.hasAny(ModuleCode.CORE, CoreRoleCode.ADMIN.name());
}
}
@@ -1,20 +1,33 @@
package com.imyeyu.api.modules.gao.vo;
import com.imyeyu.spring.bean.CaptchaData;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
/// GAO 用户邀请注册请求
///
/// @author Codex
/// @since 2026-08-13
public class GaoUserInviteRegisterRequest extends CaptchaData<GaoUserInviteRegisterData> {
@Data
public class GaoUserInviteRegisterRequest {
/// 短信通知详情 ID
@NotBlank
private String detailId;
/// 短信验证码
@NotBlank
@Pattern(regexp = "\\d{6}")
private String captcha;
private GaoUserInviteRegisterData data;
/// 级联校验注册数据
@Override
@NotNull
@Valid
public GaoUserInviteRegisterData getData() {
return super.getData();
return data;
}
}