chore(logging): 降噪 PDD 同步与发单消费者常态日志
将无新单/全跳过的处理完成日志降到 debug,并压缩 decisions 详情,避免 app 日志被按日切碎。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
||||
publishWorkOrderRealtimeChange,
|
||||
} from '../realtime/realtime-event-service.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 type { OrderItemRow, OrderRow, TaskRow } from '../../types/repository/rows.js'
|
||||
|
||||
@@ -211,21 +211,14 @@ export async function upsertOrderFromSource(
|
||||
source: `${sourceLabel}_order_upsert`,
|
||||
autoOnly: true,
|
||||
})
|
||||
logInfo('[order-consumer-router]', '来源订单消费者分流完成', {
|
||||
logDebug('[order-consumer-router]', '来源订单消费者分流完成(履约未就绪)', {
|
||||
source: sourceLabel,
|
||||
orderId: order.id,
|
||||
platformOrderId: order.platform_order_id,
|
||||
fulfillment: {
|
||||
result: 'blocked',
|
||||
reason: readiness.blockReason,
|
||||
detail: readiness.reason,
|
||||
orderItemIds: configuredOrderItems.map((item) => item.id),
|
||||
},
|
||||
workerPlatform: {
|
||||
createdCount: workOrderSync.createdCount,
|
||||
skippedCount: workOrderSync.skippedCount,
|
||||
decisions: workOrderSync.decisions,
|
||||
},
|
||||
fulfillmentResult: 'blocked',
|
||||
fulfillmentReason: readiness.blockReason || readiness.reason,
|
||||
workerCreatedCount: workOrderSync.createdCount,
|
||||
workerSkippedCount: workOrderSync.skippedCount,
|
||||
})
|
||||
publishSourceOrderRealtimeChanges(order.id, workOrderSync.created)
|
||||
return {
|
||||
@@ -353,33 +346,61 @@ function logOrderConsumerOutcome({
|
||||
source: sourceLabel,
|
||||
orderId: order.id,
|
||||
platformOrderId: order.platform_order_id,
|
||||
fulfillment: {
|
||||
configuredOrderItemIds: Array.from(configuredItemIds),
|
||||
taskIds: tasks.map((task) => task.id),
|
||||
taskCount: tasks.length,
|
||||
missingItems: missingFulfillmentItems,
|
||||
},
|
||||
workerPlatform: {
|
||||
createdCount: workerCreatedCount,
|
||||
skippedCount: workerSkippedCount,
|
||||
decisions: workerDecisions,
|
||||
},
|
||||
dualConsumedOrderItemIds,
|
||||
unhandledOrderItems,
|
||||
fulfillmentTaskCount: tasks.length,
|
||||
fulfillmentConfiguredCount: configuredItemIds.size,
|
||||
fulfillmentMissingCount: missingFulfillmentItems.length,
|
||||
workerCreatedCount,
|
||||
workerSkippedCount,
|
||||
workerSkipReasons: summarizeWorkerDecisionReasons(workerDecisions),
|
||||
dualConsumedCount: dualConsumedOrderItemIds.length,
|
||||
unhandledCount: unhandledOrderItems.length,
|
||||
}
|
||||
|
||||
const hasSignal =
|
||||
workerCreatedCount > 0 ||
|
||||
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) {
|
||||
logWarn('[order-consumer-router]', '已配置履约的订单项存在任务缺口', detail)
|
||||
logWarn('[order-consumer-router]', '已配置履约的订单项存在任务缺口', {
|
||||
...detail,
|
||||
missingItems: missingFulfillmentItems.slice(0, 5),
|
||||
})
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
const snapshot = parseOrderItemSnapshot(item.item_snapshot_json) || {}
|
||||
const sendCode =
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { findExistingPlatformOrderIds } from '../../../repositories/order-repo.js'
|
||||
import { notifyPddCookieExpired } from '../../notification/domain-notifications.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 {
|
||||
pageNumber?: number | undefined
|
||||
@@ -44,6 +44,8 @@ export interface SyncStoreResult {
|
||||
total: number
|
||||
fetchedCount: number
|
||||
upsertedCount: number
|
||||
/** 本轮判定为库内未见过的新单数量(用于日志降噪,不等于 upsert 次数)。 */
|
||||
newOrderCount: number
|
||||
ignoredCount: number
|
||||
failedCount: number
|
||||
pagesFetched: number
|
||||
@@ -108,6 +110,7 @@ export async function syncStoreOrders(
|
||||
total: 0,
|
||||
fetchedCount: 0,
|
||||
upsertedCount: 0,
|
||||
newOrderCount: 0,
|
||||
ignoredCount: 0,
|
||||
failedCount: 0,
|
||||
pagesFetched: 0,
|
||||
@@ -136,6 +139,7 @@ export async function syncStoreOrders(
|
||||
let totalReported = 0
|
||||
let totalFetched = 0
|
||||
let totalUpserted = 0
|
||||
let totalNewOrders = 0
|
||||
let totalIgnored = 0
|
||||
let totalFailed = 0
|
||||
|
||||
@@ -179,6 +183,7 @@ export async function syncStoreOrders(
|
||||
|
||||
const existingCountOnPage = existingOrderSns.size
|
||||
const newCountOnPage = pageOrderSns.length - existingCountOnPage
|
||||
totalNewOrders += Math.max(0, newCountOnPage)
|
||||
|
||||
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]',
|
||||
`店铺 ${shopName} 第 ${currentPage} 页处理完成 (本页拉取: ${items.length}, 新单: ${newCountOnPage}, 库中已有: ${existingCountOnPage})`,
|
||||
{
|
||||
@@ -269,12 +276,15 @@ export async function syncStoreOrders(
|
||||
totalOrders: totalReported,
|
||||
}).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,
|
||||
total: totalReported,
|
||||
pagesFetched,
|
||||
fetchedCount: totalFetched,
|
||||
upsertedCount: totalUpserted,
|
||||
newOrderCount: totalNewOrders,
|
||||
ignoredCount: totalIgnored,
|
||||
failedCount: totalFailed,
|
||||
})
|
||||
@@ -285,6 +295,7 @@ export async function syncStoreOrders(
|
||||
total: totalReported,
|
||||
fetchedCount: totalFetched,
|
||||
upsertedCount: totalUpserted,
|
||||
newOrderCount: totalNewOrders,
|
||||
ignoredCount: totalIgnored,
|
||||
failedCount: totalFailed,
|
||||
pagesFetched,
|
||||
@@ -319,6 +330,7 @@ export async function syncStoreOrders(
|
||||
total: totalReported,
|
||||
fetchedCount: totalFetched,
|
||||
upsertedCount: totalUpserted,
|
||||
newOrderCount: totalNewOrders,
|
||||
ignoredCount: totalIgnored,
|
||||
failedCount: totalFailed,
|
||||
pagesFetched,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
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 { 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 totalUpserted = syncResult.results.reduce((acc, r) => acc + r.upsertedCount, 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,
|
||||
syncedCount: syncResult.syncedCount,
|
||||
okCount,
|
||||
failedCount,
|
||||
totalFetched,
|
||||
totalUpserted,
|
||||
totalNewOrders,
|
||||
})
|
||||
|
||||
return {
|
||||
ok: failedCount === 0,
|
||||
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,
|
||||
checkedCount: syncResult.syncedCount,
|
||||
okCount,
|
||||
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 type { JsonObject } from '../../types/json.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),
|
||||
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,
|
||||
type: job.type,
|
||||
status: String(summary.status || 'ok'),
|
||||
status,
|
||||
...(job.type === 'data_retention'
|
||||
? {
|
||||
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 { generateWorkOrderNo, randomId } from '../../utils/random.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 { createWorkOrderMaterialAdminNotification } from '../admin/admin-notification-service.js'
|
||||
import {
|
||||
@@ -272,23 +272,29 @@ export async function syncWorkerOrdersForSourceOrder(
|
||||
}
|
||||
}
|
||||
|
||||
logInfo('[worker-order-consumer]', '发单平台消费者处理完成', {
|
||||
const summary = {
|
||||
source: options.source || 'source_order',
|
||||
orderId: order.id,
|
||||
platformOrderId: order.platform_order_id,
|
||||
orderItemCount: orderItems.length,
|
||||
createdCount: created.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')
|
||||
if (failedDecisions.length > 0) {
|
||||
logWarn('[worker-order-consumer]', '发单平台消费者存在未成功落库的订单项', {
|
||||
source: options.source || 'source_order',
|
||||
orderId: order.id,
|
||||
platformOrderId: order.platform_order_id,
|
||||
decisions: failedDecisions,
|
||||
...summary,
|
||||
failedCount: failedDecisions.length,
|
||||
failedReasons: formatDecisionReasonCounts(failedDecisions),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -323,3 +329,17 @@ function buildWorkerConsumerDecision(
|
||||
...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