Files
fdesk/app/common/audit/AuditBuffer.php
T

77 lines
2.3 KiB
PHP
Raw Normal View History

2025-09-26 15:52:45 +08:00
<?php
namespace app\common\audit;
use Webman\Context;
class AuditBuffer
{
const CTX_KEY = '__audit_buffer__';
public array $changes = []; // 每项:['scene','object_id','field','old','new']
public ?string $action = null; // 业务动作(可覆盖)
public array $meta = []; // 业务自定义元信息(如需扩展入库,可在消费者处处理)
public array $maskFields = ['password','pwd','token','secret','access_token','refresh_token'];
public static function instance(): self
{
$buf = Context::get(self::CTX_KEY);
if (!$buf) {
$buf = new self();
Context::set(self::CTX_KEY, $buf);
}
return $buf;
}
public static function reset(): void
{
Context::set(self::CTX_KEY, new self());
}
public function setAction(?string $action): void
{
$this->action = $action;
}
public function setMeta(array $meta): void
{
$this->meta = $meta + $this->meta;
}
/** 模型字段级变更推送(由 Trait 调用) */
public function pushModelChange(string $scene, $objectId, array $old, array $new): void
{
$keys = array_unique(array_merge(array_keys($old), array_keys($new)));
foreach ($keys as $k) {
$ov = $old[$k] ?? null;
$nv = $new[$k] ?? null;
if ($ov === $nv) continue;
[$ovMasked, $nvMasked] = $this->maskIfNeeded($k, $ov, $nv);
$this->changes[] = [
'scene' => $scene,
'object_id' => $objectId,
'field' => $k,
'old' => $ovMasked,
'new' => $nvMasked,
];
}
}
private function maskIfNeeded(string $field, $old, $new): array
{
foreach ($this->maskFields as $m) {
if (stripos($field, $m) !== false) {
return [$old===null?null:'***', $new===null?null:'***'];
}
}
if (preg_match('/(mobile|phone|tel|idcard|id_card)/i', $field)) {
$mask = function ($v) {
if ($v === null) return null;
$s = (string)$v;
return mb_substr($s, 0, 3).'****'.mb_substr($s, -4);
};
return [$mask($old), $mask($new)];
}
return [$old, $new];
}
}