278 lines
6.9 KiB
TypeScript
278 lines
6.9 KiB
TypeScript
import { Router, Request, Response } from 'express';
|
||
import { z } from 'zod';
|
||
import { authMiddleware, optionalAuth } from '../middleware/auth';
|
||
import { encrypt } from '../utils/encryption';
|
||
import { prisma } from '../utils/prisma';
|
||
|
||
const router = Router();
|
||
|
||
// ============================================
|
||
// 获取方案列表
|
||
// ============================================
|
||
router.get('/', optionalAuth, async (req: Request, res: Response) => {
|
||
try {
|
||
const {
|
||
page = 1,
|
||
limit = 20,
|
||
weapon,
|
||
category,
|
||
sort = 'newest',
|
||
} = req.query;
|
||
|
||
const skip = (Number(page) - 1) * Number(limit);
|
||
|
||
const where: any = { status: 'PUBLISHED' };
|
||
if (weapon) where.weaponName = { contains: String(weapon) };
|
||
if (category) where.category = String(category);
|
||
|
||
let orderBy: any = { createdAt: 'desc' };
|
||
if (sort === 'popular') orderBy = { viewsCount: 'desc' };
|
||
if (sort === 'downloads') orderBy = { downloadsCount: 'desc' };
|
||
|
||
const schemes = await prisma.scheme.findMany({
|
||
where,
|
||
orderBy,
|
||
skip,
|
||
take: Number(limit),
|
||
select: {
|
||
id: true,
|
||
title: true,
|
||
description: true,
|
||
weaponName: true,
|
||
category: true,
|
||
price: true,
|
||
viewsCount: true,
|
||
downloadsCount: true,
|
||
likesCount: true,
|
||
isOfficial: true,
|
||
createdAt: true,
|
||
user: {
|
||
select: {
|
||
id: true,
|
||
username: true,
|
||
avatar: true,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
data: schemes,
|
||
});
|
||
} catch (error) {
|
||
console.error('Get schemes error:', error);
|
||
res.status(500).json({ success: false, message: '获取失败' });
|
||
}
|
||
});
|
||
|
||
// ============================================
|
||
// 获取单个方案详情
|
||
// ============================================
|
||
router.get('/:id', optionalAuth, async (req: Request, res: Response) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const scheme = await prisma.scheme.findUnique({
|
||
where: { id },
|
||
include: {
|
||
user: {
|
||
select: {
|
||
id: true,
|
||
username: true,
|
||
avatar: true,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
|
||
if (!scheme) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '方案不存在',
|
||
});
|
||
}
|
||
|
||
if (scheme.status !== 'PUBLISHED' && scheme.userId !== req.user?.userId) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '方案不存在',
|
||
});
|
||
}
|
||
|
||
// 增加浏览量
|
||
await prisma.scheme.update({
|
||
where: { id },
|
||
data: { viewsCount: { increment: 1 } },
|
||
});
|
||
|
||
// 检查是否收藏
|
||
let isFavorited = false;
|
||
if (req.user) {
|
||
const fav = await prisma.favorite.findFirst({
|
||
where: {
|
||
userId: req.user.userId,
|
||
targetType: 'SCHEME',
|
||
targetId: id,
|
||
},
|
||
});
|
||
isFavorited = !!fav;
|
||
}
|
||
|
||
// 加密方案内容
|
||
const encryptedContent = encrypt(scheme.schemeContent);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: {
|
||
...scheme,
|
||
schemeContent: encryptedContent,
|
||
isFavorited,
|
||
},
|
||
});
|
||
} catch (error) {
|
||
console.error('Get scheme error:', error);
|
||
res.status(500).json({ success: false, message: '获取失败' });
|
||
}
|
||
});
|
||
|
||
// ============================================
|
||
// 创建方案
|
||
// ============================================
|
||
const createSchemeSchema = z.object({
|
||
title: z.string().min(1).max(100),
|
||
description: z.string().optional(),
|
||
weaponName: z.string().optional(),
|
||
category: z.string().optional(),
|
||
schemeContent: z.string().min(1),
|
||
price: z.number().int().min(0).default(0),
|
||
gpuModel: z.string().optional(),
|
||
driverVersion: z.string().optional(),
|
||
appVersion: z.string().optional(),
|
||
});
|
||
|
||
router.post('/', authMiddleware, async (req: Request, res: Response) => {
|
||
try {
|
||
const body = createSchemeSchema.parse(req.body);
|
||
|
||
const scheme = await prisma.scheme.create({
|
||
data: {
|
||
userId: req.user!.userId,
|
||
title: body.title,
|
||
description: body.description,
|
||
weaponName: body.weaponName,
|
||
category: body.category,
|
||
schemeContent: body.schemeContent,
|
||
price: body.price,
|
||
gpuModel: body.gpuModel,
|
||
driverVersion: body.driverVersion,
|
||
appVersion: body.appVersion,
|
||
status: 'PUBLISHED',
|
||
},
|
||
});
|
||
|
||
// 更新用户方案数
|
||
await prisma.user.update({
|
||
where: { id: req.user!.userId },
|
||
data: { schemesCount: { increment: 1 } },
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '方案创建成功',
|
||
data: scheme,
|
||
});
|
||
} catch (error) {
|
||
console.error('Create scheme error:', error);
|
||
res.status(500).json({ success: false, message: '创建失败' });
|
||
}
|
||
});
|
||
|
||
// ============================================
|
||
// 删除方案
|
||
// ============================================
|
||
router.delete('/:id', authMiddleware, async (req: Request, res: Response) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const scheme = await prisma.scheme.findUnique({
|
||
where: { id },
|
||
select: { userId: true },
|
||
});
|
||
|
||
if (!scheme) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '方案不存在',
|
||
});
|
||
}
|
||
|
||
if (scheme.userId !== req.user!.userId) {
|
||
return res.status(403).json({
|
||
success: false,
|
||
message: '无权删除此方案',
|
||
});
|
||
}
|
||
|
||
await prisma.scheme.update({
|
||
where: { id },
|
||
data: { status: 'DELETED' },
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
message: '方案已删除',
|
||
});
|
||
} catch (error) {
|
||
console.error('Delete scheme error:', error);
|
||
res.status(500).json({ success: false, message: '删除失败' });
|
||
}
|
||
});
|
||
|
||
// 记录方案使用(增加下载计数)
|
||
router.post('/:id/use', authMiddleware, async (req: Request, res: Response) => {
|
||
try {
|
||
const { id } = req.params;
|
||
const { source } = req.body;
|
||
|
||
const scheme = await prisma.scheme.findUnique({
|
||
where: { id },
|
||
select: { id: true, downloadsCount: true },
|
||
});
|
||
|
||
if (!scheme) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: '方案不存在',
|
||
});
|
||
}
|
||
|
||
// 使用计数 +1(增加 downloads 计数)
|
||
const updated = await prisma.scheme.update({
|
||
where: { id },
|
||
data: { downloadsCount: { increment: 1 } },
|
||
select: { downloadsCount: true },
|
||
});
|
||
|
||
// 记录日志
|
||
await prisma.userLog.create({
|
||
data: {
|
||
userId: req.user!.userId,
|
||
action: 'SchemeUse',
|
||
targetType: 'Scheme',
|
||
targetId: id,
|
||
},
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
downloadsCount: updated.downloadsCount,
|
||
});
|
||
} catch (error) {
|
||
console.error('Scheme use error:', error);
|
||
res.status(500).json({ success: false, message: '记录失败' });
|
||
}
|
||
});
|
||
|
||
export default router;
|