# 新增需求 04:全局多视角审计方案 > 状态:已合并至标准评审稿,本文保留为实施明细。 > 评审主文档:`../7月迭代技术方案-标准评审稿.md` > 范围:全系统业务写操作、安全操作、外部集成和审计查询前端。 ## 一、目标 审计系统必须能够从不同视角回答: ```text 谁做了什么? 某个资源经历了什么? 一次请求引发了哪些跨模块变化? 哪些操作失败、被拒绝或存在风险? 外部 Gateway、运营商和企业微信交互发生了什么? 资金变化对应哪个业务动作和审批结果? ``` 不是把所有日志塞进一张表,而是建立统一审计事件,并通过 Query 组合访问日志、领域流水和外部集成记录。 ## 二、当前代码问题 现有实现已经有账号审计和资产审计,但没有形成全系统能力: - `tb_account_operation_log` 和 `tb_asset_operation_log` 字段、操作类型和查询入口不一致。 - 账号、资产审计通过 goroutine Best Effort 写入,进程退出或数据库短暂失败时会丢失。 - 部分调用方又在审计服务外增加一层 goroutine,形成双重异步。 - 退款审批等资金操作没有统一业务审计。 - 线下充值借用账号操作日志,资源类型和关联关系不准确。 - 访问日志只脱敏请求体,响应体仍按原文记录,可能泄露 Token、个人信息和敏感配置。 - 现有审计表只能从单个账号或单个资产查看,无法按请求、关联业务、异常等级和外部交互串联。 ## 三、四类记录边界 | 类型 | 权威性 | 存储 | 用途 | |---|---|---|---| | Access Log | 非业务权威 | 文件/日志平台 | HTTP 调试、性能和请求追踪 | | Audit Event | 业务审计权威 | PostgreSQL | 谁对什么做了什么、结果如何 | | Domain Ledger | 领域事实权威 | 现有业务表 | 钱包流水、退款单、审批实例、订单状态 | | Integration Log | 外部交互权威 | PostgreSQL | Gateway、运营商回调、企微 API、回调和同步尝试 | 规则: - Audit Event 不替代钱包流水、退款记录和企微审批详情。 - Access Log 不作为业务审计依据。 - 数据同步的轮询、事件阶梯尝试、手动刷新和运营商回调统一进入 `tb_integration_log`,不再新建 `tb_card_sync_execution`。 - 高频轮询只有在状态变化、人工强制触发、连续失败或高风险异常时生成 Audit Event。 - Audit Query 可以把四类记录组合成时间线,但必须标明数据来源。 ## 四、审计事件模型 ### 4.1 主表 ```sql CREATE TABLE tb_audit_event ( id BIGSERIAL PRIMARY KEY, event_id VARCHAR(64) NOT NULL UNIQUE, occurred_at TIMESTAMPTZ NOT NULL, category VARCHAR(32) NOT NULL, action VARCHAR(128) NOT NULL, action_name VARCHAR(255) NOT NULL, actor_kind VARCHAR(32) NOT NULL, actor_id BIGINT, actor_name VARCHAR(255) NOT NULL DEFAULT '', actor_user_type INT, source VARCHAR(64) NOT NULL, tenant_type VARCHAR(32), tenant_id BIGINT, shop_id BIGINT, enterprise_id BIGINT, result VARCHAR(16) NOT NULL, risk_level VARCHAR(16) NOT NULL DEFAULT 'normal', summary TEXT NOT NULL, error_code VARCHAR(64), error_message TEXT, before_data JSONB, after_data JSONB, metadata JSONB, request_id VARCHAR(64), correlation_id VARCHAR(64), parent_event_id VARCHAR(64), ip_address VARCHAR(64), user_agent TEXT, request_path VARCHAR(255), request_method VARCHAR(16), content_hash VARCHAR(64) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_audit_event_time ON tb_audit_event (occurred_at DESC, id DESC); CREATE INDEX idx_audit_event_actor ON tb_audit_event (actor_kind, actor_id, occurred_at DESC); CREATE INDEX idx_audit_event_action ON tb_audit_event (category, action, occurred_at DESC); CREATE INDEX idx_audit_event_result ON tb_audit_event (risk_level, result, occurred_at DESC); CREATE INDEX idx_audit_event_request ON tb_audit_event (request_id, occurred_at ASC); CREATE INDEX idx_audit_event_correlation ON tb_audit_event (correlation_id, occurred_at ASC); ``` ### 4.2 资源关系表 一次退款审批通过会同时影响退款单、订单、钱包、钱包流水、套餐和资产,只在主表保存一个 `resource_id` 无法完整查询。 ```sql CREATE TABLE tb_audit_event_resource ( id BIGSERIAL PRIMARY KEY, audit_event_id BIGINT NOT NULL, resource_type VARCHAR(64) NOT NULL, resource_id BIGINT, resource_key VARCHAR(128) NOT NULL DEFAULT '', relation VARCHAR(20) NOT NULL DEFAULT 'affected', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE UNIQUE INDEX uq_audit_event_resource ON tb_audit_event_resource ( audit_event_id, resource_type, COALESCE(resource_id, 0), resource_key, relation ); CREATE INDEX idx_audit_resource_timeline ON tb_audit_event_resource (resource_type, resource_id, audit_event_id DESC); CREATE INDEX idx_audit_resource_key ON tb_audit_event_resource (resource_type, resource_key, audit_event_id DESC); ``` 不建立数据库外键。`relation`: | relation | 含义 | |---|---| | `primary` | 本次操作的主要对象 | | `affected` | 被本次操作修改的对象 | | `reference` | 只用于解释上下文的关联对象 | ### 4.3 操作者 `actor_kind`: ```text admin_user agent_user enterprise_user personal_customer open_api_account system_task carrier_callback wecom_callback ``` 系统和外部回调允许 `actor_id=NULL`,但必须保存清晰的 `actor_name`,例如“移动实名回调”“企微审批状态同步”“套餐到期任务”。 ### 4.4 来源 `source` 表示入口,不表示操作者: ```text admin_api personal_api open_api asynq scheduled_job gateway carrier_callback.cmcc carrier_callback.cucc carrier_callback.ctcc wecom_callback wecom_polling data_migration ``` ### 4.5 结果与风险 `result`:`success / failed / denied / partial`。 `risk_level`:`normal / warning / high / critical`。 默认风险示例: | 操作 | 风险等级 | |---|---| | 普通字段修改成功 | normal | | 批量部分失败 | warning | | 权限拒绝、重复回调冲突 | warning | | 钱包余额、退款、角色权限、支付配置修改 | high | | 企微通过后撤销但资金已执行、审计写入失败 | critical | ## 五、领域模型和代码结构 ```text internal/ ├── domain/audit/ │ ├── event.go 不可变 AuditEvent 实体及不变量 │ ├── actor.go 操作者值对象 │ ├── resource.go 多资源关联 │ ├── sanitizer.go 审计数据脱敏规则 │ ├── action_registry.go 操作编码、名称、分类和默认风险 │ └── repository.go ├── application/audit/ │ ├── append_event.go 成功/失败审计写入 │ ├── append_with_tx.go 关键业务事务内写入 │ └── append_system_event.go ├── query/audit/ │ ├── event_list.go │ ├── actor_view.go │ ├── resource_view.go │ ├── request_view.go │ ├── correlation_view.go │ ├── risk_view.go │ ├── integration_view.go │ └── finance_view.go └── infrastructure/persistence/ └── audit_repository.go ``` Audit Event 具有独立 `event_id`,因此是不可变领域实体,不是值对象;Actor、Resource 和字段差异是值对象。事件创建后不提供 Update/Delete Repository 方法。 ## 六、操作注册表 操作编码禁止在各 Service 中自由拼字符串: ```go type ActionDefinition struct { Code string Name string Category string DefaultRiskLevel string SensitiveFields []string } var ActionRegistry = map[string]ActionDefinition{ "account.role.assign": { Name: "分配账号角色", Category: "security", DefaultRiskLevel: "high", }, "refund.wecom.approved": { Name: "企微审批通过并确认人工退款", Category: "finance", DefaultRiskLevel: "high", }, "recharge.wecom.approved": { Name: "企微审批通过并完成线下充值", Category: "finance", DefaultRiskLevel: "high", }, "iot_card.realname.callback": { Name: "运营商回调更新实名状态", Category: "asset", DefaultRiskLevel: "normal", }, } ``` DTO `description`、前端中文名称和筛选项都从同一注册表生成,避免操作编码与展示名称不一致。 ## 七、写入可靠性 ### 7.1 关键成功事件 以下事件必须与业务变更同事务写入: - 钱包余额变化。 - 人工退款结果确认和代理钱包回溯。 - 线下充值入账。 - 账号角色和权限变化。 - 支付、企微和系统关键配置变化。 - 卡实名、网络状态被人工修改。 - 手工绑定企微 `sp_no`。 Application 在事务内调用: ```go auditWriter.AppendWithTx(ctx, tx, event) ``` 审计写入失败时事务回滚,禁止业务成功但关键审计缺失。 ### 7.2 异步系统事件 轮询、事件阶梯同步、运营商回调和企微轮询等外部交互统一进入 Integration Log。只有以下情况同时写 Audit Event: - 状态发生业务变化。 - 连续失败达到阈值。 - 人工强制触发。 - 出现高风险异常。 系统事件通过同一业务事务或 Outbox 可靠写入,不启动裸 goroutine。 ### 7.3 失败和拒绝事件 业务事务已经回滚时,使用独立短事务写 `failed/denied` 审计。审计写入失败不能把原业务错误改成成功,但必须记录 `critical` 应用日志和监控指标。 ## 八、关联链路 ### 8.1 request_id 表示一次 HTTP 请求,由现有 Request ID 中间件产生。一次请求触发的所有审计事件使用同一个 `request_id`。 ### 8.2 correlation_id 表示跨请求、跨任务的完整业务链路: ```text 退款申请创建 → 企微审批提交 → 企微回调 → 人工退款结果入账 → 代理钱包退款流水(仅代理钱包支付订单) → 佣金回扣 → 套餐失效和停机 ``` 以上事件共享退款申请创建时生成的 `correlation_id`。Asynq、Outbox、企微实例和同步任务载荷都必须传递该值。 ### 8.3 parent_event_id 表示直接因果关系。例如“套餐失效”事件的父事件是“退款终态处理成功”,用于前端画出树状链路。 ## 九、数据脱敏 ### 9.1 永不进入审计的数据 ```text password operation_password access_token / refresh_token agent_secret / app_secret callback_token / encoding_aes_key 支付私钥、公钥原文 短信验证码 完整身份证号 对象存储签名 URL 企微 media_id ``` 这些字段直接删除,不保存为 `******`,避免字段存在本身造成误用。 ### 9.2 按权限展示的数据 手机、IP、ICCID、钱包余额和第三方交易号可以存储受控快照,但 Query 根据权限返回: - 普通审计权限:脱敏展示。 - 敏感审计权限:展示完整值。 - 导出权限单独控制,不能因为可查看页面就默认可导出完整值。 ### 9.3 大字段 `before_data`、`after_data` 和 `metadata` 分别限制 16KB。超过后保存: ```json { "_truncated": true, "_original_bytes": 38210, "_summary": "批量修改500条资产", "_artifact_ref": "batch_task:123" } ``` 批量明细保存在对应任务结果表或对象存储,不塞入审计 JSON。 ## 十、外部集成日志 企业微信、运营商回调、Gateway 请求和事件同步尝试使用通用 Integration Log。同步尝试即使在真正发送 HTTP 前因合并、限流或预期状态已满足而结束,也写一条结果记录,保证能够解释“为什么没有请求上游”。 ```sql CREATE TABLE tb_integration_log ( id BIGSERIAL PRIMARY KEY, integration_id VARCHAR(64) NOT NULL UNIQUE, provider VARCHAR(32) NOT NULL, direction VARCHAR(16) NOT NULL, operation VARCHAR(64) NOT NULL, external_id VARCHAR(128), resource_type VARCHAR(64), resource_id BIGINT, resource_key VARCHAR(128), trigger_source VARCHAR(32), trigger_scene VARCHAR(128), trigger_series VARCHAR(64), scheduled_at TIMESTAMPTZ, started_at TIMESTAMPTZ, attempt INT NOT NULL DEFAULT 1, result VARCHAR(20) NOT NULL, http_status INT, provider_code VARCHAR(64), provider_message TEXT, request_summary JSONB, response_summary JSONB, duration_ms BIGINT NOT NULL DEFAULT 0, state_changed BOOLEAN NOT NULL DEFAULT FALSE, metadata JSONB, request_id VARCHAR(64), correlation_id VARCHAR(64), audit_event_id BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_integration_log_time ON tb_integration_log (created_at DESC, id DESC); CREATE INDEX idx_integration_log_provider ON tb_integration_log (provider, operation, result, created_at DESC); CREATE INDEX idx_integration_log_resource ON tb_integration_log (resource_type, resource_id, created_at DESC); CREATE INDEX idx_integration_log_resource_key ON tb_integration_log (resource_type, resource_key, created_at DESC); CREATE INDEX idx_integration_log_trigger ON tb_integration_log (trigger_series, attempt); CREATE INDEX idx_integration_log_correlation ON tb_integration_log (correlation_id, created_at ASC); ``` `direction`:`inbound / outbound`。 `result` 至少支持: ```text success failed not_found invalid_payload ignored merged rate_limited completed cancelled ``` 数据同步字段约定: - `provider=gateway`:实名、流量、卡状态和设备信息查询。 - `provider=cmcc/cucc/ctcc` 且 `direction=inbound`:运营商实名或解除实名回调。 - `trigger_source`:`polling/business_event/open_api/manual_refresh/carrier_callback`。 - `trigger_scene`:触发埋点场景,例如 `client_asset_info`、`card_resume`、`device_switch_card`。 - `trigger_series + attempt`:关联一次 `0/3/5` 阶梯同步。 - `result=merged/rate_limited/completed` 时允许没有 HTTP 状态和响应摘要,因为没有真正请求上游。 - `state_changed=true` 时通过 `audit_event_id` 关联对应业务状态变化事件。 普通高频轮询成功也写 Integration Log,但不生成 Audit Event。查询和清理必须使用时间索引及分批删除,不能在业务请求中同步聚合全量历史。 外部交互正文只保存脱敏摘要。企微加密报文、运营商原始回调和 Gateway 加密请求不进入普通审计详情。 ## 十一、审计范围 ### 11.1 必须审计的写操作 | 模块 | 操作 | |---|---| | 账号权限 | 创建、禁用、删除、角色分配、密码重置 | | 资产 | 分配、回收、绑定、解绑、停复机、实名策略、手工实名、限速 | | 套餐 | 创建、修改、上下架、分配、价格、已用量和到期时间调整 | | 钱包资金 | 充值、扣款、退款、信用变化、人工调整 | | 订单退款 | 创建退款、企微终态、人工退款确认、代理钱包回溯、资产后处理 | | 线下充值 | 创建、企微终态、入账、通过后撤销异常 | | 系统配置 | 支付配置、企微配置状态、模板版本发布、导出字段权限 | | 数据同步 | 人工强制同步、回调更新业务状态、连续失败告警 | | 导入导出 | 创建任务、下载敏感导出、批量任务结果 | | 登录安全 | 登录成功/失败、Token 撤销、开放接口签名失败和重放拒绝 | ### 11.2 不进入业务审计的普通读操作 - 普通列表、详情和未读数查询只进入 Access Log。 - 下载敏感附件、导出、查看完整密钥状态、查看完整个人信息属于敏感读取,需要 Audit Event。 ## 十二、多视角 API ### 12.1 全局事件视角 ```http GET /api/admin/audit/events ?category=finance &action= &result=success &risk_level=high &source=wecom_callback &start_at= &end_at= &keyword= &page=1&page_size=50 ``` `keyword` 只匹配事件摘要、资源编号、操作者名称和 request_id,不对 JSONB 做无索引模糊扫描。 ### 12.2 事件详情 ```http GET /api/admin/audit/events/{event_id} ``` 返回: - 操作者快照。 - 入口、租户和请求信息。 - 主要资源、影响资源和引用资源。 - 前后数据差异。 - 错误信息和风险等级。 - request/correlation/parent 关联。 - 可访问的领域流水和 Integration Log 链接。 ### 12.3 操作者视角 ```http GET /api/admin/audit/actors ?actor_kind=admin_user &keyword= &range=30d &page=1&page_size=20 GET /api/admin/audit/actors/{actor_kind}/{actor_id}/summary?range=30d GET /api/admin/audit/actors/{actor_kind}/{actor_id}/events?page=1&page_size=50 ``` 人员列表返回操作者 ID、名称、类型、最近操作时间、操作量、失败量和最高风险等级;`keyword` 只做 ID、账号和名称的前缀匹配,不扫描审计 JSON。Summary 返回操作量、失败量、拒绝量、高风险量、常用操作、常操作资源和最近登录 IP。 ### 12.4 资源视角 ```http GET /api/admin/audit/resources/search ?resource_type= &keyword=RF202607150001 &page=1&page_size=20 GET /api/admin/audit/resources/{resource_type}/{resource_id}/timeline ?include_related=true &page=1&page_size=50 ``` 示例资源:`iot_card / device / refund / order / agent_wallet / wecom_approval / account / system_config`。 资源搜索属于 Query 模块,允许直接查询退款、订单、充值、账号和资产等读模型,不要求加载领域聚合。返回候选项的 `resource_type/resource_id/resource_key/display_name`,解决同一关键词命中多个资源的问题。静态 `resources/search` 路由必须先于动态资源路由注册。 `include_related=true` 时通过资源关系表返回影响该资源的跨模块事件,但不无限递归加载关联资源。 ### 12.5 请求视角 ```http GET /api/admin/audit/requests/{request_id}/timeline ``` 按时间展示一次 HTTP 请求产生的 Access Log 摘要、Audit Event、Outbox 和 Integration Log。 ### 12.6 业务链路视角 ```http GET /api/admin/audit/correlations/{correlation_id}/timeline ``` 用于查看退款、企微审批、钱包和资产处理等跨请求链路。返回节点包含 `parent_event_id`,前端可展示因果树。 ### 12.7 风险视角 ```http GET /api/admin/audit/risks/overview?range=24h GET /api/admin/audit/risks/events?risk_level=critical&page=1&page_size=50 ``` Overview: - 高风险和严重事件数量。 - 权限拒绝、开放接口重放、资金冲突和外部回调异常趋势。 - 按操作人、来源和资源类型聚合。 ### 12.8 外部集成视角 ```http GET /api/admin/audit/integrations ?provider=wecom &direction=inbound &operation=getapprovaldetail &result=failed &external_id= &resource_type= &resource_key= &trigger_source= &trigger_scene= &trigger_series= &page=1&page_size=50 GET /api/admin/audit/integrations/{integration_log_id} ``` 列表查询 `tb_integration_log`,并返回关联审计事件和业务链路 ID;详情返回脱敏后的请求摘要、响应摘要、耗时、错误、尝试次数、计划时间、触发场景和关联资源,不返回密钥、完整回调正文或附件内容。 当 `trigger_series` 有值时,详情同时返回该序列的全部尝试,前端按“立即、3 分钟、5 分钟”展示,不要求用户逐条搜索。同步记录不再通过独立 `/card-sync/executions` 接口查询。 ### 12.9 资金视角 ```http GET /api/admin/audit/finance/timeline ?business_no= &wallet_id= &transaction_no= &page=1&page_size=50 ``` Finance Query 组合: - Audit Event。 - 代理钱包和资产钱包流水。 - 退款单、充值单和订单。 - 企微审批实例。 每条记录明确 `record_source`,钱包流水仍是金额变化的权威数据。 ### 12.10 导出 ```http POST /api/admin/audit/exports ``` 复用现有导出任务系统。导出字段按角色配置,默认不允许导出完整 IP、手机号、ICCID、前后 JSON 和外部响应摘要。 ## 十三、权限 建议权限码: ```text audit:global:view audit:actor:view audit:resource:view audit:request:view audit:risk:view audit:integration:view audit:finance:view audit:sensitive:view audit:export ``` 第一版审计中心只开放给超级管理员和具有对应权限的平台角色。 代理、企业账号不进入全局审计中心;它们在资产、订单、充值等业务详情页只能查看自己有权限资源的资源时间线,且返回字段经过脱敏。 Service/Query 必须重新应用资源权限,前端隐藏 Tab 不能替代后端授权。 ## 十四、前端多视角界面 ### 14.1 审计中心 路由:`/operations/audit` 使用工作台式紧凑布局,不做营销式卡片页面。顶部为时间范围、关键词和常用过滤器,下方使用 Tab: 1. **全局事件**:按时间倒序的审计事件表。 2. **人员行为**:操作者列表、风险统计和行为时间线。 3. **资源轨迹**:输入资源类型和标识,展示资源生命周期。 4. **请求链路**:按 request_id/correlation_id 展示跨模块时间线。 5. **资金审计**:订单、审批、钱包流水和退款/充值组合视图。 6. **风险事件**:失败、拒绝、高风险和严重事件。 7. **外部集成**:Gateway、企微、运营商回调和事件同步阶梯尝试。 Tab 根据权限返回,前端不展示无权限入口。 外部集成 Tab 增加紧凑的来源切换:`全部 / Gateway 同步 / 运营商回调 / 企业微信`。选择 Gateway 同步后展示触发来源、触发场景、资源、序列、尝试、结果、耗时和状态是否变化;点击序列打开三次尝试时间线。 ### 14.2 全局事件表 字段: ```text 时间 | 风险 | 操作者 | 操作 | 主要资源 | 来源 | 结果 | request_id ``` - 支持服务端分页和列筛选。 - 点击行打开右侧详情抽屉。 - 风险仅用图标和有限颜色表达,普通成功事件保持中性色。 - `request_id`、资源编号可点击进入对应视角。 ### 14.3 事件详情抽屉 分区: ```text 事件摘要 操作者与入口 关联资源 字段变更对比 请求与业务链路 错误和外部交互 ``` 字段变更使用结构化键值对比,不直接把整段 JSON 原样堆在页面。未知字段可以折叠显示原始 JSON。 敏感字段默认脱敏;有 `audit:sensitive:view` 权限时提供“显示敏感数据”按钮,并对该次查看再写一条敏感读取审计。 ### 14.4 人员行为视角 左侧为操作者搜索和筛选,右侧显示: - 30 天操作量、失败率、高风险操作数。 - 常用来源 IP。 - 操作类型分布。 - 最近行为时间线。 - 涉及资源列表。 不做基于行为的自动封禁,只提供审计判断依据。 左侧人员列表调用 `/api/admin/audit/actors`,选择人员后再并行加载 Summary 和事件列表,避免前端从全局事件自行聚合。 ### 14.5 资源轨迹视角 支持输入 ICCID、设备虚拟号、退款单号、订单号、充值单号、账号名称等,先调用 `/api/admin/audit/resources/search` 返回候选资源;只有一个候选时直接打开时间线,多个候选时由用户选择,前端不自行猜测资源类型。 时间线同时展示: - 业务状态变化。 - 人工操作。 - 企微审批结果。 - 资金流水链接。 - 重要 Gateway/回调事件。 普通高频轮询成功不进入资源审计时间线,避免有效信息被淹没。 ### 14.6 请求和业务链路视角 使用纵向时间线展示节点,节点固定包含时间、来源、动作、结果和耗时。父子事件使用缩进和连接线表达,不使用复杂自由拖拽图编辑器。 ### 14.7 风险视角 顶部显示 24 小时趋势和风险分类,下方是风险事件表。支持一键跳转操作者、资源和 correlation 链路,但第一版不建设风险处置工单。 ## 十五、Access Log 修正 当前访问日志的响应体需要使用与请求体相同的递归脱敏逻辑: ```go responseBody := sanitizeBody(c.Response().Body()) ``` 并增加路由级策略: | 路由 | Body 记录策略 | |---|---| | 登录、Token、支付配置 | 只记录字段名和长度,不记录值 | | 企微/支付/运营商回调 | 记录摘要和哈希,不记录完整正文 | | 文件上传下载 | 不记录文件内容和签名 URL | | 普通 JSON API | 脱敏后最多 50KB | Access Log 建议保留 30 天;不得因为 Access Log 已存在而省略关键业务 Audit Event。 ## 十六、保留策略 | 数据 | 保留期限 | |---|---:| | 资金、权限、审批和关键配置 Audit Event | 5 年 | | 普通资产和业务操作 Audit Event | 2 年 | | Integration Log | 180 天 | | Gateway 高频同步 Integration Log | 正常 30 天,异常或状态变化 180 天 | | Access Log | 30 天 | Audit Event 默认不在线删除。数据量达到单表维护阈值后按月分区,超期分区归档到低成本存储,再由专用维护流程删除;应用账号不具备 Update/Delete 审计表权限。 ## 十七、一次性审计切换 本需求与普通 DDD 触碰式迁移不同:审计基础设施、写入入口、Query API 和前端多视角界面在同一次停机发布中全局切换,不能让新旧审计长期并行。 ### 17.1 新写入 - 数据同步、企微审批、站内通知和后续新功能只写统一 `tb_audit_event` 与 `tb_integration_log`。 - 账号、角色、权限、资产、套餐、订单、退款、充值、钱包、系统配置、导入导出和登录安全等现有敏感操作同时切换到统一 Audit Writer。 - 卡同步不创建 `tb_card_sync_execution`;轮询、手动刷新、事件阶梯尝试和运营商回调统一写 Integration Log。 - 发布后旧 `tb_account_operation_log`、`tb_asset_operation_log`、手动轮询触发日志等表不再产生新记录。 - 禁止双写新旧审计表,避免同一操作产生两条不一致记录。 一次性切换审计不等于一次性把所有旧业务模块迁移为 DDD。未迁移的旧 Service 通过统一审计 Adapter 写入新模型;退款、资金、卡状态等复杂且本次触碰的用例按 DDD 规范迁入 Application/Domain。 ### 17.2 历史数据 - 旧审计表保留只读,不在线回填到新表。 - 审计 Query 使用 `UNION ALL` 把旧账号、资产和手动触发日志标准化为历史投影,返回 `record_source=legacy_account/legacy_asset/legacy_polling`。 - 历史记录只用于查询,不允许更新或补写。 - 后续达到归档条件后离线迁移或归档旧表,不影响本次新写入一次性切换。 ## 十八、代码迁移重点 - 删除 `account_audit.Service.LogOperation` 和 `asset_audit.Service.LogOperation` 内部裸 goroutine。 - 删除调用方外围 `go auditService.LogOperation(...)`。 - 为尚未迁入 DDD 的旧 Service 提供统一 Audit Writer Adapter,并在本次发布中替换全部敏感操作旧写入口。 - 退款、线下充值资金事务使用 `AppendWithTx`。 - 卡实名领域用例在状态变化事务内写审计。 - 轮询、事件同步、手动刷新和运营商回调统一写 Integration Log;只有状态变化、连续失败、手动操作和高风险异常追加 Audit Event。 - 企微模板发布、手工绑定 sp_no、通过后撤销写高风险事件。 - `pkg/logger/middleware.go` 对响应体和特殊路由实施脱敏策略。 - 现有账号、资产和业务日志详情接口统一改为调用 `query/audit` 的资源时间线或历史投影。 ## 十九、人工验证 1. 关键资金事务在审计写入失败时整体回滚。 2. 同一事件不会因 Outbox/Asynq 重试生成重复审计。 3. 一次退款能够从退款单、订单、钱包和资产四个资源视角查到同一事件。 4. request_id 能串联一次 API 请求产生的多条审计记录。 5. correlation_id 能串联退款申请、企微审批、退款、佣金和资产处理。 6. 普通用户不能访问全局、人员、风险和集成视角。 7. 敏感字段默认脱敏,查看完整敏感字段的动作本身再次被审计。 8. Access Log 请求和响应均不泄露 Token、Secret、操作密码和签名 URL。 9. 高频轮询成功只进入外部集成视角,不会淹没业务资源时间线,状态变化和连续失败仍可见。 10. 企微通过后撤销且资金已执行时生成 critical 事件,不自动冲正。 11. 历史账号/资产日志可以通过统一审计页面查询,发布后的新写入不再双写旧表。 12. 审计导出字段受角色权限控制,页面可见不等于可导出完整敏感值。 13. 人员、资源和集成视角都有独立查询接口,前端不通过下载全局事件后自行聚合。 14. 同一次事件同步的立即、3 分钟、5 分钟尝试能通过 `trigger_series` 连续展示,合并、限流和提前完成都有可解释结果。 15. 发布后旧账号、资产和轮询日志表不再新增记录,新旧历史仍能在同一审计界面查询。