架构说明
AirlineSim Autobuy 采用管道架构设计,将监控、解析、匹配和执行各阶段分离为独立的模块,通过 Go 的并发原语协同工作。
架构概览
┌─────────────────────────────────────────────────────────────┐
│ main.go │
│ 初始化配置 → 创建引擎 → 启动 WebUI → 等待信号 │
└──────────┬──────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Engine │
│ 监控管道编排器(每个服务器一个 goroutine) │
└──┬──────────┬──────────┬──────────┬──────────┬──────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│Auth │ │Client│ │Coll.│ │Pars.│ │Rules│ │Exec.│
│认证 │ │HTTP │ │采集 │ │解析 │ │规则 │ │执行 │
│会话 │ │客户端│ │市场 │ │HTML │ │引擎 │ │购买 │
└──────┘ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘
┌─────────────────────────────────────────────────────────────┐
│ WebUI (HTTP Server) │
│ chi 路由器 → 静态文件服务 (Vue SPA) + REST API │
└─────────────────────────────────────────────────────────────┘组件概述
1. 入口(cmd/autobuy/main.go)
应用程序的启动点,负责:
- 配置日志系统(使用
log/slog结构化日志) - 确保配置文件存在,若不存在则生成默认配置
- 加载配置,初始化配置存储
- 创建通知器和引擎实例
- 如果启用了 WebUI,启动 HTTP 服务器
- 等待系统信号(SIGINT/SIGTERM)实现优雅关闭
2. 配置管理(internal/config)
配置系统围绕线程安全的 Store 实现:
- Store:封装了配置的读写操作,使用
sync.RWMutex保证并发安全 - Watcher:通过轮询(每 5 秒)监控文件修改时间,实现配置热重载
- 类型定义:
Config结构体映射 YAML 配置的所有字段 - 默认配置:
DefaultConfig()提供合理的默认值 - 向后兼容:自动迁移旧的单服务器/单账户配置到多服务器/多账户格式
3. 认证模块(internal/auth)
管理与 AirlineSim 游戏服务器的认证会话:
- Session:封装 HTTP 客户端、Cookie 存储和登录状态
- 登录流程:通过
sar.simulogics.gamesAPI 进行密码认证,获取as-sid令牌 - 会话持久化:将 Cookie 序列化到本地文件,重启后可恢复
- 健康检查:定期验证会话有效性,过期自动重新登录
- 安全传输:
secureTransport限制只允许连接到已知的游戏服务器域名
4. HTTP 客户端(internal/client)
提供安全的、带限速的 HTTP 客户端:
- 速率限制:基于令牌桶算法,控制请求频率
- 随机延迟(Jitter):在每次请求前添加随机延迟,模拟人类行为
- 自动重试:遇到服务器错误(5xx)或限流(429)时自动重试最多 3 次
- URL 验证:严格的安全检查,防止请求到内网地址或恶意域名
5. 市场采集器(internal/collector)
负责从游戏服务器获取市场页面数据:
- URL 发现:首次访问时自动发现 Wicket 市场页面的实际 URL
- 参数化请求:支持按飞机系列、型号和排序方式过滤市场数据
- Wicket 兼容:使用 Wicket 风格的 URL 参数格式
6. HTML 解析器(internal/parser)
将 Wicket 市场页面的 HTML 解析为结构化数据:
- HTML 解析:使用
goquery库解析 HTML 表格 - 字段提取:从表格行中提取机型、价格、机龄、状态、循环数等信息
- 多格式支持:处理不同地区的数字格式(欧洲格式
1.234.567,89和美国格式1,234,567.89) - 交易类型检测:通过页面内容判断是拍卖还是立即购买
7. 规则引擎(internal/rules/engine.go)
评估飞机是否符合购买规则并计算评分:
- 规则匹配:依次检查所有启用规则的条件(机型、价格、机龄、状态等)
- 评分系统:综合价格优势、机身状态、机龄、交易类型和规则优先级计算评分
- 结果排序:按评分降序排列,返回最佳匹配
8. 购买执行器(internal/executor)
执行实际的飞机购买操作:
- 立即购买:访问飞机详情页,提交购买表单
- 竞拍出价:提交竞拍出价,支持自定义加价幅度
- 余额检查:在购买前检查最低余额保护
- 价格验证:验证当前价格是否与预期一致
9. 通知器(internal/notifier)
通过配置的渠道发送事件通知:
- 控制台输出:使用
slog输出带表情符号的结构化日志 - Discord Webhook:支持通过 Webhook 发送通知到 Discord 频道
- 事件类型:发现飞机、购买成功、购买失败、出价成功、错误、信息
10. Web 管理界面(internal/webui)
提供基于 Web 的图形化管理界面:
- HTTP 服务器:使用
chi路由库 - REST API:提供完整的 REST API 用于引擎控制和配置管理
- 静态文件服务:嵌入 Vue 3 单页应用,非 API 路由全部回退到
index.html - CORS 支持:开发模式下允许跨域请求
数据流
以下是单次市场扫描周期的数据流:
1. 定时器触发 ──→ Engine.runServerCycle()
│
2. 检查会话健康 ──→ Session.HealthCheck()
│ 如果过期则重新登录
▼
3. 构建过滤参数 ──→ 读取第一条启用规则的过滤条件
│
▼
4. 获取市场页面 ──→ Collector.Fetch(filterParams)
│
▼
5. 解析 HTML ────→ Parser.Parse(html)
│ 返回 AircraftOffer 列表
▼
6. 去重检查 ──────→ 使用 seen map 检查是否已处理过
│
▼
7. 规则匹配 ──────→ RulesEngine.Evaluate(aircraft)
│ 返回 MatchResult 列表
▼
8. 执行购买 ──────→ Executor.Execute(request)
│ 立即购买或竞拍出价
▼
9. 发送通知 ──────→ Notifier.Notify(event)
│ 控制台和/或 Discord
▼
10. 更新状态 ─────→ 更新 ServerStatus 统计并发模型
程序使用 Go 的 goroutine 和 channel 实现并发:
引擎并发
- 每个配置的服务器在独立的 goroutine 中运行(
Engine.runServerLoop) - 所有服务器共享同一个配置存储和规则引擎
- 引擎停止时通过
context.Context的取消函数通知所有 goroutine 退出
数据同步
- 配置存储使用
sync.RWMutex保护读写 - 引擎状态使用
sync.RWMutex保护 - 已处理飞机 ID 使用
seen map配合互斥锁去重 - 每个服务器使用独立的
serverRunner实例,避免共享状态
速率控制
- HTTP 客户端使用令牌桶模式限制请求频率
- 每个服务器独立控制请求速率
- 随机延迟(Jitter)防止请求模式被检测
目录结构
airlinesim-autobuy/
├── cmd/autobuy/ # 应用程序入口
│ └── main.go
├── internal/
│ ├── auth/ # 认证与会话管理
│ │ └── auth.go
│ ├── client/ # 限速 HTTP 客户端
│ │ └── client.go
│ ├── collector/ # 市场数据采集
│ │ └── collector.go
│ ├── config/ # 配置管理与热重载
│ │ ├── config.go
│ │ ├── types.go
│ │ └── watcher.go
│ ├── engine/ # 管道编排
│ │ └── engine.go
│ ├── executor/ # 购买执行
│ │ └── executor.go
│ ├── marketdata/ # 飞机市场参考数据
│ │ └── data.go
│ ├── notifier/ # 通知系统
│ │ └── notifier.go
│ ├── parser/ # HTML 页面解析
│ │ └── parser.go
│ ├── rules/ # 规则引擎
│ │ └── engine.go
│ └── webui/ # Web 管理界面
│ ├── server.go
│ └── frontend/ # Vue 3 前端源码
├── configs/ # 配置文件目录
├── docs/ # VitePress 文档
├── Makefile
└── go.mod