Files
wxauto_api/doc/wxauto_center/DEVELOPMENT.md
T
2026-04-07 14:11:45 +08:00

771 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WXAuto Center 开发指南
## 开发环境搭建
### 环境要求
- Python 3.8+
- Node.js 14+ (可选,用于前端开发)
- Git
### 克隆与安装
```bash
# 克隆项目
git clone <repository-url>
cd wxauto_api/wxauto_center
# 创建虚拟环境(推荐)
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或 venv\Scripts\activate # Windows
# 安装依赖
pip install -r requirements.txt
```
### 配置开发环境
```bash
# 复制环境变量示例文件
cp .env.example .env
# 编辑 .env 文件
vim .env
```
### 启动开发服务器
```bash
# 方式1:使用 run.py(推荐)
python run.py
# 方式2:单进程开发模式
python run.py --reload
# 方式3:使用 uvicorn
uvicorn main:app --host 0.0.0.0 --port 8080 --reload
# 方式4:多进程模式(需要 Redis)
python run.py --workers 4
```
### 访问服务
- 主界面:http://localhost:8080/static/index.html
- API 文档:http://localhost:8080/docs
- ReDochttp://localhost:8080/redoc
---
## 项目结构详解
### 目录结构
```
wxauto_center/
├── main.py # FastAPI 应用入口
├── run.py # 多进程启动脚本
├── config.py # 配置管理模块
├── api/ # API 路由目录
│ ├── __init__.py
│ ├── nodes.py # 节点管理 API
│ ├── messages.py # 消息发送 API
│ ├── external.py # 外部扩展 API
│ ├── callback.py # 回调接口 API
│ └── plugins.py # 插件管理 API
├── services/ # 业务逻辑目录
│ ├── __init__.py
│ ├── node_manager.py # 节点管理器
│ ├── monitor_service.py # 监控服务
│ ├── log_service.py # 日志服务(支持Redis+文件持久化)
│ └── queue_service.py # 消息队列服务(支持Redis后端)
├── plugins/ # 插件目录
│ ├── __init__.py
│ ├── base.py # 插件基类
│ └── builtin.py # 内置插件
├── models/ # 数据模型目录
├── utils/ # 工具函数目录
├── static/ # 静态文件目录
│ ├── index.html # 主页面
│ ├── css/ # 样式目录
│ └── js/ # JavaScript 目录
├── logs/ # 日志目录
│ └── app.log # 应用日志
├── config.json # 配置文件
└── requirements.txt # 依赖列表
```
---
## 代码架构说明
### 主入口 (main.py)
`main.py` 是 FastAPI 应用的入口文件,负责:
1. 初始化 FastAPI 应用
2. 配置 CORS 中间件
3. 注册路由(nodes, messages, external, plugins
4. 处理启动/关闭事件
关键代码流程:
```python
# 启动事件
@app.on_event("startup")
async def startup_event():
load_config_from_file("config.json") # 加载配置
register_builtin_plugins() # 注册内置插件
for node_data in config_manager.get_all_nodes():
await node_manager.add_node(...) # 添加节点
await monitor_service.start() # 启动监控服务
# 关闭事件
@app.on_event("shutdown")
async def shutdown_event():
await monitor_service.stop() # 停止监控
save_config_to_file("config.json") # 保存配置
```
### 配置管理 (config.py)
采用 Pydantic Settings 进行配置管理:
```python
# 配置模型
Settings # 主配置类
NodeConfig # 节点配置
PluginConfig # 插件配置
WebhookConfig # Webhook 配置
MonitorConfig # 监控配置
# 配置管理器
ConfigManager # 提供节点配置的增删改查
# 配置加载/保存
load_config_from_file() # 从文件加载
save_config_to_file() # 保存到文件
```
### 节点管理器 (services/node_manager.py)
`NodeManager` 是核心服务类,负责与 WXAuto-HTTP-API 节点通信。
```python
# 节点状态
class NodeStatus:
ACTIVE = "active" # API 可用
INACTIVE = "inactive" # API 不可用
ERROR = "error" # API 错误
# 节点类
class Node:
node_id: str # 节点唯一标识
name: str # 节点显示名称
api_url: str
api_key: str
description: str
group: str
enabled: bool
status: str # NodeStatus
wechat_status: str
is_healthy: bool
health_message: str
# NodeManager 方法
class NodeManager:
async def add_node(...) # 添加节点
async def remove_node(node_id) # 删除节点
async def get_node(node_id) # 获取单个节点
async def get_all_nodes() # 获取所有节点
async def get_enabled_nodes() # 获取已启用节点
async def get_nodes_by_group() # 按分组获取节点
async def update_node(node_id, **kwargs) # 更新节点
async def check_node_status(node_id) # 检查节点状态
async def is_node_healthy(node_id) # 检查节点健康状态
async def send_message(node_id, who, msg, msg_type) # 发送消息
async def send_message_to_group(node_id, group_name, msg, msg_type) # 发送群消息
async def get_friends_list(node_id) # 获取好友列表
async def get_groups_list(node_id) # 获取群列表
async def get_messages(node_id, chat_name, limit) # 获取消息历史
**注意**已移除 `broadcast_message()` 方法广播功能不再支持
### 监控服务 (services/monitor_service.py)
`MonitorService` 负责定时检测节点和微信状态
```python
class MonitorService:
async def start() # 启动监控
async def stop() # 停止监控
async def _monitor_loop() # 主监控循环
async def _check_all_nodes() # 检查所有节点
async def _check_node(node_id) # 检查单个节点
async def _send_webhook(event, data) # 发送Webhook通知
def get_last_status(node_id) # 获取最后状态
def get_all_status() # 获取所有状态
```
### 日志服务 (services/log_service.py)
`LogService` 提供日志记录和实时推送功能。
```python
class LogService:
def __init__(self, max_logs=500):
self.max_logs = max_logs
self._logs: deque = deque(maxlen=max_logs) # 限制500条
self._subscribers: List[asyncio.Queue] = []
# 日志方法
def info/warning/error/success(message, source)
# SSE 订阅
async def subscribe() -> asyncio.Queue # 订阅日志流
def unsubscribe(queue) # 取消订阅
# 日志查询
def get_recent_logs(count) # 获取最近日志
def get_all_logs() # 获取所有日志
```
---
## 插件开发指南
### 插件架构
系统采用插件化设计,支持扩展:
```
PluginBase (抽象基类)
├── MessageHandlerPlugin
├── DataSourcePlugin
├── ActionTriggerPlugin
└── AIAgentPlugin
```
### 开发自定义插件
#### 1. 创建插件类
```python
# my_plugin.py
from plugins.base import PluginBase, PluginType
class MyCustomPlugin(PluginBase):
plugin_name = "my_custom_plugin"
plugin_version = "1.0.0"
plugin_type = PluginType.CUSTOM
plugin_description = "我的自定义插件"
def initialize(self, config: Dict[str, Any]) -> bool:
self.config = config
# 初始化逻辑
return True
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
# 插件逻辑
return {"success": True, "result": "done"}
```
#### 2. 注册插件
```python
# 在 builtin.py 或 main.py 中注册
from plugins.base import PluginRegistry
my_plugin = MyCustomPlugin()
my_plugin.initialize({"key": "value"})
PluginRegistry.register(my_plugin)
```
### 插件类型详解
#### DataSourcePlugin
用于从外部数据源获取数据:
```python
class MyDataSourcePlugin(DataSourcePlugin):
plugin_name = "my_data_source"
def fetch_data(self, query: str, params: Dict[str, Any]) -> List[Dict[str, Any]]:
# 获取数据逻辑
return [...]
```
#### AIAgentPlugin
用于 AI 消息处理:
```python
class MyAIAgentPlugin(AIAgentPlugin):
plugin_name = "my_ai_agent"
def process(self, input_text: str, context: Dict[str, Any]) -> str:
# 处理输入,返回 AI 回复
return "AI 回复"
def get_response(self, messages: List[Dict[str, str]]) -> str:
# 获取对话响应
return "AI 回复"
```
---
## API 调用示例
### 使用 Python 调用
```python
import httpx
API_KEY = "your-external-api-key"
BASE_URL = "http://localhost:8080/api/v1"
headers = {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
}
# 添加节点
response = httpx.post(
f"{BASE_URL}/nodes",
json={
"node_id": "wx1",
"name": "微信1",
"api_url": "http://192.168.1.100:5000",
"api_key": "node-api-key"
},
headers=headers
)
print(response.json())
# 发送消息
response = httpx.post(
f"{BASE_URL}/messages/send",
json={
"node_id": "wx1",
"who": "wxid_friend",
"msg": "Hello!"
},
headers=headers
)
print(response.json())
```
### 使用 curl 调用
```bash
# 添加节点
curl -X POST http://localhost:8080/api/v1/nodes \
-H "X-API-Key: your-external-api-key" \
-H "Content-Type: application/json" \
-d '{"node_id": "wx1", "name": "微信1", "api_url": "http://192.168.1.100:5000", "api_key": "key"}'
# 发送消息
curl -X POST http://localhost:8080/api/v1/messages/send \
-H "X-API-Key: your-external-api-key" \
-H "Content-Type: application/json" \
-d '{"node_id": "wx1", "who": "wxid_friend", "msg": "Hello"}'
# 获取节点状态
curl http://localhost:8080/api/v1/nodes/wx1/status \
-H "X-API-Key: your-external-api-key"
# 获取监控状态
curl http://localhost:8080/api/v1/monitor/status \
-H "X-API-Key: your-external-api-key"
# 获取最近日志
curl "http://localhost:8080/api/v1/logs/recent?count=20" \
-H "X-API-Key: your-external-api-key"
# 重置节点微信状态(回调接口)
curl -X POST http://localhost:8080/api/v1/callback/node/reset \
-H "X-API-Key: your-external-api-key" \
-H "Content-Type: application/json" \
-d '{"node_id": "wx1"}'
```
### 使用 JavaScript 调用
```javascript
const API_KEY = 'your-external-api-key';
const BASE_URL = 'http://localhost:8080/api/v1';
async function sendMessage(nodeId, who, msg) {
const response = await fetch(`${BASE_URL}/messages/send`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
node_id: nodeId,
who: who,
msg: msg
})
});
return await response.json();
}
// 使用示例
sendMessage('wx1', 'wxid_friend', 'Hello!')
.then(result => console.log(result));
```
---
## 前端开发
### 前端架构
前端采用纯 JavaScript 模块化设计:
```
static/
├── index.html # 主页面
├── css/
│ └── style.css # 样式文件
└── js/
├── api.js # API 调用封装
├── app.js # 主逻辑
└── pages/
└── templates.js # 页面模板
```
### 页面模块
系统前端包含以下页面:
| 页面 | 功能 |
|------|------|
| dashboard | 系统概览 |
| nodes | 节点管理 |
| monitor | 监控状态 |
| send | 消息发送 |
| logs | 日志查看 |
| external | 外部接口 |
| docs | 文档 |
| settings | 设置 |
### API 封装 (api.js)
```javascript
// 基础配置
const API_BASE = '/api/v1';
const API_KEY = 'your-api-key';
// 通用请求函数
async function apiRequest(endpoint, options = {}) {
const url = `${API_BASE}${endpoint}`;
const headers = {
'X-API-Key': API_KEY,
'Content-Type': 'application/json',
...options.headers
};
const response = await fetch(url, {
...options,
headers
});
return response.json();
}
// API 方法封装
const API = {
// 节点管理
getNodes: (group) => apiRequest(`/nodes${group ? `?group=${group}` : ''}`),
addNode: (data) => apiRequest('/nodes', { method: 'POST', body: JSON.stringify(data) }),
deleteNode: (name) => apiRequest(`/nodes/${name}`, { method: 'DELETE' }),
// 消息发送
sendMessage: (data) => apiRequest('/messages/send', { method: 'POST', body: JSON.stringify(data) }),
sendGroupMessage: (data) => apiRequest('/messages/send_group', { method: 'POST', body: JSON.stringify(data) }),
batchSendMessage: (data) => apiRequest('/messages/batch', { method: 'POST', body: JSON.stringify(data) }),
// 监控状态
getMonitorStatus: () => apiRequest('/monitor/status'),
getRecentLogs: (count) => apiRequest(`/logs/recent?count=${count || 50}`)
};
```
### SSE 日志订阅
```javascript
async function subscribeLogs(callback) {
const eventSource = new EventSource('/api/v1/logs/stream');
eventSource.onmessage = (event) => {
const logEntry = JSON.parse(event.data);
callback(logEntry);
};
eventSource.onerror = (error) => {
console.error('SSE Error:', error);
eventSource.close();
};
return eventSource; // 使用完毕后调用 close()
}
// 使用示例
const es = subscribeLogs((log) => {
console.log(`[${log.level}] ${log.message}`);
});
// 关闭时: es.close();
```
---
## 测试
### 运行测试
```bash
# 安装测试依赖
pip install pytest pytest-asyncio httpx
# 运行所有测试
pytest
# 运行指定测试文件
pytest tests/test_nodes.py
# 带详细输出
pytest -v
```
### 编写测试
```python
# tests/test_nodes.py
import pytest
from httpx import AsyncClient
from main import app
@pytest.mark.asyncio
async def test_add_node():
async with AsyncClient(app=app, base_url="http://test") as client:
response = await client.post(
"/api/v1/nodes",
json={
"name": "test_node",
"api_url": "http://localhost:5000",
"api_key": "test_key"
},
headers={"X-API-Key": "test-key"}
)
assert response.status_code == 200
data = response.json()
assert data["success"] is True
```
---
## 调试技巧
### 启用调试模式
```bash
# 方式1:环境变量
export DEBUG=true
python main.py
# 方式2:命令行参数(如果支持)
uvicorn main:app --reload --log-level debug
# 方式3:修改 .env
DEBUG=true
```
### 查看详细日志
系统内置日志服务,可通过 API 查看:
```bash
# 获取最近日志
curl http://localhost:8080/api/v1/logs/recent?count=100 \
-H "X-API-Key: your-api-key"
# 订阅实时日志流
curl -N http://localhost:8080/api/v1/logs/stream \
-H "X-API-Key: your-api-key"
```
### API 测试工具
- FastAPI 内置 docshttp://localhost:8080/docs
- ReDochttp://localhost:8080/redoc
- Postman / Insomnia
---
## 代码规范
### Python 代码规范
遵循 PEP 8 和项目规则:
- 使用 4 空格缩进
- 类名使用 PascalCase
- 函数/变量使用 camelCase
- 常量使用 UPPER_SNAKE_CASE
- 每个文件包含头部注释
### 注释要求
```python
# ///
# filename.py
# 描述:该文件负责 [核心功能]
# 作者:AI Generated
# 创建日期:YYYY-MM-DD
# ///
def function_name(param1, param2):
"""
函数简要描述
@param {type} param1 - 参数说明
@return {type} 返回值说明
@throws {异常类型} 异常情况说明
"""
pass
```
---
## 部署注意事项
### 生产环境配置
1. 修改 `secret_key` 为强密码
2. 设置 `debug: false`
3. 配置正确的 `cors_origins`
4. 使用 HTTPS
5. 配置防火墙规则
### 使用 Nginx 反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
}
}
```
### 使用 systemd 服务
```ini
# /etc/systemd/system/wxauto-center.service
[Unit]
Description=WXAuto Center Service
After=network.target
[Service]
Type=simple
User=your-user
WorkingDirectory=/path/to/wxauto_center
ExecStart=/path/to/venv/bin/python main.py
Restart=always
[Install]
WantedBy=multi-user.target
```
```bash
sudo systemctl enable wxauto-center
sudo systemctl start wxauto-center
```
---
## 关键实现细节
### 1. 健康检测机制 (is_node_healthy)
每次发送消息前,系统会调用此方法检测节点健康状态:
```python
async def is_node_healthy(self, name: str) -> tuple[bool, str]:
# 1. 检查 API 状态
api_status = await self.check_node_status(name)
if api_status != NodeStatus.ACTIVE:
return False, f"API status: {api_status}"
# 2. 调用 /api/wechat/initialize 检测微信
init_result = await self.call_node_api(name, "/api/wechat/initialize", method="POST")
# 3. 解析返回状态
if not init_result.get("success"):
return False, f"WeChat check failed: {init_result.get('error')}"
# 4. 检查 code 和 status
wx_code = init_result.get("code") or init_result.get("data", {}).get("code")
wx_status = init_result.get("data", {}).get("data", {}).get("status")
if wx_code != 0:
return False, "WeChat error: ..."
if wx_status not in ["connected", "online"]:
return False, f"WeChat status: {wx_status}"
return True, "OK"
```
### 2. 消息发送流程
```python
async def send_message(self, name: str, who: str, msg: str, msg_type: str = "text"):
# 1. 先初始化微信
await self.call_node_api(name, "/api/wechat/initialize", method="POST")
# 2. 发送消息
return await self.call_node_api(
name,
"/api/message/send",
method="POST",
data={"receiver": who, "message": msg}
)
```
### 3. 监控告警流程
```python
async def _check_node(self, node_id: str):
# 检测 API 状态变化
api_status = await node_manager.check_node_status(node_id)
if old_api_status != api_status:
event = "node_online" if api_status == "active" else "node_offline"
await self._send_webhook(event, {...})
# 检测微信状态变化(通过发送消息检测)
wechat_status = node.wechat_status # 从节点对象获取
if old_wechat_status != wechat_status:
event = "wechat_online" if wechat_status == "online" else "wechat_offline"
await self._send_webhook(event, {...})
```
**注意**:微信状态现在通过消息发送结果自动更新,不需要主动检测。