Spaces:
Running
Running
Update routes/features.js
Browse files- routes/features.js +52 -2
routes/features.js
CHANGED
|
@@ -122,7 +122,57 @@ router.post('/games/monster-config', async (req, res) => { const data = injectSc
|
|
| 122 |
router.get('/games/zen-config', async (req, res) => { const filter = getQueryFilter(req); const ownerFilter = await getGameOwnerFilter(req); if (req.query.className) filter.className = req.query.className; Object.assign(filter, ownerFilter); const config = await GameZenConfigModel.findOne(filter); res.json(config || {}); });
|
| 123 |
router.post('/games/zen-config', async (req, res) => { const data = injectSchoolId(req, req.body); if (req.headers['x-user-role'] === 'TEACHER') { const user = await User.findOne({ username: req.headers['x-user-username'] }); data.ownerId = user ? user._id.toString() : null; } await GameZenConfigModel.findOneAndUpdate({ className: data.className, ...getQueryFilter(req), ownerId: data.ownerId }, data, { upsert: true }); res.json({ success: true }); });
|
| 124 |
router.get('/games/mountain', async (req, res) => { res.json(await GameSessionModel.findOne({...getQueryFilter(req), className: req.query.className})); });
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
router.get('/rewards', async (req, res) => { const filter = getQueryFilter(req); if (req.headers['x-user-role'] === 'TEACHER') { const user = await User.findOne({ username: req.headers['x-user-username'] }); if (user) filter.ownerId = user._id.toString(); } if(req.query.studentId) filter.studentId = req.query.studentId; if (req.query.className) { const classStudents = await Student.find({ className: req.query.className, ...getQueryFilter(req) }, '_id'); filter.studentId = { $in: classStudents.map(s => s._id.toString()) }; } if (req.query.excludeType) filter.rewardType = { $ne: req.query.excludeType }; const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 20; const skip = (page - 1) * limit; const total = await StudentRewardModel.countDocuments(filter); const list = await StudentRewardModel.find(filter).sort({createTime:-1}).skip(skip).limit(limit); res.json({ list, total }); });
|
| 127 |
router.post('/rewards', async (req, res) => { const data = injectSchoolId(req, req.body); if (!data.count) data.count = 1; if (req.headers['x-user-role'] === 'TEACHER') { const user = await User.findOne({ username: req.headers['x-user-username'] }); data.ownerId = user ? user._id.toString() : null; } if(data.rewardType==='DRAW_COUNT') { data.status='REDEEMED'; await Student.findByIdAndUpdate(data.studentId, {$inc:{drawAttempts:data.count}}); } await StudentRewardModel.create(data); res.json({}); });
|
| 128 |
router.put('/rewards/:id', async (req, res) => { await StudentRewardModel.findByIdAndUpdate(req.params.id, req.body); res.json({}); });
|
|
@@ -204,4 +254,4 @@ router.delete('/achievements/record/:id', async (req, res) => {
|
|
| 204 |
});
|
| 205 |
router.post('/achievements/exchange', async (req, res) => { const { studentId, ruleId, teacherId } = req.body; const sId = req.headers['x-school-id']; const student = await Student.findById(studentId); if (!student) return res.status(404).json({ error: 'Student not found' }); let rule = null; let ownerId = null; if (teacherId) { const tConfig = await TeacherExchangeConfigModel.findOne({ teacherId, schoolId: sId }); rule = tConfig?.rules.find(r => r.id === ruleId); ownerId = teacherId; } else { const config = await AchievementConfigModel.findOne({ className: student.className, schoolId: sId }); rule = config?.exchangeRules?.find(r => r.id === ruleId); } if (!rule) return res.status(404).json({ error: 'Rule not found' }); if (student.flowerBalance < rule.cost) { return res.status(400).json({ error: 'INSUFFICIENT_FUNDS', message: '小红花余额不足' }); } await Student.findByIdAndUpdate(studentId, { $inc: { flowerBalance: -rule.cost } }); await StudentRewardModel.create({ schoolId: sId, studentId, studentName: student.name, rewardType: rule.rewardType, name: rule.rewardName, count: rule.rewardValue, status: rule.rewardType === 'DRAW_COUNT' ? 'REDEEMED' : 'PENDING', source: '积分兑换', ownerId, createTime: new Date() }); if (rule.rewardType === 'DRAW_COUNT') { await Student.findByIdAndUpdate(studentId, { $inc: { drawAttempts: rule.rewardValue } }); } res.json({ success: true }); });
|
| 206 |
|
| 207 |
-
module.exports = router;
|
|
|
|
| 122 |
router.get('/games/zen-config', async (req, res) => { const filter = getQueryFilter(req); const ownerFilter = await getGameOwnerFilter(req); if (req.query.className) filter.className = req.query.className; Object.assign(filter, ownerFilter); const config = await GameZenConfigModel.findOne(filter); res.json(config || {}); });
|
| 123 |
router.post('/games/zen-config', async (req, res) => { const data = injectSchoolId(req, req.body); if (req.headers['x-user-role'] === 'TEACHER') { const user = await User.findOne({ username: req.headers['x-user-username'] }); data.ownerId = user ? user._id.toString() : null; } await GameZenConfigModel.findOneAndUpdate({ className: data.className, ...getQueryFilter(req), ownerId: data.ownerId }, data, { upsert: true }); res.json({ success: true }); });
|
| 124 |
router.get('/games/mountain', async (req, res) => { res.json(await GameSessionModel.findOne({...getQueryFilter(req), className: req.query.className})); });
|
| 125 |
+
|
| 126 |
+
// Updated mountain game handler with robust permission check
|
| 127 |
+
router.post('/games/mountain', async (req, res) => {
|
| 128 |
+
const { className } = req.body;
|
| 129 |
+
const sId = req.headers['x-school-id'];
|
| 130 |
+
const role = req.headers['x-user-role'];
|
| 131 |
+
const username = req.headers['x-user-username'];
|
| 132 |
+
|
| 133 |
+
if (role === 'TEACHER') {
|
| 134 |
+
const user = await User.findOne({ username });
|
| 135 |
+
|
| 136 |
+
let isAuthorized = false;
|
| 137 |
+
|
| 138 |
+
// 1. Check User Profile (Primary authority)
|
| 139 |
+
if (user && user.homeroomClass === className) {
|
| 140 |
+
isAuthorized = true;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
// 2. Check Class Database (Fallback for legacy/ID-linked)
|
| 144 |
+
if (!isAuthorized) {
|
| 145 |
+
const cls = await ClassModel.findOne({
|
| 146 |
+
schoolId: sId,
|
| 147 |
+
$or: [
|
| 148 |
+
{ $expr: { $eq: [{ $concat: ["$grade", "$className"] }, className] } },
|
| 149 |
+
{ className: className }
|
| 150 |
+
]
|
| 151 |
+
});
|
| 152 |
+
|
| 153 |
+
if (cls) {
|
| 154 |
+
const allowedIds = cls.homeroomTeacherIds || [];
|
| 155 |
+
if (allowedIds.includes(user._id.toString())) {
|
| 156 |
+
isAuthorized = true;
|
| 157 |
+
} else if (cls.teacherName && (cls.teacherName.includes(user.trueName) || cls.teacherName.includes(user.username))) {
|
| 158 |
+
// Legacy Name Match
|
| 159 |
+
isAuthorized = true;
|
| 160 |
+
}
|
| 161 |
+
} else {
|
| 162 |
+
// If class not found in DB but user has it in profile, we allow (user is creator/authority)
|
| 163 |
+
// But typically class should exist. We'll stick to logic above.
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
if (!isAuthorized) {
|
| 168 |
+
return res.status(403).json({ error: 'PERMISSION_DENIED', message: '只有班主任可以操作登峰游戏' });
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
await GameSessionModel.findOneAndUpdate({ className, ...getQueryFilter(req) }, injectSchoolId(req, req.body), {upsert:true});
|
| 173 |
+
res.json({});
|
| 174 |
+
});
|
| 175 |
+
|
| 176 |
router.get('/rewards', async (req, res) => { const filter = getQueryFilter(req); if (req.headers['x-user-role'] === 'TEACHER') { const user = await User.findOne({ username: req.headers['x-user-username'] }); if (user) filter.ownerId = user._id.toString(); } if(req.query.studentId) filter.studentId = req.query.studentId; if (req.query.className) { const classStudents = await Student.find({ className: req.query.className, ...getQueryFilter(req) }, '_id'); filter.studentId = { $in: classStudents.map(s => s._id.toString()) }; } if (req.query.excludeType) filter.rewardType = { $ne: req.query.excludeType }; const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 20; const skip = (page - 1) * limit; const total = await StudentRewardModel.countDocuments(filter); const list = await StudentRewardModel.find(filter).sort({createTime:-1}).skip(skip).limit(limit); res.json({ list, total }); });
|
| 177 |
router.post('/rewards', async (req, res) => { const data = injectSchoolId(req, req.body); if (!data.count) data.count = 1; if (req.headers['x-user-role'] === 'TEACHER') { const user = await User.findOne({ username: req.headers['x-user-username'] }); data.ownerId = user ? user._id.toString() : null; } if(data.rewardType==='DRAW_COUNT') { data.status='REDEEMED'; await Student.findByIdAndUpdate(data.studentId, {$inc:{drawAttempts:data.count}}); } await StudentRewardModel.create(data); res.json({}); });
|
| 178 |
router.put('/rewards/:id', async (req, res) => { await StudentRewardModel.findByIdAndUpdate(req.params.id, req.body); res.json({}); });
|
|
|
|
| 254 |
});
|
| 255 |
router.post('/achievements/exchange', async (req, res) => { const { studentId, ruleId, teacherId } = req.body; const sId = req.headers['x-school-id']; const student = await Student.findById(studentId); if (!student) return res.status(404).json({ error: 'Student not found' }); let rule = null; let ownerId = null; if (teacherId) { const tConfig = await TeacherExchangeConfigModel.findOne({ teacherId, schoolId: sId }); rule = tConfig?.rules.find(r => r.id === ruleId); ownerId = teacherId; } else { const config = await AchievementConfigModel.findOne({ className: student.className, schoolId: sId }); rule = config?.exchangeRules?.find(r => r.id === ruleId); } if (!rule) return res.status(404).json({ error: 'Rule not found' }); if (student.flowerBalance < rule.cost) { return res.status(400).json({ error: 'INSUFFICIENT_FUNDS', message: '小红花余额不足' }); } await Student.findByIdAndUpdate(studentId, { $inc: { flowerBalance: -rule.cost } }); await StudentRewardModel.create({ schoolId: sId, studentId, studentName: student.name, rewardType: rule.rewardType, name: rule.rewardName, count: rule.rewardValue, status: rule.rewardType === 'DRAW_COUNT' ? 'REDEEMED' : 'PENDING', source: '积分兑换', ownerId, createTime: new Date() }); if (rule.rewardType === 'DRAW_COUNT') { await Student.findByIdAndUpdate(studentId, { $inc: { drawAttempts: rule.rewardValue } }); } res.json({ success: true }); });
|
| 256 |
|
| 257 |
+
module.exports = router;
|