const express = require('express'); const router = express.Router(); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const auth = require('../middleware/auth'); const authSuper = require('../middleware/authSuper'); const { upload, getFileUrls } = require('../middleware/upload'); const { Admin, Activity, Registration, Expense, Photo, Sponsor, Prize } = require('../models'); const fs = require('fs'); const path = require('path'); // 管理员登录 router.post('/login', async (req, res) => { try { const { username, password } = req.body; const admin = await Admin.findOne({ where: { username } }); if (!admin || !bcrypt.compareSync(password, admin.password)) { return res.status(401).json({ error: '用户名或密码错误' }); } const token = jwt.sign({ id: admin.id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.json({ token, admin: { id: admin.id, username: admin.username, name: admin.name, role: admin.role } }); } catch (error) { res.status(500).json({ error: error.message }); } }); // Token续期 router.post('/refresh-token', auth, async (req, res) => { try { const admin = await Admin.findByPk(req.adminId); if (!admin) { return res.status(401).json({ error: '用户不存在' }); } const token = jwt.sign({ id: admin.id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.json({ token }); } catch (error) { res.status(500).json({ error: error.message }); } }); // ========== 管理员管理API(仅超级管理员) ========== // 获取管理员列表 router.get('/admins', authSuper, async (req, res) => { try { const admins = await Admin.findAll({ attributes: ['id', 'username', 'name', 'role', 'createdAt'], order: [['createdAt', 'ASC']], raw: true }); res.json(admins); } catch (error) { res.status(500).json({ error: error.message }); } }); // 创建管理员 router.post('/admins', authSuper, async (req, res) => { try { const { username, password, name, role } = req.body; if (!username || !password) { return res.status(400).json({ error: '用户名和密码不能为空' }); } const existing = await Admin.findOne({ where: { username } }); if (existing) { return res.status(400).json({ error: '用户名已存在' }); } const hashedPassword = bcrypt.hashSync(password, 10); const admin = await Admin.create({ username, password: hashedPassword, name: name || username, role: role || 'admin' }); res.json({ id: admin.id, username: admin.username, name: admin.name, role: admin.role }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 修改管理员信息 router.put('/admins/:id', authSuper, async (req, res) => { try { const { name, role } = req.body; const admin = await Admin.findByPk(req.params.id); if (!admin) { return res.status(404).json({ error: '管理员不存在' }); } const updateData = {}; if (name !== undefined) updateData.name = name; if (role !== undefined) updateData.role = role; await Admin.update(updateData, { where: { id: req.params.id } }); const updated = await Admin.findByPk(req.params.id, { attributes: ['id', 'username', 'name', 'role'] }); res.json(updated); } catch (error) { res.status(500).json({ error: error.message }); } }); // 修改管理员密码 router.put('/admins/:id/password', authSuper, async (req, res) => { try { const { newPassword } = req.body; if (!newPassword) { return res.status(400).json({ error: '新密码不能为空' }); } const admin = await Admin.findByPk(req.params.id); if (!admin) { return res.status(404).json({ error: '管理员不存在' }); } const hashedPassword = bcrypt.hashSync(newPassword, 10); await Admin.update({ password: hashedPassword }, { where: { id: req.params.id } }); res.json({ message: '密码修改成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 删除管理员 router.delete('/admins/:id', authSuper, async (req, res) => { try { const admin = await Admin.findByPk(req.params.id); if (!admin) { return res.status(404).json({ error: '管理员不存在' }); } // 防止删除超级管理员 if (admin.role === 'super') { return res.status(403).json({ error: '不能删除超级管理员' }); } await Admin.destroy({ where: { id: req.params.id } }); res.json({ message: '删除成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 创建活动 router.post('/activities', auth, async (req, res) => { try { const activity = await Activity.create(req.body); res.json(activity); } catch (error) { res.status(500).json({ error: error.message }); } }); // 编辑活动 router.put('/activities/:id', auth, async (req, res) => { try { await Activity.update(req.body, { where: { id: req.params.id } }); const activity = await Activity.findByPk(req.params.id); res.json(activity); } catch (error) { res.status(500).json({ error: error.message }); } }); // 审核报名 router.put('/registrations/:id', auth, async (req, res) => { try { const { status, peopleCount, carNumber, groupStatus } = req.body; const updateData = {}; if (status) updateData.status = status; if (peopleCount !== undefined) updateData.peopleCount = peopleCount; if (carNumber !== undefined) updateData.carNumber = carNumber; if (groupStatus !== undefined) updateData.groupStatus = groupStatus; await Registration.update(updateData, { where: { id: req.params.id } }); const registration = await Registration.findByPk(req.params.id); res.json(registration); } catch (error) { res.status(500).json({ error: error.message }); } }); // 签到/取消签到 router.put('/registrations/:id/checkin', auth, async (req, res) => { try { const registration = await Registration.findByPk(req.params.id); if (!registration) { return res.status(404).json({ error: '报名信息不存在' }); } const newStatus = !registration.checkInStatus; await Registration.update({ checkInStatus: newStatus, checkInTime: newStatus ? new Date() : null }, { where: { id: req.params.id } }); const updated = await Registration.findByPk(req.params.id); res.json(updated); } catch (error) { res.status(500).json({ error: error.message }); } }); // 进群/取消进群 router.put('/registrations/:id/group', auth, async (req, res) => { try { const registration = await Registration.findByPk(req.params.id); if (!registration) { return res.status(404).json({ error: '报名信息不存在' }); } const newStatus = !registration.groupStatus; await Registration.update({ groupStatus: newStatus }, { where: { id: req.params.id } }); const updated = await Registration.findByPk(req.params.id); res.json(updated); } catch (error) { res.status(500).json({ error: error.message }); } }); // 获取所有报名(含待审核) router.get('/registrations', auth, async (req, res) => { try { const { activityId } = req.query; const where = activityId ? { activityId } : {}; const registrations = await Registration.findAll({ where, order: [['createdAt', 'DESC']] }); res.json(registrations); } catch (error) { res.status(500).json({ error: error.message }); } }); // 删除报名 router.delete('/registrations/:id', auth, async (req, res) => { try { await Registration.destroy({ where: { id: req.params.id } }); res.json({ message: '删除成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 上传经费明细 router.post('/expenses', auth, upload.array('media', 5), async (req, res) => { try { const { activityId, item, amount, category, note } = req.body; let receipt = null; let media = []; if (req.files && req.files.length > 0) { const uploaded = getFileUrls(req.files); media = uploaded.map(file => ({ url: file.url, type: file.mimetype.startsWith('video/') ? 'video' : 'image' })); } const expense = await Expense.create({ activityId, item, amount, category, receipt, note, media }); res.json(expense); } catch (error) { res.status(500).json({ error: error.message }); } }); // 获取经费列表 router.get('/expenses', auth, async (req, res) => { try { const { activityId } = req.query; const where = activityId ? { activityId } : {}; const expenses = await Expense.findAll({ where, raw: true }); // 手动解析JSON字段 const parsedExpenses = expenses.map(expense => ({ ...expense, media: typeof expense.media === 'string' ? JSON.parse(expense.media || '[]') : (expense.media || []) })); res.json(parsedExpenses); } catch (error) { res.status(500).json({ error: error.message }); } }); // 更新经费 router.put('/expenses/:id', auth, upload.array('media', 5), async (req, res) => { try { const { item, amount, category, note, existingMedia } = req.body; const updateData = { item, amount, category, note }; // 处理现有媒体 let media = []; if (existingMedia) { try { media = JSON.parse(existingMedia); } catch (e) { media = []; } } // 处理新上传的媒体 if (req.files && req.files.length > 0) { const uploaded = getFileUrls(req.files); const newMedia = uploaded.map(file => ({ url: file.url, type: file.mimetype.startsWith('video/') ? 'video' : 'image' })); media = [...media, ...newMedia].slice(0, 5); // 限制最多5个 } updateData.media = media; await Expense.update(updateData, { where: { id: req.params.id } }); const expense = await Expense.findByPk(req.params.id); res.json(expense); } catch (error) { res.status(500).json({ error: error.message }); } }); // 删除经费 router.delete('/expenses/:id', auth, async (req, res) => { try { await Expense.destroy({ where: { id: req.params.id } }); res.json({ message: '删除成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 上传活动照片 router.post('/photos', auth, upload.array('photos', 10), async (req, res) => { try { const { activityId } = req.body; const uploaded = getFileUrls(req.files); const photos = uploaded.map(file => ({ activityId, url: file.url, type: /\.(mp4|mov|avi)$/i.test(file.filename) ? 'video' : 'image', status: 'approved' })); const created = await Photo.bulkCreate(photos); res.json(created); } catch (error) { res.status(500).json({ error: error.message }); } }); // 获取待审核照片 router.get('/photos/pending', auth, async (req, res) => { try { const photos = await Photo.findAll({ where: { status: 'pending' }, order: [['createdAt', 'DESC']] }); res.json(photos); } catch (error) { res.status(500).json({ error: error.message }); } }); // 审核照片 router.put('/photos/:id', auth, async (req, res) => { try { const { status } = req.body; await Photo.update({ status }, { where: { id: req.params.id } }); const photo = await Photo.findByPk(req.params.id); res.json(photo); } catch (error) { res.status(500).json({ error: error.message }); } }); // 删除照片 router.delete('/photos/:id', auth, async (req, res) => { try { const photo = await Photo.findByPk(req.params.id); if (!photo) { return res.status(404).json({ error: '照片不存在' }); } // 删除本地文件 const filename = photo.url.split('/').pop(); const filePath = path.join(__dirname, '../uploads', filename); if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } // 从数据库删除记录 await photo.destroy(); res.json({ message: '删除成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 添加赞助商 router.post('/sponsors', auth, upload.single('logo'), async (req, res) => { try { const { activityId, name, type, link, sortOrder } = req.body; let logo = null; if (req.file) { const [uploaded] = getFileUrls(req.file); logo = uploaded.url; } const sponsor = await Sponsor.create({ activityId, name, logo, type, link, sortOrder: sortOrder || 0 }); res.json(sponsor); } catch (error) { res.status(500).json({ error: error.message }); } }); // 更新赞助商 router.put('/sponsors/:id', auth, upload.single('logo'), async (req, res) => { try { const { name, type, link, sortOrder, removeLogo } = req.body; const updateData = { name, type, link }; if (sortOrder !== undefined) { updateData.sortOrder = sortOrder; } if (req.file) { const [uploaded] = getFileUrls(req.file); updateData.logo = uploaded.url; } else if (removeLogo === 'true') { updateData.logo = null; } await Sponsor.update(updateData, { where: { id: req.params.id } }); const sponsor = await Sponsor.findByPk(req.params.id); res.json(sponsor); } catch (error) { res.status(500).json({ error: error.message }); } }); // 删除赞助商 router.delete('/sponsors/:id', auth, async (req, res) => { try { await Sponsor.destroy({ where: { id: req.params.id } }); res.json({ message: '删除成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // 添加奖品 router.post('/prizes', auth, upload.single('image'), async (req, res) => { try { const { activityId, name, level, description, quantity, sortOrder, source, sponsor } = req.body; let image = null; if (req.file) { const [uploaded] = getFileUrls([req.file]); image = uploaded.url; } const prize = await Prize.create({ activityId, name, level, description, image, quantity: quantity || 1, sortOrder: sortOrder || 0, source: source || 'purchase', sponsor: sponsor || null }); res.json(prize); } catch (error) { res.status(500).json({ error: error.message }); } }); // 更新奖品 router.put('/prizes/:id', auth, upload.single('image'), async (req, res) => { try { const { name, level, description, quantity, sortOrder, source, sponsor, removeImage } = req.body; const updateData = { name, level, description, quantity, sortOrder, source, sponsor }; if (req.file) { const [uploaded] = getFileUrls([req.file]); updateData.image = uploaded.url; } else if (removeImage === 'true') { updateData.image = null; } await Prize.update(updateData, { where: { id: req.params.id } }); const prize = await Prize.findByPk(req.params.id); res.json(prize); } catch (error) { res.status(500).json({ error: error.message }); } }); // 删除奖品 router.delete('/prizes/:id', auth, async (req, res) => { try { await Prize.destroy({ where: { id: req.params.id } }); res.json({ message: '删除成功' }); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router;