H1-H10 优化OCR实时体验闭环
这个提交包含在:
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env node
|
||||
import process from "node:process";
|
||||
import { PrismaClient } from "../../server/node_modules/@prisma/client/index.js";
|
||||
|
||||
function parseThresholds() {
|
||||
const arg = process.argv.find((item) => item.startsWith("--thresholds="));
|
||||
const raw = arg ? arg.slice("--thresholds=".length) : "2,5,10";
|
||||
return raw
|
||||
.split(",")
|
||||
.map((item) => Number(item.trim()))
|
||||
.filter((item) => Number.isFinite(item) && item > 0)
|
||||
.sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
function minutesAgo(minutes) {
|
||||
return new Date(Date.now() - minutes * 60_000);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (!process.env.DATABASE_URL) {
|
||||
console.log("[pending-ocr][SKIP] missing DATABASE_URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const thresholds = parseThresholds();
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
try {
|
||||
const summary = [];
|
||||
for (const minutes of thresholds) {
|
||||
const where = {
|
||||
status: "pending_ocr",
|
||||
created_at: { lte: minutesAgo(minutes) },
|
||||
};
|
||||
const count = await prisma.invoicePool.count({ where });
|
||||
const rows = await prisma.invoicePool.findMany({
|
||||
where,
|
||||
orderBy: { created_at: "asc" },
|
||||
take: 50,
|
||||
select: {
|
||||
id: true,
|
||||
created_at: true,
|
||||
updated_at: true,
|
||||
ocr_attempts: true,
|
||||
ocr_last_requested_at: true,
|
||||
ocr_last_error: true,
|
||||
ocr_locked_at: true,
|
||||
ocr_lock_owner: true,
|
||||
},
|
||||
});
|
||||
|
||||
summary.push({
|
||||
thresholdMinutes: minutes,
|
||||
count,
|
||||
sampleLimit: 50,
|
||||
items: rows.map((item) => ({
|
||||
id: item.id,
|
||||
createdAt: item.created_at?.toISOString?.() ?? null,
|
||||
updatedAt: item.updated_at?.toISOString?.() ?? null,
|
||||
attempts: item.ocr_attempts ?? 0,
|
||||
lastRequestedAt: item.ocr_last_requested_at?.toISOString?.() ?? null,
|
||||
lastError: item.ocr_last_error ?? null,
|
||||
lockedAt: item.ocr_locked_at?.toISOString?.() ?? null,
|
||||
lockOwner: item.ocr_lock_owner ?? null,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
console.log(JSON.stringify({ ok: true, thresholds: summary }, null, 2));
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(`[pending-ocr][FAIL] ${error instanceof Error ? error.message : String(error)}`);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -132,7 +132,7 @@ function scanNativeFileInputs(file, source) {
|
||||
|
||||
function scanMobileCapturePdfInputs(file, source) {
|
||||
const normalized = toPosix(path.relative(rootDir, file));
|
||||
if (normalized !== "client/src/pages/MobileCapturePage.tsx") {
|
||||
if (!/client\/src\/pages\/Mobile.*\.tsx$/.test(normalized)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ curl -fsS "$PUBLIC_BASE_URL/ready" >/dev/null
|
||||
echo ">>> [live-readiness] 公网资源文案扫描"
|
||||
node "$ROOT_DIR/scripts/qa/check-public-bundle-copy.mjs" "$PUBLIC_BASE_URL" | tee "$REPORT_DIR/public-bundle-copy.txt"
|
||||
|
||||
echo ">>> [live-readiness] pending OCR 扫描"
|
||||
node "$ROOT_DIR/scripts/qa/check-pending-ocr.mjs" --thresholds=2,5,10 | tee "$REPORT_DIR/pending-ocr.txt"
|
||||
|
||||
if has_live_smoke_credentials; then
|
||||
echo ">>> [live-readiness] 执行公网 live smoke"
|
||||
LIVE_SMOKE_BASE_URL="$PUBLIC_BASE_URL" bash "$ROOT_DIR/scripts/qa/run-web-gates.sh" smoke-live | tee "$REPORT_DIR/live-smoke.txt"
|
||||
|
||||
在新工单中引用
屏蔽一个用户