434 lines
11 KiB
Markdown
434 lines
11 KiB
Markdown
# 注册接口优化总结
|
||
|
||
## 🎯 优化目标
|
||
|
||
简化用户注册流程,仅需要手机号、密码和短信验证码即可完成注册。
|
||
|
||
## ✅ 完成的优化
|
||
|
||
### 1. 简化RegisterRequest
|
||
|
||
**文件**: `backend-single/src/main/java/com/emotion/dto/request/RegisterRequest.java`
|
||
|
||
**修改前**:需要账号、密码、确认密码、用户名、昵称、邮箱、手机号、图形验证码等多个字段
|
||
|
||
**修改后**:仅需3个字段
|
||
```java
|
||
@Data
|
||
@EqualsAndHashCode(callSuper = true)
|
||
public class RegisterRequest extends BaseRequest {
|
||
|
||
/**
|
||
* 手机号(作为账号)
|
||
*/
|
||
@NotBlank(message = "手机号不能为空")
|
||
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
||
private String phone;
|
||
|
||
/**
|
||
* 密码
|
||
*/
|
||
@NotBlank(message = "密码不能为空")
|
||
@Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
|
||
private String password;
|
||
|
||
/**
|
||
* 短信验证码
|
||
*/
|
||
@NotBlank(message = "验证码不能为空")
|
||
@Size(min = 6, max = 6, message = "验证码必须为6位")
|
||
private String smsCode;
|
||
}
|
||
```
|
||
|
||
### 2. 新增SmsCodeResponse
|
||
|
||
**文件**: `backend-single/src/main/java/com/emotion/dto/response/SmsCodeResponse.java`
|
||
|
||
```java
|
||
@Data
|
||
@Builder
|
||
@NoArgsConstructor
|
||
@AllArgsConstructor
|
||
public class SmsCodeResponse {
|
||
|
||
/**
|
||
* 验证码(开发环境返回,生产环境不返回)
|
||
*/
|
||
private String code;
|
||
|
||
/**
|
||
* 过期时间(秒)
|
||
*/
|
||
private Long expiresIn;
|
||
|
||
/**
|
||
* 提示信息
|
||
*/
|
||
private String message;
|
||
}
|
||
```
|
||
|
||
### 3. 新增获取短信验证码接口
|
||
|
||
**文件**: `backend-single/src/main/java/com/emotion/controller/AuthController.java`
|
||
|
||
```java
|
||
/**
|
||
* 获取短信验证码(用于注册)
|
||
*/
|
||
@GetMapping("/sms-code")
|
||
@Operation(summary = "获取短信验证码", description = "用于注册时的短信验证码")
|
||
public Result<SmsCodeResponse> getSmsCode(
|
||
@Parameter(description = "手机号", required = true)
|
||
@RequestParam String phone) {
|
||
SmsCodeResponse response = authService.sendSmsCode(phone);
|
||
return Result.success("验证码已发送", response);
|
||
}
|
||
```
|
||
|
||
**接口地址**: `GET /api/auth/sms-code?phone=13800138000`
|
||
|
||
**响应示例**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "验证码已发送",
|
||
"data": {
|
||
"code": "123456",
|
||
"expiresIn": 300,
|
||
"message": "验证码已发送,有效期5分钟"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 优化注册接口逻辑
|
||
|
||
**文件**: `backend-single/src/main/java/com/emotion/service/impl/AuthServiceImpl.java`
|
||
|
||
#### 4.1 新增常量定义
|
||
```java
|
||
private static final String SMS_CODE_PREFIX = "sms_code:";
|
||
private static final int SMS_CODE_EXPIRE_MINUTES = 5;
|
||
private static final String DEFAULT_SMS_CODE = "123456";
|
||
```
|
||
|
||
#### 4.2 简化注册逻辑
|
||
```java
|
||
@Override
|
||
public AuthResponse register(RegisterRequest request) {
|
||
// 1. 验证短信验证码
|
||
if (!validateSmsCode(request.getPhone(), request.getSmsCode())) {
|
||
throw new CaptchaException("验证码错误或已过期");
|
||
}
|
||
|
||
// 2. 检查手机号是否已存在
|
||
if (userService.getByPhone(request.getPhone()) != null) {
|
||
throw new BusinessException("手机号已被注册");
|
||
}
|
||
|
||
// 3. 生成随机用户名:开心 + 6位随机大小写字母和数字
|
||
String username = generateRandomUsername();
|
||
|
||
// 4. 使用手机号作为账号
|
||
String account = request.getPhone();
|
||
|
||
// 5. 创建用户
|
||
User user = userService.createUser(
|
||
account,
|
||
username,
|
||
request.getPassword(),
|
||
null, // 邮箱为空
|
||
request.getPhone()
|
||
);
|
||
|
||
// 6. 生成令牌并返回
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### 4.3 实现短信验证码发送
|
||
```java
|
||
@Override
|
||
public SmsCodeResponse sendSmsCode(String phone) {
|
||
// 验证手机号格式
|
||
if (!StringUtils.hasText(phone) || !phone.matches("^1[3-9]\\d{9}$")) {
|
||
throw new BusinessException("手机号格式不正确");
|
||
}
|
||
|
||
// 检查手机号是否已注册
|
||
if (existsByPhone(phone)) {
|
||
throw new BusinessException("该手机号已被注册");
|
||
}
|
||
|
||
// TODO: 接入真实的短信服务商(阿里云、腾讯云等)
|
||
// 目前使用固定验证码 123456
|
||
String code = DEFAULT_SMS_CODE;
|
||
|
||
// 将验证码存储到Redis,有效期5分钟
|
||
String key = SMS_CODE_PREFIX + phone;
|
||
redisTemplate.opsForValue().set(key, code, SMS_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||
|
||
log.info("发送短信验证码: phone={}, code={}", phone, code);
|
||
|
||
// 构建响应
|
||
return SmsCodeResponse.builder()
|
||
.code(code) // 开发环境返回验证码,生产环境应该删除此行
|
||
.expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60)
|
||
.message("验证码已发送,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟")
|
||
.build();
|
||
}
|
||
```
|
||
|
||
#### 4.4 实现短信验证码校验
|
||
```java
|
||
@Override
|
||
public boolean validateSmsCode(String phone, String code) {
|
||
if (!StringUtils.hasText(phone) || !StringUtils.hasText(code)) {
|
||
return false;
|
||
}
|
||
|
||
String key = SMS_CODE_PREFIX + phone;
|
||
String storedCode = (String) redisTemplate.opsForValue().get(key);
|
||
|
||
if (storedCode == null) {
|
||
log.warn("短信验证码不存在或已过期: phone={}", phone);
|
||
return false;
|
||
}
|
||
|
||
boolean isValid = storedCode.equals(code);
|
||
|
||
// 验证成功后删除验证码(一次性使用)
|
||
if (isValid) {
|
||
redisTemplate.delete(key);
|
||
log.info("短信验证码验证成功: phone={}", phone);
|
||
} else {
|
||
log.warn("短信验证码错误: phone={}, expected={}, actual={}", phone, storedCode, code);
|
||
}
|
||
|
||
return isValid;
|
||
}
|
||
```
|
||
|
||
#### 4.5 实现随机用户名生成
|
||
```java
|
||
/**
|
||
* 生成随机用户名
|
||
* 格式:开心 + 6位随机大小写字母和数字
|
||
*
|
||
* @return 随机用户名
|
||
*/
|
||
private String generateRandomUsername() {
|
||
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||
Random random = new Random();
|
||
StringBuilder sb = new StringBuilder("开心");
|
||
|
||
for (int i = 0; i < 6; i++) {
|
||
int index = random.nextInt(chars.length());
|
||
sb.append(chars.charAt(index));
|
||
}
|
||
|
||
return sb.toString();
|
||
}
|
||
```
|
||
|
||
**用户名示例**:
|
||
- 开心A3bC9z
|
||
- 开心X7yK2m
|
||
- 开心P1qW5n
|
||
|
||
### 5. 更新AuthService接口
|
||
|
||
**文件**: `backend-single/src/main/java/com/emotion/service/AuthService.java`
|
||
|
||
新增方法定义:
|
||
```java
|
||
/**
|
||
* 发送短信验证码
|
||
*
|
||
* @param phone 手机号
|
||
* @return 短信验证码响应
|
||
*/
|
||
SmsCodeResponse sendSmsCode(String phone);
|
||
|
||
/**
|
||
* 验证短信验证码
|
||
*
|
||
* @param phone 手机号
|
||
* @param code 验证码
|
||
* @return 是否验证成功
|
||
*/
|
||
boolean validateSmsCode(String phone, String code);
|
||
```
|
||
|
||
## 📊 优化对比
|
||
|
||
| 项目 | 优化前 | 优化后 |
|
||
|------|--------|--------|
|
||
| 必填字段 | 账号、密码、确认密码、验证码、验证码key | 手机号、密码、短信验证码 |
|
||
| 验证码类型 | 图形验证码 | 短信验证码 |
|
||
| 用户名生成 | 手动输入或使用账号 | 自动生成(开心+6位随机) |
|
||
| 账号 | 手动输入 | 自动使用手机号 |
|
||
| 邮箱 | 可选输入 | 不需要 |
|
||
| 昵称 | 可选输入 | 自动使用用户名 |
|
||
|
||
## 🔧 核心特性
|
||
|
||
### 1. 简化注册流程
|
||
- ✅ 仅需3个字段:手机号、密码、短信验证码
|
||
- ✅ 自动生成用户名:开心 + 6位随机字符
|
||
- ✅ 手机号作为账号
|
||
- ✅ 移除了确认密码、邮箱、昵称等非必要字段
|
||
|
||
### 2. 短信验证码机制
|
||
- ✅ 默认验证码:123456(开发环境)
|
||
- ✅ 有效期:5分钟
|
||
- ✅ 一次性使用:验证成功后自动删除
|
||
- ✅ Redis存储:支持分布式部署
|
||
- ✅ TODO标记:预留真实短信服务接入点
|
||
|
||
### 3. 安全性保障
|
||
- ✅ 手机号格式验证
|
||
- ✅ 手机号唯一性检查
|
||
- ✅ 验证码过期检查
|
||
- ✅ 验证码一次性使用
|
||
- ✅ 密码加密存储
|
||
|
||
### 4. 用户体验优化
|
||
- ✅ 注册流程简单快捷
|
||
- ✅ 自动生成友好的用户名
|
||
- ✅ 清晰的错误提示
|
||
- ✅ 完整的API文档(Swagger)
|
||
|
||
## 🚀 使用示例
|
||
|
||
### 1. 获取短信验证码
|
||
|
||
**请求**:
|
||
```bash
|
||
curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13800138000"
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "验证码已发送",
|
||
"data": {
|
||
"code": "123456",
|
||
"expiresIn": 300,
|
||
"message": "验证码已发送,有效期5分钟"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 用户注册
|
||
|
||
**请求**:
|
||
```bash
|
||
curl -X POST "http://localhost:19089/api/auth/register" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"phone": "13800138000",
|
||
"password": "password123",
|
||
"smsCode": "123456"
|
||
}'
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "注册成功",
|
||
"data": {
|
||
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"expiresIn": 86400,
|
||
"userInfo": {
|
||
"id": "1844234567890123456",
|
||
"account": "13800138000",
|
||
"username": "开心A3bC9z",
|
||
"nickname": "开心A3bC9z",
|
||
"phone": "13800138000",
|
||
"memberLevel": "free",
|
||
"status": 1
|
||
},
|
||
"loginTime": "2025-10-06 21:49:33"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 📝 TODO事项
|
||
|
||
### 1. 接入真实短信服务
|
||
|
||
**位置**: `AuthServiceImpl.sendSmsCode()` 方法
|
||
|
||
**当前代码**:
|
||
```java
|
||
// TODO: 接入真实的短信服务商(阿里云、腾讯云等)
|
||
// 目前使用固定验证码 123456
|
||
String code = DEFAULT_SMS_CODE;
|
||
```
|
||
|
||
**后续改进**:
|
||
```java
|
||
// 生成随机6位数字验证码
|
||
String code = String.format("%06d", new Random().nextInt(1000000));
|
||
|
||
// 调用短信服务商API发送验证码
|
||
// 示例:阿里云短信服务
|
||
// smsService.sendSms(phone, code);
|
||
```
|
||
|
||
### 2. 生产环境优化
|
||
|
||
**需要修改的地方**:
|
||
1. **移除响应中的验证码**:
|
||
```java
|
||
return SmsCodeResponse.builder()
|
||
// .code(code) // 生产环境删除此行
|
||
.expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60)
|
||
.message("验证码已发送,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟")
|
||
.build();
|
||
```
|
||
|
||
2. **添加发送频率限制**:
|
||
- 同一手机号60秒内只能发送一次
|
||
- 同一IP每天最多发送10次
|
||
|
||
3. **添加验证码尝试次数限制**:
|
||
- 同一手机号最多尝试5次
|
||
- 超过次数后需要重新获取验证码
|
||
|
||
## ✅ 验证清单
|
||
|
||
- [x] 编译成功,无错误
|
||
- [x] RegisterRequest简化为3个字段
|
||
- [x] 新增SmsCodeResponse响应类
|
||
- [x] 新增获取短信验证码接口
|
||
- [x] 实现短信验证码发送逻辑
|
||
- [x] 实现短信验证码校验逻辑
|
||
- [x] 实现随机用户名生成
|
||
- [x] 优化注册接口逻辑
|
||
- [x] 添加Swagger API文档注解
|
||
- [x] 添加详细的日志记录
|
||
|
||
## 🎉 总结
|
||
|
||
成功优化了注册接口,实现了以下目标:
|
||
|
||
1. ✅ **简化注册流程** - 仅需手机号、密码和短信验证码
|
||
2. ✅ **自动生成用户名** - 开心 + 6位随机大小写字母和数字
|
||
3. ✅ **短信验证码机制** - 默认123456,预留真实短信服务接入点
|
||
4. ✅ **完整的校验逻辑** - 手机号格式、唯一性、验证码有效性
|
||
5. ✅ **良好的代码质量** - 清晰的注释、完善的日志、标准的异常处理
|
||
|
||
现在用户可以通过简单的3步完成注册:
|
||
1. 输入手机号获取验证码
|
||
2. 输入密码和验证码
|
||
3. 自动生成用户名并完成注册
|
||
|
||
注册成功后自动登录,返回访问令牌和用户信息!
|
||
|