Преглед изворни кода

✨ feat(exam): 实现答题结果保存到数据库功能

- 添加saveAnswersToSubmissionRecords方法,将Redis中的答题数据保存到数据库
- 在清理房间数据前自动保存答题结果,确保数据不丢失
- 实现calculateScore方法计算答题得分,基于收益率进行评分
- 导入SubmissionRecords实体和数据库连接,支持批量保存提交记录
yourname пре 6 месеци
родитељ
комит
0285958b12
1 измењених фајлова са 68 додато и 1 уклоњено
  1. 68 1
      src/server/socket/services/exam.service.ts

+ 68 - 1
src/server/socket/services/exam.service.ts

@@ -1,6 +1,8 @@
 import { Server } from 'socket.io';
 import { Server } from 'socket.io';
 import { AuthenticatedSocket } from '../middleware/auth.middleware';
 import { AuthenticatedSocket } from '../middleware/auth.middleware';
 import { redisService } from './redis.service';
 import { redisService } from './redis.service';
+import { AppDataSource } from '@/server/data-source';
+import { SubmissionRecords } from '@/server/modules/submission/submission-records.entity';
 import debug from 'debug';
 import debug from 'debug';
 
 
 const log = debug('socket:exam');
 const log = debug('socket:exam');
@@ -147,11 +149,15 @@ export class ExamService {
       if (!socket.user) throw new Error('用户未认证');
       if (!socket.user) throw new Error('用户未认证');
       
       
       const user = socket.user;
       const user = socket.user;
+      
+      // 在清理Redis数据前,先保存答题结果到数据库
+      await this.saveAnswersToSubmissionRecords(roomId, questionId);
+      
       await redisService.cleanupRoomData(roomId, questionId);
       await redisService.cleanupRoomData(roomId, questionId);
       
       
       socket.to(roomId).emit('exam:cleaned', {
       socket.to(roomId).emit('exam:cleaned', {
         roomId,
         roomId,
-        message: questionId 
+        message: questionId
           ? `已清理房间 ${roomId} 的问题 ${questionId} 数据`
           ? `已清理房间 ${roomId} 的问题 ${questionId} 数据`
           : `已清理房间 ${roomId} 的所有数据`
           : `已清理房间 ${roomId} 的所有数据`
       });
       });
@@ -176,4 +182,65 @@ export class ExamService {
       socket.emit('error', '广播结算消息失败');
       socket.emit('error', '广播结算消息失败');
     }
     }
   }
   }
+
+  /**
+   * 保存答题结果到提交记录表
+   */
+  private async saveAnswersToSubmissionRecords(roomId: string, questionId?: string): Promise<void> {
+    try {
+      // 获取所有答题数据
+      const answers = await redisService.getAnswers(roomId, questionId);
+      
+      if (answers.length === 0) {
+        log(`房间 ${roomId} 没有答题数据需要保存`);
+        return;
+      }
+
+      const submissionRecordsRepository = AppDataSource.getRepository(SubmissionRecords);
+      const recordsToSave: SubmissionRecords[] = [];
+
+      for (const answer of answers) {
+        // 转换Redis中的答案数据为提交记录实体
+        const submissionRecord = new SubmissionRecords();
+        submissionRecord.classroomNo = roomId;
+        submissionRecord.userId = answer.userId?.toString() || null;
+        submissionRecord.nickname = answer.username || null;
+        submissionRecord.score = this.calculateScore(answer);
+        submissionRecord.code = answer.code || null;
+        submissionRecord.trainingDate = answer.date ? new Date(answer.date) : null;
+        submissionRecord.mark = null; // 标记字段,可根据需要设置
+        submissionRecord.status = 1; // 状态:1-正常
+        submissionRecord.holdingStock = answer.holdingStock || null;
+        submissionRecord.holdingCash = answer.holdingCash || null;
+        submissionRecord.price = answer.price ? parseFloat(answer.price) : null;
+        submissionRecord.profitAmount = answer.profitAmount || null;
+        submissionRecord.profitPercent = answer.profitPercent || null;
+        submissionRecord.totalProfitAmount = answer.totalProfitAmount || null;
+        submissionRecord.totalProfitPercent = answer.totalProfitPercent || null;
+
+        recordsToSave.push(submissionRecord);
+      }
+
+      // 批量保存到数据库
+      if (recordsToSave.length > 0) {
+        await submissionRecordsRepository.save(recordsToSave);
+        log(`成功保存 ${recordsToSave.length} 条答题记录到数据库,房间: ${roomId}`);
+      }
+    } catch (error) {
+      log('保存答题记录到数据库失败:', error);
+      // 不抛出错误,避免影响正常的清理操作
+    }
+  }
+
+  /**
+   * 计算得分(可根据业务需求自定义评分逻辑)
+   */
+  private calculateScore(answer: any): number | null {
+    // 这里可以根据答题内容计算得分
+    // 示例:根据收益率计算得分,收益率越高得分越高
+    if (answer.profitPercent !== undefined && answer.profitPercent !== null) {
+      return Math.max(0, Math.min(100, 50 + (answer.profitPercent * 2)));
+    }
+    return null;
+  }
 }
 }