perf: 支持分享提交答案的接口

This commit is contained in:
richarjiang
2026-05-10 16:04:21 +08:00
parent 8443f8844d
commit 642ccd31d3
15 changed files with 917 additions and 447 deletions

View File

@@ -8,6 +8,14 @@ type ShareParticipantCountRow = {
participantCount: string;
};
export type ShareParticipantRankingRow = {
shareConfigId: string;
participantId: string;
correctCount: number;
totalTimeSpent: number;
submittedAt: Date;
};
@Injectable()
export class ShareParticipantRepository {
constructor(
@@ -55,6 +63,107 @@ export class ShareParticipantRepository {
);
}
async upsertSubmissionSummary(data: {
shareConfigId: string;
participantId: string;
correctCount: number;
totalTimeSpent: number;
submittedAt: Date;
}): Promise<void> {
await this.repository
.createQueryBuilder()
.insert()
.into(ShareParticipant)
.values(data)
.orUpdate(
['correctCount', 'totalTimeSpent', 'submittedAt'],
['shareConfigId', 'participantId'],
)
.execute();
}
async countSubmittedByShareConfigId(shareConfigId: string): Promise<number> {
return this.repository
.createQueryBuilder('participant')
.where('participant.shareConfigId = :shareConfigId', { shareConfigId })
.andWhere('participant.submittedAt IS NOT NULL')
.getCount();
}
async countSubmittedByShareConfigIds(
shareConfigIds: string[],
): Promise<Map<string, number>> {
if (shareConfigIds.length === 0) {
return new Map();
}
const rows = await this.repository
.createQueryBuilder('participant')
.select('participant.shareConfigId', 'shareConfigId')
.addSelect('COUNT(*)', 'participantCount')
.where('participant.shareConfigId IN (:...shareConfigIds)', {
shareConfigIds,
})
.andWhere('participant.submittedAt IS NOT NULL')
.groupBy('participant.shareConfigId')
.getRawMany<ShareParticipantCountRow>();
return new Map(
rows.map((row) => [row.shareConfigId, Number(row.participantCount)]),
);
}
async findSubmittedRankingsByShareConfigId(
shareConfigId: string,
): Promise<ShareParticipantRankingRow[]> {
const rows = await this.repository
.createQueryBuilder('participant')
.where('participant.shareConfigId = :shareConfigId', { shareConfigId })
.andWhere('participant.submittedAt IS NOT NULL')
.orderBy('participant.correctCount', 'DESC')
.addOrderBy('participant.totalTimeSpent', 'ASC')
.addOrderBy('participant.submittedAt', 'ASC')
.addOrderBy('participant.participantId', 'ASC')
.getMany();
return rows.map((row) => ({
shareConfigId: row.shareConfigId,
participantId: row.participantId,
correctCount: row.correctCount,
totalTimeSpent: row.totalTimeSpent,
submittedAt: row.submittedAt!,
}));
}
async findSubmittedRankingsByShareConfigIds(
shareConfigIds: string[],
): Promise<ShareParticipantRankingRow[]> {
if (shareConfigIds.length === 0) {
return [];
}
const rows = await this.repository
.createQueryBuilder('participant')
.where('participant.shareConfigId IN (:...shareConfigIds)', {
shareConfigIds,
})
.andWhere('participant.submittedAt IS NOT NULL')
.orderBy('participant.shareConfigId', 'ASC')
.addOrderBy('participant.correctCount', 'DESC')
.addOrderBy('participant.totalTimeSpent', 'ASC')
.addOrderBy('participant.submittedAt', 'ASC')
.addOrderBy('participant.participantId', 'ASC')
.getMany();
return rows.map((row) => ({
shareConfigId: row.shareConfigId,
participantId: row.participantId,
correctCount: row.correctCount,
totalTimeSpent: row.totalTimeSpent,
submittedAt: row.submittedAt!,
}));
}
async existsByShareConfigAndParticipant(
shareConfigId: string,
participantId: string,