## 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.Service`
- `device.Service`
- `iot_card.StopResumeService`
- `asset.LifecycleService`
- `polling.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

1. 新增模型与迁移：`tb_asset_operation_log`（含必要索引）。
2. 新增 `asset_audit` store/service，并在 bootstrap 完成注入。
3. 按操作矩阵分批接入：
   - 第一批：停复机、绑定解绑、分配回收、停用、实名策略、轮询开关
   - 第二批：远程控制、导入任务创建、删除/批量删除
4. 补充常量与日志构建 helper，统一字段格式与脱敏。
5. 使用 PostgreSQL MCP + Postman/curl 执行手工验收，输出证据（请求、响应、DB 快照）。

**回滚策略：**
- 业务回滚：关闭审计调用开关（或临时空实现），不影响主流程。
- 数据回滚：执行 migration down 删除新表。

## Open Questions

- 是否在本期即提供后台审计查询 API，还是维持 DB 手工查询到下一期？
- `after_data` 的最大存储体积阈值定为多少（如 16KB/32KB）？
- 是否需要为“自动停复机（系统触发）”定义专门 `operator_type`（system）常量？
