--- feature_id: "MCA" title: "多渠道适配主框架架构设计" status: "draft" version: "0.2.0" owners: - "architect" - "backend" last_updated: "2026-02-24" --- # 多渠道适配主框架架构设计(design.md) ## 1. 系统架构 ### 1.1 整体架构图 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 外部系统 │ ├─────────────────┬─────────────────┬─────────────────┬───────────────────────┤ │ 企业微信 API │ 抖音 API │ 京东 API │ 前端工作台 │ └────────┬────────┴────────┬────────┴────────┬────────┴──────────┬────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Java 主框架 (Spring Boot) │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 入口层 (Controller Layer) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │WecomCallback │ │DouyinCallback│ │ JdCallback │ (预留) │ │ │ │ │ Controller │ │ Controller │ │ Controller │ │ │ │ │ └──────┬───────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ │ │ │ │ 验签/解密/解析 → InboundMessage │ │ │ │ ▼ │ │ │ └─────────┼───────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 消息路由层 (Message Router) │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ │ MessageRouterService (渠道无关) │ │ │ │ │ │ - processInboundMessage(InboundMessage) │ │ │ │ │ │ - routeBySessionState(Session, InboundMessage) │ │ │ │ │ │ - dispatchToAiService(Session, InboundMessage) │ │ │ │ │ │ - dispatchToManualCs(Session, InboundMessage) │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 渠道适配层 (Channel Adapter) │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ │ ChannelAdapter 接口 (核心能力) │ │ │ │ │ │ - getChannelType() │ │ │ │ │ │ - sendMessage(OutboundMessage) │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ │ │ 可选能力接口 (Optional Capabilities) │ │ │ │ │ │ - ServiceStateCapable - TransferCapable - MessageSyncCapable│ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ WeChatAdapter│ │DouyinAdapter │ │ JdAdapter │ (预留) │ │ │ │ │ (已实现) │ │ (预留) │ │ (预留) │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌─────────────────────────┼─────────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────────────┐ ┌──────────────┐ │ │ │ AI 服务客户端 │ │ 会话管理层 │ │ WebSocket 服务│ │ │ │AiServiceClient│ │SessionManagerService │ │WebSocketService│ │ │ └──────┬───────┘ └──────────────────────┘ └──────────────┘ │ │ │ │ └─────────┼───────────────────────────────────────────────────────────────────┘ │ HTTP ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Python AI 服务 (独立部署) │ ├─────────────────────────────────────────────────────────────────────────────┤ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ OpenAI Client│ │DeepSeek Client│ │ 其他模型 │ │ │ └──────┬───────┘ └──────┬───────┘ └──────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ AI 服务核心逻辑 │ │ │ │ - /ai/chat 生成 AI 回复 │ │ │ │ - /ai/health 健康检查 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### 1.2 模块职责 | 模块 | 职责 | 关联 AC | |-----|------|--------| | **入口层** | 接收渠道回调,验签/解密/解析,转换为统一的 InboundMessage | AC-MCA-08 | | **消息路由层** | 渠道无关的消息路由,根据会话状态分发到 AI 或人工 | AC-MCA-08 ~ AC-MCA-10 | | **渠道适配层** | 封装各渠道 API 差异,提供统一的消息发送接口 | AC-MCA-01 ~ AC-MCA-03 | | **AI 服务客户端** | 调用 Python AI 服务,处理超时/降级 | AC-MCA-04 ~ AC-MCA-07 | | **会话管理层** | 管理会话生命周期、状态变更、消息持久化 | AC-MCA-11 ~ AC-MCA-12 | | **WebSocket 服务** | 实时推送消息到人工客服工作台 | AC-MCA-10 | | **Python AI 服务** | AI 模型推理、置信度评估、转人工建议 | AC-MCA-04 ~ AC-MCA-05 | ## 2. 统一消息模型 ### 2.1 入站消息 (InboundMessage) ```java @Data public class InboundMessage { private String channelType; // 渠道类型: wechat/douyin/jd private String channelMessageId; // 渠道原始消息ID (用于幂等) private String sessionKey; // 会话标识 (customerId + kfId 组合) private String customerId; // 客户ID private String kfId; // 客服账号ID private String sender; // 发送者标识 private String content; // 消息内容 (统一字段名) private String msgType; // 消息类型: text/image/voice 等 private String rawPayload; // 原始消息体 (JSON/XML) private Long timestamp; // 消息时间戳 private SignatureInfo signatureInfo; // 签名信息 private Map metadata; // 扩展元数据 } @Data public class SignatureInfo { private String signature; // 签名值 private String timestamp; // 签名时间戳 private String nonce; // 随机数 private String algorithm; // 签名算法 (可选) } ``` ### 2.2 出站消息 (OutboundMessage) ```java @Data public class OutboundMessage { private String channelType; // 渠道类型 private String receiver; // 接收者ID (customerId) private String kfId; // 客服账号ID private String content; // 消息内容 private String msgType; // 消息类型 private Map metadata; // 扩展元数据 } ``` ### 2.3 字段映射策略 > **重要**:内部统一使用 `content` 字段名,与 AI 服务契约 (`currentMessage`) 的映射在 AiServiceClient 层处理。 | 内部字段 | AI 服务契约字段 | 映射位置 | |---------|----------------|---------| | `InboundMessage.content` | `ChatRequest.currentMessage` | `AiServiceClient.generateReply()` | | `InboundMessage.sessionKey` | `ChatRequest.sessionId` | `AiServiceClient.generateReply()` | | `InboundMessage.channelType` | `ChatRequest.channelType` | `AiServiceClient.generateReply()` | ```java public ChatRequest toChatRequest(InboundMessage msg, List history) { ChatRequest request = new ChatRequest(); request.setSessionId(msg.getSessionKey()); request.setCurrentMessage(msg.getContent()); // content → currentMessage request.setChannelType(msg.getChannelType()); request.setHistory(history); return request; } ``` ## 3. 核心接口设计 ### 3.1 渠道适配器接口 ```java // 核心能力接口(所有渠道必须实现) public interface ChannelAdapter { String getChannelType(); void sendMessage(OutboundMessage message); } // 可选能力接口:服务状态管理 public interface ServiceStateCapable { ServiceState getServiceState(String kfId, String customerId); boolean transServiceState(String kfId, String customerId, int newState, String servicerId); } // 可选能力接口:转人工 public interface TransferCapable { boolean transferToPool(String kfId, String customerId); boolean transferToManual(String kfId, String customerId, String servicerId); } // 可选能力接口:消息同步 public interface MessageSyncCapable { SyncMsgResponse syncMessages(String kfId, String cursor); } ``` ### 3.2 消息路由服务接口 ```java public interface MessageRouterService { void processInboundMessage(InboundMessage message); void routeBySessionState(Session session, InboundMessage message); void dispatchToAiService(Session session, InboundMessage message); void dispatchToManualCs(Session session, InboundMessage message); void dispatchToPendingPool(Session session, InboundMessage message); } ``` ### 3.3 AI 服务客户端接口 ```java public interface AiServiceClient { ChatResponse generateReply(ChatRequest request); boolean healthCheck(); } ``` ## 4. 核心流程 ### 4.1 消息处理主流程(渠道无关) ``` ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ 渠道回调入口 │────▶│ 验签/解密/解析 │────▶│ 构建 InboundMessage│ │ (Controller) │ │ (渠道专属逻辑) │ │ (统一消息模型) │ └──────────────────┘ └────────┬─────────┘ └──────────────────┘ │ ▼ ┌──────────────────┐ │ MessageRouter │ │ processInbound │ │ Message() │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ 幂等检查 (msgId) │ │ Redis SETNX │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ 获取/创建会话 │ │ SessionManager │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ 获取渠道服务状态 │ │ (可选能力检测) │ └────────┬─────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ AI 状态 │ │ POOL 状态 │ │MANUAL 状态│ │ │ │ │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────┐ ┌──────────────┐ │dispatchTo │ │dispatchTo│ │dispatchTo │ │ AiService │ │PendingPool│ │ ManualCs │ └──────┬───────┘ └──────────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ 判断是否转人工│ │shouldTransfer│ └──────┬───────┘ │ ┌───────┴───────┐ ▼ ▼ ┌───────┐ ┌──────────────┐ │发送回复│ │ 转入待接入池 │ │给用户 │ │ TransferCapable│ └───────┘ └──────────────┘ ``` ### 4.2 AI 服务调用流程 ``` ┌──────────────────┐ │ 构造 ChatRequest │ │ sessionId │ │ currentMessage │←── content 映射 │ channelType │ │ history (可选) │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ ┌──────────────────┐ │ HTTP POST │────▶│ Python AI 服务 │ │ /ai/chat │ │ 超时: 5s │ └────────┬─────────┘ └────────┬─────────┘ │ │ │ ┌──────────────┼──────────────┐ │ ▼ ▼ ▼ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ 成功响应 │ │ 超时/失败 │ │ 服务不可用│ │ │ 200 OK │ │ Timeout │ │ 503 │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ ▼ ▼ ▼ │ ┌──────────┐ ┌──────────────────────┐ │ │ 返回回复 │ │ 降级处理 │ │ │ reply │ │ 返回固定回复 │ │ │confidence│ │ "正在转接人工客服..." │ │ │shouldTransfer│ └──────────┬───────────┘ │ └──────────┘ │ │ ▼ │ ┌──────────────┐ │ │ 触发转人工 │ │ │ TransferCapable│ │ └──────────────┘ │ ▼ ┌──────────────────┐ │ 处理响应 │ │ - 保存消息 │ │ - 发送给用户 │ │ - 判断转人工 │ └──────────────────┘ ``` ## 5. 数据模型 ### 5.1 实体关系图 ``` ┌──────────────────┐ ┌──────────────────┐ │ Session │ │ Message │ ├──────────────────┤ ├──────────────────┤ │ sessionId (PK) │──────▶│ msgId (PK) │ │ customerId │ 1:N │ sessionId (FK) │ │ kfId │ │ senderType │ │ channelType (新) │ │ senderId │ │ status │ │ content │ │ wxServiceState │ │ msgType │ │ manualCsId │ │ rawData │ │ createdAt │ │ createdAt │ │ updatedAt │ └──────────────────┘ └──────────────────┘ │ │ 1:N ▼ ┌──────────────────┐ │ TransferLog │ ├──────────────────┤ │ id (PK) │ │ sessionId (FK) │ │ triggerReason │ │ triggerTime │ │ acceptedCsId │ │ acceptedTime │ └──────────────────┘ ``` ### 5.2 数据库变更 > **口径说明**:本次仅做最小 schema 变更,新增 `channel_type` 字段,默认值为 `wechat`;可通过在线 DDL 方式执行;不涉及数据迁移。符合 requirements.md 中"仅增加渠道类型字段,不进行大规模迁移"的范围约定。 | 表名 | 变更类型 | 变更内容 | |-----|---------|---------| | `session` | 新增字段 | `channel_type VARCHAR(20) DEFAULT 'wechat'` | **DDL 示例**: ```sql ALTER TABLE session ADD COLUMN channel_type VARCHAR(20) DEFAULT 'wechat' COMMENT '渠道类型: wechat/douyin/jd'; ``` ### 5.3 Redis 缓存结构 | Key 模式 | 类型 | 说明 | TTL | |---------|------|------|-----| | `wecom:access_token` | String | 微信 access_token | 7200s - 300s | | `wecom:cursor:{openKfId}` | String | 消息同步游标 | 永久 | | `session:status:{sessionId}` | String | 会话状态缓存 | 24h | | `session:msg_count:{sessionId}` | String | 消息计数 | 24h | | `idempotent:{msgId}` | String | 消息幂等键 | 1h | ## 6. 跨模块调用策略 ### 6.1 AI 服务调用 | 配置项 | 值 | 说明 | |-------|---|------| | **超时时间** | 5 秒 | 连接 + 读取总超时 | | **重试次数** | 0 | 不重试,直接降级 | | **熔断阈值** | 5 次/分钟 | 连续失败 5 次触发熔断 | | **熔断时间** | 30 秒 | 熔断后等待时间 | | **降级策略** | 返回固定回复 + 转人工 | 见下方降级逻辑 | ### 6.2 熔断器选型 > **选型决策**:使用 **Resilience4j** 作为熔断器实现,与 Spring Boot 2.7 兼容。 | 方案 | 说明 | |-----|------| | **Resilience4j** | 推荐。轻量级,支持断路器、限流、重试,与 Spring Boot 2.7 兼容良好 | | 最小实现 | 仅做 timeout + fallback,不做熔断(不推荐,与 requirements 不一致) | **熔断状态存储**: - 单实例:内存存储(CircuitBreakerRegistry) - 多实例:可扩展为 Redis 存储(通过 Resilience4j + Redis 实现) **依赖配置**: ```xml io.github.resilience4j resilience4j-spring-boot2 2.1.0 ``` ### 6.3 降级逻辑 ```java @Service public class AiServiceClientImpl implements AiServiceClient { @CircuitBreaker(name = "aiService", fallbackMethod = "fallback") @TimeLimiter(name = "aiService") public ChatResponse generateReply(ChatRequest request) { // HTTP 调用 Python AI 服务 } public ChatResponse fallback(ChatRequest request, Throwable cause) { log.warn("AI 服务降级: sessionId={}, cause={}", request.getSessionId(), cause.getMessage()); ChatResponse response = new ChatResponse(); response.setReply("抱歉,我暂时无法回答您的问题,正在为您转接人工客服..."); response.setConfidence(0.0); response.setShouldTransfer(true); return response; } } ``` ### 6.4 错误映射 | AI 服务错误 | 主框架处理 | 用户感知 | |------------|-----------|---------| | 200 OK | 正常处理 | 返回 AI 回复 | | 400 Bad Request | 记录日志,降级 | 转人工 | | 500 Internal Error | 记录日志,降级 | 转人工 | | 503 Service Unavailable | 记录日志,降级 | 转人工 | | Timeout | 记录日志,降级 | 转人工 | | Connection Refused | 触发熔断,降级 | 转人工 | ## 7. 消息幂等性设计 ### 7.1 幂等键 - 使用 `InboundMessage.channelMessageId` 作为幂等键 - 微信渠道:使用微信返回的 `msgId` - 其他渠道:使用渠道返回的消息 ID 或生成唯一 ID ### 7.2 幂等处理流程 ``` ┌──────────────────┐ │ 收到消息 │ │ channelMessageId │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ ┌──────────────────┐ │ Redis 检查 │────▶│ Key 不存在 │ │ idempotent:{msgId}│ │ 继续处理 │ └────────┬─────────┘ └────────┬─────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ Key 已存在 │ │ 设置 Key (TTL 1h)│ │ 跳过处理 │ │ 处理消息 │ └──────────────────┘ └──────────────────┘ ``` ### 7.3 实现代码 ```java public boolean processMessageIdempotent(String channelMessageId, Runnable processor) { String key = "idempotent:" + channelMessageId; Boolean absent = redisTemplate.opsForValue() .setIfAbsent(key, "1", 1, TimeUnit.HOURS); if (Boolean.TRUE.equals(absent)) { processor.run(); return true; } log.info("重复消息,跳过处理: channelMessageId={}", channelMessageId); return false; } ``` ## 8. 配置管理 ### 8.1 新增配置项 ```yaml # application.yml 新增配置 ai-service: url: http://ai-service:8080 timeout: 5000 resilience4j: circuitbreaker: instances: aiService: failure-rate-threshold: 50 sliding-window-size: 10 sliding-window-type: COUNT_BASED wait-duration-in-open-state: 30s permitted-number-of-calls-in-half-open-state: 3 timelimiter: instances: aiService: timeout-duration: 5s channel: default: wechat adapters: wechat: enabled: true douyin: enabled: false jd: enabled: false ``` ### 8.2 配置类 ```java @Data @Component @ConfigurationProperties(prefix = "ai-service") public class AiServiceConfig { private String url; private int timeout = 5000; } @Data @Component @ConfigurationProperties(prefix = "channel") public class ChannelConfig { private String default; private Map adapters; @Data public static class AdapterConfig { private boolean enabled; } } ``` ## 9. 部署架构 ### 9.1 部署拓扑 ``` ┌─────────────────────────────────────────────────────────────┐ │ 负载均衡器 │ └─────────────────────────────┬───────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Java 主 │ │ Java 主 │ │ Java 主 │ │ 框架实例1│ │ 框架实例2│ │ 框架实例3│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ └──────────────┼──────────────┘ │ ┌──────────────────┼──────────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Redis │ │ MySQL │ │Python AI │ │ (Cluster)│ │ (Master) │ │ 服务 │ └──────────┘ └──────────┘ └──────────┘ ``` ### 9.2 服务依赖 | 服务 | 依赖关系 | 健康检查 | |-----|---------|---------| | Java 主框架 | 依赖 Redis, MySQL, Python AI | `/actuator/health` | | Python AI 服务 | 无外部依赖 | `/ai/health` | ## 10. 安全设计 ### 10.1 渠道回调鉴权 | 渠道 | 鉴权方式 | 验证逻辑 | |-----|---------|---------| | 微信 | msg_signature + timestamp + nonce | **沿用现有 WeCom 官方验签/解密方案**(复用现有 `WXBizMsgCrypt` 实现) | | 抖音 | X-Signature + X-Timestamp | 待实现 | | 京东 | signature + timestamp | 待实现 | > **说明**:微信回调验签/加解密使用企业微信官方方案,具体算法细节封装在现有 `WXBizMsgCrypt` 类中,不在本设计文档展开。 ### 10.2 内部服务鉴权 - Java 主框架 → Python AI 服务:内网调用,无需鉴权(可扩展为 mTLS) - WebSocket 连接:路径参数 `{csId}` 标识身份(可扩展为 Token 验证) ## 11. 监控与告警 > **说明**:本节为后续演进预留,MVP 阶段可暂不实现。 ### 11.1 关键指标 | 指标 | 类型 | 说明 | |-----|------|------| | `ai.service.latency` | Histogram | AI 服务调用延迟 | | `ai.service.error.rate` | Counter | AI 服务错误率 | | `ai.service.circuit.breaker.open` | Gauge | 熔断器状态 | | `message.process.count` | Counter | 消息处理数量 | | `message.idempotent.skip` | Counter | 幂等跳过数量 | | `session.active.count` | Gauge | 活跃会话数 | ### 11.2 告警规则 | 规则 | 条件 | 级别 | |-----|------|------| | AI 服务不可用 | 连续失败 5 次 | Critical | | AI 服务延迟过高 | P99 > 3s | Warning | | 熔断器触发 | circuit.breaker.open = 1 | Critical |