# 渠道账号、会话与发送任务数据边界设计

> **版本**：v1.0  
> **日期**：2026-07-08  
> **状态**：已确认，待实施  
> **关联文档**：`docs/architecture/2026-07-06-message-customer-interaction-design.md`

---

## 1. 背景与目标

在 `2026-07-06-message-customer-interaction-design.md` 已完成整体架构与数据模型设计的基础上，进一步明确中台与业务系统之间的数据边界，以及“配置 + 选取人员 → 任务发送”的最小可用实现路径。

**本次目标**：
- 业务系统通过中台 API 维护人员（Person）、渠道身份（ChannelIdentity）、会话（Conversation）、群聊关系；
- 中台只保存渠道侧最小数据集，业务属性（机构、供应商、合同等）由业务系统写入；
- 企微官方接口只能拿到有限信息（`userid`、`external_userid`、`chat_id`、群名等），同步接口不得覆盖业务系统写入的字段；
- 所有出站发送通过 `/api/v1/send` 任务接口驱动，任务与发送日志合并为 `mci_send_record`；
- 当前阶段不拆分 `openapi` 包，所有修改在现有 `mci-server` 与 `admin-web` 内完成；跑通后下一阶段再独立拆分第三方接入包。

---

## 2. 数据边界设计

### 2.1 中台保存 vs 业务系统维护

| 数据 | 中台保存 | 业务系统维护 | 说明 |
|------|---------|-------------|------|
| 人员基础信息 `mci_person` | 是 | 可同步 | 中台保存 `person_code`、`person_name`、`phone`、`person_type`；业务系统可写入或同步，中台不强制唯一来源。 |
| 业务属性（机构/供应商/合同） | 是字段，业务系统写入 | 是 | `institution_id`、`supplier_id`、`contract_id` 由业务系统赋值，企微同步不覆盖。 |
| 渠道身份 `mci_channel_identity` | 是 | 可同步 | 中台保存渠道用户 ID（`userid`、`external_userid`、个微 `wxid` 等），属于渠道侧最小数据集。 |
| 会话 `mci_conversation` | 是 | 可同步/创建 | 中台保存 `channel_conversation_id`、群名、群主等；业务属性由业务系统写入。 |
| 标签 `mci_tag` | 是 | 可同步 | 标签表达角色、合同、机构、供应商等业务属性，支持层级；业务系统可通过 API 维护。 |
| 账号-会话绑定 `mci_channel_account_conversation` | 是 | 否 | 发送主备、接收主备属于渠道调度能力，由中台管理。 |
| 发送内容 | 否（接收最终内容） | 是 | 业务系统渲染最终消息后调用中台发送接口。 |
| 模板与变量 | 否 | 是 | 中台不保存模板。 |

### 2.2 企微同步的边界

`ChannelAccountService.syncContacts` 与 `syncConversations` 只能写入/更新以下字段：

- `mci_person`：`person_name`（企微返回的昵称/姓名）、`phone`（如有）
- `mci_channel_identity`：`channel_user_id`、`channel_user_name`、`subject_id`、`channel_type`
- `mci_conversation`：`conversation_type`、`channel_type`、`channel_conversation_id`、`conversation_name`、`owner_account_id`、`subject_id`、`create_source=1`
- `mci_conversation_participant`：群成员关系

**禁止行为**：
- 不得覆盖 `institution_id`、`supplier_id`、`contract_id`；
- 不得删除业务系统已绑定的标签；
- 不得修改 `channel_account_conversation` 中业务系统未授权的主备绑定。

---

## 3. 发送任务模型

沿用 `mci_send_record` 单表设计：一次 API 调用产生一个 `task_id`，每个最终目标（人/会话）一条记录。

### 3.1 支持的目标类型

| target_type | 说明 | target_value 示例 |
|-------------|------|-------------------|
| `conversation` | 单个会话 | `{"conversation_id": 1001}` |
| `person` | 单个联系人（单聊） | `{"person_id": 2001}` |
| `person_list` | 联系人列表 | `{"person_ids": [2001, 2002]}` |
| `tag` | 标签人群/群聊 | `{"tag_id": 3001, "scope": "all"}` |
| `rule` | 复合受众规则 | `{"logic": "and", "rules": [...]}` |

### 3.2 渠道策略

`channel_strategy` 描述渠道选择偏好：

```json
{
  "channel_type": "wecom",
  "strategy": "primary",
  "fallback": true
}
```

中台根据目标解析结果选择实际账号：
- 群聊：从 `mci_channel_account_conversation` 取发送主号，主号不可用 fallback 到备用号；
- 单聊：取目标人对应的 `ChannelIdentity`，再选择默认 `ChannelAccount`。

### 3.3 结果回调

发送请求可携带 `callback_url`，任务完成后按条或按任务聚合回调业务系统。

---

## 4. 本次实施范围（方案 A）

### 4.1 后端

1. **校验与边界保护**
   - 在 `ConversationService`、`ChannelAccountService` 的同步方法中增加“业务字段保护”：企微同步不覆盖 `institution_id`、`supplier_id`、`contract_id`。
   - 在 `SendService.resolveByType` 中增加目标存在性、租户隔离、账号可用性校验。

2. **账号-会话绑定补全**
   - 确认 `ConversationController.bindAccount` 支持 `binding_type`：1 发送主号、2 发送备用号、3 接收主号、4 接收备用号。
   - 确认 `SendService.selectSendAccount` 按主备顺序选择。

3. **发送任务端到端**
   - 验证 `POST /api/v1/send`、`POST /api/v1/send/test` 可正常创建任务、拆解记录、返回 `task_id`。
   - 验证 `SendMessageConsumer` 可调用 `ChannelAdapter` 实际发送并回写状态。

4. **渠道账号初始化**
   - 验证 `POST /api/v1/channel-account/init` 支持企微内部账号、企微应用、个微、钉钉/飞书机器人；返回初始化结果或二维码信息。

5. **会话/群聊同步**
   - 验证 `POST /api/v1/channel-account/{account_id}/sync-conversations` 能拉取账号所在群聊并创建 `Conversation`、`ConversationParticipant`、`ChannelAccountConversation` 默认绑定。

### 4.2 前端

1. **发送页面完善**
   - `SendRecord.vue` 已支持测试发送和记录查询，补充“选择标签发送”和“选择规则发送”的测试入口。
   - 在会话/人员列表中提供“发送消息”快捷入口，跳转或填充 `SendRecord.vue` 测试表单。

2. **账号-群绑定页面**
   - 在 `Conversation.vue` 或新增页面中，支持为会话绑定发送主备号、接收主备号。

3. **标签层级展示**
   - `Tag.vue` 已支持层级，确认在人员/会话绑定时可以展示多级标签并直接选择。

4. **管理后台权限**
   - 确认租户管理员、运营操作员、只读用户三类角色已落地，页面按钮按角色显隐。

### 4.3 测试验证

1. **单元/接口测试**
   - 发送目标解析：conversation、person、tag、rule；
   - 同步边界：企微同步不覆盖业务字段；
   - 主备切换：发送主号不可用时启用备用号。

2. **端到端流程**
   - 使用 mock 渠道适配器或企微测试企业号，完成：
     1. 创建租户/用户；
     2. 创建渠道主体；
     3. 初始化渠道账号；
     4. 同步/创建会话；
     5. 绑定发送账号；
     6. 调用发送任务；
     7. 查询发送记录与统计。

---

## 5. 验收标准

- [ ] 第三方平台可通过 API 完成：创建 Person、绑定 ChannelIdentity、创建/更新 Conversation、绑定标签、绑定发送账号；
- [ ] 企微 `sync-contacts` / `sync-conversations` 不覆盖 `institution_id`、`supplier_id`、`contract_id`；
- [ ] `POST /api/v1/send` 支持 conversation / person / person_list / tag / rule 五种目标类型；
- [ ] 发送任务按目标拆解为 `mci_send_record` 记录，可查询任务状态、记录列表、统计；
- [ ] 前端可在“会话/人员/标签”维度完成测试发送，并查看发送记录；
- [ ] Docker 全栈服务健康，接口可通。

---

## 6. 未来工作

下一阶段（方案 A 验证通过后）将拆分独立的 `openapi` 包：
- 路径前缀 `/api/v1/open/**`；
- 面向业务系统的 DTO 更严格，隐藏内部状态字段；
- 独立的鉴权方式（`X-Tenant-Code` + `X-App-Key`）；
- 管理后台 API 与第三方 API 代码分离，便于文档生成和权限控制。

---

## 7. 关键文件清单

| 模块 | 文件 |
|------|------|
| 发送核心 | `backend/mci-server/src/main/java/com/sino/mci/send/service/SendService.java` |
| 发送控制器 | `backend/mci-server/src/main/java/com/sino/mci/send/controller/SendController.java` |
| 会话管理 | `backend/mci-server/src/main/java/com/sino/mci/channel/service/ConversationService.java` |
| 渠道账号 | `backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java` |
| 人员管理 | `backend/mci-server/src/main/java/com/sino/mci/crm/service/PersonService.java` |
| 发送页面 | `admin-web/src/views/SendRecord.vue` |
| 会话页面 | `admin-web/src/views/Conversation.vue` |
| 标签页面 | `admin-web/src/views/Tag.vue` |
