6.8 KiB
Context
当前系统已有 tb_account_operation_log,但该模型以账号为中心(如 target_account_id、target_username),不适合承载资产域(卡/设备)高频敏感操作。现有资产相关写操作分散在多个模块:
iot_card:分配、回收、系列绑定、轮询开关、实名策略、手动实名、删除device:删除、分配回收、绑定解绑、系列绑定、设备停复机、网关远程控制asset:统一入口停复机、停用、轮询状态、实名策略auto-stop-resume:轮询触发自动停复机
这些链路当前多为业务日志(zap),缺少可结构化查询的审计落库,无法稳定支持安全审计、操作追责与问题复盘。
约束:
- 必须遵循
Handler -> Service -> Store -> Model,审计写入在 Service 层触发。 - 审计能力按业务域拆分,本次仅实现资产域,不做全局万能日志表。
- 主流程性能不可明显退化,审计写入采用异步非阻塞。
- 不新增自动化测试文件,采用 PostgreSQL MCP + Postman/curl 手工验证。
Goals / Non-Goals
Goals:
- 新增资产域专用审计日志模型、存储与服务(
tb_asset_operation_log+asset_audit)。 - 覆盖卡与设备敏感写操作,要求成功/失败/拒绝全部记录。
- 统一日志字段:操作人、时间、动作、目标资产、变更前后、请求上下文、结果。
- 在现有模块中实现可复用的审计调用模式,减少重复拼装逻辑。
- 提供可回滚的迁移方案与明确的手工验收清单。
Non-Goals:
- 本阶段不实现通用“跨域审计查询平台”。
- 本阶段不改造账号域日志(
tb_account_operation_log)历史数据。 - 本阶段不新增审计查询 API(先完成采集落库,查询按 DB 手工验证)。
Decisions
决策1:资产域独立日志模型与表
选择:新增 tb_asset_operation_log,不复用 tb_account_operation_log。
理由:
- 资产日志与账号日志目标对象不同,字段语义差异大。
- 独立表可避免“万能表”字段污染,支持未来按域继续拆分(财务/权限等)。
- 资产操作体量高,独立索引策略可控。
备选方案:复用 tb_account_operation_log 增加 nullable 字段。
放弃原因:语义混杂、索引冲突、后续演进成本高。
决策2:统一资产审计服务 + 结构化日志构建器
选择:新增 internal/service/asset_audit,提供统一 LogOperation(ctx, entry);各业务 Service 在成功/失败/拒绝节点调用。
理由:
- 降低每个模块重复组装字段成本。
- 便于统一控制日志格式、字段裁剪、异常降级。
- 与现有账号审计模式一致,降低团队理解成本。
备选方案:每个业务 Service 直接写 Store。 放弃原因:重复代码多,格式易漂移,难做统一治理。
决策3:全结果态记录(success/failed/denied)
选择:结果字段使用 result_status(成功/失败/拒绝),并记录 error_code/error_msg(失败或拒绝时)。
理由:
- 仅记录成功不足以支撑安全审计(越权尝试、失败操作同样关键)。
- 便于风控与运维排查(失败高发点、越权热点)。
备选方案:只记录成功。 放弃原因:审计链不完整。
决策4:前后镜像字段规范化
选择:before_data/after_data 统一为 JSONB;
- create/绑定类:
before_data可为空,after_data必填关键字段 - update/状态流转类:
before_data与after_data同时记录 - delete/解绑类:
before_data必填,after_data可为空或最小状态
理由:
- 满足“从什么变成什么”的核心诉求。
- 便于后续做差异化检索与审计导出。
决策5:依赖注入与接入矩阵
选择:在 bootstrap 注入 assetAuditService 到以下服务:
iot_card.Servicedevice.Serviceiot_card.StopResumeServiceasset.LifecycleServicepolling.AssetPollingService(设备轮询开关路径)device_import.Service/iot_card_import.Service(任务创建记录)
理由:
- 关键写操作分布在多个服务,必须覆盖所有真实入口。
- 避免仅在 Handler 记录导致丢失 Service 内部失败/回滚细节。
决策6:异步写入 + 失败不阻断
选择:审计写入使用 Goroutine 异步落库,写入失败仅记录 Error 日志,不影响主业务结果。
理由:
- 符合当前项目审计日志规范。
- 避免写操作主路径被审计 IO 放大。
补充约束:
- 单条日志大小限制(对大字段做截断/脱敏),避免超大 JSON 影响 DB。
- 脱敏字段(如 WiFi 密码)仅记录摘要,不明文落库。
决策7:常量与操作类型字典化
选择:在 pkg/constants 定义资产审计操作类型、结果类型、目标类型常量,禁止硬编码。
理由:
- 统一语义,避免多模块字符串漂移。
- 便于统计与后续扩展。
Risks / Trade-offs
-
[风险] 日志量增长导致表膨胀和查询变慢
→ 缓解:增加组合索引(资产、操作人、时间、结果),后续按月归档策略预留。 -
[风险] before/after 组装不一致,影响可读性
→ 缓解:统一字段模板与 helper,代码评审按固定清单检查。 -
[风险] 异步写入在进程异常退出时丢少量日志
→ 缓解:接受该权衡;关键安全场景后续可升级为异步队列化写入。 -
[风险] 远程控制接口包含敏感参数(如密码)
→ 缓解:明确脱敏规则,禁止明文入after_data。 -
[权衡] 全量记录失败/拒绝会增加写入量
→ 接受:审计完整性优先于少量存储成本。
Migration Plan
- 新增模型与迁移:
tb_asset_operation_log(含必要索引)。 - 新增
asset_auditstore/service,并在 bootstrap 完成注入。 - 按操作矩阵分批接入:
- 第一批:停复机、绑定解绑、分配回收、停用、实名策略、轮询开关
- 第二批:远程控制、导入任务创建、删除/批量删除
- 补充常量与日志构建 helper,统一字段格式与脱敏。
- 使用 PostgreSQL MCP + Postman/curl 执行手工验收,输出证据(请求、响应、DB 快照)。
回滚策略:
- 业务回滚:关闭审计调用开关(或临时空实现),不影响主流程。
- 数据回滚:执行 migration down 删除新表。
Open Questions
- 是否在本期即提供后台审计查询 API,还是维持 DB 手工查询到下一期?
after_data的最大存储体积阈值定为多少(如 16KB/32KB)?- 是否需要为“自动停复机(系统触发)”定义专门
operator_type(system)常量?