333 lines
8.9 KiB
TypeScript
333 lines
8.9 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.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 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;
|
|
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: '用户不存在' });
|
|
}
|
|
|
|
const now = new Date();
|
|
const isVipActive = user.isVip && user.vipExpireAt && user.vipExpireAt > now;
|
|
|
|
const limits = isVipActive
|
|
? {
|
|
maxDailyUses: 500,
|
|
dailyUsesLeft: 500,
|
|
maxFavorites: 1000,
|
|
maxSchemes: 200,
|
|
canUploadIcc: true,
|
|
}
|
|
: {
|
|
maxDailyUses: 50,
|
|
dailyUsesLeft: 50,
|
|
maxFavorites: 100,
|
|
maxSchemes: 50,
|
|
canUploadIcc: false,
|
|
};
|
|
|
|
res.json({ success: true, ...limits });
|
|
} catch (error) {
|
|
console.error('Get user limits error:', error);
|
|
res.status(500).json({ success: false, message: '获取失败' });
|
|
}
|
|
});
|
|
|
|
// ============================================
|
|
// 以下路由需要认证
|
|
// ============================================
|
|
router.use(authMiddleware);
|
|
const updateUsernameSchema = z.object({
|
|
userId: z.string().uuid(),
|
|
username: z.string().min(3).max(50),
|
|
});
|
|
|
|
router.put('/username', async (req: Request, res: Response) => {
|
|
try {
|
|
const body = updateUsernameSchema.parse(req.body);
|
|
|
|
// 验证用户 ID
|
|
if (body.userId !== req.user!.userId) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
message: '无权修改其他用户信息',
|
|
});
|
|
}
|
|
|
|
// 检查用户名是否已存在
|
|
const existing = await prisma.user.findUnique({
|
|
where: { username: body.username },
|
|
});
|
|
|
|
if (existing) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '用户名已被使用',
|
|
});
|
|
}
|
|
|
|
// 检查修改次数限制
|
|
const limit = await prisma.userLog.count({
|
|
where: {
|
|
userId: req.user!.userId,
|
|
action: 'UPDATE_USERNAME',
|
|
},
|
|
});
|
|
|
|
if (limit >= 4) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '用户名修改次数已达上限',
|
|
});
|
|
}
|
|
|
|
// 更新用户名
|
|
await prisma.user.update({
|
|
where: { id: body.userId },
|
|
data: { username: body.username },
|
|
});
|
|
|
|
// 记录日志
|
|
await prisma.userLog.create({
|
|
data: {
|
|
userId: req.user!.userId,
|
|
action: 'UPDATE_USERNAME',
|
|
ipAddress: req.ip,
|
|
},
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
message: '用户名更新成功',
|
|
username: body.username,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
|
}
|
|
console.error('Update username error:', error);
|
|
res.status(500).json({ success: false, message: '更新失败' });
|
|
}
|
|
});
|
|
|
|
// ============================================
|
|
// 更新邮箱
|
|
// ============================================
|
|
const updateEmailSchema = z.object({
|
|
userId: z.string().uuid(),
|
|
email: z.string().email(),
|
|
});
|
|
|
|
router.put('/email', async (req: Request, res: Response) => {
|
|
try {
|
|
const body = updateEmailSchema.parse(req.body);
|
|
|
|
if (body.userId !== req.user!.userId) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
message: '无权修改其他用户信息',
|
|
});
|
|
}
|
|
|
|
// 检查邮箱是否已存在
|
|
const existing = await prisma.user.findUnique({
|
|
where: { email: body.email },
|
|
});
|
|
|
|
if (existing) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: '邮箱已被使用',
|
|
});
|
|
}
|
|
|
|
await prisma.user.update({
|
|
where: { id: body.userId },
|
|
data: { email: body.email },
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
message: '邮箱更新成功',
|
|
email: body.email,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
|
}
|
|
console.error('Update email error:', error);
|
|
res.status(500).json({ success: false, message: '更新失败' });
|
|
}
|
|
});
|
|
|
|
// ============================================
|
|
// 更新头像
|
|
// ============================================
|
|
const updateAvatarSchema = z.object({
|
|
userId: z.string().uuid(),
|
|
avatar: z.string().url(),
|
|
});
|
|
|
|
router.put('/avatar', async (req: Request, res: Response) => {
|
|
try {
|
|
const body = updateAvatarSchema.parse(req.body);
|
|
|
|
if (body.userId !== req.user!.userId) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
message: '无权修改其他用户信息',
|
|
});
|
|
}
|
|
|
|
await prisma.user.update({
|
|
where: { id: body.userId },
|
|
data: { avatar: body.avatar },
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
message: '头像更新成功',
|
|
avatar: body.avatar,
|
|
});
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ success: false, message: '参数验证失败', errors: error.errors });
|
|
}
|
|
console.error('Update avatar error:', error);
|
|
res.status(500).json({ success: false, message: '更新失败' });
|
|
}
|
|
});
|
|
|
|
export default router;
|