align API response format with maqt.top real specs: weapon-categories, weapons, schemes, schemes_aob, favorites check, encryption key derivation

This commit is contained in:
2026-05-24 01:47:08 +08:00
parent ea4e0f6e07
commit 8bcb6c7e7a
13 changed files with 1861 additions and 366 deletions

View File

@@ -7,31 +7,85 @@ const router = Router();
router.use(authMiddleware);
const TARGET_TYPES = ['SCHEME', 'SCHEME_AOB', 'FILTER'] as const;
// 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: parseInt(s.id, 36) || String(s.id).split('-')[0] || 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 { type, page = 1, limit = 20 } = req.query;
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)) || 20));
const skip = (pageNum - 1) * limitNum;
const where: any = { userId: req.user!.userId };
if (type && TARGET_TYPES.includes(type as any)) {
where.targetType = type;
}
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,
where: { userId: req.user!.userId },
orderBy: { createdAt: 'desc' },
skip,
take: Number(limit),
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 },
});
res.json({ success: true, data: favorites });
} catch (error) {
console.error('Get favorites error:', error);
res.status(500).json({ success: false, message: '获取失败' });
@@ -39,66 +93,43 @@ router.get('/', async (req: Request, res: Response) => {
});
// ============================================
// 添加收藏
// 添加收藏 (对齐 POST /api/favorites)
// body: { schemeId, source: "烽火地带" }
// ============================================
const addFavoriteSchema = z.object({
targetType: z.enum(TARGET_TYPES),
targetId: z.string().uuid(),
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: body.targetType,
targetId: body.targetId,
},
where: { userId: req.user!.userId, targetType, targetId },
});
if (existing) {
return res.status(400).json({ success: false, message: '已收藏' });
return res.json({ success: true, alreadyFavorited: true });
}
// 验证目标是否存在
// 验证目标存在
let targetExists = false;
if (body.targetType === 'SCHEME') {
targetExists = !!(await prisma.scheme.findFirst({
where: { id: body.targetId, status: 'PUBLISHED' },
}));
} else if (body.targetType === 'SCHEME_AOB') {
targetExists = !!(await prisma.schemeAob.findFirst({
where: { id: body.targetId, status: 'PUBLISHED' },
}));
} else if (body.targetType === 'FILTER') {
targetExists = !!(await prisma.filterShare.findFirst({
where: { id: body.targetId, status: 'PUBLISHED' },
}));
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: '目标不存在' });
return res.status(404).json({ success: false, message: '方案不存在' });
}
// 创建收藏
await prisma.favorite.create({
data: {
userId: req.user!.userId,
targetType: body.targetType,
targetId: body.targetId,
},
data: { userId: req.user!.userId, targetType, targetId },
});
// 更新用户收藏数
await prisma.user.update({
where: { id: req.user!.userId },
data: { favoritesCount: { increment: 1 } },
});
res.json({ success: true, message: '收藏成功' });
res.json({ success: true, alreadyFavorited: false });
} catch (error) {
console.error('Add favorite error:', error);
res.status(500).json({ success: false, message: '收藏失败' });
@@ -106,29 +137,24 @@ router.post('/', async (req: Request, res: Response) => {
});
// ============================================
// 取消收藏
// 取消收藏 (对齐 DELETE /api/favorites/:schemeId?source=xxx)
// ============================================
router.delete('/:id', async (req: Request, res: Response) => {
router.delete('/:schemeId', async (req: Request, res: Response) => {
try {
const { id } = req.params;
const favorite = await prisma.favorite.findUnique({
where: { id },
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 || favorite.userId !== req.user!.userId) {
if (!favorite) {
return res.status(404).json({ success: false, message: '收藏不存在' });
}
await prisma.favorite.delete({ where: { id } });
// 更新用户收藏数
await prisma.user.update({
where: { id: req.user!.userId },
data: { favoritesCount: { decrement: 1 } },
});
res.json({ success: true, 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: '取消收藏失败' });
@@ -136,33 +162,25 @@ router.delete('/:id', async (req: Request, res: Response) => {
});
// ============================================
// 检查是否已收藏
// 检查收藏状态 (对齐 GET /api/favorites/check?schemeId=xx&source=xx)
// ============================================
router.get('/check', async (req: Request, res: Response) => {
try {
const { targetType, targetId } = req.query;
if (!targetType || !targetId) {
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: String(targetType),
targetId: String(targetId),
},
});
res.json({
success: true,
isFavorited: !!favorite,
favoriteId: favorite?.id || null,
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;
export default router;