This commit is contained in:
@@ -223,11 +223,13 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
`/v20plus/analyze/${matchId}`,
|
||||
{ simulate: true, is_simulation: true, pre_match_only: true },
|
||||
);
|
||||
await this.recordPredictionRun(matchId, response.data);
|
||||
return this.enrichPredictionResponse(
|
||||
response.data as MatchPredictionDto,
|
||||
const prediction = this.enrichPredictionResponse(
|
||||
response.data,
|
||||
matchContext,
|
||||
);
|
||||
await this.recordPredictionRun(matchId, response.data);
|
||||
await this.cachePrediction(matchId, prediction);
|
||||
return prediction;
|
||||
} catch (e: unknown) {
|
||||
const requestError =
|
||||
e instanceof AiEngineRequestError
|
||||
@@ -235,6 +237,20 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
: new AiEngineRequestError("AI Engine request failed");
|
||||
const status = requestError.status;
|
||||
const detail = requestError.detail || requestError.message;
|
||||
|
||||
if (
|
||||
status === HttpStatus.SERVICE_UNAVAILABLE &&
|
||||
this.hasCooldown(detail)
|
||||
) {
|
||||
const storedPrediction = await this.getStoredPrediction(matchId);
|
||||
if (storedPrediction) {
|
||||
this.logger.warn(
|
||||
`AI Engine cooldown for ${matchId}; returning stored prediction`,
|
||||
);
|
||||
return this.enrichPredictionResponse(storedPrediction, matchContext);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.error(
|
||||
`Direct AI Engine call failed for ${matchId}: status=${status}, detail=${JSON.stringify(detail)}`,
|
||||
);
|
||||
@@ -674,6 +690,11 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
odds: this.normalizeDisplayOdds(odds, impliedProb),
|
||||
implied_prob: impliedProb,
|
||||
ev_edge: evEdge,
|
||||
playable: Boolean(record.playable) && interval.threshold_met,
|
||||
stake_units:
|
||||
Boolean(record.playable) && interval.threshold_met
|
||||
? this.asNumber(record.stake_units)
|
||||
: 0,
|
||||
reasons: Array.isArray(record.reasons)
|
||||
? record.reasons.map((reason) => this.translateReason(String(reason)))
|
||||
: [],
|
||||
@@ -919,15 +940,39 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const normalizedPick = pickName.toUpperCase();
|
||||
const normalizedPick = this.normalizePickKey(pickName);
|
||||
for (const [key, value] of Object.entries(probabilities)) {
|
||||
if (key.toUpperCase() === normalizedPick) {
|
||||
if (this.normalizePickKey(key) === normalizedPick) {
|
||||
return this.asNumber(value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private normalizePickKey(value: string): string {
|
||||
const normalized = value.trim().toUpperCase();
|
||||
const aliases: Record<string, string> = {
|
||||
ÜST: "OVER",
|
||||
UST: "OVER",
|
||||
OVER: "OVER",
|
||||
ALT: "UNDER",
|
||||
UNDER: "UNDER",
|
||||
"KG VAR": "YES",
|
||||
VAR: "YES",
|
||||
YES: "YES",
|
||||
"KG YOK": "NO",
|
||||
YOK: "NO",
|
||||
NO: "NO",
|
||||
TEK: "ODD",
|
||||
ODD: "ODD",
|
||||
ÇİFT: "EVEN",
|
||||
CIFT: "EVEN",
|
||||
EVEN: "EVEN",
|
||||
};
|
||||
|
||||
return aliases[normalized] ?? normalized;
|
||||
}
|
||||
|
||||
private impliedProbabilityFromOdds(odds: number): number {
|
||||
if (odds <= 1) {
|
||||
return 0;
|
||||
@@ -1132,6 +1177,30 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
return prediction.predictionJson as unknown as MatchPredictionDto;
|
||||
}
|
||||
|
||||
private async getStoredPrediction(
|
||||
matchId: string,
|
||||
): Promise<MatchPredictionDto | null> {
|
||||
const prediction = await this.prisma.prediction.findUnique({
|
||||
where: { matchId },
|
||||
});
|
||||
|
||||
return prediction
|
||||
? (prediction.predictionJson as unknown as MatchPredictionDto)
|
||||
: null;
|
||||
}
|
||||
|
||||
private hasCooldown(detail: unknown): boolean {
|
||||
if (typeof detail === "string") {
|
||||
return detail.includes("cooldownRemainingMs");
|
||||
}
|
||||
|
||||
if (detail && typeof detail === "object") {
|
||||
return "cooldownRemainingMs" in detail;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async ensureSmartCouponDataReady(matchIds: string[]): Promise<void> {
|
||||
const uniqueMatchIds = [...new Set(matchIds.filter((id) => !!id))];
|
||||
if (uniqueMatchIds.length === 0) {
|
||||
|
||||
Reference in New Issue
Block a user