docs: 添加多渠道适配主框架架构设计 (design.md) [AC-MCA-01]
This commit is contained in:
parent
d8bf3a1e7a
commit
872f0a5d75
|
|
@ -0,0 +1,636 @@
|
|||
---
|
||||
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<String, Object> 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<String, Object> 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<Message> 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
|
||||
<dependency>
|
||||
<groupId>io.github.resilience4j</groupId>
|
||||
<artifactId>resilience4j-spring-boot2</artifactId>
|
||||
<version>2.1.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 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<String, AdapterConfig> 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 |
|
||||
Loading…
Reference in New Issue