| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- # -*- coding: utf-8 -*-
- """公开模式脱敏: 隐藏核心参数/经验值/阈值 (用户 2026-08-23 令). 默认公开; 内部分析设 WINDCMS_INTERNAL=1 才显示全部.
- 原则: 数据 (实测值/倍数/台号) 可见, 判据的数值门槛不可见; 判据名 (G1..G6/绝对锚/污染门) 可见, 其阈值替换为 ▪."""
- import os, re
- PUBLIC = os.environ.get('WINDCMS_INTERNAL') != '1'
- # 阈值模式: 比较符后面的数字 (含单位), p90 倍数, CV 界, bin 容差, 门槛词后数字
- _PATS = [
- (re.compile(r'(≥|>=|≤|<=|>|<|>|<)\s*=?\s*[0-9][0-9.,]*\s*(×|%|µs|us|ms|bin|dB|Hz|kHz|窗|条|台|倍|σ)?'), r'\1▪'),
- (re.compile(r'(阈值?|阈|门槛|门限|判据|容差|界)\s*[:=:]?\s*[0-9][0-9.]*\s*(×|%|µs|bin|Hz|倍)?'), r'\1▪'),
- (re.compile(r'[0-9][0-9.]*\s*×\s*(同\s*shard\s*)?(fleet\s*)?p90'), '▪×p90'),
- (re.compile(r'p90\s*[0-9][0-9.]*'), 'p90 ▪'),
- (re.compile(r'CV\s*(≤|<=|<)\s*[0-9][0-9.]*%?'), 'CV≤▪'),
- (re.compile(r'(相干|coh\w*|群延迟|gd|相位合成向量 R|R|峰/本底|峰/邻域|选择性|结构度|个体|持续|占比|share|weak_over_p90|min_over_p90|min_share)\s*(≥|>=|≤|<=|>|<|=)\s*[0-9][0-9.]*\s*(×|%|µs|bin|窗)?', re.I), r'\1\2▪'),
- (re.compile(r'(coh_shared|gd_max_us|r_min|coh_independent|min_share|min_over_p90|weak_over_p90)\s*=\s*[0-9][0-9.]*'), r'\1=▪'),
- (re.compile(r'±\s*[0-9][0-9.]*\s*(bin|Hz|%)'), '±▪\\1'),
- (re.compile(r'(\d+(?:\.\d+)?)\s*(bin)\b'), '▪ \\2'),
- # 特征频率一律隐藏 (用户 2026-08-23): 任何 数值+Hz/kHz/阶, 频带 a–b kHz, @xxxHz, 齿数 (LSS×92 类)
- (re.compile(r'\d+(?:\.\d+)?\s*[–\-~]\s*\d+(?:\.\d+)?\s*(k?Hz)'), '▪–▪ \\1'),
- (re.compile(r'@\s*\d+(?:\.\d+)?\s*(k?Hz)'), '@▪\\1'),
- (re.compile(r'\d+(?:\.\d+)?\s*(k?Hz|赫兹)'), '▪ \\1'),
- (re.compile(r'(\d+(?:\.\d+)?)\s*阶'), '▪阶'),
- (re.compile(r'(LSS|C2|HSS|IMS|转频|环齿|小齿轮齿)\s*×\s*\d+'), '\\1×▪'),
- (re.compile(r'(BPFI|BPFO|BSF|FTF|GMF\d?|1P|2P|3P)\s*[==]\s*\d+(?:\.\d+)?'), '\\1=▪'),
- ]
- _HZ_KEYS = ('hz', 'peak_hz', 'freq', 'frequency', 'f_e_hz', 'fgen2_hz', 'comb_spacing_med_hz', 'line_hz')
- _PHRASES = [
- ('峰/邻域≥3 才算有线', '峰/邻域达到判据才算有线'), ('G2 有线阈 3.0', 'G2 有线判据'), ('(峰/邻域≥3 才算有线, 定级仍须六闸+绝对锚)', '(判线走六道闸与绝对锚)'),
- ]
- def redact(text):
- if not PUBLIC or text is None:
- return text
- s = str(text)
- for a, b in _PHRASES:
- s = s.replace(a, b)
- for pat, rep in _PATS:
- s = pat.sub(rep, s)
- return s
- def redact_obj(o):
- """递归脱敏 dict/list 里的字符串 (插件返回的 data/text/reason)."""
- if not PUBLIC:
- return o
- if isinstance(o, str):
- return redact(o)
- if isinstance(o, dict):
- return {k: ('▪' if str(k).lower() in _HZ_KEYS else redact_obj(v)) for k, v in o.items()}
- if isinstance(o, list):
- return [redact_obj(v) for v in o]
- return o
- def redact_always(text):
- """**强制**脱敏, 不看 PUBLIC 开关。
- 用于**客户版交付物** — 客户版的脱敏不能依赖 WINDCMS_INTERNAL 环境变量:
- 谁在内部模式下跑一次 build, 客户版 HTML 就会连特征频率一起写进去 (2026-08-28 自逮)。
- 交付物的脱敏必须是**结构性的**, 不是配置性的。
- """
- if text is None:
- return text
- s = str(text)
- for a, b in _PHRASES:
- s = s.replace(a, b)
- for pat, rep in _PATS:
- s = pat.sub(rep, s)
- return s
|