fix: remove dead code, add ZodError handling, fix NaN pagination, add like routes, fix schemesCount sync
This commit is contained in:
@@ -13,11 +13,15 @@ import schemeAobRoutes from './routes/schemesAob';
|
|||||||
import filterRoutes from './routes/filters';
|
import filterRoutes from './routes/filters';
|
||||||
import vipRoutes from './routes/vip';
|
import vipRoutes from './routes/vip';
|
||||||
import favoriteRoutes from './routes/favorites';
|
import favoriteRoutes from './routes/favorites';
|
||||||
|
import likeRoutes from './routes/likes';
|
||||||
import { prisma } from './utils/prisma';
|
import { prisma } from './utils/prisma';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3001;
|
const PORT = process.env.PORT || 3001;
|
||||||
|
|
||||||
|
// 信任代理 (用于 express-rate-limit 正确识别客户端 IP)
|
||||||
|
app.set('trust proxy', 1);
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 中间件
|
// 中间件
|
||||||
// ============================================
|
// ============================================
|
||||||
@@ -112,6 +116,7 @@ app.get('/api/favorites/count', async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.use('/api/favorites', favoriteRoutes);
|
app.use('/api/favorites', favoriteRoutes);
|
||||||
|
app.use('/api/likes', likeRoutes);
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// 缺失的存根接口(防止前端 404)
|
// 缺失的存根接口(防止前端 404)
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ const TARGET_TYPES = ['SCHEME', 'SCHEME_AOB', 'FILTER'] as const;
|
|||||||
router.get('/', async (req: Request, res: Response) => {
|
router.get('/', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { type, page = 1, limit = 20 } = req.query;
|
const { type, page = 1, limit = 20 } = req.query;
|
||||||
const skip = (Number(page) - 1) * Number(limit);
|
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 };
|
const where: any = { userId: req.user!.userId };
|
||||||
if (type && TARGET_TYPES.includes(type as any)) {
|
if (type && TARGET_TYPES.includes(type as any)) {
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ const router = Router();
|
|||||||
router.get('/', optionalAuth, async (req: Request, res: Response) => {
|
router.get('/', optionalAuth, async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const { page = 1, limit = 20, category } = req.query;
|
const { page = 1, limit = 20, category } = req.query;
|
||||||
const skip = (Number(page) - 1) * Number(limit);
|
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 = { status: 'PUBLISHED' };
|
const where: any = { status: 'PUBLISHED' };
|
||||||
if (category) where.category = String(category);
|
if (category) where.category = String(category);
|
||||||
|
|||||||
148
src/routes/likes.ts
Normal file
148
src/routes/likes.ts
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
const TARGET_TYPES = ['SCHEME', 'SCHEME_AOB', 'FILTER'] as const;
|
||||||
|
|
||||||
|
router.post('/', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const body = z.object({
|
||||||
|
targetType: z.enum(TARGET_TYPES),
|
||||||
|
targetId: z.string().uuid(),
|
||||||
|
}).parse(req.body);
|
||||||
|
|
||||||
|
const existing = await prisma.like.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: req.user!.userId,
|
||||||
|
targetType: body.targetType,
|
||||||
|
targetId: body.targetId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
return res.status(400).json({ success: false, message: '已点赞' });
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!targetExists) {
|
||||||
|
return res.status(404).json({ success: false, message: '目标不存在' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.like.create({
|
||||||
|
data: {
|
||||||
|
userId: req.user!.userId,
|
||||||
|
targetType: body.targetType,
|
||||||
|
targetId: body.targetId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (body.targetType === 'SCHEME') {
|
||||||
|
await prisma.scheme.update({
|
||||||
|
where: { id: body.targetId },
|
||||||
|
data: { likesCount: { increment: 1 } },
|
||||||
|
});
|
||||||
|
} else if (body.targetType === 'SCHEME_AOB') {
|
||||||
|
await prisma.schemeAob.update({
|
||||||
|
where: { id: body.targetId },
|
||||||
|
data: { likesCount: { increment: 1 } },
|
||||||
|
});
|
||||||
|
} else if (body.targetType === 'FILTER') {
|
||||||
|
await prisma.filterShare.update({
|
||||||
|
where: { id: body.targetId },
|
||||||
|
data: { likesCount: { increment: 1 } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ success: true, message: '点赞成功' });
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
||||||
|
}
|
||||||
|
console.error('Add like error:', error);
|
||||||
|
res.status(500).json({ success: false, message: '点赞失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/:id', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const like = await prisma.like.findUnique({ where: { id } });
|
||||||
|
|
||||||
|
if (!like || like.userId !== req.user!.userId) {
|
||||||
|
return res.status(404).json({ success: false, message: '点赞记录不存在' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.like.delete({ where: { id } });
|
||||||
|
|
||||||
|
if (like.targetType === 'SCHEME') {
|
||||||
|
await prisma.scheme.update({
|
||||||
|
where: { id: like.targetId },
|
||||||
|
data: { likesCount: { decrement: 1 } },
|
||||||
|
});
|
||||||
|
} else if (like.targetType === 'SCHEME_AOB') {
|
||||||
|
await prisma.schemeAob.update({
|
||||||
|
where: { id: like.targetId },
|
||||||
|
data: { likesCount: { decrement: 1 } },
|
||||||
|
});
|
||||||
|
} else if (like.targetType === 'FILTER') {
|
||||||
|
await prisma.filterShare.update({
|
||||||
|
where: { id: like.targetId },
|
||||||
|
data: { likesCount: { decrement: 1 } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({ success: true, message: '已取消点赞' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Remove like error:', error);
|
||||||
|
res.status(500).json({ success: false, message: '取消点赞失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/check', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { targetType, targetId } = req.query;
|
||||||
|
|
||||||
|
if (!targetType || !targetId) {
|
||||||
|
return res.status(400).json({ success: false, message: '参数缺失' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const like = await prisma.like.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: req.user!.userId,
|
||||||
|
targetType: String(targetType),
|
||||||
|
targetId: String(targetId),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
isLiked: !!like,
|
||||||
|
likeId: like?.id || null,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Check like error:', error);
|
||||||
|
res.status(500).json({ success: false, message: '检查失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -19,7 +19,9 @@ router.get('/', optionalAuth, async (req: Request, res: Response) => {
|
|||||||
sort = 'newest',
|
sort = 'newest',
|
||||||
} = req.query;
|
} = req.query;
|
||||||
|
|
||||||
const skip = (Number(page) - 1) * Number(limit);
|
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 = { status: 'PUBLISHED' };
|
const where: any = { status: 'PUBLISHED' };
|
||||||
if (weapon) where.weaponName = { contains: String(weapon) };
|
if (weapon) where.weaponName = { contains: String(weapon) };
|
||||||
@@ -219,6 +221,11 @@ router.delete('/:id', authMiddleware, async (req: Request, res: Response) => {
|
|||||||
data: { status: 'DELETED' },
|
data: { status: 'DELETED' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { id: req.user!.userId },
|
||||||
|
data: { schemesCount: { decrement: 1 } },
|
||||||
|
});
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: '方案已删除',
|
message: '方案已删除',
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ router.get('/', optionalAuth, async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
const { page = 1, limit = 20, weapon, category, sort = 'newest' } = req.query;
|
const { page = 1, limit = 20, weapon, category, sort = 'newest' } = req.query;
|
||||||
|
|
||||||
const skip = (Number(page) - 1) * Number(limit);
|
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 = { status: 'PUBLISHED' };
|
const where: any = { status: 'PUBLISHED' };
|
||||||
if (weapon) where.weaponName = { contains: String(weapon) };
|
if (weapon) where.weaponName = { contains: String(weapon) };
|
||||||
@@ -111,6 +113,11 @@ router.post('/', authMiddleware, async (req: Request, res: Response) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { id: req.user!.userId },
|
||||||
|
data: { schemesCount: { increment: 1 } },
|
||||||
|
});
|
||||||
|
|
||||||
res.json({ success: true, message: '方案创建成功', data: scheme });
|
res.json({ success: true, message: '方案创建成功', data: scheme });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Create scheme_aob error:', error);
|
console.error('Create scheme_aob error:', error);
|
||||||
@@ -118,4 +125,41 @@ router.post('/', authMiddleware, async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// 删除方案
|
||||||
|
// ============================================
|
||||||
|
router.delete('/:id', authMiddleware, async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const scheme = await prisma.schemeAob.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.schemeAob.update({
|
||||||
|
where: { id },
|
||||||
|
data: { status: 'DELETED' },
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { id: req.user!.userId },
|
||||||
|
data: { schemesCount: { decrement: 1 } },
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true, message: '方案已删除' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Delete scheme_aob error:', error);
|
||||||
|
res.status(500).json({ success: false, message: '删除失败' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@@ -54,7 +54,9 @@ router.get('/schemes/:userId', async (req: Request, res: Response) => {
|
|||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
const { type = 'schemes', page = 1, limit = 20 } = req.query;
|
const { type = 'schemes', page = 1, limit = 20 } = req.query;
|
||||||
|
|
||||||
const skip = (Number(page) - 1) * Number(limit);
|
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;
|
||||||
|
|
||||||
let schemes;
|
let schemes;
|
||||||
if (type === 'aob') {
|
if (type === 'aob') {
|
||||||
@@ -231,6 +233,9 @@ router.put('/username', async (req: Request, res: Response) => {
|
|||||||
username: body.username,
|
username: body.username,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
||||||
|
}
|
||||||
console.error('Update username error:', error);
|
console.error('Update username error:', error);
|
||||||
res.status(500).json({ success: false, message: '更新失败' });
|
res.status(500).json({ success: false, message: '更新失败' });
|
||||||
}
|
}
|
||||||
@@ -278,6 +283,9 @@ router.put('/email', async (req: Request, res: Response) => {
|
|||||||
email: body.email,
|
email: body.email,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
||||||
|
}
|
||||||
console.error('Update email error:', error);
|
console.error('Update email error:', error);
|
||||||
res.status(500).json({ success: false, message: '更新失败' });
|
res.status(500).json({ success: false, message: '更新失败' });
|
||||||
}
|
}
|
||||||
@@ -313,179 +321,12 @@ router.put('/avatar', async (req: Request, res: Response) => {
|
|||||||
avatar: body.avatar,
|
avatar: body.avatar,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof z.ZodError) {
|
||||||
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
||||||
|
}
|
||||||
console.error('Update avatar error:', error);
|
console.error('Update avatar error:', error);
|
||||||
res.status(500).json({ success: false, message: '更新失败' });
|
res.status(500).json({ success: false, message: '更新失败' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// 获取用户统计
|
|
||||||
// ============================================
|
|
||||||
router.get('/stats/:userId', async (req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const { userId } = req.params;
|
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id: userId },
|
|
||||||
select: {
|
|
||||||
schemesCount: true,
|
|
||||||
favoritesCount: true,
|
|
||||||
isVip: true,
|
|
||||||
vipExpireAt: true,
|
|
||||||
createdAt: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return res.status(404).json({
|
|
||||||
success: false,
|
|
||||||
message: '用户不存在',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
schemesCount: user.schemesCount,
|
|
||||||
favoritesCount: user.favoritesCount,
|
|
||||||
isVip: user.isVip,
|
|
||||||
vipExpireAt: user.vipExpireAt,
|
|
||||||
memberSince: user.createdAt,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Get user stats error:', error);
|
|
||||||
res.status(500).json({ success: false, message: '获取失败' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// 获取用户方案列表
|
|
||||||
// ============================================
|
|
||||||
router.get('/schemes/:userId', async (req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const { userId } = req.params;
|
|
||||||
const { type = 'schemes', page = 1, limit = 20 } = req.query;
|
|
||||||
|
|
||||||
const skip = (Number(page) - 1) * Number(limit);
|
|
||||||
|
|
||||||
let schemes;
|
|
||||||
if (type === 'aob') {
|
|
||||||
schemes = await prisma.schemeAob.findMany({
|
|
||||||
where: { userId, status: 'PUBLISHED' },
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
skip,
|
|
||||||
take: Number(limit),
|
|
||||||
select: {
|
|
||||||
id: true, title: true, weaponName: true, category: true,
|
|
||||||
viewsCount: true, downloadsCount: true, likesCount: true, createdAt: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
schemes = await prisma.scheme.findMany({
|
|
||||||
where: { userId, status: 'PUBLISHED' },
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
skip,
|
|
||||||
take: Number(limit),
|
|
||||||
select: {
|
|
||||||
id: true, title: true, weaponName: true, category: true,
|
|
||||||
viewsCount: true, downloadsCount: true, likesCount: true, createdAt: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: schemes,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Get user schemes error:', error);
|
|
||||||
res.status(500).json({ success: false, message: '获取失败' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// 获取用户被收藏次数
|
|
||||||
// ============================================
|
|
||||||
router.get('/favorited-count/:userId', async (req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const { userId } = req.params;
|
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id: userId },
|
|
||||||
select: { favoritesCount: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return res.status(404).json({
|
|
||||||
success: false,
|
|
||||||
message: '用户不存在',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
favoritedCount: user.favoritesCount,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Get favorited count error:', error);
|
|
||||||
res.status(500).json({ success: false, message: '获取失败' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// 获取用户功能限制信息
|
|
||||||
// ============================================
|
|
||||||
router.get('/limits/:userId', async (req: Request, res: Response) => {
|
|
||||||
try {
|
|
||||||
const { userId } = req.params;
|
|
||||||
|
|
||||||
// 验证用户是否存在
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: { id: userId },
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
isVip: true,
|
|
||||||
vipExpireAt: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return res.status(404).json({
|
|
||||||
success: false,
|
|
||||||
message: '用户不存在',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 VIP 是否过期
|
|
||||||
const now = new Date();
|
|
||||||
const isVipActive = user.isVip && user.vipExpireAt && user.vipExpireAt > now;
|
|
||||||
|
|
||||||
// 根据 VIP 状态返回不同的限制
|
|
||||||
const limits = isVipActive
|
|
||||||
? {
|
|
||||||
maxDailyUses: 500, // VIP: 每日最多使用500次
|
|
||||||
dailyUsesLeft: 500, // 剩余次数(简化处理,实际需要统计当日使用)
|
|
||||||
maxFavorites: 1000, // VIP: 最多收藏1000个
|
|
||||||
maxSchemes: 200, // VIP: 最多创建200个方案
|
|
||||||
canUploadIcc: true, // VIP: 可以上传 ICC
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
maxDailyUses: 50, // 非VIP: 每日最多使用50次
|
|
||||||
dailyUsesLeft: 50, // 剩余次数
|
|
||||||
maxFavorites: 100, // 非VIP: 最多收藏100个
|
|
||||||
maxSchemes: 50, // 非VIP: 最多创建50个方案
|
|
||||||
canUploadIcc: false, // 非VIP: 不能上传 ICC
|
|
||||||
};
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
...limits,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Get user limits error:', error);
|
|
||||||
res.status(500).json({ success: false, message: '获取失败' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
Reference in New Issue
Block a user