# 07 — flare-im-core 业务扩展点与 API 接入

## 1. 结论

`flare-im-core` 的业务扩展必须走显式扩展面，不允许核心服务直接依赖业务 crate。生产推荐按能力类型选择接入方式：

| 类型 | 接入面 | 适用场景 | 是否影响主链 |
|------|--------|----------|--------------|
| 生命周期校验 | `HookPlugin.Call` / `PreSendHook` | 好友关系、黑名单、内容风控、租户策略 | `pre_send` 可拒绝 |
| 生命周期旁路 | `post_send` / `delivery` / `recall` Hook | 审计、指标、合规归档、运营分析 | 默认不阻塞 |
| 命令/查询扩展 | `ExtensionPlugin.Call` / `CapabilityService.Dispatch` | 链接预览、翻译、业务查询、管理操作 | 由调用方决定 |
| 同步扩展 | `SyncService.ExecuteSync` + 注入端口 | 群目录、通讯录、用户资料增量 | 不改消息 seq |
| 写后投影 | 业务 Domain Event + bridge | Social 写成功后建会话、发系统消息 | 业务侧控制 |
| 业务接入埋点 | `BusinessProbeEvent` / `BusinessProbeSink` | BI、审计、计费、推荐、风控特征 | 不阻塞 |
| HTTP BFF | `flare-api-gateway` | App/管理后台访问 Core 能力 | 只做边界适配 |

核心约束：

- `request_id` 负责幂等和追踪，不等于 `message_id`。
- `message_id` 是消息全局标识，不等于会话内 `seq`。
- `seq` 只由 IM Core 在会话维度分配，业务服务不得生成或回退。
- `cursor` 用于读模型同步，不得替代消息 `seq`。
- 所有服务间调用必须透传 `Ctx`：`x-trace-id`、`x-request-id`、`x-tenant-id`、`x-user-id`。

## 2. 扩展点清单

| 扩展点 | Proto / Trait | 触发时机 | 业务可做 | 禁止事项 |
|--------|---------------|----------|----------|----------|
| `pre_send` | `PreSendHookRequest` / `PreSendHook` | 消息校验后、seq 分配前 | 拒绝发送、补充草稿元数据、路由建议 | 分配 seq、直接写消息存储 |
| `post_send` | `PostSendHookRequest` / `PostSendHook` | 消息推入主链后 | 审计、统计、合规归档、异步通知 | 依赖其完成后再对客户端确认 |
| `delivery` | `DeliveryHookRequest` / `DeliveryHook` | 下行送达后 | 送达指标、渠道分析 | 修改消息事实 |
| `recall` | `RecallHookRequest` / `RecallHook` | 撤回流程 | 审计、权限补充检查 | 绕过 `MessageActionService` |
| `message_read` | `MessageReadHookRequest` / `MessageReadHook` | 已读水位更新后 | 已读率、重要消息阅读审计 | 修改游标/未读权威状态 |
| `message_reaction` | `MessageReactionHookRequest` / `MessageReactionHook` | reaction 变更时 | 表态白名单、互动统计、推荐特征 | 修改消息内容 |
| `conversation_lifecycle` | `ConversationLifecycleHookRequest` / `ConversationLifecycleHook` | 会话状态事实提交后 | 外部目录投影、生命周期审计 | 失败后回滚已提交事实 |
| `conversation_member` | `ConversationMemberHookRequest` / `ConversationMemberHook` | 成员变更时 | 成员审计、风控、版本通知 | 把业务目录完整资料写入 Core |
| `GetConversationParticipantsHook` | Rust trait | 需要参与者列表时 | 从业务目录提供成员列表 | 把好友列表当会话参与者 |
| `CapabilityService.Dispatch` | `DispatchCapabilityRequest` | 显式能力调用 | 业务能力路由 | 替代生命周期 Hook |
| `ExtensionPlugin.Call` | `GenericRequest` + `Any` | 显式 operation 调用 | 业务查询/命令插件 | 硬编码到 Core |
| `SyncService.ExecuteSync` | `SyncRequest` | 客户端同步 | 群目录、通讯录、资料增量 | 混用消息 `last_seq` |
| `MessageSendService.SendSystemMessage` | gRPC | 业务写后通知 | 系统消息、通知消息 | 业务普通聊天绕过客户端上行链路 |
| `ConversationManageService` | gRPC | 业务目录变化后 | 建会话、成员变更投影 | 在 Social 内维护消息 unread |
| `ConversationReadService.ListConversationParticipants` | gRPC / gateway | 成员页、@ 候选人、参与者增量 | 分页读取 Core 参与者投影 | 把群目录完整业务资料塞入 Core |
| `BusinessProbeSink` | Rust port / MQ adapter | Core 事实观测 | 输出标准业务埋点 | 在 Sink 中反向调用 Core 主链 |

## 3. HookPlugin API

服务定义：

| 项 | 说明 |
|----|------|
| Package | `flare.capability.v1` |
| Service | `HookPlugin` |
| RPC | `Call(GenericRequest) returns (GenericResponse)` |

`GenericRequest` 约定：

| 字段 | 说明 |
|------|------|
| `operation` | Hook 类型，例如 `flare.hook.v1.pre_send` |
| `request_id` | 幂等键，必须与 metadata 可关联 |
| `metadata` | 轻量路由/灰度/签名字段 |
| `payload` | `google.protobuf.Any`，内嵌具体 Hook Request |

常用 operation：

| operation | payload | 默认失败策略 |
|-----------|---------|--------------|
| `flare.hook.v1.pre_send` | `PreSendHookRequest` | 强校验，推荐 fail-fast |
| `flare.hook.v1.post_send` | `PostSendHookRequest` | 旁路，推荐 fail-open |
| `flare.hook.v1.delivery` | `DeliveryHookRequest` | 旁路，推荐 fail-open |
| `flare.hook.v1.recall` | `RecallHookRequest` | 视权限模型决定 |
| `flare.hook.v1.message_read` | `MessageReadHookRequest` | 旁路，推荐 fail-open |
| `flare.hook.v1.message_reaction` | `MessageReactionHookRequest` | 视表态策略决定 |
| `flare.hook.v1.conversation_lifecycle` | `ConversationLifecycleHookRequest` | 事实后旁路，推荐 fail-open |
| `flare.hook.v1.conversation_member` | `ConversationMemberHookRequest` | 视成员变更发起方决定 |

`pre_send` 的业务语义：

1. Orchestrator 已完成基础消息校验。
2. 尚未分配最终 `seq`，业务不能依赖 `seq`。
3. Hook 可以返回 `allow = false` 拒绝发送。
4. Hook 可以回写 `draft.metadata` 或 `annotations`，但必须由宿主决定哪些字段进入持久化事实。
5. 业务拒绝应返回机器可读 `deny_reason_code`，例如 `SOCIAL_PRESEND_DENIED`。

## 4. Capability / Extension API

`CapabilityService` 面向能力目录、授权和命令分发：

| RPC | 用途 |
|-----|------|
| `ListCapabilities` | 列出当前可用能力 |
| `ListUserCapabilities` | 查询用户可用能力 |
| `Dispatch` | 按 `capability_id` 分发命令 |
| `GrantUserCapability` / `RevokeUserCapability` | 授权管理 |
| `SetTenantCapabilitySwitch` | 租户级开关 |
| `RegisterPluginEndpoint` / `DeregisterPluginEndpoint` | 插件实例注册 |

`ExtensionPlugin.Call` 面向通用 operation：

| 命名建议 | 示例 |
|----------|------|
| `{domain}.{subsystem}.{action}` | `social.sync.group_directory` |
| `{domain}.{capability}.v{n}.{action}` | `link.preview.v1` |

使用原则：

- 能力调用必须带 `tenant_id`、`user_id`、`conversation_id`、`request_id`。
- 大 payload 不应塞进 `payload_json`；应先落对象存储或业务存储，再传引用。
- operation 必须版本化或保持向后兼容。
- 长耗时任务应返回 accepted / task_id，不阻塞 IM 主链。

## 5. 服务间 gRPC API

| API | 归属 | 使用者 | 说明 |
|-----|------|--------|------|
| `RouterUpstreamService.RouteMessage` | signaling route | 长连接网关 | 客户端上行入口 |
| `MessageSendService.SendMessage` | orchestrator | route / gateway 内部 | 普通消息发送 |
| `MessageSendService.SendSystemMessage` | orchestrator | bridge / 后台 | 系统消息 |
| `MessageActionService.RecallMessage` | orchestrator | gateway / 后台 | 撤回消息 |
| `ConversationManageService` | conversation | bridge / 管理面 | 会话和成员投影 |
| `ConversationReadService` | conversation | gateway / sync | 会话读模型 |
| `StorageReaderService` | storage-reader | query / sync | 历史消息读取 |
| `SyncService.ExecuteSync` | sync-orchestrator | SDK / gateway | 多端同步 |
| `MediaService` | media | gateway / SDK | 媒体上传、处理、引用 |
| `CapabilityService` | capability | gateway / orchestrator | 能力目录和分发 |
| `HookPlugin` | capability / business hook | orchestrator | 生命周期回调 |
| `ExtensionPlugin` | business plugin | capability / 工具面 | 扩展命令/查询 |

## 6. 扩展接入决策

| 需求 | 推荐接入 | 原因 |
|------|----------|------|
| 发送前校验好友/群权限 | `pre_send` Hook | 必须在 seq 分配前拒绝 |
| 发消息后写审计日志 | `post_send` Hook | 不影响主链确认 |
| 好友通过后自动创建单聊 | Social Domain Event + bridge | 业务写模型成功后投影 IM |
| 群成员变更同步到会话参与者 | bridge 调 `ConversationManageService` | 会话投影由 Core 管 |
| 客户端拉群目录增量 | `SyncService.ExecuteSync` 注入 Social 端口 | 与消息同步解耦 |
| 客户端发现成员变化 | 对比 `participant_version` 后拉 `ConversationParticipants` | 大群成员同步不污染消息 seq |
| 业务做 BI/计费/推荐特征 | `BusinessProbeEvent` + MQ/Hook/Capability | 非侵入式观察 Core 事实 |
| 链接预览 | `CapabilityService.Dispatch` + message event enrich | 命令型能力，非消息存储 |
| 管理后台调用 Core | `flare-api-gateway` | 统一认证、错误、返回格式 |

更完整的消息/会话接入点、群体变更和 Core 独立运行策略见 `docs/integration/09-message-conversation-access-points.md`（内部稿，未在站点发布）。
业务接入埋点模型见 [10-business-instrumentation.md](./10-business-instrumentation.md)。

## 7. 生产要求

- 幂等：所有写操作使用 `request_id` 或业务事件 ID 去重。
- 顺序：业务不得生成或覆盖消息 `seq`；会话内顺序只认 Core 分配结果。
- 超时：主链 Hook 超时必须短，`pre_send` 建议 100-500ms。
- 降级：强一致门禁 fail-fast；审计、指标、通知 fail-open。
- 观测：Hook/Extension 必须输出 `trace_id`、`request_id`、`tenant_id`、`operation`、耗时、结果。
- 安全：Hook body 里的用户字段不能替代认证；服务端必须从 metadata / token 还原可信身份。
