R92 A2 修复发票 OCR provider 与二进制写库
这个提交包含在:
@@ -1,20 +1,39 @@
|
||||
const crypto = require("node:crypto");
|
||||
|
||||
function decodeMarker(imageBase64) {
|
||||
function decodeImageBase64(imageBase64) {
|
||||
try {
|
||||
return Buffer.from(imageBase64, "base64").toString("utf8");
|
||||
return Buffer.from(imageBase64, "base64");
|
||||
} catch {
|
||||
return "";
|
||||
return Buffer.alloc(0);
|
||||
}
|
||||
}
|
||||
|
||||
function buildInvoiceKey(markerText) {
|
||||
function isTextLike(value) {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sample = value.slice(0, 256);
|
||||
const controlChars = sample.match(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g) ?? [];
|
||||
return controlChars.length === 0;
|
||||
}
|
||||
|
||||
function decodeMarker(buffer) {
|
||||
const text = buffer.toString("utf8");
|
||||
if (!isTextLike(text)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return text.replace(/[\u0000-\u001F\u007F]/g, " ").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
function buildInvoiceKey(buffer, 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");
|
||||
const hash = crypto.createHash("sha256").update(buffer.length ? buffer : "mock-empty").digest("hex");
|
||||
return `hash:${hash.slice(0, 20)}`;
|
||||
}
|
||||
|
||||
@@ -40,12 +59,13 @@ function toAmount(seedHex, fallback) {
|
||||
}
|
||||
|
||||
async function runMockOcr({ ocrType, imageBase64 }) {
|
||||
const markerText = decodeMarker(imageBase64);
|
||||
const buffer = decodeImageBase64(imageBase64);
|
||||
const markerText = decodeMarker(buffer);
|
||||
if (markerText.includes("MOCK_OCR_FAIL")) {
|
||||
throw new Error("mock_ocr_failed");
|
||||
}
|
||||
|
||||
const invoiceKey = buildInvoiceKey(markerText);
|
||||
const invoiceKey = buildInvoiceKey(buffer, 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();
|
||||
@@ -62,7 +82,8 @@ async function runMockOcr({ ocrType, imageBase64 }) {
|
||||
raw: {
|
||||
provider: "mock",
|
||||
invoiceKey,
|
||||
marker: markerText.slice(0, 128),
|
||||
marker: markerText ? markerText.slice(0, 128) : null,
|
||||
sourceSha256: crypto.createHash("sha256").update(buffer).digest("hex"),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户