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

这个提交包含在:
2026-05-09 11:48:57 +08:00
父节点 549bd81e30
当前提交 c26cf9b954
共修改 34 个文件,包含 1217 行新增和 184 行删除
+3 -3
查看文件
@@ -105,11 +105,11 @@ export function mapApiErrorMessage(
}
if (code === "VALID_006") {
return "OCR 重试次数已达上限,请稍后人工处理。";
return "票据识别重试次数已达上限,请稍后人工处理。";
}
if (code === "VALID_007") {
return "OCR 重试过于频繁,请稍后再试。";
return "票据识别重试过于频繁,请稍后再试。";
}
if (code === "VALID_001") {
@@ -188,7 +188,7 @@ export function mapApiErrorMessage(
}
if (details?.ocr === "disabled") {
return "当前环境未开启 OCR,请联系管理员或切换到 mock/tencent。";
return "当前环境未开启票据识别,请联系管理员处理。";
}
if (details?.ocr === "missing_required_fields") {
+81
查看文件
@@ -0,0 +1,81 @@
export type AppEvent = {
id?: string;
type: string;
invoiceId?: string;
status?: string | null;
updatedAt?: string | null;
createdAt?: string | null;
};
type SubscribeAppEventsOptions = {
accessToken: string;
onEvent: (event: AppEvent) => void;
onError?: (error: unknown) => void;
};
function parseSseMessage(message: string): AppEvent | null {
const dataLines = message
.split(/\r?\n/)
.filter((line) => line.startsWith("data:"))
.map((line) => line.slice("data:".length).trimStart());
if (!dataLines.length) {
return null;
}
try {
return JSON.parse(dataLines.join("\n")) as AppEvent;
} catch {
return null;
}
}
export function subscribeAppEvents(options: SubscribeAppEventsOptions) {
const controller = new AbortController();
void (async () => {
try {
const response = await fetch("/api/events", {
method: "GET",
credentials: "include",
headers: {
authorization: `Bearer ${options.accessToken}`,
accept: "text/event-stream",
},
signal: controller.signal,
});
if (!response.ok || !response.body) {
throw new Error(`events_stream_unavailable:${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (!controller.signal.aborted) {
const { done, value } = await reader.read();
if (done) {
break;
}
buffer += decoder.decode(value, { stream: true });
const chunks = buffer.split(/\r?\n\r?\n/);
buffer = chunks.pop() ?? "";
for (const chunk of chunks) {
const event = parseSseMessage(chunk);
if (event) {
options.onEvent(event);
}
}
}
} catch (error) {
if (!controller.signal.aborted) {
options.onError?.(error);
}
}
})();
return () => controller.abort();
}
+8
查看文件
@@ -21,6 +21,8 @@ export type InvoiceVerificationRecord = {
export type InvoicePoolListItem = {
id: string;
status: InvoicePoolStatus;
availabilityReason: string | null;
availabilityMessage: string | null;
ocrType: string;
ocrProvider: string;
ocrAttempts: number;
@@ -68,11 +70,15 @@ export type UploadInvoiceResponse = {
uploadAction?: "create" | "reuse_existing" | "blocked";
unavailableReason?: string | null;
unavailableMessage?: string | null;
availabilityReason?: string | null;
availabilityMessage?: string | null;
};
export type InvoicePoolDetail = {
id: string;
status: InvoicePoolStatus;
availabilityReason: string | null;
availabilityMessage: string | null;
archivedReason: string | null;
archivedAt: string | null;
ownerEmployeeId: string;
@@ -144,6 +150,8 @@ export type InvoiceStatusItem = {
status: InvoicePoolStatus;
reason: string;
message: string;
availabilityReason: string | null;
availabilityMessage: string | null;
invoiceCode: string | null;
invoiceNumber: string | null;
invoiceDate: string | null;