fix(merge): PR 集成 - fix merge 上传状态与易支付校验

在 #544、#545、#546 的原作者 merge commit 之后独立修复:保留上传 Promise、处理重复上传与复制失败、格式对齐,以及易支付网关、订单证据、签名、通知幂等与安全关单校验。同步重建六平台插件产物和包。

验证:前端 1832 tests、build/typecheck、lint、相关格式检查通过;财务 SQLite、支付、插件 RPC 与产物专项通过。后端全量出现一项未改动的素材删除 SQLite 表锁失败,单独复跑 3 次通过,保留全量未通过记录。浏览器与真实商户验收待完成。#543 因迁移历史与能力限制回退暂缓。
This commit is contained in:
ddCat
2026-09-20 05:18:16 +08:00
parent 9c22ea1d76
commit 3e35f7d687
20 changed files with 398 additions and 117 deletions

View File

@@ -4,12 +4,24 @@ import { analyticsFinanceColumns } from "../src/pages/admin/components/analytics
function renderFinanceCells(finance?: AnalyticsFinance | null) {
const row: AdminAnalytics["models"][number] = {
model: "example", capability: "text", tasks: 1, requests: 1, uniqueUsers: 1,
taskSuccessRate: 100, requestSuccessRate: 100, p50DurationMs: 100, p95DurationMs: 100,
inputTokens: 10, outputTokens: 10, cachedTokens: 0, usageAvailable: true,
mediaCount: 0, videoSeconds: 0, ...(finance === undefined ? {} : { finance }),
model: "example",
capability: "text",
tasks: 1,
requests: 1,
uniqueUsers: 1,
taskSuccessRate: 100,
requestSuccessRate: 100,
p50DurationMs: 100,
p95DurationMs: 100,
inputTokens: 10,
outputTokens: 10,
cachedTokens: 0,
usageAvailable: true,
mediaCount: 0,
videoSeconds: 0,
...(finance === undefined ? {} : { finance }),
};
return analyticsFinanceColumns.map((column) => "render" in column ? column.render?.(undefined, row, 0) : undefined);
return analyticsFinanceColumns.map((column) => ("render" in column ? column.render?.(undefined, row, 0) : undefined));
}
test("model finance cells tolerate missing and null backend finance", () => {
@@ -18,16 +30,13 @@ test("model finance cells tolerate missing and null backend finance", () => {
});
test("model finance cells preserve real zero values and unknown margins", () => {
expect(renderFinanceCells({ settledOrders: 1, costedOrders: 1, revenueMicrocredits: 0, costMicrocredits: 0, profitMicrocredits: 0, profitMargin: null }))
.toEqual(["0", "0", "0", "--"]);
expect(renderFinanceCells({ settledOrders: 1, costedOrders: 1, revenueMicrocredits: 0, costMicrocredits: 0, profitMicrocredits: 0, profitMargin: null })).toEqual(["0", "0", "0", "--"]);
});
test("model finance cells show partial costs without inventing profits", () => {
expect(renderFinanceCells({ settledOrders: 2, costedOrders: 1, revenueMicrocredits: 1500000, costMicrocredits: 250000, profitMicrocredits: null, profitMargin: null }))
.toEqual(["1.5", "0.25(部分)", "--", "--"]);
expect(renderFinanceCells({ settledOrders: 2, costedOrders: 1, revenueMicrocredits: 1500000, costMicrocredits: 250000, profitMicrocredits: null, profitMargin: null })).toEqual(["1.5", "0.25(部分)", "--", "--"]);
});
test("model finance cells display known losses", () => {
expect(renderFinanceCells({ settledOrders: 1, costedOrders: 1, revenueMicrocredits: 1000000, costMicrocredits: 1500000, profitMicrocredits: -500000, profitMargin: -50 }))
.toEqual(["1", "1.5", "-0.5", "-50.0%"]);
expect(renderFinanceCells({ settledOrders: 1, costedOrders: 1, revenueMicrocredits: 1000000, costMicrocredits: 1500000, profitMicrocredits: -500000, profitMargin: -50 })).toEqual(["1", "1.5", "-0.5", "-50.0%"]);
});

View File

@@ -0,0 +1,43 @@
import { expect, test } from "bun:test";
import { registerPlugin, unregisterPlugin } from "../src/lib/plugins/plugin-registry";
import type { PluginManifest } from "../src/lib/plugins/plugin-types";
test("plugin upload caller preserves the installation promise", async () => {
const page = await Bun.file(new URL("../src/pages/admin/plugins/plugins-page.tsx", import.meta.url)).text();
expect(page).toContain("onUpload={upload}");
expect(page).not.toContain("onUpload={(file) => void upload(file)}");
});
test("plugin upload guards concurrent events and exposes installation failures", async () => {
const modal = await Bun.file(new URL("../src/pages/plugins/plugin-documentation-modals.tsx", import.meta.url)).text();
expect(modal).toContain("if (uploadInFlight.current) return;");
expect(modal.indexOf("uploadInFlight.current = true")).toBeLessThan(modal.indexOf("await onUpload(file)"));
expect(modal).toContain("uploadInFlight.current = false");
expect(modal).toContain("closable={!uploading}");
expect(modal).toContain('message.error(error instanceof Error ? error.message : "安装插件失败")');
expect(modal).toContain('aria-pressed={activeTab === "install"}');
expect(modal).toContain('aria-pressed={activeTab === "guide"}');
expect(modal).toContain("if (!navigator.clipboard) throw new Error");
expect(modal).toContain("clearTimeout(copyTimer.current)");
});
test("copied guide manifest is accepted by the real plugin registry", async () => {
const guide = await Bun.file(new URL("../src/pages/plugins/plugin-development-guide.md", import.meta.url)).text();
const example = guide.match(/```json\s*([\s\S]*?)```/)?.[1].trim();
expect(example).toBeTruthy();
const manifest = { ...(JSON.parse(example!) as PluginManifest), id: "merge-test-manifest-example" };
try {
expect(() => registerPlugin({ manifest })).not.toThrow();
} finally {
unregisterPlugin(manifest.id);
}
});
test("reduced motion preserves modal completion events", async () => {
const css = await Bun.file(new URL("../src/pages/admin/theme/admin-chrome.css", import.meta.url)).text();
const reducedMotion = css.slice(css.lastIndexOf("@media (prefers-reduced-motion: reduce)"));
expect(reducedMotion).toContain("animation-duration: 0.01ms !important");
expect(reducedMotion).toContain("transition-duration: 0.01ms !important");
expect(reducedMotion).not.toContain("animation: none");
expect(reducedMotion).not.toContain("transition: none");
});