chore(logging): 降噪 PDD 同步与发单消费者常态日志
将无新单/全跳过的处理完成日志降到 debug,并压缩 decisions 详情,避免 app 日志被按日切碎。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
|||||||
publishWorkOrderRealtimeChange,
|
publishWorkOrderRealtimeChange,
|
||||||
} from '../realtime/realtime-event-service.js'
|
} from '../realtime/realtime-event-service.js'
|
||||||
import { nowIso } from '../../utils/time.js'
|
import { nowIso } from '../../utils/time.js'
|
||||||
import { logInfo, logIntegration, logWarn } from '../../utils/logger.js'
|
import { logDebug, logInfo, logIntegration, logWarn } from '../../utils/logger.js'
|
||||||
import { createHttpError } from '../../utils/http.js'
|
import { createHttpError } from '../../utils/http.js'
|
||||||
import type { OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js'
|
import type { OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js'
|
||||||
|
|
||||||
@@ -211,21 +211,14 @@ export async function upsertOrderFromSource(
|
|||||||
source: `${sourceLabel}_order_upsert`,
|
source: `${sourceLabel}_order_upsert`,
|
||||||
autoOnly: true,
|
autoOnly: true,
|
||||||
})
|
})
|
||||||
logInfo('[order-consumer-router]', '来源订单消费者分流完成', {
|
logDebug('[order-consumer-router]', '来源订单消费者分流完成(履约未就绪)', {
|
||||||
source: sourceLabel,
|
source: sourceLabel,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
fulfillment: {
|
fulfillmentResult: 'blocked',
|
||||||
result: 'blocked',
|
fulfillmentReason: readiness.blockReason || readiness.reason,
|
||||||
reason: readiness.blockReason,
|
workerCreatedCount: workOrderSync.createdCount,
|
||||||
detail: readiness.reason,
|
workerSkippedCount: workOrderSync.skippedCount,
|
||||||
orderItemIds: configuredOrderItems.map((item) => item.id),
|
|
||||||
},
|
|
||||||
workerPlatform: {
|
|
||||||
createdCount: workOrderSync.createdCount,
|
|
||||||
skippedCount: workOrderSync.skippedCount,
|
|
||||||
decisions: workOrderSync.decisions,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
publishSourceOrderRealtimeChanges(order.id, workOrderSync.created)
|
publishSourceOrderRealtimeChanges(order.id, workOrderSync.created)
|
||||||
return {
|
return {
|
||||||
@@ -353,33 +346,61 @@ function logOrderConsumerOutcome({
|
|||||||
source: sourceLabel,
|
source: sourceLabel,
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
fulfillment: {
|
fulfillmentTaskCount: tasks.length,
|
||||||
configuredOrderItemIds: Array.from(configuredItemIds),
|
fulfillmentConfiguredCount: configuredItemIds.size,
|
||||||
taskIds: tasks.map((task) => task.id),
|
fulfillmentMissingCount: missingFulfillmentItems.length,
|
||||||
taskCount: tasks.length,
|
workerCreatedCount,
|
||||||
missingItems: missingFulfillmentItems,
|
workerSkippedCount,
|
||||||
},
|
workerSkipReasons: summarizeWorkerDecisionReasons(workerDecisions),
|
||||||
workerPlatform: {
|
dualConsumedCount: dualConsumedOrderItemIds.length,
|
||||||
createdCount: workerCreatedCount,
|
unhandledCount: unhandledOrderItems.length,
|
||||||
skippedCount: workerSkippedCount,
|
}
|
||||||
decisions: workerDecisions,
|
|
||||||
},
|
const hasSignal =
|
||||||
dualConsumedOrderItemIds,
|
workerCreatedCount > 0 ||
|
||||||
unhandledOrderItems,
|
tasks.length > 0 ||
|
||||||
|
missingFulfillmentItems.length > 0 ||
|
||||||
|
dualConsumedOrderItemIds.length > 0
|
||||||
|
|
||||||
|
// 常态 PDD 重拉:无履约任务、工单全跳过,仅 debug;避免 decisions 大对象撑爆日志文件。
|
||||||
|
if (hasSignal) {
|
||||||
|
logInfo('[order-consumer-router]', '来源订单消费者分流完成', detail)
|
||||||
|
} else {
|
||||||
|
logDebug('[order-consumer-router]', '来源订单消费者分流完成', detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo('[order-consumer-router]', '来源订单消费者分流完成', detail)
|
|
||||||
if (missingFulfillmentItems.length > 0) {
|
if (missingFulfillmentItems.length > 0) {
|
||||||
logWarn('[order-consumer-router]', '已配置履约的订单项存在任务缺口', detail)
|
logWarn('[order-consumer-router]', '已配置履约的订单项存在任务缺口', {
|
||||||
|
...detail,
|
||||||
|
missingItems: missingFulfillmentItems.slice(0, 5),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (dualConsumedOrderItemIds.length > 0) {
|
if (dualConsumedOrderItemIds.length > 0) {
|
||||||
logWarn('[order-consumer-router]', '同一订单项被履约与发单平台消费者同时接收', detail)
|
logWarn('[order-consumer-router]', '同一订单项被履约与发单平台消费者同时接收', {
|
||||||
|
...detail,
|
||||||
|
dualConsumedOrderItemIds: dualConsumedOrderItemIds.slice(0, 20),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
// 「未匹配接单规则」对大量 PDD 商品是常态,降到 debug;真正的缺口用上面的 missing 告警。
|
||||||
if (unhandledOrderItems.length > 0) {
|
if (unhandledOrderItems.length > 0) {
|
||||||
logWarn('[order-consumer-router]', '订单项未被履约或发单平台消费者接收', detail)
|
logDebug('[order-consumer-router]', '订单项未被履约或发单平台消费者接收', {
|
||||||
|
...detail,
|
||||||
|
unhandledSkuCodes: unhandledOrderItems
|
||||||
|
.slice(0, 10)
|
||||||
|
.map((item) => String(item.skuCode || item.skuName || item.orderItemId)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function summarizeWorkerDecisionReasons(decisions: Array<Record<string, unknown>>) {
|
||||||
|
const counts: Record<string, number> = {}
|
||||||
|
for (const decision of decisions) {
|
||||||
|
const reason = String(decision.reason || decision.result || 'unknown').trim() || 'unknown'
|
||||||
|
counts[reason] = (counts[reason] || 0) + 1
|
||||||
|
}
|
||||||
|
return Object.keys(counts).length > 0 ? JSON.stringify(counts) : ''
|
||||||
|
}
|
||||||
|
|
||||||
function resolveOrderItemRouteDiagnostic(item: OrderItemRow) {
|
function resolveOrderItemRouteDiagnostic(item: OrderItemRow) {
|
||||||
const snapshot = parseOrderItemSnapshot(item.item_snapshot_json) || {}
|
const snapshot = parseOrderItemSnapshot(item.item_snapshot_json) || {}
|
||||||
const sendCode =
|
const sendCode =
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
import { findExistingPlatformOrderIds } from '../../../repositories/order-repo.js'
|
import { findExistingPlatformOrderIds } from '../../../repositories/order-repo.js'
|
||||||
import { notifyPddCookieExpired } from '../../notification/domain-notifications.js'
|
import { notifyPddCookieExpired } from '../../notification/domain-notifications.js'
|
||||||
import { upsertOrderFromSource } from '../../order/order-service.js'
|
import { upsertOrderFromSource } from '../../order/order-service.js'
|
||||||
import { logError, logInfo, logWarn } from '../../../utils/logger.js'
|
import { logDebug, logError, logInfo, logWarn } from '../../../utils/logger.js'
|
||||||
|
|
||||||
export interface SyncStoreOrdersOptions {
|
export interface SyncStoreOrdersOptions {
|
||||||
pageNumber?: number | undefined
|
pageNumber?: number | undefined
|
||||||
@@ -44,6 +44,8 @@ export interface SyncStoreResult {
|
|||||||
total: number
|
total: number
|
||||||
fetchedCount: number
|
fetchedCount: number
|
||||||
upsertedCount: number
|
upsertedCount: number
|
||||||
|
/** 本轮判定为库内未见过的新单数量(用于日志降噪,不等于 upsert 次数)。 */
|
||||||
|
newOrderCount: number
|
||||||
ignoredCount: number
|
ignoredCount: number
|
||||||
failedCount: number
|
failedCount: number
|
||||||
pagesFetched: number
|
pagesFetched: number
|
||||||
@@ -108,6 +110,7 @@ export async function syncStoreOrders(
|
|||||||
total: 0,
|
total: 0,
|
||||||
fetchedCount: 0,
|
fetchedCount: 0,
|
||||||
upsertedCount: 0,
|
upsertedCount: 0,
|
||||||
|
newOrderCount: 0,
|
||||||
ignoredCount: 0,
|
ignoredCount: 0,
|
||||||
failedCount: 0,
|
failedCount: 0,
|
||||||
pagesFetched: 0,
|
pagesFetched: 0,
|
||||||
@@ -136,6 +139,7 @@ export async function syncStoreOrders(
|
|||||||
let totalReported = 0
|
let totalReported = 0
|
||||||
let totalFetched = 0
|
let totalFetched = 0
|
||||||
let totalUpserted = 0
|
let totalUpserted = 0
|
||||||
|
let totalNewOrders = 0
|
||||||
let totalIgnored = 0
|
let totalIgnored = 0
|
||||||
let totalFailed = 0
|
let totalFailed = 0
|
||||||
|
|
||||||
@@ -179,6 +183,7 @@ export async function syncStoreOrders(
|
|||||||
|
|
||||||
const existingCountOnPage = existingOrderSns.size
|
const existingCountOnPage = existingOrderSns.size
|
||||||
const newCountOnPage = pageOrderSns.length - existingCountOnPage
|
const newCountOnPage = pageOrderSns.length - existingCountOnPage
|
||||||
|
totalNewOrders += Math.max(0, newCountOnPage)
|
||||||
|
|
||||||
const doUpsert = options.upsertFn || upsertOrderFromSource
|
const doUpsert = options.upsertFn || upsertOrderFromSource
|
||||||
|
|
||||||
@@ -205,7 +210,9 @@ export async function syncStoreOrders(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo(
|
// 本页无新单时降到 debug,避免每 20s 一轮把日志打满。
|
||||||
|
const pageLog = newCountOnPage > 0 || totalFailed > 0 ? logInfo : logDebug
|
||||||
|
pageLog(
|
||||||
'[pdd-sync]',
|
'[pdd-sync]',
|
||||||
`店铺 ${shopName} 第 ${currentPage} 页处理完成 (本页拉取: ${items.length}, 新单: ${newCountOnPage}, 库中已有: ${existingCountOnPage})`,
|
`店铺 ${shopName} 第 ${currentPage} 页处理完成 (本页拉取: ${items.length}, 新单: ${newCountOnPage}, 库中已有: ${existingCountOnPage})`,
|
||||||
{
|
{
|
||||||
@@ -269,12 +276,15 @@ export async function syncStoreOrders(
|
|||||||
totalOrders: totalReported,
|
totalOrders: totalReported,
|
||||||
}).catch(() => null)
|
}).catch(() => null)
|
||||||
|
|
||||||
logInfo('[pdd-sync]', `店铺 ${shopName} 同步完成 (共拉取 ${pagesFetched} 页)`, {
|
// 仅「真正新单 / 失败 / 多页回填」打 info;常态触达旧单后停页的空跑降到 debug。
|
||||||
|
const storeDoneLog = totalNewOrders > 0 || totalFailed > 0 || pagesFetched > 1 ? logInfo : logDebug
|
||||||
|
storeDoneLog('[pdd-sync]', `店铺 ${shopName} 同步完成 (共拉取 ${pagesFetched} 页)`, {
|
||||||
shopId,
|
shopId,
|
||||||
total: totalReported,
|
total: totalReported,
|
||||||
pagesFetched,
|
pagesFetched,
|
||||||
fetchedCount: totalFetched,
|
fetchedCount: totalFetched,
|
||||||
upsertedCount: totalUpserted,
|
upsertedCount: totalUpserted,
|
||||||
|
newOrderCount: totalNewOrders,
|
||||||
ignoredCount: totalIgnored,
|
ignoredCount: totalIgnored,
|
||||||
failedCount: totalFailed,
|
failedCount: totalFailed,
|
||||||
})
|
})
|
||||||
@@ -285,6 +295,7 @@ export async function syncStoreOrders(
|
|||||||
total: totalReported,
|
total: totalReported,
|
||||||
fetchedCount: totalFetched,
|
fetchedCount: totalFetched,
|
||||||
upsertedCount: totalUpserted,
|
upsertedCount: totalUpserted,
|
||||||
|
newOrderCount: totalNewOrders,
|
||||||
ignoredCount: totalIgnored,
|
ignoredCount: totalIgnored,
|
||||||
failedCount: totalFailed,
|
failedCount: totalFailed,
|
||||||
pagesFetched,
|
pagesFetched,
|
||||||
@@ -319,6 +330,7 @@ export async function syncStoreOrders(
|
|||||||
total: totalReported,
|
total: totalReported,
|
||||||
fetchedCount: totalFetched,
|
fetchedCount: totalFetched,
|
||||||
upsertedCount: totalUpserted,
|
upsertedCount: totalUpserted,
|
||||||
|
newOrderCount: totalNewOrders,
|
||||||
ignoredCount: totalIgnored,
|
ignoredCount: totalIgnored,
|
||||||
failedCount: totalFailed,
|
failedCount: totalFailed,
|
||||||
pagesFetched,
|
pagesFetched,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { JsonObject } from '../../types/json.js'
|
import type { JsonObject } from '../../types/json.js'
|
||||||
import { logInfo } from '../../utils/logger.js'
|
import { logDebug, logInfo } from '../../utils/logger.js'
|
||||||
import { syncAllPddStores } from '../platforms/pdd/pdd-sync-service.js'
|
import { syncAllPddStores } from '../platforms/pdd/pdd-sync-service.js'
|
||||||
import { getPddSourcesConfig } from '../platforms/pdd/pdd-source-config-service.js'
|
import { getPddSourcesConfig } from '../platforms/pdd/pdd-source-config-service.js'
|
||||||
|
|
||||||
@@ -39,24 +39,29 @@ export async function runPddOrderSyncJob(job: JsonObject) {
|
|||||||
const failedCount = syncResult.results.filter((r) => Boolean(r.error)).length
|
const failedCount = syncResult.results.filter((r) => Boolean(r.error)).length
|
||||||
const totalUpserted = syncResult.results.reduce((acc, r) => acc + r.upsertedCount, 0)
|
const totalUpserted = syncResult.results.reduce((acc, r) => acc + r.upsertedCount, 0)
|
||||||
const totalFetched = syncResult.results.reduce((acc, r) => acc + r.fetchedCount, 0)
|
const totalFetched = syncResult.results.reduce((acc, r) => acc + r.fetchedCount, 0)
|
||||||
|
const totalNewOrders = syncResult.results.reduce((acc, r) => acc + r.newOrderCount, 0)
|
||||||
|
|
||||||
logInfo('[pdd-order-sync-job]', '拼多多定时拉单执行完成', {
|
const jobLog = totalNewOrders > 0 || failedCount > 0 ? logInfo : logDebug
|
||||||
|
jobLog('[pdd-order-sync-job]', '拼多多定时拉单执行完成', {
|
||||||
storeCount: syncResult.storeCount,
|
storeCount: syncResult.storeCount,
|
||||||
syncedCount: syncResult.syncedCount,
|
syncedCount: syncResult.syncedCount,
|
||||||
okCount,
|
okCount,
|
||||||
failedCount,
|
failedCount,
|
||||||
totalFetched,
|
totalFetched,
|
||||||
totalUpserted,
|
totalUpserted,
|
||||||
|
totalNewOrders,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ok: failedCount === 0,
|
ok: failedCount === 0,
|
||||||
status: failedCount > 0 ? (okCount > 0 ? 'partial_error' : 'failed') : 'ok',
|
status: failedCount > 0 ? (okCount > 0 ? 'partial_error' : 'failed') : 'ok',
|
||||||
message: `已同步 ${syncResult.syncedCount} 个店铺,拉取 ${totalFetched} 单,入库/更新 ${totalUpserted} 单${failedCount > 0 ? `,失败 ${failedCount} 个店铺` : ''}`,
|
message: `已同步 ${syncResult.syncedCount} 个店铺,拉取 ${totalFetched} 单,新单 ${totalNewOrders},入库/更新 ${totalUpserted} 单${failedCount > 0 ? `,失败 ${failedCount} 个店铺` : ''}`,
|
||||||
accountCount: syncResult.storeCount,
|
accountCount: syncResult.storeCount,
|
||||||
checkedCount: syncResult.syncedCount,
|
checkedCount: syncResult.syncedCount,
|
||||||
okCount,
|
okCount,
|
||||||
failedCount,
|
failedCount,
|
||||||
asset: totalUpserted,
|
asset: totalNewOrders,
|
||||||
|
totalUpserted,
|
||||||
|
totalNewOrders,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { logError, logInfo, logWarn } from '../../utils/logger.js'
|
import { logDebug, logError, logInfo, logWarn } from '../../utils/logger.js'
|
||||||
import { createHttpError } from '../../utils/http.js'
|
import { createHttpError } from '../../utils/http.js'
|
||||||
import type { JsonObject } from '../../types/json.js'
|
import type { JsonObject } from '../../types/json.js'
|
||||||
import { getScheduledJobById, getScheduledJobsConfig } from './config-service.js'
|
import { getScheduledJobById, getScheduledJobsConfig } from './config-service.js'
|
||||||
@@ -147,10 +147,15 @@ async function runJob(job: JsonObject, { manual = false }: { manual?: boolean }
|
|||||||
lastFailedCount: Number(summary.failedCount || 0),
|
lastFailedCount: Number(summary.failedCount || 0),
|
||||||
lastManual: manual,
|
lastManual: manual,
|
||||||
})
|
})
|
||||||
logInfo('[scheduler]', '定时任务执行完成', {
|
const status = String(summary.status || 'ok')
|
||||||
|
const failedCount = Number(summary.failedCount || 0)
|
||||||
|
// 成功且无失败的周期空跑降到 debug(PDD 约 20s 一轮),手动触发与失败仍保留 info。
|
||||||
|
const isQuietSuccess = !manual && status === 'ok' && failedCount <= 0 && job.type !== 'data_retention'
|
||||||
|
const doneLog = isQuietSuccess ? logDebug : logInfo
|
||||||
|
doneLog('[scheduler]', '定时任务执行完成', {
|
||||||
jobId,
|
jobId,
|
||||||
type: job.type,
|
type: job.type,
|
||||||
status: String(summary.status || 'ok'),
|
status,
|
||||||
...(job.type === 'data_retention'
|
...(job.type === 'data_retention'
|
||||||
? {
|
? {
|
||||||
rawPayloadRows: Number(summary.rawPayloadRows || 0),
|
rawPayloadRows: Number(summary.rawPayloadRows || 0),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import type { JsonObject } from '../../types/json.js'
|
|||||||
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
|
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
|
||||||
import { generateWorkOrderNo, randomId } from '../../utils/random.js'
|
import { generateWorkOrderNo, randomId } from '../../utils/random.js'
|
||||||
import { nowIso } from '../../utils/time.js'
|
import { nowIso } from '../../utils/time.js'
|
||||||
import { logInfo, logWarn } from '../../utils/logger.js'
|
import { logDebug, logInfo, logWarn } from '../../utils/logger.js'
|
||||||
import { safeParseJson } from '../admin/admin-query-utils.js'
|
import { safeParseJson } from '../admin/admin-query-utils.js'
|
||||||
import { createWorkOrderMaterialAdminNotification } from '../admin/admin-notification-service.js'
|
import { createWorkOrderMaterialAdminNotification } from '../admin/admin-notification-service.js'
|
||||||
import {
|
import {
|
||||||
@@ -272,23 +272,29 @@ export async function syncWorkerOrdersForSourceOrder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo('[worker-order-consumer]', '发单平台消费者处理完成', {
|
const summary = {
|
||||||
source: options.source || 'source_order',
|
source: options.source || 'source_order',
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
platformOrderId: order.platform_order_id,
|
platformOrderId: order.platform_order_id,
|
||||||
orderItemCount: orderItems.length,
|
orderItemCount: orderItems.length,
|
||||||
createdCount: created.length,
|
createdCount: created.length,
|
||||||
skippedCount: skipped.length,
|
skippedCount: skipped.length,
|
||||||
decisions,
|
skipReasons: formatDecisionReasonCounts(skipped),
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// 常态「全跳过」每轮 PDD upsert 都会触发,降到 debug,避免日志按天被切碎。
|
||||||
|
if (created.length > 0) {
|
||||||
|
logInfo('[worker-order-consumer]', '发单平台消费者处理完成', summary)
|
||||||
|
} else {
|
||||||
|
logDebug('[worker-order-consumer]', '发单平台消费者无新建工单', summary)
|
||||||
|
}
|
||||||
|
|
||||||
const failedDecisions = decisions.filter((decision) => decision.result === 'failed')
|
const failedDecisions = decisions.filter((decision) => decision.result === 'failed')
|
||||||
if (failedDecisions.length > 0) {
|
if (failedDecisions.length > 0) {
|
||||||
logWarn('[worker-order-consumer]', '发单平台消费者存在未成功落库的订单项', {
|
logWarn('[worker-order-consumer]', '发单平台消费者存在未成功落库的订单项', {
|
||||||
source: options.source || 'source_order',
|
...summary,
|
||||||
orderId: order.id,
|
failedCount: failedDecisions.length,
|
||||||
platformOrderId: order.platform_order_id,
|
failedReasons: formatDecisionReasonCounts(failedDecisions),
|
||||||
decisions: failedDecisions,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,3 +329,17 @@ function buildWorkerConsumerDecision(
|
|||||||
...detail,
|
...detail,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatDecisionReasonCounts(decisions: Array<Record<string, unknown>>) {
|
||||||
|
const counts = summarizeDecisionReasons(decisions)
|
||||||
|
return Object.keys(counts).length > 0 ? JSON.stringify(counts) : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeDecisionReasons(decisions: Array<Record<string, unknown>>) {
|
||||||
|
const counts: Record<string, number> = {}
|
||||||
|
for (const decision of decisions) {
|
||||||
|
const reason = String(decision.reason || decision.result || 'unknown').trim() || 'unknown'
|
||||||
|
counts[reason] = (counts[reason] || 0) + 1
|
||||||
|
}
|
||||||
|
return counts
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user