重构
This commit is contained in:
@@ -9,12 +9,24 @@ export interface ScoreInput {
|
||||
ftAway: number;
|
||||
}
|
||||
|
||||
export interface MatchStatsInput {
|
||||
homeCorners?: number | null;
|
||||
awayCorners?: number | null;
|
||||
homeYellowCards?: number | null;
|
||||
awayYellowCards?: number | null;
|
||||
homeRedCards?: number | null;
|
||||
awayRedCards?: number | null;
|
||||
homeCards?: number | null;
|
||||
awayCards?: number | null;
|
||||
}
|
||||
|
||||
export interface SettlementInput {
|
||||
marketType: string;
|
||||
selectionCode: string;
|
||||
handicapLine?: number | null;
|
||||
totalLine?: number | null;
|
||||
score: ScoreInput;
|
||||
stats?: MatchStatsInput | null;
|
||||
templateScores?: string[];
|
||||
/** 冠军盘:获胜球队 code,如 FRA、BRA */
|
||||
winnerTeamCode?: string | null;
|
||||
@@ -143,8 +155,41 @@ function settleCorrectScore(
|
||||
return 'LOSE';
|
||||
}
|
||||
|
||||
function resultSide(home: number, away: number): 'HOME' | 'DRAW' | 'AWAY' {
|
||||
if (home > away) return 'HOME';
|
||||
if (home < away) return 'AWAY';
|
||||
return 'DRAW';
|
||||
}
|
||||
|
||||
function settleHtFt(score: ScoreInput, selectionCode: string): SelectionResult {
|
||||
const expected = `${resultSide(score.htHome, score.htAway)}_${resultSide(score.ftHome, score.ftAway)}`;
|
||||
return selectionCode === expected ? 'WIN' : 'LOSE';
|
||||
}
|
||||
|
||||
function settleTotalGoalsRange(totalGoals: number, selectionCode: string): SelectionResult {
|
||||
if (selectionCode === 'TG_0_1') return totalGoals <= 1 ? 'WIN' : 'LOSE';
|
||||
if (selectionCode === 'TG_2_3') return totalGoals >= 2 && totalGoals <= 3 ? 'WIN' : 'LOSE';
|
||||
if (selectionCode === 'TG_4_6') return totalGoals >= 4 && totalGoals <= 6 ? 'WIN' : 'LOSE';
|
||||
if (selectionCode === 'TG_7_PLUS') return totalGoals >= 7 ? 'WIN' : 'LOSE';
|
||||
|
||||
const exact = selectionCode.match(/^GOALS_(\d+)$/);
|
||||
if (exact) return totalGoals === Number(exact[1]) ? 'WIN' : 'LOSE';
|
||||
const exactPlus = selectionCode.match(/^GOALS_(\d+)_PLUS$/);
|
||||
if (exactPlus) return totalGoals >= Number(exactPlus[1]) ? 'WIN' : 'LOSE';
|
||||
|
||||
return 'LOSE';
|
||||
}
|
||||
|
||||
function requireStat(value: number | null | undefined, field: keyof MatchStatsInput): number {
|
||||
if (value == null || Number.isNaN(value)) {
|
||||
throw new Error(`SETTLEMENT_STAT_MISSING:${field}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function settleSelection(input: SettlementInput): SelectionResult {
|
||||
const { marketType, selectionCode, handicapLine, totalLine, score } = input;
|
||||
const stats = input.stats ?? {};
|
||||
const templates = input.templateScores ?? [];
|
||||
|
||||
switch (marketType) {
|
||||
@@ -189,6 +234,35 @@ export function settleSelection(input: SettlementInput): SelectionResult {
|
||||
const total = score.htHome + score.htAway;
|
||||
return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
|
||||
}
|
||||
case 'FT_TEAM_TOTAL_HOME':
|
||||
return settleOverUnder(score.ftHome, totalLine ?? 0, selectionCode === 'OVER');
|
||||
case 'FT_TEAM_TOTAL_AWAY':
|
||||
return settleOverUnder(score.ftAway, totalLine ?? 0, selectionCode === 'OVER');
|
||||
case 'HT_FT':
|
||||
return settleHtFt(score, selectionCode);
|
||||
case 'FT_TOTAL_GOALS':
|
||||
return settleTotalGoalsRange(score.ftHome + score.ftAway, selectionCode);
|
||||
case 'FT_CORNERS_HANDICAP': {
|
||||
const homeCorners = requireStat(stats.homeCorners, 'homeCorners');
|
||||
const awayCorners = requireStat(stats.awayCorners, 'awayCorners');
|
||||
const line = handicapLine ?? 0;
|
||||
const isHome = selectionCode === 'HOME';
|
||||
const corners = isHome ? homeCorners : awayCorners;
|
||||
const opp = isHome ? awayCorners : homeCorners;
|
||||
return settleHandicap(corners, opp, isHome ? line : -line, isHome);
|
||||
}
|
||||
case 'FT_CORNERS_OVER_UNDER': {
|
||||
const total =
|
||||
requireStat(stats.homeCorners, 'homeCorners') +
|
||||
requireStat(stats.awayCorners, 'awayCorners');
|
||||
return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
|
||||
}
|
||||
case 'FT_CARDS_OVER_UNDER': {
|
||||
const total =
|
||||
requireStat(stats.homeCards, 'homeCards') +
|
||||
requireStat(stats.awayCards, 'awayCards');
|
||||
return settleOverUnder(total, totalLine ?? 0, selectionCode === 'OVER');
|
||||
}
|
||||
case 'FT_CORRECT_SCORE':
|
||||
return settleCorrectScore(score.ftHome, score.ftAway, selectionCode, templates);
|
||||
case 'HT_CORRECT_SCORE':
|
||||
@@ -262,18 +336,8 @@ export function calculateParlayPayout(
|
||||
return { betResult: 'WON', payout: s.mul(combinedOdds), effectiveOdds: combinedOdds };
|
||||
}
|
||||
|
||||
export { isQuarterHandicapOrTotal } from '@thebet365/shared';
|
||||
|
||||
export const FT_CORRECT_SCORE_TEMPLATE = [
|
||||
'SCORE_0_0', 'SCORE_1_1', 'SCORE_2_2', 'SCORE_3_3', 'SCORE_4_4', 'OTHER_DRAW',
|
||||
'SCORE_1_0', 'SCORE_2_0', 'SCORE_2_1', 'SCORE_3_0', 'SCORE_3_1', 'SCORE_3_2',
|
||||
'SCORE_4_0', 'SCORE_4_1', 'SCORE_4_2', 'SCORE_4_3', 'OTHER_HOME',
|
||||
'SCORE_0_1', 'SCORE_0_2', 'SCORE_1_2', 'SCORE_0_3', 'SCORE_1_3', 'SCORE_2_3',
|
||||
'SCORE_0_4', 'SCORE_1_4', 'SCORE_2_4', 'SCORE_3_4', 'OTHER_AWAY',
|
||||
];
|
||||
|
||||
export const HT_CORRECT_SCORE_TEMPLATE = [
|
||||
'SCORE_0_0', 'SCORE_1_1', 'SCORE_2_2', 'OTHER_DRAW',
|
||||
'SCORE_1_0', 'SCORE_2_0', 'SCORE_2_1', 'SCORE_3_0', 'OTHER_HOME',
|
||||
'SCORE_0_1', 'SCORE_0_2', 'SCORE_1_2', 'SCORE_0_3', 'OTHER_AWAY',
|
||||
];
|
||||
export {
|
||||
FT_CORRECT_SCORE_TEMPLATE,
|
||||
HT_CORRECT_SCORE_TEMPLATE,
|
||||
isQuarterHandicapOrTotal,
|
||||
} from '@thebet365/shared';
|
||||
|
||||
Reference in New Issue
Block a user