feat(admin,api,player): 赛事分组管理、盘口独立页与多语言展示优化
- 管理端按联赛展示单场,新增赛事/单场流程与列表展开状态保持 - 盘口赔率迁至独立页面,保存按钮仅在有修改时高亮 - API 新增联赛列表与子场查询,按 locale 返回队名并修复编译 - 波胆其它选项与促销标签等 i18n 补齐,文案更易懂
This commit is contained in:
@@ -49,6 +49,22 @@ export class MarketsService {
|
||||
return created;
|
||||
}
|
||||
|
||||
private formatHandicapName(side: 'home' | 'away', line: number, half = false) {
|
||||
const sideLabel = side === 'home' ? '主队' : '客队';
|
||||
const value = side === 'home' ? line : -line;
|
||||
const lineText = value > 0 ? `+${value}` : `${value}`;
|
||||
return half ? `半场${sideLabel} ${lineText}` : `${sideLabel} ${lineText}`;
|
||||
}
|
||||
|
||||
private formatOuName(side: 'over' | 'under', line: number, half = false) {
|
||||
const sideLabel = side === 'over' ? '大' : '小';
|
||||
return half ? `半场${sideLabel} ${line}` : `${sideLabel} ${line}`;
|
||||
}
|
||||
|
||||
private formatScoreName(code: string) {
|
||||
return code.replace('SCORE_', '').replace('_', '-');
|
||||
}
|
||||
|
||||
private getMarketConfig(marketType: string) {
|
||||
const configs: Record<string, {
|
||||
period: string;
|
||||
@@ -62,9 +78,9 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 1,
|
||||
selections: [
|
||||
{ code: 'HOME', name: 'Home', odds: 2.5 },
|
||||
{ code: 'DRAW', name: 'Draw', odds: 3.2 },
|
||||
{ code: 'AWAY', name: 'Away', odds: 2.8 },
|
||||
{ code: 'HOME', name: '主胜', odds: 2.5 },
|
||||
{ code: 'DRAW', name: '和', odds: 3.2 },
|
||||
{ code: 'AWAY', name: '客胜', odds: 2.8 },
|
||||
],
|
||||
},
|
||||
HT_1X2: {
|
||||
@@ -72,9 +88,9 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 5,
|
||||
selections: [
|
||||
{ code: 'HOME', name: 'HT Home', odds: 3.0 },
|
||||
{ code: 'DRAW', name: 'HT Draw', odds: 2.0 },
|
||||
{ code: 'AWAY', name: 'HT Away', odds: 3.5 },
|
||||
{ code: 'HOME', name: '半场主胜', odds: 3.0 },
|
||||
{ code: 'DRAW', name: '半场和', odds: 2.0 },
|
||||
{ code: 'AWAY', name: '半场客胜', odds: 3.5 },
|
||||
],
|
||||
},
|
||||
FT_HANDICAP: {
|
||||
@@ -83,8 +99,8 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 2,
|
||||
selections: [
|
||||
{ code: 'HOME', name: 'Home -0.5', odds: 1.9 },
|
||||
{ code: 'AWAY', name: 'Away +0.5', odds: 1.9 },
|
||||
{ code: 'HOME', name: this.formatHandicapName('home', -0.5), odds: 1.9 },
|
||||
{ code: 'AWAY', name: this.formatHandicapName('away', -0.5), odds: 1.9 },
|
||||
],
|
||||
},
|
||||
HT_HANDICAP: {
|
||||
@@ -93,8 +109,8 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 6,
|
||||
selections: [
|
||||
{ code: 'HOME', name: 'HT Home -0.5', odds: 1.9 },
|
||||
{ code: 'AWAY', name: 'HT Away +0.5', odds: 1.9 },
|
||||
{ code: 'HOME', name: this.formatHandicapName('home', -0.5, true), odds: 1.9 },
|
||||
{ code: 'AWAY', name: this.formatHandicapName('away', -0.5, true), odds: 1.9 },
|
||||
],
|
||||
},
|
||||
FT_OVER_UNDER: {
|
||||
@@ -103,8 +119,8 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 3,
|
||||
selections: [
|
||||
{ code: 'OVER', name: 'Over 2.5', odds: 1.85 },
|
||||
{ code: 'UNDER', name: 'Under 2.5', odds: 1.95 },
|
||||
{ code: 'OVER', name: this.formatOuName('over', 2.5), odds: 1.85 },
|
||||
{ code: 'UNDER', name: this.formatOuName('under', 2.5), odds: 1.95 },
|
||||
],
|
||||
},
|
||||
HT_OVER_UNDER: {
|
||||
@@ -113,8 +129,8 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 7,
|
||||
selections: [
|
||||
{ code: 'OVER', name: 'HT Over 1.5', odds: 2.0 },
|
||||
{ code: 'UNDER', name: 'HT Under 1.5', odds: 1.75 },
|
||||
{ code: 'OVER', name: this.formatOuName('over', 1.5, true), odds: 2.0 },
|
||||
{ code: 'UNDER', name: this.formatOuName('under', 1.5, true), odds: 1.75 },
|
||||
],
|
||||
},
|
||||
FT_ODD_EVEN: {
|
||||
@@ -122,8 +138,8 @@ export class MarketsService {
|
||||
allowParlay: true,
|
||||
sortOrder: 4,
|
||||
selections: [
|
||||
{ code: 'ODD', name: 'Odd', odds: 1.9 },
|
||||
{ code: 'EVEN', name: 'Even', odds: 1.9 },
|
||||
{ code: 'ODD', name: '单', odds: 1.9 },
|
||||
{ code: 'EVEN', name: '双', odds: 1.9 },
|
||||
],
|
||||
},
|
||||
FT_CORRECT_SCORE: {
|
||||
@@ -132,7 +148,7 @@ export class MarketsService {
|
||||
sortOrder: 8,
|
||||
selections: FT_CORRECT_SCORE_TEMPLATE.map((code) => ({
|
||||
code,
|
||||
name: code.replace('SCORE_', '').replace('_', '-') || code,
|
||||
name: this.formatScoreName(code),
|
||||
odds: 8.0,
|
||||
})),
|
||||
},
|
||||
@@ -142,7 +158,7 @@ export class MarketsService {
|
||||
sortOrder: 9,
|
||||
selections: HT_CORRECT_SCORE_TEMPLATE.map((code) => ({
|
||||
code,
|
||||
name: code.replace('SCORE_', '').replace('_', '-') || code,
|
||||
name: this.formatScoreName(code),
|
||||
odds: 6.0,
|
||||
})),
|
||||
},
|
||||
@@ -152,7 +168,7 @@ export class MarketsService {
|
||||
sortOrder: 10,
|
||||
selections: HT_CORRECT_SCORE_TEMPLATE.map((code) => ({
|
||||
code,
|
||||
name: code.replace('SCORE_', '').replace('_', '-') || code,
|
||||
name: this.formatScoreName(code),
|
||||
odds: 6.0,
|
||||
})),
|
||||
},
|
||||
@@ -206,4 +222,45 @@ export class MarketsService {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async updateMarket(
|
||||
marketId: bigint,
|
||||
data: { promoLabel?: string | null; status?: string; lineValue?: number | null },
|
||||
) {
|
||||
const market = await this.prisma.market.findUnique({ where: { id: marketId } });
|
||||
if (!market) throw new NotFoundException('Market not found');
|
||||
|
||||
return this.prisma.market.update({
|
||||
where: { id: marketId },
|
||||
data: {
|
||||
...(data.promoLabel !== undefined ? { promoLabel: data.promoLabel?.trim() || null } : {}),
|
||||
...(data.status !== undefined ? { status: data.status } : {}),
|
||||
...(data.lineValue !== undefined ? { lineValue: data.lineValue } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async updateSelection(
|
||||
selectionId: bigint,
|
||||
data: { selectionName?: string; odds?: number; status?: string },
|
||||
operatorId?: bigint,
|
||||
) {
|
||||
const selection = await this.prisma.marketSelection.findUnique({
|
||||
where: { id: selectionId },
|
||||
});
|
||||
if (!selection) throw new NotFoundException('Selection not found');
|
||||
|
||||
if (data.odds != null) {
|
||||
if (!operatorId) throw new BadRequestException('Operator required for odds update');
|
||||
return this.updateOdds(selectionId, data.odds, operatorId);
|
||||
}
|
||||
|
||||
return this.prisma.marketSelection.update({
|
||||
where: { id: selectionId },
|
||||
data: {
|
||||
...(data.selectionName !== undefined ? { selectionName: data.selectionName.trim() } : {}),
|
||||
...(data.status !== undefined ? { status: data.status } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user