179 lines
6.2 KiB
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();
|
|
}
|
|
}
|