8.8 KiB
8.8 KiB
卡密商业化系统实现总结
项目概述
成功为 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
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`)
);
使用流程
管理员生成卡密
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": "批量生成月卡"
}'
用户激活卡密
curl -X POST http://localhost:3030/license/activate \
-H "Authorization: Bearer {user_token}" \
-H "Content-Type: application/json" \
-d '{
"code": "ABCD-1234-EFGH-5678"
}'
用户查询授权
curl -X GET http://localhost:3030/license/my \
-H "Authorization: Bearer {user_token}"
使用任务功能
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 test-license-system.sh
2. 手动测试步骤
- 启动项目:
npm run start:dev - 访问 Swagger:
http://localhost:3030/api - 注册用户
- 生成卡密(管理员)
- 激活卡密
- 测试任务接口
3. 测试场景
- ✅ 未授权访问任务接口 → 返回 403
- ✅ 激活卡密后访问 → 正常使用
- ✅ 重复激活同一卡密 → 返回 409
- ✅ 激活第二个卡密 → 自动延期
- ✅ 授权过期后访问 → 返回 403
- ✅ 查询剩余天数 → 正确显示
- ✅ 撤销卡密 → 无法再使用
Swagger 文档
所有接口都已添加完整的 Swagger 注释,包括:
- 接口描述
- 请求参数说明
- 响应示例
- 错误码说明
访问地址: http://localhost:3030/api
技术亮点
1. 安全性
- 卡密随机生成,去除易混淆字符
- 全局唯一性保证
- 状态机防止重复激活
- 守卫层面统一验证
2. 用户体验
- 支持授权延期(多卡密叠加)
- 清晰的错误提示
- 剩余天数实时查询
- 授权历史记录
3. 可维护性
- 模块化设计
- 装饰器 + 守卫模式
- 完整的 TypeScript 类型
- 详细的代码注释
4. 可扩展性
- 支持多种卡密类型
- 可配置有效天数
- 易于添加新的授权模块
- 支持批量生成
后续优化建议
功能扩展
- 支持不同模块的独立授权
- 添加授权转让功能
- 实现自动续费机制
- 添加授权即将过期提醒
- 支持授权使用日志
性能优化
- 卡密验证结果缓存(Redis)
- 批量生成优化
- 数据库索引优化
- 添加分页查询
安全增强
- 添加 IP 白名单限制
- 实现设备绑定
- 添加异常登录检测
- 卡密使用频率限制
前端集成建议
1. 授权状态管理
// 存储授权信息
interface LicenseState {
hasValidLicense: boolean;
remainingDays: number;
expiresAt: string;
type: string;
}
// 在应用启动时查询
const checkLicense = async () => {
const response = await api.get('/license/my');
return response.data;
};
2. 路由守卫
// 需要授权的路由添加守卫
const ProtectedRoute = ({ children }) => {
const { hasValidLicense } = useLicense();
if (!hasValidLicense) {
return <Navigate to="/activate" />;
}
return children;
};
3. 激活页面
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. 授权状态显示
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 - 详细使用说明
- Swagger API 文档 - 在线接口文档
test-license-system.sh- 测试脚本
总结
本次实现完成了一个功能完整、安全可靠的卡密商业化授权系统。系统具有以下特点:
- 功能完整: 涵盖生成、激活、验证、查询、统计等全流程
- 安全可靠: 多层验证,状态机管理,防止滥用
- 易于使用: 清晰的 API 设计,完整的文档和测试
- 易于扩展: 模块化设计,支持多种授权类型和场景
系统已经可以直接投入使用,后续可根据实际需求进行功能扩展和优化。