diff --git a/src/api/admin-bet-providers.ts b/src/api/admin-bet-providers.ts
new file mode 100644
index 0000000..1004f7c
--- /dev/null
+++ b/src/api/admin-bet-providers.ts
@@ -0,0 +1,26 @@
+import { adminRequest } from "@/lib/admin-http";
+import type {
+ AdminBetProviderCreatePayload,
+ AdminBetProviderListData,
+ AdminBetProviderRow,
+ AdminBetProviderUpdatePayload,
+} from "@/types/api/admin-bet-provider";
+
+const A = `/admin`;
+
+export async function getAdminBetProviders(): Promise {
+ return adminRequest.get(`${A}/bet-providers`);
+}
+
+export async function postAdminBetProvider(
+ body: AdminBetProviderCreatePayload,
+): Promise {
+ return adminRequest.post(`${A}/bet-providers`, body);
+}
+
+export async function putAdminBetProvider(
+ id: number,
+ body: AdminBetProviderUpdatePayload,
+): Promise {
+ return adminRequest.put(`${A}/bet-providers/${id}`, body);
+}
diff --git a/src/api/admin-tickets.ts b/src/api/admin-tickets.ts
index 19c9756..7666b50 100644
--- a/src/api/admin-tickets.ts
+++ b/src/api/admin-tickets.ts
@@ -13,6 +13,7 @@ export type TicketItemsListQuery = {
site_code?: string;
agent_node_id?: number;
draw_no?: string;
+ provider_code?: string;
status?: string[];
number?: string;
start_date?: string;
diff --git a/src/app/admin/(shell)/config/bet-providers/page.tsx b/src/app/admin/(shell)/config/bet-providers/page.tsx
new file mode 100644
index 0000000..61c6ef7
--- /dev/null
+++ b/src/app/admin/(shell)/config/bet-providers/page.tsx
@@ -0,0 +1,15 @@
+import { AdminPermissionGate } from "@/components/admin/admin-permission-gate";
+import { BetProviderSettingsPanel } from "@/modules/config/bet-provider-settings-panel";
+import { buildPageMetadata } from "@/lib/page-metadata";
+import { PRD_BET_PROVIDER_MANAGE } from "@/lib/admin-prd";
+import type { Metadata } from "next";
+
+export const metadata: Metadata = buildPageMetadata("config", "betProviders.title");
+
+export default function AdminBetProvidersPage() {
+ return (
+
+
+
+ );
+}
diff --git a/src/i18n/locales/en/config.json b/src/i18n/locales/en/config.json
index ddbfa2a..9050896 100644
--- a/src/i18n/locales/en/config.json
+++ b/src/i18n/locales/en/config.json
@@ -58,6 +58,8 @@
"currenciesTitle": "Currencies",
"jackpotDesc": "Pool parameters and ledger records",
"jackpotTitle": "Jackpot",
+ "betProvidersDesc": "Manage selectable betting providers",
+ "betProvidersTitle": "Bet providers",
"oddsDesc": "Odds matrix and base rebate in one version stream",
"oddsTitle": "Odds & base rebate",
"playsDesc": "Play switches, limits, and rule copy",
@@ -68,6 +70,9 @@
"settingsTitle": "System settings",
"title": "Operations configuration"
},
+ "betProviders": {
+ "title": "Bet providers"
+ },
"integrationSites": {
"adminAccountCreated": "Created site admin account {{username}}",
"adminAccountSectionDescription": "Creating a site will also create one admin account bound to that site.",
diff --git a/src/i18n/locales/ne/config.json b/src/i18n/locales/ne/config.json
index 7df3fe0..0b48fc2 100644
--- a/src/i18n/locales/ne/config.json
+++ b/src/i18n/locales/ne/config.json
@@ -58,6 +58,8 @@
"currenciesTitle": "मुद्रा व्यवस्थापन",
"jackpotDesc": "पूल प्यारामिटर र लेजर",
"jackpotTitle": "ज्याकपट",
+ "betProvidersDesc": "छनोट गर्न मिल्ने बेट प्रदायक व्यवस्थापन",
+ "betProvidersTitle": "बेट प्रदायक",
"oddsDesc": "बाधा म्याट्रिक्स र रिबेट, एउटै संस्करण",
"oddsTitle": "बाधा र रिबेट",
"playsDesc": "खेल स्विच, सीमा र नियम पाठ",
@@ -68,6 +70,9 @@
"settingsTitle": "प्रणाली सेटिङ",
"title": "सञ्चालन कन्फिगरेसन अवलोकन"
},
+ "betProviders": {
+ "title": "बेट प्रदायक"
+ },
"integrationSites": {
"adminAccountCreated": "साइट एडमिन खाता {{username}} सिर्जना भयो",
"adminAccountSectionDescription": "साइट सिर्जना गर्दा त्यस साइटसँग बाँधिएको एउटा एडमिन खाता पनि सिर्जना हुन्छ।",
diff --git a/src/i18n/locales/zh/config.json b/src/i18n/locales/zh/config.json
index dd59a99..bba28d5 100644
--- a/src/i18n/locales/zh/config.json
+++ b/src/i18n/locales/zh/config.json
@@ -29,6 +29,8 @@
"oddsDesc": "赔率矩阵与基础回水,版本一体发布",
"jackpotTitle": "奖池",
"jackpotDesc": "奖池参数与进账流水",
+ "betProvidersTitle": "开注商管理",
+ "betProvidersDesc": "维护玩家可勾选的下注公司",
"riskCapTitle": "限额版本",
"riskCapDesc": "号码赔付封顶与占用视图",
"integrationTitle": "接入站点",
@@ -38,6 +40,9 @@
"currenciesTitle": "币种管理",
"currenciesDesc": "维护支持的币种与精度"
},
+ "betProviders": {
+ "title": "开注商管理"
+ },
"integrationSites": {
"title": "接入站点",
"description": "由运营在后台维护各主站对接参数,并通过权限控制谁能查看或修改。site_code 创建后不可修改。",
diff --git a/src/lib/admin-prd.ts b/src/lib/admin-prd.ts
index e165847..01a1da5 100644
--- a/src/lib/admin-prd.ts
+++ b/src/lib/admin-prd.ts
@@ -24,6 +24,7 @@ export const PRD_DRAW_RESULT_VIEW = "prd.draw_result.view" as const;
export const PRD_DRAW_REOPEN_MANAGE = "prd.draw_reopen.manage" as const;
export const PRD_PLAY_SWITCH_MANAGE = "prd.play_switch.manage" as const;
+export const PRD_BET_PROVIDER_MANAGE = "prd.bet_provider.manage" as const;
export const PRD_ODDS_MANAGE = "prd.odds.manage" as const;
export const PRD_REBATE_MANAGE = "prd.rebate.manage" as const;
export const PRD_REBATE_VIEW = "prd.rebate.view" as const;
@@ -169,6 +170,7 @@ export const PRD_INTEGRATION_ACCESS_ANY = [PRD_INTEGRATION_VIEW, PRD_INTEGRATION
/** 运营配置首页 */
export const PRD_CONFIG_HUB_ACCESS_ANY = [
...PRD_RULES_PLAYS_ACCESS_ANY,
+ PRD_BET_PROVIDER_MANAGE,
...PRD_RULES_ODDS_ACCESS_ANY,
...PRD_JACKPOT_ACCESS_ANY,
...PRD_RISK_CAP_ACCESS_ANY,
diff --git a/src/modules/_config/admin-nav-icons.tsx b/src/modules/_config/admin-nav-icons.tsx
index 61b2278..55a85fb 100644
--- a/src/modules/_config/admin-nav-icons.tsx
+++ b/src/modules/_config/admin-nav-icons.tsx
@@ -1,5 +1,6 @@
import type { LucideIcon } from "lucide-react";
import {
+ Building2,
CalendarClock,
ClipboardList,
Coins,
@@ -34,6 +35,7 @@ export const adminNavIconBySegment: Record
players: Users,
draws: CalendarClock,
rules_plays: ClipboardList,
+ bet_providers: Building2,
rules_odds: Percent,
jackpot: Trophy,
risk_cap: ShieldAlert,
diff --git a/src/modules/_config/admin-nav.ts b/src/modules/_config/admin-nav.ts
index e36548d..01cd102 100644
--- a/src/modules/_config/admin-nav.ts
+++ b/src/modules/_config/admin-nav.ts
@@ -14,6 +14,7 @@ export type AdminNavSegment =
| "players"
| "draws"
| "rules_plays"
+ | "bet_providers"
| "rules_odds"
| "jackpot"
| "risk_cap"
diff --git a/src/modules/config/bet-provider-settings-panel.tsx b/src/modules/config/bet-provider-settings-panel.tsx
new file mode 100644
index 0000000..d6d8f82
--- /dev/null
+++ b/src/modules/config/bet-provider-settings-panel.tsx
@@ -0,0 +1,308 @@
+"use client";
+
+import { Pencil } from "lucide-react";
+import { useCallback, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { toast } from "sonner";
+
+import {
+ getAdminBetProviders,
+ postAdminBetProvider,
+ putAdminBetProvider,
+} from "@/api/admin-bet-providers";
+import { AdminPageCard } from "@/components/admin/admin-page-card";
+import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
+import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
+import { AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Switch } from "@/components/ui/switch";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { useAsyncEffect } from "@/hooks/use-async-effect";
+import { useTranslationRef } from "@/hooks/use-translation-ref";
+import { LotteryApiBizError } from "@/types/api/errors";
+import type { AdminBetProviderRow } from "@/types/api/admin-bet-provider";
+
+type ProviderFormState = {
+ code: string;
+ name: string;
+ is_enabled: boolean;
+ sort_order: string;
+};
+
+const EMPTY_FORM: ProviderFormState = {
+ code: "",
+ name: "",
+ is_enabled: true,
+ sort_order: "0",
+};
+
+function toFormState(row: AdminBetProviderRow): ProviderFormState {
+ return {
+ code: row.code,
+ name: row.name,
+ is_enabled: row.is_enabled,
+ sort_order: String(row.sort_order),
+ };
+}
+
+function parseSortOrder(value: string): number {
+ const parsed = Number(value);
+ return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 0;
+}
+
+export function BetProviderSettingsPanel() {
+ const { t } = useTranslation(["config", "adminUsers"]);
+ const tRef = useTranslationRef(["config", "adminUsers"]);
+ const [items, setItems] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [saving, setSaving] = useState(false);
+ const [dialogOpen, setDialogOpen] = useState(false);
+ const [mode, setMode] = useState<"create" | "edit">("create");
+ const [editingId, setEditingId] = useState(null);
+ const [form, setForm] = useState(EMPTY_FORM);
+
+ const load = useCallback(async () => {
+ setLoading(true);
+ try {
+ const data = await getAdminBetProviders();
+ setItems(data.items);
+ } catch (error) {
+ toast.error(
+ error instanceof LotteryApiBizError
+ ? error.message
+ : tRef.current("betProviders.loadFailed", {
+ ns: "config",
+ defaultValue: "加载开注商失败",
+ }),
+ );
+ } finally {
+ setLoading(false);
+ }
+ }, [tRef]);
+
+ useAsyncEffect(() => {
+ void load();
+ }, []);
+
+ function openCreate(): void {
+ setMode("create");
+ setEditingId(null);
+ setForm(EMPTY_FORM);
+ setDialogOpen(true);
+ }
+
+ function openEdit(row: AdminBetProviderRow): void {
+ setMode("edit");
+ setEditingId(row.id);
+ setForm(toFormState(row));
+ setDialogOpen(true);
+ }
+
+ function updateForm(key: K, value: ProviderFormState[K]): void {
+ setForm((prev) => ({ ...prev, [key]: value }));
+ }
+
+ async function handleSubmit(): Promise {
+ const name = form.name.trim();
+ const sortOrder = parseSortOrder(form.sort_order);
+ if (name === "" || (mode === "create" && form.code.trim() === "")) {
+ toast.error(t("betProviders.form.required", { ns: "config", defaultValue: "请填写代码和名称" }));
+ return;
+ }
+
+ setSaving(true);
+ try {
+ if (mode === "create") {
+ await postAdminBetProvider({
+ code: form.code.trim().toUpperCase(),
+ name,
+ is_enabled: form.is_enabled,
+ sort_order: sortOrder,
+ });
+ toast.success(t("betProviders.createSuccess", { ns: "config", defaultValue: "已创建开注商" }));
+ } else if (editingId !== null) {
+ await putAdminBetProvider(editingId, {
+ name,
+ is_enabled: form.is_enabled,
+ sort_order: sortOrder,
+ });
+ toast.success(t("betProviders.updateSuccess", { ns: "config", defaultValue: "已更新开注商" }));
+ }
+ setDialogOpen(false);
+ await load();
+ } catch (error) {
+ toast.error(
+ error instanceof LotteryApiBizError
+ ? error.message
+ : t("betProviders.saveFailed", { ns: "config", defaultValue: "保存开注商失败" }),
+ );
+ } finally {
+ setSaving(false);
+ }
+ }
+
+ return (
+ <>
+ {t("betProviders.create", { ns: "config", defaultValue: "新增开注商" })}}
+ >
+
+
+
+
+ {t("betProviders.table.code", { ns: "config", defaultValue: "代码" })}
+ {t("betProviders.table.name", { ns: "config", defaultValue: "名称" })}
+ {t("betProviders.table.enabled", { ns: "config", defaultValue: "状态" })}
+ {t("betProviders.table.sort", { ns: "config", defaultValue: "排序" })}
+
+ {t("betProviders.table.actions", { ns: "config", defaultValue: "操作" })}
+
+
+
+
+ {loading ? (
+
+
+ {t("betProviders.loading", { ns: "config", defaultValue: "加载中..." })}
+
+
+ ) : items.length === 0 ? (
+
+ ) : (
+ items.map((row) => (
+
+ {row.code}
+
+ {row.name}
+ {row.is_default ? (
+
+ {t("betProviders.default", { ns: "config", defaultValue: "默认" })}
+
+ ) : null}
+
+
+
+ {row.is_enabled
+ ? t("system.states.enabled", { ns: "config" })
+ : t("system.states.disabled", { ns: "config" })}
+
+
+ {row.sort_order}
+
+ openEdit(row),
+ },
+ ]}
+ />
+
+
+ ))
+ )}
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/modules/config/config-hub-screen.tsx b/src/modules/config/config-hub-screen.tsx
index 1407aef..cd1d03a 100644
--- a/src/modules/config/config-hub-screen.tsx
+++ b/src/modules/config/config-hub-screen.tsx
@@ -6,7 +6,12 @@ import { ChevronRight } from "lucide-react";
import { ModuleScaffold } from "@/components/admin/module-scaffold";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
-import { PRD_INTEGRATION_ACCESS_ANY, PRD_SETTINGS_ACCESS_ANY, PRD_CURRENCY_MANAGE } from "@/lib/admin-prd";
+import {
+ PRD_BET_PROVIDER_MANAGE,
+ PRD_CURRENCY_MANAGE,
+ PRD_INTEGRATION_ACCESS_ANY,
+ PRD_SETTINGS_ACCESS_ANY,
+} from "@/lib/admin-prd";
import { useAdminProfile } from "@/stores/admin-session";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
@@ -36,6 +41,12 @@ const HUB_CARDS: HubCard[] = [
descKey: "hub.jackpotDesc",
requiredAny: ["prd.jackpot.manage", "prd.jackpot.view"],
},
+ {
+ href: "/admin/config/bet-providers",
+ titleKey: "hub.betProvidersTitle",
+ descKey: "hub.betProvidersDesc",
+ requiredAny: [PRD_BET_PROVIDER_MANAGE],
+ },
{
href: "/admin/risk/cap",
titleKey: "hub.riskCapTitle",
diff --git a/src/modules/players/player-detail-console.tsx b/src/modules/players/player-detail-console.tsx
index 1497d67..afe140e 100644
--- a/src/modules/players/player-detail-console.tsx
+++ b/src/modules/players/player-detail-console.tsx
@@ -58,6 +58,10 @@ function ticketStatusText(status: string, t: (key: string, opts?: { ns?: string
return translated === key ? status : translated;
}
+function providerLabel(providerName?: string | null, providerCode?: string | null): string {
+ return providerName || providerCode || "—";
+}
+
function walletStatusLabel(status: string, t: (key: string, opts?: { ns?: string }) => string): string {
switch (status) {
case "processing":
@@ -438,6 +442,7 @@ export function PlayerDetailConsole({ playerId }: { playerId: number }) {
{t("ticketNo", { ns: "tickets" })}
{t("orderNo", { ns: "tickets" })}
{t("drawNo", { ns: "tickets" })}
+ {t("provider", { ns: "tickets", defaultValue: "开注商" })}
{t("playCode", { ns: "tickets" })}
{t("number", { ns: "tickets" })}
{t("actualDeduct", { ns: "tickets" })}
@@ -450,7 +455,7 @@ export function PlayerDetailConsole({ playerId }: { playerId: number }) {
{ticketsLoading && tickets.length === 0 ? (
-
+
) : null}
{tickets.map((row) => (
@@ -464,6 +469,9 @@ export function PlayerDetailConsole({ playerId }: { playerId: number }) {
{row.order_no ?? "—"}
{row.draw_no ?? "—"}
+
+ {providerLabel(row.provider_name, row.provider_code)}
+
{playCodeLabel(row.play_code)}
{row.original_number ?? "—"}
@@ -491,7 +499,7 @@ export function PlayerDetailConsole({ playerId }: { playerId: number }) {
))}
{!ticketsLoading && tickets.length === 0 ? (
-
+
) : null}
diff --git a/src/modules/tickets/player-tickets-console.tsx b/src/modules/tickets/player-tickets-console.tsx
index 9c07bbc..09dbe13 100644
--- a/src/modules/tickets/player-tickets-console.tsx
+++ b/src/modules/tickets/player-tickets-console.tsx
@@ -61,6 +61,7 @@ const TICKET_STATUS_OPTIONS = [
type TicketFilters = {
playerQuery: string;
drawNo: string;
+ providerCode: string;
numberKeyword: string;
startDate: string;
endDate: string;
@@ -70,6 +71,7 @@ type TicketFilters = {
const emptyTicketFilters: TicketFilters = {
playerQuery: "",
drawNo: "",
+ providerCode: "",
numberKeyword: "",
startDate: "",
endDate: "",
@@ -103,6 +105,10 @@ function ticketDetailPath(ticketNo: string): string {
return `/admin/tickets/${encodeURIComponent(ticketNo)}`;
}
+function providerLabel(providerName?: string | null, providerCode?: string | null): string {
+ return providerName || providerCode || "—";
+}
+
function TicketFilterField({
id,
label,
@@ -164,6 +170,7 @@ export function PlayerTicketsConsole(): React.ReactElement {
per_page: perPage,
...query,
draw_no: applied.drawNo.trim() || undefined,
+ provider_code: applied.providerCode.trim().toUpperCase() || undefined,
status: applied.statuses.length > 0 ? applied.statuses : undefined,
number: applied.numberKeyword.trim() || undefined,
start_date: applied.startDate || undefined,
@@ -188,6 +195,7 @@ export function PlayerTicketsConsole(): React.ReactElement {
...draft,
playerQuery: draft.playerQuery.trim(),
drawNo: draft.drawNo.trim(),
+ providerCode: draft.providerCode.trim().toUpperCase(),
numberKeyword: draft.numberKeyword.trim(),
});
setPage(1);
@@ -263,6 +271,22 @@ export function PlayerTicketsConsole(): React.ReactElement {
}}
/>
+
+
+ setDraft((current) => ({ ...current, providerCode: e.target.value.toUpperCase() }))
+ }
+ onKeyDown={(e) => {
+ if (e.key === "Enter") {
+ runSearch();
+ }
+ }}
+ />
+
- {applied.playerQuery || applied.drawNo || applied.numberKeyword || applied.startDate || applied.endDate || applied.statuses.length > 0 ? (
+ {applied.playerQuery || applied.drawNo || applied.providerCode || applied.numberKeyword || applied.startDate || applied.endDate || applied.statuses.length > 0 ? (
{applied.playerQuery ? (
<>
@@ -337,6 +361,12 @@ export function PlayerTicketsConsole(): React.ReactElement {
{t("drawNo")}:{applied.drawNo}
>
) : null}
+ {applied.providerCode ? (
+ <>
+ {" · "}
+ {t("provider", { defaultValue: "开注商" })}:{applied.providerCode}
+ >
+ ) : null}
) : null}
@@ -353,6 +383,7 @@ export function PlayerTicketsConsole(): React.ReactElement {
{t("fundingMode", { ns: "players" })}
{t("orderNo")}
{t("drawNo")}
+ {t("provider", { defaultValue: "开注商" })}
{t("playCode")}
{t("number")}
{t("betAmount")}
@@ -367,9 +398,9 @@ export function PlayerTicketsConsole(): React.ReactElement {
{loading && !data ? (
-
+
) : !data || data.items.length === 0 ? (
-
+
) : (
data.items.map((row) => {
const winLabel = row.jackpot_win_amount > 0
@@ -392,6 +423,9 @@ export function PlayerTicketsConsole(): React.ReactElement {
{row.order_no ?? "—"}
{row.draw_no ?? "—"}
+
+ {providerLabel(row.provider_name, row.provider_code)}
+
{playCodeLabel(row.play_code)}
{row.original_number ?? "—"}
diff --git a/src/modules/tickets/ticket-detail-console.tsx b/src/modules/tickets/ticket-detail-console.tsx
index 7dfffab..1248f77 100644
--- a/src/modules/tickets/ticket-detail-console.tsx
+++ b/src/modules/tickets/ticket-detail-console.tsx
@@ -37,6 +37,10 @@ function ticketStatusText(value: string, t: (key: string) => string): string {
return translated === key ? value : translated;
}
+function providerLabel(providerName?: string | null, providerCode?: string | null): string {
+ return providerName || providerCode || "—";
+}
+
export function TicketDetailConsole({ ticketNo }: { ticketNo: string }) {
const { t } = useTranslation(["tickets", "common"]);
const tRef = useTranslationRef(["tickets", "common"]);
@@ -126,6 +130,12 @@ export function TicketDetailConsole({ ticketNo }: { ticketNo: string }) {
{t("number")}:
{data.original_number ?? "—"}
+
+ {t("provider", { defaultValue: "开注商" })}:
+
+ {providerLabel(data.provider_name, data.provider_code)}
+
+
{t("combinationCount")}:
{data.combination_count}
diff --git a/src/types/api/admin-bet-provider.ts b/src/types/api/admin-bet-provider.ts
new file mode 100644
index 0000000..a666cd3
--- /dev/null
+++ b/src/types/api/admin-bet-provider.ts
@@ -0,0 +1,27 @@
+export type AdminBetProviderRow = {
+ id: number;
+ code: string;
+ name: string;
+ is_enabled: boolean;
+ sort_order: number;
+ is_default: boolean;
+ created_at: string | null;
+ updated_at: string | null;
+};
+
+export type AdminBetProviderListData = {
+ items: AdminBetProviderRow[];
+};
+
+export type AdminBetProviderCreatePayload = {
+ code: string;
+ name: string;
+ is_enabled?: boolean;
+ sort_order?: number;
+};
+
+export type AdminBetProviderUpdatePayload = {
+ name?: string;
+ is_enabled?: boolean;
+ sort_order?: number;
+};
diff --git a/src/types/api/admin-player-tickets.ts b/src/types/api/admin-player-tickets.ts
index 52df231..d85df19 100644
--- a/src/types/api/admin-player-tickets.ts
+++ b/src/types/api/admin-player-tickets.ts
@@ -3,6 +3,8 @@ export type AdminPlayerTicketItemRow = {
order_no: string | null;
draw_no: string | null;
currency_code: string | null;
+ provider_code?: string | null;
+ provider_name?: string | null;
play_code: string;
original_number: string | null;
/** @deprecated 历史字段,单位为 minor;优先使用 total_bet_amount_minor */
diff --git a/src/types/api/admin-tickets.ts b/src/types/api/admin-tickets.ts
index 2afe8fb..2bbc54a 100644
--- a/src/types/api/admin-tickets.ts
+++ b/src/types/api/admin-tickets.ts
@@ -14,6 +14,8 @@ export type AdminTicketItemRow = {
order_no: string | null;
draw_no: string | null;
currency_code: string | null;
+ provider_code?: string | null;
+ provider_name?: string | null;
play_code: string;
original_number: string | null;
/** @deprecated 历史字段,单位为 minor;优先使用 total_bet_amount_minor */
@@ -63,6 +65,8 @@ export type AdminTicketItemDetail = {
agent_node_id: number | null;
agent_code: string | null;
agent_name: string | null;
+ provider_code?: string | null;
+ provider_name?: string | null;
play_code: string;
dimension: number | null;
digit_slot: number | null;