重构
This commit is contained in:
@@ -81,6 +81,14 @@ type AdminUploadUser = {
|
||||
permissions?: string[];
|
||||
};
|
||||
|
||||
function parseMatchStartTime(value: string): Date {
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
throw appBadRequest('MATCH_START_TIME_INVALID');
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
function uploadCategory(value?: string): UploadCategory {
|
||||
const category = (value || 'contents').trim();
|
||||
if (UPLOAD_CATEGORIES.includes(category as UploadCategory)) {
|
||||
@@ -503,9 +511,6 @@ class CreatePlatformMatchDto {
|
||||
@IsString()
|
||||
awayTeamLogoUrl?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
correctScoreEnabled?: boolean;
|
||||
}
|
||||
|
||||
class UpdatePlatformMatchDto {
|
||||
@@ -560,9 +565,6 @@ class UpdatePlatformMatchDto {
|
||||
@IsString()
|
||||
awayTeamLogoUrl?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
correctScoreEnabled?: boolean;
|
||||
}
|
||||
|
||||
class ReopenMatchDto {
|
||||
@@ -591,6 +593,12 @@ class UpdateMarketDto {
|
||||
@IsString()
|
||||
promoLabel?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
promoLabelI18n?: Record<string, string> | null;
|
||||
|
||||
@IsOptional()
|
||||
nameI18n?: Record<string, string> | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
@@ -598,6 +606,10 @@ class UpdateMarketDto {
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
lineValue?: number | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
showOnPlayer?: boolean;
|
||||
}
|
||||
|
||||
class UpdateSelectionDto {
|
||||
@@ -605,6 +617,9 @@ class UpdateSelectionDto {
|
||||
@IsString()
|
||||
selectionName?: string;
|
||||
|
||||
@IsOptional()
|
||||
nameI18n?: Record<string, string> | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(1.01)
|
||||
@@ -637,6 +652,38 @@ class ScoreDto {
|
||||
@IsNumber()
|
||||
ftAway?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeCorners?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayCorners?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeYellowCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayYellowCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeRedCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayRedCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayCards?: number;
|
||||
|
||||
/** 冠军盘结算:获胜球队 ID */
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@@ -674,6 +721,136 @@ class MarketTemplatesDto {
|
||||
marketTypes!: string[];
|
||||
}
|
||||
|
||||
class MarketDraftSelectionDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
id?: string;
|
||||
|
||||
@IsString()
|
||||
selectionCode!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
selectionName?: string;
|
||||
|
||||
@IsOptional()
|
||||
nameI18n?: Record<string, string> | null;
|
||||
|
||||
@IsNumber()
|
||||
@Min(1.01)
|
||||
odds!: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
class MarketDraftDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
id?: string;
|
||||
|
||||
@IsString()
|
||||
marketType!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
marketKey?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
lineKey?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
period?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
lineValue?: number | null;
|
||||
|
||||
@IsOptional()
|
||||
paramsJson?: Record<string, unknown> | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
allowSingle?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
allowParlay?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
showOnPlayer?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
sortOrder?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
promoLabel?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
promoLabelI18n?: Record<string, string> | null;
|
||||
|
||||
@IsOptional()
|
||||
nameI18n?: Record<string, string> | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
selections?: MarketDraftSelectionDto[];
|
||||
}
|
||||
|
||||
class MarketTemplateSaveDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
nameI18n?: Record<string, string> | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
description?: string | null;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
isDefault?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
sortOrder?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
items?: MarketDraftDto[];
|
||||
}
|
||||
|
||||
class ApplyMarketTemplateDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
templateId?: string;
|
||||
}
|
||||
|
||||
class BulkMatchMarketsDto {
|
||||
@IsArray()
|
||||
markets!: MarketDraftDto[];
|
||||
}
|
||||
|
||||
class UpdateOddsDto {
|
||||
@IsNumber()
|
||||
odds!: number;
|
||||
@@ -892,6 +1069,38 @@ class ResettlePreviewDto {
|
||||
@IsNumber()
|
||||
ftAway?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeCorners?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayCorners?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeYellowCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayYellowCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeRedCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayRedCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
homeCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
awayCards?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
reason?: string;
|
||||
@@ -1665,7 +1874,7 @@ export class AdminController {
|
||||
awayTeamEn: dto.awayTeamEn,
|
||||
awayTeamZh: dto.awayTeamZh,
|
||||
awayTeamMs: dto.awayTeamMs,
|
||||
startTime: new Date(dto.startTime),
|
||||
startTime: parseMatchStartTime(dto.startTime),
|
||||
isHot: dto.isHot,
|
||||
displayOrder: dto.displayOrder,
|
||||
matchName: dto.matchName,
|
||||
@@ -1673,7 +1882,6 @@ export class AdminController {
|
||||
groupName: dto.groupName,
|
||||
homeTeamLogoUrl: dto.homeTeamLogoUrl,
|
||||
awayTeamLogoUrl: dto.awayTeamLogoUrl,
|
||||
correctScoreEnabled: dto.correctScoreEnabled,
|
||||
updatedBy: operatorId,
|
||||
});
|
||||
await this.outright.syncOutrightTeamsForLeagueIfExists(match.leagueId);
|
||||
@@ -1725,9 +1933,8 @@ export class AdminController {
|
||||
awayTeamEn: dto.awayTeamEn,
|
||||
awayTeamZh: dto.awayTeamZh,
|
||||
awayTeamMs: dto.awayTeamMs,
|
||||
startTime: new Date(dto.startTime),
|
||||
startTime: parseMatchStartTime(dto.startTime),
|
||||
isHot: dto.isHot,
|
||||
correctScoreEnabled: dto.correctScoreEnabled,
|
||||
displayOrder: dto.displayOrder,
|
||||
matchName: dto.matchName,
|
||||
stage: dto.stage,
|
||||
@@ -1775,7 +1982,7 @@ export class AdminController {
|
||||
@Post('matches/:id/reopen')
|
||||
@RequirePermissions(P.matches)
|
||||
async reopenMatch(@Param('id') id: string, @Body() dto: ReopenMatchDto) {
|
||||
const startTime = dto.startTime ? new Date(dto.startTime) : undefined;
|
||||
const startTime = dto.startTime ? parseMatchStartTime(dto.startTime) : undefined;
|
||||
const match = await this.matches.reopenMatch(BigInt(id), startTime);
|
||||
return jsonResponse(match);
|
||||
}
|
||||
@@ -1783,11 +1990,58 @@ export class AdminController {
|
||||
@Post('matches/:id/cancel')
|
||||
@RequirePermissions(P.matches)
|
||||
async cancelMatch(@Param('id') id: string) {
|
||||
await this.matches.cancelMatch(BigInt(id));
|
||||
const voided = await this.settlement.voidMatchBets(BigInt(id));
|
||||
const voided = await this.settlement.cancelMatchAndVoidBets(BigInt(id));
|
||||
return jsonResponse(voided);
|
||||
}
|
||||
|
||||
@Get('market-definitions')
|
||||
@RequirePermissions(P.matches)
|
||||
async listMarketDefinitions() {
|
||||
return jsonResponse(this.markets.listMarketDefinitions());
|
||||
}
|
||||
|
||||
@Get('market-templates')
|
||||
@RequirePermissions(P.matches)
|
||||
async listMarketTemplates() {
|
||||
const templates = await this.markets.listTemplates();
|
||||
return jsonResponse(templates);
|
||||
}
|
||||
|
||||
@Post('market-templates')
|
||||
@RequirePermissions(P.matches)
|
||||
async createMarketTemplate(@Body() dto: MarketTemplateSaveDto) {
|
||||
const template = await this.markets.createTemplate(dto);
|
||||
return jsonResponse(template);
|
||||
}
|
||||
|
||||
@Get('market-templates/:id')
|
||||
@RequirePermissions(P.matches)
|
||||
async getMarketTemplate(@Param('id') id: string) {
|
||||
const template = await this.markets.getTemplate(BigInt(id));
|
||||
return jsonResponse(template);
|
||||
}
|
||||
|
||||
@Put('market-templates/:id')
|
||||
@RequirePermissions(P.matches)
|
||||
async updateMarketTemplate(@Param('id') id: string, @Body() dto: MarketTemplateSaveDto) {
|
||||
const template = await this.markets.updateTemplate(BigInt(id), dto);
|
||||
return jsonResponse(template);
|
||||
}
|
||||
|
||||
@Post('market-templates/:id/duplicate')
|
||||
@RequirePermissions(P.matches)
|
||||
async duplicateMarketTemplate(@Param('id') id: string) {
|
||||
const template = await this.markets.duplicateTemplate(BigInt(id));
|
||||
return jsonResponse(template);
|
||||
}
|
||||
|
||||
@Post('market-templates/:id/set-default')
|
||||
@RequirePermissions(P.matches)
|
||||
async setDefaultMarketTemplate(@Param('id') id: string) {
|
||||
const template = await this.markets.setDefaultTemplate(BigInt(id));
|
||||
return jsonResponse(template);
|
||||
}
|
||||
|
||||
@Post('matches/:id/markets/templates')
|
||||
@RequirePermissions(P.matches)
|
||||
async generateTemplates(@Param('id') id: string, @Body() dto: MarketTemplatesDto) {
|
||||
@@ -1795,6 +2049,27 @@ export class AdminController {
|
||||
return jsonResponse(markets);
|
||||
}
|
||||
|
||||
@Post('matches/:id/markets/apply-template')
|
||||
@RequirePermissions(P.matches)
|
||||
async applyMarketTemplate(@Param('id') id: string, @Body() dto: ApplyMarketTemplateDto) {
|
||||
const result = await this.markets.applyTemplateToMatch(
|
||||
BigInt(id),
|
||||
dto.templateId ? BigInt(dto.templateId) : null,
|
||||
);
|
||||
return jsonResponse(result);
|
||||
}
|
||||
|
||||
@Put('matches/:id/markets/bulk')
|
||||
@RequirePermissions(P.matches)
|
||||
async bulkSaveMatchMarkets(
|
||||
@CurrentUser('id') operatorId: bigint,
|
||||
@Param('id') id: string,
|
||||
@Body() dto: BulkMatchMarketsDto,
|
||||
) {
|
||||
const result = await this.markets.saveMatchMarkets(BigInt(id), dto.markets, operatorId);
|
||||
return jsonResponse(result);
|
||||
}
|
||||
|
||||
@Put('matches/:id/odds')
|
||||
@RequirePermissions(P.matches)
|
||||
async batchUpdateMatchOdds(
|
||||
@@ -1815,8 +2090,11 @@ export class AdminController {
|
||||
async updateMarket(@Param('id') id: string, @Body() dto: UpdateMarketDto) {
|
||||
const market = await this.markets.updateMarket(BigInt(id), {
|
||||
promoLabel: dto.promoLabel,
|
||||
promoLabelI18n: dto.promoLabelI18n,
|
||||
nameI18n: dto.nameI18n,
|
||||
status: dto.status,
|
||||
lineValue: dto.lineValue,
|
||||
showOnPlayer: dto.showOnPlayer,
|
||||
});
|
||||
return jsonResponse(market);
|
||||
}
|
||||
@@ -1832,6 +2110,7 @@ export class AdminController {
|
||||
BigInt(id),
|
||||
{
|
||||
selectionName: dto.selectionName,
|
||||
nameI18n: dto.nameI18n,
|
||||
odds: dto.odds,
|
||||
status: dto.status,
|
||||
},
|
||||
@@ -2057,6 +2336,16 @@ export class AdminController {
|
||||
dto.ftAway ?? 0,
|
||||
operatorId,
|
||||
dto.winnerTeamId != null ? BigInt(dto.winnerTeamId) : undefined,
|
||||
{
|
||||
homeCorners: dto.homeCorners ?? null,
|
||||
awayCorners: dto.awayCorners ?? null,
|
||||
homeYellowCards: dto.homeYellowCards ?? null,
|
||||
awayYellowCards: dto.awayYellowCards ?? null,
|
||||
homeRedCards: dto.homeRedCards ?? null,
|
||||
awayRedCards: dto.awayRedCards ?? null,
|
||||
homeCards: dto.homeCards ?? null,
|
||||
awayCards: dto.awayCards ?? null,
|
||||
},
|
||||
);
|
||||
return jsonResponse(result);
|
||||
}
|
||||
@@ -2074,6 +2363,14 @@ export class AdminController {
|
||||
htAway: dto?.htAway,
|
||||
ftHome: dto?.ftHome,
|
||||
ftAway: dto?.ftAway,
|
||||
homeCorners: dto?.homeCorners,
|
||||
awayCorners: dto?.awayCorners,
|
||||
homeYellowCards: dto?.homeYellowCards,
|
||||
awayYellowCards: dto?.awayYellowCards,
|
||||
homeRedCards: dto?.homeRedCards,
|
||||
awayRedCards: dto?.awayRedCards,
|
||||
homeCards: dto?.homeCards,
|
||||
awayCards: dto?.awayCards,
|
||||
winnerTeamId: dto?.winnerTeamId != null ? BigInt(dto.winnerTeamId) : undefined,
|
||||
page: dto?.page ? Math.max(1, dto.page) : 1,
|
||||
pageSize: dto?.pageSize ? Math.min(100, Math.max(1, dto.pageSize)) : 10,
|
||||
@@ -2123,6 +2420,14 @@ export class AdminController {
|
||||
htAway: dto.htAway ?? 0,
|
||||
ftHome: dto.ftHome ?? 0,
|
||||
ftAway: dto.ftAway ?? 0,
|
||||
homeCorners: dto.homeCorners ?? null,
|
||||
awayCorners: dto.awayCorners ?? null,
|
||||
homeYellowCards: dto.homeYellowCards ?? null,
|
||||
awayYellowCards: dto.awayYellowCards ?? null,
|
||||
homeRedCards: dto.homeRedCards ?? null,
|
||||
awayRedCards: dto.awayRedCards ?? null,
|
||||
homeCards: dto.homeCards ?? null,
|
||||
awayCards: dto.awayCards ?? null,
|
||||
},
|
||||
operatorId,
|
||||
dto.reason,
|
||||
|
||||
Reference in New Issue
Block a user