@@ -1525,6 +1525,37 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
payload: MatchPredictionDto,
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Finished-match re-analyses (manual validation runs) must not pollute
|
||||
// the forward track record: they would bias settlement ROI, the
|
||||
// per-league karne and engine-version comparisons. Tag them into their
|
||||
// own engine_version bucket so every GROUP BY engine_version isolates
|
||||
// them automatically — the data is kept, the live karne stays clean.
|
||||
const auditMatch = await this.prisma.match.findUnique({
|
||||
where: { id: matchId },
|
||||
select: {
|
||||
state: true,
|
||||
status: true,
|
||||
scoreHome: true,
|
||||
scoreAway: true,
|
||||
mstUtc: true,
|
||||
},
|
||||
});
|
||||
const kickoffMs =
|
||||
auditMatch?.mstUtc != null ? Number(auditMatch.mstUtc) : null;
|
||||
const kickoffLongPast =
|
||||
kickoffMs !== null && Date.now() - kickoffMs > 3 * 60 * 60 * 1000;
|
||||
const isCompletedRun =
|
||||
isMatchCompleted({
|
||||
state: auditMatch?.state ?? null,
|
||||
status: auditMatch?.status ?? null,
|
||||
scoreHome: auditMatch?.scoreHome,
|
||||
scoreAway: auditMatch?.scoreAway,
|
||||
}) || kickoffLongPast;
|
||||
const baseVersion = String(payload.model_version || "unknown");
|
||||
const engineVersion = isCompletedRun
|
||||
? `${baseVersion}.sim-finished`
|
||||
: baseVersion;
|
||||
|
||||
const oddsSnapshot = await this.getPredictionOddsSnapshot(matchId);
|
||||
const payloadSummary = this.buildPredictionPayloadSummary(payload);
|
||||
await this.prisma.$executeRawUnsafe(
|
||||
@@ -1539,7 +1570,7 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
VALUES ($1, $2, $3, $4::jsonb, $5::jsonb)
|
||||
`,
|
||||
matchId,
|
||||
String(payload.model_version || "unknown"),
|
||||
engineVersion,
|
||||
typeof payload.decision_trace_id === "string"
|
||||
? payload.decision_trace_id
|
||||
: null,
|
||||
|
||||
Reference in New Issue
Block a user