diff --git a/contracts/admin-openapi.json b/contracts/admin-openapi.json index fffcf0b..184c83d 100644 --- a/contracts/admin-openapi.json +++ b/contracts/admin-openapi.json @@ -2922,6 +2922,18 @@ "x-permissions": ["payment-bill:view"] } }, + "/admin/payment/recharge-apps": { + "get": { + "operationId": "listRechargeApps", + "responses": { + "200": { + "$ref": "#/components/responses/EmptyResponse" + } + }, + "x-permission": "payment-bill:view", + "x-permissions": ["payment-bill:view"] + } + }, "/admin/payment/recharge-products": { "get": { "operationId": "listRechargeProducts", diff --git a/finance/src/FinanceApp.jsx b/finance/src/FinanceApp.jsx index 7969149..18b5e10 100644 --- a/finance/src/FinanceApp.jsx +++ b/finance/src/FinanceApp.jsx @@ -7,6 +7,7 @@ import { approveFinanceWithdrawalApplication, createFinanceApplication, fetchFinanceApplicationOptions, + fetchFinanceRechargeApps, fetchFinanceRechargeSummary, fetchFinanceSession, filterOperationsByPermissions, @@ -40,6 +41,7 @@ export function FinanceApp() { const [rechargeDetailsState, setRechargeDetailsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false }); const [rechargeSummaryState, setRechargeSummaryState] = useState({ data: EMPTY_RECHARGE_SUMMARY, error: "", loading: false }); const [rechargeRegionsState, setRechargeRegionsState] = useState({ appCode: "", items: [], loading: false }); + const [rechargeAppsState, setRechargeAppsState] = useState({ items: [], loaded: false }); const [googlePaidRefreshing, setGooglePaidRefreshing] = useState(false); const [myApplicationsState, setMyApplicationsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false }); const [applicationsState, setApplicationsState] = useState({ data: { items: [], page: 1, pageSize, total: 0 }, error: "", loading: false }); @@ -159,18 +161,36 @@ export function FinanceApp() { } }, [myApplicationQuery, session?.canCreateApplication]); + // 充值详情的 App 目录来自 recharge-apps(含 Yumi 等 legacy 外部账单源);接口未就绪时回退到财务通用 App 列表。 + const rechargeApps = useMemo( + () => (rechargeAppsState.loaded && rechargeAppsState.items.length ? rechargeAppsState.items : optionsState.data.apps), + [optionsState.data.apps, rechargeAppsState] + ); + + const loadRechargeApps = useCallback(async () => { + if (!session?.canViewAppRechargeDetails) { + return; + } + try { + const items = await fetchFinanceRechargeApps(); + setRechargeAppsState({ items, loaded: true }); + } catch { + setRechargeAppsState({ items: [], loaded: false }); + } + }, [session?.canViewAppRechargeDetails]); + const loadRechargeDetails = useCallback(async () => { if (!session?.canViewAppRechargeDetails) { return; } setRechargeDetailsState((current) => ({ ...current, error: "", loading: true })); try { - const data = await listFinanceRechargeDetails(rechargeDetailQuery, optionsState.data.apps); + const data = await listFinanceRechargeDetails(rechargeDetailQuery, rechargeApps); setRechargeDetailsState({ data, error: "", loading: false }); } catch (err) { setRechargeDetailsState((current) => ({ ...current, error: err.message || "加载 APP 充值详情失败", loading: false })); } - }, [optionsState.data.apps, rechargeDetailQuery, session?.canViewAppRechargeDetails]); + }, [rechargeApps, rechargeDetailQuery, session?.canViewAppRechargeDetails]); const loadRechargeSummary = useCallback(async () => { if (!session?.canViewAppRechargeDetails) { @@ -178,12 +198,12 @@ export function FinanceApp() { } setRechargeSummaryState((current) => ({ ...current, error: "", loading: true })); try { - const data = await fetchFinanceRechargeSummary(rechargeDetailQuery, optionsState.data.apps); + const data = await fetchFinanceRechargeSummary(rechargeDetailQuery, rechargeApps); setRechargeSummaryState({ data, error: "", loading: false }); } catch (err) { setRechargeSummaryState((current) => ({ ...current, error: err.message || "加载充值汇总失败", loading: false })); } - }, [optionsState.data.apps, rechargeDetailQuery, session?.canViewAppRechargeDetails]); + }, [rechargeApps, rechargeDetailQuery, session?.canViewAppRechargeDetails]); const rechargeAppCode = rechargeDetailFilters.appCode; const loadRechargeRegions = useCallback(async () => { @@ -258,6 +278,10 @@ export function FinanceApp() { loadMyApplications(); }, [loadMyApplications]); + useEffect(() => { + loadRechargeApps(); + }, [loadRechargeApps]); + useEffect(() => { loadRechargeDetails(); }, [loadRechargeDetails]); @@ -403,7 +427,7 @@ export function FinanceApp() { filters={rechargeDetailFilters} googlePaidRefreshing={googlePaidRefreshing} loading={rechargeDetailsState.loading} - options={optionsState.data} + options={{ ...optionsState.data, apps: rechargeApps }} regions={rechargeRegionsState.items} summary={rechargeSummaryState.data} summaryLoading={rechargeSummaryState.loading} diff --git a/finance/src/api.js b/finance/src/api.js index 5c26cbe..f4af0e6 100644 --- a/finance/src/api.js +++ b/finance/src/api.js @@ -43,6 +43,15 @@ export async function fetchFinanceApplicationOptions() { return normalizeOptions({ apps: data }); } +// 充值详情的 App 目录与其他财务页不同:包含 Yumi 这类 legacy 外部账单源,只用于账单查询。 +export async function fetchFinanceRechargeApps() { + const endpoint = API_ENDPOINTS.listRechargeApps; + const data = await apiRequest(apiEndpointPath(API_OPERATIONS.listRechargeApps), { + method: endpoint.method + }); + return normalizeApps(data); +} + export async function listFinanceApplications(query = {}) { const endpoint = API_ENDPOINTS.listFinanceApplications; const data = await apiRequest(apiEndpointPath(API_OPERATIONS.listFinanceApplications), { diff --git a/finance/src/format.js b/finance/src/format.js index e491e3a..ecd308b 100644 --- a/finance/src/format.js +++ b/finance/src/format.js @@ -269,6 +269,18 @@ export function rechargeTypeLabel(value) { if (value === "usdt_trc20") { return "三方充值 · USDT"; } + if (value === "apple_recharge") { + return "苹果充值"; + } + if (value === "huawei_recharge") { + return "华为充值"; + } + if (value === "telegram_recharge") { + return "Telegram 充值"; + } + if (value === "web_recharge") { + return "三方充值 · Web"; + } return value || "-"; } diff --git a/src/shared/api/generated/endpoints.ts b/src/shared/api/generated/endpoints.ts index ea583c5..f472da2 100644 --- a/src/shared/api/generated/endpoints.ts +++ b/src/shared/api/generated/endpoints.ts @@ -192,6 +192,7 @@ export const API_OPERATIONS = { listPopups: "listPopups", listPrettyIdPools: "listPrettyIdPools", listPrettyIds: "listPrettyIds", + listRechargeApps: "listRechargeApps", listRechargeBills: "listRechargeBills", listRechargeProducts: "listRechargeProducts", listRechargeRegions: "listRechargeRegions", @@ -1583,6 +1584,13 @@ export const API_ENDPOINTS: Record = { permission: "pretty-id:view", permissions: ["pretty-id:view"] }, + listRechargeApps: { + method: "GET", + operationId: API_OPERATIONS.listRechargeApps, + path: "/v1/admin/payment/recharge-apps", + permission: "payment-bill:view", + permissions: ["payment-bill:view"] + }, listRechargeBills: { method: "GET", operationId: API_OPERATIONS.listRechargeBills, diff --git a/src/shared/api/generated/schema.d.ts b/src/shared/api/generated/schema.d.ts index f7b6f12..1e985f0 100644 --- a/src/shared/api/generated/schema.d.ts +++ b/src/shared/api/generated/schema.d.ts @@ -1924,6 +1924,22 @@ export interface paths { patch?: never; trace?: never; }; + "/admin/payment/recharge-apps": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["listRechargeApps"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/admin/payment/recharge-products": { parameters: { query?: never; @@ -6832,6 +6848,18 @@ export interface operations { 200: components["responses"]["EmptyResponse"]; }; }; + listRechargeApps: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: components["responses"]["EmptyResponse"]; + }; + }; listRechargeProducts: { parameters: { query?: never;