112 lines
2.9 KiB
TypeScript
112 lines
2.9 KiB
TypeScript
import { Router, Request, Response } from 'express';
|
|
import { PrismaClient } from '@prisma/client';
|
|
import { z } from 'zod';
|
|
import { authMiddleware } from '../middleware/auth';
|
|
|
|
const router = Router();
|
|
const prisma = new PrismaClient();
|
|
|
|
// ============================================
|
|
// VIP 卡密激活
|
|
// ============================================
|
|
const activateSchema = z.object({
|
|
cardKey: z.string().regex(/^VIP\d+-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/),
|
|
});
|
|
|
|
router.post('/activate-vip', authMiddleware, async (req: Request, res: Response) => {
|
|
try {
|
|
const body = activateSchema.parse(req.body);
|
|
|
|
// 查找卡密
|
|
const card = await prisma.vipCard.findUnique({
|
|
where: { cardKey: body.cardKey },
|
|
});
|
|
|
|
if (!card) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '卡密不存在',
|
|
});
|
|
}
|
|
|
|
if (card.status !== 'UNUSED') {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '卡密已被使用或已失效',
|
|
});
|
|
}
|
|
|
|
// 获取用户当前 VIP 状态
|
|
const user = await prisma.user.findUnique({
|
|
where: { id: req.user!.userId },
|
|
select: { isVip: true, vipExpireAt: true, vipLevel: true },
|
|
});
|
|
|
|
if (!user) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: '用户不存在',
|
|
});
|
|
}
|
|
|
|
// 计算新的 VIP 过期时间
|
|
let newExpireAt: Date;
|
|
if (user.vipExpireAt && user.isVip && new Date() < user.vipExpireAt) {
|
|
// 已是 VIP,延长时间
|
|
newExpireAt = new Date(user.vipExpireAt.getTime() + card.days * 24 * 60 * 60 * 1000);
|
|
} else {
|
|
// 非 VIP,从现在开始计算
|
|
newExpireAt = new Date(Date.now() + card.days * 24 * 60 * 60 * 1000);
|
|
}
|
|
|
|
// 更新用户 VIP 状态
|
|
const updatedUser = await prisma.user.update({
|
|
where: { id: req.user!.userId },
|
|
data: {
|
|
isVip: true,
|
|
vipExpireAt: newExpireAt,
|
|
vipLevel: Math.max(user.vipLevel, 1),
|
|
},
|
|
});
|
|
|
|
// 更新卡密状态
|
|
await prisma.vipCard.update({
|
|
where: { id: card.id },
|
|
data: {
|
|
status: 'USED',
|
|
usedBy: req.user!.userId,
|
|
usedAt: new Date(),
|
|
},
|
|
});
|
|
|
|
// 记录日志
|
|
await prisma.userLog.create({
|
|
data: {
|
|
userId: req.user!.userId,
|
|
action: 'VIP_ACTIVATE',
|
|
targetId: card.id,
|
|
ipAddress: req.ip,
|
|
},
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
isVip: true,
|
|
vipExpireAt: newExpireAt,
|
|
vipLevel: updatedUser.vipLevel,
|
|
message: `激活成功,VIP 有效期延长 ${card.days} 天`,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '卡密格式无效',
|
|
});
|
|
}
|
|
|
|
console.error('Activate VIP error:', error);
|
|
res.status(500).json({ success: false, message: '激活失败' });
|
|
}
|
|
});
|
|
|
|
export default router; |