import { apiRequest } from "@/shared/api/request"; import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints"; import type { ApiPage, CoinAdjustmentCreateDto, CoinAdjustmentDto, CoinAdjustmentPayload, CoinLedgerEntryDto, CoinLedgerUserDto, CoinSellerLedgerDto, PageQuery, ReportDto, } from "@/shared/api/types"; export interface GiftDiamondRatioItem { effectiveRegionId: number; giftTypeCode: string; ratioPercent: string; returnCoinRatioPercent: string; regionId: number; status: string; updatedAtMs?: number; } export interface GiftRecordUserDto { userId: string; displayUserId?: string; defaultDisplayUserId?: string; prettyDisplayUserId?: string; prettyId?: string; username?: string; avatar?: string; } export interface GiftRecordDto { transactionId: string; scene: "room" | "direct"; roomId?: string; sender: GiftRecordUserDto; receiver: GiftRecordUserDto; gift: { giftId: string; name: string; coverUrl?: string; }; giftCount: number; unitValue: number; giftValue: number; createdAtMs: number; } export interface GiftDiamondRatioResponse { regionId: number; items: GiftDiamondRatioItem[]; } export type WheelRewardType = "coin" | "dress" | "gift" | "prop"; export interface WheelTierDto { tierId: string; displayName: string; rewardType: WheelRewardType; rewardId: string; rewardCount: number; rewardCoins: number; rtpValueCoins: number; probabilityPercent: number; enabled: boolean; metadataJson: string; } export interface WheelConfigDto { appCode?: string; wheelId: string; enabled: boolean; ruleVersion: number; drawPriceCoins: number; targetRTPPercent: number; poolRatePercent: number; settlementWindowDraws: number; initialPoolCoins: number; poolReserveCoins: number; maxSingleRTPPayout: number; effectiveFromMs: number; createdByAdminId?: number; createdAtMs?: number; tiers: WheelTierDto[]; } export interface WheelConfigPayload { wheel_id: string; enabled: boolean; draw_price_coins: number; target_rtp_percent: number; pool_rate_percent: number; settlement_window_draws: number; initial_pool_coins: number; pool_reserve_coins: number; max_single_rtp_payout: number; effective_from_ms: number; tiers: Array<{ tier_id: string; display_name: string; reward_type: WheelRewardType; reward_id: string; reward_count: number; reward_coins: number; rtp_value_coins: number; probability_percent: number; enabled: boolean; metadata_json: string; }>; } export interface WheelDrawDto { drawId: string; drawIds: string[]; commandId: string; wheelId: string; ruleVersion: number; selectedTierId: string; rewardType: string; rewardId: string; rewardCount: number; rewardCoins: number; rtpValueCoins: number; rewardStatus: string; rtpWindowIndex: number; actualRTPPPM: number; createdAtMs: number; walletTransactionId: string; coinBalanceAfter: number; metadataJson: string; } export interface WheelDrawSummaryDto { wheelId: string; totalDraws: number; uniqueUsers: number; totalSpentCoins: number; totalRTPValueCoins: number; actualRTPPPM: number; pendingDraws: number; grantedDraws: number; failedDraws: number; } type RawWheelConfig = WheelConfigDto & Record; type RawWheelTier = WheelTierDto & Record; type RawWheelDraw = WheelDrawDto & Record; type RawWheelSummary = WheelDrawSummaryDto & Record; type WheelSummaryQuery = Record; export function listCoinLedger(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listCoinLedger; return apiRequest>(apiEndpointPath(API_OPERATIONS.listCoinLedger), { method: endpoint.method, query, }); } export function listGiftRecords(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listGiftRecords; return apiRequest>(apiEndpointPath(API_OPERATIONS.listGiftRecords), { method: endpoint.method, query, }); } export function listCoinSellerLedger(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listCoinSellerLedger; return apiRequest>(apiEndpointPath(API_OPERATIONS.listCoinSellerLedger), { method: endpoint.method, query, }); } export function exportCoinSellerLedger(query: PageQuery = {}): Promise { const endpoint = API_ENDPOINTS.exportCoinSellerLedger; return apiRequest(apiEndpointPath(API_OPERATIONS.exportCoinSellerLedger), { method: endpoint.method, query, raw: true, }); } export function listCoinAdjustments(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listCoinAdjustments; return apiRequest>(apiEndpointPath(API_OPERATIONS.listCoinAdjustments), { method: endpoint.method, query, }); } export function listReports(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listReports; return apiRequest>(apiEndpointPath(API_OPERATIONS.listReports), { method: endpoint.method, query, }); } export function lookupCoinAdjustmentTarget(userId: string): Promise { const endpoint = API_ENDPOINTS.lookupCoinAdjustmentTarget; return apiRequest(apiEndpointPath(API_OPERATIONS.lookupCoinAdjustmentTarget), { method: endpoint.method, query: { user_id: userId }, }); } export function createCoinAdjustment(payload: CoinAdjustmentPayload): Promise { const endpoint = API_ENDPOINTS.createCoinAdjustment; return apiRequest( apiEndpointPath(API_OPERATIONS.createCoinAdjustment), { body: payload, method: endpoint.method, }, ); } export function getGiftDiamondRatios(regionId: string | number): Promise { return apiRequest("/v1/admin/operations/gift-diamond-ratios", { method: "GET", query: { region_id: regionId }, }); } export function updateGiftDiamondRatios(payload: { regionId: number; ratios: Record; returnCoinRatios: Record; }): Promise { return apiRequest("/v1/admin/operations/gift-diamond-ratios", { body: payload, method: "PUT", }); } export function getWheelConfig(wheelId = "default"): Promise { const endpoint = API_ENDPOINTS.getWheelConfig; return apiRequest(apiEndpointPath(API_OPERATIONS.getWheelConfig), { method: endpoint.method, query: { wheel_id: wheelId }, }).then(normalizeWheelConfig); } export function updateWheelConfig(payload: WheelConfigPayload): Promise { const endpoint = API_ENDPOINTS.upsertWheelConfig; return apiRequest(apiEndpointPath(API_OPERATIONS.upsertWheelConfig), { body: payload, method: endpoint.method, query: { wheel_id: payload.wheel_id }, }).then(normalizeWheelConfig); } export function listWheelDraws(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listWheelDraws; return apiRequest>(apiEndpointPath(API_OPERATIONS.listWheelDraws), { method: endpoint.method, query, }).then((page) => ({ ...page, items: (page.items || []).map(normalizeWheelDraw), })); } export function getWheelDrawSummary(query: WheelSummaryQuery = {}): Promise { const endpoint = API_ENDPOINTS.getWheelDrawSummary; return apiRequest(apiEndpointPath(API_OPERATIONS.getWheelDrawSummary), { method: endpoint.method, query, }).then(normalizeWheelSummary); } function normalizeWheelConfig(item: RawWheelConfig): WheelConfigDto { // 后端为了兼容 Go DTO 使用 snake_case;页面统一消费 camelCase, // 避免表单、表格、保存 payload 混用两套字段名导致配置保存缺字段。 return { appCode: stringValue(item.appCode ?? item.app_code), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), createdByAdminId: numberValue(item.createdByAdminId ?? item.created_by_admin_id), drawPriceCoins: numberValue(item.drawPriceCoins ?? item.draw_price_coins), effectiveFromMs: numberValue(item.effectiveFromMs ?? item.effective_from_ms), enabled: booleanValue(item.enabled), initialPoolCoins: numberValue(item.initialPoolCoins ?? item.initial_pool_coins), maxSingleRTPPayout: numberValue(item.maxSingleRTPPayout ?? item.max_single_rtp_payout), poolRatePercent: numberValue(item.poolRatePercent ?? item.pool_rate_percent), poolReserveCoins: numberValue(item.poolReserveCoins ?? item.pool_reserve_coins), ruleVersion: numberValue(item.ruleVersion ?? item.rule_version), settlementWindowDraws: numberValue(item.settlementWindowDraws ?? item.settlement_window_draws), targetRTPPercent: numberValue(item.targetRTPPercent ?? item.target_rtp_percent), tiers: arrayValue(item.tiers).map(normalizeWheelTier), wheelId: stringValue(item.wheelId ?? item.wheel_id), }; } function normalizeWheelTier(raw: unknown): WheelTierDto { const item = asRecord(raw) as RawWheelTier; const rewardType = normalizeRewardType(item.rewardType ?? item.reward_type); return { displayName: stringValue(item.displayName ?? item.display_name), enabled: booleanValue(item.enabled), metadataJson: stringValue(item.metadataJson ?? item.metadata_json) || "{}", probabilityPercent: numberValue(item.probabilityPercent ?? item.probability_percent), rewardCoins: numberValue(item.rewardCoins ?? item.reward_coins), rewardCount: numberValue(item.rewardCount ?? item.reward_count), rewardId: stringValue(item.rewardId ?? item.reward_id), rewardType, rtpValueCoins: rewardType === "prop" || rewardType === "dress" ? 0 : numberValue(item.rtpValueCoins ?? item.rtp_value_coins), tierId: stringValue(item.tierId ?? item.tier_id) || rewardType, }; } function normalizeWheelDraw(item: RawWheelDraw): WheelDrawDto { return { actualRTPPPM: numberValue(item.actualRTPPPM ?? item.actual_rtp_ppm), coinBalanceAfter: numberValue(item.coinBalanceAfter ?? item.coin_balance_after), commandId: stringValue(item.commandId ?? item.command_id), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), drawId: stringValue(item.drawId ?? item.draw_id), drawIds: arrayValue(item.drawIds ?? item.draw_ids).map(stringValue), metadataJson: stringValue(item.metadataJson ?? item.metadata_json) || "{}", rewardCoins: numberValue(item.rewardCoins ?? item.reward_coins), rewardCount: numberValue(item.rewardCount ?? item.reward_count), rewardId: stringValue(item.rewardId ?? item.reward_id), rewardStatus: stringValue(item.rewardStatus ?? item.reward_status), rewardType: stringValue(item.rewardType ?? item.reward_type), rtpValueCoins: numberValue(item.rtpValueCoins ?? item.rtp_value_coins), rtpWindowIndex: numberValue(item.rtpWindowIndex ?? item.rtp_window_index), ruleVersion: numberValue(item.ruleVersion ?? item.rule_version), selectedTierId: stringValue(item.selectedTierId ?? item.selected_tier_id), walletTransactionId: stringValue(item.walletTransactionId ?? item.wallet_transaction_id), wheelId: stringValue(item.wheelId ?? item.wheel_id), }; } function normalizeWheelSummary(item: RawWheelSummary): WheelDrawSummaryDto { return { actualRTPPPM: numberValue(item.actualRTPPPM ?? item.actual_rtp_ppm), failedDraws: numberValue(item.failedDraws ?? item.failed_draws), grantedDraws: numberValue(item.grantedDraws ?? item.granted_draws), pendingDraws: numberValue(item.pendingDraws ?? item.pending_draws), totalDraws: numberValue(item.totalDraws ?? item.total_draws), totalRTPValueCoins: numberValue(item.totalRTPValueCoins ?? item.total_rtp_value_coins), totalSpentCoins: numberValue(item.totalSpentCoins ?? item.total_spent_coins), uniqueUsers: numberValue(item.uniqueUsers ?? item.unique_users), wheelId: stringValue(item.wheelId ?? item.wheel_id), }; } function asRecord(value: unknown): Record { return value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; } function arrayValue(value: unknown): unknown[] { return Array.isArray(value) ? value : []; } function booleanValue(value: unknown) { return value === true || value === "true" || value === 1 || value === "1"; } function normalizeRewardType(value: unknown): WheelRewardType { const normalized = stringValue(value).trim().toLowerCase(); if (normalized === "dress" || normalized === "gift" || normalized === "prop") { return normalized; } return "coin"; } function stringValue(value: unknown) { return typeof value === "string" ? value : value === undefined || value === null ? "" : String(value); } function numberValue(value: unknown) { const number = Number(value || 0); return Number.isFinite(number) ? number : 0; }