|
@@ -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;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|