78 lines
3.4 KiB
PHP
78 lines
3.4 KiB
PHP
<?php
|
||
|
||
namespace App\Support;
|
||
|
||
use Illuminate\Support\Facades\Validator;
|
||
use Illuminate\Validation\ValidationException;
|
||
|
||
/** A temporary quotation may execute during several separate, inclusive periods. */
|
||
final class CourierQuotePeriods
|
||
{
|
||
public static function rules(): array
|
||
{
|
||
return [
|
||
'execution_mode' => ['nullable', 'in:permanent,temporary'],
|
||
'periods' => ['nullable', 'array', 'min:1', 'max:20'],
|
||
'periods.*.effective_date' => ['required', 'date_format:Y-m-d'],
|
||
'periods.*.end_date' => ['required', 'date_format:Y-m-d'],
|
||
];
|
||
}
|
||
|
||
public static function normalize(array $data, string $start = 'effective_date', string $end = 'end_date'): array
|
||
{
|
||
Validator::make($data, self::rules())->validate();
|
||
if (empty($data['periods']) || ($data['execution_mode'] ?? null) === 'permanent') {
|
||
Validator::make($data, [$start => ['required', 'date_format:Y-m-d'], $end => ['nullable', 'date_format:Y-m-d']])->validate();
|
||
}
|
||
$mode = $data['execution_mode'] ?? (isset($data['periods']) || filled($data[$end] ?? null) ? 'temporary' : 'permanent');
|
||
if ($mode === 'permanent') {
|
||
$data[$end] = null;
|
||
$data['execution_periods'] = [];
|
||
return $data;
|
||
}
|
||
|
||
$periods = $data['periods'] ?? [['effective_date' => $data[$start] ?? null, 'end_date' => $data[$end] ?? null]];
|
||
Validator::make(['periods' => $periods], self::rules())->validate();
|
||
usort($periods, static fn ($a, $b) => strcmp($a['effective_date'], $b['effective_date']));
|
||
$previous = null;
|
||
foreach ($periods as $index => $period) {
|
||
if ($period['end_date'] < $period['effective_date']) {
|
||
throw ValidationException::withMessages([isset($data['periods']) ? 'periods' : $end => '截止日期不能早于开始日期。']);
|
||
}
|
||
if ($previous !== null && $period['effective_date'] <= $previous) {
|
||
throw ValidationException::withMessages(['periods' => '执行区间不能重叠或重复,开始和截止日期均包含当天。']);
|
||
}
|
||
$periods[$index] = array_intersect_key($period, array_flip(['effective_date', 'end_date']));
|
||
$previous = $period['end_date'];
|
||
}
|
||
$data[$start] = $periods[0]['effective_date'];
|
||
$data[$end] = $periods[count($periods) - 1]['end_date'];
|
||
$data['execution_periods'] = $periods;
|
||
return $data;
|
||
}
|
||
|
||
public static function payload(?array $payload, array $periods): ?array
|
||
{
|
||
$payload = $payload ?? [];
|
||
unset($payload['execution_periods']);
|
||
if ($periods !== []) $payload['execution_periods'] = $periods;
|
||
return $payload ?: null;
|
||
}
|
||
|
||
public static function label(array $periods, bool $temporary = true): string
|
||
{
|
||
return implode(';', array_map(static fn ($period) => $period['effective_date']
|
||
. (filled($period['end_date'] ?? null) ? ' 至 ' . $period['end_date'] : ' 起'), $periods))
|
||
. ($temporary ? '(临时)' : '');
|
||
}
|
||
|
||
public static function overlaps(array $a, array $b): bool
|
||
{
|
||
foreach ($a as $left) foreach ($b as $right) {
|
||
if ($left['effective_date'] <= ($right['end_date'] ?? '9999-12-31')
|
||
&& $right['effective_date'] <= ($left['end_date'] ?? '9999-12-31')) return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|