fix: 免审验收容错与成本中心归一回填超时修复

- worker:等级升级计数移出验收事务,瞬时错误重试一次,最终失败转人工待办且不再返回 500
- worker:待验收阶段修改验收资料时保留 reviewMode/evidenceStatus/evidenceDueAt
- cost-center:迁移 107 新增人工待归一覆盖部分索引(生产实测候选聚合 19.6s → 391ms)
- cost-center:归一人工商品/按工单回填商品/模板预览前端超时放宽到 300s
- cost-center:批处理接口语句超时放宽到 60s(SET LOCAL,在线接口仍为 15s)
This commit is contained in:
yml2213
2026-10-29 14:33:35 +08:00
parent 814b357d46
commit 979a1bfa41
13 changed files with 617 additions and 68 deletions
@@ -39,35 +39,51 @@ import {
syncAdminWorkOrderTemplateCostVersions,
} from '../../services/cost-center/work-order-product-service.js'
import type { JsonObject } from '../../types/json.js'
import { BATCH_STATEMENT_TIMEOUT_MS, runWithStatementTimeout } from '../../db/client.js'
import { bodyOf, idOf, sessionOf } from './request-helpers.js'
import { createJsonHandler, requireAdminPermission } from './session.js'
const router = Router()
/**
* 回填/归一这类批处理要扫十几万行,单批给 60s;在线接口仍是 15s。
* 只影响本次请求的异步调用链,不改变连接池默认值。
*/
function withBatchTimeout<T>(fn: () => Promise<T>) {
return runWithStatementTimeout(BATCH_STATEMENT_TIMEOUT_MS, fn)
}
router.get(
'/cost-center/template-bootstrap/preview',
requireAdminPermission('finance.view'),
createJsonHandler((req) => previewAdminTemplateProductBootstrap(req.query as JsonObject), {
successMessage: 'ok',
errorMessage: '读取接单模板初始化预览失败',
scope: '[admin/cost-center/template-bootstrap/preview]',
}),
createJsonHandler(
(req) => withBatchTimeout(() => previewAdminTemplateProductBootstrap(req.query as JsonObject)),
{
successMessage: 'ok',
errorMessage: '读取接单模板初始化预览失败',
scope: '[admin/cost-center/template-bootstrap/preview]',
},
),
)
router.post(
'/cost-center/template-bootstrap/apply',
requireAdminPermission('finance.config'),
createJsonHandler((req) => applyAdminTemplateProductBootstrap(bodyOf(req), sessionOf(req)), {
successMessage: '接单模板初始化完成',
errorMessage: '接单模板初始化失败',
scope: '[admin/cost-center/template-bootstrap/apply]',
audit: (_req, data) => ({
action: 'cost_template_bootstrap_applied',
targetType: 'product_catalog',
targetId: '',
data: data as Record<string, unknown>,
}),
}),
createJsonHandler(
(req) =>
withBatchTimeout(() => applyAdminTemplateProductBootstrap(bodyOf(req), sessionOf(req))),
{
successMessage: '接单模板初始化完成',
errorMessage: '接单模板初始化失败',
scope: '[admin/cost-center/template-bootstrap/apply]',
audit: (_req, data) => ({
action: 'cost_template_bootstrap_applied',
targetType: 'product_catalog',
targetId: '',
data: data as Record<string, unknown>,
}),
},
),
)
router.post(
@@ -140,7 +156,7 @@ router.post(
router.post(
'/cost-center/affiliate-products/sync-snapshots',
requireAdminPermission('finance.config'),
createJsonHandler(() => syncAdminAffiliateDashCostSnapshots(), {
createJsonHandler(() => withBatchTimeout(() => syncAdminAffiliateDashCostSnapshots()), {
successMessage: '已重算自动发货预估成本快照与销售事实',
errorMessage: '重算自动发货成本快照失败',
scope: '[admin/cost-center/affiliate-products/sync-snapshots]',
@@ -156,17 +172,20 @@ router.post(
router.post(
'/cost-center/work-order-products/backfill',
requireAdminPermission('finance.config'),
createJsonHandler((req) => backfillAdminWorkOrderProductSkus(bodyOf(req)), {
successMessage: '接单平台商品已回填',
errorMessage: '回填接单平台商品失败',
scope: '[admin/cost-center/work-order-products/backfill]',
audit: (_req, data) => ({
action: 'cost_work_order_products_backfilled',
targetType: 'order_items',
targetId: '',
data: data as Record<string, unknown>,
}),
}),
createJsonHandler(
(req) => withBatchTimeout(() => backfillAdminWorkOrderProductSkus(bodyOf(req))),
{
successMessage: '接单平台商品已回填',
errorMessage: '回填接单平台商品失败',
scope: '[admin/cost-center/work-order-products/backfill]',
audit: (_req, data) => ({
action: 'cost_work_order_products_backfilled',
targetType: 'order_items',
targetId: '',
data: data as Record<string, unknown>,
}),
},
),
)
router.post(
@@ -190,11 +209,13 @@ router.post(
requireAdminPermission('finance.config'),
createJsonHandler(
(req) =>
normalizeManualProducts({
dateFrom: bodyOf(req).dateFrom as string | undefined,
dateTo: bodyOf(req).dateTo as string | undefined,
dryRun: bodyOf(req).dryRun === true,
}),
withBatchTimeout(() =>
normalizeManualProducts({
dateFrom: bodyOf(req).dateFrom as string | undefined,
dateTo: bodyOf(req).dateTo as string | undefined,
dryRun: bodyOf(req).dryRun === true,
}),
),
{
successMessage: '人工商品归一完成',
errorMessage: '归一人工商品失败',
@@ -411,7 +432,7 @@ router.delete(
router.post(
'/cost-center/mappings/backfill',
requireAdminPermission('finance.config'),
createJsonHandler((req) => backfillAdminProductMappings(bodyOf(req)), {
createJsonHandler((req) => withBatchTimeout(() => backfillAdminProductMappings(bodyOf(req))), {
successMessage: '商品映射回填完成',
errorMessage: '回填商品映射失败',
scope: '[admin/cost-center/mappings/backfill]',
@@ -427,17 +448,20 @@ router.post(
router.post(
'/cost-center/normalized-quantities/backfill',
requireAdminPermission('finance.config'),
createJsonHandler((req) => backfillAdminNormalizedQuantities(bodyOf(req)), {
successMessage: '归一数量回填完成',
errorMessage: '回填归一数量失败',
scope: '[admin/cost-center/normalized-quantities/backfill]',
audit: (_req, data) => ({
action: 'order_item_quantity_backfilled',
targetType: 'order_item',
targetId: '',
data: data as Record<string, unknown>,
}),
}),
createJsonHandler(
(req) => withBatchTimeout(() => backfillAdminNormalizedQuantities(bodyOf(req))),
{
successMessage: '归一数量回填完成',
errorMessage: '回填归一数量失败',
scope: '[admin/cost-center/normalized-quantities/backfill]',
audit: (_req, data) => ({
action: 'order_item_quantity_backfilled',
targetType: 'order_item',
targetId: '',
data: data as Record<string, unknown>,
}),
},
),
)
export default router