From 8171373812614d7db9d21d83197b98e87e7e0ddf Mon Sep 17 00:00:00 2001 From: bizwings Date: Fri, 3 Jul 2026 16:34:34 +0800 Subject: [PATCH] =?UTF-8?q?BIZ-74:=20=E5=8F=8C=E8=89=B2=E7=90=83=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=9E=B6=E6=9E=84=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= =?UTF-8?q?=20v1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完整文件级/函数级架构拆解 - 10个文件、40+函数的签名/输入/输出/边界条件 - 部署拓扑、数据流、数据模型 - 非功能需求(性能/安全/兼容性) - 8项风险评估 + 改进建议 - 5项产研评审决策点 - ADR-007 架构决策记录 架构师: 梁思筑 Issue: BIZ-74 Co-authored-by: multica-agent --- .../双色球系统架构设计文档-BIZ-74.md | 827 ++++++++++++++++++ 1 file changed, 827 insertions(+) create mode 100644 docs/architecture/双色球系统架构设计文档-BIZ-74.md diff --git a/docs/architecture/双色球系统架构设计文档-BIZ-74.md b/docs/architecture/双色球系统架构设计文档-BIZ-74.md new file mode 100644 index 0000000..9f345e6 --- /dev/null +++ b/docs/architecture/双色球系统架构设计文档-BIZ-74.md @@ -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/` | DELETE | record_id | JSON | 记录不存在→404; 同时删除文件 | +| `api_download` | `/api/download/` | 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 service(P0) +- 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 → 发起产研评审 → 评审通过后通知胡蓉推进开发 \ No newline at end of file