520 lines
15 KiB
TypeScript
520 lines
15 KiB
TypeScript
import { apiClient } from '@/shared/api/client'
|
|
import type { ApiResponse, PaginatedResult } from '@/shared/types/types'
|
|
import type { HandoffStatus, OrderStatus, SettlementStatus } from '@/shared/types/status'
|
|
import { yuanToCent } from '@/shared/utils/money'
|
|
|
|
export interface Order {
|
|
id: number
|
|
order_no: string
|
|
listing_id: number
|
|
listing_no: string
|
|
account_id: number
|
|
owner_id: number
|
|
renter_id: number
|
|
owner_phone?: string
|
|
renter_phone?: string
|
|
title: string
|
|
server_region: string
|
|
login_platform: string
|
|
rented_at?: string
|
|
estimated_duration_hours: number
|
|
estimated_end_at?: string
|
|
price_role?: 'renter' | 'owner' | 'admin' | string
|
|
display_amount_cent: number
|
|
rent_amount_cent?: number
|
|
owner_rent_amount_cent?: number
|
|
deposit_amount_cent: number
|
|
deposit_original_amount_cent: number
|
|
deposit_waived_amount_cent: number
|
|
deposit_free_level_quota_cent?: number
|
|
deposit_free_manual_quota_cent?: number
|
|
deposit_free_used_before_cent?: number
|
|
platform_fee_cent?: number
|
|
rent_original_amount_cent?: number
|
|
rent_discount_amount_cent?: number
|
|
pure_coin_original_amount_cent?: number
|
|
extra_item_original_amount_cent?: number
|
|
actual_coin_consumed_m?: number
|
|
actual_pure_coin_amount_cent?: number
|
|
actual_pure_coin_discount_cent?: number
|
|
actual_extra_item_amount_cent?: number
|
|
renter_growth_level?: string
|
|
renter_growth_level_name?: string
|
|
renter_discount_bps?: number
|
|
growth_points_basis_cent?: number
|
|
growth_points_per_yuan?: number
|
|
growth_points_awarded?: number
|
|
growth_points_awarded_at?: string
|
|
account_snapshot?: Record<string, unknown>
|
|
listing_snapshot?: string
|
|
checkout_info?: string
|
|
counter_info?: string
|
|
status: OrderStatus
|
|
handoff_status: HandoffStatus
|
|
handoff_mode?: string
|
|
settlement_mode?: string
|
|
managed_admin_id?: number
|
|
settlement_status: SettlementStatus
|
|
offline_settlement_status?: string
|
|
offline_settlement_amount_cent?: number
|
|
offline_settlement_remark?: string
|
|
offline_settled_by?: number
|
|
offline_settled_at?: string
|
|
refund_status?: string
|
|
refund_amount_cent?: number
|
|
deposit_hold_status?: string
|
|
deposit_hold_amount_cent?: number
|
|
deposit_hold_reason?: string
|
|
deposit_held_at?: string
|
|
deposit_hold_released_at?: string
|
|
active_dispute?: ActiveDispute
|
|
checkout?: Checkout
|
|
admin_actions?: AdminActions
|
|
payment_deadline_at?: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface AdminActions {
|
|
reset_handoff?: AdminAction
|
|
platform_handoff?: AdminAction
|
|
force_handoff?: AdminAction
|
|
platform_checkout_confirm?: AdminAction
|
|
platform_checkout_counter?: AdminAction
|
|
platform_checkout_dispute?: AdminAction
|
|
platform_offline_settlement?: AdminAction
|
|
}
|
|
|
|
export interface AdminAction {
|
|
enabled: boolean
|
|
label: string
|
|
}
|
|
|
|
export interface ActiveDispute {
|
|
id: number
|
|
initiator_id: number
|
|
initiator_type?: 'user' | 'admin' | string
|
|
initiator_admin_id?: number
|
|
type: string
|
|
status: string
|
|
}
|
|
|
|
export interface Checkout {
|
|
id: number
|
|
order_id: number
|
|
initiated_by: number
|
|
status: SettlementStatus
|
|
round_count?: number
|
|
turn?: 'owner' | 'renter' | string
|
|
proposed_by?: number
|
|
max_rounds?: number
|
|
can_counter?: boolean
|
|
can_accept?: boolean
|
|
price_role?: 'renter' | 'owner' | 'admin' | string
|
|
display_amount_cent: number
|
|
rent_amount_cent?: number
|
|
owner_rent_amount_cent?: number
|
|
platform_fee_cent?: number
|
|
deposit_amount_cent: number
|
|
pure_coin_amount_cent: number
|
|
pure_coin_discount_cent: number
|
|
pure_coin_payable_cent: number
|
|
consumable_amount_cent: number
|
|
coin_consumed_m: number
|
|
other_amount_cent: number
|
|
deposit_deduct_amount_cent: number
|
|
renter_refund_amount_cent?: number
|
|
owner_income_amount_cent?: number
|
|
shortfall_cent?: number
|
|
overshoot_amount_cent?: number
|
|
content: string
|
|
evidence_urls: string[]
|
|
owner_adjustment_reason: string
|
|
owner_adjusted_at?: string
|
|
renter_confirmed_at?: string
|
|
renter_rejected_at?: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface HandoffRecord {
|
|
id: number
|
|
order_id: number
|
|
from_user_id: number
|
|
to_user_id: number
|
|
type: string
|
|
content: string
|
|
confirmed_by_renter_at?: string
|
|
confirmed_by_owner_at?: string
|
|
created_at: string
|
|
}
|
|
|
|
export interface PaymentOrder {
|
|
id: number
|
|
payment_no: string
|
|
order_id: number
|
|
order_no: string
|
|
provider: string
|
|
third_order_id: string
|
|
provider_order_id: string
|
|
pay_way: string
|
|
jspay_flag: string
|
|
amount_cent: number
|
|
status: string
|
|
td_code?: string
|
|
jspay_url?: string
|
|
jspay_info?: string
|
|
paid: boolean
|
|
paid_at?: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface OrderAgreements {
|
|
virtual_asset_purchase: { title: string; content: string }
|
|
renter_agreement: { title: string; content: string }
|
|
}
|
|
|
|
export interface PostRentalNotice {
|
|
title: string
|
|
content: string
|
|
}
|
|
|
|
export interface SubmitCheckoutPayload {
|
|
content: string
|
|
consumableAmountYuan?: number
|
|
coin_consumed_m: number
|
|
otherAmountYuan?: number
|
|
depositDeductAmountYuan?: number
|
|
evidence_urls: string[]
|
|
reason?: string
|
|
}
|
|
|
|
export interface AdminOrderQuery {
|
|
keyword?: string
|
|
status?: string
|
|
handoff_status?: string
|
|
handoff_mode?: string
|
|
settlement_status?: string
|
|
offline_settlement_status?: 'pending' | 'settled' | ''
|
|
}
|
|
|
|
interface SubmitCheckoutRequest {
|
|
content: string
|
|
consumable_amount_cent?: number
|
|
coin_consumed_m: number
|
|
other_amount_cent?: number
|
|
deposit_deduct_amount_cent?: number
|
|
evidence_urls: string[]
|
|
reason?: string
|
|
}
|
|
|
|
export interface StartOrderPaymentRequest {
|
|
pay_way?: string
|
|
jspay_flag?: string
|
|
}
|
|
|
|
export type PaymentPayWay = 'ZFBZF' | 'WXZF'
|
|
|
|
export interface RefundStatus {
|
|
order_id: number
|
|
refund_status: string
|
|
refund_amount_cent: number
|
|
refund_no?: string
|
|
refunded_at?: string
|
|
}
|
|
|
|
function toCheckoutRequest(payload: SubmitCheckoutPayload): SubmitCheckoutRequest {
|
|
return {
|
|
content: payload.content,
|
|
consumable_amount_cent: yuanToCent(payload.consumableAmountYuan),
|
|
coin_consumed_m: payload.coin_consumed_m,
|
|
other_amount_cent: yuanToCent(payload.otherAmountYuan),
|
|
deposit_deduct_amount_cent: yuanToCent(payload.depositDeductAmountYuan),
|
|
evidence_urls: payload.evidence_urls,
|
|
reason: payload.reason,
|
|
}
|
|
}
|
|
|
|
export async function fetchOrderAgreements() {
|
|
const { data } = await apiClient.get<ApiResponse<OrderAgreements>>('/order-agreements')
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchPostRentalNotice() {
|
|
const { data } = await apiClient.get<ApiResponse<PostRentalNotice>>('/post-rental-notice')
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchOrders() {
|
|
const { data } = await apiClient.get<ApiResponse<{ items: Order[] }>>('/orders')
|
|
return Array.isArray(data.data?.items) ? data.data.items : []
|
|
}
|
|
|
|
export async function fetchOrder(id: string | number) {
|
|
const { data } = await apiClient.get<ApiResponse<Order>>(`/orders/${id}`)
|
|
return data.data
|
|
}
|
|
|
|
export async function createOrder(listingId: number) {
|
|
const { data } = await apiClient.post<ApiResponse<Order>>('/orders', { listing_id: listingId })
|
|
return data.data
|
|
}
|
|
|
|
export async function cancelOrder(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ cancelled: boolean }>>(`/orders/${id}/cancel`)
|
|
return data.data
|
|
}
|
|
|
|
export async function submitHandoff(id: number, content: string) {
|
|
const { data } = await apiClient.post<ApiResponse<HandoffRecord>>(`/orders/${id}/handoff`, {
|
|
content,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function confirmHandoff(orderId: number, type: string, content: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(
|
|
`/orders/${orderId}/handoff/${type}/confirm`,
|
|
{ content }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchHandoffRecords(id: string | number) {
|
|
const { data } = await apiClient.get<ApiResponse<{ items: HandoffRecord[] }>>(
|
|
`/orders/${id}/handoff-records`
|
|
)
|
|
return Array.isArray(data.data?.items) ? data.data.items : []
|
|
}
|
|
|
|
export async function confirmReceive(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(
|
|
`/orders/${id}/confirm-receive`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function startOrderPayment(orderId: number, req: StartOrderPaymentRequest = {}) {
|
|
const { data } = await apiClient.post<ApiResponse<PaymentOrder>>(
|
|
`/orders/${orderId}/start-payment`,
|
|
{
|
|
pay_way: req.pay_way || 'ZFBZF',
|
|
jspay_flag: req.jspay_flag || '',
|
|
}
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export function startPayment(orderId: number, payWay: string, jsPayFlag?: string) {
|
|
return startOrderPayment(orderId, { pay_way: payWay, jspay_flag: jsPayFlag || '' })
|
|
}
|
|
|
|
export async function queryOrderPayment(orderId: number) {
|
|
const { data } = await apiClient.get<ApiResponse<PaymentOrder>>(
|
|
`/orders/${orderId}/query-payment`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export function queryPayment(orderId: number) {
|
|
return queryOrderPayment(orderId)
|
|
}
|
|
|
|
export async function submitCheckout(id: number, payload: SubmitCheckoutPayload) {
|
|
const { data } = await apiClient.post<ApiResponse<Checkout>>(
|
|
`/orders/${id}/checkout`,
|
|
toCheckoutRequest(payload)
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export function createCheckout(id: number, payload: SubmitCheckoutPayload) {
|
|
return submitCheckout(id, payload)
|
|
}
|
|
|
|
export async function confirmCheckout(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(
|
|
`/orders/${id}/checkout/confirm`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function acceptCheckout(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ accepted: boolean }>>(
|
|
`/orders/${id}/checkout/accept`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function rejectCheckout(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ rejected: boolean }>>(
|
|
`/orders/${id}/checkout/reject`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function counterCheckout(id: number, payload: SubmitCheckoutPayload) {
|
|
const { data } = await apiClient.post<ApiResponse<Checkout>>(
|
|
`/orders/${id}/checkout/counter`,
|
|
toCheckoutRequest(payload)
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminOrders(page = 1, pageSize = 20, query: AdminOrderQuery = {}) {
|
|
const { data } = await apiClient.get<ApiResponse<PaginatedResult<Order>>>('/admin/orders', {
|
|
params: { page, page_size: pageSize, ...query },
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminOrder(id: string | number) {
|
|
const { data } = await apiClient.get<ApiResponse<Order>>(`/admin/orders/${id}`)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminLatestOrderByListing(listingId: string | number) {
|
|
const { data } = await apiClient.get<ApiResponse<Order>>(
|
|
`/admin/listing-orders/${listingId}/latest`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchAdminHandoffRecords(id: string | number) {
|
|
const { data } = await apiClient.get<ApiResponse<{ items: HandoffRecord[] }>>(
|
|
`/admin/orders/${id}/handoff-records`
|
|
)
|
|
return Array.isArray(data.data?.items) ? data.data.items : []
|
|
}
|
|
|
|
export async function adminCloseOrder(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<Order>>(`/admin/orders/${id}/close`, { reason })
|
|
return data.data
|
|
}
|
|
|
|
export async function adminSealOrder(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<Order>>(`/admin/orders/${id}/seal`, { reason })
|
|
return data.data
|
|
}
|
|
|
|
export async function adminMarkOrderAbnormal(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<Order>>(`/admin/orders/${id}/mark-abnormal`, {
|
|
reason,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function adminResetHandoff(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<Order>>(`/admin/orders/${id}/reset-handoff`, {
|
|
reason,
|
|
})
|
|
return data.data
|
|
}
|
|
|
|
export async function adminPlatformHandoff(id: number, content: string, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<HandoffRecord>>(
|
|
`/admin/orders/${id}/platform-handoff`,
|
|
{ content, reason }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminForceHandoff(id: number, content: string, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ received: boolean }>>(
|
|
`/admin/orders/${id}/force-handoff`,
|
|
{ content, reason }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminPlatformCheckoutConfirm(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ confirmed: boolean }>>(
|
|
`/admin/orders/${id}/platform-checkout/confirm`,
|
|
{ reason }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminPlatformCheckoutCounter(id: number, payload: SubmitCheckoutPayload) {
|
|
const { data } = await apiClient.post<ApiResponse<Checkout>>(
|
|
`/admin/orders/${id}/platform-checkout/counter`,
|
|
toCheckoutRequest(payload)
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminPlatformCheckoutDispute(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ disputed: boolean }>>(
|
|
`/admin/orders/${id}/platform-checkout/dispute`,
|
|
{ reason }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminMarkOfflineSettlement(id: number, remark: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ settled: boolean }>>(
|
|
`/admin/orders/${id}/offline-settlement`,
|
|
{ remark }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminRefundStatus(id: number) {
|
|
const { data } = await apiClient.get<ApiResponse<RefundStatus>>(
|
|
`/admin/orders/${id}/refund-status`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminRefundOrder(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<RefundStatus>>(`/admin/orders/${id}/refund`)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminApproveRefund(id: number) {
|
|
const { data } = await apiClient.post<ApiResponse<{ approved: boolean }>>(
|
|
`/admin/orders/${id}/refund/approve`
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminRejectRefund(id: number, action: 'restore' | 'close') {
|
|
const { data } = await apiClient.post<ApiResponse<{ rejected: boolean }>>(
|
|
`/admin/orders/${id}/refund/reject`,
|
|
{ action }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminHoldDeposit(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ held: boolean }>>(
|
|
`/admin/orders/${id}/deposit-hold`,
|
|
{ reason }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function adminReleaseDeposit(id: number, reason: string) {
|
|
const { data } = await apiClient.post<ApiResponse<{ released: boolean }>>(
|
|
`/admin/orders/${id}/deposit-release`,
|
|
{ reason }
|
|
)
|
|
return data.data
|
|
}
|
|
|
|
export async function fetchPendingRefundOrders() {
|
|
const { data } = await apiClient.get<ApiResponse<{ items: Order[] }>>(
|
|
'/admin/orders/refund-pending'
|
|
)
|
|
return data.data.items
|
|
}
|
|
|
|
export async function fetchPendingRefundCount() {
|
|
const { data } = await apiClient.get<ApiResponse<{ pending_refund_count: number }>>(
|
|
'/admin/orders/refund-pending-count',
|
|
{ silent: true }
|
|
)
|
|
return data.data.pending_refund_count
|
|
}
|