356 lines
8.8 KiB
Markdown
356 lines
8.8 KiB
Markdown
# 卡密商业化系统实现总结
|
||
|
||
## 项目概述
|
||
|
||
成功为 Task 任务管理模块添加了完整的卡密商业化授权系统。用户需要激活有效的卡密才能使用任务管理功能。
|
||
|
||
## 实现的功能
|
||
|
||
### 1. 核心功能
|
||
- ✅ 卡密生成(批量、唯一性保证)
|
||
- ✅ 卡密激活(自动验证、防重复)
|
||
- ✅ 授权延期(多卡密叠加)
|
||
- ✅ 授权验证(守卫拦截)
|
||
- ✅ 授权查询(剩余天数)
|
||
- ✅ 卡密撤销
|
||
- ✅ 统计分析
|
||
|
||
### 2. 卡密类型
|
||
- 试用版 (7天)
|
||
- 月度订阅 (30天)
|
||
- 年度订阅 (365天)
|
||
- 终身授权 (100年)
|
||
|
||
### 3. 安全机制
|
||
- 卡密格式: `XXXX-XXXX-XXXX-XXXX`
|
||
- 随机生成算法(去除易混淆字符)
|
||
- 全局唯一性检查
|
||
- 状态机管理(unused → active → expired/revoked)
|
||
- 守卫层面统一拦截
|
||
|
||
## 文件结构
|
||
|
||
```
|
||
src/
|
||
├── license/
|
||
│ ├── entities/
|
||
│ │ └── license.entity.ts # 卡密实体
|
||
│ ├── dto/
|
||
│ │ ├── generate-license.dto.ts # 生成卡密 DTO
|
||
│ │ ├── activate-license.dto.ts # 激活卡密 DTO
|
||
│ │ └── query-license.dto.ts # 查询卡密 DTO
|
||
│ ├── decorators/
|
||
│ │ └── require-license.decorator.ts # 授权装饰器
|
||
│ ├── guards/
|
||
│ │ └── license.guard.ts # 授权守卫
|
||
│ ├── license.service.ts # 卡密服务
|
||
│ ├── license.controller.ts # 卡密控制器
|
||
│ └── license.module.ts # 卡密模块
|
||
│
|
||
├── task/
|
||
│ └── task.controller.ts # 已添加卡密验证
|
||
│
|
||
├── common/
|
||
│ └── dto/
|
||
│ └── error-response.dto.ts # 统一错误响应
|
||
│
|
||
└── app.module.ts # 注册 License Module
|
||
```
|
||
|
||
## API 接口
|
||
|
||
### 用户接口
|
||
|
||
| 接口 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/license/activate` | POST | 激活卡密 |
|
||
| `/license/my` | GET | 查询我的授权 |
|
||
| `/license/my/history` | GET | 查询卡密历史 |
|
||
|
||
### 管理员接口
|
||
|
||
| 接口 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/license/generate` | POST | 生成卡密 |
|
||
| `/license` | GET | 查询所有卡密 |
|
||
| `/license/statistics` | GET | 获取统计信息 |
|
||
| `/license/:id` | GET | 查询单个卡密 |
|
||
| `/license/:id/revoke` | POST | 撤销卡密 |
|
||
| `/license/:id` | DELETE | 删除卡密 |
|
||
|
||
### 受保护的接口
|
||
|
||
所有 `/task/*` 接口都需要有效授权:
|
||
- `POST /task` - 创建任务
|
||
- `GET /task` - 获取任务列表
|
||
- `GET /task/:id` - 获取单个任务
|
||
- `PATCH /task/:id` - 更新任务
|
||
- `PATCH /task/reorder` - 重新排序
|
||
- `DELETE /task/:id` - 删除任务
|
||
|
||
## 数据库变更
|
||
|
||
### 新增表: license
|
||
|
||
```sql
|
||
CREATE TABLE `license` (
|
||
`id` INT PRIMARY KEY AUTO_INCREMENT,
|
||
`code` VARCHAR(32) UNIQUE NOT NULL,
|
||
`type` ENUM('trial', 'monthly', 'yearly', 'lifetime') DEFAULT 'monthly',
|
||
`status` ENUM('unused', 'active', 'expired', 'revoked') DEFAULT 'unused',
|
||
`validDays` INT DEFAULT 30,
|
||
`activatedAt` DATETIME NULL,
|
||
`expiresAt` DATETIME NULL,
|
||
`userId` INT NULL,
|
||
`remarks` TEXT NULL,
|
||
`createdAt` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||
`updatedAt` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
FOREIGN KEY (`userId`) REFERENCES `user`(`id`)
|
||
);
|
||
```
|
||
|
||
## 使用流程
|
||
|
||
### 管理员生成卡密
|
||
|
||
```bash
|
||
curl -X POST http://localhost:3030/license/generate \
|
||
-H "Authorization: Bearer {admin_token}" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"type": "monthly",
|
||
"validDays": 30,
|
||
"count": 10,
|
||
"remarks": "批量生成月卡"
|
||
}'
|
||
```
|
||
|
||
### 用户激活卡密
|
||
|
||
```bash
|
||
curl -X POST http://localhost:3030/license/activate \
|
||
-H "Authorization: Bearer {user_token}" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"code": "ABCD-1234-EFGH-5678"
|
||
}'
|
||
```
|
||
|
||
### 用户查询授权
|
||
|
||
```bash
|
||
curl -X GET http://localhost:3030/license/my \
|
||
-H "Authorization: Bearer {user_token}"
|
||
```
|
||
|
||
### 使用任务功能
|
||
|
||
```bash
|
||
curl -X POST http://localhost:3030/task \
|
||
-H "Authorization: Bearer {user_token}" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"title": "完成项目文档",
|
||
"description": "编写 API 文档",
|
||
"priority": "high"
|
||
}'
|
||
```
|
||
|
||
## 测试
|
||
|
||
### 1. 运行测试脚本
|
||
|
||
```bash
|
||
bash test-license-system.sh
|
||
```
|
||
|
||
### 2. 手动测试步骤
|
||
|
||
1. 启动项目: `npm run start:dev`
|
||
2. 访问 Swagger: `http://localhost:3030/api`
|
||
3. 注册用户
|
||
4. 生成卡密(管理员)
|
||
5. 激活卡密
|
||
6. 测试任务接口
|
||
|
||
### 3. 测试场景
|
||
|
||
- ✅ 未授权访问任务接口 → 返回 403
|
||
- ✅ 激活卡密后访问 → 正常使用
|
||
- ✅ 重复激活同一卡密 → 返回 409
|
||
- ✅ 激活第二个卡密 → 自动延期
|
||
- ✅ 授权过期后访问 → 返回 403
|
||
- ✅ 查询剩余天数 → 正确显示
|
||
- ✅ 撤销卡密 → 无法再使用
|
||
|
||
## Swagger 文档
|
||
|
||
所有接口都已添加完整的 Swagger 注释,包括:
|
||
- 接口描述
|
||
- 请求参数说明
|
||
- 响应示例
|
||
- 错误码说明
|
||
|
||
访问地址: `http://localhost:3030/api`
|
||
|
||
## 技术亮点
|
||
|
||
### 1. 安全性
|
||
- 卡密随机生成,去除易混淆字符
|
||
- 全局唯一性保证
|
||
- 状态机防止重复激活
|
||
- 守卫层面统一验证
|
||
|
||
### 2. 用户体验
|
||
- 支持授权延期(多卡密叠加)
|
||
- 清晰的错误提示
|
||
- 剩余天数实时查询
|
||
- 授权历史记录
|
||
|
||
### 3. 可维护性
|
||
- 模块化设计
|
||
- 装饰器 + 守卫模式
|
||
- 完整的 TypeScript 类型
|
||
- 详细的代码注释
|
||
|
||
### 4. 可扩展性
|
||
- 支持多种卡密类型
|
||
- 可配置有效天数
|
||
- 易于添加新的授权模块
|
||
- 支持批量生成
|
||
|
||
## 后续优化建议
|
||
|
||
### 功能扩展
|
||
1. 支持不同模块的独立授权
|
||
2. 添加授权转让功能
|
||
3. 实现自动续费机制
|
||
4. 添加授权即将过期提醒
|
||
5. 支持授权使用日志
|
||
|
||
### 性能优化
|
||
1. 卡密验证结果缓存(Redis)
|
||
2. 批量生成优化
|
||
3. 数据库索引优化
|
||
4. 添加分页查询
|
||
|
||
### 安全增强
|
||
1. 添加 IP 白名单限制
|
||
2. 实现设备绑定
|
||
3. 添加异常登录检测
|
||
4. 卡密使用频率限制
|
||
|
||
## 前端集成建议
|
||
|
||
### 1. 授权状态管理
|
||
|
||
```typescript
|
||
// 存储授权信息
|
||
interface LicenseState {
|
||
hasValidLicense: boolean;
|
||
remainingDays: number;
|
||
expiresAt: string;
|
||
type: string;
|
||
}
|
||
|
||
// 在应用启动时查询
|
||
const checkLicense = async () => {
|
||
const response = await api.get('/license/my');
|
||
return response.data;
|
||
};
|
||
```
|
||
|
||
### 2. 路由守卫
|
||
|
||
```typescript
|
||
// 需要授权的路由添加守卫
|
||
const ProtectedRoute = ({ children }) => {
|
||
const { hasValidLicense } = useLicense();
|
||
|
||
if (!hasValidLicense) {
|
||
return <Navigate to="/activate" />;
|
||
}
|
||
|
||
return children;
|
||
};
|
||
```
|
||
|
||
### 3. 激活页面
|
||
|
||
```typescript
|
||
const ActivatePage = () => {
|
||
const [code, setCode] = useState('');
|
||
|
||
const handleActivate = async () => {
|
||
try {
|
||
const response = await api.post('/license/activate', { code });
|
||
toast.success(response.data.message);
|
||
navigate('/tasks');
|
||
} catch (error) {
|
||
toast.error(error.response.data.message);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div>
|
||
<input
|
||
value={code}
|
||
onChange={e => setCode(e.target.value)}
|
||
placeholder="输入卡密: XXXX-XXXX-XXXX-XXXX"
|
||
/>
|
||
<button onClick={handleActivate}>激活</button>
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
### 4. 授权状态显示
|
||
|
||
```typescript
|
||
const LicenseStatus = () => {
|
||
const { license, remainingDays } = useLicense();
|
||
|
||
return (
|
||
<div>
|
||
<p>授权类型: {license.type}</p>
|
||
<p>剩余天数: {remainingDays} 天</p>
|
||
<p>到期时间: {new Date(license.expiresAt).toLocaleDateString()}</p>
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### Q: 如何生成第一个管理员卡密?
|
||
A: 可以直接在数据库中插入一条记录,或者临时修改代码去除权限验证。
|
||
|
||
### Q: 卡密格式有什么要求?
|
||
A: 必须是 `XXXX-XXXX-XXXX-XXXX` 格式,共19个字符(包含连字符)。
|
||
|
||
### Q: 用户激活多个卡密会怎样?
|
||
A: 如果已有有效授权,新卡密的时间会在原有基础上累加延期。
|
||
|
||
### Q: 授权过期后数据会丢失吗?
|
||
A: 不会,数据仍然保留,只是无法访问。重新激活后可以继续使用。
|
||
|
||
### Q: 如何撤销已发放的卡密?
|
||
A: 使用管理员接口 `POST /license/:id/revoke` 撤销指定卡密。
|
||
|
||
## 相关文档
|
||
|
||
- [LICENSE_SYSTEM.md](LICENSE_SYSTEM.md) - 详细使用说明
|
||
- [Swagger API 文档](http://localhost:3030/api) - 在线接口文档
|
||
- `test-license-system.sh` - 测试脚本
|
||
|
||
## 总结
|
||
|
||
本次实现完成了一个功能完整、安全可靠的卡密商业化授权系统。系统具有以下特点:
|
||
|
||
1. **功能完整**: 涵盖生成、激活、验证、查询、统计等全流程
|
||
2. **安全可靠**: 多层验证,状态机管理,防止滥用
|
||
3. **易于使用**: 清晰的 API 设计,完整的文档和测试
|
||
4. **易于扩展**: 模块化设计,支持多种授权类型和场景
|
||
|
||
系统已经可以直接投入使用,后续可根据实际需求进行功能扩展和优化。
|