Files
uniprop-manual/prop-acc/scenarios/billing/audit-activitylog-trace.md
2026-05-26 01:18:18 +08:00

9.9 KiB

title, aliases, tags, audience, status, sub_feature, last_review, code_version
title aliases tags audience status sub_feature last_review code_version
prop-acc · billing · 场景 - activitylog 审计追溯
activitylog 审计
操作日志查询
audit-activitylog-trace
场景-activitylog 追溯
场景
prop-acc
账单
审计
合规
业务人员
财务
审计师
法务
已发布 billing 2026-05-26 2026-05-22

场景:activitylog 审计追溯

billing 模块首次启用 spatie/laravel-activitylog(prop-acc 其他模块仅用 meta JSON)。所有关键操作(作废 / 批删 / 挂起 / 恢复 / 收款 / 创建)记 activitylog,审计 / 内审 / 法务可精准追溯:谁 / 什么时候 / 在哪 / 改了什么。

典型情境

[!example] 真实情境 5 月 20 日,内审师审计 5 月嘉禾花园账单数据,发现:

  • 5 月 15 日有一次批量删除 92 张账单(bulk_deleted)
  • 同日有 5 条 bill voided(账单作废)
  • 还有几张账单的 status 从 Suspended → Unpaid(恢复)

审计师要查清:

  • 谁操作的?
  • 操作的原因?
  • 影响了哪些具体账单?
  • 是否合规?

业务人员 / 审计师视角

第 1 步:确定查询维度

  • 谁操作:causer_id(操作员)
  • 什么时候:created_at 范围
  • 操作类型:event(created / voided / bulk_deleted / suspended / resumed / collected / split)
  • 针对哪个对象:subject_type + subject_id(单条操作)/ properties.affected_bill_nos(批量)
  • 操作详情:properties JSON 字段

第 2 步:运行 SQL 查询

查询 1:某员工某月所有操作

SELECT
    id,
    event,
    subject_type,
    subject_id,
    properties,
    created_at
FROM activity_log
WHERE causer_id = ?    -- 王主管 ID
  AND created_at BETWEEN '2026-05-01' AND '2026-05-31 23:59:59'
ORDER BY created_at DESC;

返回:

id event subject properties (节选) created_at
1023 bulk_deleted (null) mode=DeleteAndVoid, reason="...", deleted=92, voided=5 2026-05-15 14:32
1022 voided Bill #500 reason="业务调整", from_status=Partial 2026-05-15 14:31
1021 collected Bill #321 amount=800, channel=微信 2026-05-10 11:23
... ... ... ... ...

查询 2:某 Bill 的所有操作历史

SELECT
    event,
    causer_id,
    properties,
    created_at
FROM activity_log
WHERE subject_type = 'App\\Models\\Bill'
  AND subject_id = ?    -- 具体 Bill ID
ORDER BY created_at ASC;

返回该 Bill 的全生命周期:created → collected → voided 等。

查询 3:批量删除的详情

SELECT
    causer_id,
    properties->>'$.mode' AS mode,
    properties->>'$.reason' AS reason,
    properties->>'$.total_selected' AS selected,
    properties->>'$.deleted_count' AS deleted,
    properties->>'$.voided_count' AS voided,
    properties->>'$.blocked_count' AS blocked,
    JSON_LENGTH(properties->'$.affected_bill_nos') AS affected_count,
    created_at
FROM activity_log
WHERE event = 'bulk_deleted'
  AND created_at BETWEEN ? AND ?
ORDER BY created_at DESC;

可看出每次批删的统计 + 原因。

查询 4:某 bill_no 是否被批删 / 作废过

SELECT *
FROM activity_log
WHERE event = 'bulk_deleted'
  AND JSON_CONTAINS(
      properties->'$.affected_bill_nos',
      JSON_QUOTE('B-202605-501-001 [DELETED]')
  );

或:

-- 灵活模糊匹配
SELECT *
FROM activity_log
WHERE event = 'bulk_deleted'
  AND properties->'$.affected_bill_nos' LIKE '%B-202605-501-001%';

可追溯到已物理删的 Bill 是何时被谁删的。

第 3 步:解读 properties

不同 event 的 properties 结构不同:

event properties 主要字段
created(账单创建) bill_no, amount, fee_type_id, resident_id
collected(收款) amount, channel, receipt_id
voided(单作废) reason, from_status, to_status, bill_no, amount, paid_amount
suspended(挂起) reason, from_status, to_status, bill_no
resumed(恢复) reason, from_status, to_status, bill_no
split(拆账单) target_resident, amount_split, original_bill_id, new_bill_id
bulk_deleted(批删) mode, reason, total_selected, deleted_count, voided_count, blocked_count, affected_bill_nos[]

详见 smart-bulk-delete-design"activitylog 设计"段。

第 4 步:出审计报告

# 2026 年 5 月 嘉禾花园账单操作审计报告

## 审计范围
- 时段:2026-05-01 至 2026-05-31
- 模块:billing
- 关注操作:bulk_deleted, voided

## 高敏操作统计
- 批量删除(bulk_deleted):2 次
  - 5/15 王主管:92 删 + 5 作废,原因"5 月 1 日 Replace 策略误用清理"
  - 5/28 李经理:30 删,原因"测试数据清理"
- 单条作废(voided):8 次
  - 大多与上述批删事件关联
  - 1 次独立:5/22 王主管作废 Bill #321(原因:陈先生纠纷调解结果)

## 异常发现
- 无未授权操作(所有 bulk_deleted 操作员均有 bill.bulkDelete 权限)
- 无超规模操作(单次最多 92 张,合规)
- 所有操作都填了 reason(合规)

## 合规结论
- ✅ 所有高敏操作均有 audit trail
- ✅ 操作员权限符合岗位
- ✅ 原因填写规范

## 建议
- 长期保留 activitylog(至少 7 年,与会计档案同周期)
- 季度审计抽查

业户视角

业户通常不直接接触 activitylog。但法律纠纷时:

  • 业户对某账单的操作有疑问 → 业户可申请查看 activitylog
  • 物业有义务留存 + 展示操作历史(透明化、可追溯)
  • 业户/法院通过 activitylog 评估物业操作合规性

法务 / 监管视角

用途 怎么用
司法纠纷举证 业户起诉物业不当操作 → 物业拿 activitylog 证明操作合规
政府监管检查 检查批量删除 / 作废操作是否合理
行业自律审计 行业协会定期抽查
内部审计 财务总监 / 审计部门定期审

与 prop-acc 其他模块的对比

模块 审计方案
billing(本) activitylog + meta
deposit meta JSON(force_closed_* / freeze_reason 等)
prepaid meta JSON(freeze_reason / unfreeze_reason)
meter meta JSON(decommission_reason)
adhoc meta JSON 或 voided_at 字段

billing 的 activitylog 是 prop-acc 首个启用 spatie 审计日志的模块(issue.md Q6 标志性改进)。其他模块可以借鉴(未来若启用,有 billing 实施经验)。

activitylog 表结构(spatie 标准)

CREATE TABLE activity_log (
    id BIGINT PRIMARY KEY,
    log_name VARCHAR(255),
    description TEXT,            -- 简单描述
    subject_type VARCHAR(255),   -- 对象类(Bill)
    subject_id BIGINT,           -- 对象 ID
    causer_type VARCHAR(255),    -- 操作员类(User)
    causer_id BIGINT,            -- 操作员 ID
    properties JSON,             -- 详情(reason / amount / etc.)
    event VARCHAR(255),          -- 自定义事件名
    batch_uuid VARCHAR(36),      -- 可关联多条 log
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    INDEX idx_subject(subject_type, subject_id),
    INDEX idx_causer(causer_type, causer_id),
    INDEX idx_event(event),
    INDEX idx_created_at(created_at)
);

数据保留与归档

[!warning] activitylog 表会快速增长 每次高敏操作 1 条 + 收款 / 创建 / 编辑等可能也加 → 数据增长快。

建议:

  • 生产环境:保留 1-3 年热数据(查询性能优)
  • 冷数据归档:超 1 年的迁到 archive 表 / 对象存储
  • 永久保留:某些事件(bulk_deleted / voided)法定 7+ 年

当前未实施归档,数据量大后需要运维介入。

常见问题

[!question] activitylog 与 meta JSON 的关系? 互补:

方面 activitylog meta JSON
保留多久 全平台共表(易归档) 与对象同存(无法独立归档)
查询性能 按时间 / 类型查快 在对象上 random access 快
关联多个对象 (properties 数组) (只在自己 meta)
跨模块查询 (各模块字段不同)
操作上下文 (causer_id 直接) (若没存)

推荐:meta 存"对象当前状态的辅助"(如 voided_at);activitylog 存"事件链"(谁干了什么)。两者不冲突。

[!question] 业务人员如何看 activitylog? 当前没有 UI(spatie 包提供数据存储,UI 需自建)。审计师 / 业务方查询:

  • 直接 SQL(本场景示范)
  • 让运维查 / 出导出
  • 未来加 ActivityLogResource Filament UI(优先级看需求)

[!question] activitylog 能改 / 删吗? 系统层面理论上可改(就是普通表)。合规上不应改(篡改审计 = 大罪)。

高级实施:用append-only表(数据库层面 disallow UPDATE/DELETE)+ 定期写校验和(若数据被改可发现)。当前未实施。

[!question] 跨模块审计能合并查吗? 可以(activitylog 是全平台共表)。但其他模块当前没启用 activitylog(用 meta),所以跨模块审计目前只看 bill 模块。未来若其他模块启用,可统一查。

[!question] activitylog 与系统日志(Laravel log)的差异?

维度 activitylog Laravel log
内容 业务事件(带 subject / causer / properties) 技术日志(error / debug / info)
持久化 数据库 文件 / 集中日志服务
业务查询 结构化,SQL 查 全文搜索
合规价值 低(辅助排错)

两者并存,各管各的。activitylog 关注业务操作,Laravel log 关注系统行为

异常分支

  • 发现疑似篡改 → 物业内部审计 + 法务介入
  • 数据量太大查询慢 → 归档老数据
  • 业户申请查 activitylog → 出报告(由审计师 / 法务出)
  • 长期审计需求 → 加 ActivityLogResource UI(未来扩展)

相关文档