feat: 前台匿名浏览、登录引导、客服入口与返水增强

前台:
- 未登录可浏览首页/赛事/赔率,下注等操作弹出登录引导(去登录/继续浏览)
- 顶部新增客服入口与 iframe 弹窗
- 登录页支持暂不登录返回浏览

API:
- 首页/赛事/冠军盘接口改为公开访问,支持 X-Locale 头
- JWT 守卫支持可选认证

返水:
- 注单新增 is_cashbacked 字段,发放时自动标记
- 预览展示玩家余额,明确平台直发不从代理扣款
- 后台注单列表与玩家历史展示回水状态

其他:
- 串关禁止同场重复选号(SAME_MATCH)
- 补充结算资金流分析文档

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-11 09:36:44 +08:00
parent 785fa4416d
commit 844727c82e
35 changed files with 1007 additions and 49 deletions

View File

@@ -204,6 +204,11 @@ export const API_ERROR_MESSAGES = {
'en-US': 'Market not allowed in parlay',
'ms-MY': 'Pasaran tidak dibenarkan dalam parlay',
},
PARLAY_SAME_MATCH_FORBIDDEN: {
'zh-CN': '同一场比赛不能串关',
'en-US': 'Cannot parlay selections from the same match',
'ms-MY': 'Perlawanan sama tidak boleh berganda',
},
BET_NOT_FOUND: {
'zh-CN': '注单不存在',
'en-US': 'Bet not found',

View File

@@ -22,7 +22,20 @@ export const HANDICAP_TOTAL_MARKET_TYPES = [
'HT_OVER_UNDER',
] as const;
export type ParlayRejectReason = 'OUTRIGHT' | 'NOT_ALLOWED' | 'QUARTER_LINE';
export type ParlayRejectReason = 'OUTRIGHT' | 'NOT_ALLOWED' | 'QUARTER_LINE' | 'SAME_MATCH';
export function hasDuplicateParlayMatch(
matchIds: Array<string | bigint | null | undefined>,
): boolean {
const seen = new Set<string>();
for (const id of matchIds) {
if (id == null) continue;
const key = String(id);
if (seen.has(key)) return true;
seen.add(key);
}
return false;
}
export function isQuarterLine(line: number | null | undefined): boolean {
if (line == null || Number.isNaN(line)) return false;