187 lines
6.1 KiB
TypeScript
187 lines
6.1 KiB
TypeScript
import { Router, Request, Response } from 'express';
|
||
import { z } from 'zod';
|
||
import { authMiddleware } from '../middleware/auth';
|
||
import { prisma } from '../utils/prisma';
|
||
|
||
const router = Router();
|
||
|
||
router.use(authMiddleware);
|
||
|
||
// source → targetType 映射
|
||
function sourceToType(source?: string): string {
|
||
if (source === '全面战场' || source === 'schemes_aob') return 'SCHEME_AOB';
|
||
return 'SCHEME'; // 烽火地带 / schemes
|
||
}
|
||
|
||
// 格式化方案卡片(对齐顶层 API)
|
||
function formatScheme(s: any) {
|
||
return {
|
||
id: s.id,
|
||
user_id: s.userId,
|
||
description: s.description || '',
|
||
scheme_content: s.schemeContent || '',
|
||
category: s.category || '',
|
||
weapon_name: s.weaponName || '',
|
||
price: s.price ? `${s.price}W` : null,
|
||
tags: [],
|
||
uses: s.downloadsCount || 0,
|
||
status: 'normal',
|
||
comments: null,
|
||
shares: null,
|
||
created_at: s.createdAt?.toISOString?.() || s.createdAt,
|
||
updated_at: s.updatedAt?.toISOString?.() || s.updatedAt,
|
||
source: null,
|
||
total_historical_uses: s.downloadsCount || 0,
|
||
username: s.user?.username || '',
|
||
avatar: s.user?.avatar || '',
|
||
partner_type: null,
|
||
partner_level: null,
|
||
partner_badge: null,
|
||
partner_logo: null,
|
||
social_link: null,
|
||
likes: null,
|
||
};
|
||
}
|
||
|
||
// ============================================
|
||
// 获取收藏列表 (对齐 GET /api/favorites)
|
||
// ============================================
|
||
router.get('/', async (req: Request, res: Response) => {
|
||
try {
|
||
const { page = '1', limit = '12', sort = 'hot', weaponCategory, weaponName, search } = req.query;
|
||
const pageNum = Math.max(1, parseInt(String(page)) || 1);
|
||
const limitNum = Math.min(100, Math.max(1, parseInt(String(limit)) || 12));
|
||
|
||
// 先查用户的收藏记录
|
||
const total = await prisma.favorite.count({ where: { userId: req.user!.userId } });
|
||
const favorites = await prisma.favorite.findMany({
|
||
where: { userId: req.user!.userId },
|
||
orderBy: { createdAt: 'desc' },
|
||
skip: (pageNum - 1) * limitNum,
|
||
take: limitNum,
|
||
});
|
||
|
||
// 加载关联的方案数据
|
||
const data: any[] = [];
|
||
for (const fav of favorites) {
|
||
let scheme: any = null;
|
||
if (fav.targetType === 'SCHEME') {
|
||
scheme = await prisma.scheme.findUnique({
|
||
where: { id: fav.targetId },
|
||
include: { user: { select: { username: true, avatar: true } } },
|
||
});
|
||
} else if (fav.targetType === 'SCHEME_AOB') {
|
||
scheme = await prisma.schemeAob.findUnique({
|
||
where: { id: fav.targetId },
|
||
include: { user: { select: { username: true, avatar: true } } },
|
||
});
|
||
}
|
||
if (scheme && scheme.status === 'PUBLISHED') {
|
||
data.push(formatScheme(scheme));
|
||
}
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data,
|
||
pagination: { page: pageNum, limit: limitNum, hasMore: pageNum * limitNum < total },
|
||
});
|
||
} catch (error) {
|
||
console.error('Get favorites error:', error);
|
||
res.status(500).json({ success: false, message: '获取失败' });
|
||
}
|
||
});
|
||
|
||
// ============================================
|
||
// 添加收藏 (对齐 POST /api/favorites)
|
||
// body: { schemeId, source: "烽火地带" }
|
||
// ============================================
|
||
const addFavoriteSchema = z.object({
|
||
schemeId: z.string().or(z.number()),
|
||
source: z.string().optional(),
|
||
});
|
||
|
||
router.post('/', async (req: Request, res: Response) => {
|
||
try {
|
||
const body = addFavoriteSchema.parse(req.body);
|
||
const targetType = sourceToType(body.source);
|
||
const targetId = String(body.schemeId);
|
||
|
||
const existing = await prisma.favorite.findFirst({
|
||
where: { userId: req.user!.userId, targetType, targetId },
|
||
});
|
||
if (existing) {
|
||
return res.json({ success: true, alreadyFavorited: true });
|
||
}
|
||
|
||
// 验证目标存在
|
||
let targetExists = false;
|
||
if (targetType === 'SCHEME') {
|
||
targetExists = !!(await prisma.scheme.findFirst({ where: { id: targetId, status: 'PUBLISHED' } }));
|
||
} else {
|
||
targetExists = !!(await prisma.schemeAob.findFirst({ where: { id: targetId, status: 'PUBLISHED' } }));
|
||
}
|
||
if (!targetExists) {
|
||
return res.status(404).json({ success: false, message: '方案不存在' });
|
||
}
|
||
|
||
await prisma.favorite.create({
|
||
data: { userId: req.user!.userId, targetType, targetId },
|
||
});
|
||
|
||
res.json({ success: true, alreadyFavorited: false });
|
||
} catch (error) {
|
||
console.error('Add favorite error:', error);
|
||
res.status(500).json({ success: false, message: '收藏失败' });
|
||
}
|
||
});
|
||
|
||
// ============================================
|
||
// 取消收藏 (对齐 DELETE /api/favorites/:schemeId?source=xxx)
|
||
// ============================================
|
||
router.delete('/:schemeId', async (req: Request, res: Response) => {
|
||
try {
|
||
const { schemeId } = req.params;
|
||
const source = req.query.source as string | undefined;
|
||
const targetType = sourceToType(source);
|
||
|
||
const favorite = await prisma.favorite.findFirst({
|
||
where: { userId: req.user!.userId, targetType, targetId: schemeId },
|
||
});
|
||
if (!favorite) {
|
||
return res.status(404).json({ success: false, message: '收藏不存在' });
|
||
}
|
||
|
||
await prisma.favorite.delete({ where: { id: favorite.id } });
|
||
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
console.error('Remove favorite error:', error);
|
||
res.status(500).json({ success: false, message: '取消收藏失败' });
|
||
}
|
||
});
|
||
|
||
// ============================================
|
||
// 检查收藏状态 (对齐 GET /api/favorites/check?schemeId=xx&source=xx)
|
||
// ============================================
|
||
router.get('/check', async (req: Request, res: Response) => {
|
||
try {
|
||
const { schemeId, source } = req.query;
|
||
if (!schemeId) {
|
||
return res.status(400).json({ success: false, message: '参数缺失' });
|
||
}
|
||
const targetType = sourceToType(source as string | undefined);
|
||
|
||
const favorite = await prisma.favorite.findFirst({
|
||
where: { userId: req.user!.userId, targetType, targetId: String(schemeId) },
|
||
});
|
||
|
||
res.json({ isFavorited: !!favorite });
|
||
} catch (error) {
|
||
console.error('Check favorite error:', error);
|
||
res.status(500).json({ success: false, message: '检查失败' });
|
||
}
|
||
});
|
||
|
||
export default router;
|