CaiWuXiTong/modules/EcommerceFinance/Services/RefundPendingSyncStatus.php
renshen 23f2e9943c feat: 增加退款每日同步和驾驶舱预警
退款待确认改为每日06:00专属同步,异常状态显示到财务驾驶舱。
2026-11-04 17:46:57 +08:00

178 lines
6.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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 => '淘系退款明细',
];
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 自动同步,10: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();
}
}