Files
ZDXX/2.1版本/routes/api.py

233 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)})