Compare commits

..

1 Commits

Author SHA1 Message Date
vincent 8171373812 BIZ-74: 双色球系统架构设计文档 v1.0
- 完整文件级/函数级架构拆解
- 10个文件、40+函数的签名/输入/输出/边界条件
- 部署拓扑、数据流、数据模型
- 非功能需求(性能/安全/兼容性)
- 8项风险评估 + 改进建议
- 5项产研评审决策点
- ADR-007 架构决策记录

架构师: 梁思筑
Issue: BIZ-74

Co-authored-by: multica-agent <github@multica.ai>
2026-07-03 16:34:34 +08:00
2 changed files with 827 additions and 214 deletions
@@ -0,0 +1,827 @@
# 双色球自动化系统 — 架构设计文档
**文档编号**: ADR-007
**Issue**: BIZ-74
**项目**: 双色球自动化系统(lottoData
**架构师**: 梁思筑(Serena
**日期**: 2026-07-03
**版本**: v1.0
**PRD 来源**: BIZ-73(沈路明,双色球 WebUI PRD v1.0
---
## 一、系统概述
### 1.1 系统定位
双色球自动化系统是一个局域网 Web 应用,提供双色球号码生成、历史数据查看、生成记录管理和统计分析功能。系统基于历史开奖数据分析,使用热冷号策略 + 奇偶比/大小比/和值/跨度调整算法生成推荐号码。
### 1.2 部署拓扑
```
┌──────────────────────────────────────────────────────────┐
│ Ubuntu Server (192.168.1.99) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ lotto-web.service (systemd) │ │
│ │ web_executor.py (Flask :5000) │ │
│ │ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ / │ │ /api/status │ │ │
│ │ │ /api/execute │ │ │ │ │
│ │ └──────┬───────┘ └───────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ fetch_data.py ── HTTP ──► 55128.cn │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 双色球历史数据.xlsx (Excel 文件) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ app.py (Flask :8085) ← 手动启动 / 尚未 systemd │ │
│ │ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ / │ │ /api/generate │ │ │
│ │ │ /api/history │ │ /api/records │ │ │
│ │ │ /api/status │ │ /api/download │ │ │
│ │ │ /api/config │ │ /api/statistics│ │ │
│ │ └──────┬───────┘ └───────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ lottery.py (DoubleColorBallGenerator) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 双色球历史数据.xlsx (Excel) │ │
│ │ .generation_records.json (JSON) │ │
│ │ lottery/ (生成结果 .xlsx) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Cron (每日 02:30) │ │
│ │ deploy/fetch_daily.sh → fetch_data.py │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 局域网用户 (PC / 移动端) │ │
│ │ → http://192.168.1.99:5000 (数据抓取控制台) │ │
│ │ → http://192.168.1.99:8085 (号码生成 WebUI) │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
### 1.3 数据流
```
55128.cn ──HTTP fetch──► fetch_data.py ──写入──► 双色球历史数据.xlsx
读取 │
lottery.py (DoubleColorBallGenerator)
├─ 加载历史数据
├─ 计算热冷号统计
├─ 生成号码(高级/基础策略)
└─ 保存结果 → lottery/*.xlsx
调用 │
app.py (Flask :8085)
├─ /api/generate → 生成号码
├─ /api/history → 读历史数据
├─ /api/records → 管理生成记录
├─ /api/statistics → 统计分析
└─ /api/download → 文件下载
HTTP │
index.html (前端 UI)
├─ 号码生成页
├─ 历史数据页
├─ 生成记录页
└─ 统计分析页
```
---
## 二、技术选型
### 2.1 当前技术栈
| 层级 | 技术 | 版本 | 说明 |
|------|------|------|------|
| 后端框架 | Flask | 3.1.3 | 轻量 Web 框架 |
| 号码生成 | Python | 3.x | 核心算法 |
| 数据处理 | Pandas + NumPy | 3.0.4 / 1.x | Excel 读写 + 统计计算 |
| Excel 读写 | openpyxl | 3.1.5 | .xlsx 文件操作 |
| 数据抓取 | requests + BeautifulSoup4 | 2.34.2 / 4.15.0 | HTTP + HTML 解析 |
| 前端 | 原生 HTML/CSS/JS | ES6+ | 单文件 SPA,无构建工具 |
| 数据存储 | Excel + JSON 文件 | — | 无数据库 |
| 部署 | systemd + Cron | — | 直接运行 |
### 2.2 选型评估
| 决策点 | 当前方案 | 备选方案 | 评估 |
|--------|----------|----------|------|
| Web 框架 | Flask | FastAPI | ✅ 保持 Flask。项目规模小,同步处理足够;团队熟悉 Flask |
| 前端 | 原生 HTML/JS | Vue/React | ✅ 保持原生。单页面应用,无构建工具依赖,部署简单 |
| 数据存储 | Excel + JSON | SQLite | ⚠️ 建议中期迁移 SQLite。当前 Excel 足够,但并发写入和查询效率有瓶颈 |
| 部署 | systemd | Docker | ✅ 保持 systemd。内网环境,单服务简单可靠 |
| 进程模型 | 双 Flask 服务 | 合并为单服务 | ⚠️ 建议合并。当前 web_executor.py(:5000) 和 app.py(:8085) 分离,但功能可整合 |
---
## 三、文件级架构拆解
### 3.1 文件总览
| # | 文件 | 职责 | 行数 | 依赖 | 部署状态 |
|---|------|------|------|------|----------|
| 1 | `lottery.py` | 号码生成核心引擎 | ~1090 | pandas, numpy, openpyxl | ✅ 已部署 |
| 2 | `fetch_data.py` | 历史数据抓取脚本 | ~115 | requests, bs4, pandas, openpyxl | ✅ 已部署 |
| 3 | `web_executor.py` | 数据抓取 Web 控制台 | ~170 | flask | ✅ systemd 已部署 |
| 4 | `web_console.html` | 抓取控制台前端 | ~300 | 无 | ✅ 已部署 |
| 5 | `app.py` | 号码生成 Web 服务(主应用) | ~480 | flask, pandas, lottery.py | ⚠️ 未做 systemd |
| 6 | `index.html` | 号码生成 WebUI 前端 | ~1170 | 无 | ✅ 已部署 |
| 7 | `deploy/fetch_daily.sh` | 每日定时抓取脚本 | ~15 | bash | ✅ Cron 已配置 |
| 8 | `deploy/lotto-web.service` | web_executor systemd 单元 | ~15 | systemd | ✅ 已部署 |
| 9 | `.generation_records.json` | 生成记录持久化 | — | 无 | ✅ 运行时生成 |
| 10 | `.fetch_status.json` | 抓取状态持久化 | — | 无 | ✅ 运行时生成 |
### 3.2 模块依赖关系
```
index.html (前端)
│ HTTP (Flask routes)
app.py ──────► lottery.py ──────► 双色球历史数据.xlsx
│ │
│ ├─► .generation_records.json
│ └─► lottery/*.xlsx
└─► (独立于 web_executor.py)
web_console.html (前端)
│ HTTP (Flask routes)
web_executor.py ──► fetch_data.py ──► 双色球历史数据.xlsx
│ │
│ └─► HTTP GET → 55128.cn
└─► .fetch_status.json
```
**关键观察**`app.py``web_executor.py` 共享 `双色球历史数据.xlsx` 文件,但无进程间通信。两个 Flask 服务独立运行。
---
## 四、逐文件函数级设计
### 4.1 `lottery.py` — 号码生成核心引擎
**职责**: 加载历史数据、统计分析、生成号码、保存 Excel
**依赖**: `pandas`, `numpy`, `openpyxl`, `random`, `re`, `collections.Counter`
#### 4.1.1 类: `DoubleColorBallGenerator`
| 方法 | 签名 | 输入 | 输出 | 边界条件 |
|------|------|------|------|----------|
| `__init__` | `(history_file, config=None)` | Excel 文件路径, 配置字典 | — | config=None 时使用默认配置 |
| `load_history_data` | `()` | 无 | `bool` | 文件不存在→False; 数据为空→False; 缺"号码"列→False; 有效数据<10条→WARNING |
| `_calculate_statistics` | `()` | 无 | `None` | 数据为空时直接返回; 统计红球频次、蓝球频次、奇偶比、大小比、和值范围、跨度范围 |
| `get_hot_red_balls` | `(n=10)` | 数量 | `List[int]` | 统计为空→随机返回; 不足n个→用其他球补全 |
| `get_cold_red_balls` | `(n=10)` | 数量 | `List[int]` | 同上 |
| `get_hot_blue_balls` | `(n=5)` | 数量 | `List[int]` | 同上 |
| `parse_ratio` | `(ratio_str)` | 比率字符串 | `(int, int)` | NaN→(3,3) 默认值; 非法格式→(3,3) |
| `_adjust_balls_by_criteria` | `(red_balls, current_value, target_value, get_balls_to_remove, get_candidates, recalculate_current)` | 红球列表+3个回调函数 | `List[int]` | 最多尝试 max_adjustment_attempts=20 次 |
| `_select_hot_cold_balls` | `()` | 无 | `List[int]` | 热号2-4个+冷号补全; 不足6个→随机补全 |
| `_adjust_odd_even_ratio` | `(red_balls)` | 红球列表 | `List[int]` | 80%概率选最常见奇偶比, 20%随机; 无统计→不调整 |
| `_adjust_size_ratio` | `(red_balls)` | 红球列表 | `List[int]` | 80%概率选最常见大小比, 20%随机; 无统计→不调整 |
| `_adjust_sum_range` | `(red_balls)` | 红球列表 | `List[int]` | 90%概率调整到 mean±std 范围内, 10%保持原样 |
| `_adjust_span_range` | `(red_balls)` | 红球列表 | `List[int]` | 90%概率调整到 mean±std 范围内, 10%保持原样 |
| `_select_blue_ball` | `()` | 无 | `int` | hot_blue_probability=0.7 概率选热号, 否则随机 |
| `generate_single_ticket_advanced` | `()` | 无 | `(List[int], int)` | 返回 (红球6个排序, 蓝球1个) |
| `generate_single_ticket_basic` | `()` | 无 | `(List[int], int)` | 完全随机 |
| `generate_multiple_tickets` | `(num_tickets, strategy="advanced")` | 注数+策略 | `pd.DataFrame` | 去重检查; 每注最多尝试100次; 总失败超限→降级为basic |
| `save_to_excel` | `(tickets_df, num_tickets, strategy)` | DataFrame+注数+策略 | `str\|None` | 返回文件路径或None; 自动避免覆盖; 含统计信息sheet |
| `display_statistics` | `()` | 无 | `None` | 打印统计信息到控制台 |
| `run_tests` | `()` | 无 | `None` | 运行内置测试用例 |
| `main` | 模块级 | 无 | `int` | 交互式命令行入口 |
#### 4.1.2 异常场景
| 场景 | 处理方式 | 风险 |
|------|----------|------|
| 历史数据文件不存在 | `load_history_data` 返回 False | 低 — 调用方应检查返回值 |
| Excel 文件格式错误 | pandas 抛异常, catch 后返回 False | 低 |
| 有效数据 < 10 条 | 打印 WARNING, 继续执行 | 中 — 统计结果可能不可靠 |
| 号码解析失败 | 返回空列表 + 0, 继续处理其他行 | 低 |
| 生成号码重复 | 最多尝试 100 次/注, 超限降级为 basic | 低 |
| Excel 写入失败 | catch 异常, 删除部分文件, 返回 None | 中 — 磁盘满或权限问题 |
| 统计数据为空 | 返回随机号码作为默认值 | 中 — 历史数据不足时退化为随机 |
#### 4.1.3 配置参数
```python
DEFAULT_CONFIG = {
'hot_red_count': 15, # 热号红球取前15
'cold_red_count': 10, # 冷号红球取前10
'hot_blue_count': 8, # 热号蓝球取前8
'hot_blue_probability': 0.7, # 70%概率选热号蓝球
'max_adjustment_attempts': 20, # 号码调整最大尝试次数
'hot_red_display_count': 10, # 展示热号前10
'cold_red_display_count': 10, # 展示冷号前10
'hot_blue_display_count': 5, # 展示热号蓝球前5
'min_tickets': 1,
'max_tickets': 1000
}
```
---
### 4.2 `fetch_data.py` — 历史数据抓取脚本
**职责**: 从 55128.cn 抓取双色球历史开奖数据,保存到 Excel
**依赖**: `requests`, `bs4.BeautifulSoup`, `pandas`, `openpyxl`
#### 4.2.1 函数清单
| 函数 | 签名 | 输入 | 输出 | 边界条件 |
|------|------|------|------|----------|
| `fetch_lottery_data` | `()` | 无 | `List[List[str]]\|None` | 网络超时30s→None; 未找到表格→None; 解析0条→None |
| `save_to_excel` | `(data_rows)` | 二维数据列表 | `bool` | 空数据→False; 列数不匹配→截取; 保存失败→False |
| `main` | `()` | 无 | `int` | 返回 0(成功) / 1(失败) |
#### 4.2.2 关键常量
```python
URL = "https://www.55128.cn/kjh/fcssq-history-120.htm"
OUTPUT_FILE = os.path.join(SCRIPT_DIR, "双色球历史数据.xlsx")
HEADERS = { "User-Agent": "Mozilla/5.0 ...", ... }
```
#### 4.2.3 异常场景
| 场景 | 处理方式 | 风险 |
|------|----------|------|
| 网络请求超时 | 30s 超时, 返回 None | 中 — 网站不可达时抓取失败 |
| 网站返回非 200 | `raise_for_status` 抛异常, catch 返回 None | 低 |
| HTML 结构变更 | `soup.find("table")` 返回 None | 高 — 55128.cn 改版会导致解析失败 |
| 表格行解析失败 | `continue` 跳过该行 | 低 |
| Excel 保存失败 | catch 异常, 返回 False | 低 |
---
### 4.3 `web_executor.py` — 数据抓取 Web 控制台
**职责**: 提供 Web 界面执行数据抓取任务,异步运行 fetch_data.py
**依赖**: `flask`, `subprocess`, `threading`
#### 4.3.1 函数清单
| 函数 | 签名 | 输入 | 输出 | 边界条件 |
|------|------|------|------|----------|
| `load_status` | `()` | 无 | `None` | 从 .fetch_status.json 加载; 文件不存在→使用默认状态 |
| `save_status` | `()` | 无 | `None` | 使用 status_lock 保证线程安全 |
| `index` | Flask route `GET /` | 无 | HTML 页面 | 返回 web_console.html |
| `api_status` | Flask route `GET /api/status` | 无 | JSON | 返回当前执行状态 |
| `api_execute` | Flask route `POST /api/execute` | 无 | JSON | 已有任务运行中→409; 否则启动后台线程 |
| `run_script` (内部) | `()` | 无 | `None` | 在 daemon 线程中执行 fetch_data.py; 超时300s; 结果写入状态文件 |
| `check_dependencies` | `()` | 无 | `bool` | 检查 flask/requests/bs4/pandas/openpyxl 是否安装 |
#### 4.3.2 异常场景
| 场景 | 处理方式 | 风险 |
|------|----------|------|
| 重复执行 | 检查 is_running 标志, 返回 409 | 低 |
| 脚本超时 | `subprocess.run` 超时 300s, 设置 last_error | 中 — 长时间占用资源 |
| 脚本失败 | 解析 stderr, 记录 last_error | 低 |
| 线程异常 | catch Exception, 记录错误, 释放 is_running | 中 |
| 状态文件损坏 | `load_status` catch 异常, 使用默认状态 | 低 |
#### 4.3.3 端口与监听
- 监听: `0.0.0.0:5000`
- 线程模式: `threaded=True`
- systemd 服务: `lotto-web.service`
---
### 4.4 `app.py` — 号码生成 Web 服务(主应用)
**职责**: 提供号码生成、历史数据查看、生成记录管理、统计分析 API
**依赖**: `flask`, `pandas`, `lottery.py (DoubleColorBallGenerator)`
#### 4.4.1 配置
```python
CONFIG = {
'host': '0.0.0.0',
'port': 8085,
'history_file': '双色球历史数据.xlsx',
'lottery_output_dir': 'lottery',
'records_file': '.generation_records.json',
'api_token': 'lotto2026',
'auth_enabled': False, # Token 认证默认关闭
'max_tickets': 1000,
'default_tickets': 10,
}
```
#### 4.4.2 函数清单
| 函数 | 路由 | 方法 | 输入 | 输出 | 边界条件 |
|------|------|------|------|------|----------|
| `load_records` | — | — | 无 | `List[dict]` | 文件不存在→[]; JSON 解析失败→[] |
| `save_records` | — | — | `List[dict]` | `None` | 自动创建目录 |
| `add_record` | — | — | strategy, num_tickets, filename | `dict` | 插入到列表头部, 自动生成 id |
| `require_auth` | 装饰器 | — | Flask request | — | auth_enabled=False 时跳过认证 |
| `api_generate` | `/api/generate` | POST | JSON body (num_tickets, strategy) | JSON | 参数校验 1-1000; 策略 advanced/basic; 生成失败→500 |
| `get_statistics_data` | — | — | generator(可选) | `dict` | 文件不存在→{}; 直接解析 Excel |
| `api_statistics` | `/api/statistics` | GET | 无 | JSON | 异常→500 |
| `api_records` | `/api/records` | GET | page, page_size | JSON | 分页; 默认 page=1, page_size=20 |
| `api_delete_record` | `/api/records/<id>` | DELETE | record_id | JSON | 记录不存在→404; 同时删除文件 |
| `api_download` | `/api/download/<path>` | GET | filepath | File | 路径安全检查(禁止 .. 和绝对路径); 文件不存在→404 |
| `api_history` | `/api/history` | GET | page, page_size, search | JSON | 读取 Excel; 跳过描述行; 支持搜索过滤; 分页 |
| `api_status` | `/api/status` | GET | 无 | JSON | 返回服务状态(历史数据是否存在、文件大小等) |
| `api_config` | `/api/config` | GET | 无 | JSON | 返回前端配置 |
| `index` | `/` | GET | 无 | HTML | 返回 index.html |
#### 4.4.3 异常场景
| 场景 | 处理方式 | 风险 |
|------|----------|------|
| 历史数据文件不存在 | `/api/generate` 返回 500; `/api/history` 返回 404 | 中 — 首次部署或数据被删 |
| 号码生成失败 | catch Exception, 返回 500 + 错误信息 | 低 |
| Excel 读取失败 | catch Exception, 返回 500 | 中 — 文件损坏 |
| 目录遍历攻击 | `api_download` 检查 .. 和绝对路径, 返回 403 | 低 |
| 生成记录文件损坏 | `load_records` catch 异常, 返回 [] | 低 |
| 并发写入 records | **无锁保护** | ⚠️ 高 — 多用户并发时可能导致数据丢失 |
| 并发生成号码 | **无锁保护** | ⚠️ 中 — Excel 文件并发写入可能损坏 |
#### 4.4.4 端口与监听
- 监听: `0.0.0.0:8085`
- 线程模式: `threaded=True`
- systemd 服务: **未配置**(仅手动启动 `python3 app.py`
---
### 4.5 `index.html` — 前端 UI
**职责**: 单文件 SPA,提供号码生成、历史数据、生成记录、统计分析四个 Tab
**依赖**: 无外部库(纯原生 HTML/CSS/JS
#### 4.5.1 页面结构
```
┌─────────────────────────────────┐
│ Header (标题 + 副标题) │
├─────────────────────────────────┤
│ Nav Tabs: 生成 | 历史 | 记录 | 统计│
├─────────────────────────────────┤
│ │
│ Page Content (Tab 切换) │
│ │
└─────────────────────────────────┘
```
#### 4.5.2 JS 函数清单
| 函数 | 职责 | 输入 | 输出 |
|------|------|------|------|
| `switchTab(tabName)` | 切换 Tab | tab 名称 | DOM 显示/隐藏 |
| `generateTickets()` | 调用 /api/generate | 从表单读取参数 | 渲染号码卡片 |
| `loadHistory(page)` | 调用 /api/history | 页码 | 渲染历史数据表格 |
| `loadRecords(page)` | 调用 /api/records | 页码 | 渲染记录卡片列表 |
| `loadStatistics()` | 调用 /api/statistics | 无 | 渲染统计卡片 |
| `downloadFile(path)` | 触发文件下载 | 文件路径 | 浏览器下载 |
| `deleteRecord(id)` | 调用 DELETE /api/records/:id | 记录 ID | 刷新列表 |
| `searchHistory(query)` | 搜索历史数据 | 搜索词(防抖500ms) | 过滤结果 |
#### 4.5.3 CSS 规范
| 元素 | 规范 |
|------|------|
| 主色 | `#e74c3c` (红色) |
| 辅色 | `#3498db` (蓝色), `#8e44ad` (紫色渐变) |
| 卡片圆角 | `12px` |
| 阴影 | `0 2px 12px rgba(0,0,0,0.08)` |
| 响应式 | viewport 320px-1920px 自适应 |
---
### 4.6 `web_console.html` — 抓取控制台前端
**职责**: 提供数据抓取的 Web 界面,显示抓取状态和执行按钮
**依赖**: 无外部库
---
### 4.7 `deploy/fetch_daily.sh` — 定时抓取脚本
**职责**: Cron 定时执行数据抓取
**Cron 表达式**: `30 2 * * *` (每日 02:30)
```bash
#!/bin/bash
SCRIPT_DIR="/home/vincent/Studio/lottoData"
VENV_PYTHON="${SCRIPT_DIR}/venv/bin/python3"
FETCH_SCRIPT="${SCRIPT_DIR}/fetch_data.py"
LOG_FILE="${LOG_DIR}/fetch_$(date +%Y%m%d).log"
"${VENV_PYTHON}" "${FETCH_SCRIPT}" >> "${LOG_FILE}" 2>&1
```
#### 异常场景
| 场景 | 处理方式 | 风险 |
|------|----------|------|
| venv 路径不存在 | 脚本执行失败, cron 记录错误 | 中 — Python 环境变更后需更新 |
| 网站不可达 | fetch_data.py 返回非 0, 日志记录 | 低 — 次日自动重试 |
| 磁盘满 | Excel 写入失败, 日志记录 | 中 — 需监控磁盘 |
---
## 五、数据模型
### 5.1 存储结构
本项目不使用数据库,所有数据基于文件存储:
| 数据文件 | 格式 | 读取者 | 写入者 | 大小 |
|----------|------|--------|--------|------|
| `双色球历史数据.xlsx` | Excel | app.py, lottery.py | fetch_data.py | ~12KB |
| `.generation_records.json` | JSON | app.py | app.py | <100KB |
| `.fetch_status.json` | JSON | web_executor.py | web_executor.py | <1KB |
| `lottery/*.xlsx` | Excel | app.py (下载) | lottery.py | 6-64KB/文件 |
### 5.2 Excel 数据结构
#### `双色球历史数据.xlsx` (历史开奖数据)
| 列 | 类型 | 说明 | 示例 |
|----|------|------|------|
| 开奖日期 | str | 日期 | "2026-07-02" |
| 期号 | str | 期号 | "2026076" |
| 红球 | str | 6红+1蓝拼接 | "09101316192108" |
| 开机号 | str | 开机号码 | — |
| 和值特征 | str | 和值 | "100" |
| 奇偶形态 | str | 奇偶比 | "3:3" |
| 大小比 | str | 大小比 | "4:2" |
| 奇偶形态2 | str | 奇偶形态(重复) | — |
| 跨度 | str | 跨度 | "25" |
| 其他 | str | 其他信息 | — |
**注意**: 第一行为描述行,实际数据从第二行开始。
#### `lottery/*.xlsx` (生成号码)
Sheet 1: "生成号码"
| 列 | 类型 | 说明 |
|----|------|------|
| 序号 | int | 1-N |
| 红球1-红球6 | int | 排序后的红球 |
| 蓝球 | int | 1-16 |
| 和值 | int | 红球之和 |
| 奇偶比 | str | "3:3" 格式 |
| 大小比 | str | "4:2" 格式 |
| 跨度 | int | max(红球)-min(红球) |
Sheet 2: "统计信息"
| 统计项 | 统计值 |
|--------|--------|
| 生成时间 | 2026-07-03 14:25:30 |
| 生成策略 | 高级策略/基础策略 |
| 生成注数 | 10 |
| 红球热号(前10) | 12, 15, 23, ... |
| 红球冷号(前10) | 4, 7, 31, ... |
| 蓝球热号(前5) | 8, 3, 11, ... |
| 最常见奇偶比 | 3:3 |
| 最常见大小比 | 4:2 |
| 和值范围 | 60-150 |
| 跨度范围 | 15-30 |
#### `.generation_records.json`
```json
[
{
"id": "a1b2c3d4",
"created_at": "2026-07-03 14:25:30",
"strategy": "高级策略",
"num_tickets": 10,
"filename": "lottery/双色球模拟号码-10注-20260703-001.xlsx",
"filesize": 11264
}
]
```
---
## 六、非功能需求
### 6.1 性能指标
| 指标 | 目标值 | 当前状态 | 说明 |
|------|--------|----------|------|
| 页面加载时间 | < 3s (P95) | ✅ 满足 | 单文件 HTML, 无外部依赖 |
| API 响应时间 | < 2s (P95) | ✅ 满足 | 除号码生成外 |
| 号码生成时间 | < 10s (P95) | ✅ 满足 | 100 注以内高级策略 |
| 并发用户数 | ≥ 10 | ⚠️ 未验证 | Flask threaded=True, 但无并发测试 |
| 数据文件大小 | < 1MB | ✅ 满足 | 历史 Excel ~12KB |
### 6.2 可用性 SLA
| 指标 | 目标值 | 说明 |
|------|--------|------|
| 服务可用性 | ≥ 99% (工作时段 9:00-22:00) | 内网工具, 非生产系统 |
| 数据持久化 | 生成记录永久保存 | 除非用户主动删除 |
| 数据备份 | 每日 Excel 由 Cron 自动更新 | 历史数据可从 55128.cn 重新抓取 |
### 6.3 安全策略
| 维度 | 当前措施 | 风险评估 | 建议 |
|------|----------|----------|------|
| 网络暴露 | 0.0.0.0:5000 + 0.0.0.0:8085 | ⚠️ 内网全开放 | 限制为 192.168.1.0/24 网段 |
| API 认证 | 可选 Token (auth_enabled=False) | ⚠️ 当前关闭 | 内网可接受, 外网部署需开启 |
| 目录遍历 | /api/download 路径检查 | ✅ 已防护 | — |
| 输入校验 | 参数范围检查 | ✅ 基本覆盖 | — |
| HTTPS | 未配置 | ⚠️ 内网 HTTP | 内网可接受, 外网需 SSL |
| 依赖安全 | Flask 3.1.3 最新 | ✅ 无已知漏洞 | 定期更新依赖 |
### 6.4 兼容性
| 平台 | 浏览器 | 版本要求 | 测试状态 |
|------|--------|----------|----------|
| PC | Chrome | 90+ | ✅ 主力测试 |
| PC | Safari | 14+ | ⏳ 待测 |
| PC | Edge | 90+ | ⏳ 待测 |
| 移动端 | iOS Safari | 14+ | ⏳ 待测 |
| 移动端 | Android Chrome | 90+ | ⏳ 待测 |
| 移动端 | 微信内置浏览器 | 最新版 | ⏳ 待测 |
---
## 七、风险评估
| # | 风险 | 概率 | 影响 | 应对措施 |
|---|------|------|------|----------|
| R1 | 55128.cn 网站改版导致抓取失败 | 中 | 高 | 增加 HTML 结构版本检测; 备选数据源 |
| R2 | Excel 并发写入导致数据损坏 | 低 | 高 | 加文件锁或迁移 SQLite |
| R3 | .generation_records.json 并发写入丢失 | 中 | 中 | 加 threading.Lock 或迁移 SQLite |
| R4 | app.py 未做 systemd, 进程异常不自动重启 | 高 | 中 | 创建 lotto-app.service systemd 单元 |
| R5 | 历史数据文件损坏后无法生成号码 | 低 | 高 | 定期备份 .xlsx; 增加自动重新抓取 |
| R6 | 爬虫被 55128.cn 限流或封 IP | 低 | 中 | 增加请求间隔; 备选数据源 |
| R7 | 生成大量号码时内存占用高 | 低 | 低 | 限制 max_tickets=1000; 分批生成 |
| R8 | 双 Flask 服务端口管理混乱 | 中 | 低 | 合并为单服务或统一 systemd 管理 |
---
## 八、改进建议
### 8.1 短期改进(1-2 周)
| 优先级 | 改进项 | 工作量 | 负责人 |
|--------|--------|--------|--------|
| P0 | 为 app.py 创建 systemd service | 0.5h | opengineer |
| P0 | app.py 加入 threading.Lock 保护 records 并发写入 | 1h | costcodev |
| P1 | 统一两个 Flask 服务为单服务(合并 web_executor 功能到 app.py | 3h | costcodev |
| P1 | 增加请求间隔到 fetch_data.py(避免被封) | 0.5h | costcodev |
| P2 | 增加移动端真机测试 | 2h | designer |
### 8.2 中期改进(1 月)
| 优先级 | 改进项 | 工作量 | 说明 |
|--------|--------|--------|------|
| P1 | 数据存储迁移 SQLite | 8h | 替代 Excel + JSON, 支持并发查询 |
| P2 | 数据可视化图表 | 4h | Chart.js 展示走势图/分布图 |
| P2 | 备选数据源 | 4h | 增加第二个数据源防单点故障 |
### 8.3 长期演进(本季度)
| 优先级 | 改进项 | 工作量 | 说明 |
|--------|--------|--------|------|
| P3 | 多彩种支持 | 16h | 大乐透、福彩3D 等 |
| P3 | 微信推送 | 8h | 生成结果推送到微信 |
| P3 | 用户权限系统 | 8h | 多用户管理 |
---
## 九、开发排期建议
### 9.1 当前状态评估
**系统已完成度**: ~90%
- ✅ 号码生成核心 (lottery.py) — 完成且功能丰富
- ✅ 数据抓取 (fetch_data.py + web_executor.py) — 完成且有 systemd 部署
- ✅ Web UI (app.py + index.html) — 完成且有响应式布局
- ✅ 定时任务 (Cron) — 完成且每日运行
- ⚠️ systemd 部署 — app.py 尚未配置 systemd
- ⚠️ 并发安全 — 记录文件无锁保护
### 9.2 交付计划
本系统主体代码已完成(由刘总本人编写),架构文档主要是对现有代码的规范化拆解和改进建议。后续工作量主要集中在:
| 阶段 | 工作内容 | 负责人 | 预估工时 |
|------|----------|--------|----------|
| 阶段1 | 创建 app.py systemd service | 严维序 | 0.5h |
| 阶段2 | records 并发写入加锁 | 徐聪 | 1h |
| 阶段3 | 功能测试 + 兼容性测试 | 苏绘锦 | 4h |
| 阶段4 | 刘总验收 | 刘总 | 待定 |
| **合计** | — | — | **~6h** |
---
## 十、编码规范与技术栈约束
### 10.1 Python 编码规范
| 规则 | 要求 |
|------|------|
| Python 版本 | 3.x (项目 venv 已有) |
| 类型注解 | 公开函数推荐标注参数和返回值类型 |
| 命名规范 | snake_case (函数/变量), PascalCase (类) |
| 常量 | UPPER_SNAKE_CASE |
| 错误处理 | 绝不 bare except; 明确捕获异常类型 |
| 日志 | 使用 print (当前模式) 或 logging 模块 |
| 文档字符串 | 公开函数推荐 docstring |
### 10.2 前端编码规范
| 规则 | 要求 |
|------|------|
| HTML | 语义化标签, viewport meta 必须设置 |
| CSS | CSS 变量管理主题色; 响应式断点 768px |
| JS | ES6+; 原生无框架; fetch API 调用后端 |
| 安全 | 所有用户输入需校验; innerHTML 谨慎使用 |
### 10.3 部署规范
| 规则 | 要求 |
|------|------|
| 进程管理 | systemd (Type=simple, Restart=on-failure) |
| 定时任务 | crontab (每日 02:30) |
| Python 环境 | venv 虚拟环境隔离 |
| 依赖管理 | requirements.txt (未创建, 建议补充) |
| 日志 | journalctl (systemd) + 文件日志 (Cron) |
| 监控 | systemctl status + curl /api/status |
---
## 十一、API 接口规范
### 11.1 接口清单(app.py :8085
| # | 路径 | 方法 | 认证 | 请求参数 | 响应格式 | 说明 |
|---|------|------|------|----------|----------|------|
| 1 | `/` | GET | 无 | 无 | HTML | 首页 (index.html) |
| 2 | `/api/generate` | POST | 可选 | `JSON: {num_tickets: int, strategy: str}` | `JSON: {success, data: {tickets, total, filename, download_url, record, statistics}}` | 生成号码 |
| 3 | `/api/history` | GET | 可选 | `?page=1&page_size=20&search=keyword` | `JSON: {success, data: {records, total, page, page_size}}` | 历史数据 |
| 4 | `/api/records` | GET | 可选 | `?page=1&page_size=20` | `JSON: {success, data: {records, total, page, page_size}}` | 生成记录列表 |
| 5 | `/api/records/:id` | DELETE | 可选 | URL 参数 id | `JSON: {success, message}` | 删除记录 |
| 6 | `/api/download/:path` | GET | 可选 | URL 参数 filepath | File (Excel) | 下载文件 |
| 7 | `/api/statistics` | GET | 可选 | 无 | `JSON: {success, data: {hot_reds, cold_reds, hot_blues, ...}}` | 统计数据 |
| 8 | `/api/status` | GET | 无 | 无 | `JSON: {success, data: {server_time, history_exists, ...}}` | 系统状态 |
| 9 | `/api/config` | GET | 无 | 无 | `JSON: {success, data: {max_tickets, default_tickets, auth_enabled}}` | 前端配置 |
### 11.2 接口清单(web_executor.py :5000
| # | 路径 | 方法 | 说明 |
|---|------|------|------|
| 1 | `/` | GET | 抓取控制台 (web_console.html) |
| 2 | `/api/status` | GET | 抓取执行状态 |
| 3 | `/api/execute` | POST | 触发数据抓取 |
### 11.3 错误响应格式
```json
{
"success": false,
"error": "错误描述信息"
}
```
**HTTP 状态码使用**:
- 200: 成功
- 400: 参数错误
- 401: 未授权(Token 认证开启时)
- 403: 禁止访问(目录遍历等)
- 404: 资源不存在
- 409: 冲突(重复执行)
- 500: 服务器内部错误
---
## 十二、部署架构
### 12.1 当前部署
```
┌─────────────────────────────────────────────────────┐
│ Ubuntu-OpenClaw (192.168.1.99) │
│ │
│ systemd services: │
│ ├── lotto-web.service → web_executor.py :5000 ✅ │
│ └── (lotto-app.service → app.py :8085) ⚠️ 未创建 │
│ │
│ Cron: │
│ └── 30 2 * * * fetch_daily.sh → fetch_data.py ✅ │
│ │
│ Python venv: /home/vincent/Studio/lottoData/venv/ │
│ │
│ 项目目录: /home/vincent/Studio/lottoData/ │
└─────────────────────────────────────────────────────┘
```
### 12.2 建议部署改进
```ini
# /etc/systemd/system/lotto-app.service
[Unit]
Description=双色球号码生成 Web 服务
After=network.target
[Service]
Type=simple
User=vincent
WorkingDirectory=/home/vincent/Studio/lottoData
ExecStart=/home/vincent/Studio/lottoData/venv/bin/python3 /home/vincent/Studio/lottoData/app.py
ExecStartPre=/home/vincent/Studio/lottoData/venv/bin/python3 -c "import flask; import pandas; import openpyxl; import numpy"
Restart=on-failure
RestartSec=5
KillMode=control-group
[Install]
WantedBy=multi-user.target
```
### 12.3 依赖清单(requirements.txt
```
flask>=3.0
pandas>=2.0
numpy>=1.24
openpyxl>=3.1
requests>=2.31
beautifulsoup4>=4.12
```
---
## 十三、产研评审项
### 需要评审确认的决策点
| # | 议题 | 当前方案 | 备选方案 | 我的建议 |
|---|------|----------|----------|----------|
| 1 | app.py 是否做 systemd | 未做 | 创建 lotto-app.service | ✅ 建议创建,确保自动重启 |
| 2 | 双 Flask 服务是否合并 | 分离(port 5000+8085) | 合并为单服务 | ✅ 建议合并,简化部署管理 |
| 3 | 数据存储是否迁移 SQLite | Excel+JSON | SQLite | ⚠️ 短期保持文件, 中期迁移 |
| 4 | 前端是否引入框架 | 原生 HTML/JS | Vue/Alpine.js | ✅ 保持原生, 功能不复杂 |
| 5 | API 认证是否启用 | 关闭 | 开启 Token 认证 | ⚠️ 内网可关闭, 外网需开启 |
---
## 十四、总结
### 架构评估结论
**✅ 系统可用,建议小幅改进后正式交付。**
**优势**:
- 代码已完整实现,功能覆盖全部 PRD 需求
- 号码生成算法丰富(热冷号策略 + 多维度调整)
- 前端响应式设计,PC + 移动端适配
- 部署简单(systemd + cron
**待改进**:
- app.py 需创建 systemd serviceP0
- records 文件并发写入需加锁(P0)
- 两个 Flask 服务建议合并(P1)
**风险评估**: 低风险。系统规模小,部署在内网,无生产级别可用性要求。
### 架构决策记录
#### ADR-007-01: 保持 Flask 框架,不迁移 FastAPI
- **Context**: 项目规模小,同步处理足够,团队熟悉 Flask
- **Decision**: 保持 Flask
- **Consequences**: 无异步性能优势,但足够满足需求
#### ADR-007-02: 保持文件存储,中期考虑 SQLite
- **Context**: 当前 Excel + JSON 满足数据量需求
- **Decision**: 短期保持文件存储;数据量 > 5000 条或并发写入问题时迁移 SQLite
- **Consequences**: 并发写入需加锁保护
#### ADR-007-03: 保持原生前端,不引入框架
- **Context**: 单页面 SPA,功能简单,无构建工具依赖
- **Decision**: 保持原生 HTML/CSS/JS
- **Consequences**: 复杂交互扩展能力有限,但满足当前需求
---
> **架构师**: 梁思筑
> **日期**: 2026-07-03
> **下一步**: 提交 Git → 发起产研评审 → 评审通过后通知胡蓉推进开发
@@ -1,214 +0,0 @@
# 开发文档:双色球 Web UI 系统
**版本**: v1.0
**开发人员**: 徐聪(costcodev
**日期**: 2026-07-03
**Issue**: BIZ-75
---
## 1. 项目概述
双色球自动化系统 Web UI,提供号码生成、历史数据查看、生成记录管理和统计分析功能。支持 PC 端和移动端响应式访问,监听 0.0.0.0:8085,局域网可访问。
## 2. 技术栈
| 层级 | 技术 | 说明 |
|------|------|------|
| 后端 | Python 3 + Flask | REST API 服务 |
| 前端 | 原生 HTML/CSS/JS | 单文件,响应式布局 |
| 数据分析 | Pandas + NumPy | 号码统计分析 |
| 数据存储 | Excel + JSON | 历史数据 + 生成记录 |
| 部署 | systemd / nohup | Linux 服务部署 |
## 3. 目录结构
```
lottoData/
├── app.py # Flask 主服务(统一入口)
├── index.html # 前端 UI(响应式,4 Tab 页面)
├── lottery.py # 号码生成核心逻辑
├── fetch_data.py # 历史数据抓取脚本
├── web_console.html # 数据抓取控制台前端
├── requirements.txt # Python 依赖
├── 双色球历史数据.xlsx # 历史数据文件
├── lottery/ # 号码生成结果输出目录
├── .generation_records.json # 生成记录索引(JSON
├── .fetch_status.json # 抓取状态文件
├── deploy/ # 部署相关文件
│ ├── DEPLOY.md # 部署说明
│ ├── lotto-app.service # systemd 服务文件
│ ├── fetch_daily.sh # 定时抓取脚本
│ └── backup.sh # 备份脚本
└── docs/ # 文档目录
├── PRD-双色球 WebUI-v1.0.md
└── 开发文档-双色球WebUI-v1.0.md ← 本文件
```
## 4. API 接口
### 4.1 接口清单
| 接口 | 方法 | 描述 | 认证 |
|------|------|------|------|
| `/api/generate` | POST | 生成号码 | 可选 |
| `/api/history` | GET | 获取历史开奖数据(分页+搜索) | 可选 |
| `/api/records` | GET | 获取生成记录列表(分页) | 可选 |
| `/api/records/:id` | DELETE | 删除生成记录 | 可选 |
| `/api/statistics` | GET | 获取统计分析数据 | 可选 |
| `/api/download/:filepath` | GET | 下载文件 | 可选 |
| `/api/status` | GET | 系统状态 | 无 |
| `/api/config` | GET | 前端配置 | 无 |
| `/api/fetch/status` | GET | 抓取执行状态 | 无 |
| `/api/fetch/execute` | POST | 触发数据抓取 | 无 |
### 4.2 关键接口参数
#### POST /api/generate
```json
// 请求
{
"num_tickets": 10,
"strategy": "advanced" // 或 "basic"
}
// 响应
{
"success": true,
"data": {
"tickets": [...],
"total": 10,
"filename": "lottery/xxx.xlsx",
"download_url": "/api/download/lottery/xxx.xlsx",
"record": {...},
"statistics": {...}
}
}
```
#### GET /api/history
参数: `page` (页码), `page_size` (每页条数), `search` (搜索关键词)
#### GET /api/records
参数: `page` (页码), `page_size` (每页条数)
## 5. 前端页面
### 5.1 页面结构
- **Header**: 标题 + 副标题
- **导航 Tab**: 号码生成 | 历史数据 | 生成记录 | 统计分析
- **移动端**: 底部固定导航栏
### 5.2 功能页面
#### 号码生成页(首页)
- 统计概览(历史期数、常见奇偶比、和值范围等)
- 策略选择(高级策略/基础策略)
- 注数输入(1-1000
- 生成结果展示(红球+蓝球+统计指标)
- Excel 下载按钮
#### 历史数据页
- 搜索框(500ms 防抖)
- 数据表格(期号、日期、红球、蓝球、统计字段)
- 分页控件
#### 生成记录页
- 记录列表(策略、注数、时间、文件大小)
- 下载/删除操作
- 分页控件
#### 统计分析页
- 历史开奖期数
- 红球热号 TOP15 / 冷号 TOP15
- 蓝球热号 TOP8
- 奇偶比/大小比/和值/跨度统计
## 6. 关键修复说明
### 6.1 数据格式兼容修复(核心 Bug 修复)
**问题**: `lottery.py` 期望 Excel 含"号码"列(拼接格式如 `08121821243001`),但 `fetch_data.py` 抓取的 Excel 使用分列格式("红球 1"~"红球 6"+"蓝球"),导致号码生成器无法加载历史数据。
**根因**: Excel 文件包含两行 header
- Row 0: 新格式列名(期号、开奖日期、红球 1~6、蓝球、特别号)
- Row 1: 旧格式列名(开奖时间、期数、号码、开机号、...)
- Row 2+: 实际数据
**修复方案**:
1. `lottery.py``load_history_data()`: 添加多格式检测逻辑,识别格式A(双行 header)并自动跳过,使用旧列名作为标准列名
2. `lottery.py``parse_numbers()`: 新增对拼接字符串格式(14位无分隔符)的直接解析,避免 `re.findall` 将整个字符串视为一个数字
3. `app.py``load_history_dataframe()`: 同步修复多格式兼容逻辑
### 6.2 线程安全
- 生成记录的读-改-写操作使用 `threading.Lock` 保护
- 文件写入使用临时文件+原子替换(`os.replace`),防止崩溃导致数据损坏
## 7. 部署方式
### 7.1 直接运行
```bash
cd /home/vincent/Studio/lottoData
source .venv/bin/activate
python3 app.py
# 访问 http://localhost:8085
```
### 7.2 systemd 服务
```bash
# 服务文件: deploy/lotto-app.service
sudo cp deploy/lotto-app.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable lotto-app
sudo systemctl start lotto-app
```
### 7.3 定时数据抓取
```bash
# 添加 cron 任务
crontab -e
# 每天 02:30 自动抓取最新数据
30 2 * * * /home/vincent/Studio/lottoData/deploy/fetch_daily.sh >> /home/vincent/Studio/lottoData/deploy/cron.log 2>&1
```
## 8. 测试验证
### 8.1 API 测试结果
| 接口 | 状态 | 说明 |
|------|------|------|
| GET /api/status | ✅ 通过 | 返回服务状态 |
| GET /api/statistics | ✅ 通过 | 120条历史数据统计正确 |
| GET /api/history | ✅ 通过 | 分页+红蓝球解析正确 |
| POST /api/generate | ✅ 通过 | 5注号码生成成功,含统计 |
| GET /api/records | ✅ 通过 | 生成记录列表正确 |
| GET / (前端页面) | ✅ 通过 | HTML 页面正常加载 |
### 8.2 数据格式验证
- 历史数据: 120 条记录全部成功解析 ✅
- 红球解析: 6个红球正确提取 ✅
- 蓝球解析: 1个蓝球正确提取 ✅
- 号码范围校验: 1-33(红) + 1-16(蓝) ✅
## 9. 已知限制
- 前端为单 HTML 文件,未使用构建工具
- 无用户登录系统(Token 认证为可选项,默认关闭)
- 历史数据来源为 55128.cn,如网站改版需更新 `fetch_data.py`
- 不支持 HTTPS(内网环境)
## 10. 后续优化建议
| 功能 | 优先级 | 说明 |
|------|--------|------|
| 数据可视化图表 | P2 | 走势图、分布图 |
| 用户登录系统 | P2 | 多用户权限管理 |
| 定时自动生成 | P2 | 定时生成+推送 |
| 微信推送 | P3 | 生成结果推送至微信 |
| 多彩种支持 | P3 | 大乐透、福彩 3D 等 |
---
**开发完成日期**: 2026-07-03
**代码仓库**: http://192.168.1.99:12299/vincent/Lottery.git
**开发人员**: 徐聪(costcodev