Skip to content

架构说明 ​

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.games API 进行密码认证,获取 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

Released under AGPL-3.0.