---
date: 2026-04-23 20:30
slug: drop-exposure-system
title: 砍曝光系统，M4 虚拟信封彻底替代
status: active
tags: [m4, exposure, cleanup, breaking-change]
---

# 砍曝光系统，M4 虚拟信封彻底替代

## 场景

M4 ADR（`2026-04-23-m4-mail-model-touchreach.md`）引入虚拟信封 + agent gateway 通知时，为保守起见在代码里**保留**了旧版付费曝光系统（`xq expose` / `exposure.py` / `exposure_orders` / `exposure_usage` / `payment/alipay.py`）。

M4 实装完跑通后判断：两套付费并存反而给用户更难理解，信封已完整覆盖"付费提升可见性"语义（付费才能主动敲门，对方 agent 才收到通知，物理直觉与 LinkedIn InMail 一致）。

## 选项

- A) 两套并存（原先保守选的路径，已实装但未发 live）
- **B) 砍 exposure，M4 虚拟信封彻底替代（采纳）**
- C) 砍 exposure + 同步砍所有 payment provider（alipay/wechat/mock/base）

## 选 B/C，因为

- M4 信封付费点更清晰：花钱敲门，结果取决于对方响应——对齐 LinkedIn InMail 直觉
- 旧曝光的"付费让 query 命中时置顶"在 agent 时代弱：查询权在用户 agent，置顶没价值
- 两套代码并存 → 测试面翻倍、概念解释复杂化、新用户学习曲线陡
- payment/alipay.py 的唯一消费方就是 exposure，一起砍；payment/wechat.py 从未实装
- canary 已发 4 个 dev 版（0.1.0.dev / 0.2.0.dev / 0.3.0.dev）但没发 live；砍后直接 M4 设计发首个 live 版

## 具体砍什么

### 代码

- `src/xiangqin/exposure.py`（整个模块，约 211 行）
- `src/xiangqin/payment/`（整个目录：base / mock / alipay / wechat / __init__）
- `src/xiangqin/app.py` 里 `/exposure/*` endpoints 段 + alipay webhook 端点
- `src/xiangqin/cli.py` 里 `xq expose buy/balance/history` 系列命令
- `src/xiangqin/query.py` 简化 `ORDER BY` 去掉 `exposure_remaining>0 DESC`，保留 `updated_at DESC`
- `pyproject.toml` 删 `cryptography` 依赖（仅被 alipay 用）

### 数据库

- SCHEMA 删 `exposure_orders` / `exposure_usage` 建表语句
- 删 `_migrate_orders_columns` 函数
- 加 `_drop_exposure_tables` 迁移函数：`DROP TABLE IF EXISTS exposure_orders; DROP TABLE IF EXISTS exposure_usage;`（生产 epsilon 部署时执行，幂等）

### 测试

- `test/test_exposure.py` 整个删
- `test/test_query.py` 去掉付费排序相关用例（`test_exposure_drains_to_free_pool` 等）
- `test/test_cli.py` 去掉 `xq expose *` 测试
- `test/test_app.py` 去掉 exposure endpoint 测试
- 目标：coverage 维持 ≥70%（砍掉大量未测代码可能反而提升覆盖率）

### 文档

- `docs/design.md` 去"付费排序语义"段，改为"排序：`ORDER BY updated_at DESC`"
- `docs/cli-reference.md` 去 Exposure 段
- `docs/usage/cookbook.md` 删 recipe 1-8（旧曝光玩法），保留 9-15（私信）
- `docs/faq.md` 搜曝光字样清理
- `docs/usage/getting-started.md` 买曝光段替换为买信封
- `docs/internal/schema.md` 去 exposure 表段
- `docs/internal/architecture.md` 去 exposure 模块段
- `SKILL.md` 去"旧版：买曝光置顶（保留）"段
- `README.md` 心智模型 / 设计原则 / 为什么做 改"付费置顶" → "付费接触"

### CHANGELOG

- 重写为干净版本段（0.1/0.2/0.3 canary + Unreleased/M4 最终形态）
- 删过程流水（M1/M2/M3 通关、方向调整、命名改等多次迭代记录）

## 代价 / 放弃

- **破坏性变更**：dev 版装过的用户（如果真有）升级后 `xq expose` 命令不存在、`/exposure/*` API 返回 404；但 canary 用户应为 0-1（老板本人测过），影响可忽略
- **支付 provider 抽象化工作废弃**：`payment/base.py` / `payment/alipay.py` / `payment/wechat.py` 一并砍；未来如果 M5 重新做付费商品要从零搭支付层（但支付宝验签代码可从 git 历史 `6d142b4` 前恢复）
- **alipay 证书资产不改动**：vault 里的 `alipay-kongxuanpin` 凭证保留（未来可能用），只是 xiangqin 代码里不再引用；vault.json 声明的 `alipay` 别名保留或可同步删（下一步清理）

## 实施顺序

见 `brain/plan/M4-sell-first-user.md` 实装节；或 TaskCreate 链：
ADR → 代码删除 → DB migration → 测试清理 → 文档清理 → CHANGELOG 重写 → 测试/build 验证 → commit 推送

## 记录源

- 老板 2026-04-23 晚决策："删"（直接砍 exposure 系统）
- 2026-04-23 早 ADR：`2026-04-23-m4-mail-model-touchreach.md` 原本说"保留"，本 ADR 覆盖之
