- workflow: 任务详情/转审候选/列表分页字段(D5) - reimbursements: 列表API + pay凭证字段/幂等增强(D7) - invoice-pool: 列表/详情API + 上传pending_ocr(D9) - ocr/audit: mock provider + 回归脚本 + regression-all开关(D2/D3) - CI: 新增 ci-minimal / ci-regression 工作流(D4)
61 行
1.6 KiB
JavaScript
61 行
1.6 KiB
JavaScript
const crypto = require("node:crypto");
|
|
|
|
function decodeMarker(imageBase64) {
|
|
try {
|
|
return Buffer.from(imageBase64, "base64").toString("utf8");
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function buildInvoiceKey(markerText) {
|
|
const duplicateMatch = markerText.match(/MOCK_DUP:([A-Za-z0-9_-]+)/);
|
|
if (duplicateMatch?.[1]) {
|
|
return `dup:${duplicateMatch[1]}`;
|
|
}
|
|
|
|
const hash = crypto.createHash("sha256").update(markerText || "mock-empty").digest("hex");
|
|
return `hash:${hash.slice(0, 20)}`;
|
|
}
|
|
|
|
function toAmount(seedHex, fallback) {
|
|
const value = Number.parseInt(seedHex.slice(0, 8), 16);
|
|
if (!Number.isFinite(value)) {
|
|
return fallback;
|
|
}
|
|
|
|
return ((value % 50000) / 100 + fallback).toFixed(2);
|
|
}
|
|
|
|
async function runMockOcr({ ocrType, imageBase64 }) {
|
|
const markerText = decodeMarker(imageBase64);
|
|
if (markerText.includes("MOCK_OCR_FAIL")) {
|
|
throw new Error("mock_ocr_failed");
|
|
}
|
|
|
|
const invoiceKey = buildInvoiceKey(markerText);
|
|
const hash = crypto.createHash("sha256").update(invoiceKey).digest("hex");
|
|
const invoiceCode = `MOCK${hash.slice(0, 8).toUpperCase()}`;
|
|
const invoiceNumber = hash.slice(8, 16).toUpperCase();
|
|
|
|
return {
|
|
invoiceCode,
|
|
invoiceNumber,
|
|
invoiceDate: "2026-02-08",
|
|
amount: toAmount(hash.slice(16, 24), 100),
|
|
taxAmount: toAmount(hash.slice(24, 32), 10),
|
|
totalAmount: toAmount(hash.slice(32, 40), 110),
|
|
sellerName: `Mock Seller (${ocrType})`,
|
|
buyerName: "Mock Buyer",
|
|
raw: {
|
|
provider: "mock",
|
|
invoiceKey,
|
|
marker: markerText.slice(0, 128),
|
|
},
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
runMockOcr,
|
|
};
|