CaiWuXiTong/app/Business/StoreReceiptPlantingDailySynchronizer.php

166 lines
5.2 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 App\Business;
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use RuntimeException;
use Throwable;
/**
* 每日编排:先核对标签覆盖,再同步种菜报销工单,最后重算业绩明细种菜金额。
*/
class StoreReceiptPlantingDailySynchronizer
{
public const WORK_ORDER_DATASET = 'jiansun.dbo.jst_workOrderInfo';
public function __construct(
private readonly DatabaseManager $databases,
private readonly JstOrderLabelDailySynchronizer $labelCoverage,
private readonly PlantingWorkOrderSynchronizer $plantingWorkOrders,
) {}
/**
* @param list<string> $months
* @return array<string,mixed>
*/
public function sync(
string $from,
string $to,
int $companyId,
array $months = [],
bool $dryRun = false,
): array {
[$fromDate, $toDate] = $this->dateRange($from, $to);
if ($companyId <= 0) {
throw new InvalidArgumentException('公司 ID 无效。');
}
$coverage = $this->labelCoverage->assertCovered(
$fromDate->toDateString(),
$toDate->toDateString(),
$companyId,
);
$months = $this->months($months, $toDate);
$this->assertWorkOrderSourceReady();
if ($dryRun) {
return [
'status' => 'ready',
'from' => $fromDate->toDateString(),
'to' => $toDate->toDateString(),
'checked_days' => count($coverage),
'months' => $months,
'dry_run' => true,
];
}
$plantingSync = [];
foreach ($months as $month) {
$plantingSync[$month] = $this->plantingWorkOrders->sync(
$month . '-01',
$this->monthEnd($month),
$companyId,
);
}
return [
'status' => 'ready_to_refresh',
'from' => $fromDate->toDateString(),
'to' => $toDate->toDateString(),
'checked_days' => count($coverage),
'months' => $months,
'planting_sync' => $plantingSync,
'dry_run' => false,
];
}
/** @return list<string> */
private function months(array $requested, Carbon $through): array
{
if ($requested === []) {
return [
$through->copy()->subMonthNoOverflow()->format('Y-m'),
$through->format('Y-m'),
];
}
$months = [];
foreach ($requested as $month) {
$month = trim((string) $month);
$normalized = null;
if (preg_match('/^\d{4}-\d{2}$/', $month) === 1) {
try {
$normalized = Carbon::createFromFormat('!Y-m', $month, 'Asia/Shanghai')->format('Y-m');
} catch (Throwable) {
$normalized = null;
}
}
if ($normalized !== $month) {
throw new InvalidArgumentException('月份格式无效,应为 YYYY-MM:' . $month);
}
$months[$month] = true;
}
$months = array_keys($months);
sort($months);
return $months;
}
private function assertWorkOrderSourceReady(): void
{
$mirror = $this->databases->connection(
(string) config('upstream_mirrors.connection', 'business_mirror'),
);
$schema = $mirror->getSchemaBuilder();
if (! $schema->hasTable('business_mirror_table_states')) {
throw new RuntimeException('聚水潭协同工单镜像同步状态表尚未建立。');
}
$state = $mirror->table('business_mirror_table_states')
->where('dataset_key', self::WORK_ORDER_DATASET)
->first(['status', 'initial_completed_at']);
if ($state === null || $state->initial_completed_at === null) {
throw new RuntimeException('聚水潭协同工单镜像尚未完成首次同步。');
}
if ((string) $state->status !== 'succeeded') {
throw new RuntimeException('聚水潭协同工单镜像最近同步未成功。');
}
}
/** @return array{0:Carbon,1:Carbon} */
private function dateRange(string $from, string $to): array
{
$fromDate = $this->date($from);
$toDate = $this->date($to);
if ($fromDate === null || $toDate === null || $fromDate->gt($toDate)) {
throw new InvalidArgumentException('日期范围无效,应为 from <= to,格式 YYYY-MM-DD。');
}
return [$fromDate, $toDate];
}
private function date(string $value): ?Carbon
{
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) !== 1) {
return null;
}
try {
$date = Carbon::createFromFormat('!Y-m-d', $value, 'Asia/Shanghai');
} catch (Throwable) {
return null;
}
return $date->format('Y-m-d') === $value ? $date : null;
}
private function monthEnd(string $month): string
{
return Carbon::createFromFormat('!Y-m', $month, 'Asia/Shanghai')
->endOfMonth()
->toDateString();
}
}