# 元场 · 需求说明书（交给外包开发团队）

> 这份文档回答一个问题：**本期要做的这个小程序（微信端 C 端个人版，19 页），到底要实现哪些功能。**
> 它不写接口字段（那是 `API.md` 接口手册），也不写版式细节（那是 `UI-SPEC.md`）——
> 它是**需求清单**：每条需求一个编号，配一条**能被验证的验收标准**，后端行为的依据指向接口手册条目。

**版本**：第三十轮（2026-09-13）· 与代码同步

> **第三十轮（2026-09-13）**：全仓改名 —— 对外一律叫「星链」（C 端小程序标题 `星链 · 圈层`、B 端 `星链 · 圈层 · 机构版`），推理内核的名字改成「Lucy星链」，旧名只留在服务端的品牌别名常量（历史别名，新界面一律不用），**内部工程标识有意未改**；本轮主账本 `data/store.json` 逐字节未变。

**配套文档**（同一交付包 `handoff/`）：`API.md`（接口手册，唯一权威）· `UI-SPEC.md`（页面与版式）· `COMPLIANCE-SUMMARY.md`（合规红线与保密）

---

## 0. 怎么用这份文档

| 你想知道 | 看哪份 |
| --- | --- |
| **要实现什么功能、做到什么程度算做完** | **本份** |
| 某个接口的路径 / 参数 / 返回字段 / 错误码 | `API.md`（**接口手册**，字段名以它为唯一权威；按「谁在用」分好组） |
| 这个页面放什么、按钮怎么标、点完去哪、空态错误态长什么样 | `UI-SPEC.md` |
| 哪些事微信不允许、替代做法是什么 | `COMPLIANCE-SUMMARY.md` |

**编号规则**：`R-C-xx`（C 端个人版，本期全部要做）· `R-M-xx`（交付口径）· `R-S-xx`（保密与交付）· `R-N-xx`（非功能）· `R-X-xx`（明确不做）。
验收时逐条打勾；`UI-SPEC.md` 第 7 节是同一件事的**页面级**清单，两者一起用。

---

## 1. 产品是什么、给谁用

**一句话**：把线下商务场合里的名片变成结构化信息，算出**谁和谁值得聊、为什么**，再生成一张能发朋友圈的商务成就美图。

**本期只做一个小程序：微信端 C 端（个人版），19 页。**

| | C 端 · 个人版（本期） |
| --- | --- |
| 用户 | 商务个人 |
| 解决什么 | 我在现场见到的人里，谁值得聊；我怎么让人记住我 |
| 底栏 | 附近 · 录入 · 榜单 · 我的 |
| 页面数 | **19 页** |
| 用到的接口 | **70 个方法-路径对**（= `API.md` 主表行数，改接口时同步） |

**本期范围 = 微信小程序 · C 端个人版 · 19 页**（基于 **uni-app（Vue 3）**，只构建微信端 `mp-weixin`）。

**本期不做、留到后续阶段另行加购的**：B 端机构版（13 页）· 抖音端 · 小红书端 · 支付宝端。工程结构请留出后续加端的余地，但**不需要**本期实现，也**不在本期验收范围**（见附录 C 第 1.2 条）。

**后端已经跑起来了**：你们按接口文档联调，**不要碰后端实现、也不要索取后端源码**，更不要索取账号、令牌与运行数据。

---

## 2. 交付范围

**一句话**：你们做的是**前端** —— 按 UI 模板还原版式、把微信端小程序跑起来、按接口手册把数据接上。后端由我们提供并在我们的服务器上跑，你们只按文档调 URL。

**要做的**

1. **UI 还原（1:1）**：按我们提供的 **UI 模板 / 原型图**逐页还原，版式与 `UI-SPEC.md` 一致。**起点**：我们会给一套 **uni-app 前端骨架包**（见 `R-S-03`）—— 工程配置、页面清单、请求层与 Mock 开关都是现成的，从它起步，不必从零搭工程；**结构照骨架、版式照模板**。
2. **工程**：基于 **uni-app（Vue 3）** 建**一个**工程，**只构建微信端**（`mp-weixin`）。**抖音端 / 小红书端 / 支付宝端、以及 B 端机构版本期都不做**（见**附录 C 第 1.2 条**）；工程配置留出后续加端的余地即可，本期不要求实现。
3. **微信端适配**：导航栏高度与右上角胶囊位置、状态栏与安全区、`scroll-view` / 表单 / 弹窗 / `canvas` 的行为；平台能力不可用时给中文降级提示，**不白屏**。
4. **Mock 数据先行**：所有涉及数据交互的地方，先用 **Mock（假）数据**把页面渲染与交互跑通 —— 后端还没通、网络不通、数据为空时，UI 也要能看、能点、能演示。
5. **按文档接接口**：**测试接口我们已经在服务器上配好**，你们按 `API.md` 的路径与字段调用即可 —— **不需要知道后端内部实现**。
6. **提审辅助**：协助准备**微信端**提审材料 —— 类目选择、截图、说明文本。
7. 语言与实现纪律照旧：用户可见文案**中文**；JSON 字段名、枚举代码、`theme` 值保持英文（线上协议键名）；颜色**只从既有配色令牌取**；后端返回什么就渲染什么（理由、`message`、`label`、`report_reasons[]`），**别把中文抄进前端**。

**不要碰的 / 严禁索取的**

- **后端源码（严禁索取）**：我们**不提供后端核心源码**，只提供 **API 接口文档** 与 **UI 模板 / 原型图**；数据库结构、算法实现、密钥、运行数据同样不提供、也不要索取。
- **业务逻辑不写在前端**：匹配、评分、排名、额度、鉴权这类判断都在后端。前端**只保留视图层与接口请求层**。
- 运行数据、账号、令牌、本机配置 —— 不在交给你们的包里，也不要索取。

**要交回来的**

1. **一个私有代码仓库的完整访问权限**：你们为本项目建 **私有仓库**（GitHub / Gitee 等），把我们的账号加为 **Owner**。**最终交付物是仓库权限，不是一个 ZIP 包** —— 我们要能随时看进度，代码始终在我们的控制下。
   - 如果你们习惯按节点另发 ZIP 包作为辅助，可以发，但**验收以仓库为准**。
2. **可复现的构建步骤**：换一台机器，照着说明能构建、能在**微信开发者工具**里跑起来（见 `R-M-05`）。
3. **提审材料**：**微信端**类目建议、截图、说明文本。

> 正式条款文字见**附录 C**（可直接粘贴进合同）：交付物清单（Git 仓库 + Owner）、源码形式与内容范围（**只有视图层与请求层**）、可复现构建、验收标准、知识产权、保密与严禁索取后端源码、交付与款项。

**交给你们的包里有什么、没什么**

| 有 | 没有 |
| --- | --- |
| `manifest.json`（uni-app 配置）、`pages.json`、页面文件、组件、样式与静态资源 | `node_modules/`（依赖你们自己装） |
| 接口文档、UI 模板 / 原型图、脱敏的 Mock 数据 | `.env` 或任何密钥 / 令牌 / AppSecret |
| **UI 模板 / 原型图**（版式依据） | 我们的**现有小程序实现源码**、后端源码、核心逻辑 / 算法目录（如 `src/core`、`src/logic` 之类） |

> 三条硬规矩：**依赖不打包**（`node_modules` 由你们自己装）、**凭据不下发**（`.env` 与任何密钥都没有）、**核心逻辑不外发**（那类目录压根不在包里）。若你手上的包里出现过这三样，请以本表为准并告诉我们。

**起点：UI 模板 / 原型图 + 骨架包**

这一版**不是从零开始**：我们这边已经有一版能跑的现有实现（原生微信小程序写的），每一页的版式、交互、字段都按它对齐 —— **它不交给你们，也不要索取**；交给你们的是 **API 接口手册 + UI 模板 / 原型图 + uni-app 骨架包**。

- 你们**按 UI 模板在 uni-app 里重建前端**，从骨架包起步；**原生小程序那版实现不给你们**（它不能直接当 uni-app 工程，也属于我们的自有资产）。
- 工程结构与页面命名照 `UI-SPEC.md` 第 2 节的页面清单走（**以骨架包 `c/src/pages.json` 为准**），便于我们逐页对照验收。

### 2.1 交付口径（`R-M-*`）

| # | 需求 | 验收标准 |
| --- | --- | --- |
| R-M-01 | **框架与端**：**uni-app（Vue 3，本期锁定）**，本期**只构建微信端**（`mp-weixin`） | 仓库里有 uni-app 工程配置；能构建出**微信端**产物；本期**不做**抖音 / 小红书 / 支付宝端，也**不做** B 端机构版（见附录 C 第 1.2 条） |
| R-M-02 | **UI 还原 1:1** | 逐页对照 UI 模板 / 原型图与 `UI-SPEC.md`：版式、间距、层级、文案一致；颜色只取既有配色令牌，不新造颜色 |
| R-M-03 | **微信端适配** | 微信开发者工具里打开：导航栏高度 / 右上角胶囊 / 状态栏与安全区正常；`scroll-view`、表单、弹窗、`canvas` 行为正常；平台能力不可用时给中文降级提示（**不白屏、不静默失败**） |
| R-M-04 | **Mock 数据先行** | 数据交互处先用假数据把页面渲染与交互跑通：断开真实接口（或打开 Mock 开关）时，每个页面仍有可看可点的内容。Mock 集中在一个目录 + 一个开关，**不许把假数据抄进每个页面** |
| R-M-05 | **按文档调接口** | 按 `API.md` 的路径、方法、字段与错误码调用；`message` 与枚举表由后端给、前端直接渲染；接口不可用时不白屏、不静默失败（同 R-N-04） |
| R-M-06 | **可复现构建** | 换一台机器，照仓库里的中文说明装完依赖就能构建、能在**微信开发者工具**里打开 |
| R-M-07 | **提审辅助** | **微信端**的类目建议、截图、说明文本整理成一份材料，随仓库一起给 |

### 2.2 保密与交付方式（`R-S-*`，第二十二轮）

| # | 需求 | 验收标准 |
| --- | --- | --- |
| R-S-01 | **严禁索取后端源码** | 全程不索取、不试图获取后端源码 / 数据库结构 / 算法实现 / 密钥 / 运行数据；我们只提供**接口文档 + UI 模板 / 原型图 + 测试接口** |
| R-S-02 | **代码内容范围：只保留视图层与接口请求层** | 仓库里没有后端业务逻辑，没有匹配 / 评分 / 排名 / 额度 / 鉴权这类算法实现，也没有任何硬编码的密钥、令牌、AppSecret 或 `.env` |
| R-S-03 | **起步包内容** | 我们会给一个 **uni-app 骨架包**（`星链.圈层_前端骨架_C端_微信小程序_V1.0.zip`）：**C 端工程（`c/`，19 页）**的 `manifest.json`、`pages.json`、页面与组件空壳、样式与静态资源、**接口请求层与 Mock 开关**、中文 README；另有接口文档、UI 模板 / 原型图、脱敏 Mock 数据。**不含** `node_modules/`、`.env`、核心逻辑目录（如 `src/core` / `src/logic`），也**不含任何后端实现、算法或真实数据** |
| R-S-04 | **交付方式 = Git 私有仓库** | 私有仓库已建、委托方账号已加为 **Owner**；交付以**仓库权限**为准（另发 ZIP 只作辅助）。仓库带中文 README：怎么装依赖、怎么构建**微信端**、Mock 开关在哪、已知限制清单 |
| R-S-05 | **保密** | 接口文档、UI 模板、测试接口地址、演示数据不得向第三方披露、不得上传公开托管平台、不得用于本项目之外的任何用途 |

---

## 3. C 端 · 个人版（`miniprogram/`，19 页）

### R-C-01 启动与账号（页面 `pages/auth/index`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-01.1 | **登录即注册**：首次 `wx.login` 换 `code` → `POST /api/auth/login` 顺手建号；之后每次打开静默自动登录 | 本地已有有效令牌时，启动页**不停留**，直接进主流程 |
| R-C-01.2 | 登录前必须勾选《用户协议》《隐私政策》《订阅消息说明》，三份全文从 `GET /api/agreements` 取，可展开阅读 | 三份都在页面上能读到全文；未勾选时主按钮不可用 |
| R-C-01.3 | **后端连不上时不把人挡在门外**：如实说明，允许勾选协议后按「本机名片」继续用（游客态） | 断开后端启动，页面有中文说明且能继续进入，不白屏 |
| R-C-01.4 | `code` 拿不到时退 `device_id` 也能登录（浏览器 / 联调环境） | 只用 `device_id` 调登录接口能拿到 `token` |

### R-C-02 名片录入与本体（页面 `pages/card/index`，tab「录入」）

用户的诉求是「每个环节尽量快、尽量自动化」，所以**全流程在一页里走完**。

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-02.1 | 第 1 步·名片：**四条录入路径** —— 拍照 / 相册多选 / 连续粘贴文字 / 连拍成批（最多 20 张），外加**扫一扫** | 五条入口都能走通并出结果；批量时允许部分失败、失败项单独提示 |
| R-C-02.2 | 识别完**原地切到第 2 步**补录，不跳页；步进条可点回上一步，第 2 步用 `hidden` 保活（组件不重建） | 从识别到补录 **0 次跳转**；返回上一步草稿还在（落本地存储） |
| R-C-02.3 | 第 2 步复用一个补录组件（`components/profile-form`），**字段顺序、中文标签、灰字提示全部来自 `GET /api/profile`** | 前端不写死标签与顺序；同一组件在录入页与补录页行为一致 |
| R-C-02.4 | 第 3 步保存后**原地出 brief 匹配**（谁最值得聊，按合作价值降序），想深看再进详情页 | 保存到出结果**不跳页**；一次录入链路的总请求数明显少于跳页方案 |
| R-C-02.5 | 本体补录共 **5 组 22 个字段**，其中**12 个靠手填**（学校与社会身份 / 资源与资质 / 个人），另两组由名片与匹配自动填 | 进度条给出的 `已填 / 总数` 与服务端 `profile` 一致 |
| R-C-02.6 | 支持**一句话补录**（大白话交给服务端拆字段，约 6 秒）与**语音输入**（键盘⇄语音切换） | 两种输入都能落到字段上；语音不可用时回落键盘且不报错 |
| R-C-02.7 | 补录**只往本体加字段**，不改姓名 / 公司，也不改变去重结果 | 补录前后仍是同一个人 |
| R-C-02.8 | **名片结构化信息卡**（第二十六轮）：补录表单底部放一张可折叠的卡，列出 7 个派生字段（行业代码 / 行政区划代码 / 企业规模等级 / 关键词标签 / 学历院校 / 手机号脱敏哈希 / 手机号后四位），每行给「值」或**中文的「暂缺」**（**不是空字符串**）+ 一句**依据**（为什么是这个值 / 为什么缺）；所有值都来自接口回包，前端不自己算 | 一份齐全的名片 → 行业代码 / 区划代码 / 企业规模三行都有值且写得清依据；一份只有姓名的名片 → 这几行写「暂缺」而不是空着；折叠 / 展开都正常 |

### R-C-03 量化匹配（页面 `pages/match/index`、`pages/index/index`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-03.1 | **附近雷达**：先取定位（`wx.getLocation`），带坐标调匹配；**强制现取坐标**，不许拿旧位置顶替 | 不带定位一律被服务端拦下并给出「去哪儿打开定位」的步骤，页面有直进设置的按钮 |
| R-C-03.2 | **合作名单**：按合作价值排序；支持最近录入 / 人工顺序两种排法，并支持**长按拖拽重排**（人工顺序优先于算法） | 拖拽后刷新页面顺序不变 |
| R-C-03.3 | **匹配结论必须可解释**：每条最多 **2 条理由**，理由里**不带分数**；有多条时公司业务维度的理由排在个人社交之前 | 界面上看不到裸露的分数；理由标签是中文 |
| R-C-03.4 | 匹配口径是**公司业务需求与资源为主（约 80%）、个人为次（约 20%）**，知名公司有加成；个人关系再强也不能单独把总分推过 80 | 展示的匹配度是 0–100 整数；同一对手「公司强互补」明显高于「只有社交关系」 |
| R-C-03.5 | 打开匹配详情即记一次访问；**双方都访问过主页就自动建立订阅关系**（用户可关闭） | 互访之后订阅列表里出现对方 |
| R-C-03.6 | **匹配的授权依据（正式口径）**：**别人递给你名片，本身就是他授权你使用他的名片信息。** 所以能匹配的只有两类人：① **你手里有他的名片**（你录入过 / 你们交换过）；② **你们同场**（同一场活动里都进过场）。两条都没有的人**不参与匹配** | 请求没有授权依据的人时，服务端回 **403 `consent_required`** 并附中文引导（提示「把名片录进来」或「同场扫码进场」）；前端照返回渲染，不自己编规则、不出现裸错误码 |
| R-C-03.7 | **名录 = 我持卡的人**：`/api/people`、`/api/vip` 只返回**我持有其名片**的人（不含同场但没录卡的），不再返回全库 | 换一个账号登录，看到的名录完全不同；李娜看不到张伟的客户 |

### R-C-04 榜单与荣誉（页面 `pages/board/index`，tab「榜单」）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-04.1 | 日 / 周 / 月 / 季**四档**榜单，按服务端 `groups[]` 分组渲染（投融资 / 供需对接 / 渠道拓展 / 专业服务 / 资源撮合），组内名次**完全按后端** | 前端不做二次排序 |
| R-C-04.2 | 每条排名带**高级头衔**（资源枢纽 / 隐形冠军 / 金桥奖 / 链接之王）与**数据依据** | 头衔与依据都来自服务端，前端不编、不猜 |
| R-C-04.3 | 一条都没有时给中文空状态，并能当场点「立即生成」算一期 | 空态可点、生成后列表刷新 |
| R-C-04.4 | 我自己的名字**不出现在榜单里**（上榜的是我的客户） | 自查：以自己的 `person_id` 为 `self_id` 的匹配对象不参与排名 |

### R-C-05 分享与增长（页面 `pages/poster/index`、`pages/sharegate/index`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-05.1 | 商务成就美图：按服务端给的 `layers[]` 用 `canvas type="2d"` 真实绘制，三套配色（`black_gold` / `porcelain` / `midnight_blue`） | 三套配色都能出图；**层里的颜色由配色表决定，前端不写死色值** |
| R-C-05.2 | 美图底部带**小程序码**（无微信凭据时退化成普通二维码，页面要如实标注差别） | 码区不遮挡其它图层文字 |
| R-C-05.3 | 分享按钮文案就是「**直接发朋友圈**」，点下去走 `wx.showShareImageMenu`（微信原生「分享图片」面板） | 文案全页统一；`success` 记一次「发起了分享」，`cancel` **什么都不记** |
| R-C-05.4 | **平台边界必须写在页面上**：微信**没有**「直接打开朋友圈发布页」的 API，也**永远不返回**那条朋友圈有没有发出去 —— 所以我们记的是「用户在小程序里发起了这次分享」 | 页面上不出现「微信已确认」「分享成功」这类说法 |
| R-C-05.5 | 支持保存到相册；老基础库 / 接口不可用时**按钮整条隐藏**、退回「保存到相册」这条路，不抛错不白屏 | 在不支持的环境里页面照常可用 |
| R-C-05.6 | **晒图解锁**：距上次晒图满 **7 天**，或距上次晒图后**新增匹配对象满 10 位**，提醒一次晒图后可继续用新匹配 | 两条线任一触发即提示；被拦**不是报错**（页面照服务端的中文说明渲染，并给出两条出路） |
| R-C-05.7 | 解锁页把**全部状态**摆出来：该不该晒、哪条线触发、进度（`matched / need`、距上次几天）、上次晒图的时间 / 渠道 / **证据级别**、以及诚实说明 | 页面上能看到证据级别；解锁记录 `verified` **恒为 false**，不许假装验证过 |
| R-C-05.8 | 出图接口不可用时，退回匹配详情里缓存的那份美图参数本地绘制 | 后端不可用不导致该页空白 |

### R-C-06 关系与隐私（页面 `pages/relations/index`、`pages/requests/index`、`pages/mine/security/index`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-06.1 | **关系管理**四件事围绕同一个对象「我和他之间」：拉黑 / 设置对方权限 / 投诉 / 删除 | 四个动作都真调接口，成功后刷新名单 |
| R-C-06.2 | **拉黑是双向的**（对方发不了请求，我也发不出；双方的推荐里都不再出现对方），所以二次确认必须把这句说全 | 拉黑后双方列表里都不再出现对方 |
| R-C-06.3 | 三个权限开关的中文（`label` / `on_text` / `off_text`）与投诉理由**全部来自接口** | 前端一个字都不抄；「恢复默认」提交后那一行真的消失 |
| R-C-06.4 | 投诉**只落「已受理」**，接口不假装处置完成 | 页面上不出现「已处理完成」这类措辞 |
| R-C-06.5 | **加好友请求是纯收件箱**：待我处理（最多 5 条，可通过 / 忽略 / 删除）与我发出的（含三种状态） | 这里**没有任何次数限制**；能拦住一条请求的只有用户在关系管理里自己设的东西 |
| R-C-06.6 | 记的是「**意向**」不是「已经加上了」：微信没有代加好友的接口，跳转有五种方式（`qr` / `clipboard` / `miniprogram_code` / `customer_service` / `unavailable`），按服务端给的 `mode` 分派、沿 `fallback[]` 依次退 | 五种 mode 都有对应界面；文案统一在一处，不重复实现 |
| R-C-06.7 | 账号与安全：换绑 / 解绑手机号（脱敏显示）、登录设备列表、退出登录、**注销账号**（合规必备入口） | 四个入口都在，且都有中文反馈 |
| R-C-06.8 | 被拦（拉黑 / 对方关了申请）**不是红色报错**，按服务端中文说明引导 | 界面上没有裸错误码 |

### R-C-07 现场活动与机构（个人视角：`pages/join/index`、`pages/events/index`、`pages/orgs/index`、`pages/org/index`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-07.1 | 扫码进场有**固定顺序**：先看活动 → 再进场（**没名片或没补过本体不放行**，回 `need: "card" \| "profile"`）→ 引导去补 → 回来自动重试 → 填收集项 | 缺名片 / 缺本体时给的是**引导**不是报错；补完回来不用重新扫码 |
| R-C-07.2 | 主办方**随时可能加收集项**，每次回到页面重新拉一次，把没答的挑出来提示补答（**不用重新进场**） | 加项后老参会者能补答 |
| R-C-07.3 | 我参加的活动是**只读的个人视角**：我在哪些活动里、这场遇到了谁 | 这一页**没有**入口码 / 收集项 / 参会名单 / 报告 —— 那些是主办方的活 |
| R-C-07.4 | 机构侧：能看到哪些机构想认识我（可接受 / 婉拒）、我已连上谁、平台上有哪些通过审查的机构 | 三个区块都有，且都能进机构主页 |
| R-C-07.5 | 机构主页对个人只说三件事：它是谁、认证到哪一级；它能给什么、想找什么；**我身上有没有它要的、它有没有我要的** | 价值对照是这一页的重点，不是附件 |

### R-C-08 推送与订阅（全局）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-08.1 | 双方**相互访问主页 / 点击美图 / 交换名片**即默认建立订阅关系 | 订阅列表里能看到对方 |
| R-C-08.2 | 订阅后持续接收对方的商务动态（合同签约、企业访问、投融资、身份变动、IP / 权力结构变动） | 「订阅与推送」区块里可见订阅对象与开关 |
| R-C-08.3 | 被订阅者动态命中后 **5 分钟内（≤300 秒）**送达 | 端上要正确处理订阅消息打开小程序并跳到对应页面 |
| R-C-08.4 | **用户可关闭推送**；关闭后不得再收到，也不得出现在推送对象里 | 开关关闭后不再收到任何推送 |
| R-C-08.5 | 「**就在附近**」：相互订阅 + 进半径（默认 **3 公里**）+ 冷却期未到（默认 **6 小时**）三条同时成立才提醒 | 冷却由服务端记账，前端不自己判断、也不重复弹第二次；刚提醒过的灰化显示、只列不弹 |
| R-C-08.6 | 该弹的人可以直接「约个咖啡」，对方在 5 分钟内收到一条邀约消息 | 邀约记录出现在「我的」里 |
| R-C-08.7 | **一次网络抖动不能把消息弄丢**：首次下发没成功的，在 5 分钟窗口内自动重试（间隔 8 / 30 / 90 秒）；重试解决了、窗口过了、或者错误本身重试无用（模板不对、对方没绑微信、对方拒收），服务端都要给出**中文的最终结论**，不长期挂在「发送中」 | 「订阅与推送」区块里每条记录都只呈现「已送达 / 未送达＋原因」，没有永远转圈的状态 |
| R-C-08.8 | **名片更新提醒**（第二十六轮）：我订阅的人（或订阅我的人、以及我自己）改了名片后，**首页要跳出一张提醒卡**（带未读条数，点一下切到「我的」），「我的」里有一行「名片更新」可以展开看这次改了什么（**旧值 → 新值**）、对方**现在那一版名片**、什么时候改的；**展开看过就把「看过」落给服务端、红点清零**。没有订阅关系的人的名片变更**不给看**（服务端回空列表 + 中文说明，页面照空态渲染，不弹红框） | A 订阅 B → B 改名片 → A 的首页角标 +1、展开列表里能看到「旧 → 新」；A 看过之后角标归零；换成跟谁都没订阅关系的名片 → 列表空、页面写中文空态 |

### R-C-09 我的（页面 `pages/mine/index`，tab「我的」）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-09.1 | 一屏顺序：账号卡（头像 `open-type="chooseAvatar"` + 昵称 `type="nickname"` + 脱敏手机号）→ 我方名片卡 + 补充资料进度 → 影响力面板 → 我的二维码 → 列表项 | 顺序与 `UI-SPEC.md` 4.5 一致 |
| R-C-09.2 | 影响力（养号）只奖励**真实商务动作**：补全资料、互订、被查看、被约、客户上榜、发动态 | 没有签到、没有刷量、没有拉人头这类入口 |
| R-C-09.3 | 影响力面板显示：分数 / 档位阶梯 / 本周增量 / 分项进度 / 带预计加分的下一步 | 下一步的 `action` 是页面标识，**必须映射成中文**，不许英文上屏 |
| R-C-09.4 | 我的名片码：码内容是 `bizlink://p/<person_id>`（**后端生成，前端不拼**），可预览 / 保存 | 用微信扫一扫能进我的匹配 |
| R-C-09.5 | 扫一扫判三种情况：`person`（进匹配详情）/ `text`（交给录入链路接着识别）/ `unknown`（中文提示） | 三种都有对应出口 |

### R-C-10 首屏性能（页面 `pages/index/index`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-10.1 | 进小程序 / 切前台**只发一次**首屏聚合请求（拿 `self` / `nearby` / `board.top` / `unread`） | 首屏请求数为 1，不是 3 |
| R-C-10.2 | 聚合接口不可用时自动退回旧链路（附近提醒 + 名录各一次），**页面照常可用** | 后端 404 时页面不白屏 |
| R-C-10.3 | 名录 / 榜单等重数据按需补拉，**先出本地缓存再刷新** | 二次进入先见内容后见刷新 |

### R-C-11 我是机构 / 我要办活动（页面 `pages/mine/index`；第二十一轮）

**为什么要有**：个人与机构是同一套商务本体的两端 —— 一个人今天在活动现场收名片，明天可能代表商会办一场活动。两端要能互相走过去，才算「商业个体与团体的无缝衔接」。

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-11.1 | 「我的」页列表里有一行「**我是机构 / 我要办活动**」，那一行的摘要如实反映**现在能不能直接跳** | 摘要来自 `GET /api/bridge?from=c` 的 `mode`，不是前端猜的 |
| R-C-11.2 | 能跳才跳：只有 `mode: "navigate"` 才调 `wx.navigateToMiniProgram(appId, path)` | 未配 AppID 时**不调**跳转接口、不出现「点了就过去」 |
| R-C-11.3 | 跳不过去时把原因说清（目标 AppID 未配 / 两个小程序都还没发布），并给两条**现在就能走**的退路：小程序码、网页控制台 `/b` | 弹窗里两条退路都在；「复制网页地址」把 `BASE_URL + /b` 放进剪贴板 |
| R-C-11.4 | 用户点「取消」跳转（微信返回 `fail cancel`）时落到同一张说明卡，不静默、不报错 | 跳转失败也有中文说明 |
| R-C-11.5 | 「无缝衔接」是**业务口径**，不是平台承诺：平台限制（必须用户点击、跳前弹确认、开发者工具里不会真跳）要如实告诉用户与外包 | 接口返回 `platform_facts[]`；交付文档里不出现「一键互跳」这类说法 |

### R-C-12 战略中枢：同一份名片库的第二种用法（页面 `pages/index/index` 入口 + `pages/strategist/index`；第二十七轮）

**为什么要有**：匹配回答的是「这个人跟我合不合」；但用户手里那堆名片还有另一种用法 —— **「我现在该干嘛」**。「谁手里有谁能补上我的缺口」这种判断现在只活在人脑里，翻名片翻不出来。这一页把它变成一句问话。

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-12.1 | **首页有真的输入框**，里面有默认提示词：「我现在该干嘛？ / 看看机会 / 下周三去上海3天，找芯片渠道」；旁边三个快捷入口：本地深耕 / 看看机会 / 出差规划 | 输入框与提示词都在首页可见；三个快捷入口点一下都能到战略中枢页 |
| R-C-12.2 | 首页**不判断、不预判**：它只把用户的原话（或那个 chip 的示例话）原样带到战略中枢页，认意图是服务端的事 | 首页代码里没有任何「什么话对应哪个模式」的表 |
| R-C-12.3 | 四种模式：**本地驻守**（本地名片 > 5 张自动触发）/ **主动出击**（「城市 + 天数」）/ **价值雷达**（「我现在该干嘛 / 看看机会」，或单城 > 10 张且 ≥ 4 个维度）/ **招商**（B 端场景） | 四句话分别命中四种模式，`mode` 与 `modeLabel` 都正确 |
| R-C-12.4 | 模式一 = **谁和谁该组合**：按「政企 + 技术 + 产业」「资本 + 产业 + 渠道」「学研 + 技术 + 资本」三套模板凑闭环，每个成员写清「角色」与「能给什么」 | 组合里每个人都能点进他的名片；没有的维度写进 `gaps` / `missing`，不硬凑 |
| R-C-12.5 | 模式二 = **排得出来的行程**：同片区聚成一天、每天 2–4 场、每天至少一位决策人、只排关系度 > 0.5 的人 | 行程按天铺开，每场写时间 / 人 / 人所在公司职务 / 关系度 / 「为什么见他」 |
| R-C-12.6 | 模式二缺信息时**不硬排**：没城市或没天数 → `status:"partial"` + `missing_fields`，页面就地提示缺什么 | 说「我要出差去一趟」时页面写明还差城市或天数 |
| R-C-12.7 | 模式三 = **哪座城值得开发**：城市存量、覆盖维度、还差几张、高价值区域、建议开发哪座城 | 每个数字都能在「城市存量」那一段里对上 |
| R-C-12.8 | **禁止模糊描述**：每条结论都指向具体的人 / 资源 / 渠道 / 标的；拼不出来就写「还差什么」 | 回包里没有「可能有合作机会」这类话，`missing[]` 如实列出缺口 |
| R-C-12.9 | **门槛数字不写死在页面里**：够不够触发、本地几张、雷达阈值都由 `GET /api/strategist` 给 | 页面上任何「> 5 张」这类数字都来自接口 |
| R-C-12.10 | **只在我持有的名片里算**（录入 / 交换 / 同场授权），我自己的名片不进这个库 | 「没有可看的东西」时不越权凑名单 |
| R-C-12.11 | 样板数据一键可载可摘：`POST /api/strategist/seed` 载入**样本名片库**（全部虚构、带标记、幂等、不与真名片合并），`/clear` 整批摘掉且真实名片一张不动 | 载入后四种模式立刻能跑；摘掉后名片库回到真实规模 |
| R-C-12.12 | 这一页**不改匹配那条链路**：原有的匹配算法与召回一个字没动 | 战略中枢上线后 `/api/match` 的返回结构不变，同一个人的匹配结论与上线前一致 |

### R-C-13 门槛体检与手动补录资源方（页面 `pages/strategist/index`；第二十八轮）

**为什么要有**：用户原话 ——「资源不够就要提醒**至少还需要输入多少**，或者**可以手动填写资源方信息**」。只说一句「不够」等于把人挡在门外：他既不知道该补多少，也不知道**现在这一步**能做什么。所以门槛要算成**结构化的数**，并且**旁边就给一条不用拍名片就能补的口子**。

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-13.1 | **说清「还差多少」**：服务端给 `onboarding` 块 —— `{ enough, headline, gaps[], manual_entry, steps[] }`，每条缺口是 `{ what, have, required, short_by, unit, hint }`，**量词（张 / 家 / 项 / 个）由服务端给**，前端只渲染、不自己拼 | 一条缺口在页面上是「还差 N + 量词」＋「现有 have / 需要 required」两行，数字与接口一致；页面上**没有任何写死的门槛数字** |
| R-C-13.2 | 四套门槛各自算清：**本地驻守** = 本地名片 **> 5 张**（`required` 6）＋ 关键维度「资本 / 政企」两项；**价值雷达** = 单城 **> 10 张**（`required` 11）且覆盖 ≥ **4** 个维度；**招商** = 对口企业 **6 家**、名单来源（本地名片）**6 张**；`hint` 原样上屏 | 四种情况下体检卡里的缺口与 `GET /api/strategist` 的 `overview.onboarding` 一致 |
| R-C-13.3 | **门槛算不出来时不报假缺口**：出差模式连「城市 + 天数」都没说全时，只回一句「先把「去哪座城市 + 几天」说全，行程才有得排。」（`enough:true`、`gaps:[]`）；招商**还不知道对口企业数**时（`targets` 为 `null`）**不报这一条** | 说「我要出差去一趟」时页面显示的是「把话说全」，不是「目的地还差 2 位」；招商第一次进来不会出现编出来的对口企业数 |
| R-C-13.4 | **手动补录资源方**：表单 **8 栏**（他叫什么 / 他在哪座城市 / 公司在哪 / 他是什么职务 / 他能提供什么资源 / 他现在想找什么 / 他哪几天有空 / 怎么联系他），**只有前两栏必填**；字段的中文标签、必填性、灰字提示、「可写多条」全部来自接口（`manual_entry` / `RESOURCE_FIELDS`），前端不写死 | 只填姓名 + 城市就能提交；多值栏按 `、` `,` `，` `;` `；` 与换行切开；**同名同公司再补一次是幂等更新**，列表里不出现第二条 |
| R-C-13.5 | **补录不进主名片库**：只落 `data/strategist-resources.json`，**不写 `store.json`**；页面上必须写明「补录只用于战略中枢出方案，不进主名片库，也不参与匹配 / 授权闸 / 排行榜」 | 补录前后 `data/store.json` 的 md5 不变（真账本 md5 `f642b9729ff2e2e24ba8330aabf48f62`）；名录 / 匹配 / 榜单里查不到这位补录的人 |
| R-C-13.6 | 补过的人**看得见、删得掉、清得空**：`GET /api/strategist/resources` 列出（姓名 / 一句 line / 能给 / 要找 / 有空 + 条数徽标），单条删回 404 `resource_not_found`（不存在时说清），清空**只动自己的** | 删一条后列表少一条；清空后提示「别人的补录与真实名片都没动」 |
| R-C-13.7 | **两栏不齐就地拦**：只填了姓名或只填了城市 → 一句中文提示、**不发请求**；服务端同样回 400 `missing_fields` 并给出缺哪栏的中文标签 | 只填姓名点提交：页面出中文提示，网络面板里没有这一次请求 |

### R-C-14 合作方案 PDF 与手机号登录（页面 `pages/strategist/index`、`pages/auth/index`；第二十八轮）

**为什么要有**：用户原话 ——「形成合作方案 + 希望约谈的方式 / 时间，可生成一个 PDF 一键转发给对方的微信；PDF 带元场二维码……首次登录只需**手机号短信验证**或**实名微信验证**」。谈完一件事，需要一张能发出去的纸；而对方点开之后，**不该先被一道微信授权拦住**。

| # | 需求 | 验收 |
| --- | --- | --- |
| R-C-14.1 | **合作方案 PDF**：`POST /api/strategist/report` 把**当前这一份方案**排成 A4 PDF（复用同一次方案结果，不另算一遍）；约谈方式 / 时间 / 地点 / 时长 / 备注由用户在页面上填 | 生成成功回 `url: /download/report/<id>.pdf`、`page_count`、`bytes`；屏幕上的方案与 PDF 里的是同一份 |
| R-C-14.2 | **没填就如实标「待定」**：`meet` 里没给的栏，报告里写「待定」并列进 `meet_missing`；页面把「报告里标了 N 处「待定」」显示出来 | 一栏不填也能生成；报告里出现的是「待定」，**不是一个编出来的时间** |
| R-C-14.3 | **PDF 带小程序码**：回包 `qr.kind` / `qr.verified` 如实标注 —— 配了 `WECHAT_APPID` / `WECHAT_SECRET` 时是真·小程序码（扫码直达小程序），否则是普通二维码（内容 `bizlink://` 协议，**微信扫它不会直接进小程序**） | 页面上能看出带的是「真·小程序码（扫码直达）」还是「普通二维码（配好微信凭据后换真码）」，**不美化** |
| R-C-14.4 | **一键转发三步**：下载（`wx.downloadFile`）→ 打开（`wx.openDocument`）→ 转发给微信好友（`wx.shareFileMessage`），与回包 `share.steps = [downloadFile, openDocument, shareFileMessage]` 一一对应 | 转发由**用户自己的微信客户端**完成；**服务端不代发、不留存收件人**；基础库没有 `shareFileMessage` 时给中文提示并指向预览页右上角「…」，不抛错、不白屏 |
| R-C-14.5 | 还没算出方案就生成 → 服务端回 400 `no_plan` + 中文引导（先说一句要干什么），页面照提示渲染 | 不出现裸错误码；PDF 不存在时取文件回 404 `report_not_found` |
| R-C-14.6 | **首次登录的第二条路：手机号短信验证**：`POST /api/auth/sms/send` → `POST /api/auth/sms/verify`，**登录即注册**（没有账号顺手建一个）；验证码 **5 分钟**有效、同号码 **60 秒**内不能重发、最多校验 **5 次** | 两条登录路都能进主流程；成功后回 `token` 与脱敏号码 `account.phone_masked` |
| R-C-14.7 | **验证码只存加盐哈希、用完即焚**：复用 「服务端手机号哈希工具」 那把盐，服务端不保存明文验证码；账号锚点由号码哈希而来 | 账本里搜不到明文验证码；同一号码换台手机登录仍是同一个人 |
| R-C-14.8 | **如实边界**：短信通道未配时，**本机（回环）**请求走演示通道、验证码直接回在 `demo_code` 里由页面显示出来；**非回环**请求如实回 `sms_not_configured`；号码不合法 400 `bad_phone`、60 秒内重发 429 `too_frequent`、错码回 `wrong_code` 带 `attempts_left`、验证码用过或过期回 `not_sent` | 页面把演示验证码**明说成「本机演示通道」**，不假装短信到了手机；四条错误都按服务端中文提示上屏，不弹红框 |
| R-C-14.9 | 这条链路**同样受合规条约束**：PDF 自己身上带「AI 生成」与免责（页脚两行 + 尾页一整段），转发出去之后仍然成立（见 `R-N-16`） | 把 PDF 转到另一台设备打开，页脚与尾页的标注都在 |

---

## 5. 配套网页（由我们维护，**不在你们的范围内**）

后端自带一组零安装的网页（我们自己联调用的工具、活动现场大屏、入场落地页等）。**这些由我们维护，你们不需要实现、也不需要去访问**。这一期你们**不需要**它们，也**不需要**去访问。

- 你们要做的**只有微信端 C 端小程序前端**（本期 1 个产物）；
- 联调靠两样东西：**我们给的测试接口地址** + 你们自己的 **Mock**（见 `R-M-04`）。

## 6. 非功能需求（`R-N-*`）

| # | 需求 | 验收 |
| --- | --- | --- |
| R-N-01 | **语言纪律**：用户可见的一切中文；JSON 字段名、枚举值代码、`theme` 值保持英文 | 页面上不出现裸露的英文枚举（如 `wrong_contact`）；原始 JSON 只在联调页带调试参数时显示 |
| R-N-02 | **颜色纪律**：只从既有配色令牌取；`<switch>` 等收不了 CSS 变量的位置用与令牌一致的字面量并就近注明 | 全工程 grep 不到新造色值 |
| R-N-03 | **错误与空态**：每个接口失败 / 空数据都要有中文说明与下一步；**被业务规则拦下不算错误**（按服务端 `message` 与 `actions` 渲染，不弹红框） | 断网 / 空列表 / 被拦，三种情况页面都能操作 |
| R-N-04 | **不白屏、不静默失败**：接口不可用时给可操作的降级路径 | 关掉后端跑一遍关键路径，没有白屏 |
| R-N-05 | **性能预算**：首屏一次请求；列表首屏先出缓存再刷新；图片按需加载 | 首屏请求数、二次进入的体感都符合 R-C-10 |
| R-N-06 | **隐私**：匹配只发生在**有授权依据**的人之间 —— **持有他的名片**，或**与他同场**（见 `R-C-03.6`）；**名录按账号隔离**（见 `R-C-03.7`）；推送可关闭；拉黑 / 删除 / 投诉可用；**越权请求由服务端拒绝** | 关闭推送后确实收不到；换账号登录看不到别人的名录；挪用了别人的 `person_id` 会被服务端拒绝 |
| R-N-07 | **身份与鉴权**：`person_id` / `org_id` 是数据主键，**不是凭据**；请求都应带 `Authorization: Bearer <token>`（小程序已默认带） | 后端开启「令牌优先」档位后，自指参数写成别人会回 403 `identity_mismatch`，前端要能处理这条错误与 `action:"relogin"` |
| R-N-08 | **合规红线**：不读通讯录、不给微信好友群发 / 批量加好友（平台不允许，见 `COMPLIANCE-SUMMARY.md`） | 代码里没有这两个能力 |
| R-N-09 | **交付纪律**：不把密钥、账号、测试令牌提交进代码库或写进注释 / 截图 | 交付前自查一遍 |
| R-N-10 | **数据真实性**：演示数据是脱敏假数据（人名公司名虚构），不要当真实客户信息处理 | — |
| R-N-11 | **故障要留痕**：任何接口 5xx / 未捕获异常 / 数据文件损坏都**自动进台账**（不靠人记）；同类故障按指纹**合并**（只累加次数、刷新最后发生时间、留最近 3 个样本）；可标「已修复 / 已知不改」并写一句说明；**已标修复的同类问题再出现要自动重新打开**；台账**只增不删**（没有删除路由） | 写坏一个账本文件再重启：当天台账里出现一条「数据文件损坏」，`count` 再犯时加一而**不是多一行**；标「已修复」但没写说明 → 返回 `needs_note:true`；再触发一次同类问题，这一条回到「未修复」；接口清单里**没有删除路由** |
| R-N-12 | **召回不许静默失败**（第二十五轮）：匹配的候选召回是「词法 BM25 ∪ 语义向量」两路融合 —— 两路各取 `2 × MATCH_RECALL` 条，按**名次**用 RRF 融合（`k=60`），取 `MATCH_RECALL` 条交给模型。**语义那一路不可用（分词装不上 / 没有向量模型 / 端点点不通）时只降级、不报错**：`retrieval.strategy` 变 `lexical`、`recall.vector.degraded_reason` 给中文原因 | `GET /api/retrieval` 能看到此刻的分词器、向量提供方与是否可用、缓存条数；把 `EMBEDDING_PROVIDER=off` 再打一次匹配 → `retrieval.strategy:"lexical"` 且仍返回候选；`POST /api/match` 带 `"recall_mode":"lexical"` 可做 A/B 对照。**不允许对外说「已做语义检索」而实际没跑** |
| R-N-13 | **名片结构化字段**（第二十六轮）：每张名片除姓名 / 职位 / 公司（抽取）外，还要有 **行业代码**（GB/T 4754—2017 门类）、**行政区划代码**（GB/T 2260）、**企业规模等级**（国统字〔2017〕213 号）、**关键词标签**、**学历 / 院校**、**手机号脱敏哈希**。后六个是**派生字段**：由本体里已有内容按规则库折成标准码，**每次全量重算**（不留旧值），每个字段回一份中文依据（`basis`）与 `confidence`。**映射不出就是 `null` 并进 `unverified`，绝不编造、不给「疑似」**；企业规模必须**人数与营收双指标齐全**才定档 | `GET /api/card-fields` 能看到字段表与规则库名；一份带行业 / 城市 / 人数 / 营收的名片调 `POST /api/card` 或 `POST /api/profile` → 回包 `card_fields[]` 里这三项有值且 `basis` 说得清；只有姓名的名片 → 这些字段 `null`、`basis` 写「缺什么」；把行业改掉再保存 → 行业代码**跟着变** |
| R-N-14 | **名片更新要同步给订阅的人、并提醒他们看**（第二十六轮）：任何人（含自己）的名片被新增或补录修改后，**与该名片有订阅关系的所有人（双向任一方，含本人）**都能看到这次变更的**旧值 → 新值**、**对方当前那一版名片**、以及「谁改的、什么时候」。收件人 = 订阅者 **− 操作者本人**；**一个收件人都没有就不发推送**（如实回 `subscribers:0`）；**关掉推送的人不打扰**；**300 秒窗口内的多次改动合并成一条、只推一次**。小程序要有「跳出来的提醒」：首页角标 + 「我的」里的变更列表，看过即清 | 用 A 订阅 B 的名片 → B 补录保存 → A 调 `GET /api/card-updates?self_id=A` 能看到这条（`relation:"mine"`、`unread:true`）、`/api/boot` 的 `unread.card_updates` 加一；B 自己保存自己的补录时，收件人里**没有 B**；A 关掉推送后再改 → 推送列表里 A 是「未打扰」而 `notified` 里没有他；窗口内改两次 → 只发一条推送、列表里是两次改动的并集；点过「看过」后角标归零；对没有订阅关系的人查变更 → **空列表 + 中文说明**（不是报错） |
| R-N-15 | **手机号哈希必须加盐，盐不出本机**（第二十六轮）：`phone_hash` = 归一化号码的 **HMAC-SHA256 前 16 位**，盐放**本机数据根**（随工程走、权限 0600 的那个目录），**不进业务数据目录、不进交付包、不进任何响应**。**任何一条路径都不许产出无盐哈希** —— 裸哈希对 11 位手机号可穷举反查 | `GET /api/card-fields` 与 `GET /api/health` 只吐 `salt_source`（中文一句）与 `salt_fingerprint`（前 8 位），**响应里没有盐本身**；`data/` 与交付包里搜不到盐文件；把号码换成只差一位的另一个号 → 哈希完全不同 |
| R-N-16 | **「AI 生成」与免责必须标注到每一个产物上**（第二十八轮）：小程序**每个页面底部**都要有一条合规条（全局组件 `legal-note`，在 `app.json` 注册，页面 wxml 的**最后一行**是 `<legal-note />`）；**所有产出内容**也要带标注 —— 分享图上有 `ai_tag` 图层（服务端 SVG 与端上画布同一套几何），方案 PDF 有页眉水印、首页 meta 行、页脚两行与**尾页一整段「AI 生成与免责声明」**。文案**唯一出处**是 `GET /api/agreements` 的 `legal` 块（「服务端合规文案模块」：`LEGAL_VERSION` = `2026-09-13`、五条口径 `ai` / `source` / `verify` / `no_promise` / `operator`），端上**不另写一份**；产出类回包同时给 `legal` 与 `ai_generated:true`（`scope` 取值：`agreements` / `strategist` / `invest` / `match` / `board` / `report` / `share_card`） | 两端**每个页面**都有这条（C 端 19/19、B 端 13/13）；点开能读到五条口径与版本号；**断网时仍显示同源兜底文案**，不消失；**转发出去之后仍然成立** —— 图与 PDF 自己身上带着标注，不依赖小程序页面；全工程搜不到第二份免责文案（除组件内那份同源兜底） |

---

## 7. 明确不做的（`R-X-*`，别做进来）

| # | 不做 | 为什么 |
| --- | --- | --- |
| R-X-01 | 读手机通讯录 | 微信小程序申请不到这个权限，规范也不允许 |
| R-X-02 | 给微信好友群发消息 / 批量加好友 | 微信没有这个接口；替代是「拍照 / 相册批量识别 + 扫一扫 + 互扫名片码 + 用户主动分享」 |
| R-X-03 | 读取朋友圈 / 判断朋友圈发出去了没有 | 微信不提供，`verified` 恒为 `false`；不许假装验证过 |
| R-X-04 | 一键跳到朋友圈发布页 | 微信没有这个 API；最接近的一步是 `wx.showShareImageMenu` 拉起原生「分享图片」面板 |
| R-X-05 | 代加好友 | 微信没有；只记「意向」，加法靠二维码 / 复制微信号 / 小程序码等五种跳转 |
| R-X-06 | 支付通道 | 未接入；订单停在「待确认到账」，页面上不许写「支付成功」 |
| R-X-07 | C 端主办活动 | 现场活动是机构能力，主办方是机构主体不是个人 |
| R-X-10 | 让个人主动向机构发起连接 | 只能回应 —— 否则认证机构会被人海淹没 |
| R-X-11 | 删除活动的收集项 | 现场收上来的信息不能因为改了表单就消失（只增不减） |
| R-X-12 | **B 端机构版（13 页）** | 本期只做 C 端个人版；B 端作为**后续阶段**另行加购，不在本期范围 |
| R-X-13 | **抖音端 / 小红书端 / 支付宝端** | 同上；本期**只构建微信端**，不为其它端写条件编译分支 |

### 7.1 本轮的如实边界（未做 / 未配置 —— 别写成已完成；第二十八轮）

> 这一节记的是第二十八轮收尾时的**真话**：哪些是**真没接**、哪些是**发布配置**、哪些是**上线前必须做的一步**。
> 验收材料与对外话术里都别把它们写成「已完成」。

| # | 边界 | 实情 |
| --- | --- | --- |
| 1 | 短信通道**未配置真实服务商** | 本机（回环）请求走 `dry_run`，验证码**直接回在 `demo_code` 里**供演示；**非回环**请求如实返回 `sms_not_configured`。配 `SMS_PROVIDER` + `SMS_API_KEY` 即可真发 |
| 2 | PDF 里的二维码**默认是普通二维码** | 内容是我们自己的 `bizlink://` 协议，**微信扫它不会直接进小程序**；配了 `WECHAT_APPID` / `WECHAT_SECRET` 才换成真·小程序码，回包 `qr.verified` 如实标注 |
| 3 | PDF **服务端不代发、不留存收件人** | 转发由用户自己的微信客户端完成（`shareFileMessage`）；PDF 本地生成、本地留存 |
| 4 | 合规文案是**产品可用初稿** | 正式上线前需**法务过一遍**（主体全称、联系方式、争议解决）；「服务端合规文案模块」 里写明了这一点 |
| 5 | 微信平台的《用户隐私保护指引》等**后台配置项** | 不在这份代码里（属于发布配置） |
| 6 | **未做** | PDF 的「合作方案」里**第三方签署栏、电子签章**；短信通道的**模板报备** |

**第三十轮更新的一条**（别写成已完成）：

| # | 边界 | 实情 |
| --- | --- | --- |
| 7 | **全仓改名已经做完（第三十轮）** | 对外文案与两个小程序的标题已一律改成「星链」（C 端 `星链 · 圈层`、B 端 `星链 · 圈层 · 机构版`），脚本化替换**62 个文件 / 139 处**；旧名只留在服务端的品牌别名常量里（历史别名，新界面一律不用）。**内部标识有意未改**（工程目录名、npm 包名、骨架包标识，改了会牵动路径与构建），历史数据里的旧名不追改；**桌面交付包名与桌面目录仍是旧名**（在仓库之外，等定口径） |

---

## 8. 验收方式

1. **逐条打勾**：本份的 `R-*` 编号 + `UI-SPEC.md` 第 7 节的页面级清单。
2. **用测试接口自查**：把每个页面对应的接口**真调一次**（用我们给的地址，并显式带上 `self_id` / `org_id`），确认状态码与字段 —— 接口通不通、字段对不对，先在这一步确认，别等 UI 做完才发现。
3. **接口手册优先**：字段名以 `API.md`（接口手册）为准；接口手册与本份冲突时以接口手册为准，并把冲突提给我们。
4. **提修改意见的方式**：直接在 `docs/` 对应文件上改，并在改动处标注 —— 我们会并回源文档再同步一次。
5. **交付物**：私有代码仓库（**Owner 已加**）+ 可复现构建 + 提审材料（见 §2「要交回来的」与 `R-S-04`）。
6. **微信端验收**：**微信开发者工具**里打开 —— **无白屏、无报错**，页面交互流畅、符合 UI 模板，接口调用成功（返回 Mock 数据或我们提供的测试数据）—— 逐条对照 `R-M-01`~`R-M-07`。

---

## 附录 A · 页面清单（对照 `app.json`）

**本期要做：C 端（19 页）**：`auth`（登录/欢迎）· `index`（附近：雷达 + 合作名单 + VIP 名片夹）· `card`（录入三步一页）· `board`（榜单）· `mine`（我的）· `mine/security`（账号与安全）· `match`（匹配详情）· `profile`（补录资料）· `poster`（美图）· `qrcode`（名片码 + 扫一扫）· `nearby`（就在附近）· `join`（扫码进场）· `events`（我参加的活动）· `orgs`（机构与连接）· `org`（机构主页）· `requests`（加好友请求）· `relations`（关系管理）· `sharegate`（晒图解锁）· `strategist`（战略中枢：一句话出方案）


## 附录 B · 关键业务规则速查

| 规则 | 数值 / 口径 |
| --- | --- |
| 匹配度 | **0–100 的整数，由服务端算好返回**；附带分维度分数与一句话理由 —— 界面**直接渲染，不在前端重算、不做二次加权** |
| 匹配理由 | 最多 **2 条**，不带分数，公司级排在个人社交之前 |
| 榜单 | 日 / 周 / 月 / 季；头衔 资源枢纽（≥85）/ 隐形冠军（≥75）/ 金桥奖（≥65）/ 链接之王 |
| 本体补录 | **5 组 22 字段**，其中 **12 个手填** |
| 晒图解锁 | **7 天** 或 新增 **10 位**匹配对象；`verified` 恒 `false` |
| 就在附近 | 相互订阅 + **3 公里** + 冷却 **6 小时** |
| 推送 SLA | **≤ 300 秒**（5 分钟） |
| 价值类型 | **12 类**（C 端与机构端共用同一张表） |
| 美图配色 | `black_gold` / `porcelain` / `midnight_blue` |
| 加好友跳转 | 五种 `mode`：`qr` / `clipboard` / `miniprogram_code` / `customer_service` / `unavailable` |
| 战略中枢触发 | 本地名片 **> 5 张**自动本地驻守；单城 **> 10 张且 ≥ 4 维**自动雷达；出差要「城市 + 天数」；关系度门槛 **> 0.5**；每天 **2–4 场**、至少 **1 位决策人** |
| 样本名片库 | 工程自带 `data/cards.sample.json`（38 张，全部虚构），`POST /api/strategist/seed` 载入 / `/clear` 整批摘掉 |
| 门槛体检（第二十八轮） | **本地驻守**：本地名片 **> 5 张**（`required` 6）且关键维度 **资本 / 政企** 两项齐全；**价值雷达**：单城 **> 10 张**（`required` 11）且覆盖 ≥ **4** 维；**招商**：对口企业 **6 家**、名单来源（本地名片）**6 张**。量词由服务端给（张 / 家 / 项 / 个）；说不出城市 + 天数时只回「把话说全」，还不知道对口企业数时**不报那一条** |
| 手动补录资源方 | **8 栏**（姓名 / 城市 / 公司 / 职务 / 能给 / 要找 / 有空 / 联系方式），**只有姓名 + 城市必填**；多值栏按 `、` `,` `，` `;` `；` 与换行切开；同名同公司**幂等更新**；落 `data/strategist-resources.json`，**不进 `store.json`** |
| 合作方案 PDF | A4 一页纸（`595 × 842` pt、2 倍渲染）：约谈方式 / 时间留白就写「待定」并进 `meet_missing`；页眉水印与页脚带「AI 生成」，尾页固定一段「AI 生成与免责声明」；转发走 `downloadFile` → `openDocument` → `shareFileMessage`，**服务端不代发** |
| 手机号验证码 | **5 分钟**有效 / **60 秒**重发冷却 / 最多校验 **5 次**；只存加盐哈希、用完即焚；未配通道时本机走 `demo_code` 演示、非回环如实回 `sms_not_configured` |

---

## 附录 C · 开发与交付条款（**可直接粘贴进合同**）

> 这一节是给**合同 / 订单 / 委托开发协议**用的正式条款文字，可以直接复制到合同正文，也可以作为附件（附件与正文冲突时以正文为准）。
> 方括号 `【】` 里是需要你们自己填的：金额比例、日期、公司全称、覆盖的小程序清单。其余文字可以照抄。
> 说明：本节由技术方撰写，**不构成法律意见**；签字前建议让法务或律师过一遍。

**第 1 条（定义）**

1.1 「委托方」指【委托方公司全称】；「受托方」指【受托方公司全称】。
1.2 「本项目」指「元场」**微信小程序 C 端（个人版）前端**，共 **19 个页面**，基于 **uni-app（Vue 3）** 实现，构建目标为**微信端（`mp-weixin`）**。**本期不含**：B 端机构版小程序、抖音端、小红书端、支付宝端，以及任何后端开发；上述内容作为后续阶段另行委托。
1.3 「交付物」指第 2 条列明的全部内容。
1.4 「委托方资料」指委托方以书面或电子形式提供给受托方的资料：**API 接口文档**、**UI 模板 / 原型图**、脱敏演示（Mock）数据，以及（如有）委托方提供的**前端视图层参考代码**。
1.5 「后端系统」指委托方自有的服务端程序与其数据。**后端系统不属于本项目范围，委托方不向受托方提供其源码。**

**第 2 条（交付物）**

2.1 **私有代码仓库的完整访问权限**：受托方应为本项目建立**私有** Git 仓库（GitHub / Gitee 等），并将委托方指定账号加为 **Owner**。**本项目以仓库权限作为交付形式**；另发压缩包仅作辅助，不作为验收依据。
2.2 **前端源码（视图层与接口请求层）**：微信端页面、组件、样式、静态资源、工程配置（含 uni-app 的 `manifest.json`、`pages.json`），以及按「委托方资料」调用后端的请求层封装。
2.3 **不含密钥与凭据**：源码中不得出现任何硬编码的密钥、令牌、账号、AppSecret、第三方私密凭据或 `.env` 等凭据文件；需要配置项时一律走平台配置或运行环境变量，并在说明文档中写明。
2.4 **不含后端业务逻辑**：源码中不得包含后端业务逻辑、算法实现（含匹配、评分、排名、额度、鉴权等），也不得以本地计算替代后端判断。
2.5 **构建与运行说明**（中文）：依赖安装、**微信端**的构建与预览步骤、发布步骤、Mock 数据的开关位置与用法。
2.6 **适配与已知限制说明**：微信端的适配处理方式与已知限制清单。
2.7 **提审辅助材料**：**微信端**类目建议、截图与说明文本。
2.8 **第三方资源清单**：使用的第三方组件、库、字体、图片及其授权方式；不得包含授权不明或要求对外开源的内容。

**第 3 条（源码形式要求）**

3.1 交付的源码必须是**可读、可编辑、完整**的工程源码，**不得**以满足功能为由交付以下任何一种：

- （a）仅可上传或仅可体验的打包件、体验版二维码、上传码；
- （b）经压缩、混淆、加密或去注释处理的代码；
- （c）缺少源码、以远程服务或私有环境替代的模块；
- （d）只有受托方持有、委托方无法独立构建的私有依赖或私有仓库。

3.2 交付物中不得留存与本项目无关的代码或数据；测试用假数据应集中在独立目录，并可通过一个开关关闭。
3.3 源码注释与文档使用中文（与项目既有语言约定一致）。
3.4 第 2.4 条**不得**被解释为「可以省掉功能」：本项目要求实现的页面、交互与接口调用必须全部实现，只是不在前端重复后端已有的判断。

**第 4 条（验收标准）**

4.1 **能跑**：微信端小程序可在**微信开发者工具**中正常运行，**无白屏、无报错**。
4.2 **像**：页面交互流畅，UI 与委托方提供的 UI 模板 / 原型图一致（1:1 还原）。
4.3 **通**：接口调用成功 —— 返回 Mock 数据或委托方提供的测试数据，字段与接口文档一致。
4.4 **能构建**：委托方有权在自己的机器上按第 2.5 条说明完成构建并运行；**无法独立构建的，视为未完成交付**。
4.5 **逐条打勾**：验收依据为委托方的《需求说明书》（`R-*` 编号条目与验收标准）与页面级清单。
4.6 受托方应在交付时以书面（含电子形式）说明本轮验收范围与未完成项；**未说明的缺口不视为已完成**。

**第 5 条（知识产权）**

5.1 受托方因本项目开发所产生的全部交付物（含源码、文档）的著作权及其他知识产权，自交付之日起归**委托方**所有；委托方有权自行或委托第三方继续开发、修改、发布、许可他人使用。
5.2 受托方对「委托方资料」与「后端系统」不享有任何权利，仅可在本项目范围内使用。
5.3 受托方保证交付物不侵犯任何第三方权利；如因此引发争议，由受托方负责处理并承担相应责任。

**第 6 条（保密与范围限制）**

6.1 **委托方不提供后端核心源码**：委托方仅提供接口文档、UI 模板 / 原型图、测试接口与脱敏数据。受托方**不得索取**后端源码、数据库结构、算法实现、密钥或运行数据。
6.2 受托方及其人员对「委托方资料」「后端系统」及其接口、本项目文档、业务规则与演示数据负有保密义务，**不得**向第三方披露、公开、上传至任何公开代码托管平台或用于本项目之外的任何用途。
6.3 受托方**不得**对委托方提供的测试接口做压力测试、漏洞扫描、渗透测试或任何超出本项目联调需要的访问，也不得试图绕过接口鉴权或反推后端实现。
6.4 委托方提供的演示数据均为**脱敏的虚构数据**，受托方不得据此推断、收集或对外发布委托方的真实用户信息。
6.5 本条保密义务在项目结束或合同解除后**继续有效，且不设期限（永久）**；属于商业秘密的部分，保密义务不因合同终止而终止。

**第 7 条（交付与款项）**

7.1 受托方交付物中**未包含第 2 条全部内容**（包括但不限于未交付仓库权限与可构建的源码）的，委托方有权拒绝验收，相应款项不予支付。
7.2 尾款【____】% 于受托方完成第 2 条全部交付物并通过第 4 条验收后支付。
7.3 交付与协作方式：**以私有 Git 仓库为准** —— 委托方账号具有 Owner 权限，可随时查看提交记录与进度；受托方按【每周】提交一次可构建的版本。交付后受托方仍保留必要配合义务，配合期限为交付之日起【____】日。

**第 8 条（变更与后续维护）**

8.1 委托方提出的需求变更，双方以书面方式确认范围、工期与费用后执行。
8.2 项目交付后 **6** 个月内，受托方对因其开发原因造成的缺陷免费修复；因委托方新增需求造成的改动另行计价。
8.3 **改动池**：交付后 6 个月内的“修复缺陷 + 小改动（文案 / 颜色 / 间距 / 字段对不上）”按次计费，每次不超过半个工作日，总次数以双方约定的改动池为准；**加页面、加功能、加端一律不在改动池内**，须另行书面报价。

---

**一句话版（用于微信 / 邮件里跟对方先确认口径，不是合同条款）**

> 我们只做前端：按 UI 模板在 uni-app 里实现**微信端 C 端**小程序（19 页）。后端在我们服务器上跑，只给接口手册、UI 模板和测试接口 —— **后端源码不提供，也不要索取**；数据交互先用假数据把 UI 跑通。你们**建一个私有 Git 仓库并把我们加为 Owner**，交付就是这个仓库（不是发个 ZIP）；仓库里只放视图层与接口请求层，不许有硬编码密钥，也不许有业务逻辑或算法。微信开发者工具里能跑、不白屏不报错、UI 对得上模板、接口调得通，才算验收通过。

