若干更新迭代

This commit is contained in:
andy committed 2026-10-02 19:56:32 +08:00
1 parent deb9cc5bcf
commit 4ac786aafb
64 files changed
+3251 -291

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
export type OrganizationBalance = {
organization: { id: string; name: string };
wallet: { organizationId: string; balanceFen: number; updatedAt: string };
};
export async function fetchOrganizationBalance(organizationId: string, signal?: AbortSignal): Promise<OrganizationBalance> {
const response = await fetch("/api/billing/balance", { cache: "no-store", signal });
const payload = await response.json().catch(() => null);
if (response.status === 401 || response.status === 403 || response.status === 422) {
throw new Error("登录状态或组织信息已变更,请刷新页面后重试。");
}
if (!response.ok) throw new Error("余额暂时无法读取,请重试。");
if (typeof payload?.organization?.id === "string" && payload.organization.id !== organizationId) {
throw new Error("账号所属组织已变更,请刷新页面后重试。");
}
if (!payload || payload.organization?.id !== organizationId
|| typeof payload.organization?.name !== "string"
|| payload.wallet?.organizationId !== organizationId
|| !Number.isSafeInteger(payload.wallet?.balanceFen)
|| typeof payload.wallet?.updatedAt !== "string") {
throw new Error("余额信息不完整,请刷新后重试。");
}
return payload as OrganizationBalance;
}
+52
View File
@@ -0,0 +1,52 @@
import type { GenerationJob } from "@/lib/types";
export type ProviderErrorDiagnostic = {
status?: number;
code?: string;
requestId?: string;
detail?: string;
phase?: "submit" | "query";
kind?: string;
};
const unknownSubmission = "provider submission outcome is unknown; refusing duplicate submission";
function nonemptyString(value: unknown): string | undefined {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
export function providerErrorDiagnostic(job: GenerationJob): ProviderErrorDiagnostic | null {
const value = job.responsePayload?.providerError;
if (!value || typeof value !== "object" || Array.isArray(value)) {
const legacyCode = job.error?.code;
if (typeof legacyCode === "string" && /^[A-Za-z0-9_.:-]{1,160}$/.test(legacyCode)) return { code: legacyCode };
if (typeof legacyCode === "number" && Number.isSafeInteger(legacyCode)) return { code: String(legacyCode) };
return null;
}
const source = value as Record<string, unknown>;
const status = typeof source.status === "number" && Number.isInteger(source.status) && source.status >= 100 && source.status <= 599
? source.status : undefined;
const phase = source.phase === "submit" || source.phase === "query" ? source.phase : undefined;
const diagnostic: ProviderErrorDiagnostic = {
status,
code: nonemptyString(source.code),
requestId: nonemptyString(source.requestId),
detail: nonemptyString(source.detail),
phase,
kind: nonemptyString(source.kind)
};
return Object.values(diagnostic).some((item) => item !== undefined) ? diagnostic : null;
}
export function generationErrorMessage(job: GenerationJob): string {
const message = job.error?.message?.trim();
const providerCode = providerErrorDiagnostic(job)?.code || job.error?.code;
if (providerCode === "InputImageSensitiveContentDetected.PrivacyInformation" &&
(!message || message === unknownSubmission)) {
return "参考图片涉及真人肖像,未通过服务商审核。请更换符合服务商要求的素材后重试。";
}
if (message === unknownSubmission) {
return "暂时无法确认服务商是否已接收本次请求,为避免重复生成和扣费,平台已停止自动重试。请联系管理员核查任务状态。";
}
return message || "生成失败,服务商未提供具体原因,请联系管理员。";
}