网页版选型改版 (仅网页版, 桌面版待同步)
- 新增零件号实时条: 边选边拼, 已确定片段实心高亮, 未定片段虚化, 进度条随选推进;
浅色配色 (初版深色块与页面不协调, 已改); 选满且唯一命中时整块转青色实心
- 技术方案: 40 系列中仅 KC 有编码模板 → 改用「当前候选型号的最长公共前缀」,
已验证 40/40 系列前缀单调增长(不回退)
- 参数选择由下拉菜单改为可视化选项块: 悬停抬升+青色投影, 选中青色填充+✓,
不可选取值置灰(虚线+划线, 不消失, 让用户看到选项存在但当前组合不适用),
再点一次取消; 有说明的显示代号+说明, 纯数值用紧凑文字块
- 参数联动仍复用 allowedOptions 规则引擎, 语义未变, 只改渲染
参数组合补全
- 原逻辑: 必须有真实数模才能生成型号 → 库里没有的组合选不出来也出不了号
- infer-sku-template.py 从已有型号反推编码模板, 得 23 个系列 100% 可复现
- verify-templates.py 作为门禁: 用模板重渲染每个已有型号必须逐字一致 ——
该检查抓出 ULM/ULP 行程补零位数写错(2位写3位), 否则会生成 3000 个不存在的零件号
- gen-missing-combos.py 补全 8 个系列共 3157 个型号 (全库 54136 → 57293)
- 占位数模: 3157 个型号共用 1 个 PLACEHOLDER.step(4.5KB), 构建器按路径打包只存一份,
数据包仅 2.10GB → 2.14GB (中途用硬链接方案踩坑: NTFS 上限 1024, 超出退化为拷贝白占 500MB)
- _pending_models.csv 输出待补数模清单, 供 AI 补完后替换 step_file
新增脚本: infer-sku-template / verify-templates / quantify-combo-gap /
gen-missing-combos / gen-placeholder-step / check-combo-unique
新增文档: docs/更改说明-v2.1.md
发现未处理 (已记录在更改说明):
- 夹紧缸 9 系列一组参数对应多型号 (310 个型号, 编码含参数外尾缀, 旧码/新码混用)
- UAGP/LAGP 参数顺序与官方编码结构不符 (传感器应在转角之前)
- 17 个系列因模板不干净未补全 (宁可少补, 不生成可能不存在的零件号)
Co-Authored-By: Claude Code <noreply@anthropic.com>
110 lines
3.4 KiB
Python
110 lines
3.4 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
编码模板反推 v2 —— 支持零填充与空值占位符。
|
||
|
||
v1 的 explain() 贪心匹配"非零填充", 把 ULP50040AEK0 的 "040" 拆成 字面量"0" + 值"40",
|
||
产生大量假阴性。真实规则是:
|
||
- 数值参数常带前导零 (stroke=40 -> "040")
|
||
- 枚举参数为空时, 编码里用 '_' 占位 (UAGP075_K045WS0)
|
||
|
||
本脚本对每个参数尝试 {原值, 各前导零位数, '_'占位} 的所有组合, 取能对齐的;
|
||
若某系列的"字面量序列"完全一致 → 可模板化。
|
||
|
||
用法: PYTHONUTF8=1 python infer-sku-template.py
|
||
"""
|
||
import json, io, collections, sys
|
||
|
||
CAT = r'D:/开发/OnebotCatalog/release/web_2026.08/catalog.json'
|
||
d = json.load(io.open(CAT, encoding='utf-8-sig'))
|
||
|
||
MAXPAD = 4
|
||
|
||
|
||
def align_all(code, params, values):
|
||
"""返回全部可能的字面量序列 (元组)。每个参数尝试: 原值 / 前导零 / '_'占位。"""
|
||
results = []
|
||
|
||
def dfs(i, pos, lits):
|
||
if i == len(params):
|
||
results.append(tuple(lits + [code[pos:]]))
|
||
return
|
||
pc = params[i]
|
||
v = str(values.get(pc, ''))
|
||
cands = []
|
||
if v == '':
|
||
cands = ['_', ''] # 空值: 占位符或什么都不写
|
||
else:
|
||
cands = [v] + ['0' * z + v for z in range(1, MAXPAD + 1)]
|
||
seen = set()
|
||
for pat in cands:
|
||
if pat in seen:
|
||
continue
|
||
seen.add(pat)
|
||
if pat == '':
|
||
dfs(i + 1, pos, lits + [''])
|
||
continue
|
||
idx = code.find(pat, pos)
|
||
if idx >= 0:
|
||
dfs(i + 1, idx + len(pat), lits + [code[pos:idx]])
|
||
dfs(0, 0, [])
|
||
return results
|
||
|
||
|
||
def render_template(params, lits):
|
||
"""字面量序列 -> 模板串。空值参数渲染为 '_'。"""
|
||
out = ''
|
||
for i, pc in enumerate(params):
|
||
out += lits[i] + '{' + pc + '}'
|
||
out += lits[len(params)]
|
||
return out
|
||
|
||
|
||
good, bad = [], []
|
||
for s in d['series']:
|
||
vs = s.get('variants') or []
|
||
if not vs:
|
||
continue
|
||
params = [p['code'] for p in s['parameters']]
|
||
lit_sets = []
|
||
unaligned = 0
|
||
for v in vs:
|
||
alts = align_all(v['modelCode'], params, v['params'])
|
||
if not alts:
|
||
unaligned += 1
|
||
continue
|
||
lit_sets.append(alts)
|
||
|
||
if not lit_sets:
|
||
bad.append((s['code'], len(vs), '完全无法对齐', [], 0))
|
||
continue
|
||
|
||
# 统计"出现在最多变体里"的字面量序列 (每个变体取任一可对齐解)
|
||
cnt = collections.Counter()
|
||
for alts in lit_sets:
|
||
for a in set(alts):
|
||
cnt[a] += 1
|
||
top, hit = cnt.most_common(1)[0]
|
||
rate = hit / len(vs) * 100
|
||
|
||
if rate >= 99.5 and unaligned == 0:
|
||
good.append((s['code'], len(vs), rate, render_template(params, top), len(cnt)))
|
||
else:
|
||
bad.append((s['code'], len(vs), f'{rate:.1f}% ({len(cnt)} 种)', cnt.most_common(3), unaligned))
|
||
|
||
print('=' * 96)
|
||
print(f'可模板化的系列 ({len(good)} 个)')
|
||
print('=' * 96)
|
||
for code, n, rate, tpl, k in sorted(good):
|
||
print(f'{code:7s} {n:6d} 变体 {rate:6.1f}% {tpl}')
|
||
|
||
print()
|
||
print('=' * 96)
|
||
print(f'不可模板化的系列 ({len(bad)} 个)')
|
||
print('=' * 96)
|
||
for code, n, why, tops, un in sorted(bad, key=lambda x: -x[1]):
|
||
print(f'{code:7s} {n:6d} 变体 {why}' + (f' 未对齐 {un}' if un else ''))
|
||
for lits, c in tops[:3]:
|
||
print(f' {c:5d}× {lits}')
|
||
|
||
print(f'\n合计: 可模板化 {len(good)} / 共 {len(good) + len(bad)} 个系列')
|