feat: 出站补全渲染——首封e164+标注/主题行/推送派生/canon双保险/面板尾注

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-08 19:51:46 +08:00
parent 9d16235454
commit 7df2e4ae1e
8 changed files with 524 additions and 37 deletions
+24 -4
View File
@@ -23,6 +23,7 @@ from app.models.database import (
Conversation, CustomerProfile, Evaluation, IntentLevel, LeadPushLog,
Message, MessageRole,
)
from app.services.cc_completion import cc_state_for, completion_note, outbound_e164
from app.services.ip_service import resolve_country
logger = logging.getLogger(__name__)
@@ -86,6 +87,23 @@ async def _build_payload(
raw_info = profile.raw_info or {}
client_info = conv.client_info or {}
# Task 6(spec §5 先消费后过滤,顺序钉死): 先读 raw_info 的 _cc 派生——
# contact 的 phone/whatsapp 换成对应渠道 e164(首封即补全号,确认与否都给;
# 无 e164 态用原文),标注进"号码补全说明"派生键;之后才做 _ 前缀过滤,
# _cc 本体绝不进 payload,派生键(无 _ 前缀)存活。canon 不匹配的残留态
# 在 cc_state_for 内判 None——旧补全永不跟随新号。
cc_map = raw_info.get("_cc") or {}
contact_out: dict = {}
cc_notes: list[str] = []
for k, v in (confirmed or {}).items():
if k in ("phone", "whatsapp") and v:
state = cc_state_for(cc_map, k, str(v))
contact_out[k] = outbound_e164(str(v), state)
note = completion_note(str(v), state)
if note:
cc_notes.append(note)
else:
contact_out[k] = v
# _ 前缀内部状态(一次性计数等)不进推送画像(spec §4.3——渲染侧
# 三处有过滤(enrich/reminder/email_service),推送端曾整体拷贝泄漏)
profile_out = {k: v for k, v in raw_info.items()
@@ -94,6 +112,8 @@ async def _build_payload(
profile_out["source"] = conv.source.value
if client_info.get("lang"):
profile_out["语言"] = client_info["lang"]
if cc_notes:
profile_out["号码补全说明"] = ";".join(cc_notes)
reasoning = (evaluation.get("reasoning") or "") if evaluated else ""
@@ -103,10 +123,10 @@ async def _build_payload(
"occurred_at": _fmt_cst(datetime.now(timezone.utc)),
"contact": {
"name": None, # 我方 contact_info 快照无称呼字段,契约可空
"phone": confirmed.get("phone"),
"whatsapp": confirmed.get("whatsapp"),
"email": confirmed.get("email"),
"wechat": confirmed.get("wechat"),
"phone": contact_out.get("phone"),
"whatsapp": contact_out.get("whatsapp"),
"email": contact_out.get("email"),
"wechat": contact_out.get("wechat"),
"skype": None,
},
"intent_level": map_intent(evaluation.get("intent_level"), evaluated),