feat(admin,player,api): 优胜冠军通用管理与界面精简
管理端新增冠军盘列表/编辑、展开懒加载与 ECharts 修复;各列表页去掉重复标题。玩家端支持多赛事冠军盘、分批加载与语言切换刷新。API 扩展 outright CRUD 与列表性能优化。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,12 +14,9 @@ import {
|
||||
toVenueJson,
|
||||
translationsFromZhiboNames,
|
||||
} from './zhibo-match.mapper';
|
||||
import { WC2026_OUTRIGHT_TEAMS } from './wc2026-outright-teams';
|
||||
import { syncWc2026OutrightMarket } from './wc2026-outright.sync';
|
||||
|
||||
const WC2026_OUTRIGHT_RANK = new Map(
|
||||
WC2026_OUTRIGHT_TEAMS.map((t) => [t.code, t.rank]),
|
||||
);
|
||||
const WC2026_OUTRIGHT_CODES = new Set(WC2026_OUTRIGHT_TEAMS.map((t) => t.code));
|
||||
const OUTRIGHT_PLACEHOLDER_CODE = 'OUT';
|
||||
|
||||
@Injectable()
|
||||
export class MatchesService {
|
||||
@@ -570,8 +567,19 @@ export class MatchesService {
|
||||
}
|
||||
|
||||
async listOutrights(locale = 'en-US') {
|
||||
try {
|
||||
await syncWc2026OutrightMarket(this.prisma, { forceCanonical: false });
|
||||
} catch {
|
||||
/* 联赛未 seed 时忽略,仍返回已有数据 */
|
||||
}
|
||||
|
||||
const matches = await this.prisma.match.findMany({
|
||||
where: { status: 'PUBLISHED', isOutright: true, sportType: 'FOOTBALL' },
|
||||
where: {
|
||||
status: 'PUBLISHED',
|
||||
isOutright: true,
|
||||
sportType: 'FOOTBALL',
|
||||
deletedAt: null,
|
||||
},
|
||||
include: {
|
||||
markets: {
|
||||
where: { marketType: 'OUTRIGHT_WINNER', status: 'OPEN' },
|
||||
@@ -592,27 +600,28 @@ export class MatchesService {
|
||||
const market = match.markets[0];
|
||||
if (!market) continue;
|
||||
|
||||
const selections = (
|
||||
await Promise.all(
|
||||
market.selections
|
||||
.filter((sel) => WC2026_OUTRIGHT_CODES.has(sel.selectionCode))
|
||||
.map(async (sel) => {
|
||||
const teamCode = sel.selectionCode.replace(/^TEAM_/, '');
|
||||
const team = await this.prisma.team.findUnique({ where: { code: teamCode } });
|
||||
const teamName = team
|
||||
? await this.getTranslation('TEAM', team.id, locale)
|
||||
: sel.selectionName;
|
||||
return {
|
||||
id: sel.id.toString(),
|
||||
teamCode,
|
||||
teamName,
|
||||
rank: WC2026_OUTRIGHT_RANK.get(teamCode) ?? 999,
|
||||
odds: sel.odds.toString(),
|
||||
oddsVersion: sel.oddsVersion.toString(),
|
||||
};
|
||||
}),
|
||||
)
|
||||
).sort((a, b) => a.rank - b.rank);
|
||||
const selections = await Promise.all(
|
||||
market.selections
|
||||
.filter((sel) => sel.selectionCode !== OUTRIGHT_PLACEHOLDER_CODE)
|
||||
.map(async (sel) => {
|
||||
const team = await this.prisma.team.findUnique({
|
||||
where: { code: sel.selectionCode },
|
||||
});
|
||||
const teamName = team
|
||||
? await this.getTranslation('TEAM', team.id, locale)
|
||||
: sel.selectionName;
|
||||
return {
|
||||
id: sel.id.toString(),
|
||||
teamCode: sel.selectionCode,
|
||||
teamName,
|
||||
rank: sel.sortOrder + 1,
|
||||
odds: sel.odds.toString(),
|
||||
oddsVersion: sel.oddsVersion.toString(),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
if (selections.length === 0) continue;
|
||||
|
||||
results.push({
|
||||
id: match.id.toString(),
|
||||
|
||||
Reference in New Issue
Block a user