feat(pdd): 店铺连续同步失败 3 次后熔断,停止自动重试
背景:生产上「木塔网游专营店」Cookie 已过期,每约 24 秒重试一次、每轮刷一条 ERROR 日志,
持续数天。Cookie 失效不会自愈,继续重试只刷日志、白白占用同步周期。
行为:
- 失败 1~2 次:累计计数,继续重试;
- 第 3 次:熔断,打一条显式 ERROR(含"更新 Cookie 会自动恢复"提示);
- 熔断后:自动同步直接跳过,不再刷错误日志;
- 恢复:更新 Cookie(后台编辑或浏览器插件)自动解除;手动同步成功一次亦可。
熔断状态持久化在店铺配置里,容器重启后依然生效。
改动:
- pdd-source-config-service:阈值常量 + consecutiveFailures/syncSuspendedAt/
syncSuspendedReason 持久化字段 + 纯函数 resolvePddStoreFailureState /
applyPddStoreBreakerState / isPddStoreSyncSuspended / shouldResetPddSyncBreaker。
- pdd-sync-service:syncAllPddStores 跳过熔断店铺并上报 suspendedCount;
syncStoreOrders 失败记录、成功清零(健康店铺不产生额外配置写)。
- pdd-order-sync-job:上报熔断数,且存在熔断店铺时强制留在 INFO 级
(否则"全部店铺已停同步"会因新单为 0 被降级成 debug 而不可见)。
- 前端加「已熔断 · 停止重试」标签与类型字段。
顺带修掉一个隐患:saveAdminPddStore 收的是 Partial 载荷,normalizePddStoreItem
会为缺失字段产出 undefined,导致一次「只改店铺名」的保存就把熔断洗掉。
由 applyPddStoreBreakerState 回填既有状态解决。
验证:9 项熔断单测(含局部保存不洗状态、Cookie 变更解除熔断)。
This commit is contained in:
@@ -32,6 +32,7 @@ export async function runPddOrderSyncJob(job: JsonObject) {
|
||||
checkedCount: 0,
|
||||
okCount: 0,
|
||||
failedCount: 0,
|
||||
suspendedCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +42,13 @@ export async function runPddOrderSyncJob(job: JsonObject) {
|
||||
const totalFetched = syncResult.results.reduce((acc, r) => acc + r.fetchedCount, 0)
|
||||
const totalNewOrders = syncResult.results.reduce((acc, r) => acc + r.newOrderCount, 0)
|
||||
|
||||
const jobLog = totalNewOrders > 0 || failedCount > 0 ? logInfo : logDebug
|
||||
// 有店铺被熔断时必须留在 info 级:否则"全部店铺已停止同步"会因新单为 0 被降级成 debug 而不可见。
|
||||
const jobLog =
|
||||
totalNewOrders > 0 || failedCount > 0 || syncResult.suspendedCount > 0 ? logInfo : logDebug
|
||||
jobLog('[pdd-order-sync-job]', '拼多多定时拉单执行完成', {
|
||||
storeCount: syncResult.storeCount,
|
||||
syncedCount: syncResult.syncedCount,
|
||||
suspendedCount: syncResult.suspendedCount,
|
||||
okCount,
|
||||
failedCount,
|
||||
totalFetched,
|
||||
@@ -55,11 +59,12 @@ export async function runPddOrderSyncJob(job: JsonObject) {
|
||||
return {
|
||||
ok: failedCount === 0,
|
||||
status: failedCount > 0 ? (okCount > 0 ? 'partial_error' : 'failed') : 'ok',
|
||||
message: `已同步 ${syncResult.syncedCount} 个店铺,拉取 ${totalFetched} 单,新单 ${totalNewOrders},入库/更新 ${totalUpserted} 单${failedCount > 0 ? `,失败 ${failedCount} 个店铺` : ''}`,
|
||||
message: `已同步 ${syncResult.syncedCount} 个店铺,拉取 ${totalFetched} 单,新单 ${totalNewOrders},入库/更新 ${totalUpserted} 单${failedCount > 0 ? `,失败 ${failedCount} 个店铺` : ''}${syncResult.suspendedCount > 0 ? `,${syncResult.suspendedCount} 个店铺已熔断待更新 Cookie` : ''}`,
|
||||
accountCount: syncResult.storeCount,
|
||||
checkedCount: syncResult.syncedCount,
|
||||
okCount,
|
||||
failedCount,
|
||||
suspendedCount: syncResult.suspendedCount,
|
||||
asset: totalNewOrders,
|
||||
totalUpserted,
|
||||
totalNewOrders,
|
||||
|
||||
Reference in New Issue
Block a user