154 lines
4.1 KiB
TypeScript
154 lines
4.1 KiB
TypeScript
import { allow, deny } from "@/lib/decision"
|
|
import { isApiError, toApiError, type ApiDecision } from "@/lib/errors"
|
|
import type { Order, OrderStatus } from "@/lib/types"
|
|
|
|
import { httpJson } from "./http"
|
|
|
|
type Paginated<T> = {
|
|
items: T[]
|
|
meta: {
|
|
total: number
|
|
offset: number
|
|
limit: number
|
|
}
|
|
}
|
|
|
|
export type ListOrdersOptions = {
|
|
role?: "consumer" | "player" | "owner"
|
|
status?: OrderStatus
|
|
offset?: number
|
|
limit?: number
|
|
}
|
|
|
|
type ActionResult = { decision: ApiDecision; order?: Order }
|
|
|
|
function withOffsetLimit(path: string, options?: ListOrdersOptions): string {
|
|
const offset = options?.offset ?? 0
|
|
const limit = options?.limit ?? 100
|
|
|
|
const searchParams = new URLSearchParams({
|
|
offset: String(offset),
|
|
limit: String(limit),
|
|
})
|
|
|
|
if (options?.role) searchParams.set("role", options.role)
|
|
if (options?.status) searchParams.set("status", options.status)
|
|
|
|
return `${path}?${searchParams.toString()}`
|
|
}
|
|
|
|
function unwrapOrder(value: unknown): Order | undefined {
|
|
if (typeof value !== "object" || value === null) return undefined
|
|
if ("order" in value) {
|
|
const envelope = value as { order?: Order }
|
|
return envelope.order
|
|
}
|
|
return value as Order
|
|
}
|
|
|
|
function denyFromError(error: unknown): ApiDecision {
|
|
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
|
return deny(401, "请先登录")
|
|
}
|
|
|
|
const apiError = toApiError(error)
|
|
return deny(apiError.code, apiError.msg)
|
|
}
|
|
|
|
export async function listOrders(options?: ListOrdersOptions): Promise<Order[]> {
|
|
const res = await httpJson<Paginated<Order>>(withOffsetLimit("/api/v1/orders", options), {
|
|
cache: "no-store",
|
|
})
|
|
return res.items
|
|
}
|
|
|
|
export async function getOrderById(orderId: string): Promise<Order | undefined> {
|
|
try {
|
|
const res = await httpJson<unknown>(`/api/v1/orders/${encodeURIComponent(orderId)}`, {
|
|
cache: "no-store",
|
|
})
|
|
return unwrapOrder(res)
|
|
} catch (error) {
|
|
if (error instanceof Error && error.message === "UNAUTHORIZED") {
|
|
throw error
|
|
}
|
|
if (isApiError(error) && error.code === 404) {
|
|
return undefined
|
|
}
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export async function listOrdersByConsumer(consumerId: string): Promise<Order[]> {
|
|
const items = await listOrders({ role: "consumer" })
|
|
return items.filter((order) => order.consumerId === consumerId)
|
|
}
|
|
|
|
interface CreatePaidOrderInput {
|
|
playerId: string
|
|
serviceId: string
|
|
shopId?: string
|
|
quantity: number
|
|
note?: string
|
|
}
|
|
|
|
export async function createPaidOrder(input: CreatePaidOrderInput): Promise<ActionResult> {
|
|
try {
|
|
const res = await httpJson<unknown>("/api/v1/orders/paid", {
|
|
method: "POST",
|
|
cache: "no-store",
|
|
json: input,
|
|
})
|
|
const order = unwrapOrder(res)
|
|
if (!order) {
|
|
return { decision: deny(500, "订单创建失败") }
|
|
}
|
|
return { decision: allow(), order }
|
|
} catch (error) {
|
|
return { decision: denyFromError(error) }
|
|
}
|
|
}
|
|
|
|
async function postOrderAction(orderId: string, action: string): Promise<ActionResult> {
|
|
try {
|
|
const res = await httpJson<unknown>(`/api/v1/orders/${encodeURIComponent(orderId)}/${action}`, {
|
|
method: "POST",
|
|
cache: "no-store",
|
|
})
|
|
|
|
const order = unwrapOrder(res)
|
|
if (order) {
|
|
return { decision: allow(), order }
|
|
}
|
|
|
|
const refetched = await getOrderById(orderId).catch(() => undefined)
|
|
if (refetched) {
|
|
return { decision: allow(), order: refetched }
|
|
}
|
|
|
|
return { decision: allow() }
|
|
} catch (error) {
|
|
return { decision: denyFromError(error) }
|
|
}
|
|
}
|
|
|
|
export async function payOrder(orderId: string): Promise<ActionResult> {
|
|
return postOrderAction(orderId, "pay")
|
|
}
|
|
|
|
export async function acceptOrder(orderId: string): Promise<ActionResult> {
|
|
return postOrderAction(orderId, "accept")
|
|
}
|
|
|
|
export async function requestClose(orderId: string): Promise<ActionResult> {
|
|
return postOrderAction(orderId, "request-close")
|
|
}
|
|
|
|
export async function confirmClose(orderId: string): Promise<ActionResult> {
|
|
return postOrderAction(orderId, "confirm-close")
|
|
}
|
|
|
|
export async function cancelPreAccept(orderId: string): Promise<ActionResult> {
|
|
return postOrderAction(orderId, "cancel")
|
|
}
|