49 lines
2.3 KiB
Python
49 lines
2.3 KiB
Python
|
|
# -*- 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)
|