chore: mqsrv backend

This commit is contained in:
Chen Gu
2026-05-09 00:52:04 +08:00
commit b84f111e8f
21 changed files with 4593 additions and 0 deletions

337
src/routes/user.ts Normal file
View File

@@ -0,0 +1,337 @@
import { Router, Request, Response } from 'express';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';
import { authMiddleware } from '../middleware/auth';
const router = Router();
const prisma = new PrismaClient();
// 所有路由都需要认证
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) {
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) {
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) {
console.error('Update avatar error:', error);
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;