> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Contacts 字段设计

> **目的**：对照 Contacts UI 设计和数据指标需求，逐字段分析数据来源，确定 `PhoneBook` 表

> **字段放置原则**: PhoneBook 表的定位是客户级别的判定和标记，尽量不重复存储其他表已有的数据。

通话数据 → 查 call-analysis
Lead 数据 → 查 LeadTracking-v2
SMS 数据 → 查 MessageStore / Conversations
客户级别的聚合判定（温度、意图、分类、分配、标签） → 存 PhoneBook

> **分析范围说明**：本次改版已对生命周期的 **Lead 阶段**进行了全面的数据源分析和字段设计。**Member 和 Churned 阶段**暂未进行详细分析（见 1.3、1.4 标注），因此文档中涉及这两个阶段的字段多为预留或待定状态，后续迭代再细化。

***

## 一、Contacts 信息分析

### 1.1 通用信息（所有 Stage 共用）

| 类别          | 字段                                                                                                                                                                          |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **身份信息**    | 姓名、电话、邮箱、性别、地址、工作、生日/年龄、作息模式                                                                                                                                                |
| **生命周期**    | stage 生命阶段、state 活跃状态                                                                                                                                                       |
| **运营信息**    | 分配给谁、员工备注、自定义标签、doNotContact (DNC)                                                                                                                                          |
| **活动数据**    | 来源渠道、获取时间、首次尝试联系时间、首次触达时间、通话次数、短信次数、最后活动时间                                                                                                                                  |
| **联系偏好**    | 联系响应模式、最佳联系时段、偏好联系渠道                                                                                                                                                        |
| **健身画像**    | 核心需求、感兴趣的课程类型、偏好时段、健身经验（含过往经历）、当前运动习惯、关注点、健康状况                                                                                                                              |
| **通讯记录**    | SMS 内容、通话录音（音频回放）、逐句对话记录（Conversation Data，分 Speaker + 时间戳）、语音信箱、系统备注（System Note，如 Lead 提交、预约等系统事件）                                                                        |
| **AI通话分析**  | **通话摘要**：AI 摘要、本次是否需要跟进（follow\_up\_needed）、跟进原因（follow\_up\_reasons） · **关键信息**：主分类、子分类、次要话题、通话结果分类、是否获取信用卡、营收优先级 · **通话详情**：接听结果、通话方向、通话时长、客户电话、来电方名称、通话时间、接听员工、通话状态、客户类型 |
| **AI跨通话分析** | 客户摘要（customerSummary）、是否需要跟进（actionNeeded，跨通话综合判断）、建议动作列表（suggestedActions，List，每个 action 内含 priority）、建议截止时间（suggestedFollowUpBy）                                          |

### 1.2 Lead 专属信息（仅 stage = Lead 时适用）

| 类别          | 字段                                         |
| ----------- | ------------------------------------------ |
| **Lead 跟进** | leadStatus、temperature                     |
| **Lead 分析** | 意向等级、提交次数                                  |
| **消费决策**    | 预算区间、价格敏感度、决策风格、决策周期                       |
| **决策障碍**    | objections（客户犹豫中的顾虑：价格贵、要和家人商量、时间不确定等）     |
| **条件性拒绝原因** | rejectionReasons（客户已明确拒绝的原因：太远、已有会籍、时间冲突等） |

### 1.3 Member 专属信息（仅 stage = Member 时适用）

> ⏳ **本次改版暂不涉及 Member 阶段，以下内容仅作规划参考，不纳入当前分析和实现范围。**

| 类别            | 字段                                                 |
| ------------- | -------------------------------------------------- |
| **Member 状态** | memberStatus（Onboarding / Active / At-risk / VIP）  |
| **合同信息**      | 购买套餐、签约日期、合同周期、到期日                                 |
| **使用行为**      | 到课频率、偏好课程、偏好教练、社交活跃度、最后到课日期、连续缺课天数                 |
| **附加消费**      | 私教课、附加服务、商品购买                                      |
| **客户价值**      | LTV（累计贡献收入）、CAC（获客成本）、ROI（投资回报率）、消费频次+金额           |
| **体测进展**      | 入会体测、阶段体测、目标达成率                                    |
| **续费分析**      | 续费意愿、续费障碍（价格涨幅/使用频率低/体验不满等）、合同到期预警、历史续费次数、上次续费价格变动 |
| **推荐裂变**      | 推荐人数、NPS 评分、是否愿意推荐                                 |
| **流失风险**      | 流失风险等级、满意度                                         |

> ⚠️ 合同信息、使用行为、客户价值、附加消费、体测进展依赖会员管理 / 排课签到 / 交易系统，V1 暂无数据源。

### 1.4 Churned 专属信息（仅 stage = Churned 时适用）

> ⏳ **本次改版暂不涉及 Churned 阶段，以下内容仅作规划参考，不纳入当前分析和实现范围。**

| 类别       | 字段                        |
| -------- | ------------------------- |
| **流失原因** | 流失原因（价格/距离/服务不满/竞品/生活变化等） |
| **流失画像** | 竞品去向、回归可能性                |
| **回归画像** | 回归动机、期望变化、可接受条件           |

***

## 二、Contacts 字段数据分析和设计

### 2.1 身份信息

> 对应 1.1 身份信息：姓名、电话、邮箱、性别、地址、工作、生日/年龄、作息模式

| # | 字段    | 是否已有数据源 | 数据源                             | 字段名                      | 类型     | 存入 PhoneBook              | 说明                    |
| - | ----- | ------- | ------------------------------- | ------------------------ | ------ | ------------------------- | --------------------- |
| 1 | 姓名    | 有       | LeadTracking-v2                 | `firstName` / `lastName` | String | 是，冗余存储便于列表排序和搜索           | 拼接显示；头像取首字母生成彩色圆圈     |
| 2 | 电话    | 有       | LeadTracking-v2 / call-analysis | `phone`                  | String | 是，作为 PK                   | E.164 格式，PhoneBook 主键 |
| 3 | 邮箱    | 有       | LeadTracking-v2                 | `leadContactEmail`       | String | 否，查询时从 LeadTracking-v2 读取 | -                     |
| 4 | 性别    | 无       | —                               | `gender`                 | String | 是，预留字段                    | V1 暂无数据源              |
| 5 | 地址    | 无       | —                               | `address`                | String | 是，预留字段                    | V1 暂无数据源              |
| 6 | 工作    | 无       | —                               | `occupation`             | String | 是，预留字段                    | V1 暂无数据源              |
| 7 | 生日/年龄 | 无       | —                               | `birthday`               | String | 是，预留字段                    | V1 暂无数据源              |
| 8 | 作息模式  | 无       | —                               | `dailyRoutine`           | String | 是，预留字段                    | V1 暂无数据源              |

> ⚠️ 第 4-8 项在 V1 中暂无数据源，PhoneBook 表预留字段位置。未来可能的写入方式：AI 从通话内容中提取、员工手动录入、或对接第三方 CRM/表单系统。

### 2.2 生命周期

> 对应 1.1 生命周期：stage 生命阶段、state 活跃状态
> 详细定义见 [客户生命周期管理](/product-design/v1/contacts-feature/user-lifecycle-management.md)

| # | 字段              | 是否已有数据源 | 数据源 | 字段名              | 类型     | 存入 PhoneBook | 说明                                           |
| - | --------------- | ------- | --- | ---------------- | ------ | ------------ | -------------------------------------------- |
| 1 | Lifecycle Stage | 无       | -   | `lifecycleStage` | String | 是            | 枚举：`lead` / `member` / `churned` / `unknown` |
| 2 | Lifecycle State | 无       | -   | `lifecycleState` | String | 是            | 枚举：`active` / `frozen` / `terminal`          |

> **现有数据源分析**：
>
> 数据库中有两个近似字段，但都不直接等同于"生命阶段（lifecycleStage）"：
>
> 1. **LeadTracking-v2 `leadType`**（值：`Web Lead`、`Online Intro` 等）— 这是 Lead 的**来源渠道**，描述客户从哪个入口进来，不是生命阶段。归入后续运营信息分析。
> 2. **call-analysis `customer_type`**（值：`prospective_client` / `existing_member` / `other`）— 这是 AI 对**单通电话**的判定，表示本次通话中对方是潜在客户还是现有会员。它是单次通话粒度的，不是客户级别的状态。
>
> **lifecycleStage 判定逻辑**：
>
> | 值         | 触发条件                                                           | 数据源                                                                        |
> | --------- | -------------------------------------------------------------- | -------------------------------------------------------------------------- |
> | `lead`    | 凡是从 LeadTracking-v2 进来的联系人，除了 leadStatus 为 converted 的，都是 lead | LeadTracking-v2 存在该 phone 的记录                                              |
> | `member`  | LeadTracking-v2 的 `leadStatus` 变为 `converted`                  | LeadTracking-v2 `leadStatus`（当前尚未落库，待实现）                                   |
> | `churned` | 会员流失                                                           | ⏳ 需要外部数据源（如会员管理系统），待定                                                      |
> | `unknown` | 打错电话、陌生来电等无法判定身份的号码                                            | call-analysis `customer_type` = `other` 可作为信号；员工确认身份后可转为 `lead` + `active` |
>
> 员工可手动覆盖系统判定。
>
> **lifecycleState 判定逻辑**：
>
> Lead 阶段的 State 可从 `leadStatus` 推导；Member 和 Churned 阶段的 State 判定逻辑在生命周期管理文档中已有清晰定义，待对应数据源接入后即可实现。
>
> | Stage   | State                            | leadStatus                                                    | 数据源                               |
> | ------- | -------------------------------- | ------------------------------------------------------------- | --------------------------------- |
> | Lead    | `active`                         | New, Attempted, Connected, Booked, Showed, Trialed, Converted | LeadTracking-v2 `leadStatus`（待落库） |
> | Lead    | `frozen`                         | Bad Timing                                                    | LeadTracking-v2 `leadStatus`（待落库） |
> | Lead    | `terminal`                       | Not Interested, Unreachable, Lost Contact, Neglected          | LeadTracking-v2 `leadStatus`（待落库） |
> | Member  | `active`                         | —                                                             | ⏳ 待会员管理系统数据接入                     |
> | Churned | `frozen` / `active` / `terminal` | —                                                             | ⏳ 待会员管理系统数据接入                     |
>
> Stage × State 的完整组合和流转路径见 [客户生命周期管理 §2.4](/product-design/v1/contacts-feature/user-lifecycle-management.md)。

### 2.3 运营信息

> 对应 1.1 运营信息：分配给谁、员工备注、自定义标签、doNotContact (DNC)

| # | 字段    | 是否已有数据源 | 数据源 | 字段名            | 类型         | 存入 PhoneBook | 说明                                                       |
| - | ----- | ------- | --- | -------------- | ---------- | ------------ | -------------------------------------------------------- |
| 1 | Owner | 无       | —   | `owner`        | String     | 是            | 负责跟进该联系人的员工姓名；员工手动分配或系统自动轮转                              |
| 2 | 员工备注  | 无       | —   | `notes`        | String     | 是            | 员工手动录入的备注信息                                              |
| 3 | 自定义标签 | 无       | —   | `tags`         | String Set | 是            | 员工自定义标签，用于分组筛选                                           |
| 4 | DNC   | 无       | —   | `doNotContact` | Boolean    | 是            | 是否禁止联系；标记后系统停止一切主动触达。来源：AI 从通话/短信内容中识别 DNC 信号后写入，或员工手动标记 |

> **说明**：运营信息全部为员工操作产生的数据，当前数据库中没有现成数据源，均为 PhoneBook 新字段。
>
> **为什么叫 Owner**：沿用 CRM 行业惯例（Salesforce 叫 `OwnerId`，HubSpot 叫 `Contact Owner`）。Owner 不绑定具体角色——Lead 阶段的 Owner 可能是 BDR，Member 阶段可能是客户经理，Churned 阶段可能是留存专员，通用性更好。当前 `owner` 存储员工姓名（String），暂无独立的 Owner 表。未来如果做任务分配功能，可能会建立独立的 Owner 表存储员工详细信息（姓名、角色、门店归属等），届时可改为 `ownerId` 作为外键关联。call-analysis 中有 `staff_name` 字段（接听员工姓名），但这是单通电话的接听人，不等同于 Owner——`owner` 是客户级别的责任归属，由管理层分配。

### 2.4 活动数据

> 对应 1.1 活动数据：来源渠道、获取日期、首次尝试联系时间、首次触达时间、通话次数、短信次数、最后活动时间

| # | 字段       | 是否已有数据源 | 数据源                          | 字段名                | 类型     | 存入 PhoneBook | 说明                                                                                                                       |
| - | -------- | ------- | ---------------------------- | ------------------ | ------ | ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| 1 | 来源渠道     | 无       | —                            | `source`           | String | 是            | 指 Lead 从哪个广告平台来（Facebook / Instagram / Google 等），当前无数据源                                                                  |
| 2 | 获取时间     | 有       | LeadTracking-v2 `receivedAt` | `acquiredAt`       | String | 是            | ISO 8601；该客户首次进入系统的时间                                                                                                    |
| 3 | 首次尝试联系时间 | 无       | —                            | `firstAttemptedAt` | String | 是            | ISO 8601；员工首次尝试联系该客户的时间（不论是否接通）。LeadTracking-v2 和 PhoneBook 都存储                                                          |
| 4 | 首次触达时间   | 无       | —                            | `firstConnectedAt` | String | 是            | ISO 8601；员工首次真正接通/产生双向沟通的时间。判定依据待定（可选方案：call\_state = `human_conversation`）；仅首次写入，已有值不覆盖。LeadTracking-v2 和 PhoneBook 都存储 |
| 5 | 通话次数     | 无       | —                            | `callCount`        | Number | 是            | call-analysis 中没有此字段，每条记录只是一通电话。PhoneBook 预计算存储，通话管道写入 call-analysis 时同步对 PhoneBook 原子递增                                 |
| 6 | 短信次数     | 无       | —                            | `smsCount`         | Number | 是            | 同上，SMS 管道写入时同步对 PhoneBook 原子递增                                                                                           |
| 7 | 最后活动时间   | 无       | —                            | `lastActivityAt`   | String | 是            | ISO 8601；PhoneBook 新字段，每次新通话或 SMS 写入时自动更新为当前时间, LeadTracking-v2 和 PhoneBook 都存储                                          |

> **说明**：除来源渠道外，其余字段均有现成数据源，但源数据分散在多个表且粒度为单条记录。PhoneBook 存储的是客户级别的聚合值（如 callCount 是总次数，lastActivityAt 是最新时间），由各数据管道在写入源表时同步更新 PhoneBook。
>
> **关于 LeadTracking-v2 `leadType`**：该字段值为 `Web Lead`、`Online Intro`，描述的是 Lead 表单的**提交类型**（网页留资 vs 线上预约体验课），不是来源渠道。真正的来源渠道（Facebook / Instagram / Google / Walk-in / 转介绍等）需要对接广告平台或 CRM 系统，当前 LeadTracking-v2 中没有存储。`leadType` 已在 2.2 生命周期中分析过，不属于活动数据。
>
> **关于 LeadTracking-v2 `receivedAt`**：当前数据库中同一号码存在多条记录（未去重），`receivedAt` 记录的可能是最近一次提交时间，这是不对的。Lead 入库管道首先应去重，重复提交的数据不再存入，这样 `receivedAt` 才是真正的首次获取时间。
>
> **为什么 PhoneBook 叫 `acquiredAt` 而不是 `receivedAt`**：LeadTracking-v2 的字段叫 `receivedAt`（收到 Lead 表单），但 PhoneBook 改名为 `acquiredAt`，原因："acquired" 更通用——walk-in、主动来电等 non-Lead 路径客户不是 "received" 来的.
>
> **为什么 PhoneBook 要保留自己的时间字段**：活动数据中的 4 个时间字段（`acquiredAt`、`firstAttemptedAt`、`firstConnectedAt`、`lastActivityAt`）在 LeadTracking-v2 和 PhoneBook 中都存储。原因有二：
>
> 1. **非 Lead 路径客户没有 LeadTracking-v2 记录**：walk-in、主动来电、转介绍等场景下客户直接进入系统，不经过 Lead 提交流程。这类客户的时间数据无法从 LeadTracking-v2 获取，只能由 PhoneBook 自身记录。
> 2. **PhoneBook 是客户级别的统一视图**：即使是 Lead 路径客户，PhoneBook 也需要自己的时间字段来支撑列表排序、筛选、温度衰减等功能，避免每次都跨表查询 LeadTracking-v2。

### 2.5 联系偏好

> 对应 1.1 联系偏好：联系响应模式、最佳联系时段、偏好联系渠道

| # | 字段     | 是否已有数据源 | 数据源 | 字段名                | 类型     | 存入 PhoneBook | 说明                                                              |
| - | ------ | ------- | --- | ------------------ | ------ | ------------ | --------------------------------------------------------------- |
| 1 | 联系响应模式 | 无       | —   | `reachability`     | String | 是            | AI 从历史通话和 SMS 中归纳的响应特征，如 "首次通常不接，短信回复率高"、"来电秒接"                 |
| 2 | 最佳联系时段 | 无       | —   | `bestTimeToReach`  | String | 是            | AI 分析历史成功接通/回复的时间分布，如 "weekday 10:00-12:00"、"evening after 6pm" |
| 3 | 偏好联系渠道 | 无       | —   | `preferredChannel` | String | 是            | 枚举：`call` / `sms` / `either`；AI 根据渠道响应率推导，或员工手动标记               |

> **数据源分析**：
>
> 三个字段当前都没有直接数据源。原始信号分散在 call-analysis（接通率、通话时段、call\_state）和 MessageStore（回复率、回复时段）中，需要跨多条记录聚合分析才能得出结论。
>
> **写入方式**：Layer 2 Daily Batch AI 写入（与 `aiSummary`、`actionNeeded`、`suggestedActions` 同一管道）。员工可手动覆盖。
>
> | 字段                 | AI 推导逻辑                          | 数据来源                                                             |
> | ------------------ | -------------------------------- | ---------------------------------------------------------------- |
> | `reachability`     | 分析历史交互模式：接通率、首次 vs 多次尝试、SMS 回复速度 | call-analysis（call\_state、callResult）+ MessageStore（回复时间差）       |
> | `bestTimeToReach`  | 统计历史成功接通/回复的时间分布，取 Top 时间窗口      | call-analysis（callStartTime + call\_state = human\_conversation） |
> | `preferredChannel` | 对比 call 接通率 vs SMS 回复率，取响应率更高的渠道 | call-analysis（接通次数/总拨打次数）+ MessageStore（回复条数/总发送条数）              |

### 2.6 健身画像

> 对应 1.1 健身画像：核心需求、感兴趣的课程类型、偏好时段、健身经验（含过往经历）、当前运动习惯、关注点

| # | 字段       | 是否已有数据源 | 数据源 | 字段名                 | 类型         | 存入 PhoneBook | 说明                                                                                              |
| - | -------- | ------- | --- | ------------------- | ---------- | ------------ | ----------------------------------------------------------------------------------------------- |
| 1 | 核心需求     | 无       | —   | `fitnessGoal`       | String     | 是            | 客户的健身目标，如 "减重"、"增肌"、"康复训练"、"保持体能"                                                               |
| 2 | 感兴趣的课程类型 | 无       | —   | `classInterests`    | String Set | 是            | 感兴趣的课程，如 `{"yoga", "tread 50", "strength training"}`                                            |
| 3 | 偏好时段     | 无       | —   | `preferredTime`     | String     | 是            | 偏好的上课/到店时段，如 "weekday morning"、"weekend afternoon"                                              |
| 4 | 健身经验     | 无       | —   | `fitnessExperience` | String     | 是            | 过往健身经历，如 "3 years CrossFit"、"complete beginner"、"former member at another gym"                  |
| 5 | 当前运动习惯   | 无       | —   | `exerciseHabits`    | String     | 是            | 当前的运动习惯，如 "runs 3x/week"、"no regular exercise"、"home workouts"                                  |
| 6 | 关注点      | 无       | —   | `fitnessConcerns`   | String Set | 是            | 客户的顾虑或关注点，如 `{"injury history", "schedule flexibility", "intimidation"}`                        |
| 7 | 健康状况     | 无       | —   | `healthConditions`  | String Set | 是            | 身体限制/健康状况，影响训练方案和课程推荐，如 `{"hypertension", "knee injury", "diabetes", "pregnancy", "back pain"}` |

> **数据源分析**：
>
> 七个字段当前都没有直接数据源。但 call-analysis 的 `executive_summary` 中已经包含健身相关信号（如 "tread 50 class"、"first class experience"、"membership pricing"），只是以自然语言形式散落在摘要中，未被结构化提取。
>
> **写入方式**：Layer 2 Daily Batch AI 写入。AI 从该客户所有通话的 transcript + executive\_summary + SMS 内容中提取健身画像信息，结构化后写入 PhoneBook。员工可手动补充或覆盖。
>
> | 字段                  | AI 提取逻辑                 | 数据来源                                                                 |
> | ------------------- | ----------------------- | -------------------------------------------------------------------- |
> | `fitnessGoal`       | 从对话中识别客户的健身目标表述         | call-analysis（transcript、executive\_summary）+ MessageStore           |
> | `classInterests`    | 提取客户提到或预约过的课程类型         | call-analysis（executive\_summary、primary\_subcategory）+ MessageStore |
> | `preferredTime`     | 从预约行为和对话中推导偏好时段         | call-analysis（executive\_summary 中的时间提及）+ MessageStore               |
> | `fitnessExperience` | 从对话中识别过往健身经历的描述         | call-analysis（transcript）                                            |
> | `exerciseHabits`    | 从对话中识别当前运动习惯的描述         | call-analysis（transcript）                                            |
> | `fitnessConcerns`   | 从对话中识别客户顾虑和担心           | call-analysis（transcript、executive\_summary）+ MessageStore           |
> | `healthConditions`  | 从对话中识别客户提到的身体状况、伤病史、慢性病 | call-analysis（transcript）；员工手动补充优先级更高                                |

### 2.7 通讯记录

> 对应 1.1 通讯记录：SMS 内容、通话录音（音频回放）、逐句对话记录（Conversation Data，分 Speaker + 时间戳）、语音信箱、系统备注
>
> ⚠️ 与 2.1–2.6 不同：通讯记录是**原始事件流**，**不存入 PhoneBook**。通过统一的 `ActivityLog` 索引表按时间倒序渲染为 Communication Timeline。

#### 数据源概览

| 记录类型   | 原始数据源                                      | 当前状态                                               |
| ------ | ------------------------------------------ | -------------------------------------------------- |
| 通话录音   | call-analysis → S3 音频文件                    | ✅ 28,275 条录音已下载至 S3                                |
| 语音信箱   | call-analysis（`isVoicemail = true`）→ S3 音频 | ✅ 20,224 条语音信箱                                     |
| 逐句对话记录 | call-analysis → S3 transcript JSON         | ⏳ 语音信箱转录完成 765 条；双向对话转录管道已启动但未完成（27,620 条 pending） |
| SMS 内容 | MessageStore                               | ❌ 表尚未创建                                            |
| 系统备注   | 各业务管道触发写入                                  | ❌ 待建                                               |

#### ActivityLog — ~~统一 Timeline 索引表~~ **已废弃**

::: warning 架构决策 (2026-03-09)：不再建独立 ActivityLog DynamoDB 表
**结论**：计划将 call-analysis、MessageStore、LeadTracking-v2 全量迁移到 Neon PostgreSQL 后，用 `UNION ALL` VIEW 替代 ActivityLog。

**原因**：ActivityLog 的唯一动机是 DynamoDB 无法跨表查询。PostgreSQL 原生支持 `UNION ALL` + `ORDER BY` + `LIMIT`，三张表的时间线在数据库层合并，无需预建索引表。

**替代方案**：在 Neon 中创建 `contact_timeline` VIEW（`calls UNION ALL sms_messages UNION ALL leads`），每张表加 `(phone, created_at DESC)` 索引。预期延迟 \<50ms。

**参考**：GitLab 在 PostgreSQL 上明确禁止 polymorphic events 表，采用分表 + UNION ALL；HubSpot 用分表 JOIN 而非独立 timeline 表。

:::
以下为原始设计（保留供参考，**不实施**）。

***

**原设计背景**：通讯记录分散在多个源表（call-analysis、MessageStore、系统操作），如果 Timeline UI 每次加载都要查多张表再合并排序，查询复杂且性能差。ActivityLog 作为预建索引，让 UI 只查一张表就能拿到所有事件的时间线。

**原设计工作方式**：各数据管道在写入源表的同时，同步写入一条轻量索引记录到 ActivityLog。每条记录只存展示摘要 + 源表引用键，不重复存储完整数据。

**表设计**：

| 字段                  | 类型     | 说明                                                                                       |
| ------------------- | ------ | ---------------------------------------------------------------------------------------- |
| `phone`             | String | PK，客户手机号（E.164）                                                                          |
| `timestamp#eventId` | String | SK，事件时间 + 唯一 ID，保证时间排序且不冲突                                                               |
| `type`              | String | 事件类型：`call` / `sms` / `system`                                                           |
| `summary`           | String | 展示摘要，如 "Outbound · 66s · Call connected"                                                 |
| `ref`               | String | 源表引用键（通话为 `telephonySessionId`，SMS 为 `messageId`，系统备注无 ref）                              |
| `direction`         | String | `inbound` / `outbound`（仅 call / sms）；UI 用左右布局区分：inbound 显示在左侧（客户发起），outbound 显示在右侧（员工发起） |
| `metadata`          | Map    | 类型相关的轻量摘要数据（见下方）                                                                         |

**metadata 示例**：

```json
// type = "call"
{ "duration": 66, "callResult": "Call connected", "call_state": "human_conversation", "staff": "Lucas", "hasRecording": true }

// type = "sms"
{ "status": "delivered", "preview": "Hi, thanks for your interest..." }

// type = "system"
{ "event": "owner_changed", "detail": "Owner assigned: Lucas" }
```

**写入方式**：

| 触发场景    | type     | 写入时机                                   |
| ------- | -------- | -------------------------------------- |
| 通话管道    | `call`   | 写入 call-analysis 时同步写入                 |
| SMS 管道  | `sms`    | 写入 MessageStore 时同步写入                  |
| Lead 入库 | `system` | LeadTracking-v2 写入时同步写入                |
| 员工操作    | `system` | PhoneBook 字段变更（Owner、DNC、Tag、Stage）时写入 |
| CRM 回调  | `system` | 未来 CRM 对接后写入                           |

**查询方式**：Timeline UI 查询 `ActivityLog`（PK=`phone`，SK 倒序），一次查询拿到所有事件。用户点击某条记录展开详情时，根据 `ref` 去源表（call-analysis / MessageStore）加载完整数据（录音、转录、完整短信等）。

#### 系统备注（System Note，待建）

人工或系统触发的操作事件，在 Timeline 中穿插显示，帮助员工了解客户旅程的关键节点。**不包含 AI 通话分析推导出的信息**（那些属于 2.8/2.9）。

| # | 事件类型       | 触发场景                  | 说明                                           |
| - | ---------- | --------------------- | -------------------------------------------- |
| 1 | Lead 提交    | LeadTracking-v2 写入时   | "New lead submitted via Facebook — Web Lead" |
| 2 | Owner 变更   | PhoneBook `owner` 更新时 | "Owner assigned: Lucas"                      |
| 3 | DNC 手动标记   | 员工手动标记/取消             | "Marked as Do Not Contact" / "DNC removed"   |
| 4 | Tag 变更     | 员工手动添加/移除             | "Tag added: VIP" / "Tag removed: Hot Lead"   |
| 5 | Stage 手动变更 | 员工手动调整                | "Stage changed: Lead → Member"               |

**CRM 对接后扩展**（当前无数据源，未来接入 CRM 系统后可增加）：

| # | 事件类型       | 触发场景   | 说明                                                          |
| - | ---------- | ------ | ----------------------------------------------------------- |
| 6 | 会籍变更       | CRM 回调 | "Membership purchased: Elite Plan" / "Membership cancelled" |
| 7 | 预约创建/取消/改期 | CRM 回调 | "Intro class booked for Mar 5, 2026 10:00 AM"               |
| 8 | 课程签到       | CRM 回调 | "Checked in: Tread 50 @ 9:00 AM"                            |
| 9 | 付款事件       | CRM 回调 | "Payment processed: $59.00" / "Payment failed"              |

> **说明**：系统备注直接以 `type=system` 写入 ActivityLog，不需要独立的源表——ActivityLog 本身就是系统备注的存储位置。各业务管道在执行操作时（Lead 入库、员工改 Owner/DNC/Tag/Stage、CRM 回调等）同步写入一条 `type=system` 记录。

### 2.8 AI 通话分析（Per-Call）

> 对应 1.1 AI通话分析：通话摘要（AI 摘要、follow\_up\_needed、follow\_up\_reasons）· 关键信息（主分类、子分类、次要话题、通话结果分类、是否获取信用卡、营收优先级）· 通话详情（接听结果、通话方向、通话时长等）
>
> ⚠️ 与 2.7 一样，**不存入 PhoneBook**。这些是 call-analysis 表中每通电话的 AI 分析字段，存储在 call-analysis DynamoDB + S3。用户在 Timeline（ActivityLog）中点击某通电话，通过 `ref`（`telephonySessionId`）查询 call-analysis 获取完整分析数据并展示。

### 2.9 AI 跨通话分析（Cross-Call）

> 对应 1.1 AI跨通话分析：客户摘要（customerSummary）、是否需要跟进（actionNeeded，跨通话综合判断）、建议动作、建议截止时间、建议优先级
>
> ⚠️ 与 2.7/2.8 不同：2.9 字段**存入 PhoneBook**。这是 Layer 2（Daily Batch）读取该客户所有通话的 2.8 分析数据后，跨通话聚合生成的客户级别判断。

| # | 字段     | 字段名                   | 类型                                                 | 存入 PhoneBook | 说明                                                                                                                                                                |
| - | ------ | --------------------- | -------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | 客户摘要   | `customerSummary`     | String                                             | 是            | 综合该客户所有通话和 SMS 生成的整体画像摘要，如 "Prospective client interested in Tread 50, visited once, price-sensitive, prefers morning slots. Follow-up twice but no booking yet." |
| 2 | 是否需要跟进 | `actionNeeded`        | Boolean                                            | 是            | 跨通话综合判断。区别于 2.8 的 `follow_up_needed`（单通电话级别）：即使最近一通电话说 "no"，但综合历史通话模式仍可能判定为 "yes"                                                                                 |
| 3 | 建议动作列表 | `suggestedActions`    | List\<\{action, reason, priority, priorityReason}> | 是            | AI 建议的下一步操作列表，每个元素包含 action、reason、priority、priorityReason                                                                                                        |
| 4 | 建议截止时间 | `suggestedFollowUpBy` | ISO 8601 DateTime                                  | 是            | AI 建议的跟进最晚时间，系统用此判定任务 Upcoming / Due / Overdue。详见 [触发规则 §4.1](/product-design/v1/tasks-feature/tasks-overview.md)                                                 |

> **数据源分析**：
>
> 四个字段当前都没有数据源，均为 PhoneBook 新字段。
>
> **写入方式**：Layer 2 Daily Batch AI 写入。每日批量读取该客户在 call-analysis 中的所有通话记录（2.8 字段）+ MessageStore 中的 SMS 记录，综合分析后写入 PhoneBook。员工可手动覆盖。
>
> **与 2.5/2.6 的关系**：2.5（联系偏好）、2.6（健身画像）也是 Layer 2 写入的 PhoneBook 字段，但它们提取的是具体的结构化数据（偏好时段、健身目标等）。2.9 产出的是高层判断：这个客户整体情况如何（`customerSummary`）、是否需要行动（`actionNeeded`）、具体做什么（`suggestedActions`，每个 action 内含 priority）、最晚什么时候做（`suggestedFollowUpBy`）。

### 2.10 Lead 跟进（Lead 专属）

> 对应 1.2 Lead 跟进：leadStatus、temperature
>
> ⚠️ 仅在 `lifecycleStage = Lead` 时适用，**同时存入 PhoneBook + LeadTracking-v2**。

| # | 字段      | 是否已有数据源 | 数据源 | 字段名           | 类型     | 存入 PhoneBook | 说明                                                                                                                                                                                                                                                                                          |
| - | ------- | ------- | --- | ------------- | ------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Lead 状态 | 无       | —   | `leadStatus`  | String | 是            | 枚举（12 选 1）：`new` / `attempted` / `connected` / `booked` / `showed` / `trialed` / `converted` /  `unreachable` / `lost_contact` / `neglected` / `bad_timing` / `not_interested`。完整定义见 [Lead Funnel 与 Status 汇总](/product-design/v1/lead-tracker-feature/lead-funnel-status.md#一lead-的-9-个状态) |
| 2 | Lead 温度 | 无       | —   | `temperature` | String | 是            | 枚举（4 选 1）：`hot` / `warm` / `cold` / `unscored`。基于时间衰减 + 互动事件自动计算，员工可手动覆盖。完整规则见 [Lead Temperature](/product-design/future-plans/lead-temperature.md)                                                                                                                                         |

> **写入方式**：
>
> | 字段            | 写入方式                                                                                                               |
> | ------------- | ------------------------------------------------------------------------------------------------------------------ |
> | `leadStatus`  | 系统自动流转（通话管道 + AI 识别）+ 员工手动覆盖。前 4 步（new→attempted→connected→booked）由 Lead-Outcome-Mapper 自动推进；终态由温度冷却触发或 AI 识别+员工确认 |
> | `temperature` | 系统自动计算（时间衰减 + 互动升温）+ 员工手动覆盖（最高优先级）                                                                                 |
>
> | temperature | 含义                 | 响应窗口          |
> | ----------- | ------------------ | ------------- |
> | `hot`       | 高意向 / 刚互动（Day 0–1） | 当天            |
> | `warm`      | 有意向但不紧急（Day 2–5）   | 2–3 天内        |
> | `cold`      | 跟进窗口已过（Day 5+）     | 停止主动跟进，等待升温事件 |
> | `unscored`  | 数据不足，无法评分          | 按来源排序         |
>
> **关于 `Duplicate` 和 `Invalid Number`**：不属于 `leadStatus`——它们是数据质量判定，与跟进状态是两个独立维度。应在 LeadTracking-v2 中以独立字段标记（如 `isDuplicate`、`isValidNumber`），不写入 PhoneBook，用于计算有效率等指标。

### 2.11 Lead 分析（Lead 专属）

> 对应 1.2 Lead 分析：意向等级、提交次数
>
> ⚠️ 仅在 `lifecycleStage = Lead` 时适用，**同时存入 PhoneBook + LeadTracking-v2**。

| # | 字段   | 是否已有数据源 | 数据源 | 字段名               | 类型     | 存入 PhoneBook | 说明                                               |
| - | ---- | ------- | --- | ----------------- | ------ | ------------ | ------------------------------------------------ |
| 1 | 意向等级 | 无       | —   | `purchaseIntent`  | String | 是            | 枚举：`high` / `medium` / `low`。AI 综合通话内容判断客户购买意向强度 |
| 2 | 提交次数 | 无       | —   | `submissionCount` | Number | 是            | 同一手机号提交 Lead 表单的次数，反映客户兴趣强度。由 Lead 入库管道计算并写入     |

> **写入方式**：
>
> | 字段                | AI 推导逻辑                                                      | 数据来源                                                       |
> | ----------------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
> | `purchaseIntent`  | 从对话中识别购买信号强度：主动询价/预约 = high，犹豫/收集信息 = medium，明确拒绝/兴趣极低 = low | call-analysis（transcript、executive\_summary）+ MessageStore |
> | `submissionCount` | 统计 LeadTracking-v2 中同一 phone 的记录数                            | LeadTracking-v2（COUNT by phone）                            |
>
> **关于 `submissionCount`**：LeadTracking-v2 中没有现成的提交次数字段，但同一手机号确实存在多条记录（5,078 条记录中有 678 个手机号重复提交，最多达 11 次）。Lead 入库管道在写入时统计同一 phone 的记录数，写入 LeadTracking-v2，同步更新 PhoneBook。

### 2.12 消费决策（Lead 专属）

> 对应 1.2 消费决策：预算区间、价格敏感度、决策风格、决策周期
>
> ⚠️ 仅在 `lifecycleStage = Lead` 时适用，**同时存入 PhoneBook + LeadTracking-v2**。

| # | 字段    | 是否已有数据源 | 数据源 | 字段名                | 类型     | 存入 PhoneBook | 说明                                                |
| - | ----- | ------- | --- | ------------------ | ------ | ------------ | ------------------------------------------------- |
| 1 | 预算区间  | 无       | —   | `budgetRange`      | String | 是            | 客户表述的可接受价格范围，如 "$50-100/month"、"under $200"       |
| 2 | 价格敏感度 | 无       | —   | `priceSensitivity` | String | 是            | 枚举：`high` / `medium` / `low`。AI 从对话中判断客户对价格的关注程度  |
| 3 | 决策风格  | 无       | —   | `decisionProcess`  | String | 是            | 如 "需要和家人商量"、"当场决定型"、"需要多次沟通"                      |
| 4 | 决策周期  | 无       | —   | `decisionTimeline` | String | 是            | 客户表述的预期决策时间，如 "this week"、"next month"、"not sure" |

> **写入方式**：Layer 2 Daily Batch AI 写入。AI 从通话 transcript + SMS 中提取消费决策信号，结构化后写入 PhoneBook。员工可手动补充或覆盖。
>
> | 字段                 | AI 提取逻辑          | 数据来源                                         |
> | ------------------ | ---------------- | -------------------------------------------- |
> | `budgetRange`      | 识别客户提到的价格/预算相关表述 | call-analysis（transcript）+ MessageStore      |
> | `priceSensitivity` | 分析客户对价格的反应强度和频率  | call-analysis（transcript、executive\_summary） |
> | `decisionProcess`  | 识别客户的决策模式描述      | call-analysis（transcript）                    |
> | `decisionTimeline` | 识别客户表述的时间预期      | call-analysis（transcript）+ MessageStore      |

### 2.13 决策障碍（Lead 专属）

> 对应 1.2 决策障碍：objections
>
> ⚠️ 仅在 `lifecycleStage = Lead` 时适用，**同时存入 PhoneBook + LeadTracking-v2**。

| # | 字段   | 是否已有数据源 | 数据源 | 字段名          | 类型         | 存入 PhoneBook | 说明                                                                                     |
| - | ---- | ------- | --- | ------------ | ---------- | ------------ | -------------------------------------------------------------------------------------- |
| 1 | 决策障碍 | 无       | —   | `objections` | String Set | 是            | 客户犹豫中的顾虑标签，如 `{"price concerns", "schedule conflicts", "need to discuss with family"}` |

> **写入方式**：员工手动输入 或 AI 从通话中自动提取。

### 2.14 条件性拒绝原因（Lead 专属）

> 对应 1.2 条件性拒绝：rejectionReasons
>
> ⚠️ 仅在 `lifecycleStage = Lead` 时适用，**同时存入 PhoneBook + LeadTracking-v2**。

| # | 字段   | 是否已有数据源 | 数据源 | 字段名                | 类型         | 存入 PhoneBook | 说明                                                                    |
| - | ---- | ------- | --- | ------------------ | ---------- | ------------ | --------------------------------------------------------------------- |
| 1 | 拒绝原因 | 无       | —   | `rejectionReasons` | String Set | 是            | 客户已明确拒绝的原因标签，如 `{"too far", "already has membership", "moving away"}` |

> **写入方式**：AI 从通话中识别明确拒绝信号后写入，员工确认或手动标记。
>
> **`objections` vs `rejectionReasons` 的区别**：
>
> - `objections`：客户还在犹豫，有挽回空间（"太贵了" → 可以介绍优惠方案）
> - `rejectionReasons`：客户已明确拒绝，挽回可能性低（"太远了" → 无法改变地理位置）
>
> `rejectionReasons` 仅在 `leadStatus = bad_timing` 时适用——客户因具体条件拒绝（太远、时间冲突、已有会籍等），条件可能变化。`not_interested` 是无条件拒绝，不记录具体原因。

***

## 三、`PhoneBook` 最终表设计

### 主表 (`PhoneBook-{env}`)

> 跳过 2.7（通讯记录）和 2.8（AI 通话分析）——它们不存入 PhoneBook，通过 ActivityLog 索引表查询。email 不存入 PhoneBook，查询时从 LeadTracking-v2 读取；邮箱反查客户使用辅助表 `PhoneBook-EmailMapping`。

| #  | 字段                    | 类型                                                 | 说明                                                                                                                                                   | 来源                              | 详见   |
| -- | --------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ---- |
| 1  | `phone`               | String (PK)                                        | E.164 格式，全局唯一客户标识                                                                                                                                    | 自动                              | —    |
| 2  | `orgId`               | String                                             | 所属组织 ID                                                                                                                                              | 自动                              | —    |
| 3  | `siteId`              | String                                             | 所属门店                                                                                                                                                 | 自动                              | —    |
| 4  | `firstName`           | String                                             | 名                                                                                                                                                    | Lead 入库管道；员工可覆盖                 | 2.1  |
| 5  | `lastName`            | String                                             | 姓                                                                                                                                                    | Lead 入库管道；员工可覆盖                 | 2.1  |
| 6  | `gender`              | String                                             | 性别                                                                                                                                                   | V1 预留                           | 2.1  |
| 7  | `address`             | String                                             | 地址                                                                                                                                                   | V1 预留                           | 2.1  |
| 8  | `occupation`          | String                                             | 工作                                                                                                                                                   | V1 预留                           | 2.1  |
| 9  | `birthday`            | String                                             | 生日                                                                                                                                                   | V1 预留                           | 2.1  |
| 10 | `dailyRoutine`        | String                                             | 作息模式                                                                                                                                                 | V1 预留                           | 2.1  |
| 11 | `lifecycleStage`      | String                                             | `lead` / `member` / `churned` / `unknown`                                                                                                            | 系统自动判定 + 员工覆盖                   | 2.2  |
| 12 | `lifecycleState`      | String                                             | `active` / `frozen` / `terminal`                                                                                                                     | 从 leadStatus 推导（Lead 阶段）        | 2.2  |
| 13 | `owner`               | String                                             | 负责跟进的员工姓名                                                                                                                                            | 员工手动 / 系统轮转                     | 2.3  |
| 14 | `notes`               | String                                             | 员工手写备注                                                                                                                                               | 员工手动                            | 2.3  |
| 15 | `tags`                | String Set                                         | 自定义标签，用于分组筛选                                                                                                                                         | 员工手动                            | 2.3  |
| 16 | `doNotContact`        | Boolean                                            | DNC 标记，全渠道停止触达                                                                                                                                       | AI 识别 + 员工手动                    | 2.3  |
| 17 | `source`              | String                                             | 来源渠道（Facebook / Instagram / Google 等）                                                                                                                | 创建时写入，不覆盖                       | 2.4  |
| 18 | `acquiredAt`          | String (ISO)                                       | 客户首次进入系统的时间                                                                                                                                          | Lead 入库管道                       | 2.4  |
| 19 | `firstAttemptedAt`    | String (ISO)                                       | 首次尝试联系时间（不论是否接通）                                                                                                                                     | 通话管道首次写入                        | 2.4  |
| 20 | `firstConnectedAt`    | String (ISO)                                       | 首次真正接通时间                                                                                                                                             | 通话管道首次写入                        | 2.4  |
| 21 | `callCount`           | Number                                             | 通话总次数                                                                                                                                                | 通话管道 `ADD +1`                   | 2.4  |
| 22 | `smsCount`            | Number                                             | 短信总条数                                                                                                                                                | SMS 管道 `ADD +1`                 | 2.4  |
| 23 | `lastActivityAt`      | String (ISO)                                       | 最后活动时间（任何互动都更新）                                                                                                                                      | 通话/Lead/SMS 管道                  | 2.4  |
| 24 | `reachability`        | String                                             | 联系响应模式                                                                                                                                               | Layer 2 AI；员工可覆盖                | 2.5  |
| 25 | `bestTimeToReach`     | String                                             | 最佳联系时段                                                                                                                                               | Layer 2 AI；员工可覆盖                | 2.5  |
| 26 | `preferredChannel`    | String                                             | `call` / `sms` / `either`                                                                                                                            | Layer 2 AI；员工可覆盖                | 2.5  |
| 27 | `fitnessGoal`         | String                                             | 核心需求（减重、增肌等）                                                                                                                                         | Layer 2 AI；员工可覆盖                | 2.6  |
| 28 | `classInterests`      | String Set                                         | 感兴趣的课程类型                                                                                                                                             | Layer 2 AI；员工可覆盖                | 2.6  |
| 29 | `preferredTime`       | String                                             | 偏好上课时段                                                                                                                                               | Layer 2 AI；员工可覆盖                | 2.6  |
| 30 | `fitnessExperience`   | String                                             | 过往健身经历                                                                                                                                               | Layer 2 AI；员工可覆盖                | 2.6  |
| 31 | `exerciseHabits`      | String                                             | 当前运动习惯                                                                                                                                               | Layer 2 AI；员工可覆盖                | 2.6  |
| 32 | `fitnessConcerns`     | String Set                                         | 关注点/顾虑                                                                                                                                               | Layer 2 AI；员工可覆盖                | 2.6  |
| 33 | `healthConditions`    | String Set                                         | 身体限制/健康状况                                                                                                                                            | Layer 2 AI；员工手动优先               | 2.6  |
| 34 | `customerSummary`     | String                                             | 综合所有通话和 SMS 的整体画像摘要                                                                                                                                  | Layer 2 AI                      | 2.9  |
| 35 | `actionNeeded`        | Boolean                                            | 跨通话综合判断是否需要跟进                                                                                                                                        | Layer 2 AI                      | 2.9  |
| 36 | `suggestedActions`    | List\<\{action, reason, priority, priorityReason}> | AI 建议的下一步操作列表，每个元素包含 action、reason、priority、priorityReason                                                                                           | Layer 2 AI                      | 2.9  |
| 37 | `suggestedFollowUpBy` | String (ISO)                                       | AI 建议的跟进最晚时间                                                                                                                                         | Layer 2 AI                      | 2.9  |
| 38 | `leadStatus`          | String                                             | 12 选 1：new / attempted / connected / booked / showed / trialed / converted / unreachable / lost\_contact / neglected / bad\_timing / not\_interested | 系统自动 + 员工覆盖；双写 LeadTracking-v2  | 2.10 |
| 39 | `temperature`         | String                                             | `hot` / `warm` / `cold` / `unscored`                                                                                                                 | 时间衰减 + 互动升温；双写 LeadTracking-v2  | 2.10 |
| 40 | `purchaseIntent`      | String                                             | `high` / `medium` / `low`                                                                                                                            | Layer 2 AI；双写 LeadTracking-v2   | 2.11 |
| 41 | `submissionCount`     | Number                                             | 同一手机号提交 Lead 表单的次数                                                                                                                                   | Lead 入库管道；双写 LeadTracking-v2    | 2.11 |
| 42 | `budgetRange`         | String                                             | 可接受价格范围                                                                                                                                              | Layer 2 AI；双写 LeadTracking-v2   | 2.12 |
| 43 | `priceSensitivity`    | String                                             | `high` / `medium` / `low`                                                                                                                            | Layer 2 AI；双写 LeadTracking-v2   | 2.12 |
| 44 | `decisionProcess`     | String                                             | 决策风格（需要和家人商量、当场决定型等）                                                                                                                                 | Layer 2 AI；双写 LeadTracking-v2   | 2.12 |
| 45 | `decisionTimeline`    | String                                             | 预期决策时间                                                                                                                                               | Layer 2 AI；双写 LeadTracking-v2   | 2.12 |
| 46 | `objections`          | String Set                                         | 犹豫中的顾虑标签                                                                                                                                             | 员工手动 / AI 提取；双写 LeadTracking-v2 | 2.13 |
| 47 | `rejectionReasons`    | String Set                                         | 明确拒绝的原因标签                                                                                                                                            | AI 识别 + 员工确认；双写 LeadTracking-v2 | 2.14 |

共 47 个字段（系统 3 + 身份 7 + 生命周期 2 + 运营 4 + 活动 7 + 联系偏好 3 + 健身画像 7 + AI 跨通话 4 + Lead 跟进 2 + Lead 分析 2 + 消费决策 4 + 决策障碍 1 + 条件性拒绝 1）

### GSI 索引

| 索引名                         | PK     | SK             | 用途                             |
| --------------------------- | ------ | -------------- | ------------------------------ |
| orgId-temperature-index     | orgId  | temperature    | 按组织筛选温度                        |
| siteId-temperature-index    | siteId | temperature    | 按门店 + 温度筛选                     |
| orgId-lifecycleStage-index  | orgId  | lifecycleStage | 按类型筛选（Lead / Member / Unknown） |
| orgId-lastActivityAt-index  | orgId  | lastActivityAt | 按组织查客户，最近活跃排前                  |
| siteId-lastActivityAt-index | siteId | lastActivityAt | 按门店查客户，最近活跃排前                  |

共 5 个 GSI

### 辅助表

| 表                             | PK                            | 字段                       | 用途                                                               |
| ----------------------------- | ----------------------------- | ------------------------ | ---------------------------------------------------------------- |
| PhoneBook-EmailMapping-\{env} | email                         | email, phone             | 邮箱反查客户（email 不存入 PhoneBook 主表）                                   |
| PhoneBook-Alias-\{env}        | aliasPhone                    | aliasPhone, primaryPhone | 旧号码映射到主号码                                                        |
| ~~ActivityLog-\{env}~~        | ~~phone + timestamp#eventId~~ | ~~见 2.7~~                | ~~统一 Timeline 索引表~~ **已废弃**，迁移 PostgreSQL 后用 `UNION ALL` VIEW 替代 |

### 独立表（非 PhoneBook 字段，但 Contacts 页面依赖）

| 表                      | 用途                                                                     | 详见        |
| ---------------------- | ---------------------------------------------------------------------- | --------- |
| ~~ActivityLog-\{env}~~ | ~~统一 Timeline 索引~~ **已废弃**，迁移 PostgreSQL 后用 `contact_timeline` VIEW 替代 | 2.7       |
| call-analysis-\{env}   | 通话原始数据 + Per-Call AI 分析结果                                              | 2.7 / 2.8 |
| MessageStore-\{env}    | SMS 原始数据（待建）                                                           | 2.7       |
| LeadTracking-v2-\{env} | Lead 表单提交数据，与 PhoneBook Lead 字段双向同步                                    | 2.10–2.14 |

***

## 四、不存入 PhoneBook 的展示数据

以下数据在 Contacts 页面中展示，但不存入 PhoneBook 表，由 API 从其他表实时获取：

| 展示内容                      | 查询来源                                           | 查询方式                                                                              |
| ------------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------- |
| Email                     | LeadTracking-v2                                | `leadContactEmail`                                                                |
| Lead 提交类型 (leadType)      | LeadTracking-v2                                | `leadType`（Web Lead / Online Intro）                                               |
| Communication Timeline    | ~~ActivityLog~~ → Neon `contact_timeline` VIEW | `UNION ALL` 跨 calls / sms\_messages / leads 表，`ORDER BY created_at DESC LIMIT 50` |
| Per-Call AI 分析（摘要、分类、跟进等） | call-analysis（迁移后 Neon `calls` 表）              | 通过 `telephonySessionId` 查询                                                        |
| Transcript（逐句对话记录）        | S3 JSON                                        | 通过 call-analysis `s3AnalysisPath` 获取                                              |
| SMS 完整内容                  | MessageStore（迁移后 Neon `sms_messages` 表）        | 通过 `messageId` 查询                                                                 |
| 通话录音（音频回放）                | S3 音频                                          | 通过 call-analysis `recordingUrl` 获取                                                |

***

## 五、写入管道

| # | 管道                  | 触发时机           | 写入 PhoneBook 字段                                                                                                                                                                                                        | 同步写入                                                    |
| - | ------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| 1 | 通话管道                | 每通电话处理完成       | `callCount++`, `lastActivityAt`, `firstAttemptedAt`（首次）, `firstConnectedAt`（首次接通）                                                                                                                                      | ~~ActivityLog~~ 不再需要，PostgreSQL 直接查 `calls` 表           |
| 2 | Lead 入库管道           | 每条新 Lead 写入    | `firstName`, `lastName`, `acquiredAt`（首次）, `submissionCount`, `lastActivityAt`                                                                                                                                         | ~~ActivityLog~~ 不再需要，PostgreSQL 直接查 `leads` 表           |
| 3 | SMS 管道              | 每条短信处理完成       | `smsCount++`, `lastActivityAt`                                                                                                                                                                                         | ~~ActivityLog~~ 不再需要，PostgreSQL 直接查 `sms_messages` 表    |
| 4 | Lead 状态管道           | leadStatus 变更时 | `leadStatus`, `lifecycleStage`, `lifecycleState`                                                                                                                                                                       | LeadTracking-v2                                         |
| 5 | Layer 2 Daily Batch | 每天凌晨 AI 批处理    | `customerSummary`, `actionNeeded`, `suggestedActions`, `suggestedFollowUpBy`, `reachability`, `bestTimeToReach`, `preferredChannel`, 2.6 健身画像 7 字段, `purchaseIntent`, 2.12 消费决策 4 字段, `objections`, `rejectionReasons` | LeadTracking-v2（Lead 字段）                                |
| 6 | Layer 3 On-Demand   | 员工点 Refresh AI | `customerSummary`（刷新）                                                                                                                                                                                                  | —                                                       |
| 7 | 员工手动操作              | 员工在 UI 中编辑     | `owner`, `notes`, `tags`, `doNotContact`, `temperature`, + 任何 AI 字段可覆盖                                                                                                                                                 | ~~ActivityLog~~ 不再需要，PostgreSQL `system_events` 表记录变更审计 |

**管道设计原则：**

1. **写入轻量化**：管道 1-3 只写计数器和时间戳（原子 `ADD` + `SET`），单次写入成本极低
2. **数据一致性**：PhoneBook 不冗余存储明细数据（call 详情、SMS 内容），避免与源表不一致
3. **AI 管道集中**：Layer 2 一次性写入所有 AI 提取字段（联系偏好 + 健身画像 + 跨通话分析 + Lead 分析 + 消费决策 + 决策障碍/拒绝），减少写入次数
4. **双写同步**：Lead 专属字段（2.10–2.14）同时写入 PhoneBook + LeadTracking-v2，保证两张表数据一致

***

## 六、Contacts 页面结构

Contacts 页面由三个核心区域组成，通过两种视图模式切换布局：

### 6.1 三大区域

| 区域                         | 功能                                  | 核心交互                 |
| -------------------------- | ----------------------------------- | -------------------- |
| **名单列表** Contact List      | 以表格形式展示所有联系人，支持排序、筛选、搜索             | 点击行 → 选中联系人，右侧面板同步更新 |
| **用户详情** Contact Detail    | 展示选中联系人的身份信息、跟进状态、AI 分析结果           | 查看/编辑联系人属性，快速了解客户全貌  |
| **通讯记录** Communication Log | 按时间线展示该联系人的所有沟通事件（SMS、通话、语音信箱、系统备注） | 浏览历史互动，展开通话详情        |

### 6.2 两种视图模式

```
列表模式（默认）                          详情模式（点击展开）
┌──────────────────┬──────────┐      ┌──────────┬──────────────────┐
│                  │ 用户详情  │      │ 用户详情  │                   │
│   名单列表        │          │      │          │   通讯记录         │
│  (Contact List)  ├──────────┤  →   │ (Detail  │ (Communication   │
│                  │ 通讯记录  │      │  Panel)  │      Log)        │
│                  │          │      │          │                  │
└──────────────────┴──────────┘      └──────────┴──────────────────┘
```

- **列表模式**：左侧为名单表格（主区域），右侧侧边栏上下排列用户详情和通讯记录
- **详情模式**：左侧为用户详情面板（完整展示），右侧为通讯记录（全屏展开），名单列表隐藏

### 6.3 通讯记录 Communication Log 详细结构

按日期分组的时间线，每条事件根据类型有不同的展示样式：

#### 6.3.1 事件类型

| 类型              | 样式             | 内容                                                            |
| --------------- | -------------- | ------------------------------------------------------------- |
| **SMS**         | 💬 SMS         | 人名 + 时间 + SMS 内容                                              |
| **Call**        | 📞 Call        | 人名 + 时间 + AI 摘要 + 音频播放器                                       |
| **Voicemail**   | 📞 Voicemail   | 人名 + 时间 + 语音文本 + 语音信箱播放器                                      |
| **System Note** | 🕐 System Note | 人名 + 时间 + 系统事件描述（如 "Lead received from website contact form"） |

**通讯记录卡片左右位置规则：**

- **左侧**：客户发起的信息（客户来电、客户发送的SMS、 客户的Voicemail）
- **右侧**：系统/门店发起的信息（门店外呼、门店发送的SMS、门店的Voicemail、System Note）

#### 6.3.2 通话卡片（Call Card）

通话卡片默认展示以下信息：

| 区块    | 内容                                                                                                             |
| ----- | -------------------------------------------------------------------------------------------------------------- |
| 头部    | 员工名 + 时间 + 通话类型标签（Call）                                                                                        |
| AI 摘要 | 本次通话的 executive\_summary（如 "Called prospect. No answer. Left voicemail with intro class times and $12 offer."） |
| 音频播放器 | 通话录音回放，显示时长进度                                                                                                  |
| 展开按钮  | Hide/Show Details，点击可展开下方详细信息                                                                                  |

#### 6.3.3 通话展开详情（Hide/Show Details）

点击通话卡片的展开按钮后，按 **AI 分析结论 → 通话元数据 → 原始对话** 的顺序展示：

_1. Key Information（AI 分析结论）_

| 字段               | 说明                                  |
| ---------------- | ----------------------------------- |
| Category         | 主分类（Sales、Revenue Impacting 等）      |
| Subcategory      | 子分类（Intro Outreach、Intro Booking 等） |
| Other Category   | 次要话题                                |
| Revenue Priority | 营收优先级（low / medium / high）          |
| Outcome          | 通话结果分类（no\_answer、attempted 等）      |
| Credit Card      | 是否获取信用卡                             |

_2. Call Details（通话元数据）_

| 字段             | 说明                                      |
| -------------- | --------------------------------------- |
| Call Result    | 接听结果（No Answer、Call Connected 等）        |
| Direction      | 通话方向（Outbound / Inbound）                |
| Duration       | 通话时长                                    |
| Customer Phone | 客户电话                                    |
| Caller Name    | 来电方名称（门店名）                              |
| Date/Time      | 通话时间                                    |
| Staff Member   | 接听/拨打员工                                 |
| Call State     | 通话状态（voicemail / human\_conversation）   |
| Customer Type  | 客户类型（new\_lead / prospective\_client 等） |

_3. Transcript（原始对话）_

Conversation Data：逐句对话记录，分 Speaker 标注 + 时间戳，Speaker 1（系统/客户）和 Speaker 2（员工）交替展示

### 6.4 信息展示分布

基于「一、客户信息分析」的分类，将每类信息分配到三个区域：

| 信息类别             | 名单列表 Contact List                   | 用户详情 Contact Detail                                  | 通讯记录 Comm Log                                                       |
| ---------------- | ----------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------- |
| **身份信息**         | 姓名、电话、邮箱、性别、年龄                      | 全部（姓名、电话、邮箱、性别、地址、工作、生日/年龄、作息模式）                     | —                                                                   |
| **生命周期**         | stage                               | stage + state                                        | —                                                                   |
| **运营信息**         | 分配给谁、备注（截断预览）、DNC                   | 全部（分配、备注完整、标签、DNC）                                   | —                                                                   |
| **活动数据**         | 获取时间、首次尝试联系时间、首次触达时间、通话/短信次数、最后活动时间 | 全部（来源渠道、获取时间、首次尝试联系时间、首次触达时间、通话/短信次数、最后活动时间）         | —                                                                   |
| **联系偏好**         | —                                   | 联系响应模式、最佳联系时段、偏好联系渠道                                 | —                                                                   |
| **健身画像**         | —                                   | 核心需求、感兴趣的课程类型、偏好时段、健身经验（含过往经历）、当前运动习惯、关注点、健康状况       | —                                                                   |
| **通讯记录**         | —                                   | —                                                    | 时间线事件卡片（System Note / SMS / Call / Voicemail）                       |
| **AI通话分析**       | —                                   | —                                                    | 通话卡片：AI 摘要 + 音频播放器；展开详情：Key Information + Call Details + Transcript |
| **AI跨通话分析**      | 是否需要跟进                              | 客户摘要、是否需要跟进、建议动作                                     | —                                                                   |
| **Lead 专属**      | leadStatus、temperature              | 全部（leadStatus、temperature、意向等级、提交次数、消费决策、决策障碍、条件性拒绝） | —                                                                   |
| **Member 专属** ⏳  | 待定                                  | 待定                                                   | —                                                                   |
| **Churned 专属** ⏳ | 待定                                  | 待定                                                   | —                                                                   |

**设计逻辑：**

- **名单列表**：只放可排序/可筛选的客户级别关键列，用于快速扫描和批量操作
- **用户详情**：完整的客户画像，是名单列表的超集；列表模式下摘要展示，详情模式下完全展开
- **通讯记录**：按日期分组的时间线，展示四种事件类型（System Note / SMS / Call / Voicemail）；点击通话卡片弹出详情弹窗

***
