# 注册功能说明文档

## 功能概述

系统支持三种注册方式：
1. **用户名注册** - 直接使用用户名+密码注册
2. **手机号注册** - 使用手机号+验证码+密码注册
3. **邮箱注册** - 使用邮箱+验证码+密码注册

## API接口

### 接口地址
```
POST /auth/register
```

### 请求参数

#### 通用参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| registerType | String | 是 | 注册类型：username/phone/email |
| password | String | 是 | 密码（至少6个字符） |
| confirmPassword | String | 是 | 确认密码 |
| studentId | String | 否 | 学号 |

#### 用户名注册 (registerType=username)
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| username | String | 是 | 用户名（3-20字符，仅字母数字下划线） |

#### 手机号注册 (registerType=phone)
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| phone | String | 是 | 手机号（11位中国手机号） |
| smsCode | String | 是 | 手机验证码（6位数字） |

#### 邮箱注册 (registerType=email)
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| email | String | 是 | 邮箱地址 |
| emailCode | String | 是 | 邮箱验证码（6位数字） |

### 请求示例

#### 1. 用户名注册
```json
{
  "registerType": "username",
  "username": "zhangsan",
  "password": "123456",
  "confirmPassword": "123456",
  "studentId": "2021001"
}
```

#### 2. 手机号注册
```json
{
  "registerType": "phone",
  "phone": "13800138000",
  "smsCode": "123456",
  "password": "123456",
  "confirmPassword": "123456",
  "studentId": "2021001"
}
```

#### 3. 邮箱注册
```json
{
  "registerType": "email",
  "email": "user@example.com",
  "emailCode": "123456",
  "password": "123456",
  "confirmPassword": "123456",
  "studentId": "2021001"
}
```

### 响应结果

#### 注册成功
```json
{
  "code": 200,
  "message": "注册成功",
  "data": {
    "user": {
      "ID": "xxx",
      "username": "zhangsan",
      "email": "user@example.com",
      "phone": "13800138000",
      "status": 0
    },
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "loginTime": "2025-11-03T10:30:00",
    "ipAddress": "127.0.0.1"
  }
}
```

#### 注册失败
```json
{
  "code": 500,
  "message": "用户已存在",
  "data": null
}
```

## 业务流程

### 1. 用户名注册流程
```
用户输入用户名和密码
    ↓
验证用户名格式（3-20字符，字母数字下划线）
    ↓
验证密码（至少6字符，两次输入一致）
    ↓
检查用户名是否已存在
    ↓
密码加密（BCrypt）
    ↓
创建用户账号
    ↓
自动登录并返回token
```

### 2. 手机号注册流程
```
用户输入手机号
    ↓
点击"获取验证码"按钮
    ↓
系统发送短信验证码（60秒限制）
    ↓
用户输入验证码和密码
    ↓
验证手机号格式
    ↓
验证验证码（10分钟有效期）
    ↓
检查手机号是否已注册
    ↓
密码加密（BCrypt）
    ↓
创建用户账号（用户名=手机号）
    ↓
删除验证码（防止重复使用）
    ↓
自动登录并返回token
```

### 3. 邮箱注册流程
```
用户输入邮箱
    ↓
点击"获取验证码"按钮
    ↓
系统发送邮件验证码（60秒限制）
    ↓
用户输入验证码和密码
    ↓
验证邮箱格式
    ↓
验证验证码（10分钟有效期）
    ↓
检查邮箱是否已注册
    ↓
密码加密（BCrypt）
    ↓
创建用户账号（用户名=邮箱前缀）
    ↓
删除验证码（防止重复使用）
    ↓
自动登录并返回token
```

## 安全机制

### 1. 密码加密
- 使用 BCryptPasswordEncoder 进行密码加密
- 不可逆加密，安全性高
- 每次加密结果不同（内置salt）

### 2. 验证码机制
- **有效期**：10分钟
- **发送频率**：60秒只能发送一次
- **一次性使用**：验证后立即删除
- **存储位置**：Redis
  - 手机验证码：`sms:code:手机号`
  - 邮箱验证码：`email:code:邮箱`

### 3. 用户唯一性校验
通过UserService.registerUser内部实现：
- 检查用户名是否重复
- 检查手机号是否重复
- 检查邮箱是否重复

### 4. 注册后自动登录
- 注册成功后自动调用登录接口
- 返回完整的登录信息（token、用户信息等）
- 用户无需再次输入密码

## 数据库设计

### 用户表 (tb_sys_user)
| 字段 | 类型 | 说明 |
|------|------|------|
| ID | VARCHAR | 用户ID（主键） |
| username | VARCHAR | 用户名 |
| password | VARCHAR | 加密后的密码 |
| email | VARCHAR | 邮箱 |
| phone | VARCHAR | 手机号 |
| wechatID | VARCHAR | 微信号 |
| status | INT | 用户状态（0-正常，1-禁用，2-锁定） |
| createTime | DATETIME | 创建时间 |
| updateTime | DATETIME | 更新时间 |
| deleted | BOOLEAN | 是否删除 |

### 用户信息表 (tb_sys_user_info)
- 与用户表一对一关系
- 存储用户详细信息（头像、性别、姓名等）
- 注册时自动创建默认记录

## 错误码说明

| 错误码 | 错误信息 | 说明 |
|--------|----------|------|
| 500 | 密码不能为空 | 密码字段为空 |
| 500 | 密码至少6个字符 | 密码长度不足 |
| 500 | 两次输入的密码不一致 | 密码和确认密码不匹配 |
| 500 | 用户名不能为空 | 用户名注册时用户名为空 |
| 500 | 用户名长度为3-20个字符 | 用户名长度不符合要求 |
| 500 | 用户名只能包含字母、数字和下划线 | 用户名包含非法字符 |
| 500 | 手机号不能为空 | 手机号注册时手机号为空 |
| 500 | 手机号格式不正确 | 手机号格式错误 |
| 500 | 请输入手机验证码 | 手机验证码为空 |
| 500 | 验证码已过期，请重新获取 | 验证码不存在或已过期 |
| 500 | 验证码错误 | 验证码不正确 |
| 500 | 邮箱不能为空 | 邮箱注册时邮箱为空 |
| 500 | 邮箱格式不正确 | 邮箱格式错误 |
| 500 | 请输入邮箱验证码 | 邮箱验证码为空 |
| 500 | 用户已存在 | 用户名/手机号/邮箱已被注册 |
| 500 | 未知的注册类型 | registerType参数错误 |

## 前端集成示例

### 1. 用户名注册
```typescript
import { authApi } from '@/apis/system/auth';

const handleRegister = async () => {
  const result = await authApi.register({
    registerType: 'username',
    username: 'zhangsan',
    password: '123456',
    confirmPassword: '123456',
    agree: true
  });
  
  if (result.code === 200) {
    // 注册成功，保存token
    localStorage.setItem('token', result.data.token);
    // 跳转到首页
    router.push('/');
  }
};
```

### 2. 手机号注册
```typescript
// 发送验证码
const sendSmsCode = async () => {
  const result = await authApi.sendSmsCode('13800138000');
  if (result.code === 200) {
    ElMessage.success('验证码已发送');
    // 开始倒计时
    startCountdown();
  }
};

// 注册
const handleRegister = async () => {
  const result = await authApi.register({
    registerType: 'phone',
    phone: '13800138000',
    smsCode: '123456',
    password: '123456',
    confirmPassword: '123456',
    agree: true
  });
  
  if (result.code === 200) {
    ElMessage.success('注册成功');
    // 保存token并跳转
  }
};
```

### 3. 邮箱注册
```typescript
// 发送验证码
const sendEmailCode = async () => {
  const result = await authApi.sendEmailCode('user@example.com');
  if (result.code === 200) {
    ElMessage.success('验证码已发送到邮箱');
    // 开始倒计时
    startCountdown();
  }
};

// 注册
const handleRegister = async () => {
  const result = await authApi.register({
    registerType: 'email',
    email: 'user@example.com',
    emailCode: '123456',
    password: '123456',
    confirmPassword: '123456',
    agree: true
  });
  
  if (result.code === 200) {
    ElMessage.success('注册成功');
    // 保存token并跳转
  }
};
```

## 测试用例

### 1. 用户名注册测试
```bash
# 正常注册
curl -X POST http://localhost:8081/schoolNewsServ/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "registerType": "username",
    "username": "testuser",
    "password": "123456",
    "confirmPassword": "123456"
  }'

# 用户名重复
curl -X POST http://localhost:8081/schoolNewsServ/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "registerType": "username",
    "username": "testuser",
    "password": "123456",
    "confirmPassword": "123456"
  }'
# 预期：返回"用户已存在"

# 用户名过短
curl -X POST http://localhost:8081/schoolNewsServ/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "registerType": "username",
    "username": "ab",
    "password": "123456",
    "confirmPassword": "123456"
  }'
# 预期：返回"用户名长度为3-20个字符"
```

### 2. 手机号注册测试
```bash
# 先发送验证码
curl -X POST http://localhost:8081/schoolNewsServ/auth/send-sms-code \
  -H "Content-Type: application/json" \
  -d '{"phone": "13800138000"}'

# 然后注册（使用日志中的验证码）
curl -X POST http://localhost:8081/schoolNewsServ/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "registerType": "phone",
    "phone": "13800138000",
    "smsCode": "123456",
    "password": "123456",
    "confirmPassword": "123456"
  }'
```

### 3. 邮箱注册测试
```bash
# 先发送验证码
curl -X POST http://localhost:8081/schoolNewsServ/auth/send-email-code \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com"}'

# 然后注册（检查邮箱获取验证码）
curl -X POST http://localhost:8081/schoolNewsServ/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "registerType": "email",
    "email": "test@example.com",
    "emailCode": "123456",
    "password": "123456",
    "confirmPassword": "123456"
  }'
```

## 注意事项

1. **验证码有效期**：验证码10分钟后失效，需要重新获取
2. **发送频率限制**：同一手机号/邮箱60秒内只能发送一次验证码
3. **密码安全**：密码使用BCrypt加密存储，不可逆
4. **自动登录**：注册成功后自动登录，前端需要保存返回的token
5. **用户名规则**：
   - 用户名注册：用户自定义
   - 手机号注册：用户名=手机号
   - 邮箱注册：用户名=邮箱前缀
6. **开发测试**：短信服务使用模拟模式时，验证码会在日志中输出

## 后续优化建议

1. 添加图形验证码，防止恶意发送验证码
2. 添加IP限流，防止批量注册
3. 添加邀请码机制
4. 实现手机号和邮箱的绑定功能
5. 添加密码强度验证
6. 实现找回密码功能
7. 添加注册协议弹窗确认
8. 记录注册日志用于分析

