'售后退款主表', NonJingxiRefundMirrorRepository::ITEM_DATASET => '售后商品明细', NonJingxiRefundMirrorRepository::SUPPLEMENT_DATASET => '淘系退款清单', NonJingxiRefundMirrorRepository::SUPPLEMENT_ITEM_DATASET => '淘系退款明细', NonJingxiRefundMirrorRepository::STATUS_DATASET => '聚水潭当前退款状态', ]; public function __construct( private readonly NonJingxiRefundMirrorRepository $refunds, ) {} /** * @return array{ * state:string, * title:string, * message:string, * last_succeeded_at:?string, * last_attempted_at:?string, * issues:list * } */ public function current(?CarbonInterface $at = null): array { $now = $at ? Carbon::instance($at) : now(); $scheduledAt = $now->copy()->startOfDay()->setTime(6, 0); $deadline = $now->copy()->startOfDay()->setTime(10, 0); try { $states = $this->refunds->pendingConfirmationDailySyncStates(); } catch (Throwable) { return [ 'state' => 'unavailable', 'title' => '退款待确认同步状态不可用', 'message' => '暂时无法读取同步状态,请检查共享镜像连接。', 'last_succeeded_at' => null, 'last_attempted_at' => null, 'issues' => [[ 'dataset' => '', 'label' => '退款待确认', 'message' => '同步状态读取失败', 'error_code' => 'status_unavailable', ]], ]; } $issues = []; $running = false; $danger = false; $successTimes = []; $attemptTimes = []; foreach (self::DATASETS as $key => $label) { $state = $states[$key] ?? null; if (! is_array($state)) { $danger = true; $issues[] = $this->issue($key, $label, '尚未建立共享镜像', null); continue; } $status = (string) ($state['status'] ?? ''); $errorCode = trim((string) ($state['last_error_code'] ?? '')) ?: null; $attemptedAt = $this->parseTime($state['last_attempted_at'] ?? null); $succeededAt = $this->parseTime($state['last_succeeded_at'] ?? null); if ($attemptedAt !== null) { $attemptTimes[] = $attemptedAt; } if ($succeededAt !== null) { $successTimes[] = $succeededAt; } if ($status === 'failed') { $danger = true; $issues[] = $this->issue($key, $label, '同步失败,已保留上次成功数据', $errorCode); continue; } if (empty($state['initial_completed_at'])) { $danger = true; $issues[] = $this->issue($key, $label, '首次同步尚未完成', $errorCode); continue; } if ($status === 'running') { $running = true; if ($now->greaterThanOrEqualTo($deadline)) { $issues[] = $this->issue($key, $label, '今日 10:00 前同步尚未完成', $errorCode); } continue; } if ($now->greaterThanOrEqualTo($deadline) && ( $attemptedAt === null || $attemptedAt->lessThan($scheduledAt) || $succeededAt === null || $succeededAt->greaterThan($deadline) )) { $issues[] = $this->issue($key, $label, '今日 10:00 前同步未完成', $errorCode); } } if ($issues !== []) { $state = $danger ? 'failed' : 'warning'; return [ 'state' => $state, 'title' => $danger ? '退款待确认同步异常' : '退款待确认同步未完成', 'message' => $danger ? '部分退款数据同步失败,页面仍显示最近一次成功数据。' : '每日同步未在 10:00 前完成,请检查同步任务。', 'last_succeeded_at' => $this->latest($successTimes), 'last_attempted_at' => $this->latest($attemptTimes), 'issues' => $issues, ]; } return [ 'state' => $running ? 'syncing' : 'healthy', 'title' => $running ? '退款待确认正在同步' : '退款待确认同步正常', 'message' => $running ? '退款镜像正在同步,完成后会自动更新。' : '基础退款镜像每日 06:00 同步,当前退款状态每分钟刷新。', 'last_succeeded_at' => $this->latest($successTimes), 'last_attempted_at' => $this->latest($attemptTimes), 'issues' => [], ]; } /** @return array{dataset:string,label:string,message:string,error_code:?string} */ private function issue(string $dataset, string $label, string $message, ?string $errorCode): array { return [ 'dataset' => $dataset, 'label' => $label, 'message' => $message, 'error_code' => $errorCode, ]; } private function parseTime(mixed $value): ?Carbon { $value = trim((string) $value); if ($value === '') { return null; } try { return Carbon::parse($value); } catch (Throwable) { return null; } } /** @param list $times */ private function latest(array $times): ?string { if ($times === []) { return null; } usort($times, static fn (Carbon $left, Carbon $right): int => $right <=> $left); return $times[0]->toDateTimeString(); } }