Files
zyt/server/app/common/service/pharmacy/EjMedicineCatalogSyncPolicy.php
T
2026-07-22 14:50:34 +08:00

93 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace app\common\service\pharmacy;
use RuntimeException;
final class EjMedicineCatalogSyncPolicy
{
/** @return array{items:array<int,array<string,mixed>>,next_cursor:int,has_more:bool} */
public static function parsePage(array $response, int $cursor): array
{
$body = is_array($response['body'] ?? null) ? $response['body'] : [];
$httpStatus = (int) ($response['http_status'] ?? 0);
if ($httpStatus < 200 || $httpStatus >= 300 || (int) ($body['code'] ?? -1) !== 0) {
$message = trim((string) ($body['message'] ?? ''));
throw new RuntimeException($message !== '' ? $message : '洛阳药房药材目录同步失败');
}
$data = is_array($body['data'] ?? null) ? $body['data'] : [];
$items = is_array($data['items'] ?? null) ? array_values(array_filter(
$data['items'],
static fn ($item): bool => is_array($item)
)) : [];
$nextCursor = max(0, (int) ($data['next_cursor'] ?? $cursor));
$hasMore = !empty($data['has_more']);
if ($hasMore && $nextCursor <= $cursor) {
throw new RuntimeException('洛阳药房药材目录游标未推进,已停止同步');
}
return ['items' => $items, 'next_cursor' => $nextCursor, 'has_more' => $hasMore];
}
/**
* @param array<string,mixed>|null $existing
* @param array<string,mixed> $remote
* @return array{action:string,values:array<string,mixed>,deactivated:int}
*/
public static function merge(?array $existing, array $remote): array
{
$code = trim((string) ($remote['medicine_code'] ?? ''));
if ($code === '') {
throw new RuntimeException('洛阳药房药材目录包含空 medicine_code');
}
$deleted = !empty($remote['deleted']) || !empty($remote['remote_deleted']);
$values = [
'medicine_code' => $code,
'name' => trim((string) ($remote['name'] ?? '')),
'brand' => trim((string) ($remote['brand'] ?? '')),
'unit' => trim((string) ($remote['unit'] ?? '')),
'settlement_price' => self::decimal($remote['settlement_price'] ?? 0),
'retail_price' => self::decimal($remote['retail_price'] ?? 0),
'status' => $deleted ? 0 : (int) ($remote['status'] ?? 0),
'catalog_version' => max(0, (int) ($remote['catalog_version'] ?? 0)),
'remote_deleted' => $deleted ? 1 : 0,
];
if ($existing === null) {
return [
'action' => 'created',
'values' => $values,
'deactivated' => $values['status'] === 0 ? 1 : 0,
];
}
$existingComparable = [
'medicine_code' => trim((string) ($existing['medicine_code'] ?? '')),
'name' => trim((string) ($existing['name'] ?? '')),
'brand' => trim((string) ($existing['brand'] ?? '')),
'unit' => trim((string) ($existing['unit'] ?? '')),
'settlement_price' => self::decimal($existing['settlement_price'] ?? 0),
'retail_price' => self::decimal($existing['retail_price'] ?? 0),
'status' => (int) ($existing['status'] ?? 0),
'catalog_version' => max(0, (int) ($existing['catalog_version'] ?? 0)),
'remote_deleted' => (int) ($existing['remote_deleted'] ?? 0),
];
$deactivated = $existingComparable['status'] === 1 && $values['status'] === 0 ? 1 : 0;
return [
'action' => $existingComparable === $values ? 'unchanged' : 'updated',
'values' => $values,
'deactivated' => $deactivated,
];
}
private static function decimal(mixed $value): string
{
return number_format(max(0.0, (float) $value), 4, '.', '');
}
}