This commit is contained in:
eric
2026-04-02 07:15:42 -05:00
parent 644a6ac3a9
commit d401a36b71
62 changed files with 13264 additions and 78 deletions

29
services/api-client.ts Normal file
View File

@@ -0,0 +1,29 @@
import { z, ZodSchema } from "zod";
interface ApiClientOptions<TSchema extends ZodSchema> {
endpoint: string;
schema: TSchema;
init?: RequestInit;
}
export async function requestWithSchema<TSchema extends ZodSchema>({
endpoint,
schema,
init,
}: ApiClientOptions<TSchema>): Promise<z.infer<TSchema>> {
const res = await fetch(endpoint, {
...init,
headers: {
"Content-Type": "application/json",
...(init?.headers ?? {}),
},
next: { revalidate: 60 },
});
if (!res.ok) {
throw new Error(`API 请求失败: ${res.status} ${res.statusText}`);
}
const json = await res.json();
return schema.parse(json);
}

25
services/apps.service.ts Normal file
View File

@@ -0,0 +1,25 @@
import { appResources } from "@/lib/mock-data";
import { AppResource } from "@/lib/types";
import { appListSchema, appResourceSchema } from "@/dto/app.dto";
import { requestWithSchema } from "@/services/api-client";
const USE_REMOTE_API = false;
export async function getApps(): Promise<AppResource[]> {
if (!USE_REMOTE_API) {
return appListSchema.parse(appResources) as AppResource[];
}
return requestWithSchema({
endpoint: "/api/apps",
schema: appListSchema,
}) as Promise<AppResource[]>;
}
export async function getAppBySlug(slug: string): Promise<AppResource | null> {
if (!USE_REMOTE_API) {
const matched = appResources.find((app) => app.slug === slug);
return matched ? (appResourceSchema.parse(matched) as AppResource) : null;
}
const all = await getApps();
return all.find((app) => app.slug === slug) ?? null;
}

53
services/order.service.ts Normal file
View File

@@ -0,0 +1,53 @@
import { orderSchema } from "@/dto/order.dto";
import { requestWithSchema } from "@/services/api-client";
const USE_REMOTE_API = false;
function amountByPlan(plan: "free" | "pro" | "ultra") {
if (plan === "ultra") return 99;
if (plan === "pro") return 29;
return 0;
}
export async function createCheckoutOrder(plan: "free" | "pro" | "ultra", slug: string) {
if (!USE_REMOTE_API) {
const orderSeed = `${plan}-${slug}`.replace(/[^a-zA-Z0-9]/g, "").slice(0, 14).toUpperCase();
return orderSchema.parse({
id: `NOVA-${orderSeed || "DEMO0001"}`,
plan,
slug,
amount: amountByPlan(plan),
status: "pending",
createdAt: "2026-04-02T00:00:00.000Z",
});
}
return requestWithSchema({
endpoint: `/api/orders/create?plan=${plan}&slug=${encodeURIComponent(slug)}`,
schema: orderSchema,
init: { method: "POST" },
});
}
export async function getOrderById(input: {
id: string;
plan: "free" | "pro" | "ultra";
slug: string;
status: "pending" | "success" | "failed";
}) {
if (!USE_REMOTE_API) {
return orderSchema.parse({
id: input.id,
plan: input.plan,
slug: input.slug,
amount: amountByPlan(input.plan),
status: input.status,
createdAt: "2026-04-02T00:00:00.000Z",
});
}
return requestWithSchema({
endpoint: `/api/orders/${input.id}?status=${input.status}&plan=${input.plan}&slug=${encodeURIComponent(input.slug)}`,
schema: orderSchema,
});
}