分步式页面布局,首页页面设计实现初稿

This commit is contained in:
YueL1331
2026-01-08 13:53:19 +08:00
parent af4b4a28c3
commit a5b0b71d26
20 changed files with 1749 additions and 569 deletions

233
2.1版本/routes/api.py Normal file
View File

@ -0,0 +1,233 @@
import os
import shutil
import json # 👈 必需:用于序列化原始数据
from flask import Blueprint, jsonify, request
from datetime import datetime
from models import db, Device, DeviceHistory, MaintenanceLog
try:
from services.core import execute_monitor_task
except ImportError:
execute_monitor_task = None
api_bp = Blueprint('api', __name__, url_prefix='/api')
# =========================================================================
# 常规接口 (隐藏、总览、地点、维修)
# =========================================================================
@api_bp.route('/toggle_hidden', methods=['POST'])
def toggle_hidden():
try:
data = request.json
device = Device.query.filter_by(name=data.get('name')).first()
if device:
device.is_hidden = data.get('is_hidden', False)
db.session.commit()
return jsonify({'message': '状态更新成功'}), 200
return jsonify({'error': '设备不存在'}), 404
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500
@api_bp.route('/devices_overview', methods=['GET'])
def devices_overview():
try:
# 直接读取 Device 表快照
devices = Device.query.all()
return jsonify({'data': [d.to_dict() for d in devices]})
except Exception as e:
return jsonify({'error': str(e)}), 500
@api_bp.route('/update_site', methods=['POST'])
def update_site():
try:
data = request.json
record = Device.query.filter_by(name=data.get('name')).first()
if record:
record.install_site = data.get('site')
db.session.commit()
return jsonify({'code': 200, 'message': '更新成功'})
return jsonify({'code': 404, 'message': '设备不存在'})
except Exception as e:
return jsonify({'code': 500, 'message': str(e)})
@api_bp.route('/toggle_maintenance', methods=['POST'])
def toggle_maintenance():
try:
data = request.json
record = Device.query.filter_by(name=data.get('name')).first()
if record:
record.is_maintaining = data.get('is_maintaining', False)
db.session.commit()
return jsonify({'code': 200, 'message': '状态更新成功'})
return jsonify({'code': 404, 'message': '设备不存在'})
except Exception as e:
return jsonify({'code': 500, 'message': str(e)})
@api_bp.route('/logs/add', methods=['POST'])
def add_log():
try:
data = request.json
new_log = MaintenanceLog(device_name=data.get('device_name'), content=data.get('content'))
db.session.add(new_log)
db.session.commit()
return jsonify({'code': 200, 'message': '日志已保存'})
except Exception as e:
return jsonify({'code': 500, 'message': str(e)})
# =========================================================================
# 🔥 核心功能区
# =========================================================================
@api_bp.route('/device_history', methods=['GET'])
def get_device_history():
"""获取单个设备的历史,包含原始 JSON 数据"""
try:
name = request.args.get('name')
if not name:
return jsonify({'error': 'Missing name parameter'}), 400
device = Device.query.filter_by(name=name).first()
if not device:
return jsonify({'error': 'Device not found'}), 404
history = DeviceHistory.query.filter_by(device_id=device.id) \
.order_by(DeviceHistory.recorded_at.desc()) \
.limit(100).all()
history_data = []
for h in history:
rec_time = h.recorded_at.strftime('%Y-%m-%d %H:%M:%S') if h.recorded_at else 'N/A'
# 🔥 将数据库里存的 JSON 字符串转回对象,发给前端
raw_obj = None
if h.json_data:
try:
raw_obj = json.loads(h.json_data)
except:
raw_obj = h.json_data
history_data.append({
'data_time': h.data_time,
'status': h.status,
'value': h.result_data,
'raw_data': raw_obj, # 🔥 前端可查看详细原始数据
'file_path': h.file_path,
'recorded_at': rec_time
})
return jsonify({
'device': device.name,
'history': history_data
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@api_bp.route('/run_monitor', methods=['POST'])
def run_monitor():
"""
🔥 真实爬虫逻辑:
1. 归档文件
2. 存入 Device 表(更新快照,若设备消失则保留旧快照)
3. 存入 DeviceHistory 表(追加历史,保存 Raw JSON
"""
try:
print(">>> 启动真实监测任务...")
if not execute_monitor_task:
return jsonify({'code': 500, 'message': 'execute_monitor_task missing'})
# 1. 准备目录
base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
binary_dir = os.path.join(base_dir, 'instance', 'binary')
if not os.path.exists(binary_dir):
os.makedirs(binary_dir)
# 2. 调用爬虫
task_result = execute_monitor_task()
if not task_result:
return jsonify({'code': 500, 'message': '爬虫未返回数据'})
scraped_data_list = task_result.get('device_list', [])
target_time_str = task_result.get('target_time', datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
temp_file_path = task_result.get('temp_file_path', None)
print(f"✅ 获取到 {len(scraped_data_list)} 条数据,时间: {target_time_str}")
# 3. 文件归档
db_rel_path = ""
safe_filename = "data_unknown.db"
if temp_file_path and os.path.exists(temp_file_path):
safe_filename = target_time_str.replace(' ', '_').replace(':', '-') + ".db"
final_file_path = os.path.join(binary_dir, safe_filename)
shutil.move(temp_file_path, final_file_path)
db_rel_path = f"binary/{safe_filename}"
print(f"✅ 文件已归档: {final_file_path}")
# 4. 数据库写入 (双表写入)
for item in scraped_data_list:
d_name = item.get('name')
if not d_name: continue
d_status = item.get('status', 'unknown')
d_value = item.get('value', '')
d_site = item.get('site', '')
# 🔥 序列化:把整个字典转成 JSON 字符串
# ensure_ascii=False 确保中文可以正常显示,而不是 \uXXXX
raw_json_str = json.dumps(item, ensure_ascii=False)
# -----------------------------------------------------------
# 表 A: Device (快照)
# 逻辑:如果设备存在,就更新它的“最新状态”;如果不存在,就新建。
# 关键点如果爬虫这次没爬到“设备X”这里就不会执行“设备X”的数据就会保持在上次的状态。
# 这就完美解决了“网页上消失但我要展示”的需求。
# -----------------------------------------------------------
device = Device.query.filter_by(name=d_name).first()
if not device:
device = Device(name=d_name, source='Auto')
db.session.add(device)
db.session.flush() # 拿 ID
device.status = d_status
device.current_value = d_value
device.latest_time = target_time_str # 数据的产生时间
device.check_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") # 系统检查时间
device.json_data = raw_json_str # 🔥 更新快照里的原始数据
if d_site:
device.install_site = d_site
# -----------------------------------------------------------
# 表 B: DeviceHistory (日志)
# 逻辑:不管有没有,永远追加一条新记录。
# -----------------------------------------------------------
new_history = DeviceHistory(
device_id=device.id,
status=d_status,
result_data=d_value,
data_time=target_time_str,
json_data=raw_json_str, # 🔥 存入历史原始数据
file_path=db_rel_path
)
db.session.add(new_history)
db.session.commit()
return jsonify({
'code': 200,
'message': f'检测完成,已归档 {safe_filename},更新 {len(scraped_data_list)} 台设备'
})
except Exception as e:
db.session.rollback()
print(f"Monitor Error: {e}")
return jsonify({'code': 500, 'message': str(e)})