# -*- coding: utf-8 -*- """给人看的文字的统一处理。后端与前端共用同一套口径。 ## 为什么要有这个文件 系统里有一批字符串**同时背两份职责**:既给机器用,又给人看。典型是规则闸的未通过项, 形如 `PORTFOLIO_CAP: 买了这一笔,总持仓会占到…`。句首那个大写代码是承重的: app/services/executor.py 靠它取账本去重键(同一条被拒指令当日只记一条) app/core/planner.py 靠它做原因聚类(命令进度那行注记) 所以**代码不能从句首挪走,也不能删**——2026-07-29 那次账本被同一条指令按分钟灌满的 事故,病根就是去重键失效。正确的做法是:码留在句首,到了显示层再剥掉。 这个文件就是那个显示层。后端两处与前端各一份,全部走同一口径,不再各写各的。 ## 长远的根治 更彻底的做法是让产出方直接返回 {"code": ..., "text": ...} 两个字段:机器只读 code, 页面只读 text,代码名泄漏这件事在结构上就消失了。那是更大的改动,本文件是它之前的 过渡口径,也是那之后前端仍然需要的兜底。 """ from __future__ import annotations def strip_code(text) -> str: """剥掉句首那个给运维看的大写代码前缀,只留中文说明。 判定条件写得严:冒号之前必须全部是大写字母、数字或下划线,且非空。这样 「PORTFOLIO_CAP: 总持仓…」会被剥掉前缀,而「预期空间: 算不出」这种正常带冒号的 中文句子不会被误伤。认不出的原样返回——宁可多显示几个字,也不要把人家的话截半句。 """ s = str(text if text is not None else "").strip() head, sep, tail = s.partition(":") if sep and head.strip() and all( ch.isupper() or ch.isdigit() or ch == "_" for ch in head.strip()): return tail.strip() or s return s def join_reasons(items, sep: str = ";") -> str: """把多条原因拼成一句给人读的话:逐条剥掉代码前缀,用中文分号连起来。 分隔符用中文分号而不是 " | ",因为这一栏是句子不是表格。空条目丢掉, 全空时返回空串,让调用方自己决定显示什么。 """ out = [strip_code(x) for x in (items or [])] return sep.join(x for x in out if x)