升鲜宝业务通用日志组件字段级前后值日志开发方案(一)---升鲜宝生鲜配送供应链管理系统源代码服务
升鲜宝业务通用日志组件
字段级前后值日志开发方案
用于审计、风控、数据安全、业务追溯、责任划分的基础组件
文档版本:V1.0
适用系统:升鲜宝供应链管理系统
后端技术:Spring Boot + MyBatis-Plus + MySQL 8.0 + Redis
前端技术:Vue3 管理端 / 业务详情页日志面板
文档用途:指导字段级业务审计日志组件开发与落地
文档修订记录
|--------|------------|---------|--------------------|
| 版本 | 日期 | 修订人 | 修订说明 |
| V1.0 | 2021-01-19 | 初始化版本 | 形成字段级前后值日志组件完整开发方案 |
目录
-
建设背景与组件定位
-
总体目标与设计原则
-
业务范围与接入场景
-
总体架构设计
-
数据库表结构设计
-
字段级差异计算引擎设计
-
后端组件开发设计
-
AOP 注解式接入方案
-
复杂业务手动接入方案
-
Outbox 异步日志方案
-
敏感字段脱敏与安全审计
-
前端页面与交互设计
-
API 接口清单
-
各业务模块接入规范
-
性能、归档与运维监控
-
测试用例与验收标准
-
实施计划与开发任务拆分
-
附录:枚举、初始化数据、代码样例
1. 建设背景与组件定位
升鲜宝后续业务会覆盖 B2B订货商城、OMS订单、PMS商品中心、WMS仓库库存、HWMS门店库存、POS门店收银、会员营销、财务对账、物流配送等多个核心业务域。系统越复杂,越需要一套统一、标准、可追溯的字段级业务日志组件。
|------------------------------------------------------------------------------------------------------------------|
| 核心定位 字段级前后值日志不是普通操作日志,而是升鲜宝后续做审计、风控、数据安全、业务追溯、责任划分的基础组件。每次关键业务操作都需要回答:谁在什么时间、通过什么入口、对哪个业务对象、把哪些字段从什么值改成了什么值。 |
传统 sys_operate_log 只能记录"用户修改了订单",无法回答"订单金额是谁改的、从多少改到多少、改价前后商品明细如何变化、该操作是否影响库存或成本"。本组件要解决的正是这个问题。
|--------|--------------|-------------------------------------------|
| 问题 | 传统操作日志不足 | 字段级业务日志能力 |
| 排查价格变化 | 只能看到某人点击了修改 | 记录 salePrice:12.50 → 13.80,并显示字段中文名"销售单价" |
| 追溯订单金额 | 无法定位金额变化来源 | 记录订单主表金额、明细数量、单价、优惠金额前后值 |
| 库存责任划分 | 只能看到审核动作 | 记录审核前库存、审核后库存、影响单据、库存流水 |
| 敏感数据审计 | 难以控制展示范围 | 支持字段敏感标识、脱敏展示、查看明文二次审计 |
| 风控预警 | 日志不可分析 | 可按字段变化触发风险规则,例如大额改价、频繁反审核 |
2. 总体目标与设计原则
2.1 建设目标
- 建立统一的业务审计日志中心,覆盖新增、修改、删除、审核、反审核、启用、停用、导入、导出、支付、退款、库存调整、成本调整等动作。
- 记录字段级前后值变化,包括字段编码、字段中文名、字段类型、原始值、展示值、敏感标记、操作人、操作来源、请求链路。
- 支持主表、明细表、嵌套对象、集合对象的字段差异对比。
- 支持不同业务域按配置启用日志字段,不要求每个业务手工写字段变化逻辑。
- 支持普通业务异步写日志,核心业务同事务写日志,高并发场景 Outbox 异步可靠落库。
- 支持前端在业务详情页直接查看"操作记录、字段变更、状态流转、库存影响、金额变化"。
- 为后续数据权限、字段权限、导出权限、脱敏权限、越权告警、风控规则提供审计基础。
2.2 设计原则
|--------|-----------------------------------------------|
| 原则 | 说明 |
| 业务可读 | 日志不能只存字段编码,要同时保存字段中文名、展示值、字典翻译值。 |
| 字段级追溯 | 核心变更必须记录到具体字段,不能只写"修改成功"。 |
| 可配置 | 哪些字段记录、哪些字段忽略、哪些字段脱敏,通过配置控制。 |
| 低侵入 | 普通业务通过注解+AOP接入,复杂业务提供手动调用接口。 |
| 强一致 | 订单审核、出入库审核、成本调整等核心业务应与日志在同一事务或通过Outbox保证最终一致。 |
| 可扩展 | 支持新增模块、新增业务类型、新增值转换器、新增脱敏规则。 |
| 安全合规 | 敏感字段默认脱敏,查看完整值需要权限并二次记录审计。 |
| 性能可控 | 差异计算、快照保存、日志明细写入要支持异步、批量、归档。 |
3. 业务范围与接入场景
第一阶段先覆盖"资料配置类 + 订单类 + 价格类 + 商城配置类";第二阶段覆盖"库存成本 + POS + 支付退款 + 会员资产";第三阶段扩展到风控预警与审计中心。
|----------|---------|------------------------|-----------------------------------|
| 业务域 | 优先级 | 典型对象 | 必须记录的字段变化 |
| PMS商品中心 | P1 | 商品、SKU、单位、条码、分类、品牌 | 商品名称、规格、单位换算、销售/采购/库存单位、启用状态、价格字段 |
| B2B订货商城 | P1 | Banner、金刚区、通知、分类展示、优惠券 | 可见范围、客户/价格组投放、生效时间、排序、优惠金额、使用门槛 |
| OMS订单中心 | P1 | 订单主表、订单明细、支付、退款 | 数量、单价、金额、优惠、状态、收货信息、审核状态 |
| 客户与价格 | P1 | 客户资料、客户价格、价格组价格 | 客户名称、账期、授信、价格、最小起订量、启用状态 |
| WMS库存成本 | P2 | 入库、出库、盘点、报损报溢、成本 | 审核状态、库存数量、成本价、成本金额、影响流水 |
| HWMS门店库存 | P2 | 门店入库、出库、盘点、报损报溢 | 门店库存数量、批次、效期、成本变化 |
| POS门店收银 | P2 | 收银订单、挂单、退款、抹零、改价 | 商品明细、会员、支付方式、抹零金额、改价原因 |
| 会员营销 | P2 | 优惠券、积分、余额、会员等级 | 领取/核销状态、余额前后值、积分前后值、等级变化 |
| 系统配置 | P3 | 菜单、角色、权限、打印模板、参数配置 | 权限范围、启用状态、配置值、模板内容 |
4. 总体架构设计
4.1 组件架构
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 业务 Controller / Service │ ├─ 注解式接入:@BizAuditLog + AOP │ ├─ 解析业务ID、业务单号、操作类型 │ ├─ 查询 before 对象 │ ├─ 执行业务方法 │ ├─ 查询 after 对象 │ └─ 调用 DiffEngine 生成字段变更 │ ├─ 手动接入:BizAuditLogService.recordChange(...) │ └─ 适合审核、反审核、库存、成本、支付等复杂业务 │ ├─ Outbox 接入:biz_audit_event │ └─ 高并发/批量导入/异步可靠写入 │ ▼ BizAuditLogService ├─ BizAuditDiffService 字段差异计算 ├─ BizAuditFieldConfigService 字段配置加载 ├─ AuditValueConverter 字典/枚举/日期/金额转换 ├─ SensitiveValueMasker 敏感字段脱敏 ├─ BizAuditSnapshotService 保存 before/after 快照 └─ BizAuditRiskService 风控规则预留 ▼ 数据库 ├─ biz_audit_log 操作主日志 ├─ biz_audit_log_detail 字段变更明细 ├─ biz_audit_snapshot 业务快照 ├─ biz_audit_field_config 字段配置 ├─ biz_audit_module_config 模块配置 └─ biz_audit_event Outbox事件 |
4.2 日志数据模型
|--------|-------------------------|------------------------------------------------|
| 层级 | 表名 | 职责 |
| 操作层 | biz_audit_log | 记录一次业务动作:谁、何时、模块、业务对象、操作类型、结果、来源。 |
| 字段层 | biz_audit_log_detail | 记录本次动作下每个字段的 oldValue/newValue、展示值、字段中文名、敏感标记。 |
| 快照层 | biz_audit_snapshot | 保存变更前和变更后的完整 JSON 快照,便于还原历史数据。 |
| 配置层 | biz_audit_field_config | 配置字段中文名、是否记录、字段类型、字典类型、脱敏规则、转换器。 |
| 模块层 | biz_audit_module_config | 配置模块是否启用日志、默认记录模式、快照开关、异步开关。 |
| 事件层 | biz_audit_event | 高并发或批量业务的可靠日志事件,用于异步消费生成日志。 |
5. 数据库表结构设计
以下表结构以 MySQL 8.0 为目标设计。主键建议使用升鲜宝现有雪花 ID 生成器,字段命名保持业务可读性。
5.1 业务操作日志主表 biz_audit_log
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TABLE `biz_audit_log` ( `id` bigint NOT NULL COMMENT '主键ID', `tenant_id` bigint DEFAULT NULL COMMENT '租户ID', `org_id` bigint DEFAULT NULL COMMENT '机构ID', `shop_id` bigint DEFAULT NULL COMMENT '门店ID', `module_code` varchar(64) NOT NULL COMMENT '模块编码:OMS/WMS/PMS/B2BMALL/POS/HWMS', `module_name` varchar(100) DEFAULT NULL COMMENT '模块名称', `biz_type` varchar(64) NOT NULL COMMENT '业务类型:ORDER/STOCK_IN/CUSTOMER_PRICE', `biz_name` varchar(100) DEFAULT NULL COMMENT '业务名称', `biz_id` bigint DEFAULT NULL COMMENT '业务主键ID', `biz_code` varchar(100) DEFAULT NULL COMMENT '业务单号/编码', `biz_title` varchar(200) DEFAULT NULL COMMENT '业务标题:客户名称/订单标题/商品名称', `operate_type` varchar(64) NOT NULL COMMENT '操作类型:CREATE/UPDATE/DELETE/AUDIT/UNAUDIT/ENABLE/DISABLE', `operate_name` varchar(100) DEFAULT NULL COMMENT '操作名称', `operate_result` tinyint NOT NULL DEFAULT 1 COMMENT '操作结果:1成功 0失败', `operate_desc` varchar(500) DEFAULT NULL COMMENT '操作说明', `before_snapshot_id` bigint DEFAULT NULL COMMENT '变更前快照ID', `after_snapshot_id` bigint DEFAULT NULL COMMENT '变更后快照ID', `change_count` int NOT NULL DEFAULT 0 COMMENT '字段变更数量', `risk_level` varchar(32) DEFAULT NULL COMMENT '风险等级:LOW/MEDIUM/HIGH', `risk_hit_flag` tinyint NOT NULL DEFAULT 0 COMMENT '是否命中风控规则:1是 0否', `operator_id` bigint DEFAULT NULL COMMENT '操作人ID', `operator_name` varchar(100) DEFAULT NULL COMMENT '操作人名称', `operator_type` varchar(32) DEFAULT NULL COMMENT '操作人类型:ADMIN/USER/CUSTOMER/MEMBER/SYSTEM/JOB/API', `source_type` varchar(32) DEFAULT NULL COMMENT '来源:ADMIN_WEB/MOBILE/POS/API/JOB/MQ', `request_id` varchar(100) DEFAULT NULL COMMENT '请求链路ID', `ip_address` varchar(64) DEFAULT NULL COMMENT 'IP地址', `user_agent` varchar(500) DEFAULT NULL COMMENT '浏览器或设备信息', `remark` varchar(500) DEFAULT NULL COMMENT '备注', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_biz` (`biz_type`, `biz_id`), KEY `idx_biz_code` (`biz_code`), KEY `idx_module_time` (`module_code`, `created_at`), KEY `idx_operator_time` (`operator_id`, `created_at`), KEY `idx_tenant_time` (`tenant_id`, `created_at`), KEY `idx_risk_time` (`risk_hit_flag`, `created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务操作日志主表'; |
5.2 字段变更明细表 biz_audit_log_detail
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TABLE `biz_audit_log_detail` ( `id` bigint NOT NULL COMMENT '主键ID', `log_id` bigint NOT NULL COMMENT '业务日志主表ID', `field_code` varchar(150) NOT NULL COMMENT '字段编码,如 customerName/detail1001.unitPrice', `field_name` varchar(200) NOT NULL COMMENT '字段中文名,如 客户名称/商品【青菜】单价', `field_type` varchar(50) DEFAULT NULL COMMENT '字段类型:STRING/DECIMAL/INTEGER/DATETIME/ENUM/BOOLEAN/JSON', `field_group` varchar(100) DEFAULT NULL COMMENT '字段分组:基本信息/金额信息/状态信息/明细信息', `old_value` text COMMENT '变更前原始值', `new_value` text COMMENT '变更后原始值', `old_display_value` text COMMENT '变更前展示值', `new_display_value` text COMMENT '变更后展示值', `change_type` varchar(32) DEFAULT NULL COMMENT '变化类型:ADD/UPDATE/REMOVE', `sensitive_flag` tinyint NOT NULL DEFAULT 0 COMMENT '是否敏感字段:1是 0否', `masked_flag` tinyint NOT NULL DEFAULT 0 COMMENT '是否已脱敏展示:1是 0否', `risk_field_flag` tinyint NOT NULL DEFAULT 0 COMMENT '是否风险字段:1是 0否', `sort_no` int DEFAULT 0 COMMENT '排序', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_log_id` (`log_id`), KEY `idx_field_code` (`field_code`), KEY `idx_risk_field` (`risk_field_flag`, `created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务日志字段变更明细表'; |
5.3 业务快照表 biz_audit_snapshot
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TABLE `biz_audit_snapshot` ( `id` bigint NOT NULL COMMENT '主键ID', `tenant_id` bigint DEFAULT NULL COMMENT '租户ID', `module_code` varchar(64) NOT NULL COMMENT '模块编码', `biz_type` varchar(64) NOT NULL COMMENT '业务类型', `biz_id` bigint DEFAULT NULL COMMENT '业务ID', `biz_code` varchar(100) DEFAULT NULL COMMENT '业务单号', `snapshot_type` varchar(32) NOT NULL COMMENT '快照类型:BEFORE/AFTER', `snapshot_json` longtext NOT NULL COMMENT '快照JSON', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_biz` (`biz_type`, `biz_id`), KEY `idx_biz_code` (`biz_code`), KEY `idx_module_time` (`module_code`, `created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务日志快照表'; |
5.4 字段日志配置表 biz_audit_field_config
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TABLE `biz_audit_field_config` ( `id` bigint NOT NULL COMMENT '主键ID', `module_code` varchar(64) NOT NULL COMMENT '模块编码', `biz_type` varchar(64) NOT NULL COMMENT '业务类型', `entity_name` varchar(200) DEFAULT NULL COMMENT '实体类名称或表名', `field_code` varchar(150) NOT NULL COMMENT '字段编码', `field_name` varchar(200) NOT NULL COMMENT '字段中文名', `field_type` varchar(50) DEFAULT NULL COMMENT '字段类型', `field_group` varchar(100) DEFAULT NULL COMMENT '字段分组', `log_flag` tinyint NOT NULL DEFAULT 1 COMMENT '是否记录日志:1是 0否', `sensitive_flag` tinyint NOT NULL DEFAULT 0 COMMENT '是否敏感字段', `mask_rule` varchar(100) DEFAULT NULL COMMENT '脱敏规则:MOBILE/ID_CARD/ADDRESS/BANK_CARD/NAME', `dict_type` varchar(100) DEFAULT NULL COMMENT '字典类型,用于状态值翻译', `value_converter` varchar(200) DEFAULT NULL COMMENT '值转换器Bean名称', `risk_field_flag` tinyint NOT NULL DEFAULT 0 COMMENT '是否风险字段:1是 0否', `ignore_blank_flag` tinyint NOT NULL DEFAULT 0 COMMENT '空值变化是否忽略:1是 0否', `sort_no` int DEFAULT 0 COMMENT '排序', `enabled` tinyint NOT NULL DEFAULT 1 COMMENT '是否启用:1启用 0停用', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_biz_field` (`module_code`, `biz_type`, `field_code`), KEY `idx_module_biz` (`module_code`, `biz_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务日志字段配置表'; |
5.5 模块日志配置表 biz_audit_module_config
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TABLE `biz_audit_module_config` ( `id` bigint NOT NULL COMMENT '主键ID', `module_code` varchar(64) NOT NULL COMMENT '模块编码', `module_name` varchar(100) NOT NULL COMMENT '模块名称', `biz_type` varchar(64) NOT NULL COMMENT '业务类型', `biz_name` varchar(100) NOT NULL COMMENT '业务名称', `enabled` tinyint NOT NULL DEFAULT 1 COMMENT '是否启用日志:1启用 0停用', `record_snapshot_flag` tinyint NOT NULL DEFAULT 1 COMMENT '是否记录快照:1是 0否', `record_detail_flag` tinyint NOT NULL DEFAULT 1 COMMENT '是否记录字段明细:1是 0否', `write_mode` varchar(32) NOT NULL DEFAULT 'ASYNC' COMMENT '写入模式:SYNC/ASYNC/OUTBOX', `retention_days` int DEFAULT 1095 COMMENT '保留天数', `remark` varchar(500) DEFAULT NULL COMMENT '备注', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_module_biz` (`module_code`, `biz_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务日志模块配置表'; |
5.6 Outbox日志事件表 biz_audit_event
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CREATE TABLE `biz_audit_event` ( `id` bigint NOT NULL COMMENT '主键ID', `event_key` varchar(150) NOT NULL COMMENT '事件幂等键,建议 module+bizType+bizId+operateType+requestId', `module_code` varchar(64) NOT NULL COMMENT '模块编码', `biz_type` varchar(64) NOT NULL COMMENT '业务类型', `biz_id` bigint DEFAULT NULL COMMENT '业务ID', `biz_code` varchar(100) DEFAULT NULL COMMENT '业务单号', `operate_type` varchar(64) NOT NULL COMMENT '操作类型', `payload_json` longtext NOT NULL COMMENT '事件载荷,包含 before/after 或查询参数', `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态:0待处理 1处理中 2成功 3失败', `retry_count` int NOT NULL DEFAULT 0 COMMENT '重试次数', `next_retry_time` datetime DEFAULT NULL COMMENT '下次重试时间', `error_message` varchar(1000) DEFAULT NULL COMMENT '错误信息', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_event_key` (`event_key`), KEY `idx_status_retry` (`status`, `next_retry_time`), KEY `idx_biz` (`biz_type`, `biz_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务审计日志Outbox事件表'; |
6. 字段级差异计算引擎设计
6.1 DiffEngine 核心流程
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 输入:moduleCode + bizType + beforeObj + afterObj │ ├─ 1. 加载字段配置 biz_audit_field_config ├─ 2. 将 beforeObj / afterObj 统一转换为 Map<String, Object> ├─ 3. 按字段配置过滤:log_flag=1 且 enabled=1 ├─ 4. 按字段类型标准化值:金额、日期、布尔、字典、枚举、JSON ├─ 5. 比较 oldValue 和 newValue ├─ 6. 生成 RespBizAuditFieldChange ├─ 7. 执行值转换:oldDisplayValue / newDisplayValue ├─ 8. 执行敏感字段脱敏 ├─ 9. 标记风险字段 └─ 10. 返回字段变更集合 |
6.2 字段比较规则
|--------------|-----------------------------|---------------------|
| 字段类型 | 比较规则 | 展示规则 |
| STRING | 默认 trim 后比较,可配置忽略空字符串变化 | 原样展示或脱敏展示 |
| DECIMAL | 按升鲜宝统一小数位/舍入组件格式化后比较 | 按金额/数量/单价小数位展示 |
| INTEGER/LONG | 数值直接比较 | 数字展示 |
| DATETIME | 统一转 yyyy-MM-dd HH:mm:ss 后比较 | 按系统时间格式展示 |
| BOOLEAN | true/false 比较 | 展示为 是/否、启用/停用 |
| ENUM/DICT | 按原始编码比较 | 通过 dict_type 转中文展示值 |
| JSON | 默认整体比较,可扩展为子字段比较 | 展示摘要或格式化JSON |
| LIST | 按明细ID或业务唯一键匹配 | 展示新增/删除/修改的明细字段 |
6.3 明细行变化记录规则
升鲜宝很多业务是"主表 + 明细表",例如 oms_order_bill + oms_order_bill_info、wms_input + wms_input_info、mall_shop_order + mall_shop_order_info。组件必须支持集合明细变化。
|----------|-----------------------------------------|-----------------|-----------|-----------|
| 变化类型 | 字段编码示例 | 字段名称示例 | 变更前 | 变更后 |
| 修改明细数量 | detail879912.quantity | 商品【青菜 500g/袋】数量 | 10 | 12 |
| 修改明细单价 | detail879912.unitPrice | 商品【青菜 500g/袋】单价 | 5.00 | 5.50 |
| 新增明细商品 | detailNEW:skuUnitId=10086.goodsName | 新增商品 | | 青菜 |
| 删除明细商品 | detail879913.deleted | 删除商品 | 苹果 1箱 | |
| 修改批次信息 | detail879914.batchNo | 商品【牛肉】批次号 | B20260501 | B20260521 |
|--------------------------------------------------------------------------------------------------------------------------------------------------|
| 明细唯一键建议 订单明细、出入库明细优先使用明细ID匹配;如果新增时无ID,可使用 skuUnitId/productSkuId+unitId+batchNo 作为临时匹配键。日志展示时一定要拼出商品名称、规格、单位,避免只显示 detail1001.unitPrice。 |
7. 后端组件开发设计
7.1 推荐目录结构
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| common/audit ├─ annotation │ └─ BizAuditLog.java ├─ aspect │ └─ BizAuditLogAspect.java ├─ constant │ └─ BizAuditConstant.java ├─ converter │ ├─ AuditValueConverter.java │ ├─ DictAuditValueConverter.java │ ├─ DateTimeAuditValueConverter.java │ ├─ DecimalAuditValueConverter.java │ └─ SensitiveValueMasker.java ├─ dao │ ├─ BizAuditLogDao.java │ ├─ BizAuditLogDetailDao.java │ ├─ BizAuditSnapshotDao.java │ ├─ BizAuditFieldConfigDao.java │ └─ BizAuditEventDao.java ├─ entity │ ├─ BizAuditLogEntity.java │ ├─ BizAuditLogDetailEntity.java │ ├─ BizAuditSnapshotEntity.java │ ├─ BizAuditFieldConfigEntity.java │ ├─ BizAuditModuleConfigEntity.java │ └─ BizAuditEventEntity.java ├─ enums │ ├─ BizAuditOperateTypeEnum.java │ ├─ BizAuditSourceTypeEnum.java │ ├─ BizAuditWriteModeEnum.java │ └─ BizAuditFieldTypeEnum.java ├─ req │ ├─ ReqBizAuditLogSave.java │ ├─ ReqBizAuditLogPage.java │ ├─ ReqBizAuditDetailPage.java │ └─ ReqBizAuditFieldConfigSave.java ├─ resp │ ├─ RespBizAuditLogPage.java │ ├─ RespBizAuditLogDetail.java │ ├─ RespBizAuditFieldChange.java │ └─ RespBizAuditSnapshotCompare.java ├─ service │ ├─ BizAuditLogService.java │ ├─ BizAuditDiffService.java │ ├─ BizAuditSnapshotService.java │ ├─ BizAuditFieldConfigService.java │ └─ BizAuditEventService.java └─ service/impl ├─ BizAuditLogServiceImpl.java ├─ BizAuditDiffServiceImpl.java ├─ BizAuditSnapshotServiceImpl.java ├─ BizAuditFieldConfigServiceImpl.java └─ BizAuditEventServiceImpl.java |
命名要求:请求对象统一使用 Req 前缀,返回对象统一使用 Resp 前缀;Service 接口不建议返回 Result 包装,Controller 层保持原项目 Result 风格即可。
7.2 核心 Service 接口
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public interface BizAuditLogService { /** * 保存业务审计日志:包含主日志、字段明细、快照。 */ Long recordChange(ReqBizAuditLogSave req); /** * 只保存操作主日志,不保存字段明细。 */ Long recordOperate(ReqBizAuditLogSave req); /** * 分页查询业务日志。 */ PageResult<RespBizAuditLogPage> page(ReqBizAuditLogPage req); /** * 查询业务日志详情,包括字段变化明细。 */ RespBizAuditLogDetail detail(Long logId); /** * 按业务对象查询日志列表,用于业务详情页内嵌展示。 */ List<RespBizAuditLogPage> listByBiz(String bizType, Long bizId, String bizCode); } public interface BizAuditDiffService { /** * 计算字段级差异。 */ List<RespBizAuditFieldChange> diff(String moduleCode, String bizType, Object beforeObj, Object afterObj); } |
7.3 请求与返回对象
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Data @Builder public class ReqBizAuditLogSave { private Long tenantId; private Long orgId; private Long shopId; private String moduleCode; private String moduleName; private String bizType; private String bizName; private Long bizId; private String bizCode; private String bizTitle; private String operateType; private String operateName; private String operateDesc; private Object beforeObj; private Object afterObj; private Boolean recordSnapshotFlag; private Boolean recordDetailFlag; private String sourceType; private String requestId; private String remark; } @Data public class RespBizAuditFieldChange { private String fieldCode; private String fieldName; private String fieldType; private String fieldGroup; private Object oldValue; private Object newValue; private String oldDisplayValue; private String newDisplayValue; private String changeType; private Boolean sensitiveFlag; private Boolean maskedFlag; private Boolean riskFieldFlag; } |
8. AOP 注解式接入方案
普通增删改业务优先使用注解式接入,减少业务代码侵入。AOP 负责在业务方法执行前后获取 before/after 数据并自动计算字段变化。
8.1 注解定义
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface BizAuditLog { String moduleCode(); String moduleName() default ""; String bizType(); String bizName() default ""; String operateType(); String operateName() default ""; /** SpEL 表达式,如 #req.id */ String bizId() default ""; /** SpEL 表达式,如 #req.orderCode */ String bizCode() default ""; String bizTitle() default ""; boolean recordSnapshot() default true; boolean recordFieldChange() default true; /** 查询变更前对象的 Spring Bean 名称 */ String beforeQueryBean() default ""; /** 查询变更前对象的方法名 */ String beforeQueryMethod() default ""; /** 查询变更后对象的 Spring Bean 名称 */ String afterQueryBean() default ""; /** 查询变更后对象的方法名 */ String afterQueryMethod() default ""; } |
8.2 商品修改示例
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @BizAuditLog( moduleCode = "PMS", moduleName = "商品中心", bizType = "GOODS", bizName = "商品资料", operateType = "UPDATE", operateName = "修改商品", bizId = "#req.id", bizCode = "#req.goodsCode", beforeQueryBean = "pmsGoodsQueryService", beforeQueryMethod = "getAuditSnapshot", afterQueryBean = "pmsGoodsQueryService", afterQueryMethod = "getAuditSnapshot" ) public void updateGoods(ReqPmsGoodsSave req) { // 原业务修改逻辑 } |
8.3 AOP 执行顺序
- 解析注解参数和 SpEL 表达式,得到 bizId、bizCode、bizTitle。
- 根据 beforeQueryBean/beforeQueryMethod 查询变更前快照对象。
- 执行原业务方法。
- 业务方法成功后查询变更后快照对象。
- 调用 BizAuditDiffService.diff 计算字段变化。
- 根据模块配置决定同步写、异步写或写入 Outbox。
- 业务方法异常时只记录失败操作日志,默认不记录字段变化。
9. 复杂业务手动接入方案
库存、成本、审核、反审核、支付、退款等复杂业务建议手动调用日志服务。原因是这些操作可能同时影响多个对象,AOP 无法准确识别业务边界。
9.1 入库审核接入示例
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Transactional(rollbackFor = Exception.class) public void auditStockIn(Long stockInId) { StockInAuditSnapshot before = stockInQueryService.getAuditSnapshot(stockInId); // 1. 校验状态 // 2. 审核入库单 // 3. 增加库存 // 4. 写库存流水 // 5. 更新移动加权成本 StockInAuditSnapshot after = stockInQueryService.getAuditSnapshot(stockInId); bizAuditLogService.recordChange(ReqBizAuditLogSave.builder() .moduleCode("WMS") .moduleName("仓库管理") .bizType("STOCK_IN") .bizName("入库单") .bizId(stockInId) .bizCode(after.getBillCode()) .bizTitle("入库单" + after.getBillCode()) .operateType("AUDIT") .operateName("入库审核") .beforeObj(before) .afterObj(after) .recordSnapshotFlag(true) .recordDetailFlag(true) .sourceType("ADMIN_WEB") .build()); } |
9.2 手动接入适用范围
|----------|-------------|---------------------------|
| 业务 | 接入方式 | 原因 |
| 订单审核/反审核 | 手动接入 | 涉及状态、库存占用、金额锁定、支付限制等多个对象。 |
| 入库审核/反审核 | 手动接入 | 影响库存汇总、库存明细、成本流水。 |
| 出库审核/反审核 | 手动接入 | 影响库存扣减、成本结转、批次库存。 |
| 盘点审核 | 手动接入 | 可能生成报损报溢和出入库单据。 |
| POS支付/退款 | 手动接入或Outbox | 高频操作,且涉及支付流水、库存、会员资产。 |
| 批量导入 | Outbox | 数据量大,需异步处理和失败重试。 |
10. Outbox 异步日志方案
高并发、批量导入、POS收银、价格批量调整等场景不建议在主业务线程中做大量字段对比与明细写入。推荐先在业务事务内写入 biz_audit_event,再由异步任务消费事件生成日志。
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 业务事务内: 1. 执行业务操作 2. 写 biz_audit_event,event_key 保证幂等 3. 提交事务 异步消费: 1. 查询 status=0 的日志事件 2. 标记处理中 3. 解析 payload_json 4. 查询 before/after 或直接使用载荷对象 5. 生成 biz_audit_log + biz_audit_log_detail + snapshot 6. 标记成功 7. 失败则记录错误并按指数退避重试 |
|-----------------|--------------------------------------------------------------------------|
| 字段 | 设计要求 |
| event_key | 必须唯一,防止重复生成日志。建议:moduleCode + bizType + bizId + operateType + requestId。 |
| status | 0待处理,1处理中,2成功,3失败。 |
| retry_count | 失败重试次数,超过阈值进入人工处理。 |
| next_retry_time | 支持指数退避,避免失败事件频繁重试。 |
| payload_json | 保存生成日志所需的核心参数,避免后续业务数据变化导致日志不准确。 |
11. 敏感字段脱敏与安全审计
字段级日志会保存大量业务数据,必须设计敏感字段保护机制。默认展示脱敏值,只有具备审计权限的用户才能查看明文,并且查看明文本身也要被记录。
|---------------|-----------------|--------------------------------------------------|
| 敏感类型 | 脱敏规则 | 示例 |
| 手机号 MOBILE | 保留前三后四 | 13812345678 → 138****5678 |
| 身份证 ID_CARD | 保留前六后四 | 330102199001011234 → 330102********1234 |
| 银行卡 BANK_CARD | 保留前六后四 | 6222020202020202020 → 622202********2020 |
| 姓名 NAME | 保留姓氏 | 张三 → 张* |
| 地址 ADDRESS | 保留省市区,详细地址打码 | 浙江省杭州市西湖区某某路88号 → 浙江省杭州市西湖区*** |
| 金额 AMOUNT | 默认不脱敏,但可按权限控制查看 | 仅财务/老板/审计角色可查看完整金额变化 |
11.1 查看明文二次审计
当用户点击"查看完整值"时,系统需要校验权限,并记录一条敏感字段访问日志。可复用 biz_audit_log,operateType 使用 VIEW_SENSITIVE。
|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| operateType = "VIEW_SENSITIVE" operateName = "查看敏感字段明文" bizType = "AUDIT_LOG" bizId = logId operateDesc = "查看字段:customerMobile,业务对象:订单 ORD202605210001" |
12. 前端页面与交互设计
12.1 业务详情页内嵌日志
在订单详情、商品详情、客户详情、入库单详情、出库单详情、POS订单详情等页面增加"操作记录"Tab。
|--------|--------------------|------------------------|
| 区域 | 展示内容 | 说明 |
| 操作时间线 | 时间、操作人、操作类型、操作说明 | 按时间倒序展示。 |
| 字段变化摘要 | 显示前3~5个关键字段变化 | 如"销售单价:12.50 → 13.80"。 |
| 展开明细 | 字段中文名、变更前、变更后、字段分组 | 支持金额、状态、明细商品变化。 |
| 快照对比 | 变更前快照、变更后快照 | 仅审计或管理员角色可用。 |
| 风险标识 | 高风险字段变化高亮 | 如改价、授信额度、成本价、库存数量。 |
12.2 独立审计日志中心
建议菜单放在"系统管理 / 审计日志 / 业务操作日志"。列表页面中业务 ID 字段默认隐藏,只展示业务单号、业务名称、模块、操作人、操作类型、操作时间等业务可读字段。
|----------|----------------------------------|
| 查询条件 | 说明 |
| 模块 | OMS/WMS/PMS/B2BMALL/POS/HWMS 等。 |
| 业务类型 | 订单、入库单、客户价格、商品、优惠券等。 |
| 业务单号 | orderCode、billCode、couponCode 等。 |
| 操作类型 | 新增、修改、审核、反审核、删除、启用、停用、导入、导出。 |
| 操作人 | 支持姓名或账号模糊查询。 |
| 操作时间 | 开始时间、结束时间。 |
| 字段名称 | 单价、数量、状态、客户名称等。 |
| 风险字段 | 只看命中风险字段变化的日志。 |
|----------|----------|------------------|
| 列表列名 | 是否显示 | 说明 |
| 日志ID | 默认隐藏 | 仅详情或调试模式显示。 |
| 模块 | 显示 | 如 订单中心、仓库管理。 |
| 业务类型 | 显示 | 如 客户订单、入库单。 |
| 业务单号 | 显示 | 点击进入业务详情。 |
| 业务标题 | 显示 | 如客户名称、商品名称、订单摘要。 |
| 操作类型 | 显示 | 修改/审核/反审核等。 |
| 操作人 | 显示 | 操作人名称。 |
| 字段变更数 | 显示 | 点击查看字段明细。 |
| 操作时间 | 显示 | created_at。 |
| 风险等级 | 显示 | LOW/MEDIUM/HIGH。 |
13. API 接口清单
接口路径可统一放在 /admin/audit/biz 下,业务详情页可调用按业务对象查询日志的接口。
|--------------------------------------|--------|------------|----------------------------|-----------------------------|
| 接口 | 方法 | 说明 | 主要入参 | 主要返回 |
| /admin/audit/biz/page | POST | 分页查询业务操作日志 | ReqBizAuditLogPage | RespBizAuditLogPage 分页 |
| /admin/audit/biz/detail/{logId} | GET | 查询日志详情 | logId | RespBizAuditLogDetail |
| /admin/audit/biz/list-by-biz | POST | 按业务对象查询日志 | bizType,bizId,bizCode | 日志列表 |
| /admin/audit/biz/compare/{logId} | GET | 查看快照对比 | logId | RespBizAuditSnapshotCompare |
| /admin/audit/biz/field-config/page | POST | 字段配置分页查询 | ReqBizAuditFieldConfigPage | 字段配置分页 |
| /admin/audit/biz/field-config/save | POST | 新增/修改字段配置 | ReqBizAuditFieldConfigSave | 成功/失败 |
| /admin/audit/biz/field-config/enable | POST | 启用/停用字段配置 | id,enabled | 成功/失败 |
| /admin/audit/biz/view-sensitive | POST | 查看敏感字段完整值 | logId,detailId,reason | 完整值,且记录二次审计 |
13.1 ReqBizAuditLogPage
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Data public class ReqBizAuditLogPage { private Integer pageNum; private Integer pageSize; private String moduleCode; private String bizType; private Long bizId; private String bizCode; private String operateType; private Long operatorId; private String operatorName; private String fieldName; private Boolean riskHitFlag; private String startTime; private String endTime; } |
13.2 RespBizAuditLogDetail
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| @Data public class RespBizAuditLogDetail { private Long id; private String moduleCode; private String moduleName; private String bizType; private String bizName; private Long bizId; private String bizCode; private String bizTitle; private String operateType; private String operateName; private String operateDesc; private Integer changeCount; private String operatorName; private String sourceType; private String ipAddress; private String requestId; private String createdAt; private List<RespBizAuditFieldChange> fieldChanges; } |
14. 各业务模块接入规范
14.1 B2B订货商城
|--------|----------------|------------------------------------------|
| 对象 | 操作 | 关键字段 |
| Banner | 新增/修改/启用/停用/删除 | 标题、图片、跳转类型、可见范围、价格组、客户范围、生效时间、排序 |
| 金刚区 | 新增/修改/排序/启用/停用 | 图标、标题、副标题、跳转类型、关联分类/商品/页面/URL、客户范围、价格组范围 |
| 商城分类 | 新增/修改/启用/停用 | 分类名称、图标、可见范围、价格组、客户、排序 |
| 通知公告 | 发布/修改/撤回 | 标题、内容、投放范围、生效时间、已读范围 |
| 优惠券/满减 | 新增/修改/发放/停用 | 门槛金额、优惠金额、适用商品、适用客户、有效期、支付方式限制 |
14.2 OMS订单中心
|--------|-----------------|---------------------------|
| 对象 | 操作 | 关键字段 |
| 订单主表 | 修改/审核/反审核/取消/作废 | 客户、收货信息、配送日期、订单金额、优惠金额、状态 |
| 订单明细 | 新增/修改/删除 | 商品、规格、单位、数量、单价、金额、备注 |
| 支付记录 | 支付/退款/补款 | 支付方式、支付金额、支付流水、支付状态 |
| 多退少补 | 生成/确认/退款/补款 | 差异金额、原订单金额、调整原因 |
14.3 WMS库存与成本
|--------|--------|-------------------------|
| 对象 | 操作 | 关键字段 |
| 入库单 | 审核/反审核 | 审核状态、入库数量、库存前后数量、成本前后价 |
| 出库单 | 审核/反审核 | 审核状态、出库数量、库存前后数量、成本结转金额 |
| 盘点单 | 审核/反审核 | 账面数量、实盘数量、盈亏数量、生成报损报溢状态 |
| 报损报溢 | 审核/反审核 | 损溢类型、损溢数量、损溢金额、影响库存 |
| 成本调整 | 调整/重算 | 调整前成本、调整后成本、调整原因、成本流水 |
14.4 POS门店收银
|--------|---------------|-----------------------------|
| 对象 | 操作 | 关键字段 |
| 收银订单 | 创建/支付/退款/作废 | 会员、商品明细、应收金额、实收金额、抹零金额、支付方式 |
| 挂单/取单 | 挂单/取单/删除挂单 | 购物车商品、会员、收银员、终端 |
| 改价 | 改价 | 原价、新价、改价原因、授权人 |
| 会员资产 | 充值/消费/退款/积分变动 | 余额前后值、积分前后值、优惠券核销状态 |
15. 性能、归档与运维监控
15.1 性能设计
- 字段配置加载后放入 Redis + 本地缓存,缓存 Key:audit:field-config:{moduleCode}:{bizType}。
- 普通业务日志支持异步批量写入,避免影响主交易耗时。
- 字段明细批量 insert,避免逐条写入。
- 快照 JSON 可按模块配置开关,核心业务开启,低价值配置类可只记录字段明细。
- 大对象快照要限制大小,必要时只保存业务摘要字段和关键明细。
- 日志查询必须走索引,业务详情页按 bizType + bizId 或 bizCode 查询。
15.2 归档策略
|----------|----------|-------------------|
| 数据类型 | 建议保留 | 归档策略 |
| 核心业务日志 | 3-5年 | 按月分区或定期迁移到归档表。 |
| 字段明细日志 | 3-5年 | 与主日志同步归档。 |
| 快照数据 | 1-3年 | 大体积快照可压缩存储或转对象存储。 |
| 失败事件 | 至少180天 | 用于排查日志消费失败。 |
| 敏感字段访问日志 | 5年 | 安全审计保留更长周期。 |
15.3 监控指标
|-------------|---------------------------|
| 指标 | 告警条件 |
| 日志写入失败数 | 5分钟内连续失败超过阈值。 |
| Outbox待处理积压 | 待处理数量超过设定阈值。 |
| Outbox失败事件 | 失败事件超过阈值或重试超过最大次数。 |
| 高风险字段变化 | 大额改价、授信额度调整、库存成本调整等触发告警。 |
| 敏感字段查看次数 | 短时间频繁查看手机号、地址、身份证等触发安全告警。 |
16. 测试用例与验收标准
16.1 核心测试用例
|--------|-----------|---------------|------------------------------------------|
| 编号 | 场景 | 操作 | 预期结果 |
| TC001 | 普通字段修改 | 修改客户名称 | 生成一条主日志和一条字段明细,记录旧客户名称和新客户名称。 |
| TC002 | 金额字段修改 | 修改客户商品价格 | 按统一小数位展示 old/new,字段中文名为销售单价。 |
| TC003 | 字典字段修改 | 订单状态从待审核改为已审核 | oldDisplayValue=待审核,newDisplayValue=已审核。 |
| TC004 | 敏感字段修改 | 修改客户手机号 | 默认展示脱敏值,原始值受权限控制。 |
| TC005 | 明细行修改 | 修改订单明细数量和单价 | 按商品名称展示数量、单价、金额变化。 |
| TC006 | 新增明细行 | 订单新增一个商品 | changeType=ADD,变更前为空,变更后为商品信息。 |
| TC007 | 删除明细行 | 订单删除一个商品 | changeType=REMOVE,变更前为商品信息,变更后为空。 |
| TC008 | 审核操作 | 入库单审核 | 记录状态变化、库存变化、成本变化。 |
| TC009 | 业务异常 | 修改商品失败并回滚 | 不生成字段明细;可选记录失败操作主日志。 |
| TC010 | Outbox重试 | 模拟日志事件消费失败 | 状态变失败,记录错误,按重试策略再次消费。 |
| TC011 | 查看敏感明文 | 有权限用户查看手机号完整值 | 返回明文,并新增 VIEW_SENSITIVE 审计日志。 |
| TC012 | 无权限查看敏感明文 | 普通用户查看手机号完整值 | 拒绝访问,并记录风险操作。 |
16.2 验收标准
- 任意接入模块完成新增、修改、审核、反审核操作后,能在业务详情页查看日志。
- 字段明细必须显示字段中文名、变更前值、变更后值、操作人、操作时间。
- 状态、枚举、字典字段不能只显示数字编码,必须显示中文含义。
- 金额、单价、数量字段必须按照升鲜宝统一小数位规则展示。
- 手机号、身份证、地址等敏感字段默认脱敏展示。
- 订单明细、入库明细、出库明细等集合数据必须能记录新增、删除、修改。
- 核心业务日志写入失败时不能悄悄丢失,必须有错误记录、重试或告警。
- 日志列表中 id、bizId、detailId 等技术ID默认隐藏,仅在详情或调试模式展示。
- 日志查询性能满足业务详情页 1 秒内返回最近日志列表。
17. 实施计划与开发任务拆分
|----------------|----------|--------------------------------------------------|----------------------|
| 阶段 | 周期建议 | 任务 | 交付物 |
| 第一阶段:基础组件 | 1-2周 | 建表、Entity/Dao/Service、字段配置、DiffEngine、主日志+字段明细写入 | 可通过手动调用记录字段变化 |
| 第二阶段:注解接入 | 1周 | BizAuditLog 注解、AOP、SpEL解析、before/after查询适配 | 商品、客户、价格、B2B配置类可自动记录 |
| 第三阶段:业务详情页 | 1周 | 日志列表、日志详情、字段变化展示、业务详情页Tab | 前端可查看字段级变化 |
| 第四阶段:核心业务接入 | 2-3周 | OMS订单、WMS库存成本、POS支付退款手动接入 | 核心业务审计可追溯 |
| 第五阶段:安全与风控 | 1-2周 | 敏感字段脱敏、查看明文二次审计、高风险字段标识、基础告警 | 形成审计与风控基础能力 |
| 第六阶段:Outbox与归档 | 1-2周 | Outbox事件、异步消费、失败重试、归档策略、监控指标 | 高并发和大批量日志稳定运行 |
17.1 开发优先级建议
- 先做 biz_audit_log、biz_audit_log_detail、biz_audit_field_config 三张核心表。
- 先跑通手动调用 recordChange,确保字段级差异计算正确。
- 再做 AOP 注解,降低普通业务接入成本。
- 先接入客户价格、商品资料、B2B商城配置、订单修改四类业务。
- 再接入订单审核、入库审核、出库审核、盘点审核等核心业务。
- 最后增加 Outbox、敏感字段明文查看、风控告警和日志归档。
18. 附录:枚举、初始化数据、代码样例
18.1 操作类型枚举
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public enum BizAuditOperateTypeEnum { CREATE("CREATE", "新增"), UPDATE("UPDATE", "修改"), DELETE("DELETE", "删除"), ENABLE("ENABLE", "启用"), DISABLE("DISABLE", "停用"), AUDIT("AUDIT", "审核"), UNAUDIT("UNAUDIT", "反审核"), CANCEL("CANCEL", "取消"), VOID("VOID", "作废"), IMPORT("IMPORT", "导入"), EXPORT("EXPORT", "导出"), ADJUST("ADJUST", "调整"), SUBMIT("SUBMIT", "提交"), PAY("PAY", "支付"), REFUND("REFUND", "退款"), SIGN("SIGN", "签收"), VIEW_SENSITIVE("VIEW_SENSITIVE", "查看敏感字段明文"); } |
18.2 来源类型枚举
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public enum BizAuditSourceTypeEnum { ADMIN_WEB("ADMIN_WEB", "后台管理端"), B2B_MALL("B2B_MALL", "B2B订货端"), MOBILE("MOBILE", "手机端"), POS("POS", "门店POS"), API("API", "开放接口"), JOB("JOB", "定时任务"), MQ("MQ", "消息队列"), SYSTEM("SYSTEM", "系统内部"); } |
18.3 字段配置初始化示例:客户价格
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| INSERT INTO biz_audit_field_config (id, module_code, biz_type, entity_name, field_code, field_name, field_type, field_group, log_flag, sensitive_flag, mask_rule, dict_type, value_converter, risk_field_flag, sort_no, enabled) VALUES (1, 'B2BMALL', 'CUSTOMER_PRICE', 'CustomerPriceAuditSnapshot', 'customerName', '客户名称', 'STRING', '基本信息', 1, 0, NULL, NULL, NULL, 0, 10, 1), (2, 'B2BMALL', 'CUSTOMER_PRICE', 'CustomerPriceAuditSnapshot', 'goodsName', '商品名称', 'STRING', '商品信息', 1, 0, NULL, NULL, NULL, 0, 20, 1), (3, 'B2BMALL', 'CUSTOMER_PRICE', 'CustomerPriceAuditSnapshot', 'salePrice', '销售单价', 'DECIMAL', '价格信息', 1, 0, NULL, NULL, 'decimalAuditValueConverter', 1, 30, 1), (4, 'B2BMALL', 'CUSTOMER_PRICE', 'CustomerPriceAuditSnapshot', 'minOrderQty', '最小起订量', 'DECIMAL', '订货规则', 1, 0, NULL, NULL, 'decimalAuditValueConverter', 0, 40, 1), (5, 'B2BMALL', 'CUSTOMER_PRICE', 'CustomerPriceAuditSnapshot', 'enabled', '启用状态', 'BOOLEAN', '状态信息', 1, 0, NULL, NULL, 'booleanAuditValueConverter', 0, 50, 1); |
18.4 字段变化展示示例
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| { "moduleCode": "B2BMALL", "moduleName": "B2B订货商城", "bizType": "CUSTOMER_PRICE", "bizName": "客户商品价格", "bizCode": "CP202605210001", "operateType": "UPDATE", "operateName": "修改客户价格", "operatorName": "管理员", "changeCount": 3, "fieldChanges": { "fieldCode": "salePrice", "fieldName": "销售单价", "oldDisplayValue": "12.50", "newDisplayValue": "13.80", "riskFieldFlag": true }, { "fieldCode": "minOrderQty", "fieldName": "最小起订量", "oldDisplayValue": "1", "newDisplayValue": "5" }, { "fieldCode": "enabled", "fieldName": "启用状态", "oldDisplayValue": "停用", "newDisplayValue": "启用" } } |
18.5 最终落地要求总结
|------------------------------------------------------------------------------------------------------------------------------------------------|
| 落地结论 升鲜宝业务通用日志组件必须按字段级前后值日志标准建设。它不是附属功能,而是系统审计、风控、数据安全、业务追溯、责任划分的基础设施。开发时要坚持"主日志记录一次动作,明细日志记录每个字段变化,快照保留完整上下文,配置控制字段展示与脱敏,核心业务保证日志不丢失"的原则。 |