feat: 新增自动下载 API 和设置页面 UI

This commit is contained in:
2026-04-29 16:18:31 +08:00
parent 89b01bb522
commit 75bdc94cfe
5 changed files with 808 additions and 0 deletions
+168
View File
@@ -6,6 +6,19 @@ import json
from datetime import datetime
import glob
import time
import asyncio
import threading
import logging
from config import Config
from automation.uploader import import_excel_file, cleanup_download
from automation.scheduler import init_scheduler, get_scheduler_status, shutdown_scheduler
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'uploads'
@@ -186,6 +199,154 @@ def cleanup_files():
except Exception as e:
return jsonify({'error': f'清理文件失败: {str(e)}'}), 500
# ============ 自动化相关路由 ============
# 全局下载任务状态
download_status = {
'running': False,
'message': '',
'last_run': None,
'last_file': None
}
@app.route('/settings')
def settings_page():
"""设置页面"""
return render_template('settings.html')
@app.route('/api/settings', methods=['GET'])
def get_settings():
"""获取配置"""
try:
return jsonify({'success': True, 'data': Config.get_all_config()})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/settings', methods=['POST'])
def save_settings():
"""保存配置"""
try:
data = request.get_json()
# 保存凭据
if 'secsion' in data:
secsion = data['secsion']
username = secsion.get('username', '').strip()
password = secsion.get('password', '').strip()
shop_id = secsion.get('shop_id', '').strip()
if username and password and password != '******':
Config.save_secsion_credentials(username, password)
if shop_id is not None:
Config.save_shop_id(shop_id)
# 保存调度配置
if 'scheduler' in data:
sched = data['scheduler']
Config.save_schedule_config(
enabled=sched.get('enabled', True),
hour=sched.get('hour', 1),
minute=sched.get('minute', 0)
)
return jsonify({'success': True, 'message': '配置已保存'})
except Exception as e:
return jsonify({'error': f'保存配置失败: {str(e)}'}), 500
@app.route('/api/auto-download', methods=['POST'])
def auto_download():
"""触发自动下载"""
global download_status
if download_status['running']:
return jsonify({'error': '已有下载任务正在执行,请稍候'}), 409
try:
data = request.get_json() or {}
start_date = data.get('start_date')
end_date = data.get('end_date', start_date)
if not start_date:
from datetime import timedelta
yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
start_date = yesterday
end_date = yesterday
# 检查凭据
creds = Config.get_secsion_credentials()
if not creds:
return jsonify({'error': '未配置 secsion.com 登录凭据,请先在设置页面配置'}), 400
username, password = creds
# 在后台线程执行下载
def run_download():
global download_status
download_status['running'] = True
download_status['message'] = f'正在下载 {start_date} ~ {end_date} 的数据...'
try:
from automation.secsion import SecsionDownloader
shop_id = Config.get_shop_id()
downloader = SecsionDownloader(username, password, download_dir='downloads', shop_id=shop_id)
file_path = asyncio.run(downloader.download_report(start_date, end_date))
if file_path:
imported_name = import_excel_file(file_path, upload_dir='uploads')
cleanup_download(file_path)
if imported_name:
download_status['message'] = f'下载完成: {imported_name}'
download_status['last_file'] = imported_name
logger.info(f"自动下载并导入成功: {imported_name}")
else:
download_status['message'] = '下载成功但导入失败'
else:
download_status['message'] = '下载失败:未获取到文件'
except Exception as e:
download_status['message'] = f'下载异常: {str(e)}'
logger.error(f"自动下载异常: {e}", exc_info=True)
finally:
download_status['running'] = False
download_status['last_run'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
thread = threading.Thread(target=run_download, daemon=True)
thread.start()
return jsonify({
'success': True,
'message': f'已开始下载 {start_date} ~ {end_date} 的数据'
})
except Exception as e:
return jsonify({'error': f'启动下载失败: {str(e)}'}), 500
@app.route('/api/auto-download/status', methods=['GET'])
def get_download_status():
"""获取下载任务状态"""
return jsonify({
'success': True,
'status': download_status
})
@app.route('/api/scheduler/status', methods=['GET'])
def get_scheduler():
"""获取定时任务状态"""
return jsonify({
'success': True,
'status': get_scheduler_status()
})
# ============ 数据处理函数 ============
def find_header_row(filepath):
"""查找表头所在的行索引"""
try:
@@ -417,4 +578,11 @@ if __name__ == '__main__':
port = int(os.environ.get('PORT', 5000))
# 生产环境建议关闭 debug
debug_mode = os.environ.get('FLASK_DEBUG', 'True').lower() == 'true'
# 初始化定时任务调度器
try:
init_scheduler(app)
except Exception as e:
logger.warning(f"定时任务调度器初始化失败: {e}")
app.run(debug=debug_mode, host='0.0.0.0', port=port)