feat(finance): 人工成本单事务/幂等 + 配置自动匹配 + 缺成本监控
- 确认履约改为单事务:锁订单项 → 复查快照 → 扣积分/备用金 → 写成本快照; 快照失败整体回滚,不再出现「钱扣了、成本没记」 - manual_case 的 subject_id 统一为 order_item_id,新增订单项级唯一索引(迁移 111), 消除 manual_expenses.id / platform_credit_entries.id 跨表撞车 - 人工 SKU 匹配增加 sku_code / sku_name 兜底;保存配置立即回填最近 30 天, 定时任务按窗口 rematch,确认时再按 SKU 兜底匹配 - 新增 manual_cost_missing 异常队列:从首次人工配置起算,确认履约立即收口 - 补单元测试与真实 PostgreSQL 集成测试(并发、重复、强制快照失败回滚、双资金链路) - 校准财务文档并新增上线初始化/验收手册(历史人工不补录,只保证配置后的新单) 验证:backend typecheck/lint/format/check:sql/test 843(839 pass / 4 skip); frontend typecheck/lint/test 172;迁移 111 在 scratch 库全量执行 + 重跑幂等。
This commit is contained in:
@@ -152,7 +152,7 @@ function normalizeCostFinanceSyncJob(rawValue: JsonObject) {
|
||||
enabled: rawValue.enabled !== false,
|
||||
intervalSeconds: normalizeRangeInteger(rawValue.intervalSeconds, 300, 60, 3600),
|
||||
config: {
|
||||
days: normalizeRangeInteger(config.days, 7, 1, 90),
|
||||
days: normalizeRangeInteger(config.days, 30, 1, 90),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ function createDefaultCostFinanceSyncJob() {
|
||||
enabled: true,
|
||||
intervalSeconds: 300,
|
||||
config: {
|
||||
days: 7,
|
||||
days: 30,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
syncEstimatedOrderItemCostSnapshots,
|
||||
} from '../cost-center/cost-snapshot-sync-service.js'
|
||||
import { normalizeManualProducts } from '../cost-center/manual-product-normalization-service.js'
|
||||
import { rematchManualFulfillments } from '../cost-center/manual-fulfillment-config-service.js'
|
||||
import { rebuildSalesDailyFacts } from '../finance/sales-facts-service.js'
|
||||
import { reconcilePendingFulfillmentModes } from '../fulfillment/fulfillment-mode-service.js'
|
||||
|
||||
@@ -26,6 +27,13 @@ export async function runCostFinanceSyncJob(job: JsonObject = {}) {
|
||||
dateTo: today(),
|
||||
resolveExceptions: false,
|
||||
})
|
||||
// 人工 SKU 配置可能在订单之后才补:每次同步都按配置回填未匹配订单项,
|
||||
// 保证「配置好人工之后的新单」自动挂上渠道,不依赖管理员手动点「重新匹配」。
|
||||
const manualMatches = await rematchManualFulfillments({
|
||||
onlyUnmatched: true,
|
||||
dateFrom: shiftDays(-(days - 1)),
|
||||
dateTo: today(),
|
||||
})
|
||||
|
||||
const estimated = await syncEstimatedOrderItemCostSnapshots({
|
||||
dateFrom: shiftDays(-(days - 1)),
|
||||
@@ -45,6 +53,7 @@ export async function runCostFinanceSyncJob(job: JsonObject = {}) {
|
||||
message:
|
||||
`履约方式 ${fulfillment.updatedCount} 项 / ` +
|
||||
`人工商品 ${manualProducts.updatedItemCount} 项 / ` +
|
||||
`人工渠道匹配 ${manualMatches.updatedCount} 项 / ` +
|
||||
`成本快照 ${estimated.createdCount} 商品预估 / ` +
|
||||
`${actual.directRewardCount + actual.upgradedCount} 工单实际 / ` +
|
||||
`${actual.estimatedRewardCount} 工单在途,销售事实 ${facts.insertedCount} 行`,
|
||||
@@ -53,6 +62,7 @@ export async function runCostFinanceSyncJob(job: JsonObject = {}) {
|
||||
fulfillmentModeRemaining: fulfillment.remainingCount,
|
||||
manualItemsNormalized: manualProducts.updatedItemCount,
|
||||
manualProductsCreated: manualProducts.createdProductCount,
|
||||
manualItemsMatched: manualMatches.updatedCount,
|
||||
estimatedCreated: estimated.createdCount,
|
||||
actualCreated: actual.directRewardCount + actual.upgradedCount,
|
||||
estimatedRewardCreated: actual.estimatedRewardCount,
|
||||
|
||||
Reference in New Issue
Block a user