177 lines
5.2 KiB
TypeScript
177 lines
5.2 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository } from 'typeorm';
|
|
import { ShareParticipant } from '../entities/share-participant.entity';
|
|
|
|
type ShareParticipantCountRow = {
|
|
shareConfigId: string;
|
|
participantCount: string;
|
|
};
|
|
|
|
export type ShareParticipantRankingRow = {
|
|
shareConfigId: string;
|
|
participantId: string;
|
|
correctCount: number;
|
|
totalTimeSpent: number;
|
|
submittedAt: Date;
|
|
};
|
|
|
|
@Injectable()
|
|
export class ShareParticipantRepository {
|
|
constructor(
|
|
@InjectRepository(ShareParticipant)
|
|
private readonly repository: Repository<ShareParticipant>,
|
|
) {}
|
|
|
|
/** 添加参与者(已存在则忽略) */
|
|
async addParticipant(
|
|
shareConfigId: string,
|
|
participantId: string,
|
|
): Promise<void> {
|
|
await this.repository
|
|
.createQueryBuilder()
|
|
.insert()
|
|
.into(ShareParticipant)
|
|
.values({ shareConfigId, participantId })
|
|
.orIgnore()
|
|
.execute();
|
|
}
|
|
|
|
async countByShareConfigId(shareConfigId: string): Promise<number> {
|
|
return this.repository.count({ where: { shareConfigId } });
|
|
}
|
|
|
|
async countByShareConfigIds(
|
|
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,
|
|
})
|
|
.groupBy('participant.shareConfigId')
|
|
.getRawMany<ShareParticipantCountRow>();
|
|
|
|
return new Map(
|
|
rows.map((row) => [row.shareConfigId, Number(row.participantCount)]),
|
|
);
|
|
}
|
|
|
|
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,
|
|
): Promise<boolean> {
|
|
const count = await this.repository.count({
|
|
where: { shareConfigId, participantId },
|
|
});
|
|
return count > 0;
|
|
}
|
|
}
|