编写短信验证码发送接口
Sync All Branches to GitHub / sync (push) Successful in 2s

This commit is contained in:
2026-01-15 22:59:58 +08:00
parent c32f06239e
commit 3cba48728d
13 changed files with 226 additions and 9 deletions
@@ -0,0 +1,29 @@
package com.jy.xiaoyishu.auth.constant;
/**
*
* @author ayi
* @version V1.0
* @title RedisKeyConstants
* @description Redis key 常量
* @date 2026/01/15 18:45
*/
public class RedisKeyConstants {
/**
* 验证码 KEY 前缀
*/
private static final String VERIFICATION_CODE_KEY_PREFIX = "verification_code:";
/**
* 构建验证码 KEY
*
* @param phone 手机号
* @return 验证码 KEY
*/
public static String buildVerificationCodeKey(String phone) {
return VERIFICATION_CODE_KEY_PREFIX + phone;
}
}
@@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.jy.framework.biz.operationlog.aspect.ApiOperationLog;
import com.jy.framework.common.response.Response;
import com.jy.xiaoyishu.auth.vo.User;
import com.jy.xiaoyishu.auth.model.vo.User;
import cn.dev33.satoken.stp.StpUtil;
@@ -0,0 +1,33 @@
package com.jy.xiaoyishu.auth.controller;
import com.jy.framework.biz.operationlog.aspect.ApiOperationLog;
import com.jy.framework.common.response.Response;
import com.jy.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
import com.jy.xiaoyishu.auth.service.VerificationCodeService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ayi
* @version V1.0
* @title VerificationCodeController
* @date 2026/01/15
* @description 验证码控制器
*/
@Slf4j
@RestController
public class VerificationCodeController {
@Resource
private VerificationCodeService verificationCodeService;
@PostMapping("/verification/code/send")
@ApiOperationLog(description = "发送短信验证码")
public Response<?> send(@Validated @RequestBody SendVerificationCodeReqVO sendVerificationCodeReqVO) {
return verificationCodeService.send(sendVerificationCodeReqVO);
}
}
@@ -18,7 +18,9 @@ import com.jy.framework.common.exception.BaseExceptionInterface;
@AllArgsConstructor
public enum ResponseCodeEnum implements BaseExceptionInterface {
// -------- 通用异常状态码 --------
SYSTEM_ERROR("AUTH-10000", "系统错误"), PARAM_NOT_VALID("AUTH-10001", "参数错误");
SYSTEM_ERROR("AUTH-10000", "系统错误"),
PARAM_NOT_VALID("AUTH-10001", "参数错误"),
VERIFICATION_CODE_SEND_FREQUENTLY("AUTH-20000", "请求太频繁,请3分钟后再试");
/**
* 错误码
@@ -15,6 +15,15 @@ import com.jy.xiaoyishu.auth.enums.ResponseCodeEnum;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
/**
*
* @author ayi
* @version V1.0
* @title GlobalExceptionHandler
* @description 全局异常处理器
* @date 2026/01/15 18:40
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@@ -1,4 +1,4 @@
package com.jy.xiaoyishu.auth.vo;
package com.jy.xiaoyishu.auth.model.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -0,0 +1,30 @@
package com.jy.xiaoyishu.auth.model.vo.verificationcode;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @author ayi
* @version V1.0
* @title SendVerificationCodeReqVO
* @description 发送验证码请求参数
* @date 2026/01/15 18:39
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SendVerificationCodeReqVO {
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空")
private String phone;
}
@@ -0,0 +1,25 @@
package com.jy.xiaoyishu.auth.service;
import com.jy.framework.common.response.Response;
import com.jy.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
/**
*
* @author ayi
* @version V1.0
* @title VerificationCodeService
* @description 验证码服务
* @date 2026/01/15 18:48
*/
public interface VerificationCodeService {
/**
* 发送验证码
*
* @param sendVerificationCodeReqVO 发送验证码请求参数
* @return 响应结果
*/
Response<?> send(SendVerificationCodeReqVO sendVerificationCodeReqVO);
}
@@ -0,0 +1,51 @@
package com.jy.xiaoyishu.auth.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.jy.framework.common.exception.BizException;
import com.jy.framework.common.response.Response;
import com.jy.xiaoyishu.auth.constant.RedisKeyConstants;
import com.jy.xiaoyishu.auth.enums.ResponseCodeEnum;
import com.jy.xiaoyishu.auth.model.vo.verificationcode.SendVerificationCodeReqVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.jy.xiaoyishu.auth.service.VerificationCodeService;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class VerificationCodeServiceImpl implements VerificationCodeService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Override
public Response<?> send(SendVerificationCodeReqVO sendVerificationCodeReqVO) {
// 手机号
String phone = sendVerificationCodeReqVO.getPhone();
// 构建验证码 redis key
String key = RedisKeyConstants.buildVerificationCodeKey(phone);
// 判断是否已发送验证码
boolean isSent = redisTemplate.hasKey(key);
if (isSent) {
// 若之前发送的验证码未过期,则提示发送频繁
throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_SEND_FREQUENTLY);
}
// 生成 6 位随机数字验证码
String verificationCode = RandomUtil.randomNumbers(6);
// todo: 调用第三方短信发送服务
log.info("==> 手机号: {}, 已发送验证码:【{}】", phone, verificationCode);
// 存储验证码到 redis, 并设置过期时间为 3 分钟
redisTemplate.opsForValue().set(key, verificationCode, 3, TimeUnit.MINUTES);
return Response.success();
}
}
@@ -55,9 +55,9 @@ spring:
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
min-idle: 0 # 连接池中的最小空闲连接
max-idle: 10 # 连接池中的最大空闲连接
# output:
# ansi:
# enabled: always
output:
ansi:
enabled: always
application:
name: xiaoyishu-auth