152 lines
5.3 KiB
PHP
152 lines
5.3 KiB
PHP
<?php
|
||
|
||
namespace App\Business;
|
||
|
||
use App\Models\Business\CourierSurchargeAuditLog;
|
||
use Illuminate\Database\QueryException;
|
||
use Illuminate\Support\Collection;
|
||
use Illuminate\Support\Facades\Schema;
|
||
|
||
/**
|
||
* 快递附加费维护审计日志的固定入口。
|
||
*
|
||
* 日志只保存当前公司、操作人和报价/类型的安全字段快照,不保存密码、
|
||
* Token、连接串或原始请求内容。
|
||
*/
|
||
class CourierSurchargeAuditLogRepository
|
||
{
|
||
/** @var array<string,string> */
|
||
private const FIELD_LABELS = [
|
||
'courier' => '快递名称',
|
||
'scope' => '快递归属',
|
||
'reconciler' => '对账人',
|
||
'supplier' => '供应商',
|
||
'types' => '附加费类型',
|
||
'mode' => '结算方式',
|
||
'standard' => '计价标准',
|
||
'condition' => '生效条件',
|
||
'effective_at' => '生效日期',
|
||
'remark' => '备注',
|
||
'status' => '状态',
|
||
'name' => '类型名称',
|
||
];
|
||
|
||
/** @var array<string,string> */
|
||
private const ACTION_LABELS = [
|
||
CourierSurchargeAuditLog::ACTION_CREATED => '新增',
|
||
CourierSurchargeAuditLog::ACTION_UPDATED => '更改',
|
||
CourierSurchargeAuditLog::ACTION_ACTIVATED => '启用',
|
||
CourierSurchargeAuditLog::ACTION_DEACTIVATED => '停用',
|
||
];
|
||
|
||
/** @var array<string,string> */
|
||
private const TARGET_LABELS = [
|
||
CourierSurchargeAuditLog::TARGET_QUOTE => '报价规则',
|
||
CourierSurchargeAuditLog::TARGET_TYPE => '附加费类型',
|
||
];
|
||
|
||
public function ready(): bool
|
||
{
|
||
try {
|
||
return Schema::hasTable((new CourierSurchargeAuditLog())->getTable());
|
||
} catch (QueryException) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/** @param array<string,mixed> $changes */
|
||
public function record(
|
||
string $action,
|
||
string $targetType,
|
||
?int $targetId,
|
||
string $targetLabel,
|
||
array $changes,
|
||
int $actorId,
|
||
): CourierSurchargeAuditLog {
|
||
abort_unless($this->ready(), 503, '附加费操作日志数据表尚未完成迁移。');
|
||
abort_unless(array_key_exists($action, self::ACTION_LABELS), 422, '附加费日志动作不合法。');
|
||
abort_unless(array_key_exists($targetType, self::TARGET_LABELS), 422, '附加费日志对象不合法。');
|
||
|
||
return CourierSurchargeAuditLog::query()->create([
|
||
'company_id' => company_id(),
|
||
'actor_id' => $actorId ?: null,
|
||
'action' => $action,
|
||
'target_type' => $targetType,
|
||
'target_id' => $targetId,
|
||
'target_label' => mb_substr(trim($targetLabel), 0, 191),
|
||
'changes' => $changes,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @return Collection<int,array<string,mixed>>
|
||
*/
|
||
public function recent(int $limit = 50): Collection
|
||
{
|
||
if (! $this->ready()) {
|
||
return collect();
|
||
}
|
||
|
||
try {
|
||
return CourierSurchargeAuditLog::query()
|
||
->with('actor')
|
||
->orderByDesc('id')
|
||
->limit(max(1, min($limit, 200)))
|
||
->get()
|
||
->map(fn (CourierSurchargeAuditLog $log): array => [
|
||
'id' => $log->id,
|
||
'occurred_at' => optional($log->created_at)->format('Y-m-d H:i:s') ?: '',
|
||
'actor_name' => $log->actor?->name ?: '系统',
|
||
'action' => $log->action,
|
||
'action_label' => self::ACTION_LABELS[$log->action] ?? $log->action,
|
||
'target_type' => $log->target_type,
|
||
'target_id' => $log->target_id,
|
||
'target_type_label' => self::TARGET_LABELS[$log->target_type] ?? $log->target_type,
|
||
'target_label' => $log->target_label,
|
||
'changes_label' => $this->changesLabel($log),
|
||
])
|
||
->values();
|
||
} catch (QueryException) {
|
||
return collect();
|
||
}
|
||
}
|
||
|
||
private function changesLabel(CourierSurchargeAuditLog $log): string
|
||
{
|
||
$changes = (array) $log->changes;
|
||
if ($log->action === CourierSurchargeAuditLog::ACTION_CREATED) {
|
||
return '新增记录';
|
||
}
|
||
|
||
if (in_array($log->action, [CourierSurchargeAuditLog::ACTION_ACTIVATED, CourierSurchargeAuditLog::ACTION_DEACTIVATED], true)) {
|
||
$before = data_get($changes, 'before.status');
|
||
$after = data_get($changes, 'after.status');
|
||
return sprintf('状态:%s → %s', $before ?: '—', $after ?: '—');
|
||
}
|
||
|
||
$before = (array) ($changes['before'] ?? []);
|
||
$after = (array) ($changes['after'] ?? []);
|
||
$parts = [];
|
||
foreach (array_unique(array_merge(array_keys($before), array_keys($after))) as $field) {
|
||
$old = $this->valueLabel($before[$field] ?? null);
|
||
$new = $this->valueLabel($after[$field] ?? null);
|
||
if ($old !== $new) {
|
||
$parts[] = sprintf('%s:%s → %s', self::FIELD_LABELS[$field] ?? $field, $old, $new);
|
||
}
|
||
}
|
||
|
||
return $parts === [] ? '未改变字段' : implode(';', $parts);
|
||
}
|
||
|
||
private function valueLabel(mixed $value): string
|
||
{
|
||
if (is_array($value)) {
|
||
return implode('、', array_map(static fn (mixed $item): string => (string) $item, $value));
|
||
}
|
||
|
||
$value = trim((string) $value);
|
||
|
||
return $value === '' ? '—' : $value;
|
||
}
|
||
}
|