Files
hfb_sys/frontend/src/features/listings/api/listings.ts
T

337 lines
9.4 KiB
TypeScript

import { apiClient } from '@/shared/api/client'
import type { ApiResponse } from '@/shared/types/types'
import { getEffectiveSiteCode } from '@/stores/distributionSite'
import type { ListingReviewStatus, ListingStatus } from '@/shared/types/status'
export interface Listing {
id: number
listing_no: string
account_id: number
owner_id: number
owner_phone?: string
owner_nickname?: string
owner_is_quality_owner?: boolean
source_channel?: string
is_external_upload?: boolean
title: string
description: string
game_name: string
server_region: string
login_platform: string
rank_level: string
haf_coin_amount: number
asset_summary?: Record<string, unknown>
screenshot_urls: string[]
cover_url: string
price_cent: number
deposit_amount_cent: number
is_accelerated_sale?: boolean
in_transaction: boolean
status: ListingStatus
review_status: ListingReviewStatus
review_reason: string
published_at?: string
distribution_site_id?: number
distribution_site_code?: string
distribution_site_name?: string
is_premium: boolean
compensation_plan: 'normal' | 'insurance' | string
insurance_fee_bps: number
listing_group_conversation_id?: number
created_at: string
updated_at: string
}
export type PublicListingItem = Listing
export type PublicListingDetail = Listing
export interface ListingPayload {
title: string
description: string
server_region: string
login_platform: string
rank_level: string
haf_coin_amount: number
asset_summary?: Record<string, unknown>
screenshot_urls: string[]
price_cent: number
deposit_amount_cent: number
is_premium: boolean
compensation_plan: 'normal' | 'insurance'
agreed_virtual_asset_sale: boolean
agreed_seller_agreement: boolean
}
export interface PublicListingQuery {
page?: number
page_size?: number
keyword?: string
sort?: string
zone?: string
server?: string
region?: string
login_method?: string
rank?: string
insurance?: string
stamina?: string
load?: string
skin_group?: string
skin_name?: string
min_coin?: number
max_coin?: number
min_ratio?: number
max_ratio?: number
min_price?: number
max_price?: number
min_deposit?: number
max_deposit?: number
min_total?: number
max_total?: number
min_fire_level?: number
max_fire_level?: number
min_daily_loss?: number
max_daily_loss?: number
min_secret_kd?: number
max_secret_kd?: number
distribution_site_code?: string
[key: `resource_${string}_min`]: number | undefined
[key: `resource_${string}_max`]: number | undefined
}
export interface PublicListingPage {
items: Listing[]
total: number
page: number
page_size: number
zone_counts: Record<string, number>
}
export async function fetchListings(query: PublicListingQuery = {}) {
const page = await fetchListingsPage(query)
return page.items
}
export async function fetchListingsPage(query: PublicListingQuery = {}) {
const siteCode = query.distribution_site_code || getEffectiveSiteCode() || undefined
const effectiveQuery = siteCode ? { distribution_site_code: siteCode, ...query } : query
const params = Object.fromEntries(
Object.entries(effectiveQuery).filter(
([, value]) => value !== '' && value !== undefined && value !== null
)
)
const { data } = await apiClient.get<ApiResponse<Partial<PublicListingPage>>>('/listings', {
params,
})
return normalizePublicListingPage(data.data, query)
}
function normalizePublicListingPage(
data: Partial<PublicListingPage>,
query: PublicListingQuery
): PublicListingPage {
const items = Array.isArray(data.items) ? data.items : []
return {
items,
total: Number.isFinite(Number(data.total)) ? Number(data.total) : items.length,
page: Number.isFinite(Number(data.page)) ? Number(data.page) : Number(query.page || 1),
page_size: Number.isFinite(Number(data.page_size))
? Number(data.page_size)
: Number(query.page_size || items.length || 20),
zone_counts: data.zone_counts && typeof data.zone_counts === 'object' ? data.zone_counts : {},
}
}
export async function fetchListing(id: string | number) {
const { data } = await apiClient.get<ApiResponse<Listing>>(`/listings/${id}`)
return data.data
}
export async function fetchSellerListings() {
const { data } = await apiClient.get<ApiResponse<{ items: Listing[] }>>('/seller/listings')
return data.data.items
}
export async function fetchSellerListing(id: string | number) {
const { data } = await apiClient.get<ApiResponse<Listing>>(`/seller/listings/${id}`)
return data.data
}
export async function createListing(payload: ListingPayload) {
const { data } = await apiClient.post<ApiResponse<Listing>>('/listings', payload)
return data.data
}
export async function updateListing(id: string | number, payload: ListingPayload) {
const { data } = await apiClient.put<ApiResponse<Listing>>(`/listings/${id}`, payload)
return data.data
}
export async function submitListingReview(id: number) {
const { data } = await apiClient.post<ApiResponse<Listing>>(`/listings/${id}/submit-review`)
return data.data
}
export async function offlineListing(id: number) {
const { data } = await apiClient.delete<ApiResponse<Listing>>(`/listings/${id}`)
return data.data
}
export async function fetchPendingReviewListings() {
const { data } = await apiClient.get<ApiResponse<{ items: Listing[] }>>('/admin/listings/pending')
return data.data.items
}
export interface AdminListingQuery {
keyword?: string
owner_id?: string
status?: ListingStatus | ''
review_status?: ListingReviewStatus | ''
compensation_plan?: 'normal' | 'insurance' | ''
zone?: 'normal' | 'premium' | ''
insurance?: string
stamina?: string
load?: string
skin_group?: string
skin_name?: string
min_coin?: number
max_coin?: number
min_deposit?: number
max_deposit?: number
limit?: number
page?: number
page_size?: number
}
export interface AdminListingPage {
items: Listing[]
total: number
page: number
page_size: number
}
export async function fetchAdminListings(query: AdminListingQuery = {}) {
const params = Object.fromEntries(
Object.entries(query).filter(
([, value]) => value !== '' && value !== undefined && value !== null
)
)
const { data } = await apiClient.get<ApiResponse<AdminListingPage>>('/admin/listings', { params })
return normalizeAdminListingPage(data.data, query)
}
function normalizeAdminListingPage(
data: Partial<AdminListingPage>,
query: AdminListingQuery
): AdminListingPage {
const items = Array.isArray(data.items) ? data.items : []
return {
items,
total: Number.isFinite(Number(data.total)) ? Number(data.total) : items.length,
page: Number.isFinite(Number(data.page)) ? Number(data.page) : Number(query.page || 1),
page_size: Number.isFinite(Number(data.page_size))
? Number(data.page_size)
: Number(query.page_size || items.length || 10),
}
}
export async function fetchAdminListing(id: string | number) {
const { data } = await apiClient.get<ApiResponse<Listing>>(`/admin/listings/${id}`)
return data.data
}
export async function adminOfflineListing(id: number, reason: string) {
const { data } = await apiClient.post<ApiResponse<Listing>>(`/admin/listings/${id}/offline`, {
reason,
})
return data.data
}
export async function adminMarkListingAbnormal(id: number, reason: string) {
const { data } = await apiClient.post<ApiResponse<Listing>>(
`/admin/listings/${id}/mark-abnormal`,
{ reason }
)
return data.data
}
export async function adminTransferListingOwner(id: number, targetUserId: number, reason: string) {
const { data } = await apiClient.post<ApiResponse<Listing>>(
`/admin/listings/${id}/transfer-owner`,
{
target_user_id: targetUserId,
reason,
}
)
return data.data
}
export async function approveListing(id: number) {
const { data } = await apiClient.post<ApiResponse<Listing>>(`/admin/listings/${id}/approve`)
return data.data
}
export interface AdminListingPriceAdjustPayload {
buyer_ratio?: number
buyer_total_price_cent?: number
reason?: string
}
export type AdminExternalListingAdjustMode = 'discount_amount' | 'sale_ratio'
export interface AdminExternalListingAdjustPayload {
haf_coin_amount: number
awm_bullets: number
level6_helmets: number
level6_armor: number
deposit_amount_cent: number
adjust_mode: AdminExternalListingAdjustMode
discount_amount_cent?: number
sale_ratio?: number
reason: string
}
export async function adjustExternalListing(
id: number,
payload: AdminExternalListingAdjustPayload
) {
const { data } = await apiClient.put<ApiResponse<Listing>>(
`/admin/listings/${id}/external-adjustment`,
payload
)
return data.data
}
export async function adjustListingReviewPrice(
id: number,
payload: AdminListingPriceAdjustPayload
) {
const { data } = await apiClient.post<ApiResponse<Listing>>(
`/admin/listings/${id}/adjust-price`,
payload
)
return data.data
}
export async function rejectListing(id: number, reason: string) {
const { data } = await apiClient.post<ApiResponse<Listing>>(`/admin/listings/${id}/reject`, {
reason,
})
return data.data
}
export interface AdminListingScreenshotsPayload {
screenshot_urls: string[]
screenshot_groups?: Record<string, unknown>
}
export async function adminUpdateListingScreenshots(
id: number,
payload: AdminListingScreenshotsPayload
) {
const { data } = await apiClient.put<ApiResponse<Listing>>(
`/admin/listings/${id}/screenshots`,
payload
)
return data.data
}