gg
This commit is contained in:
@@ -21,12 +21,17 @@ import {
|
||||
GeneratePredictionDto,
|
||||
SmartCouponRequestDto,
|
||||
} from "./dto/predictions-request.dto";
|
||||
import { Public } from "src/common/decorators";
|
||||
import { CurrentUser } from "src/common/decorators";
|
||||
import { AnalysisService } from "../analysis/analysis.service";
|
||||
import { ForbiddenException } from "@nestjs/common";
|
||||
|
||||
@ApiTags("Predictions")
|
||||
@Controller("predictions")
|
||||
export class PredictionsController {
|
||||
constructor(private readonly predictionsService: PredictionsService) {}
|
||||
constructor(
|
||||
private readonly predictionsService: PredictionsService,
|
||||
private readonly analysisService: AnalysisService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* GET /predictions/health
|
||||
@@ -93,7 +98,6 @@ export class PredictionsController {
|
||||
* Get prediction for a specific match
|
||||
*/
|
||||
@Get(":matchId")
|
||||
@Public()
|
||||
@ApiOperation({ summary: "Get prediction for a specific match" })
|
||||
@ApiParam({ name: "matchId", description: "Match ID" })
|
||||
@ApiResponse({
|
||||
@@ -103,11 +107,23 @@ export class PredictionsController {
|
||||
type: MatchPredictionDto,
|
||||
})
|
||||
@ApiResponse({ status: 404, description: "Match not found" })
|
||||
@ApiResponse({ status: 403, description: "Daily limit exceeded" })
|
||||
async getPrediction(
|
||||
@Param("matchId") matchId: string,
|
||||
@CurrentUser() user: any,
|
||||
): Promise<MatchPredictionDto> {
|
||||
const canProceed = await this.analysisService.checkUsageLimit(
|
||||
user.id,
|
||||
false,
|
||||
1,
|
||||
);
|
||||
if (!canProceed) {
|
||||
throw new ForbiddenException("ANALYSIS_LIMIT_EXCEEDED");
|
||||
}
|
||||
|
||||
const cached = await this.predictionsService.getCachedPrediction(matchId);
|
||||
if (cached) {
|
||||
await this.analysisService.recordUsage(user.id, false);
|
||||
return cached;
|
||||
}
|
||||
|
||||
@@ -115,9 +131,10 @@ export class PredictionsController {
|
||||
const prediction = await this.predictionsService.getPredictionById(matchId);
|
||||
|
||||
if (!prediction) {
|
||||
throw new NotFoundException(`Match not found: ${matchId}`);
|
||||
throw new NotFoundException("MATCH_NOT_FOUND");
|
||||
}
|
||||
|
||||
await this.analysisService.recordUsage(user.id, false);
|
||||
return prediction;
|
||||
}
|
||||
|
||||
@@ -129,17 +146,29 @@ export class PredictionsController {
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: "Generate prediction with provided match data" })
|
||||
@ApiResponse({ status: 200, type: MatchPredictionDto })
|
||||
@ApiResponse({ status: 403, description: "Daily limit exceeded" })
|
||||
async generatePrediction(
|
||||
@CurrentUser() user: any,
|
||||
@Body() dto: GeneratePredictionDto,
|
||||
): Promise<MatchPredictionDto> {
|
||||
const canProceed = await this.analysisService.checkUsageLimit(
|
||||
user.id,
|
||||
false,
|
||||
1,
|
||||
);
|
||||
if (!canProceed) {
|
||||
throw new ForbiddenException("ANALYSIS_LIMIT_EXCEEDED");
|
||||
}
|
||||
|
||||
const prediction = await this.predictionsService.getPredictionWithData({
|
||||
matchId: dto.matchId,
|
||||
});
|
||||
|
||||
if (!prediction) {
|
||||
throw new NotFoundException("Failed to generate prediction");
|
||||
throw new NotFoundException("PREDICTION_GENERATION_FAILED");
|
||||
}
|
||||
|
||||
await this.analysisService.recordUsage(user.id, false);
|
||||
return prediction;
|
||||
}
|
||||
|
||||
@@ -157,7 +186,20 @@ export class PredictionsController {
|
||||
description: "Smart coupon generated successfully",
|
||||
schema: { type: "object" },
|
||||
})
|
||||
async generateSmartCoupon(@Body() dto: SmartCouponRequestDto): Promise<any> {
|
||||
@ApiResponse({ status: 403, description: "Daily limit exceeded" })
|
||||
async generateSmartCoupon(
|
||||
@CurrentUser() user: any,
|
||||
@Body() dto: SmartCouponRequestDto,
|
||||
): Promise<any> {
|
||||
const canProceed = await this.analysisService.checkUsageLimit(
|
||||
user.id,
|
||||
true,
|
||||
dto.matchIds?.length || 1,
|
||||
);
|
||||
if (!canProceed) {
|
||||
throw new ForbiddenException("COUPON_LIMIT_EXCEEDED");
|
||||
}
|
||||
|
||||
const coupon = await this.predictionsService.getSmartCoupon(
|
||||
dto.matchIds,
|
||||
dto.strategy || "BALANCED",
|
||||
@@ -168,9 +210,10 @@ export class PredictionsController {
|
||||
);
|
||||
|
||||
if (!coupon) {
|
||||
throw new NotFoundException("Failed to generate Smart Coupon");
|
||||
throw new NotFoundException("SMART_COUPON_GENERATION_FAILED");
|
||||
}
|
||||
|
||||
await this.analysisService.recordUsage(user.id, true);
|
||||
return coupon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { PredictionsQueue } from "./queues/predictions.queue";
|
||||
import { PredictionsProcessor } from "./queues/predictions.processor";
|
||||
import { PREDICTIONS_QUEUE } from "./queues/predictions.types";
|
||||
import { FeederModule } from "../feeder/feeder.module";
|
||||
import { AnalysisModule } from "../analysis/analysis.module";
|
||||
|
||||
const redisEnabled = process.env.REDIS_ENABLED === "true";
|
||||
|
||||
@@ -25,6 +26,7 @@ const redisEnabled = process.env.REDIS_ENABLED === "true";
|
||||
: []),
|
||||
MatchesModule,
|
||||
FeederModule,
|
||||
AnalysisModule,
|
||||
],
|
||||
controllers: [PredictionsController],
|
||||
providers: [
|
||||
|
||||
@@ -1354,8 +1354,14 @@ export class PredictionsService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
|
||||
private extractCooldownMs(detail: unknown): number {
|
||||
if (detail && typeof detail === "object" && "cooldownRemainingMs" in detail) {
|
||||
return Number((detail as Record<string, unknown>).cooldownRemainingMs) || 0;
|
||||
if (
|
||||
detail &&
|
||||
typeof detail === "object" &&
|
||||
"cooldownRemainingMs" in detail
|
||||
) {
|
||||
return (
|
||||
Number((detail as Record<string, unknown>).cooldownRemainingMs) || 0
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof detail === "string") {
|
||||
|
||||
Reference in New Issue
Block a user