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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user