import { Controller, Get, Post, Patch, Body, Param, Query, UseGuards, } from '@nestjs/common'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { JwtAuthGuard, PlayerGuard } from '../../domains/identity/guards'; import { CurrentUser } from '../../shared/common/decorators'; import { jsonResponse } from '../../shared/common/filters'; import { UsersService } from '../../domains/identity/users.service'; import { WalletService } from '../../domains/ledger/wallet.service'; import { MatchesService } from '../../domains/catalog/matches.service'; import { BetsService } from '../../domains/betting/bets.service'; import { ContentService } from '../../domains/operations/content/content.service'; import { CashbackService } from '../../domains/operations/cashback/cashback.service'; import { IsString, IsNumber, IsArray, ValidateNested, Min, IsOptional } from 'class-validator'; import { Type } from 'class-transformer'; class SingleBetDto { @IsString() selectionId!: string; @IsString() oddsVersion!: string; @IsNumber() @Min(0.01) stake!: number; @IsString() requestId!: string; } class ParlayLegDto { @IsString() selectionId!: string; @IsString() oddsVersion!: string; } class ParlayBetDto { @IsArray() @ValidateNested({ each: true }) @Type(() => ParlayLegDto) legs!: ParlayLegDto[]; @IsNumber() @Min(0.01) stake!: number; @IsString() requestId!: string; } class LocaleDto { @IsString() locale!: string; } class UpdateProfileDto { @IsOptional() @IsString() phone?: string; @IsOptional() @IsString() email?: string; } @ApiTags('Player') @Controller('player') @UseGuards(JwtAuthGuard, PlayerGuard) @ApiBearerAuth() export class PlayerController { constructor( private users: UsersService, private wallet: WalletService, private matches: MatchesService, private bets: BetsService, private content: ContentService, private cashback: CashbackService, ) {} @Get('profile') async profile(@CurrentUser('id') userId: bigint) { const user = await this.users.findById(userId); return jsonResponse(user); } @Post('language') async setLanguage(@CurrentUser('id') userId: bigint, @Body() dto: LocaleDto) { const result = await this.users.updateLocale(userId, dto.locale); return jsonResponse(result); } @Patch('profile') async updateProfile(@CurrentUser('id') userId: bigint, @Body() dto: UpdateProfileDto) { const user = await this.users.updateProfile(userId, dto); return jsonResponse(user); } @Get('home') async home(@CurrentUser('locale') locale: string) { const [banners, notices, ticker, hotMatches, todayMatches] = await Promise.all([ this.content.listActive('BANNER', locale), this.content.listActive('NOTICE', locale), this.content.listActive('TICKER', locale), this.matches.listPublished(locale), this.matches.listPublished(locale), ]); return jsonResponse({ banners, notices, ticker, hotMatches: (hotMatches as Array<{ isHot?: boolean }>).filter((m) => m.isHot), todayMatches, }); } @Get('matches') async listMatches( @CurrentUser('locale') locale: string, @Query('leagueId') leagueId?: string, ) { const items = await this.matches.listPublished(locale, leagueId ? BigInt(leagueId) : undefined); return jsonResponse(items); } @Get('outrights') async listOutrights(@CurrentUser('locale') locale: string) { const items = await this.matches.listOutrights(locale); return jsonResponse(items); } @Get('matches/:id') async matchDetail(@Param('id') id: string, @CurrentUser('locale') locale: string) { const match = await this.matches.getMatchDetail(BigInt(id), locale); return jsonResponse(match); } @Post('bets/single') async singleBet(@CurrentUser('id') userId: bigint, @CurrentUser('parentId') parentId: bigint, @Body() dto: SingleBetDto) { const bet = await this.bets.placeSingleBet( userId, parentId, BigInt(dto.selectionId), BigInt(dto.oddsVersion), dto.stake, dto.requestId, ); return jsonResponse(bet); } @Post('bets/parlay') async parlayBet(@CurrentUser('id') userId: bigint, @CurrentUser('parentId') parentId: bigint, @Body() dto: ParlayBetDto) { const bet = await this.bets.placeParlayBet( userId, parentId, dto.legs.map((l) => ({ selectionId: BigInt(l.selectionId), oddsVersion: BigInt(l.oddsVersion) })), dto.stake, dto.requestId, ); return jsonResponse(bet); } @Get('bets') async myBets( @CurrentUser('id') userId: bigint, @CurrentUser('locale') locale: string, @Query('status') status?: string, @Query('page') page?: string, ) { const result = await this.bets.getUserBets(userId, status, page ? parseInt(page) : 1); const items = await this.matches.enrichBetsForHistory(result.items, locale); return jsonResponse({ ...result, items }); } @Get('bets/:betNo') async betDetail(@CurrentUser('id') userId: bigint, @Param('betNo') betNo: string) { const bet = await this.bets.getBetByNo(betNo, userId); return jsonResponse(bet); } @Get('wallet/transactions') async transactions( @CurrentUser('id') userId: bigint, @Query('page') page?: string, ) { const result = await this.wallet.getTransactions(userId, page ? parseInt(page) : 1); return jsonResponse(result); } @Get('cashbacks') async cashbacks(@CurrentUser('id') userId: bigint) { const items = await this.cashback.getUserCashbacks(userId); return jsonResponse(items); } }