305 lines
7.0 KiB
Markdown
305 lines
7.0 KiB
Markdown
# 积分冻结系统
|
||
|
||
## 概述
|
||
|
||
积分冻结系统实现了任务提交时冻结积分、任务完成时扣除、任务失败时返还的完整积分管理流程。
|
||
|
||
## 系统特性
|
||
|
||
### 🔒 **积分状态管理**
|
||
- **正常积分**: 用户可以自由使用的积分
|
||
- **冻结积分**: 任务提交时临时冻结的积分,不可使用
|
||
- **可用积分**: 正常积分 - 冻结积分
|
||
|
||
### 📋 **冻结记录管理**
|
||
- 完整的积分冻结记录追踪
|
||
- 支持多种冻结状态:已冻结、已扣除、已返还、已过期
|
||
- 自动处理过期冻结记录(24小时)
|
||
|
||
### ⚡ **自动积分处理**
|
||
- **任务提交**: 自动冻结相应积分
|
||
- **任务完成**: 自动扣除冻结积分
|
||
- **任务失败**: 自动返还冻结积分
|
||
- **任务取消**: 自动返还冻结积分
|
||
- **任务超时**: 自动返还冻结积分
|
||
|
||
## 数据库结构
|
||
|
||
### 用户表修改
|
||
```sql
|
||
-- 添加冻结积分字段
|
||
ALTER TABLE users ADD COLUMN frozen_points INT NOT NULL DEFAULT 0 COMMENT '冻结积分';
|
||
```
|
||
|
||
### 积分冻结记录表
|
||
```sql
|
||
CREATE TABLE points_freeze_records (
|
||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||
username VARCHAR(100) NOT NULL COMMENT '用户名',
|
||
task_id VARCHAR(50) NOT NULL UNIQUE COMMENT '任务ID',
|
||
task_type ENUM('TEXT_TO_VIDEO', 'IMAGE_TO_VIDEO') NOT NULL COMMENT '任务类型',
|
||
freeze_points INT NOT NULL COMMENT '冻结的积分数量',
|
||
status ENUM('FROZEN', 'DEDUCTED', 'RETURNED', 'EXPIRED') NOT NULL DEFAULT 'FROZEN' COMMENT '冻结状态',
|
||
freeze_reason VARCHAR(200) COMMENT '冻结原因',
|
||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||
completed_at DATETIME COMMENT '完成时间'
|
||
);
|
||
```
|
||
|
||
## API接口
|
||
|
||
### 获取积分信息
|
||
```
|
||
GET /api/points/info
|
||
Authorization: Bearer <token>
|
||
|
||
Response:
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"totalPoints": 1000,
|
||
"frozenPoints": 80,
|
||
"availablePoints": 920
|
||
}
|
||
}
|
||
```
|
||
|
||
### 获取冻结记录
|
||
```
|
||
GET /api/points/freeze-records
|
||
Authorization: Bearer <token>
|
||
|
||
Response:
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": 1,
|
||
"username": "user1",
|
||
"taskId": "txt2vid_123",
|
||
"taskType": "TEXT_TO_VIDEO",
|
||
"freezePoints": 80,
|
||
"status": "FROZEN",
|
||
"freezeReason": "任务提交冻结积分 - 文生视频",
|
||
"createdAt": "2024-01-01T10:00:00",
|
||
"updatedAt": "2024-01-01T10:00:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 处理过期记录(管理员)
|
||
```
|
||
POST /api/points/process-expired
|
||
Authorization: Bearer <token>
|
||
|
||
Response:
|
||
{
|
||
"success": true,
|
||
"message": "处理过期记录完成",
|
||
"processedCount": 5
|
||
}
|
||
```
|
||
|
||
## 服务方法
|
||
|
||
### UserService 积分冻结方法
|
||
|
||
#### 冻结积分
|
||
```java
|
||
PointsFreezeRecord freezePoints(String username, String taskId,
|
||
PointsFreezeRecord.TaskType taskType, Integer points, String reason)
|
||
```
|
||
|
||
#### 扣除冻结积分(任务完成)
|
||
```java
|
||
void deductFrozenPoints(String taskId)
|
||
```
|
||
|
||
#### 返还冻结积分(任务失败)
|
||
```java
|
||
void returnFrozenPoints(String taskId)
|
||
```
|
||
|
||
#### 获取可用积分
|
||
```java
|
||
Integer getAvailablePoints(String username)
|
||
```
|
||
|
||
#### 获取冻结积分
|
||
```java
|
||
Integer getFrozenPoints(String username)
|
||
```
|
||
|
||
#### 获取冻结记录
|
||
```java
|
||
List<PointsFreezeRecord> getPointsFreezeRecords(String username)
|
||
```
|
||
|
||
#### 处理过期记录
|
||
```java
|
||
int processExpiredFrozenRecords()
|
||
```
|
||
|
||
## 积分计算规则
|
||
|
||
### 默认积分消耗
|
||
- **文生视频**: 80积分
|
||
- **图生视频**: 90积分
|
||
|
||
### 积分检查逻辑
|
||
1. 检查用户可用积分是否足够
|
||
2. 冻结相应积分
|
||
3. 创建冻结记录
|
||
4. 添加到任务队列
|
||
|
||
## 定时任务
|
||
|
||
### 过期记录处理
|
||
- **频率**: 每小时执行一次
|
||
- **功能**: 自动处理超过24小时的冻结记录
|
||
- **处理方式**: 返还冻结积分,更新记录状态为"已过期"
|
||
|
||
## 工作流程
|
||
|
||
### 1. 任务提交流程
|
||
```
|
||
用户提交任务 → 检查可用积分 → 冻结积分 → 创建冻结记录 → 添加到队列
|
||
```
|
||
|
||
### 2. 任务完成流程
|
||
```
|
||
任务完成 → 扣除冻结积分 → 更新冻结记录状态 → 更新原始任务状态
|
||
```
|
||
|
||
### 3. 任务失败流程
|
||
```
|
||
任务失败 → 返还冻结积分 → 更新冻结记录状态 → 更新原始任务状态
|
||
```
|
||
|
||
### 4. 任务取消流程
|
||
```
|
||
用户取消任务 → 返还冻结积分 → 更新冻结记录状态 → 更新原始任务状态
|
||
```
|
||
|
||
## 异常处理
|
||
|
||
### 积分不足
|
||
- 检查可用积分时如果不足,抛出异常
|
||
- 异常信息包含当前可用积分和所需积分
|
||
|
||
### 冻结记录不存在
|
||
- 扣除或返还积分时如果记录不存在,抛出异常
|
||
- 确保数据一致性
|
||
|
||
### 状态不正确
|
||
- 操作冻结记录时检查状态是否正确
|
||
- 防止重复操作
|
||
|
||
## 监控和日志
|
||
|
||
### 关键日志
|
||
- 积分冻结成功/失败
|
||
- 积分扣除成功/失败
|
||
- 积分返还成功/失败
|
||
- 过期记录处理
|
||
|
||
### 监控指标
|
||
- 冻结积分总量
|
||
- 过期记录数量
|
||
- 积分操作成功率
|
||
|
||
## 安全考虑
|
||
|
||
### 数据一致性
|
||
- 使用事务确保积分和记录状态一致
|
||
- 防止并发操作导致的数据不一致
|
||
|
||
### 权限控制
|
||
- 只有任务所有者可以操作相关积分
|
||
- 管理员可以处理过期记录
|
||
|
||
### 异常恢复
|
||
- 完善的异常处理机制
|
||
- 自动重试和恢复机制
|
||
|
||
## 扩展功能
|
||
|
||
### 未来可扩展的功能
|
||
1. **动态积分计算**: 根据任务参数动态计算所需积分
|
||
2. **积分优惠**: 会员等级影响积分消耗
|
||
3. **积分返还策略**: 不同失败原因的不同返还策略
|
||
4. **积分统计**: 详细的积分使用统计和分析
|
||
5. **积分预警**: 积分不足时的预警机制
|
||
|
||
## 使用示例
|
||
|
||
### 前端集成示例
|
||
```javascript
|
||
// 获取用户积分信息
|
||
const getPointsInfo = async () => {
|
||
const response = await fetch('/api/points/info', {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
});
|
||
const data = await response.json();
|
||
return data.data;
|
||
};
|
||
|
||
// 获取冻结记录
|
||
const getFreezeRecords = async () => {
|
||
const response = await fetch('/api/points/freeze-records', {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`
|
||
}
|
||
});
|
||
const data = await response.json();
|
||
return data.data;
|
||
};
|
||
```
|
||
|
||
### 后端集成示例
|
||
```java
|
||
// 在任务创建时自动冻结积分
|
||
@Transactional
|
||
public TaskQueue addTextToVideoTask(String username, String taskId) {
|
||
// 计算所需积分
|
||
Integer requiredPoints = calculateRequiredPoints(TaskQueue.TaskType.TEXT_TO_VIDEO);
|
||
|
||
// 冻结积分
|
||
userService.freezePoints(username, taskId,
|
||
PointsFreezeRecord.TaskType.TEXT_TO_VIDEO, requiredPoints,
|
||
"任务提交冻结积分 - 文生视频");
|
||
|
||
// 添加到队列
|
||
return addTaskToQueue(username, taskId, TaskQueue.TaskType.TEXT_TO_VIDEO);
|
||
}
|
||
```
|
||
|
||
## 注意事项
|
||
|
||
1. **积分检查**: 在冻结积分前必须检查可用积分是否足够
|
||
2. **状态管理**: 确保冻结记录状态与任务状态保持一致
|
||
3. **异常处理**: 完善的异常处理确保积分不会丢失
|
||
4. **定时清理**: 定期清理过期的冻结记录
|
||
5. **监控告警**: 监控积分冻结系统的运行状态
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|