Initial commit: 全友米项目完整代码
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { Activity, Photo, Sponsor, Expense, Registration } = require('../models');
|
||||
const { upload, uploadFilesToMinio } = require('../middleware/upload');
|
||||
|
||||
// 获取活动列表
|
||||
router.get('/activities', async (req, res) => {
|
||||
try {
|
||||
const { status } = req.query;
|
||||
const where = status ? { status } : {};
|
||||
const activities = await Activity.findAll({ where, order: [['startTime', 'DESC']] });
|
||||
res.json(activities);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取活动详情
|
||||
router.get('/activities/:id', async (req, res) => {
|
||||
try {
|
||||
const activity = await Activity.findByPk(req.params.id);
|
||||
// 只返回已审核通过的照片
|
||||
const photos = await Photo.findAll({
|
||||
where: { activityId: req.params.id, status: 'approved' }
|
||||
});
|
||||
const sponsors = await Sponsor.findAll({ where: { activityId: req.params.id } });
|
||||
const expenses = await Expense.findAll({ where: { activityId: req.params.id } });
|
||||
|
||||
res.json({ activity, photos, sponsors, expenses });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取活动经费(公开接口)
|
||||
router.get('/activities/:id/expenses', async (req, res) => {
|
||||
try {
|
||||
const expenses = await Expense.findAll({ where: { activityId: req.params.id } });
|
||||
res.json(expenses);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 用户上传照片(需要审核)
|
||||
router.post('/activities/:id/photos', upload.array('photos', 10), async (req, res) => {
|
||||
try {
|
||||
const { uploadedBy } = req.body;
|
||||
const uploaded = await uploadFilesToMinio(req.files);
|
||||
const photos = uploaded.map(file => ({
|
||||
activityId: req.params.id,
|
||||
url: file.url,
|
||||
type: /\.(mp4|mov|avi)$/i.test(file.filename) ? 'video' : 'image',
|
||||
uploadedBy,
|
||||
status: 'pending'
|
||||
}));
|
||||
const created = await Photo.bulkCreate(photos);
|
||||
res.json(created);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 用户验证登录
|
||||
router.post('/auth/verify/:activityId', async (req, res) => {
|
||||
try {
|
||||
const { phone, carNumber } = req.body;
|
||||
const { activityId } = req.params;
|
||||
|
||||
console.log('验证请求:', { phone, carNumber, activityId });
|
||||
|
||||
if (!phone || !carNumber) {
|
||||
return res.status(400).json({ error: '请输入手机号和车牌号' });
|
||||
}
|
||||
|
||||
const registration = await Registration.findOne({
|
||||
where: {
|
||||
activityId,
|
||||
phone,
|
||||
carNumber
|
||||
}
|
||||
});
|
||||
|
||||
console.log('查询结果:', registration ? '找到' : '未找到');
|
||||
|
||||
if (!registration) {
|
||||
// 调试:查询该活动下该手机号的所有报名
|
||||
const allRegs = await Registration.findAll({
|
||||
where: { activityId, phone }
|
||||
});
|
||||
console.log('该手机号的所有报名:', allRegs.map(r => ({ id: r.id, carNumber: r.carNumber })));
|
||||
|
||||
return res.status(404).json({ error: '未找到报名信息,请检查车牌号是否正确' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
registration: {
|
||||
id: registration.id,
|
||||
name: registration.name,
|
||||
phone: registration.phone,
|
||||
carNumber: registration.carNumber,
|
||||
carModel: registration.carModel,
|
||||
status: registration.status,
|
||||
activityId: registration.activityId
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('验证错误:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,294 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const bcrypt = require('bcryptjs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const auth = require('../middleware/auth');
|
||||
const { upload, uploadFilesToMinio } = require('../middleware/upload');
|
||||
const { deleteFile } = require('../utils/minio');
|
||||
const { Admin, Activity, Registration, Expense, Photo, Sponsor } = require('../models');
|
||||
|
||||
// 管理员登录
|
||||
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 } });
|
||||
} 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 } = req.body;
|
||||
const updateData = {};
|
||||
if (status) updateData.status = status;
|
||||
if (peopleCount !== undefined) updateData.peopleCount = peopleCount;
|
||||
|
||||
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.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.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 = await uploadFilesToMinio(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 });
|
||||
res.json(expenses);
|
||||
} 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 = await uploadFilesToMinio(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 = await uploadFilesToMinio(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: '照片不存在' });
|
||||
}
|
||||
|
||||
// 从URL提取文件名
|
||||
const filename = photo.url.split('/').pop();
|
||||
|
||||
// 从MinIO删除文件
|
||||
await deleteFile(filename);
|
||||
|
||||
// 从数据库删除记录
|
||||
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 } = req.body;
|
||||
let logo = null;
|
||||
if (req.file) {
|
||||
const [uploaded] = await uploadFilesToMinio(req.file);
|
||||
logo = uploaded.url;
|
||||
}
|
||||
const sponsor = await Sponsor.create({ activityId, name, logo, type, link });
|
||||
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 } = req.body;
|
||||
const updateData = { name, type, link };
|
||||
|
||||
if (req.file) {
|
||||
const [uploaded] = await uploadFilesToMinio(req.file);
|
||||
updateData.logo = uploaded.url;
|
||||
}
|
||||
|
||||
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 });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,47 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { Registration, Activity } = require('../models');
|
||||
|
||||
// 提交报名
|
||||
router.post('/registrations', async (req, res) => {
|
||||
try {
|
||||
const { activityId, name, phone, carModel, carNumber, note } = req.body;
|
||||
|
||||
const activity = await Activity.findByPk(activityId);
|
||||
if (!activity) {
|
||||
return res.status(404).json({ error: '活动不存在' });
|
||||
}
|
||||
|
||||
const registration = await Registration.create({
|
||||
activityId, name, phone, carModel, carNumber, note
|
||||
});
|
||||
|
||||
res.json(registration);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 查询报名状态
|
||||
router.get('/registrations/:id', async (req, res) => {
|
||||
try {
|
||||
const registration = await Registration.findByPk(req.params.id);
|
||||
res.json(registration);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取活动报名列表(已通过)
|
||||
router.get('/activities/:id/registrations', async (req, res) => {
|
||||
try {
|
||||
const registrations = await Registration.findAll({
|
||||
where: { activityId: req.params.id, status: 'approved' }
|
||||
});
|
||||
res.json(registrations);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user