fix: 修正定时任务状态下次更新时间的显示逻辑,分别精确到早上6点和下午5点

This commit is contained in:
Mars 2025-06-26 15:02:03 +08:00
parent ff9d9a857e
commit cb46b20d15
9 changed files with 797 additions and 99 deletions

237
README.md
View File

@ -13,6 +13,7 @@
- Scikit-learn (机器学习) - Scikit-learn (机器学习)
- NumPy (数值计算) - NumPy (数值计算)
- Pandas (数据处理) - Pandas (数据处理)
- Schedule (定时任务)
### 前端 ### 前端
- Vue 3 - Vue 3
@ -30,6 +31,7 @@
- 数据导出 - 数据导出
- 手动数据录入 - 手动数据录入
- **自动数据更新** ⭐ 新功能 - **自动数据更新** ⭐ 新功能
- **定时任务系统** ⭐ 新功能
### 2. 基础统计分析 ### 2. 基础统计分析
- 号码出现频率统计 - 号码出现频率统计
@ -52,6 +54,7 @@
- **集成预测**: 多方法综合预测 - **集成预测**: 多方法综合预测
- **预测模型训练**: 可自定义训练参数 - **预测模型训练**: 可自定义训练参数
- **预测结果评估**: 预测准确率统计 - **预测结果评估**: 预测准确率统计
- **每日拼盘推荐**: 定时生成推荐号码 ⭐ 新功能
### 5. 智能选号 ### 5. 智能选号
- 随机选号 - 随机选号
@ -60,6 +63,12 @@
- 冷门号码选号 - 冷门号码选号
- 自定义选号策略 - 自定义选号策略
### 6. 定时任务系统 ⭐ 新功能
- **每天早上6点**: 自动更新开奖数据
- **每天下午5点**: 自动生成拼盘推荐
- **任务状态监控**: 实时查看任务执行状态
- **日志记录**: 详细的任务执行日志
## 项目结构 ## 项目结构
``` ```
lottery/ lottery/
@ -76,7 +85,9 @@ lottery/
│ │ ├── advanced_analysis.py # 高级分析服务 ⭐ │ │ ├── advanced_analysis.py # 高级分析服务 ⭐
│ │ └── prediction_service.py # 预测服务 ⭐ │ │ └── prediction_service.py # 预测服务 ⭐
│ ├── requirements.txt # 依赖包 │ ├── requirements.txt # 依赖包
│ └── main.py # 入口文件 │ ├── main.py # 入口文件
│ ├── scheduler.py # 定时任务调度器 ⭐
│ └── update_lottery.py # 数据更新脚本
├── frontend/ # 前端代码 ├── frontend/ # 前端代码
│ ├── src/ │ ├── src/
│ │ ├── api/ # API 接口 │ │ ├── api/ # API 接口
@ -85,6 +96,7 @@ lottery/
│ │ ├── router/ # 路由配置 │ │ ├── router/ # 路由配置
│ │ ├── views/ # 页面 │ │ ├── views/ # 页面
│ │ │ ├── AdvancedAnalysis.vue # 高级分析页面 ⭐ │ │ │ ├── AdvancedAnalysis.vue # 高级分析页面 ⭐
│ │ │ ├── NumberGenerator.vue # 一键下单页面 ⭐
│ │ │ └── Prediction.vue # 预测页面 ⭐ │ │ │ └── Prediction.vue # 预测页面 ⭐
│ │ ├── App.vue # 根组件 │ │ ├── App.vue # 根组件
│ │ └── main.js # 入口文件 │ │ └── main.js # 入口文件
@ -110,8 +122,11 @@ python start_system.py
脚本会自动: 脚本会自动:
- 检查Python版本和依赖 - 检查Python版本和依赖
- 检查数据库连接
- 询问是否启动定时任务
- 启动后端服务端口8000 - 启动后端服务端口8000
- 启动前端服务端口5173 - 启动前端服务端口5173
- 启动定时任务调度器(可选)
- 显示访问地址和使用说明 - 显示访问地址和使用说明
### 手动启动 ### 手动启动
@ -156,8 +171,46 @@ npm run dev
npm run build npm run build
``` ```
#### 定时任务 ⭐ 新功能
```bash
cd backend
python scheduler.py
```
## 使用说明 ## 使用说明
### 定时任务系统 ⭐ 新功能
#### 自动任务
系统提供两个定时任务:
1. **每天早上6点 - 数据更新任务**
- 自动从API获取最新开奖数据
- 更新双色球和大乐透数据
- 检查历史投注的中奖情况
- 记录详细执行日志
2. **每天下午5点 - 拼盘推荐任务**
- 自动生成今日拼盘推荐
- 使用集成预测算法
- 生成4注推荐号码
- 保存到数据库供前端显示
#### 手动启动定时任务
```bash
cd backend
python scheduler.py
```
#### 查看任务状态
- 在一键下单页面查看定时任务状态
- 显示下次执行时间
- 显示今日推荐生成状态
#### 任务日志
- 日志文件:`backend/lottery_scheduler.log`
- 包含详细的执行记录和错误信息
### 数据导入与更新说明 ### 数据导入与更新说明
#### 数据导入 #### 数据导入
@ -167,7 +220,8 @@ npm run build
- 支持 pandas DataFrame 或 JSON 文件导入。 - 支持 pandas DataFrame 或 JSON 文件导入。
#### 数据自动/手动更新 ⭐ 新功能 #### 数据自动/手动更新 ⭐ 新功能
- **前端更新功能**: 在首页提供"更新双色球"、"更新大乐透"、"更新全部"三个按钮 - **定时更新**: 每天早上6点自动更新推荐
- **前端更新功能**: 在首页提供"补红蓝球煎饼"、"补大乐斗豆浆"、"补全场早点"三个按钮
- **API更新接口**: 支持通过API接口更新数据 - **API更新接口**: 支持通过API接口更新数据
- `POST /api/v1/lottery/update/ssq` - 更新双色球数据 - `POST /api/v1/lottery/update/ssq` - 更新双色球数据
- `POST /api/v1/lottery/update/dlt` - 更新大乐透数据 - `POST /api/v1/lottery/update/dlt` - 更新大乐透数据
@ -188,9 +242,27 @@ python update_lottery.py
##### 自动定时更新 ##### 自动定时更新
```bash ```bash
cd backend cd backend
python schedule_update.py python scheduler.py
``` ```
系统会在每天凌晨2点自动检查并更新数据。 系统会在每天凌晨6点自动检查并更新数据。
### 拼盘推荐系统 ⭐ 新功能
#### 自动推荐
- **定时生成**: 每天下午5点自动生成
- **智能算法**: 使用集成预测算法
- **多注推荐**: 每次生成4注推荐号码
- **类型轮换**: 根据日期自动选择彩票类型
#### 手动生成
- 在一键下单页面点击"来一份拼盘"按钮
- 实时生成推荐号码
- 支持复制所有号码
#### 推荐历史
- 查看历史推荐记录
- 显示中奖情况
- 支持手动检查中奖
### 高级分析功能 ⭐ ### 高级分析功能 ⭐
1. 进入"高级分析"页面 1. 进入"高级分析"页面
@ -199,115 +271,92 @@ python schedule_update.py
- **遗漏值分析**: 查看各号码的遗漏期数 - **遗漏值分析**: 查看各号码的遗漏期数
- **和值分析**: 分析红球和值的分布规律 - **和值分析**: 分析红球和值的分布规律
- **AC值分析**: 邻号差值分析 - **AC值分析**: 邻号差值分析
- **质合比分析**: 质数与合数比例 - **质合比分析**: 质数与合数的比例分析
- **012路分析**: 除3余数分布 - **012路分析**: 除3余数分析
- **跨度分析**: 号码跨度统计 - **跨度分析**: 最大最小号码差值分析
- **综合分析**: 多维度整合分析 - **综合分析**: 多维度数据整合分析
4. 设置分析期数10-500期
5. 点击"分析"按钮查看结果
### 智能预测功能 ⭐ ### 智能预测功能 ⭐
1. 进入"智能预测"页面 1. 进入"智能预测"页面
2. 选择彩票类型 2. 选择彩票类型
3. 设置训练期数建议100-500期 3. 选择预测方法:
4. 点击"训练模型"按钮 - **机器学习预测**: 基于历史数据的AI预测
5. 训练完成后,可选择以下预测方法:
- **机器学习预测**: 基于AI算法的预测
- **模式预测**: 基于统计模式的预测 - **模式预测**: 基于统计模式的预测
- **集成预测**: 多方法综合预测 - **集成预测**: 多方法综合预测
6. 查看预测结果和置信度 4. 设置训练参数
5. 查看预测结果
### 数据查询 ## API文档
1. 在查询表单中输入查询条件 启动后端服务后,访问 http://localhost:8000/docs 查看完整的API文档。
2. 点击"查询"按钮
3. 查看查询结果
### 统计分析
1. 进入统计分析页面
2. 选择要分析的彩票类型
3. 查看统计图表
### 智能选号
1. 进入智能选号页面
2. 选择彩票类型和选号策略
3. 设置生成注数
4. 点击"生成号码"按钮
## API 文档
启动后端服务后,访问以下地址查看 API 文档:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
### 新增API端点 ⭐
#### 高级分析API
- `GET /api/v1/advanced-analysis/missing-value/{lottery_type}` - 遗漏值分析
- `GET /api/v1/advanced-analysis/sum-value/{lottery_type}` - 和值分析
- `GET /api/v1/advanced-analysis/ac-value/{lottery_type}` - AC值分析
- `GET /api/v1/advanced-analysis/prime-composite/{lottery_type}` - 质合比分析
- `GET /api/v1/advanced-analysis/road-012/{lottery_type}` - 012路分析
- `GET /api/v1/advanced-analysis/span/{lottery_type}` - 跨度分析
- `GET /api/v1/advanced-analysis/comprehensive/{lottery_type}` - 综合分析
#### 预测API
- `POST /api/v1/prediction/train/{lottery_type}` - 训练预测模型
- `GET /api/v1/prediction/predict/{lottery_type}` - 机器学习预测
- `GET /api/v1/prediction/pattern/{lottery_type}` - 模式预测
- `GET /api/v1/prediction/ensemble/{lottery_type}` - 集成预测
## 常见问题 ## 常见问题
1. 数据库连接失败
- 检查数据库服务是否启动
- 验证数据库连接配置是否正确
2. 前端无法连接后端 ### 定时任务相关
- 确认后端服务是否正常运行 **Q: 定时任务没有执行?**
- 检查前端环境配置中的 API 地址是否正确 A: 检查以下几点:
1. 确保定时任务进程正在运行
2. 检查日志文件 `backend/lottery_scheduler.log`
3. 确认系统时间正确
4. 检查数据库连接是否正常
3. 数据导入失败 **Q: 如何修改定时任务时间?**
- 检查 JSON 文件格式是否正确 A: 编辑 `backend/scheduler.py` 文件中的时间设置:
- 确认数据库表结构是否完整 ```python
schedule.every().day.at("06:00").do(self.update_lottery_data_job)
schedule.every().day.at("17:00").do(self.generate_daily_recommendations_job)
```
4. 机器学习预测失败 **Q: 如何查看任务执行日志?**
- 确保历史数据充足建议至少100期 A: 查看日志文件:
- 检查训练参数设置是否合理 ```bash
tail -f backend/lottery_scheduler.log
```
## 开发计划 ### 数据更新相关
- [x] 添加高级数据分析功能 **Q: 数据更新失败?**
- [x] 实现机器学习预测系统 A: 检查以下几点:
- [x] 优化数据可视化展示 1. 网络连接是否正常
- [ ] 添加用户认证功能 2. API密钥是否有效
- [ ] 实现数据备份和恢复 3. 数据库连接是否正常
- [ ] 优化数据导入性能 4. 查看后端日志获取详细错误信息
- [ ] 添加更多统计分析功能
- [ ] 实现自定义选号策略
- [ ] 添加移动端适配
- [ ] 实现数据导出功能增强
## 贡献指南 **Q: 如何手动更新数据?**
1. Fork 项目 A: 可以通过以下方式:
2. 创建特性分支 1. 前端页面点击更新按钮
3. 提交更改 2. 调用API接口
4. 推送到分支 3. 运行更新脚本:`python update_lottery.py`
5. 创建 Pull Request
## 许可证 ### 拼盘推荐相关
MIT License **Q: 今日推荐没有生成?**
A: 检查以下几点:
1. 定时任务是否正常运行
2. 是否有足够的历史数据
3. 预测模型是否训练完成
4. 查看日志文件获取错误信息
**Q: 如何手动生成推荐?**
A: 在一键下单页面点击"来一份拼盘"按钮即可手动生成。
## 更新日志 ## 更新日志
### v2.0.0 (2024-01-XX) ### v1.1.0 (2024-01-XX)
- ✨ 新增高级数据分析功能 - ✨ 新增定时任务系统
- ✨ 新增机器学习预测系统 - ✨ 新增每日拼盘推荐功能
- ✨ 新增遗漏值、和值、AC值等分析 - ✨ 新增任务状态监控
- ✨ 新增质合比、012路、跨度分析 - ✨ 优化一键下单页面
- ✨ 优化数据可视化展示 - 🐛 修复图表显示问题
- 🔧 更新依赖包版本 - 📝 更新使用说明
- 📝 完善API文档
### v1.0.0 (2024-01-XX) ### v1.0.0 (2024-01-XX)
- 🎉 初始版本发布 - 🎉 初始版本发布
- ✨ 基础数据管理功能 - ✨ 基础数据管理功能
- ✨ 基础统计分析 - ✨ 统计分析功能
- ✨ 智能选号功能 - ✨ 高级分析功能
- ✨ 前后端分离架构 - ✨ 智能预测功能
- ✨ 前端可视化界面
## 贡献指南
欢迎提交Issue和Pull Request来改进这个项目。
## 许可证
MIT License

View File

@ -212,3 +212,59 @@ def recommend_today(db: Session = Depends(get_db)):
'batch_id': batch_id, 'batch_id': batch_id,
'recommend': results 'recommend': results
} }
@router.get("/recommend/today")
def get_today_recommendations(db: Session = Depends(get_db)):
"""获取今日拼盘推荐"""
try:
today = datetime.datetime.now().strftime('%Y%m%d')
# 查询今日的双色球推荐
ssq_recommendations = db.query(SSQLotteryBetRecord).filter(
SSQLotteryBetRecord.batch_id.like(f'SSQ_{today}%')
).order_by(SSQLotteryBetRecord.created_at.desc()).all()
# 查询今日的大乐透推荐
dlt_recommendations = db.query(DLTLotteryBetRecord).filter(
DLTLotteryBetRecord.batch_id.like(f'DLT_{today}%')
).order_by(DLTLotteryBetRecord.created_at.desc()).all()
# 确定当前彩票类型(根据推荐数量)
if len(ssq_recommendations) > len(dlt_recommendations):
current_lottery_type = 'ssq'
today_recommendations = ssq_recommendations
else:
current_lottery_type = 'dlt'
today_recommendations = dlt_recommendations
# 格式化推荐数据
formatted_recommendations = []
for rec in today_recommendations:
numbers_parts = rec.numbers.split('|')
red_numbers = [int(n) for n in numbers_parts[0].split(
',')] if numbers_parts[0] else []
blue_numbers = [int(n) for n in numbers_parts[1].split(',')] if len(
numbers_parts) > 1 and numbers_parts[1] else []
formatted_recommendations.append({
'id': rec.id,
'batch_id': rec.batch_id,
'recommend_type': rec.recommend_type,
'numbers': rec.numbers,
'redNumbers': red_numbers,
'blueNumbers': blue_numbers,
'created_at': rec.created_at,
'is_winner': rec.is_winner,
'win_amount': rec.win_amount
})
return {
'success': True,
'lottery_type': current_lottery_type,
'recommend': formatted_recommendations,
'count': len(formatted_recommendations)
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"获取今日推荐失败: {str(e)}")

View File

@ -7,6 +7,7 @@ from sklearn.model_selection import train_test_split
from collections import defaultdict from collections import defaultdict
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from ..models.lottery import SSQLottery, DLTLottery from ..models.lottery import SSQLottery, DLTLottery
from datetime import datetime
class PredictionService: class PredictionService:
@ -545,3 +546,87 @@ class PredictionService:
"pattern_analysis": pattern_result.get('suggested_criteria', {}) if pattern_result else {}, "pattern_analysis": pattern_result.get('suggested_criteria', {}) if pattern_result else {},
"frequency_analysis": freq_result or {} "frequency_analysis": freq_result or {}
} }
def generate_daily_recommendations(self, db: Session, lottery_type: str, batch_size: int = 4) -> List[Dict]:
"""生成每日拼盘推荐(用于定时任务)
Args:
db: 数据库会话
lottery_type: 彩票类型 ('ssq' 'dlt')
batch_size: 生成推荐数量
Returns:
List[Dict]: 推荐列表
"""
try:
# 创建预测服务实例
prediction_service = PredictionService(db)
# 获取集成预测
ensemble_result = prediction_service.get_ensemble_prediction(
lottery_type, periods=100)
if not ensemble_result.get('success'):
return []
recommendations = ensemble_result.get('recommendations', [])
# 生成拼盘推荐
daily_recommendations = []
batch_id = f"{lottery_type.upper()}_{datetime.now().strftime('%Y%m%d')}"
for i, rec in enumerate(recommendations[:batch_size]):
# 格式化号码
if lottery_type == 'ssq':
red_numbers = rec['numbers']
blue_numbers = [rec['blue']] if rec['blue'] else []
numbers_str = f"{','.join(map(str, red_numbers))}|{','.join(map(str, blue_numbers))}"
else:
red_numbers = rec['numbers']
blue_numbers = rec.get('blues', [])
numbers_str = f"{','.join(map(str, red_numbers))}|{','.join(map(str, blue_numbers))}"
# 创建推荐记录
recommendation = {
'lottery_type': lottery_type,
'batch_id': batch_id,
'recommend_type': rec['method'],
'numbers': numbers_str,
'red_numbers': red_numbers,
'blue_numbers': blue_numbers,
'confidence': rec['confidence'],
'created_at': datetime.now(),
'is_winner': False,
'win_amount': 0
}
daily_recommendations.append(recommendation)
# 保存到数据库
from ..models.lottery import SSQLotteryBetRecord, DLTLotteryBetRecord
if lottery_type == 'ssq':
BetRecord = SSQLotteryBetRecord
else:
BetRecord = DLTLotteryBetRecord
# 保存推荐记录
for rec in daily_recommendations:
bet_record = BetRecord(
lottery_type=rec['lottery_type'],
batch_id=rec['batch_id'],
recommend_type=rec['recommend_type'],
numbers=rec['numbers'],
created_at=rec['created_at'],
is_winner=rec['is_winner'],
win_amount=rec['win_amount']
)
db.add(bet_record)
db.commit()
return daily_recommendations
except Exception as e:
db.rollback()
raise Exception(f"生成每日推荐失败: {str(e)}")

View File

@ -1,6 +1,6 @@
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from app.api.endpoints import analysis from app.api.endpoints import analysis, lottery, prediction
app = FastAPI( app = FastAPI(
title="彩票数据分析系统", title="彩票数据分析系统",
@ -19,8 +19,16 @@ app.add_middleware(
# 注册路由 # 注册路由
app.include_router(analysis.router, prefix="/api/analysis", tags=["analysis"]) app.include_router(analysis.router, prefix="/api/analysis", tags=["analysis"])
app.include_router(lottery.router, prefix="/api/v1/lottery", tags=["lottery"])
app.include_router(prediction.router,
prefix="/api/v1/prediction", tags=["prediction"])
@app.get("/") @app.get("/")
def read_root(): def read_root():
return {"message": "欢迎使用彩票数据分析系统"} return {"message": "欢迎使用彩票数据分析系统"}
@app.get("/health")
def health_check():
return {"status": "healthy", "message": "系统运行正常"}

113
backend/scheduler.py Normal file
View File

@ -0,0 +1,113 @@
import schedule
import time
import logging
from datetime import datetime
from update_lottery import LotteryUpdater
from app.services.prediction_service import PredictionService
from app.core.database import SessionLocal
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='lottery_scheduler.log'
)
logger = logging.getLogger(__name__)
class LotteryScheduler:
def __init__(self):
self.updater = LotteryUpdater()
def update_lottery_data_job(self):
"""每天早上6点更新开奖数据任务"""
try:
logger.info("开始执行早上6点更新开奖数据任务...")
# 更新双色球数据
logger.info("开始更新双色球数据...")
ssq_result = self.updater.update_lottery_data('ssq')
logger.info(f"双色球数据更新完成: {ssq_result}")
# 更新大乐透数据
logger.info("开始更新大乐透数据...")
dlt_result = self.updater.update_lottery_data('dlt')
logger.info(f"大乐透数据更新完成: {dlt_result}")
# 检查中奖情况
db = SessionLocal()
try:
logger.info("开始检查双色球中奖情况...")
self.updater.check_and_update_bet_wins('ssq', db)
logger.info("双色球中奖检查完成")
logger.info("开始检查大乐透中奖情况...")
self.updater.check_and_update_bet_wins('dlt', db)
logger.info("大乐透中奖检查完成")
finally:
db.close()
logger.info("早上6点更新开奖数据任务完成")
except Exception as e:
logger.error(f"早上6点更新开奖数据任务失败: {str(e)}")
def generate_daily_recommendations_job(self):
"""每天下午5点生成拼盘推荐任务"""
try:
logger.info("开始执行下午5点生成拼盘推荐任务...")
db = SessionLocal()
try:
# 创建预测服务实例
prediction_service = PredictionService(db)
# 生成双色球推荐
logger.info("开始生成双色球拼盘推荐...")
ssq_recommendations = prediction_service.generate_daily_recommendations(
db, 'ssq', batch_size=4
)
logger.info(f"双色球拼盘推荐生成完成,共生成 {len(ssq_recommendations)}")
# 生成大乐透推荐
logger.info("开始生成大乐透拼盘推荐...")
dlt_recommendations = prediction_service.generate_daily_recommendations(
db, 'dlt', batch_size=4
)
logger.info(f"大乐透拼盘推荐生成完成,共生成 {len(dlt_recommendations)}")
logger.info("下午5点生成拼盘推荐任务完成")
finally:
db.close()
except Exception as e:
logger.error(f"下午5点生成拼盘推荐任务失败: {str(e)}")
def start_scheduler(self):
"""启动定时任务调度器"""
# 每天早上6点更新开奖数据
schedule.every().day.at("06:00").do(self.update_lottery_data_job)
# 每天下午5点生成拼盘推荐
schedule.every().day.at("17:00").do(self.generate_daily_recommendations_job)
logger.info("定时任务调度器已启动")
logger.info("已设置任务:")
logger.info("- 每天早上6点: 更新开奖数据")
logger.info("- 每天下午5点: 生成拼盘推荐")
# 运行调度器
while True:
schedule.run_pending()
time.sleep(60) # 每分钟检查一次
def main():
"""主函数"""
scheduler = LotteryScheduler()
scheduler.start_scheduler()
if __name__ == "__main__":
main()

View File

@ -43,3 +43,11 @@ export function recommendToday() {
method: 'post' method: 'post'
}) })
} }
// 获取今日拼盘推荐
export function getTodayRecommendations() {
return request({
url: '/api/v1/prediction/recommend/today',
method: 'get'
})
}

View File

@ -5,6 +5,43 @@
<p class="subtitle">今日拼盘新鲜出锅懒人专属AI大厨为你配号</p> <p class="subtitle">今日拼盘新鲜出锅懒人专属AI大厨为你配号</p>
</div> </div>
<!-- 定时任务状态区域 -->
<div class="scheduler-status-section">
<div class="status-card">
<div class="status-header">
<h3>定时任务状态</h3>
<el-button type="primary" size="small" @click="updateSchedulerStatus">刷新状态</el-button>
</div>
<div class="status-content">
<div class="status-item">
<span class="status-label">数据更新任务</span>
<span class="status-value">
<el-tag type="success" size="small">每天早上6点</el-tag>
</span>
<span class="status-time">
下次更新{{ schedulerStatus.nextUpdate ? new Date(schedulerStatus.nextUpdate).toLocaleString() : '未知' }}
</span>
</div>
<div class="status-item">
<span class="status-label">拼盘推荐任务</span>
<span class="status-value">
<el-tag type="warning" size="small">每天下午5点</el-tag>
</span>
<span class="status-time">
下次推荐{{ schedulerStatus.nextRecommend ? new Date(schedulerStatus.nextRecommend).toLocaleString() : '未知' }}
</span>
</div>
<div class="status-item">
<span class="status-label">今日推荐状态</span>
<span class="status-value">
<el-tag v-if="todayRecommend.length > 0" type="success" size="small">已生成 {{ todayRecommend.length }} </el-tag>
<el-tag v-else type="info" size="small">等待生成</el-tag>
</span>
</div>
</div>
</div>
</div>
<!-- 一键下单区域 --> <!-- 一键下单区域 -->
<div class="quick-order-section"> <div class="quick-order-section">
<div class="order-card"> <div class="order-card">
@ -127,7 +164,7 @@
</template> </template>
<script> <script>
import { recommendToday } from '@/api/prediction' import { recommendToday, getTodayRecommendations } from '@/api/prediction'
import { lotteryApi } from '@/api/lottery' import { lotteryApi } from '@/api/lottery'
export default { export default {
@ -140,7 +177,11 @@ export default {
todayRecommend: [], todayRecommend: [],
ssqHistory: [], ssqHistory: [],
dltHistory: [], dltHistory: [],
checkingWin: false checkingWin: false,
schedulerStatus: {
nextUpdate: null,
nextRecommend: null
}
} }
}, },
computed: { computed: {
@ -161,9 +202,56 @@ export default {
}, },
mounted() { mounted() {
this.loadHistory() this.loadHistory()
this.loadTodayRecommendations()
this.setActiveTabByDay() this.setActiveTabByDay()
this.updateSchedulerStatus()
}, },
methods: { methods: {
getNext6am() {
const now = new Date()
const today6am = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 6, 0, 0)
if (now < today6am) {
return today6am
} else {
// 6
return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 6, 0, 0)
}
},
getNext5pm() {
const now = new Date()
const today5pm = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 17, 0, 0)
if (now < today5pm) {
return today5pm
} else {
// 5
return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 17, 0, 0)
}
},
updateSchedulerStatus() {
this.schedulerStatus = {
nextUpdate: this.getNext6am(),
nextRecommend: this.getNext5pm(),
}
},
async loadTodayRecommendations() {
try {
const response = await getTodayRecommendations()
if (response.data && response.data.success) {
this.currentLotteryType = response.data.lottery_type
this.todayRecommend = response.data.recommend.map(item => {
const { redNumbers, blueNumbers } = this.parseNumbers(item.numbers)
return {
...item,
redNumbers,
blueNumbers,
time: new Date(item.created_at).toLocaleString()
}
})
}
} catch (error) {
console.error('加载今日推荐失败:', error)
}
},
async generateNumbers() { async generateNumbers() {
this.loading = true this.loading = true
try { try {
@ -308,6 +396,55 @@ export default {
font-size: 1.1rem; font-size: 1.1rem;
} }
.scheduler-status-section {
margin-bottom: 40px;
}
.status-card {
background: white;
border-radius: 16px;
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.10);
padding: 36px 36px 28px 36px;
}
.status-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #f8f9fa;
}
.status-header h3 {
color: #2c3e50;
margin: 0;
font-size: 1.5rem;
}
.status-content {
margin-bottom: 25px;
}
.status-item {
margin-bottom: 15px;
}
.status-item .status-label {
color: #7f8c8d;
font-size: 0.95rem;
}
.status-item .status-value {
margin-left: 10px;
font-size: 0.95rem;
}
.status-item .status-time {
margin-left: 10px;
font-size: 0.95rem;
}
.quick-order-section { .quick-order-section {
margin-bottom: 40px; margin-bottom: 40px;
} }

View File

@ -29,6 +29,7 @@ def check_dependencies():
import sqlalchemy import sqlalchemy
import pandas import pandas
import requests import requests
import schedule
print("✅ 后端依赖检查通过") print("✅ 后端依赖检查通过")
return True return True
except ImportError as e: except ImportError as e:
@ -37,6 +38,51 @@ def check_dependencies():
return False return False
def start_scheduler():
"""启动定时任务调度器"""
print("\n⏰ 启动定时任务调度器...")
backend_dir = Path("backend")
if not backend_dir.exists():
print("❌ 错误: 找不到backend目录")
return False
try:
# 切换到backend目录
os.chdir(backend_dir)
# 启动定时任务调度器
print("📝 启动命令: python scheduler.py")
process = subprocess.Popen([
sys.executable, "scheduler.py"
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 等待调度器启动
print("⏳ 等待定时任务调度器启动...")
time.sleep(3)
# 检查进程是否还在运行
if process.poll() is None:
print("✅ 定时任务调度器启动成功")
print("📅 定时任务设置:")
print(" - 每天早上6点: 更新开奖数据")
print(" - 每天下午5点: 生成拼盘推荐")
return process
else:
print("❌ 定时任务调度器启动失败")
# 检查进程输出
stdout, stderr = process.communicate(timeout=1)
if stderr:
print(f"错误输出: {stderr}")
if stdout:
print(f"标准输出: {stdout}")
return False
except Exception as e:
print(f"❌ 启动定时任务调度器时出错: {e}")
return False
def start_backend(): def start_backend():
"""启动后端服务""" """启动后端服务"""
print("\n🚀 启动后端服务...") print("\n🚀 启动后端服务...")
@ -255,6 +301,13 @@ def main():
print("❌ 用户取消启动") print("❌ 用户取消启动")
return return
# 询问是否启动定时任务
print("\n⏰ 定时任务选项:")
print("1. 启动定时任务(推荐)")
print("2. 不启动定时任务")
scheduler_choice = input("请选择 (1/2): ").strip()
start_scheduler_task = scheduler_choice == "1"
# 保存当前目录 # 保存当前目录
original_dir = os.getcwd() original_dir = os.getcwd()
@ -270,10 +323,20 @@ def main():
print("\n请检查后端日志获取详细错误信息") print("\n请检查后端日志获取详细错误信息")
return return
# 启动定时任务(如果选择)
scheduler_process = None
if start_scheduler_task:
scheduler_process = start_scheduler()
if not scheduler_process:
print("⚠️ 定时任务启动失败,但系统将继续运行")
print("💡 您可以稍后手动启动定时任务: cd backend && python scheduler.py")
# 启动前端 # 启动前端
frontend_process = start_frontend(original_dir) frontend_process = start_frontend(original_dir)
if not frontend_process: if not frontend_process:
backend_process.terminate() backend_process.terminate()
if scheduler_process:
scheduler_process.terminate()
print("\n💡 前端启动失败,可能的原因:") print("\n💡 前端启动失败,可能的原因:")
print("1. Node.js未安装或版本过低") print("1. Node.js未安装或版本过低")
print("2. 端口5173被占用") print("2. 端口5173被占用")
@ -286,11 +349,18 @@ def main():
print("📊 后端API: http://localhost:8000") print("📊 后端API: http://localhost:8000")
print("📊 API文档: http://localhost:8000/docs") print("📊 API文档: http://localhost:8000/docs")
print("🌐 前端界面: http://localhost:5173") print("🌐 前端界面: http://localhost:5173")
if scheduler_process:
print("⏰ 定时任务: 已启动")
print(" - 每天早上6点: 更新开奖数据")
print(" - 每天下午5点: 生成拼盘推荐")
else:
print("⏰ 定时任务: 未启动")
print("=" * 50) print("=" * 50)
print("💡 使用说明:") print("💡 使用说明:")
print("1. 打开浏览器访问 http://localhost:5173") print("1. 打开浏览器访问 http://localhost:5173")
print("2. 在首页点击'更新数据'按钮获取最新开奖信息") print("2. 在首页点击'补全场早点'按钮获取最新开奖信息")
print("3. 使用各种分析功能进行数据分析") print("3. 在一键下单页面查看今日拼盘推荐")
print("4. 使用各种分析功能进行数据分析")
print("=" * 50) print("=" * 50)
print("按 Ctrl+C 停止服务") print("按 Ctrl+C 停止服务")
@ -302,6 +372,8 @@ def main():
print("\n🛑 正在停止服务...") print("\n🛑 正在停止服务...")
backend_process.terminate() backend_process.terminate()
frontend_process.terminate() frontend_process.terminate()
if scheduler_process:
scheduler_process.terminate()
print("✅ 服务已停止") print("✅ 服务已停止")
except Exception as e: except Exception as e:

170
定时任务使用指南.md Normal file
View File

@ -0,0 +1,170 @@
# 定时任务使用指南
## 概述
彩票数据分析系统新增了定时任务功能,可以自动执行数据更新和拼盘推荐生成任务。
## 定时任务说明
### 1. 数据更新任务
- **执行时间**: 每天早上6点
- **功能**: 自动从API获取最新开奖数据
- **更新内容**:
- 双色球最新开奖数据
- 大乐透最新开奖数据
- 检查历史投注的中奖情况
- **日志文件**: `backend/lottery_scheduler.log`
### 2. 拼盘推荐任务
- **执行时间**: 每天下午5点
- **功能**: 自动生成今日拼盘推荐
- **生成内容**:
- 使用集成预测算法
- 生成4注推荐号码
- 保存到数据库供前端显示
- **日志文件**: `backend/lottery_scheduler.log`
## 启动方式
### 方式一:一键启动(推荐)
```bash
python start_system.py
```
启动时会询问是否启动定时任务,选择"1"即可。
### 方式二:手动启动
```bash
cd backend
python scheduler.py
```
### 方式三:后台运行
```bash
cd backend
nohup python scheduler.py > scheduler.log 2>&1 &
```
## 查看任务状态
### 1. 前端查看
- 打开一键下单页面
- 查看"定时任务状态"区域
- 显示下次执行时间和今日推荐状态
### 2. 日志查看
```bash
# 实时查看日志
tail -f backend/lottery_scheduler.log
# 查看最近100行日志
tail -n 100 backend/lottery_scheduler.log
# 搜索特定任务的日志
grep "早上6点" backend/lottery_scheduler.log
grep "下午5点" backend/lottery_scheduler.log
```
### 3. 进程查看
```bash
# 查看定时任务进程
ps aux | grep scheduler.py
# 查看进程ID
pgrep -f scheduler.py
```
## 常见问题
### Q1: 定时任务没有执行?
**A**: 检查以下几点:
1. 确保定时任务进程正在运行
2. 检查日志文件是否有错误信息
3. 确认系统时间正确
4. 检查数据库连接是否正常
### Q2: 如何修改定时任务时间?
**A**: 编辑 `backend/scheduler.py` 文件中的时间设置:
```python
# 修改为其他时间
schedule.every().day.at("07:00").do(self.update_lottery_data_job) # 改为早上7点
schedule.every().day.at("18:00").do(self.generate_daily_recommendations_job) # 改为下午6点
```
### Q3: 如何停止定时任务?
**A**:
1. 找到进程ID`pgrep -f scheduler.py`
2. 停止进程:`kill <进程ID>`
3. 或者使用Ctrl+C停止如果在前台运行
### Q4: 今日推荐没有生成?
**A**: 检查以下几点:
1. 定时任务是否正常运行
2. 是否有足够的历史数据建议至少100期
3. 预测模型是否训练完成
4. 查看日志文件获取错误信息
### Q5: 如何手动执行任务?
**A**:
1. **手动更新数据**:
```bash
cd backend
python update_lottery.py
```
2. **手动生成推荐**:
- 在前端一键下单页面点击"来一份拼盘"按钮
- 或者调用API接口
## 配置说明
### 日志配置
日志文件位置:`backend/lottery_scheduler.log`
日志级别INFO
日志格式:时间 - 级别 - 消息
### 数据库配置
确保数据库连接正常,定时任务需要访问数据库进行数据更新和推荐生成。
### API配置
定时任务使用聚合数据API获取开奖数据确保API密钥有效。
## 监控建议
### 1. 定期检查日志
建议每天检查一次日志文件,确保任务正常执行。
### 2. 监控磁盘空间
日志文件会持续增长,建议定期清理或设置日志轮转。
### 3. 监控数据库
确保数据库有足够空间存储新数据。
### 4. 监控网络
确保服务器网络连接正常能够访问外部API。
## 故障排除
### 1. 任务执行失败
- 查看日志文件获取详细错误信息
- 检查数据库连接
- 检查网络连接
- 检查API密钥是否有效
### 2. 推荐生成失败
- 检查历史数据是否充足
- 检查预测模型是否正常
- 查看预测服务日志
### 3. 数据更新失败
- 检查API连接
- 检查API密钥
- 检查数据库权限
## 联系支持
如果遇到问题,请:
1. 查看日志文件获取错误信息
2. 检查常见问题部分
3. 提交Issue到项目仓库
---
**注意**: 定时任务功能需要系统持续运行,建议在服务器环境下使用。