package com.tcm.training.admin.app.auth.service;
|
|
import com.tcm.training.admin.app.auth.config.AppSmsAuthProperties;
|
import com.tcm.training.admin.app.auth.vo.AppSmsCodeSendResponse;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.stereotype.Service;
|
|
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.TimeUnit;
|
|
/**
|
* APP 登录短信验证码服务,负责验证码的生成、缓存和校验。
|
*/
|
@Service
|
public class AppSmsCodeService {
|
|
private static final String LOGIN_CODE_CACHE_PREFIX = "app:sms:login:";
|
|
private final StringRedisTemplate stringRedisTemplate;
|
private final AppSmsSender appSmsSender;
|
private final AppSmsAuthProperties appSmsAuthProperties;
|
|
/**
|
* 注入验证码缓存、发送器与调试配置。
|
*/
|
public AppSmsCodeService(StringRedisTemplate stringRedisTemplate,
|
AppSmsSender appSmsSender,
|
AppSmsAuthProperties appSmsAuthProperties) {
|
this.stringRedisTemplate = stringRedisTemplate;
|
this.appSmsSender = appSmsSender;
|
this.appSmsAuthProperties = appSmsAuthProperties;
|
}
|
|
/**
|
* 生成并发送登录验证码,同时写入缓存。
|
*/
|
public AppSmsCodeSendResponse createLoginCode(String phone) {
|
String code = generateCode();
|
stringRedisTemplate.opsForValue().set(
|
buildCacheKey(phone),
|
code,
|
appSmsAuthProperties.getExpireSeconds(),
|
TimeUnit.SECONDS
|
);
|
appSmsSender.sendLoginCode(phone, code);
|
return new AppSmsCodeSendResponse(appSmsAuthProperties.getExpireSeconds());
|
}
|
|
/**
|
* 校验登录验证码,成功后销毁缓存中的一次性验证码。
|
*/
|
public void validateLoginCode(String phone, String code) {
|
if (matchesDebugCode(code)) {
|
// 开发环境允许固定验证码直通,便于前后端联调。
|
return;
|
}
|
String cacheKey = buildCacheKey(phone);
|
String cachedCode = stringRedisTemplate.opsForValue().get(cacheKey);
|
if (cachedCode == null || cachedCode.trim().isEmpty()) {
|
throw new IllegalArgumentException("验证码已失效,请重新获取");
|
}
|
if (!cachedCode.equals(code)) {
|
throw new IllegalArgumentException("验证码错误");
|
}
|
// 验证码登录成功后立即删除,避免重复使用。
|
stringRedisTemplate.delete(cacheKey);
|
}
|
|
/**
|
* 生成 6 位数字验证码。
|
*/
|
private String generateCode() {
|
int code = ThreadLocalRandom.current().nextInt(100000, 1000000);
|
return String.valueOf(code);
|
}
|
|
/**
|
* 判断是否命中本地调试验证码。
|
*/
|
private boolean matchesDebugCode(String code) {
|
if (!appSmsAuthProperties.isDebugEnabled()) {
|
return false;
|
}
|
String debugCode = appSmsAuthProperties.getDebugCode();
|
return debugCode != null && !debugCode.trim().isEmpty() && debugCode.equals(code);
|
}
|
|
/**
|
* 构造手机号维度的验证码缓存 key。
|
*/
|
private String buildCacheKey(String phone) {
|
return LOGIN_CODE_CACHE_PREFIX + phone;
|
}
|
}
|