import { apiClient } from './client' export type ChargeMode = '赠送' | '收费' export type QuantityKey = string export type ScreenshotKey = string export type SkinCategoryKey = string export interface PublishOptionGroup { key: SkinCategoryKey | string title: string options: string[] } export interface PublishQuantityItem { key: QuantityKey label: string price: string placeholder?: string } export interface PublishScreenshotSlot { key: ScreenshotKey label: string required: boolean hint: string } export interface PublishPriceConfig { deposit_placeholder: string price_placeholder: string ratio_description: string } export interface PublishInsuranceBaseRatio { insurance: string ratio: number } export interface PublishRatioConfigItem { key: string label: string kind: string group_key?: string missing_penalty: number } export interface PublishCoinCorrection { threshold_m: number correction: number } export interface PublishRatioConfig { insurance_base_ratios: PublishInsuranceBaseRatio[] config_items: PublishRatioConfigItem[] coin_corrections: PublishCoinCorrection[] } export interface ListingPublishOptions { server_options: string[] face_options: string[] rank_options: string[] insurance_options: string[] level_options: string[] login_method_options: string[] region_options: string[] skin_groups: PublishOptionGroup[] quantity_items: PublishQuantityItem[] screenshot_slots: PublishScreenshotSlot[] ban_record_options: string[] ban_evidence_options: string[] price_config: PublishPriceConfig ratio_config: PublishRatioConfig } interface ApiResponse { code: string message: string data: T } export const emptyListingPublishOptions: ListingPublishOptions = { server_options: [], face_options: [], rank_options: [], insurance_options: [], level_options: [], login_method_options: [], region_options: [], skin_groups: [], quantity_items: [], screenshot_slots: [], ban_record_options: [], ban_evidence_options: [], price_config: { deposit_placeholder: '', price_placeholder: '', ratio_description: '', }, ratio_config: { insurance_base_ratios: [], config_items: [], coin_corrections: [], }, } export async function fetchListingPublishOptions() { const { data } = await apiClient.get>('/listing-publish-options') return mergeListingPublishOptions(data.data) } export function mergeListingPublishOptions(options?: Partial): ListingPublishOptions { return { server_options: normalizeStringList(options?.server_options), face_options: normalizeStringList(options?.face_options), rank_options: normalizeStringList(options?.rank_options), insurance_options: normalizeStringList(options?.insurance_options), level_options: normalizeStringList(options?.level_options), login_method_options: normalizeStringList(options?.login_method_options), region_options: normalizeStringList(options?.region_options), skin_groups: normalizeOptionGroups(options?.skin_groups), quantity_items: normalizeQuantityItems(options?.quantity_items), screenshot_slots: normalizeScreenshotSlots(options?.screenshot_slots), ban_record_options: normalizeStringList(options?.ban_record_options), ban_evidence_options: normalizeStringList(options?.ban_evidence_options), price_config: normalizePriceConfig(options?.price_config), ratio_config: normalizeRatioConfig(options?.ratio_config), } } function normalizeStringList(values?: unknown[]) { return Array.isArray(values) ? values.map((item) => (typeof item === 'string' ? item.trim() : '')).filter(Boolean) : [] } function normalizeOptionGroups(values?: unknown[]): PublishOptionGroup[] { if (!Array.isArray(values)) return [] return values .map((item) => { const group = isRecord(item) ? item : {} return { key: typeof group.key === 'string' ? group.key.trim() : '', title: typeof group.title === 'string' ? group.title.trim() : '', options: normalizeStringList(Array.isArray(group.options) ? group.options : []), } }) .filter((item) => item.key && item.title) } function normalizeQuantityItems(values?: unknown[]): PublishQuantityItem[] { if (!Array.isArray(values)) return [] return values .map((item) => { const row = isRecord(item) ? item : {} return { key: typeof row.key === 'string' ? row.key.trim() : '', label: typeof row.label === 'string' ? row.label.trim() : '', price: typeof row.price === 'string' ? row.price.trim() : '', placeholder: typeof row.placeholder === 'string' ? row.placeholder.trim() : undefined, } }) .filter((item) => item.key && item.label) } function normalizeScreenshotSlots(values?: unknown[]): PublishScreenshotSlot[] { if (!Array.isArray(values)) return [] return values .map((item) => { const row = isRecord(item) ? item : {} return { key: typeof row.key === 'string' ? row.key.trim() : '', label: typeof row.label === 'string' ? row.label.trim() : '', required: row.required === true, hint: typeof row.hint === 'string' ? row.hint.trim() : '', } }) .filter((item) => item.key && item.label) } function normalizePriceConfig(value?: unknown): PublishPriceConfig { const row = isRecord(value) ? value : {} return { deposit_placeholder: typeof row.deposit_placeholder === 'string' ? row.deposit_placeholder.trim() : '', price_placeholder: typeof row.price_placeholder === 'string' ? row.price_placeholder.trim() : '', ratio_description: typeof row.ratio_description === 'string' ? row.ratio_description.trim() : '', } } function normalizeRatioConfig(value?: unknown): PublishRatioConfig { const row = isRecord(value) ? value : {} return { insurance_base_ratios: normalizeInsuranceBaseRatios( Array.isArray(row.insurance_base_ratios) ? row.insurance_base_ratios : [], ), config_items: normalizeRatioConfigItems( Array.isArray(row.config_items) ? row.config_items : [], ), coin_corrections: normalizeCoinCorrections( Array.isArray(row.coin_corrections) ? row.coin_corrections : [], ), } } function normalizeInsuranceBaseRatios(values: unknown[]): PublishInsuranceBaseRatio[] { return values .map((item) => { const row = isRecord(item) ? item : {} return { insurance: typeof row.insurance === 'string' ? row.insurance.trim() : '', ratio: readNumber(row.ratio), } }) .filter((item) => item.insurance && item.ratio > 0) } function normalizeRatioConfigItems(values: unknown[]): PublishRatioConfigItem[] { return values .map((item) => { const row = isRecord(item) ? item : {} return { key: typeof row.key === 'string' ? row.key.trim() : '', label: typeof row.label === 'string' ? row.label.trim() : '', kind: typeof row.kind === 'string' ? row.kind.trim() : '', group_key: typeof row.group_key === 'string' ? row.group_key.trim() : '', missing_penalty: readNumber(row.missing_penalty), } }) .filter((item) => item.key && item.label && item.kind) } function normalizeCoinCorrections(values: unknown[]): PublishCoinCorrection[] { return values .map((item) => { const row = isRecord(item) ? item : {} return { threshold_m: readNumber(row.threshold_m), correction: readNumber(row.correction), } }) .filter((item) => item.threshold_m >= 0 && item.correction > 0) } function readNumber(value: unknown) { const parsed = Number(value) return Number.isFinite(parsed) ? parsed : 0 } function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null }