feat(audit): 提升可读性 —— 操作对象补全物料名、详情降噪、字段字典补齐

业务方反馈两点:操作对象太干瘪、详情快照噪音太多。

1. 操作对象补全为「SKU - 物料名称 (规格型号)」

   库里 target_name 大多只存了 SKU('0000002270'),入库类甚至存的是内部
   标识('stock_buy ID:1667'),业务人员完全看不懂。

   补全走两条路径,**正确性优先**:
     · 快照里的 base_id(CREATE/DELETE 的库存行)—— 权威,零歧义
     · target_id 唯一命中一张股票表 —— 实测与上一条 1230/1230 完全一致

   ★ 撞号一律放弃:target_id 同时命中 2 张股票表时解出的 base_id
     24/27 是错的、命中 3 张时 44/44 全错。补一个**错的**物料名比不补更糟 ——
     那是"看起来完全可信的错误答案",业务方会照着它去找不相干的物料。
     实测 300 条撞号行 0 条被补,300 条唯一命中行全部补上。

   target_name 原值保留(target_keyword 搜索仍按它匹配),新增 target_display
   供展示。前端去掉灰显的 #target_id —— 业务人员不需要看数据库主键。

2. 详情快照降噪

   · 前端过滤空值:null / '' / [] / {} / '-'。判据**严格**:false 和 0
     不算空(`is_returned: false`、`quantity: 0` 是明确的业务事实,
     用真值判断会把它们一起吃掉,那是在篡改数据)。
   · 过滤纯技术字段:id / created_at / updated_at / target_id / module_name
     及 pgvector 的 `*embedding`(单条可达数 KB)。清单由后端下发
     (labels.hiddenFields),前端不硬编 —— 它会随新表增长,再存一份必然漂移。
     embedding 类按后缀拦截,新表加向量列不必改代码。
   · 变更对比表过滤「等于没改」的行(null ↔ 空串)。

3. 字段字典补齐:168 项

   实测快照里出现过但字典没有的字段全部补上(buyer_email / currency /
   in_date / exchange_rate / dosage / loss_rate / child / parent /
   production_* / return_* / *_threshold 等 60+ 个)。
   现在快照字段缺中文名的数量为 **0** —— 详情页不会再裸露英文。

4. 摘要优化

   · changes_of 丢掉无意义变更(null ↔ 空串)。库里 98 条记录带这种变更,
     写进摘要就是「备注:空→空」,纯噪音还挤占截断长度。
   · CREATE/DELETE 摘要改为核心属性**按槽位取值**:
         新增(入库):入库数量 10 件、库位 ZZTEST
     而不是「新增:SKU、base、状态… 等 29 个字段」。
   · 槽位只有数量与位置两个,**不含物料** —— 物料由「操作对象」列承担,
     同一屏里再来一遍是重复。数量字段也按槽位只取一个:
     in_quantity / stock_quantity / available_quantity 值往往相同,
     取三个会得到三个一样的数字。
   · 数字去掉无意义的 .0(10.0 → 10);单位取自物料主数据,
     纯数字的占位单位(库里有一批 unit='1')丢弃。

验证:33 + 16 项断言全过,含「补全的物料与快照 base_id 零冲突」「撞号行
一律不补」「快照字段缺中文名 0 个」「摘要无空→空」「导出口径与列表一致」;
vue-tsc --noEmit 与 vite build 均 exit=0。
日报回归:三天附件 490.5K / 193.4K / 10.3K,6 列结构未变。
This commit is contained in:
yueli
2026-09-28 10:19:58 +08:00
parent 6a9e41f53c
commit 46aec0b954
4 changed files with 436 additions and 37 deletions

View File

@ -200,8 +200,130 @@ FIELD_LABELS = {
'target_code': '目标编码',
'user_agent': '浏览器标识',
'ip_address': 'IP地址',
'visibility_level': '可见级别',
'is_archived': '是否归档',
# --- 采购 / 入库(stock_* 表)---
# 这几个是实测缺失最多的:采购入库快照整表展开,缺一个就漏一排英文。
'buyer_email': '采购邮箱',
'in_date': '入库日期',
'currency': '币种',
'exchange_rate': '汇率',
'inspection_status': '质检状态',
'inspection_report': '质检报告',
'inspection_report_link': '质检报告链接',
'quality_status': '质检状态',
'quality_report_link': '质检报告链接',
'arrival_photo': '到货照片',
'product_photo': '成品照片',
'global_print_id': '全局打印ID',
'base': '基础物料',
'detail_link': '详情链接',
'original_link': '原始链接',
'supplier_link': '供应商链接',
'request_id': '申请ID',
'purchase_request': '采购申请',
'order_id': '订单ID',
'is_ordered': '是否已下单',
'sale_price': '销售价',
'location': '库位',
'common_name': '通用名',
'material': '物料',
'images': '图片',
# --- 生产(半成品/成品)---
'work_order_code': '工单号',
'bom_code': 'BOM编号',
'production_manager': '生产负责人',
'production_date': '生产日期',
'production_start_time': '生产开始时间',
'production_end_time': '生产结束时间',
'production_time_range': '生产时间段',
'raw_material_cost': '原材料成本',
'manual_cost': '人工成本',
# --- BOM ---
'loss_rate': '损耗率',
'dosage': '用量',
'parent': '父件',
'child': '子件',
'version': '版本',
'bom_remark': 'BOM备注',
'child_bom_no': '子BOM编号',
'child_bom_version': '子BOM版本',
# --- 退回 / 借还 ---
'return_signature': '归还签名',
'return_location': '归还库位',
'borrow_signature': '借出签名',
'current_holder_id': '当前持有人ID',
'current_holder_name': '当前持有人',
'borrower_id': '借用人ID',
'dispatch_operator': '发放操作人',
'source_return_id': '来源退回ID',
# --- 预警设置 ---
'yellow_threshold': '黄色阈值',
'red_threshold': '红色阈值',
'yellow_emails': '黄色预警邮箱',
'red_emails': '红色预警邮箱',
'last_notified_at': '上次通知时间',
# --- 审批 ---
'approver_name': '审批人',
'approval_status': '审批状态',
# --- 盘点 / 扫码(长尾,实测仅个位数条记录,但不补就会在详情里裸露英文)---
'diff_qty': '差异数量',
'stock_qty': '库存数量',
'scan_time': '扫码时间',
'session_id': '会话标识',
'uuid': '唯一标识',
'image_url': '图片地址',
'user_id': '用户ID',
'type': '类型',
}
# =============================================================================
# 6. 详情快照里**不展示**的纯技术字段
#
# ★ 判据:这些字段对业务人员零信息量,且常常极端冗长。
# 典型是 image_embeddings 表的 `embedding`(1536 维向量),一条快照里
# 塞进去就是几 KB 的乱码;`target_id` / `module_name` 则是审计层的元数据,
# 被早期快照采集误带进了业务对象。
#
# ★ 只收「纯技术」的。链接/图片**不收**:那是业务内容,用户点进去是有用的;
# 它们在"变更对比"里已经被 IGNORED_CHANGE_FIELDS 单独处理了。
#
# ★ 这份清单放在后端而不是前端硬编:它会随新表增长,前端再存一份必然漂移。
# 前端经 GET /audit/labels 的 hiddenFields 取。
# =============================================================================
HIDDEN_SNAPSHOT_FIELDS = frozenset({
'id',
'created_at',
'updated_at',
'tenant_id',
'target_id', # 审计层元数据,不是业务字段
'module_name',
'embedding', # pgvector 向量列,单条可达数 KB
'img_embedding',
'arrival_image_embedding',
'qc_report_image_embedding',
'image_embedding',
})
def is_hidden_snapshot_field(key):
"""
该字段是否属于「不该在详情快照里展示」的纯技术字段。
★ 除名单本身,还按**后缀**拦截 `*embedding`:向量列在各表命名不一
(img_embedding / arrival_image_embedding / qc_report_image_embedding…),
逐个登记必然漏。新表加向量列时不必再改这里。
"""
k = str(key or '').strip().lower()
return k in HIDDEN_SNAPSHOT_FIELDS or k.endswith('embedding')
def field_label(key):
"""字段名 → 中文;未命中时原样返回字段名"""
@ -552,4 +674,6 @@ def labels_payload():
# 否则会「下拉显示『入库』、表格显示『库存管理』」。
# ★ 含英文历史值,前端不再自带一份 moduleMap。
'moduleDisplay': module_display_map(),
# 详情快照里不展示的纯技术字段(前端过滤用,见 HIDDEN_SNAPSHOT_FIELDS)
'hiddenFields': sorted(HIDDEN_SNAPSHOT_FIELDS),
}