337 lines
9.4 KiB
TypeScript
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
|
|
}
|