import request from "./request"; import type { DashboardStats, AlertChannelInput, AlertNotifyChannel, ApiClient, ApiCredential, CallbackCredential, CallbackSubscription, CreateManualOrderResult, CreateTestOrderResult, DeliveryBindResult, DeliveryOrderInfo, DeliveryLinkResult, DeliverySubmitResult, FulfillmentOrder, LoginResult, LowBalanceAlertConfig, Merchant, MerchantMember, MerchantMemberGroup, MerchantOption, MerchantOwnerAccount, MerchantRole, MerchantProduct, PageResult, ProductCatalogItem, RechargeApplication, User, WalletAccount, WalletLedgerEntry, LewanProductListResult, UpdateProductRoutingInput, SyncLewanProductInput, SendLoginSMSCodeResult, PreparedDeviceLogin, DCAuthChallengeStatus, } from "../types"; export const authApi = { login: async (username: string, password: string) => { const res = await request.post("/auth/login", { username, password }); return res.data.data as LoginResult; }, sendLoginSmsCode: async (username: string, password: string) => { const res = await request.post("/auth/login/sms-code", { username, password, }); return res.data.data as SendLoginSMSCodeResult; }, prepareLogin: async (data: { username: string; password: string; smsCode: string; apiBase?: string; }) => { const res = await request.post("/auth/login/prepare", data); return res.data.data as PreparedDeviceLogin; }, completeLogin: async (data: { challengeId: string; pollToken: string; deviceTicket: string; }) => { const res = await request.post("/auth/login/complete", data); return res.data.data as LoginResult; }, deviceChallengeStatus: async (challengeId: string, pollToken: string) => { const res = await request.get( `/dcauth/challenge/${encodeURIComponent(challengeId)}/status`, { params: { pollToken }, }, ); return res.data.data as DCAuthChallengeStatus; }, profile: async () => { const res = await request.get("/auth/profile"); return res.data.data as User; }, changePassword: (data: { current_password: string; new_password: string }) => request.put("/auth/password", data).then((r) => r.data.data), updateCurrentAccount: (data: { current_password: string; username: string; nickname?: string; new_password?: string; }) => request.put("/auth/account", data).then((r) => r.data.data as User), }; export const dashboardApi = { stats: () => request.get("/dashboard").then((r) => r.data.data as DashboardStats), }; export const userApi = { list: (params?: Record) => request .get("/platform/admins", { params }) .then((r) => r.data.data as PageResult), create: (data: { username: string; password: string; nickname?: string }) => request.post("/platform/admins", data).then((r) => r.data.data as User), update: ( id: number, data: { username: string; nickname?: string; password?: string; status: number; }, ) => request .patch(`/platform/admins/${id}`, data) .then((r) => r.data.data as User), remove: (id: number) => request.delete(`/platform/admins/${id}`).then((r) => r.data.data), bindPhone: (id: number, phone: string) => request .post(`/admin/users/${id}/phone`, { phone }) .then((r) => r.data.data as User), resetDevice: (id: number) => request .post(`/admin/users/${id}/device/reset`) .then((r) => r.data.data as { reset: boolean }), }; export const merchantApi = { current: () => request.get("/merchant").then( (r) => r.data.data as { merchant: Merchant; role: string; permissions: string[]; }, ), products: (params?: Record) => request .get("/merchant/products", { params }) .then((r) => r.data.data as PageResult), updateProduct: ( id: number, data: { display_name?: string; price_amount?: number; cost_amount?: number; stock?: number; status?: "active" | "inactive"; }, ) => request.patch(`/merchant/products/${id}`, data).then((r) => r.data.data), updateProductStatus: (id: number, status: MerchantProduct["status"]) => request .patch(`/merchant/products/${id}/status`, { status }) .then((r) => r.data.data), deleteProduct: (id: number) => request.delete(`/merchant/products/${id}`).then((r) => r.data.data), orders: (params?: Record) => request .get("/merchant/orders", { params }) .then((r) => r.data.data as PageResult), manualOrderProducts: () => request .get("/merchant/orders/products") .then((r) => r.data.data as MerchantProduct[]), createTestOrder: (data: { sku: string; buyer_reference?: string; note?: string; order_status?: "paid" | "ship_failed" | "cancelled"; }) => request .post("/merchant/orders/test", data) .then((r) => r.data.data as CreateTestOrderResult), createManualOrder: (data: { client_order_no: string; sku: string; quantity: number; buyer_reference?: string; game_account?: string; note?: string; }) => request .post("/merchant/orders/manual", data) .then((r) => r.data.data as CreateManualOrderResult), cancelOrder: (orderNo: string, reason: string) => request .post(`/merchant/orders/${encodeURIComponent(orderNo)}/cancel`, { reason, }) .then( (r) => r.data.data as { order: FulfillmentOrder; refund_amount: number }, ), getDeliveryLink: (orderNo: string) => request .get(`/merchant/orders/${encodeURIComponent(orderNo)}/delivery-link`) .then((r) => r.data.data as DeliveryLinkResult), revokeDeliveryLink: (orderNo: string) => request .post( `/merchant/orders/${encodeURIComponent(orderNo)}/delivery-link/revoke`, ) .then((r) => r.data.data), restoreDeliveryLink: (orderNo: string) => request .post( `/merchant/orders/${encodeURIComponent(orderNo)}/delivery-link/restore`, ) .then((r) => r.data.data as DeliveryLinkResult), wallet: () => request.get("/merchant/wallet").then((r) => r.data.data as WalletAccount), ledger: (params?: Record) => request .get("/merchant/wallet/ledger", { params }) .then((r) => r.data.data as PageResult), apiClients: () => request .get("/merchant/api-clients") .then((r) => r.data.data as ApiClient[]), createApiClient: (data: { name: string; scopes: string; signature_version?: string; expires_at?: string; }) => request .post("/merchant/api-clients", data) .then((r) => r.data.data as ApiCredential), updateApiClientStatus: (id: number, status: ApiClient["status"]) => request .patch(`/merchant/api-clients/${id}/status`, { status }) .then((r) => r.data.data), deleteApiClient: (id: number) => request.delete(`/merchant/api-clients/${id}`).then((r) => r.data.data), callbacks: () => request .get("/merchant/callbacks") .then((r) => r.data.data as CallbackSubscription | null), saveCallback: (data: { url: string; events: string; status: CallbackSubscription["status"]; rotate_secret?: boolean; }) => request .post("/merchant/callbacks", data) .then((r) => r.data.data as CallbackCredential), members: () => request .get("/merchant/members") .then((r) => r.data.data as MerchantMember[]), addMember: (data: { user_id?: number; username?: string; password?: string; nickname?: string; role: string; is_default?: boolean; }) => request .post("/merchant/members", data) .then((r) => r.data.data as MerchantMember), updateMember: ( id: number, data: { username?: string; password?: string; nickname?: string; role: string; status: number; is_default: boolean; }, ) => request .patch(`/merchant/members/${id}`, data) .then((r) => r.data.data as MerchantMember), removeMember: (id: number) => request.delete(`/merchant/members/${id}`).then((r) => r.data.data), bindMemberPhone: (id: number, phone: string) => request .post(`/merchant/members/${id}/phone`, { phone }) .then((r) => r.data.data as User), resetMemberDevice: (id: number) => request .post(`/merchant/members/${id}/device/reset`) .then((r) => r.data.data as { reset: boolean }), roles: () => request.get("/merchant/roles").then((r) => r.data.data as MerchantRole[]), createRole: (data: { code: string; name: string; permissions: string[] }) => request .post("/merchant/roles", data) .then((r) => r.data.data as MerchantRole), updateRole: (id: number, data: { name: string; permissions: string[] }) => request .patch(`/merchant/roles/${id}`, data) .then((r) => r.data.data as MerchantRole), rechargeApplications: (params?: Record) => request .get("/merchant/recharge/applications", { params }) .then((r) => r.data.data as PageResult), createRechargeApplication: (data: { amount_cny: number; vouchers: string[]; note?: string; }) => // amount_cny 单位:分(1元 = 100分),积分按 1元 = 100积分 折算 request .post("/merchant/recharge/applications", data) .then((r) => r.data.data as RechargeApplication), alertConfig: () => request .get("/merchant/recharge/alert-config") .then((r) => r.data.data as LowBalanceAlertConfig), saveAlertConfig: (data: { enabled: boolean; threshold_points: number; notify_interval_minutes: number; max_notifications: number; }) => request .put("/merchant/recharge/alert-config", data) .then((r) => r.data.data as LowBalanceAlertConfig), alertChannels: () => request .get("/merchant/alert-channels") .then((r) => r.data.data as AlertNotifyChannel[]), createAlertChannel: (data: AlertChannelInput) => request .post("/merchant/alert-channels", data) .then((r) => r.data.data as AlertNotifyChannel), updateAlertChannel: (id: number, data: AlertChannelInput) => request .put(`/merchant/alert-channels/${id}`, data) .then((r) => r.data.data as AlertNotifyChannel), deleteAlertChannel: (id: number) => request.delete(`/merchant/alert-channels/${id}`).then((r) => r.data.data), testAlertChannel: (id: number) => request .post(`/merchant/alert-channels/${id}/test`) .then((r) => r.data.data), }; export const uploadApi = { file: (file: File) => { const form = new FormData(); form.append("file", file); return request .post("/upload", form, { headers: { "Content-Type": "multipart/form-data" }, timeout: 60000, }) .then((r) => r.data.data as { url: string }); }, }; export const deliveryApi = { getOrder: (orderNo: string, auth?: { exp?: string; sign?: string }) => request .get(`/delivery/v1/orders/${encodeURIComponent(orderNo)}`, { params: auth, }) .then((r) => r.data.data as DeliveryOrderInfo), bind: ( orderNo: string, gameAccount: string, auth?: { exp?: string; sign?: string }, ) => request .post( `/delivery/v1/orders/${encodeURIComponent(orderNo)}/bind`, { game_account: gameAccount, }, { params: auth }, ) .then((r) => r.data.data as DeliveryBindResult), getBindResult: ( orderNo: string, bindUUID: string, auth?: { exp?: string; sign?: string }, ) => request .get(`/delivery/v1/orders/${encodeURIComponent(orderNo)}/bind-result`, { params: { ...auth, bind_uuid: bindUUID }, }) .then((r) => r.data.data as DeliveryBindResult), submit: ( orderNo: string, gameAccount: string, bindUUID: string, auth?: { exp?: string; sign?: string }, ) => request .post( `/delivery/v1/orders/${encodeURIComponent(orderNo)}/submit`, { game_account: gameAccount, bind_uuid: bindUUID, }, { params: auth }, ) .then((r) => r.data.data as DeliverySubmitResult), }; export const platformApi = { merchantAccounts: (params?: Record) => request .get("/platform/merchant-accounts", { params }) .then((r) => r.data.data as PageResult), deleteMerchantAccount: (userId: number) => request .delete(`/platform/merchant-accounts/${userId}`) .then((r) => r.data.data), merchants: (params?: Record) => request .get("/platform/merchants", { params }) .then((r) => r.data.data as PageResult), // 不分页的精简商户列表,供筛选下拉使用(/platform/merchants 的 size 上限是 100) merchantOptions: () => request .get("/platform/merchant-options") .then((r) => r.data.data as MerchantOption[]), // 平台侧跨商户订单视图:不传 merchant_id 即全部商户 orders: (params?: Record) => request .get("/platform/orders", { params }) .then((r) => r.data.data as PageResult), createMerchant: (data: { code: string; name: string; contact_name?: string; contact_info?: string; owner_user_id?: number; owner_username?: string; owner_password?: string; owner_nickname?: string; features?: string; fee_type?: "rate" | "fixed"; fee_rate_bp?: number; fee_fixed_amount?: number; }) => request .post("/platform/merchants", data) .then((r) => r.data.data as Merchant), updateMerchant: (id: number, data: Partial) => request.patch(`/platform/merchants/${id}`, data).then((r) => r.data.data), members: (merchantId: number) => request .get(`/platform/merchants/${merchantId}/members`) .then((r) => r.data.data as MerchantMember[]), addMember: ( merchantId: number, data: { user_id?: number; username?: string; password?: string; nickname?: string; role: string; is_default?: boolean; }, ) => request .post(`/platform/merchants/${merchantId}/members`, data) .then((r) => r.data.data as MerchantMember), updateMember: ( merchantId: number, memberId: number, data: { role: string; status: number; is_default: boolean }, ) => request .patch(`/platform/merchants/${merchantId}/members/${memberId}`, data) .then((r) => r.data.data as MerchantMember), removeMember: (merchantId: number, memberId: number) => request .delete(`/platform/merchants/${merchantId}/members/${memberId}`) .then((r) => r.data.data), roles: (merchantId: number) => request .get(`/platform/merchants/${merchantId}/roles`) .then((r) => r.data.data as MerchantRole[]), productCatalog: () => request .get("/platform/product-catalog") .then((r) => r.data.data as ProductCatalogItem[]), merchantProducts: (merchantId: number) => request .get(`/platform/merchants/${merchantId}/products`) .then((r) => r.data.data as ProductCatalogItem[]), updateMerchantProduct: ( merchantId: number, productId: number, data: Partial, ) => request .patch(`/platform/merchants/${merchantId}/products/${productId}`, data) .then((r) => r.data.data), assignProducts: (merchantId: number, catalogIds: number[]) => request .post(`/platform/merchants/${merchantId}/products/assign`, { catalog_ids: catalogIds, }) .then((r) => r.data.data as { assigned: number }), wallet: (merchantId: number) => request .get(`/platform/merchants/${merchantId}/wallet`) .then((r) => r.data.data as WalletAccount), merchantAccount: (merchantId: number) => request .get(`/platform/merchants/${merchantId}/account`) .then((r) => r.data.data as MerchantOwnerAccount[]), updateMerchantAccount: ( merchantId: number, data: { user_id: number; username: string; nickname?: string; password?: string; status?: number; }, ) => request .patch(`/platform/merchants/${merchantId}/account`, data) .then((r) => r.data.data as MerchantOwnerAccount), walletLedger: (merchantId: number, params?: Record) => request .get(`/platform/merchants/${merchantId}/wallet/ledger`, { params }) .then((r) => r.data.data as PageResult), adjustWallet: ( merchantId: number, data: { amount: number; idempotency_key: string; note?: string }, ) => request .post(`/platform/merchants/${merchantId}/wallet/adjust`, data) .then((r) => r.data.data as WalletAccount), rechargeApplications: (params?: Record) => request .get("/platform/recharge/applications", { params }) .then((r) => r.data.data as PageResult), reviewRechargeApplication: ( id: number, data: { approved: boolean; review_note?: string }, ) => request .post(`/platform/recharge/applications/${id}/review`, data) .then((r) => r.data.data as RechargeApplication), }; export const lewanApi = { listProducts: (params?: { page?: number; size?: number; keyword?: string }) => request .get("/platform/lewan/products", { params }) .then((r) => r.data.data as LewanProductListResult), syncCatalog: (data: SyncLewanProductInput) => request .post("/platform/lewan/sync-catalog", data) .then((r) => r.data.data as ProductCatalogItem), batchSyncCatalog: (products: SyncLewanProductInput[]) => request .post("/platform/lewan/batch-sync-catalog", { products }) .then((r) => r.data.data as { synced_count: number }), updateRouting: (id: number, data: UpdateProductRoutingInput) => request .put(`/platform/products/${id}/routing`, data) .then((r) => r.data.data as { updated: boolean }), }; export interface RockConfig { id: number; auto_delivery_enabled: boolean; base_url: string; bearer_token: string; username: string; points: number; black_token_cost: number; min_points_warning: number; token_warning_stock: number; using_warn_days: number; using_danger_days: number; auto_purchase_enabled: boolean; auto_purchase_count: number; purchase_cooldown_minutes: number; login_username?: string; login_password_set?: boolean; last_token_refresh_at?: string; last_token_error?: string; last_purchased_at?: string; last_heartbeat_at?: string; } export interface RockStats { available_stock: number; using_stock: number; bound_stock: number; redeemed_stock: number; mismatch_stock: number; abnormal_stock: number; deleted_stock?: number; upstream_points: number; auto_delivery_enabled: boolean; token_warning_stock: number; auto_purchase_enabled: boolean; auto_purchase_cooldown_seconds?: number; next_auto_purchase_at?: string; username: string; last_heartbeat_at?: string; last_purchase_error?: string; last_purchase_error_at?: string; } export interface RockDashboard { available_black: number; available_blue: number; black_token_cost: number; blue_token_cost: number; cost_updated: boolean; } export interface TokenBindingRefreshResult { status: string; bound: boolean; role_name?: string; role_id?: string; channel?: string; changed: boolean; warning: boolean; message: string; redeemed_at?: string; } export interface TokenPackageLink { package_id: number; share_code: string; url: string; } export interface RockTokenItem { id: number; upstream_token_id: string; package_id?: number; package_share_code?: string; token?: string; redeem_code: string; color: string; region: string; status: string; source?: string; assigned_order_no?: string; client_order_no?: string; expected_uid?: string; actual_role_name?: string; actual_role_id?: string; actual_channel?: string; upstream_fulfill_id?: number; target_skin?: string; target_currency?: string; target_currency_quantity?: number; mismatch_at?: string; mismatch_reason?: string; remark?: string; raw_data?: string; claimed_at?: string; using_at?: string; bound_at?: string; redeemed_at?: string; created_at: string; updated_at?: string; deleted_at?: string | null; } export interface RockPurchaseResult { purchased_count: number; requested_count: number; planned_count: number; limit_reason: string; log_id: number; message: string; } export interface RockPurchaseLog { id: number; trigger_type: "auto" | "manual"; status: "running" | "success" | "partial" | "failed" | "skipped"; requested_count: number; planned_count: number; claimed_count: number; imported_count: number; upstream_available: number; points_before: number; unit_cost: number; max_by_points: number; max_by_stock: number; limit_reason: string; points_source: string; stage: string; error_message: string; package_id?: number; share_code: string; duration_ms: number; started_at: string; finished_at?: string; created_at: string; } export interface RockPurchaseStage { id: number; log_id: number; seq: number; stage: string; method: string; url: string; request_body: string; http_status: number; response_body: string; truncated: boolean; duration_ms: number; error_message: string; created_at: string; } export interface PackageImportItem { package_id: number; share_code: string; inserted: number; skipped_bound: number; error?: string; } export interface ImportAllResult { scanned: number; skipped: number; empty: number; imported: number; failed: number; tokens_imported: number; tokens_skipped_bound: number; remaining: number; results: PackageImportItem[]; } export interface RockTokenDeleteStatusCount { status: string; count: number; } export interface RockTokenDeleteItem { id: number; status: string; assigned_order_no?: string; } export interface RockTokenDeletePreview { total: number; dangerous: number; available: number; requires_force: boolean; status_counts: RockTokenDeleteStatusCount[]; dangerous_items: RockTokenDeleteItem[]; } export interface RockTokenDeleteResult { requested: number; deleted: number; forced: boolean; status_counts: RockTokenDeleteStatusCount[]; } export interface RockTokenRestoreSkipItem { id: number; status?: string; reason: string; } export interface RockTokenRestoreResult { requested: number; restored: number; skipped: number; status_counts: RockTokenDeleteStatusCount[]; skipped_items: RockTokenRestoreSkipItem[]; } export const rockApi = { getConfig: () => request.get("/platform/rock/config").then((r) => r.data.data as RockConfig), updateConfig: (data: Partial) => request .put("/platform/rock/config", data) .then((r) => r.data.data as RockConfig), testConnection: (bearer_token?: string) => request .post("/platform/rock/test-conn", { bearer_token }) .then((r) => r.data.data), testLogin: (data: { username?: string; password?: string }) => request .post("/platform/rock/login-test", data) .then((r) => r.data.data as { username: string; role: string; points: number }), getStats: () => request.get("/platform/rock/stats").then((r) => r.data.data as RockStats), refreshDashboard: () => request .post("/platform/rock/dashboard/refresh") .then((r) => r.data.data as RockDashboard), purchaseTokens: (count = 50) => request .post("/platform/rock/purchase", { count }) .then((r) => r.data.data as RockPurchaseResult), listPurchaseLogs: (params?: { page?: number; page_size?: number; trigger_type?: string; status?: string; }) => request .get("/platform/rock/purchase-logs", { params }) .then((r) => r.data.data as { list: RockPurchaseLog[]; total: number }), getPurchaseLog: (id: number) => request .get(`/platform/rock/purchase-logs/${id}`) .then( (r) => r.data.data as { log: RockPurchaseLog; stages: RockPurchaseStage[] }, ), importAllUpstreamPackages: (maxPackages = 50) => request .post("/platform/rock/upstream-packages/import-all", { max_packages: maxPackages, }) .then((r) => r.data.data as ImportAllResult), listTokens: (params?: { page?: number; page_size?: number; status?: string; search?: string; }) => request.get("/platform/rock/tokens", { params }).then( (r) => r.data.data as { list: RockTokenItem[]; total: number; page: number; page_size: number; }, ), previewTokenDelete: (params?: { status?: string; search?: string }) => request .get("/platform/rock/token-batches/preview", { params }) .then((r) => r.data.data as RockTokenDeletePreview), batchDeleteTokens: (data: { ids: number[]; force?: boolean; force_confirm_text?: string; }) => request .post("/platform/rock/token-batches/batch-delete", data) .then((r) => r.data.data as RockTokenDeleteResult), deleteAllTokens: (data: { status?: string; search?: string; expected_count: number; force?: boolean; confirm_text: string; force_confirm_text?: string; }) => request .post("/platform/rock/token-batches/delete-all", data) .then((r) => r.data.data as RockTokenDeleteResult), listDeletedTokens: (params?: { page?: number; page_size?: number; status?: string; search?: string; }) => request.get("/platform/rock/token-batches/deleted", { params }).then( (r) => r.data.data as { list: RockTokenItem[]; total: number; page: number; page_size: number; }, ), restoreTokens: (data: { ids: number[] }) => request .post("/platform/rock/token-batches/restore", data) .then((r) => r.data.data as RockTokenRestoreResult), correctExpectedUid: (id: number, expectedUid: string) => request .post(`/platform/rock/tokens/${id}/expected-uid`, { expected_uid: expectedUid, }) .then((r) => r.data.data as { message?: string; status?: string }), resetToken: (id: number) => request.post(`/platform/rock/tokens/${id}/reset`).then((r) => r.data.data), releaseToken: (id: number) => request .post(`/platform/rock/tokens/${id}/release`) .then((r) => r.data.data), refreshTokenBinding: (id: number) => request .post(`/platform/rock/tokens/${id}/refresh-binding`) .then((r) => r.data.data as TokenBindingRefreshResult), getTokenPackageLink: (id: number) => request .get(`/platform/rock/tokens/${id}/package-link`) .then((r) => r.data.data as TokenPackageLink), updateRemark: (id: number, data: { remark: string; status?: string }) => request .post(`/platform/rock/tokens/${id}/remark`, data) .then((r) => r.data.data as { message: string; token: RockTokenItem }), markAbnormal: (id: number, remark?: string) => request .post(`/platform/rock/tokens/${id}/abnormal`, { remark }) .then((r) => r.data.data as { message: string; token: RockTokenItem }), };