feat(admin,player,api): 公共管理与优胜冠军国旗、玩家端内容对接

新增公共内容 CRUD 与批量操作;公告滚动合并管理;优胜冠军内置国家选择与单行保存;玩家端统一 usePlayerHome 对接轮播与跑马灯。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-04 10:25:42 +08:00
parent 27580b2479
commit f76728dc3e
21 changed files with 1966 additions and 136 deletions

View File

@@ -5,6 +5,7 @@ import {
Get,
Post,
Put,
Patch,
Body,
Param,
Query,
@@ -304,6 +305,113 @@ class AddOutrightSelectionDto {
@IsNumber()
@Min(1.01)
odds!: number;
@IsOptional()
@IsString()
logoUrl?: string;
}
class UpdateOutrightSelectionTeamDto {
@IsOptional()
@IsString()
teamCode?: string;
@IsOptional()
@IsString()
teamZh?: string;
@IsOptional()
@IsString()
teamEn?: string;
@IsOptional()
@IsString()
logoUrl?: string | null;
}
class ContentTranslationDto {
@IsString()
locale!: string;
@IsOptional()
@IsString()
title?: string;
@IsOptional()
@IsString()
body?: string;
@IsOptional()
@IsString()
imageUrl?: string;
}
class CreateContentDto {
@IsString()
@IsIn(['BANNER', 'NOTICE', 'TICKER'])
contentType!: string;
@IsOptional()
@IsNumber()
sortOrder?: number;
@IsOptional()
@IsIn(['DRAFT', 'ACTIVE', 'INACTIVE'])
status?: string;
@IsOptional()
@IsString()
linkType?: string | null;
@IsOptional()
@IsString()
linkTarget?: string | null;
@IsOptional()
@IsString()
startTime?: string | null;
@IsOptional()
@IsString()
endTime?: string | null;
@IsArray()
translations!: ContentTranslationDto[];
}
class UpdateContentDto {
@IsOptional()
@IsNumber()
sortOrder?: number;
@IsOptional()
@IsIn(['DRAFT', 'ACTIVE', 'INACTIVE'])
status?: string;
@IsOptional()
@IsString()
linkType?: string | null;
@IsOptional()
@IsString()
linkTarget?: string | null;
@IsOptional()
@IsString()
startTime?: string | null;
@IsOptional()
@IsString()
endTime?: string | null;
@IsOptional()
@IsArray()
translations?: ContentTranslationDto[];
}
class ContentStatusDto {
@IsIn(['DRAFT', 'ACTIVE', 'INACTIVE'])
status!: string;
}
class CashbackPreviewDto {
@@ -790,6 +898,20 @@ export class AdminController {
return jsonResponse(data);
}
@Patch('outrights/:matchId/selections/:selectionId')
async updateOutrightSelectionTeam(
@Param('matchId') matchId: string,
@Param('selectionId') selectionId: string,
@Body() dto: UpdateOutrightSelectionTeamDto,
) {
const data = await this.outright.updateSelectionTeam(
BigInt(matchId),
BigInt(selectionId),
dto,
);
return jsonResponse(data);
}
@Delete('outrights/:matchId/selections/:selectionId')
async removeOutrightSelection(
@Param('matchId') matchId: string,
@@ -875,17 +997,47 @@ export class AdminController {
}
@Get('contents')
async listContents(@Query('type') type?: string) {
const items = await this.content.listAll(type);
async listContents(
@Query('type') type?: string,
@Query('status') status?: string,
) {
const items = await this.content.listForAdmin(type, status);
return jsonResponse(items);
}
@Get('contents/:id')
async getContent(@Param('id') id: string) {
const item = await this.content.getForAdmin(BigInt(id));
return jsonResponse(item);
}
@Post('contents')
async createContent(@Body() dto: Parameters<ContentService['create']>[0]) {
async createContent(@Body() dto: CreateContentDto) {
const item = await this.content.create(dto);
return jsonResponse(item);
}
@Put('contents/:id')
async updateContent(@Param('id') id: string, @Body() dto: UpdateContentDto) {
const item = await this.content.update(BigInt(id), dto);
return jsonResponse(item);
}
@Patch('contents/:id/status')
async updateContentStatus(
@Param('id') id: string,
@Body() dto: ContentStatusDto,
) {
const item = await this.content.updateStatus(BigInt(id), dto.status);
return jsonResponse(item);
}
@Delete('contents/:id')
async deleteContent(@Param('id') id: string) {
const result = await this.content.remove(BigInt(id));
return jsonResponse(result);
}
@Get('i18n/messages')
async getMessages(@Query('locale') locale = 'en-US') {
const messages = await this.i18n.getMessages(locale);