feat(fulfillment): 建立正式履约影子模型
This commit is contained in:
@@ -93,11 +93,100 @@ async function loadRecentDuplicateAttempts(): Promise<CountRow[]> {
|
||||
return result.rows
|
||||
}
|
||||
|
||||
async function loadShadowModelDiffs(): Promise<CountRow[]> {
|
||||
const result = await query<{
|
||||
missing_fulfillments: number
|
||||
fulfillment_state_diffs: number
|
||||
missing_attempts: number
|
||||
missing_open_issues: number
|
||||
unlinked_events: number
|
||||
}>(
|
||||
`
|
||||
SELECT
|
||||
COUNT(*) FILTER (WHERE fulfillment.id IS NULL)::int AS missing_fulfillments,
|
||||
COUNT(*) FILTER (
|
||||
WHERE fulfillment.id IS NOT NULL
|
||||
AND ROW(
|
||||
fulfillment.route_status,
|
||||
fulfillment.channel,
|
||||
fulfillment.carrier_status,
|
||||
fulfillment.current_attempt_type,
|
||||
fulfillment.current_attempt_source_id,
|
||||
fulfillment.issue_code,
|
||||
fulfillment.issue_category
|
||||
) IS DISTINCT FROM ROW(
|
||||
CASE WHEN state.route_status = 'configured' THEN 'configured' ELSE 'unrouted' END,
|
||||
state.intended_channel,
|
||||
state.carrier_status,
|
||||
state.current_attempt_type,
|
||||
NULLIF(state.current_attempt_id, 0),
|
||||
state.issue_code,
|
||||
state.issue_category
|
||||
)
|
||||
)::int AS fulfillment_state_diffs,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM (
|
||||
SELECT task.order_item_id
|
||||
FROM fulfillment_tasks task
|
||||
JOIN fulfillments fulfillment ON fulfillment.order_item_id = task.order_item_id
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM fulfillment_attempts attempt
|
||||
WHERE attempt.attempt_type = 'task'
|
||||
AND attempt.source_id = task.id
|
||||
)
|
||||
UNION ALL
|
||||
SELECT work_order.order_item_id
|
||||
FROM work_orders work_order
|
||||
JOIN fulfillments fulfillment ON fulfillment.order_item_id = work_order.order_item_id
|
||||
WHERE work_order.order_item_id IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM fulfillment_attempts attempt
|
||||
WHERE attempt.attempt_type = 'work_order'
|
||||
AND attempt.source_id = work_order.id
|
||||
)
|
||||
) missing
|
||||
)::int AS missing_attempts,
|
||||
COUNT(*) FILTER (
|
||||
WHERE state.issue_code <> ''
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM fulfillment_issues issue
|
||||
WHERE issue.fulfillment_id = fulfillment.id
|
||||
AND issue.issue_code = state.issue_code
|
||||
AND issue.status <> 'resolved'
|
||||
)
|
||||
)::int AS missing_open_issues,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM fulfillment_state_events event
|
||||
JOIN v_fulfillment_item_state active_state
|
||||
ON active_state.order_item_id = event.order_item_id
|
||||
WHERE event.fulfillment_id IS NULL
|
||||
) AS unlinked_events
|
||||
FROM v_fulfillment_item_state state
|
||||
LEFT JOIN fulfillments fulfillment
|
||||
ON fulfillment.order_item_id = state.order_item_id
|
||||
`,
|
||||
)
|
||||
const row = result.rows[0]
|
||||
return [
|
||||
{ label: '影子履约缺失', count: Number(row?.missing_fulfillments || 0) },
|
||||
{ label: '影子履约状态差异', count: Number(row?.fulfillment_state_diffs || 0) },
|
||||
{ label: '影子执行尝试缺失', count: Number(row?.missing_attempts || 0) },
|
||||
{ label: '影子开放异常缺失', count: Number(row?.missing_open_issues || 0) },
|
||||
{ label: '履约事件未关联影子记录', count: Number(row?.unlinked_events || 0) },
|
||||
]
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const [counts, issues, duplicates] = await Promise.all([
|
||||
const [counts, issues, duplicates, shadowDiffs] = await Promise.all([
|
||||
loadCounts(),
|
||||
loadIssueBreakdown(),
|
||||
loadRecentDuplicateAttempts(),
|
||||
loadShadowModelDiffs(),
|
||||
])
|
||||
|
||||
console.info('[fulfillment-audit] 总览')
|
||||
@@ -119,13 +208,20 @@ async function main(): Promise<void> {
|
||||
console.info(` ${row.label}: ${row.count}`)
|
||||
}
|
||||
|
||||
console.info('[fulfillment-audit] 影子模型差异')
|
||||
for (const row of shadowDiffs) {
|
||||
console.info(` ${row.label}: ${row.count}`)
|
||||
}
|
||||
|
||||
const critical = new Map(counts.map((row) => [row.label, Number(row.count || 0)]))
|
||||
const duplicateCount = Number(duplicates[0]?.count || 0)
|
||||
const shadowDiffCount = shadowDiffs.reduce((sum, row) => sum + Number(row.count || 0), 0)
|
||||
const hasCritical =
|
||||
(critical.get('状态不一致') || 0) > 0 ||
|
||||
(critical.get('售后关闭仍进入履约队列') || 0) > 0 ||
|
||||
(critical.get('已分配但无有效载体') || 0) > 0 ||
|
||||
duplicateCount > 0
|
||||
duplicateCount > 0 ||
|
||||
shadowDiffCount > 0
|
||||
|
||||
if (strict && hasCritical) {
|
||||
process.exitCode = 1
|
||||
|
||||
Reference in New Issue
Block a user