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

19 KiB
Raw Blame History

WXAuto Center 开发指南

开发环境搭建

环境要求

  • Python 3.8+
  • Node.js 14+ (可选,用于前端开发)
  • Git

克隆与安装

# 克隆项目
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

配置开发环境

# 复制环境变量示例文件
cp .env.example .env

# 编辑 .env 文件
vim .env

启动开发服务器

# 方式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

访问服务


项目结构详解

目录结构

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. 处理启动/关闭事件

关键代码流程:

# 启动事件
@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 进行配置管理:

# 配置模型
Settings              # 主配置类
NodeConfig           # 节点配置
PluginConfig         # 插件配置
WebhookConfig        # Webhook 配置
MonitorConfig        # 监控配置

# 配置管理器
ConfigManager        # 提供节点配置的增删改查

# 配置加载/保存
load_config_from_file()   # 从文件加载
save_config_to_file()     # 保存到文件

节点管理器 (services/node_manager.py)

NodeManager 是核心服务类,负责与 WXAuto-HTTP-API 节点通信。

# 节点状态
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 提供日志记录和实时推送功能。

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. 创建插件类

# 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. 注册插件

# 在 builtin.py 或 main.py 中注册
from plugins.base import PluginRegistry

my_plugin = MyCustomPlugin()
my_plugin.initialize({"key": "value"})
PluginRegistry.register(my_plugin)

插件类型详解

DataSourcePlugin

用于从外部数据源获取数据:

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 消息处理:

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 调用

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 调用

# 添加节点
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 调用

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)

// 基础配置
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 日志订阅

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();

测试

运行测试

# 安装测试依赖
pip install pytest pytest-asyncio httpx

# 运行所有测试
pytest

# 运行指定测试文件
pytest tests/test_nodes.py

# 带详细输出
pytest -v

编写测试

# 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

调试技巧

启用调试模式

# 方式1:环境变量
export DEBUG=true
python main.py

# 方式2:命令行参数(如果支持)
uvicorn main:app --reload --log-level debug

# 方式3:修改 .env
DEBUG=true

查看详细日志

系统内置日志服务,可通过 API 查看:

# 获取最近日志
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 测试工具


代码规范

Python 代码规范

遵循 PEP 8 和项目规则:

  • 使用 4 空格缩进
  • 类名使用 PascalCase
  • 函数/变量使用 camelCase
  • 常量使用 UPPER_SNAKE_CASE
  • 每个文件包含头部注释

注释要求

# ///
# 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 反向代理

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 服务

# /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
sudo systemctl enable wxauto-center
sudo systemctl start wxauto-center

关键实现细节

1. 健康检测机制 (is_node_healthy)

每次发送消息前,系统会调用此方法检测节点健康状态:

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. 消息发送流程

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. 监控告警流程

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, {...})

注意:微信状态现在通过消息发送结果自动更新,不需要主动检测。