dvc890 commited on
Commit
e10a55f
·
verified ·
1 Parent(s): 21df6a2

Upload 45 files

Browse files
Files changed (2) hide show
  1. pages/AchievementTeacher.tsx +1 -1
  2. server.js +95 -0
pages/AchievementTeacher.tsx CHANGED
@@ -47,7 +47,7 @@ export const AchievementTeacher: React.FC<{className?: string}> = ({ className }
47
  const stus = await api.students.getAll();
48
  // Filter students for homeroom & Sort by SeatNo > Name
49
  const sortedStudents = stus
50
- .filter((s: Student) => s.className === homeroomClass)
51
  .sort((a: Student, b: Student) => {
52
  const seatA = parseInt(a.seatNo || '99999');
53
  const seatB = parseInt(b.seatNo || '99999');
 
47
  const stus = await api.students.getAll();
48
  // Filter students for homeroom & Sort by SeatNo > Name
49
  const sortedStudents = stus
50
+ .filter((s: Student) => s.className.trim() === homeroomClass.trim())
51
  .sort((a: Student, b: Student) => {
52
  const seatA = parseInt(a.seatNo || '99999');
53
  const seatB = parseInt(b.seatNo || '99999');
server.js CHANGED
@@ -654,6 +654,101 @@ app.post('/api/games/grant-reward', async (req, res) => {
654
  res.json({});
655
  });
656
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
  // Update Class (Support multi-teachers)
658
  app.put('/api/classes/:id', async (req, res) => {
659
  const classId = req.params.id;
 
654
  res.json({});
655
  });
656
 
657
+ // ACHIEVEMENT ROUTES (New)
658
+ app.get('/api/achievements/config', async (req, res) => {
659
+ const { className } = req.query;
660
+ const filter = getQueryFilter(req);
661
+ if (className) filter.className = className;
662
+ res.json(await AchievementConfigModel.findOne(filter));
663
+ });
664
+
665
+ app.post('/api/achievements/config', async (req, res) => {
666
+ const data = injectSchoolId(req, req.body);
667
+ await AchievementConfigModel.findOneAndUpdate(
668
+ { className: data.className, ...getQueryFilter(req) },
669
+ data,
670
+ { upsert: true }
671
+ );
672
+ res.json({ success: true });
673
+ });
674
+
675
+ app.get('/api/achievements/student', async (req, res) => {
676
+ const { studentId, semester } = req.query;
677
+ const filter = { studentId };
678
+ if (semester) filter.semester = semester;
679
+ res.json(await StudentAchievementModel.find(filter).sort({ createTime: -1 }));
680
+ });
681
+
682
+ app.post('/api/achievements/grant', async (req, res) => {
683
+ const { studentId, achievementId, semester } = req.body;
684
+ const sId = req.headers['x-school-id'];
685
+
686
+ const student = await Student.findById(studentId);
687
+ if (!student) return res.status(404).json({ error: 'Student not found' });
688
+
689
+ const config = await AchievementConfigModel.findOne({ className: student.className, schoolId: sId });
690
+ const achievement = config?.achievements.find(a => a.id === achievementId);
691
+
692
+ if (!achievement) return res.status(404).json({ error: 'Achievement not found' });
693
+
694
+ // Add Record
695
+ await StudentAchievementModel.create({
696
+ schoolId: sId,
697
+ studentId,
698
+ studentName: student.name,
699
+ achievementId: achievement.id,
700
+ achievementName: achievement.name,
701
+ achievementIcon: achievement.icon,
702
+ semester,
703
+ createTime: new Date()
704
+ });
705
+
706
+ // Add Points (Flowers)
707
+ await Student.findByIdAndUpdate(studentId, { $inc: { flowerBalance: achievement.points } });
708
+
709
+ res.json({ success: true });
710
+ });
711
+
712
+ app.post('/api/achievements/exchange', async (req, res) => {
713
+ const { studentId, ruleId } = req.body;
714
+ const sId = req.headers['x-school-id'];
715
+
716
+ const student = await Student.findById(studentId);
717
+ if (!student) return res.status(404).json({ error: 'Student not found' });
718
+
719
+ const config = await AchievementConfigModel.findOne({ className: student.className, schoolId: sId });
720
+ const rule = config?.exchangeRules.find(r => r.id === ruleId);
721
+
722
+ if (!rule) return res.status(404).json({ error: 'Rule not found' });
723
+
724
+ if (student.flowerBalance < rule.cost) {
725
+ return res.status(400).json({ error: 'INSUFFICIENT_FUNDS', message: '小红花余额不足' });
726
+ }
727
+
728
+ // Deduct Flowers
729
+ await Student.findByIdAndUpdate(studentId, { $inc: { flowerBalance: -rule.cost } });
730
+
731
+ // Add Reward Record (using StudentRewardModel so it appears in "GameRewards" / "Shop History")
732
+ await StudentRewardModel.create({
733
+ schoolId: sId,
734
+ studentId,
735
+ studentName: student.name,
736
+ rewardType: rule.rewardType, // ITEM or DRAW_COUNT
737
+ name: rule.rewardName,
738
+ count: rule.rewardValue,
739
+ status: rule.rewardType === 'DRAW_COUNT' ? 'REDEEMED' : 'PENDING',
740
+ source: '积分兑换',
741
+ createTime: new Date()
742
+ });
743
+
744
+ // If Draw Count, add attempts immediately
745
+ if (rule.rewardType === 'DRAW_COUNT') {
746
+ await Student.findByIdAndUpdate(studentId, { $inc: { drawAttempts: rule.rewardValue } });
747
+ }
748
+
749
+ res.json({ success: true });
750
+ });
751
+
752
  // Update Class (Support multi-teachers)
753
  app.put('/api/classes/:id', async (req, res) => {
754
  const classId = req.params.id;