CaiWuXiTong/modules/EcommerceFinance/Services/RefundPendingSyncStatus.php

179 lines
6.2 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
use Carbon\CarbonInterface;
use Illuminate\Support\Carbon;
use Modules\EcommerceFinance\Repositories\NonJingxiRefundMirrorRepository;
use Throwable;
class RefundPendingSyncStatus
{
private const DATASETS = [
NonJingxiRefundMirrorRepository::INFO_DATASET => '售后退款主表',
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<array{dataset:string,label:string,message:string,error_code:?string}>
* }
*/
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<Carbon> $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();
}
}