From 5cbabb5d0ff4ea8c0d732b6dcd1d263ed562f22b Mon Sep 17 00:00:00 2001 From: kang Date: Thu, 9 Jul 2026 10:51:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20=E6=8C=89=E5=BC=80=E6=B3=A8?= =?UTF-8?q?=E5=95=86=E6=94=AF=E6=8C=81=E8=8E=B7=E5=8F=96=E8=B5=94=E7=8E=87?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=B9=B6=E5=AE=8C=E5=96=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在getPlayEffective接口中增加provider_code参数支持 - 优化接口调用时对currency和provider_code的传递逻辑 - hall-bet-preview-dialog新增previewLineKey方法,解决列表渲染key冲突问题 - 修改投注和结果弹窗中开注商标题的国际化调用方式 - hall-betting-grid中增加选中开注商的默认逻辑,接口调用时传递provider_code - 优化provider名称显示逻辑,完善开注商选择及摘要文本显示 - 订单详情页中显示开注商名称,提升显示准确性 - 补充i18n中开注商相关文案,支持多语言环境 - 更新PlayEffectiveOddsSlice和PlayEffectivePayload类型,加入provider_code字段支持 --- .env.example | 2 +- src/api/play.ts | 11 +++++-- src/features/hall/hall-bet-preview-dialog.tsx | 18 ++++++++--- src/features/hall/hall-bet-result-dialog.tsx | 2 +- src/features/hall/hall-betting-grid.tsx | 32 +++++++++++-------- .../orders/ticket-order-detail-screen.tsx | 2 +- src/i18n/locales/en/player.json | 8 +++++ src/i18n/locales/ne/player.json | 8 +++++ src/i18n/locales/zh/player.json | 8 +++++ src/types/api/play-effective.ts | 2 ++ 10 files changed, 70 insertions(+), 23 deletions(-) diff --git a/.env.example b/.env.example index cbd6815..ac06a8a 100644 --- a/.env.example +++ b/.env.example @@ -11,7 +11,7 @@ LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 # 线上 # LOTTERY_API_UPSTREAM=http://127.0.0.1:8000 -# LOTTERY_API_UPSTREAM=https://lotterylaravel.tanumo.com +# LOTTERY_API_UPSTREAM=https://lottery-api.cjdhr.top # Next 开发:局域网 IP 访问(逗号分隔 host,无协议) # ALLOWED_DEV_ORIGINS=192.168.0.101 diff --git a/src/api/play.ts b/src/api/play.ts index a43db48..a1234c1 100644 --- a/src/api/play.ts +++ b/src/api/play.ts @@ -4,6 +4,8 @@ import type { PlayEffectivePayload } from "@/types/api/play-effective"; export type GetPlayEffectiveParams = { /** 不传则后端取首个可下注币种(通常为 NPR) */ currency?: string; + /** 按开注商返回覆盖后的赔率;未传则返回通用赔率 */ + provider_code?: string; }; /** @@ -17,8 +19,13 @@ export function getPlayEffective( `/play/effective`, { params: - params?.currency !== undefined && params.currency !== "" - ? { currency: params.currency } + params + ? { + ...(params.currency !== undefined && params.currency !== "" ? { currency: params.currency } : {}), + ...(params.provider_code !== undefined && params.provider_code !== "" + ? { provider_code: params.provider_code } + : {}), + } : undefined, }, ); diff --git a/src/features/hall/hall-bet-preview-dialog.tsx b/src/features/hall/hall-bet-preview-dialog.tsx index 580c2ff..35cffa4 100644 --- a/src/features/hall/hall-bet-preview-dialog.tsx +++ b/src/features/hall/hall-bet-preview-dialog.tsx @@ -23,7 +23,7 @@ import { } from "@/components/ui/dialog"; import { formatMinorAsCurrency } from "@/lib/money"; import { playLabel } from "@/lib/play-labels"; -import type { TicketPreviewData, TicketPreviewWarning } from "@/types/api/ticket"; +import type { TicketPreviewData, TicketPreviewLine, TicketPreviewWarning } from "@/types/api/ticket"; type HallBetPreviewDialogProps = { open: boolean; @@ -65,6 +65,16 @@ function providerLabel(providerName?: string | null, providerCode?: string | nul return providerName || providerCode || "-"; } +function previewLineKey(line: TicketPreviewLine, index: number): string { + return [ + line.client_line_no, + line.provider_code ?? "provider", + line.play_code, + line.number, + index, + ].join(":"); +} + function SubmittingPanel() { const { t } = useTranslation("player"); @@ -221,7 +231,7 @@ export function HallBetPreviewDialog({ {t("orders.play")} - {t("hall.providers.title", { defaultValue: "开注商" })} + {t("hall.providers.title")} {t("hall.preview.amount")} @@ -238,8 +248,8 @@ export function HallBetPreviewDialog({ - {lines.map((ln) => ( - + {lines.map((ln, index) => ( + {ln.client_line_no} diff --git a/src/features/hall/hall-bet-result-dialog.tsx b/src/features/hall/hall-bet-result-dialog.tsx index 1f9e8e1..538a553 100644 --- a/src/features/hall/hall-bet-result-dialog.tsx +++ b/src/features/hall/hall-bet-result-dialog.tsx @@ -219,7 +219,7 @@ export function HallBetResultDialog({ {t("orders.play")} - {t("hall.providers.title", { defaultValue: "开注商" })} + {t("hall.providers.title")} {t("hall.result.actualDeduct")} diff --git a/src/features/hall/hall-betting-grid.tsx b/src/features/hall/hall-betting-grid.tsx index d2d263c..3a9b940 100644 --- a/src/features/hall/hall-betting-grid.tsx +++ b/src/features/hall/hall-betting-grid.tsx @@ -482,12 +482,19 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } }, []); const catalogSeqRef = useRef(0); const walletSeqRef = useRef(0); + const selectedCatalogProviderCode = + selectedProviderCodes[0] ?? + betProviders.find((provider) => provider.is_default)?.code ?? + betProviders[0]?.code; const loadCatalog = useCallback(async () => { const seq = ++catalogSeqRef.current; setCatalogState({ kind: "loading" }); try { - const data = await getPlayEffective({ currency: currencyParam }); + const data = await getPlayEffective({ + currency: currencyParam, + provider_code: selectedCatalogProviderCode, + }); if (seq !== catalogSeqRef.current) return; setCatalogState({ kind: "ok", data }); } catch (e) { @@ -495,7 +502,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } const msg = e instanceof LotteryApiBizError ? e.message : t("hall.loadingError"); setCatalogState({ kind: "error", message: msg }); } - }, [currencyParam, t]); + }, [currencyParam, selectedCatalogProviderCode, t]); const refreshWallet = useCallback(async () => { const seq = ++walletSeqRef.current; @@ -1257,14 +1264,13 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } const providerSummaryText = selectedProviderCodes.length > 0 ? t("hall.providers.summary", { - defaultValue: "已选 {{count}} 家,金额按每家公司计算", count: providerSummaryCount, }) - : t("hall.providers.platformDefaultSummary", { - defaultValue: implicitDefaultProvider - ? `未选择时走平台默认(${implicitDefaultProvider.name})` - : "未选择时走平台默认", - }); + : implicitDefaultProvider + ? t("hall.providers.platformDefaultSummary", { + provider: implicitDefaultProvider.name, + }) + : t("hall.providers.platformDefaultSummaryNoProvider"); return ( <> @@ -1511,7 +1517,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
- {t("hall.providers.title", { defaultValue: "开注商" })} + {t("hall.providers.title")} {!isMobile ? betProviders.map((provider) => { const checked = selectedProviderCodes.includes(provider.code); @@ -1544,11 +1550,9 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } {selectedProviderNames.length > 0 ? selectedProviderNames.join(" / ") - : t("hall.providers.platformDefault", { - defaultValue: implicitDefaultProvider - ? `平台默认(${implicitDefaultProvider.name})` - : "平台默认", - })} + : implicitDefaultProvider + ? t("hall.providers.platformDefault", { provider: implicitDefaultProvider.name }) + : t("hall.providers.platformDefaultNoProvider")} )}
diff --git a/src/features/orders/ticket-order-detail-screen.tsx b/src/features/orders/ticket-order-detail-screen.tsx index 546e4b3..1bb3500 100644 --- a/src/features/orders/ticket-order-detail-screen.tsx +++ b/src/features/orders/ticket-order-detail-screen.tsx @@ -366,7 +366,7 @@ export function TicketOrderDetailScreen({ ticketNo }: { ticketNo: string }) {
- {t("hall.providers.title", { defaultValue: "开注商" })} + {t("hall.providers.title")} {providerLabel(data.provider_name, data.provider_code)} diff --git a/src/i18n/locales/en/player.json b/src/i18n/locales/en/player.json index 86af25e..7705dc4 100644 --- a/src/i18n/locales/en/player.json +++ b/src/i18n/locales/en/player.json @@ -169,6 +169,14 @@ "updated": "Play configuration updated. The betting table has been refreshed.", "oddsUpdated": "Odds updated. Please preview the ticket again." }, + "providers": { + "title": "Bet provider", + "summary": "{{count}} selected. Amount is calculated per provider.", + "platformDefault": "Platform default ({{provider}})", + "platformDefaultNoProvider": "Platform default", + "platformDefaultSummary": "If none is selected, platform default is used ({{provider}}).", + "platformDefaultSummaryNoProvider": "If none is selected, platform default is used." + }, "jackpotBurst": { "title": "Jackpot Burst", "subtitle": "Issue {{drawNo}} triggered a pool payout", diff --git a/src/i18n/locales/ne/player.json b/src/i18n/locales/ne/player.json index 8bc30be..96aaf2d 100644 --- a/src/i18n/locales/ne/player.json +++ b/src/i18n/locales/ne/player.json @@ -169,6 +169,14 @@ "updated": "प्ले कन्फिगरेसन अपडेट भयो। बेटिङ तालिका रिफ्रेस गरियो।", "oddsUpdated": "Odds अपडेट भयो। कृपया टिकट फेरि पूर्वावलोकन गर्नुहोस्।" }, + "providers": { + "title": "Bet provider", + "summary": "{{count}} चयन गरियो। रकम प्रत्येक provider अनुसार गणना हुन्छ।", + "platformDefault": "Platform default ({{provider}})", + "platformDefaultNoProvider": "Platform default", + "platformDefaultSummary": "कुनै चयन नभए platform default प्रयोग हुन्छ ({{provider}})।", + "platformDefaultSummaryNoProvider": "कुनै चयन नभए platform default प्रयोग हुन्छ।" + }, "jackpotBurst": { "title": "ज्याकपट बर्स्ट", "subtitle": "इश्यू {{drawNo}} मा पूल payout ट्रिगर भयो", diff --git a/src/i18n/locales/zh/player.json b/src/i18n/locales/zh/player.json index d2e0a86..01d36e5 100644 --- a/src/i18n/locales/zh/player.json +++ b/src/i18n/locales/zh/player.json @@ -168,6 +168,14 @@ "updated": "玩法配置已更新,已刷新下注表格。", "oddsUpdated": "赔率已更新,请重新预览注单。" }, + "providers": { + "title": "开注商", + "summary": "已选 {{count}} 家,金额按每家公司计算", + "platformDefault": "平台默认({{provider}})", + "platformDefaultNoProvider": "平台默认", + "platformDefaultSummary": "未选择时走平台默认({{provider}})", + "platformDefaultSummaryNoProvider": "未选择时走平台默认" + }, "jackpotBurst": { "title": "Jackpot 爆池", "subtitle": "期号 {{drawNo}} 触发奖池派发", diff --git a/src/types/api/play-effective.ts b/src/types/api/play-effective.ts index 1d64321..0cc4469 100644 --- a/src/types/api/play-effective.ts +++ b/src/types/api/play-effective.ts @@ -18,6 +18,7 @@ export type PlayEffectiveConfigSlice = { }; export type PlayEffectiveOddsSlice = { + provider_code?: string; prize_scope: string; odds_value: number; rebate_rate: string; @@ -50,6 +51,7 @@ export type PlayEffectiveRiskCapRow = { export type PlayEffectivePayload = { currency_code: string; + provider_code?: string; effective_versions: { play_config: PlayEffectiveVersionHead; odds: PlayEffectiveVersionHead;