29 KiB
新增需求 04:全局多视角审计方案
状态:已合并至标准评审稿,本文保留为实施明细。 评审主文档:
../../7月迭代技术方案-标准评审稿.md范围:全系统业务写操作、安全操作、外部集成和审计查询前端。
一、目标
审计系统必须能够从不同视角回答:
谁做了什么?
某个资源经历了什么?
一次请求引发了哪些跨模块变化?
哪些操作失败、被拒绝或存在风险?
外部 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 主表
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 无法完整查询。
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:
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 表示入口,不表示操作者:
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 |
五、领域模型和代码结构
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 中自由拼字符串:
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 在事务内调用:
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
表示跨请求、跨任务的完整业务链路:
退款申请创建
→ 企微审批提交
→ 企微回调
→ 人工退款结果入账
→ 代理钱包退款流水(仅代理钱包支付订单)
→ 佣金回扣
→ 套餐失效和停机
以上事件共享退款申请创建时生成的 correlation_id。Asynq、Outbox、企微实例和同步任务载荷都必须传递该值。
8.3 parent_event_id
表示直接因果关系。例如“套餐失效”事件的父事件是“退款终态处理成功”,用于前端画出树状链路。
九、数据脱敏
9.1 永不进入审计的数据
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。超过后保存:
{
"_truncated": true,
"_original_bytes": 38210,
"_summary": "批量修改500条资产",
"_artifact_ref": "batch_task:123"
}
批量明细保存在对应任务结果表或对象存储,不塞入审计 JSON。
十、外部集成日志
企业微信、运营商回调、Gateway 请求和事件同步尝试使用通用 Integration Log。同步尝试即使在真正发送 HTTP 前因合并、限流或预期状态已满足而结束,也写一条结果记录,保证能够解释“为什么没有请求上游”。
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 至少支持:
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 全局事件视角
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 事件详情
GET /api/admin/audit/events/{event_id}
返回:
- 操作者快照。
- 入口、租户和请求信息。
- 主要资源、影响资源和引用资源。
- 前后数据差异。
- 错误信息和风险等级。
- request/correlation/parent 关联。
- 可访问的领域流水和 Integration Log 链接。
12.3 操作者视角
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 资源视角
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 请求视角
GET /api/admin/audit/requests/{request_id}/timeline
按时间展示一次 HTTP 请求产生的 Access Log 摘要、Audit Event、Outbox 和 Integration Log。
12.6 业务链路视角
GET /api/admin/audit/correlations/{correlation_id}/timeline
用于查看退款、企微审批、钱包和资产处理等跨请求链路。返回节点包含 parent_event_id,前端可展示因果树。
12.7 风险视角
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 外部集成视角
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 资金视角
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 导出
POST /api/admin/audit/exports
复用现有导出任务系统。导出字段按角色配置,默认不允许导出完整 IP、手机号、ICCID、前后 JSON 和外部响应摘要。
十三、权限
建议权限码:
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:
- 全局事件:按时间倒序的审计事件表。
- 人员行为:操作者列表、风险统计和行为时间线。
- 资源轨迹:输入资源类型和标识,展示资源生命周期。
- 请求链路:按 request_id/correlation_id 展示跨模块时间线。
- 资金审计:订单、审批、钱包流水和退款/充值组合视图。
- 风险事件:失败、拒绝、高风险和严重事件。
- 外部集成:Gateway、企微、运营商回调和事件同步阶梯尝试。
Tab 根据权限返回,前端不展示无权限入口。
外部集成 Tab 增加紧凑的来源切换:全部 / Gateway 同步 / 运营商回调 / 企业微信。选择 Gateway 同步后展示触发来源、触发场景、资源、序列、尝试、结果、耗时和状态是否变化;点击序列打开三次尝试时间线。
14.2 全局事件表
字段:
时间 | 风险 | 操作者 | 操作 | 主要资源 | 来源 | 结果 | request_id
- 支持服务端分页和列筛选。
- 点击行打开右侧详情抽屉。
- 风险仅用图标和有限颜色表达,普通成功事件保持中性色。
request_id、资源编号可点击进入对应视角。
14.3 事件详情抽屉
分区:
事件摘要
操作者与入口
关联资源
字段变更对比
请求与业务链路
错误和外部交互
字段变更使用结构化键值对比,不直接把整段 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 修正
当前访问日志的响应体需要使用与请求体相同的递归脱敏逻辑:
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的资源时间线或历史投影。
十九、人工验证
- 关键资金事务在审计写入失败时整体回滚。
- 同一事件不会因 Outbox/Asynq 重试生成重复审计。
- 一次退款能够从退款单、订单、钱包和资产四个资源视角查到同一事件。
- request_id 能串联一次 API 请求产生的多条审计记录。
- correlation_id 能串联退款申请、企微审批、退款、佣金和资产处理。
- 普通用户不能访问全局、人员、风险和集成视角。
- 敏感字段默认脱敏,查看完整敏感字段的动作本身再次被审计。
- Access Log 请求和响应均不泄露 Token、Secret、操作密码和签名 URL。
- 高频轮询成功只进入外部集成视角,不会淹没业务资源时间线,状态变化和连续失败仍可见。
- 企微通过后撤销且资金已执行时生成 critical 事件,不自动冲正。
- 历史账号/资产日志可以通过统一审计页面查询,发布后的新写入不再双写旧表。
- 审计导出字段受角色权限控制,页面可见不等于可导出完整敏感值。
- 人员、资源和集成视角都有独立查询接口,前端不通过下载全局事件后自行聚合。
- 同一次事件同步的立即、3 分钟、5 分钟尝试能通过
trigger_series连续展示,合并、限流和提前完成都有可解释结果。 - 发布后旧账号、资产和轮询日志表不再新增记录,新旧历史仍能在同一审计界面查询。