H1-H10 优化OCR实时体验闭环

这个提交包含在:
2026-05-09 11:48:57 +08:00
父节点 549bd81e30
当前提交 c26cf9b954
共修改 34 个文件,包含 1217 行新增和 184 行删除
+42 -1
查看文件
@@ -2,12 +2,14 @@ const fs = require("node:fs/promises");
const os = require("node:os");
const path = require("node:path");
const { runOcrOnce } = require("../../../src/jobs/ocrJob");
const { runOcrForInvoiceId, runOcrOnce } = require("../../../src/jobs/ocrJob");
function buildClaimedInvoice(overrides = {}) {
return {
id: "11111111-1111-4111-8111-111111111111",
file_sha256: "sha256-1",
owner_employee_id: "employee-owner-1",
uploaded_by_employee_id: "employee-upload-1",
ocr_provider: "mock",
ocr_type: "vat",
storage_path: "20260428/invoice.txt",
@@ -91,6 +93,45 @@ describe("ocrJob quality gate", () => {
);
});
it("processes a directed invoice and emits processed metadata", async () => {
await fs.writeFile(
path.join(tempDir, "20260428/invoice.txt"),
"MOCK_DIRECTED_SUCCESS_R73",
"utf8",
);
const prisma = buildPrisma([buildClaimedInvoice()]);
const logger = {
warn: vi.fn(),
error: vi.fn(),
};
const onInvoiceProcessed = vi.fn();
const result = await runOcrForInvoiceId(
prisma,
{
storageDir: tempDir,
ocrBatchSize: 10,
ocrLockTtlSeconds: 300,
ocrMaxAttempts: 3,
ocrProvider: "mock",
},
logger,
"11111111-1111-4111-8111-111111111111",
"ocr-directed-worker",
{ onInvoiceProcessed },
);
expect(result).toEqual({ claimed: 1, available: 1, duplicate: 0, failed: 0 });
expect(onInvoiceProcessed).toHaveBeenCalledWith({
invoiceId: "11111111-1111-4111-8111-111111111111",
status: "available",
outcome: "available",
ownerEmployeeId: "employee-owner-1",
uploadedByEmployeeId: "employee-upload-1",
});
});
it("marks invoice ocr_failed when required VAT fields are missing", async () => {
await fs.writeFile(
path.join(tempDir, "20260428/invoice.txt"),
+56
查看文件
@@ -0,0 +1,56 @@
const { enqueueOcrInvoice, JOB_OCR_INVOICE } = require("../../../src/jobs/pgBossJobs");
describe("pgBossJobs enqueueOcrInvoice", () => {
it("queues a directed OCR invoice job when pg-boss is available", async () => {
const send = vi.fn(async () => ({ id: "job-1" }));
const result = await enqueueOcrInvoice(
{
config: {
jobDriver: "pgboss",
ocrProvider: "mock",
},
boss: { send },
},
"11111111-1111-4111-8111-111111111111",
);
expect(result).toEqual({ mode: "queued" });
expect(send).toHaveBeenCalledWith(JOB_OCR_INVOICE, {
invoiceId: "11111111-1111-4111-8111-111111111111",
});
});
it("falls back cleanly when pg-boss is unavailable", async () => {
await expect(
enqueueOcrInvoice(
{
config: {
jobDriver: "pgboss",
ocrProvider: "mock",
},
boss: null,
},
"11111111-1111-4111-8111-111111111111",
),
).resolves.toEqual({ mode: "fallback" });
});
it("does not queue OCR when the OCR provider is disabled", async () => {
const send = vi.fn();
const result = await enqueueOcrInvoice(
{
config: {
jobDriver: "pgboss",
ocrProvider: "off",
},
boss: { send },
},
"11111111-1111-4111-8111-111111111111",
);
expect(result).toEqual({ mode: "fallback" });
expect(send).not.toHaveBeenCalled();
});
});