sync(theme-3): 同步 main 最新 API/Admin 与玩家端逻辑

从 main 同步站内信手动发送、媒体库选择、列表子路由重构等 API/Admin 改动;玩家端仅合并 ADMIN_CUSTOM 富文本、消息预览与充值截图压缩逻辑,保留 theme-3 样式。
This commit is contained in:
2026-06-22 14:11:47 +08:00
parent f92c5e8a59
commit d8e08aaed0
50 changed files with 4653 additions and 1186 deletions

View File

@@ -388,6 +388,83 @@ export class SettlementService {
};
}
async getActivePreview(
matchId: bigint,
opts?: { page?: number; pageSize?: number },
) {
const batch = await this.prisma.settlementBatch.findFirst({
where: { matchId, status: 'PREVIEW' },
orderBy: { createdAt: 'desc' },
});
if (!batch) return null;
const existingScore = await this.prisma.matchScore.findUnique({
where: { matchId },
});
const computation = await this.computePreviewComputation(matchId, {
htHome: batch.htHomeScore ?? 0,
htAway: batch.htAwayScore ?? 0,
ftHome: batch.ftHomeScore ?? 0,
ftAway: batch.ftAwayScore ?? 0,
homeCorners: batch.homeCorners ?? null,
awayCorners: batch.awayCorners ?? null,
homeYellowCards: batch.homeYellowCards ?? null,
awayYellowCards: batch.awayYellowCards ?? null,
homeRedCards: batch.homeRedCards ?? null,
awayRedCards: batch.awayRedCards ?? null,
homeCards: batch.homeCards ?? null,
awayCards: batch.awayCards ?? null,
winnerTeamId: existingScore?.winnerTeamId ?? null,
});
return this.buildPreviewResponse(computation, batch, opts);
}
async getMatchSettlementHistory(matchId: bigint) {
const batches = await this.prisma.settlementBatch.findMany({
where: { matchId, status: 'CONFIRMED' },
orderBy: { confirmedAt: 'desc' },
});
const operatorIds = batches
.map((b) => b.operatorId)
.filter((id): id is bigint => id !== null);
const operators =
operatorIds.length > 0
? await this.prisma.user.findMany({
where: { id: { in: operatorIds } },
select: { id: true, username: true },
})
: [];
const operatorMap = new Map(operators.map((o) => [o.id.toString(), o.username]));
return batches.map((b) => ({
id: b.id.toString(),
batchNo: b.batchNo,
htHomeScore: b.htHomeScore,
htAwayScore: b.htAwayScore,
ftHomeScore: b.ftHomeScore,
ftAwayScore: b.ftAwayScore,
homeCorners: b.homeCorners,
awayCorners: b.awayCorners,
homeYellowCards: b.homeYellowCards,
awayYellowCards: b.awayYellowCards,
homeRedCards: b.homeRedCards,
awayRedCards: b.awayRedCards,
homeCards: b.homeCards,
awayCards: b.awayCards,
totalBets: b.totalBets,
totalPayout: b.totalPayout.toString(),
totalRefund: b.totalRefund.toString(),
confirmedAt: b.confirmedAt?.toISOString() ?? null,
isResettle: b.isResettle,
reason: b.reason,
operatorUsername: b.operatorId ? operatorMap.get(b.operatorId.toString()) ?? '—' : '—',
}));
}
private buildPreviewResponse(
computation: {
scoreInput: ScoreInput;