462adcc424
- 部署时间/版本/分支记录 - smoke 测试结果 + waiting 契约确认 - 已知后续验证项(2026-09-01 开奖后 e2e)
307 lines
11 KiB
Markdown
307 lines
11 KiB
Markdown
# 双色球系统部署文档
|
||
|
||
## 部署信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 项目名称 | 双色球自动化系统 |
|
||
| 部署时间 | 2026-07-04 |
|
||
| 开发人员 | 徐聪 (costcodev) |
|
||
| 部署人员 | 严维序 (opengineer) |
|
||
| 服务地址 | http://192.168.1.99:8085 |
|
||
| 代码仓库 | http://192.168.1.99:12299/vincent/Lottery.git |
|
||
| 宿主服务器 | Ubuntu-OpenClaw (192.168.1.99) |
|
||
|
||
---
|
||
|
||
## 一、项目结构
|
||
|
||
```
|
||
/home/vincent/Studio/lottoData/
|
||
├── venv/ # Python 虚拟环境
|
||
├── app.py # Flask 统一 Web 服务 (监听 0.0.0.0:8085)
|
||
├── index.html # 前端 UI (响应式,4 Tab 页面)
|
||
├── lottery.py # 双色球号码生成器核心逻辑
|
||
├── fetch_data.py # 历史数据抓取脚本
|
||
├── web_console.html # 数据抓取控制台前端页面
|
||
├── web_executor.py # [已废弃] 旧版独立抓取服务,功能已整合到 app.py
|
||
├── requirements.txt # Python 依赖清单
|
||
├── 双色球历史数据.xlsx # 历史数据文件 (不纳入 git)
|
||
├── lottery/ # 号码生成结果输出目录 (不纳入 git)
|
||
├── .generation_records.json # 生成记录索引 (不纳入 git)
|
||
├── .fetch_status.json # 抓取状态文件 (不纳入 git)
|
||
├── docs/ # 文档目录
|
||
│ ├── PRD-双色球 WebUI-v1.0.md
|
||
│ └── 开发文档-双色球WebUI-v1.0.md
|
||
└── deploy/ # 部署文件
|
||
├── DEPLOY.md # 本文档
|
||
├── lotto-app.service # systemd 服务文件 (统一入口)
|
||
├── fetch_daily.sh # 每日定时抓取脚本
|
||
├── backup.sh # 备份脚本 (30天保留)
|
||
├── cron.log # Cron 执行日志
|
||
└── fetch_YYYYMMDD.log # 每日抓取详细日志
|
||
```
|
||
|
||
**说明**: `app.py` 是统一入口,整合了号码生成、历史数据、生成记录、统计数据抓取等全部功能。`web_executor.py` 已废弃,不需独立部署。
|
||
|
||
---
|
||
|
||
## 二、依赖清单
|
||
|
||
| 包 | 用途 |
|
||
|----|------|
|
||
| Flask | Web 服务框架 |
|
||
| pandas | 数据处理 |
|
||
| openpyxl | Excel 读写 |
|
||
| numpy | 数值计算 |
|
||
| requests | HTTP 请求 (数据抓取) |
|
||
| beautifulsoup4 | HTML 解析 (数据抓取) |
|
||
|
||
安装命令:
|
||
```bash
|
||
python3 -m venv venv
|
||
./venv/bin/pip install -r requirements.txt
|
||
```
|
||
|
||
---
|
||
|
||
## 三、systemd 服务配置
|
||
|
||
### 服务文件
|
||
|
||
`/etc/systemd/system/lotto-app.service`
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=双色球号码生成 Web 服务 (app.py :8085)
|
||
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
|
||
Environment=PYTHONUNBUFFERED=1
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
### 管理命令
|
||
|
||
```bash
|
||
# 安装/启用
|
||
sudo cp deploy/lotto-app.service /etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable lotto-app
|
||
sudo systemctl start lotto-app
|
||
|
||
# 日常管理
|
||
sudo systemctl status lotto-app # 查看状态
|
||
sudo systemctl restart lotto-app # 重启
|
||
sudo systemctl stop lotto-app # 停止
|
||
sudo journalctl -u lotto-app -f # 查看实时日志
|
||
```
|
||
|
||
### 生产部署建议
|
||
|
||
建议使用 gunicorn 替代 Flask 内置服务器:
|
||
```bash
|
||
./venv/bin/pip install gunicorn
|
||
# 修改 ExecStart 为:
|
||
# /home/vincent/Studio/lottoData/venv/bin/gunicorn -w 4 -b 0.0.0.0:8085 app:app
|
||
```
|
||
|
||
---
|
||
|
||
## 四、Cron 定时任务
|
||
|
||
### Cron 配置
|
||
|
||
```
|
||
30 2 * * * /home/vincent/Studio/lottoData/deploy/fetch_daily.sh >> /home/vincent/Studio/lottoData/deploy/cron.log 2>&1
|
||
0 3 * * * /home/vincent/Studio/lottoData/deploy/backup.sh >> /home/vincent/Studio/lottoData/deploy/cron.log 2>&1
|
||
```
|
||
|
||
- 每天 02:30 自动抓取双色球历史数据
|
||
- 每天 03:00 自动备份数据(保留 30 天)
|
||
|
||
### 手动执行
|
||
|
||
```bash
|
||
/home/vincent/Studio/lottoData/deploy/fetch_daily.sh
|
||
# 或通过 Web 控制台触发: http://192.168.1.99:8085/fetch
|
||
```
|
||
|
||
---
|
||
|
||
## 五、Web 接口清单
|
||
|
||
| 路径 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/` | GET | 双色球 Web UI 首页(号码生成) |
|
||
| `/fetch` | GET | 数据抓取控制台 |
|
||
| `/api/generate` | POST | 生成号码(参数: num_tickets, strategy) |
|
||
| `/api/history` | GET | 获取历史开奖数据(参数: page, page_size, search) |
|
||
| `/api/records` | GET | 获取生成记录列表(参数: page, page_size) |
|
||
| `/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 | 触发数据抓取 |
|
||
|
||
### 示例
|
||
|
||
```bash
|
||
# 查看状态
|
||
curl http://192.168.1.99:8085/api/status
|
||
|
||
# 生成号码
|
||
curl -X POST http://192.168.1.99:8085/api/generate \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"num_tickets": 10, "strategy": "advanced"}'
|
||
|
||
# 触发抓取
|
||
curl -X POST http://192.168.1.99:8085/api/fetch/execute
|
||
```
|
||
|
||
---
|
||
|
||
## 六、迁移说明(从旧版升级)
|
||
|
||
如果之前部署了旧版 `lotto-web.service`(端口 5000):
|
||
|
||
```bash
|
||
# 1. 停止旧服务
|
||
sudo systemctl stop lotto-web
|
||
sudo systemctl disable lotto-web
|
||
sudo rm /etc/systemd/system/lotto-web.service
|
||
sudo systemctl daemon-reload
|
||
|
||
# 2. 部署新服务
|
||
sudo cp deploy/lotto-app.service /etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now lotto-app
|
||
|
||
# 3. 更新 cron(指向新的 fetch_daily.sh)
|
||
crontab -l | sed 's|web_executor|app|g' | crontab -
|
||
# 或手动编辑: crontab -e
|
||
|
||
# 4. 验证
|
||
curl http://127.0.0.1:8085/api/status
|
||
```
|
||
|
||
---
|
||
|
||
## 七、验证清单
|
||
|
||
- [ ] 依赖安装完整 (Flask, pandas, openpyxl, numpy, requests, beautifulsoup4)
|
||
- [ ] systemd 服务运行正常 (active, enabled)
|
||
- [ ] Web 服务可访问 (http://192.168.1.99:8085, HTTP 200)
|
||
- [ ] API 接口正常 (/api/status, /api/generate, /api/history 等)
|
||
- [ ] 前端页面正常 (4 Tab: 号码生成、历史数据、生成记录、统计分析)
|
||
- [ ] 移动端响应式布局正常
|
||
- [ ] Cron 定时任务已配置 (每日 2:30 抓取, 3:00 备份)
|
||
- [ ] 旧版 lotto-web.service 已停止并移除
|
||
- [ ] 开机自启已配置 (systemd enable)
|
||
|
||
---
|
||
|
||
## 八、监控要点
|
||
|
||
1. **服务存活**:`systemctl status lotto-app` 确认 active
|
||
2. **Web 可达**:`curl http://127.0.0.1:8085/api/status`
|
||
3. **数据更新**:检查 `双色球历史数据.xlsx` 修改时间
|
||
4. **Cron 日志**:检查 `deploy/cron.log`
|
||
5. **磁盘空间**:Excel 文件约 13KB,定期检查 `lottery/` 目录增长
|
||
|
||
---
|
||
|
||
> 部署人:严维序 (opengineer) | 2026-07-04
|
||
|
||
---
|
||
|
||
## BIZ-105 部署记录 — 2026-08-31
|
||
|
||
| 项 | 值 |
|
||
|---|---|
|
||
| 部署时间 | 2026-08-31 05:30 (Asia/Shanghai) |
|
||
| 部署人员 | 严维序 (opengineer) |
|
||
| 任务来源 | COO 陆怀瑾 sessions_send(父 BIZ-106 子任务3) |
|
||
| 代码版本 | commit 5d459b450525063ecdbbfb5694aff30aba5fb0bf |
|
||
| 分支 | biz-105-impl-prize-display |
|
||
| PRD | BIZ-102 v1.1 + 中奖金额展示 v1.2 §4.3 |
|
||
| 改动范围 | app.py +110 / index.html +89 / 新增 tests/test_compare_prize.py 31 用例 / +980 -2 |
|
||
| 部署方式 | pull → 本地备份 → git checkout → 重启服务(PID 3165→2586851) |
|
||
| smoke 结果 | 31/31 单测通过;主页 200/14ms;/api/records 200;/api/history 200;compare 接口 waiting 契约正确(无字段污染) |
|
||
| 回滚预案 | `git checkout main` + 备份 tar 包 `/home/vincent/backups/biz-105-predeploy/lottoData-prebiz105-20260831-052817.tar.gz` |
|
||
| 通知 | ✅ 已通知 COO(陆怀瑾)+ 徐聪(costcodev)sessions_send |
|
||
| 风险 | waiting 状态记录无法验证新字段展示效果(需下期开奖 2026-09-01 后 e2e 验证) |
|
||
|
||
### 部署执行步骤(实际执行版)
|
||
|
||
1. **备份**:tar 打包 app.py/index.html/lottery.py/... → `/home/vincent/backups/biz-105-predeploy/`
|
||
2. **stash 本地修改**:`git stash push -m "biz-105-predeploy-local-changes-20260831"`
|
||
3. **checkout 分支**:`git checkout biz-105-impl-prize-display`(HEAD = 5d459b4)
|
||
4. **停止服务**:`kill 3165`(SIGTERM 优雅停止,3s 内)
|
||
5. **启动新版本**:`nohup ./.venv/bin/python3 ./app.py > /tmp/lotto-biz105-YYYYMMDD-HHMMSS.log 2>&1 &`
|
||
6. **smoke 验证**:31 单测 + HTTP 200 三接口 + waiting 契约
|
||
7. **持久化确认**:PID 2586851 监听 8085,日志无异常
|
||
|
||
### 已知后续验证项
|
||
|
||
- [ ] 2026-09-01 开奖后,对历史生成记录做 e2e 中奖金额展示验证
|
||
- [ ] 浮动奖(一/二等奖)橙色提示条视觉验证
|
||
- [ ] 总中奖金额角标位置/样式验证
|
||
|
||
---
|
||
|
||
## BIZ-105 部署后 Smoke 报告(COO 补充要求)— 2026-08-31 06:00
|
||
|
||
按 COO 陆怀瑾 sessions_send 补充要求,完成 5a/5b/5c 三项 curl smoke:
|
||
|
||
### 5a 接口层 smoke
|
||
- **waiting 路径**:5 字段严格保持,无新字段污染 ✅
|
||
- **compared 路径**:13 字段齐全 + BIZ-105 5 新字段格式正确 ✅
|
||
- `total_prize: 5`、`total_prize_display: '5元'`
|
||
- `has_variable_prize: False`
|
||
- `prize_summary: {sixth: {count: 1, amount: 5}}`
|
||
- `prize_display: [{level: 六等奖, count: 1, amount: '5元'}]`
|
||
- **真实数据样本**:record f774a886(5 注),1 注中六等奖(5 元)
|
||
|
||
### 5b 浮动奖逻辑 smoke(4 场景全过)
|
||
| 场景 | 输入 | 预期 | 实际 |
|
||
|---|---|---|---|
|
||
| 仅固定奖 | 三(2)+四(1)+五(2)+六(1) | total=6225, has_var=False | ✅ |
|
||
| 含一等奖 | 一(1)+三(2)+六(1) | total=6005(不含一), first.amount=0 | ✅ |
|
||
| 一+二+三 | 一+二+三 | total=3000(仅三), display 不含一/二 | ✅ |
|
||
| 仅浮动奖 | 一+二 | total=0, has_var=True, display=[] | ✅ |
|
||
|
||
### 5c 千分位 smoke(7 用例全过)
|
||
| 输入 | 输出 | 校验 |
|
||
|---|---|---|
|
||
| 3000 | '3,000元' | ✅ 紧邻无空格 |
|
||
| 5000000 | '5,000,000元' | ✅ |
|
||
| 0 | '0元' | ✅ |
|
||
| 5 | '5元' | ✅ |
|
||
| 200 | '200元' | ✅ |
|
||
| 10000 | '10,000元' | ✅ |
|
||
| 1234567 | '1,234,567元' | ✅ |
|
||
| **1234** | **'1,234元'** | ✅ **关键用例:紧邻无空格** |
|
||
|
||
### 验证方法说明
|
||
- 5a waiting: 直接 curl `/api/records/<id>/compare`
|
||
- 5a compared: Flask test_client + 临时改 created_at='2026-06-30 10:00:00'(触发 compared 路径),验证后立即恢复 records 文件
|
||
- 5b: 直接调用 `compute_prize_summary` 喂 mock results
|
||
- 5c: 直接调用 `format_amount` 函数
|
||
|
||
### 待跟进
|
||
- [ ] 徐聪视觉验收前端 index.html(橙色浮动奖角标 + badge + 汇总表样式)
|
||
- [ ] 2026-09-01 开奖后真实数据 e2e(含真实中奖金额展示)
|