451aa4e915dbbb461f43af8086e30c56775e6957
## 现象(2026-09-12 端到端跑通后查数据时发现) 客户 9102 的画像快照状态**彼此矛盾**: | version | is_current | current_customer_id | |---|---|---| | 2 | `0` | `9102` ← 清旧时没清空 | | 3 | `1` | `NULL` ← 建新时没写入 | ## 根因 `ProfileAssemblyService._write_snapshot` 把 `current_customer_id` **当成了生成列**: - 方法 docstring 原文写着"唯一键 `uk_profile_snapshot_current` 建立在**生成列** `current_customer_id` 上" - 因此两处都不赋值(以为 DB 会自动填) **但该列不是生成列** —— `alembic/baseline_generated.sql` 与真实库都是**普通可空列 + 唯一键**, `app/model/profile.py` 的模块 docstring 第 2 条已明确:"当前版本必须由写入方**显式写入**客户 ID (历史版本写 NULL),才能保证「每个客户最多一条当前快照」"。 后果:唯一键**形同虚设**(多个 NULL 不冲突)⇒ 不变式失效;且旧版本残留的值 一旦与新版本补上的值相同,就会**直接撞唯一键**。 > 这与本线先前修的 `CustomerProfileCandidateService._write_profile_snapshot` 是**同一个缺陷的另一处** > —— 当时只找到一处,这次是靠真实链路跑出数据后核对才暴露出来。 ## 改动 `app/service/profile_assembly_service.py`: - 旧版本:`is_current = False` 的同时 `current_customer_id = None` - 新版本:`is_current=True` 的同时 `current_customer_id=customer_id` - 订正方法 docstring 的错误认知("生成列"→ 普通可空列 + 唯一键),并写明后果 ## 已有数据订正 新增 `tools/fix_profile_snapshot_current.py`(**默认 dry-run**、幂等、`--apply` 才提交): 1. 先清空 `is_current=0` 却残留值的行 2. 再补写 `is_current=1` 却是 NULL 的行 3. **顺序要紧**:反过来的话第 2 步会与残留值撞唯一键 本机实测:清空 1 行、补写 1 行,复核两类异常均归零。 ## 验证 - `mypy app` → 0 错 / 245 文件 - `pytest tests`(全量)→ `2 failed, 1418 passed, 1 skipped` (2 个失败为既有环境项:httpx 把中文序列化成 `\uXXXX`,非本次引入) - 端到端:真实对话 → 记忆抽取 → `memory_unit` 落库已实测通过(客户 9102 `preference:risk_level = "低风险"`,候选态)
Description
番茄炒蛋组
18 MiB
Languages
Python
99.9%