From 0cf48241f988c30f67589b7ae263edaf27725ad4 Mon Sep 17 00:00:00 2001 From: lowmaster-chen <1119780489@qq.com> Date: Tue, 23 Jun 2026 15:33:53 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A6=96=E9=A1=B5=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E6=9D=A1=E7=94=A8=E6=88=B7=E5=90=8D=E7=A7=B0=E6=89=93?= =?UTF-8?q?=E7=A0=81=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改了什么:将 savings-feed 用户名脱敏改为 PRD 规则;有昵称按长度打码,无昵称真实用户显示 用户*****后两位;种子和兜底继续合成多样化脱敏名。 为什么改:避免旧规则出现 用户********xxx,与产品要求的用户标识打码格式保持一致。 验证方式:本地请求 /api/v1/platform/savings-feed?limit=8,确认返回 用户*****xx、首字+**、首字+***+末 等新格式。 --- app/repositories/ops_marquee.py | 69 +++++++++++++++++---------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/app/repositories/ops_marquee.py b/app/repositories/ops_marquee.py index 87128b7..bb4c35f 100644 --- a/app/repositories/ops_marquee.py +++ b/app/repositories/ops_marquee.py @@ -6,13 +6,13 @@ feed 取最近的成功且省到钱的比价记录(脱敏用户名);不足 N 条 种子是「生成规则」:用户名可空(空→随机合成脱敏名,避开同屏撞名)、金额是区间(每次随机取值)、 feed 公平随机抽取(不看 sort_order),所有启用种子都有机会露出。 -脱敏名规则(全 feed 统一,按字符算、中英文皆适用): - - 有昵称 → 昵称脱敏:≥3 字「首+隐藏字数个星+末」(省钱小能手→省***手 / SaveKing→S******g), - 2 字「首+星」(阿强→阿*),1 字「用户+该字」(喵→用户喵); - - 没昵称 → 合成一个假名(见下)再脱敏,少数露「用户+id 后 3 位」(用户********618)。 -真实条:设过昵称的按真实昵称脱敏;当前多数用户没设昵称,直接展示「用户****」会清一色,故给它们合成 -假名——后期真实昵称多了,真实条会直接用真实昵称,合成占比自然下降。种子 / 兜底同样合成、避开同屏撞名; -种子留空 / 旧「用户****xxx」模板名一律重新合成(自愈历史种子,无需迁移)。 +脱敏名规则(对齐 PRD「用户标识打码规则」,按字符数算、中英文皆适用): + - 有昵称 → 昵称脱敏:n≥5「首+***+末」(AAA省钱小能手→A***手 / Micky_ 毛→M***毛), + n=4「首+**+末」(你好不好→你**好),n≤3「首+**」(小确呀→小** / 阿强→阿** / 喵→喵**); + - 没昵称(真实用户)→「用户」+5星+id 后 2 位(用户*****08),按 user_id 稳定。 +真实条按上述 PRD 规则脱敏。种子 / 兜底无真实用户(无昵称/无 id,套不上「用户*****+id后2位」), +仍本地合成多样假名再走同一套昵称脱敏(避开同屏撞名),让横条不至清一色;种子留空 / 旧「用户****xxx」 +模板名一律重新合成(自愈历史种子,无需迁移)。 假名合成:纯本地语料**组合生成**(中文姓池×名字池 拼真名 / 中文网络昵称 / 英文昵称 / 英文名带数字尾), 组合空间上万、中文为主英文为辅,脱敏后像真实异质用户群。**真实条按 `user_id` 播种确定性合成**(同一用户 每次展示恒定、不同用户各异),刷新/翻页不变脸;种子 / 兜底用运行时随机源出多样填充。不依赖外部库。 @@ -44,12 +44,12 @@ _REAL_ROWS_TTL_SECONDS = 30 _REAL_ROWS_FETCH_CAP = 600 # 一次多取些,够 limit≤30 去重后取数;命中缓存后复用 _real_rows_cache: dict = {"at": None, "rows": None} -# ===== 用户标识脱敏 + 没真实昵称时的假名合成 ===== -# 脱敏规则(中英文皆适用,按字符算):有昵称→≥3 字「首+隐藏字数个星+末」(省钱小能手→省***手 / -# SaveKing→S******g)、2 字「首+星」(阿强→阿*)、1 字「用户+该字」(喵→用户喵);没昵称→用户+8星+id 后 3 位。 -# 没真实昵称时(当前多数用户 / 种子 / 兜底)合成假名:本地姓/名字池组合出真名 + 网络昵称语料(中/英) -# 混播再走同一套脱敏,像真实异质用户群。后期真实昵称多了,真实条直接用真实昵称,这里占比自然下降。 -_ANON_STARS = "*" * 8 # 无昵称匿名串固定 8 星(用户********xxx) +# ===== 用户标识脱敏(对齐 PRD) + 种子无真实昵称时的假名合成 ===== +# 脱敏规则(按字符数,中英文皆适用):有昵称→n≥5「首+***+末」、n=4「首+**+末」、n≤3「首+**」; +# 没昵称(真实用户)→「用户」+5星+id 后 2 位(用户*****08)。 +# 种子/兜底没有真实用户(无昵称/无 id),本地用姓/名字池组合真名 + 网络昵称语料(中/英)合成假名, +# 再走同一套昵称脱敏,让横条像真实异质用户群、不清一色(PRD 只约束真实用户,种子是运营填充)。 +_ANON_STARS = "*" * 5 # 「用户」匿名串固定 5 星(用户*****xx,对齐 PRD) # 中文真名组合池:姓(百家姓常见 ~100)× 名字单字(~120)拼「姓 + 1~2 字」,组合空间上万。 # 脱敏后只露「姓*」或「姓*末」,故无需真实姓名库——组合够多即看着各异(替代原 Faker zh_CN)。 @@ -86,19 +86,23 @@ _NICK_EN = [ def _mask_nickname(nick: str) -> str: - """昵称脱敏(按字符,中英文皆可):≥3 字→首+(隐藏字数个)星+末(省钱小能手→省***手 / SaveKing→S******g); - 2 字→首+星(阿强→阿*);1 字→「用户」+该字(喵→用户喵)。调用方需保证 nick 已 strip 且非空。""" + """昵称脱敏(按字符数,中英文皆可,对齐 PRD「用户标识打码规则」): + n≥5 → 首+***+末(AAA省钱小能手→A***手 / Micky_ 毛→M***毛); + n=4 → 首+**+末(你好不好→你**好 / Lady→L**y); + n≤3 → 首+**(小确呀→小** / 阿强→阿** / 喵→喵** / Lay→L**)。 + 调用方需保证 nick 已 strip 且非空。""" n = len(nick) - if n >= 3: - return nick[0] + "*" * (n - 2) + nick[-1] - if n == 2: - return nick[0] + "*" - return "用户" + nick + if n >= 5: + return nick[0] + "***" + nick[-1] + if n == 4: + return nick[0] + "**" + nick[-1] + return nick[0] + "**" -def _anon_by_id(user_id: int) -> str: - """没昵称匿名:固定「用户」+ 8 星 + id 后 3 位(不足 3 位前补 0):用户********618。""" - return "用户" + _ANON_STARS + f"{user_id % 1000:03d}" +def _mask_anon(user_id: int) -> str: + """没昵称(真实用户,对齐 PRD):「用户」+ 5 星 + id 后 2 位(不足 2 位前补 0):用户*****08。 + 注:user_id 为整数,「id 后 2 位」取末两位数字(PRD 示例「用户*****x8」的 x 为占位符)。""" + return "用户" + _ANON_STARS + f"{user_id % 100:02d}" def _synth_full_name(rng: random.Random) -> str: @@ -118,27 +122,24 @@ def _synth_full_name(rng: random.Random) -> str: def _synth_masked_name(rng: random.Random) -> str: - """合成一条「已脱敏」假名(供没真实昵称的真实用户 / 种子 / 兜底用)。用传入 rng 决定一切随机。""" + """合成一条「已脱敏」假名(供种子 / 兜底用,真实用户已改走 PRD 规则)。用传入 rng 决定一切随机。""" full = _synth_full_name(rng).strip() - return _mask_nickname(full) if full else "用户" + _ANON_STARS + f"{rng.randint(0, 999):03d}" + return _mask_nickname(full) if full else "用户" + _ANON_STARS + f"{rng.randint(0, 99):02d}" def _mask_real(nickname: str | None, user_id: int) -> str: - """真实用户脱敏:设过昵称→昵称脱敏(中英文皆可);没昵称→**按 user_id 播种确定性合成假名**再脱敏 - (同一用户每次展示恒定、刷新不变脸,不同用户各异),避免无昵称用户清一色「用户****xxx」; - 少数(~15%)露「用户+真实 id 后 3 位」。当前多数用户没昵称,后期真实昵称多了占比下降。""" + """真实用户脱敏(对齐 PRD「用户标识打码规则」):设过昵称→昵称脱敏(中英文皆可); + 没昵称→「用户」+5星+id 后 2 位(用户*****08),按 user_id 稳定、刷新不变脸。""" nick = (nickname or "").strip() if nick: return _mask_nickname(nick) - # 关键:按 user_id 播种本地 rng → 同一用户的合成名跨请求/刷新恒定(0.15 抽签也用它)。 - seeded = random.Random(user_id) - return _anon_by_id(user_id) if seeded.random() < 0.15 else _synth_masked_name(seeded) + return _mask_anon(user_id) def _synth_name(rng: random.Random) -> str: - """合成一条脱敏名(种子留空 / 旧模板名 / 兜底用):绝大多数=假名脱敏,少数=用户+随机 3 位(用户********618)。""" + """合成一条脱敏名(种子留空 / 旧模板名 / 兜底用):绝大多数=假名脱敏,少数=用户+随机 2 位(用户*****08)。""" if rng.random() < 0.15: - return "用户" + _ANON_STARS + f"{rng.randint(0, 999):03d}" + return "用户" + _ANON_STARS + f"{rng.randint(0, 99):02d}" return _synth_masked_name(rng) @@ -148,7 +149,7 @@ def _unique_name(used: set[str]) -> str: n = _synth_name(_rng) if n not in used: return n - return "用户" + _ANON_STARS + f"{_rng.randint(0, 999):03d}" # 兜底:数字尾号天然好去重 + return "用户" + _ANON_STARS + f"{_rng.randint(0, 99):02d}" # 兜底:数字尾号天然好去重 def _validate_amount(min_cents: int, max_cents: int) -> None: -- 2.52.0