#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Flask API 路由 """ import os import json from flask import Blueprint, request, jsonify, send_file from datetime import datetime from collector import ( get_unit_list, get_unit_by_code, get_tag_list, collect_data, get_latest_snapshot, get_history_data ) from ai_analyzer import ( analyze_fault, analyze_trend, generate_report as ai_generate_report, get_available_models ) from reports import ( create_word_report, create_data_table_report, generate_full_report ) from database import get_connection from tag_names import get_tag_description, enrich_data_with_names # 创建蓝图 api = Blueprint('api', __name__, url_prefix='/api') # ==================== 单位管理 ==================== @api.route('/units', methods=['GET']) def list_units(): """获取所有工作面单位列表""" try: units = get_unit_list() return jsonify({ 'success': True, 'data': units }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/units/', methods=['GET']) def get_unit(unit_code): """获取指定单位信息""" try: unit = get_unit_by_code(unit_code) if unit: return jsonify({ 'success': True, 'data': unit }) return jsonify({ 'success': False, 'error': '单位不存在' }), 404 except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/units//tags', methods=['GET']) def list_tags(unit_code): """获取标签列表""" try: device_type = request.args.get('device_type') tag_category = request.args.get('tag_category') tags = get_tag_list(unit_code, device_type=device_type, tag_category=tag_category) return jsonify({ 'success': True, 'data': tags }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ==================== 数据采集 ==================== @api.route('/collect', methods=['POST']) def start_collect(): """启动数据采集""" try: data = request.get_json() unit_code = data.get('unit_code') device_type = data.get('device_type') tag_category = data.get('tag_category') if not unit_code: return jsonify({ 'success': False, 'error': '请指定单位代码' }), 400 from flask import current_app result = collect_data( unit_code=unit_code, api_url=current_app.config['API_URL'], user_handle=current_app.config['USER_HANDLE'], batch_size=current_app.config.get('BATCH_SIZE', 100), device_type=device_type, tag_category=tag_category ) return jsonify({ 'success': result.get('status') == 'success', 'data': result }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/collect/all', methods=['POST']) def collect_all(): """采集所有单位数据""" try: from flask import current_app units = get_unit_list() results = [] for unit in units: result = collect_data( unit_code=unit['unit_code'], api_url=current_app.config['API_URL'], user_handle=current_app.config['USER_HANDLE'], batch_size=current_app.config.get('BATCH_SIZE', 100) ) results.append(result) return jsonify({ 'success': True, 'data': results }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ==================== 数据查询 ==================== @api.route('/snapshot/', methods=['GET']) def get_snapshot(unit_code): """获取最新快照数据(含中文名称)""" try: snapshot = get_latest_snapshot(unit_code) if snapshot: # 为数据添加中文名称 if 'data' in snapshot and isinstance(snapshot['data'], list): snapshot['data'] = enrich_data_with_names(snapshot['data']) return jsonify({ 'success': True, 'data': snapshot }) return jsonify({ 'success': False, 'error': '暂无数据' }), 404 except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/history', methods=['GET']) def get_history(): """获取历史数据(含中文名称)""" try: unit_code = request.args.get('unit_code') start_time = request.args.get('start_time') end_time = request.args.get('end_time') tag_name = request.args.get('tag_name') if not unit_code or not start_time or not end_time: return jsonify({ 'success': False, 'error': '请指定 unit_code, start_time, end_time' }), 400 data = get_history_data(unit_code, start_time, end_time, tag_name) # 为数据添加中文名称 if isinstance(data, list): data = enrich_data_with_names(data) elif isinstance(data, dict) and 'data' in data and isinstance(data['data'], list): data['data'] = enrich_data_with_names(data['data']) return jsonify({ 'success': True, 'data': data }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/realtime/', methods=['GET']) def get_realtime(unit_code): """获取实时数据(最新快照的数据列表,含中文名称)""" try: snapshot = get_latest_snapshot(unit_code) if snapshot and 'data' in snapshot: data = enrich_data_with_names(snapshot['data']) return jsonify({ 'success': True, 'data': data, 'snapshot_time': snapshot.get('snapshot_time') }) return jsonify({ 'success': False, 'error': '暂无数据' }), 404 except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ==================== AI分析 ==================== @api.route('/ai/fault', methods=['POST']) def ai_fault_analysis(): """AI故障诊断""" try: data = request.get_json() unit_code = data.get('unit_code') model_key = data.get('model_key') if not unit_code: return jsonify({ 'success': False, 'error': '请指定单位代码' }), 400 # 获取最新数据 snapshot = get_latest_snapshot(unit_code) if not snapshot or 'data' not in snapshot: return jsonify({ 'success': False, 'error': '暂无数据,请先采集' }), 400 # AI分析 result = analyze_fault(snapshot['data'], model_key=model_key) return jsonify({ 'success': result.get('status') == 'success', 'data': result }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/ai/trend', methods=['POST']) def ai_trend_analysis(): """AI趋势分析""" try: data = request.get_json() unit_code = data.get('unit_code') start_time = data.get('start_time') end_time = data.get('end_time') model_key = data.get('model_key') if not unit_code or not start_time or not end_time: return jsonify({ 'success': False, 'error': '请指定 unit_code, start_time, end_time' }), 400 # 获取历史数据 history = get_history_data(unit_code, start_time, end_time) # AI分析 result = analyze_trend(history, model_key=model_key) return jsonify({ 'success': result.get('status') == 'success', 'data': result }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/ai/report', methods=['POST']) def ai_generate(): """AI生成运维报告""" try: data = request.get_json() unit_code = data.get('unit_code') model_key = data.get('model_key') if not unit_code: return jsonify({ 'success': False, 'error': '请指定单位代码' }), 400 # 获取单位信息 unit = get_unit_by_code(unit_code) if not unit: return jsonify({ 'success': False, 'error': '单位不存在' }), 404 # 获取最新数据 snapshot = get_latest_snapshot(unit_code) if not snapshot or 'data' not in snapshot: return jsonify({ 'success': False, 'error': '暂无数据,请先采集' }), 400 # AI生成报告 result = ai_generate_report(snapshot['data'], unit['unit_name'], model_key=model_key) return jsonify({ 'success': result.get('status') == 'success', 'data': result }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/ai/models', methods=['GET']) def list_models(): """获取可用AI模型列表""" try: models = get_available_models() return jsonify({ 'success': True, 'data': models }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/tags/names', methods=['GET']) def get_tag_names(): """获取所有标签中文名称映射""" try: from tag_names import get_all_tag_names tag_map = get_all_tag_names() return jsonify({ 'success': True, 'data': tag_map, 'count': len(tag_map) }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/tags/lookup', methods=['POST']) def lookup_tag_name(): """查询单个标签的中文名称""" try: data = request.get_json() tag_name = data.get('tag_name', '') chinese_name = get_tag_description(tag_name) return jsonify({ 'success': True, 'data': { 'tag_name': tag_name, 'chinese_name': chinese_name } }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ==================== 报告生成 ==================== @api.route('/report/word', methods=['POST']) def generate_word_report(): """生成Word报告""" try: data = request.get_json() unit_code = data.get('unit_code') report_type = data.get('type', 'data') # data, ai, full unit = get_unit_by_code(unit_code) if not unit: return jsonify({ 'success': False, 'error': '单位不存在' }), 404 snapshot = get_latest_snapshot(unit_code) if not snapshot or 'data' not in snapshot: return jsonify({ 'success': False, 'error': '暂无数据' }), 400 if report_type == 'data': file_path = create_data_table_report(unit['unit_name'], snapshot['data']) elif report_type == 'ai': ai_result = ai_generate_report(snapshot['data'], unit['unit_name']) if ai_result.get('status') == 'success': file_path = create_word_report( f"{unit['unit_name']} AI分析报告", ai_result['content'] ) else: return jsonify({ 'success': False, 'error': ai_result.get('content', 'AI分析失败') }), 500 else: # 完整报告 ai_result = ai_generate_report(snapshot['data'], unit['unit_name']) if ai_result.get('status') == 'success': model_name = ai_result.get('model_name', 'AI') file_path = generate_full_report( unit_code, unit['unit_name'], snapshot['data'], ai_result['content'], model_name ) else: return jsonify({ 'success': False, 'error': ai_result.get('content', 'AI分析失败') }), 500 return jsonify({ 'success': True, 'data': { 'file_path': file_path, 'filename': os.path.basename(file_path) } }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/report/download/', methods=['GET']) def download_report(filename): """下载报告文件""" try: output_dir = os.path.join(os.path.dirname(__file__), 'outputs') file_path = os.path.join(output_dir, filename) if not os.path.exists(file_path): return jsonify({ 'success': False, 'error': '文件不存在' }), 404 return send_file( file_path, as_attachment=True, download_name=filename ) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ==================== 统计信息 ==================== @api.route('/stats/', methods=['GET']) def get_stats(unit_code): """获取单个工作面统计信息(增强版)""" try: conn = get_connection() try: with conn.cursor() as cursor: # 获取unit_id cursor.execute("SELECT id FROM mine_units WHERE unit_code = %s", (unit_code,)) unit = cursor.fetchone() if not unit: return jsonify({ 'success': False, 'error': '单位不存在' }), 404 unit_id = unit['id'] # 采集次数(总快照数) cursor.execute(""" SELECT COUNT(*) as count FROM data_snapshots WHERE unit_id = %s """, (unit_id,)) collect_count = cursor.fetchone()['count'] # 最新采集时间 cursor.execute(""" SELECT MAX(snapshot_time) as last_time FROM data_snapshots WHERE unit_id = %s """, (unit_id,)) last_result = cursor.fetchone() last_time = last_result['last_time'] # 今日采集次数 cursor.execute(""" SELECT COUNT(*) as count FROM data_snapshots WHERE unit_id = %s AND DATE(snapshot_time) = CURDATE() """, (unit_id,)) today_count = cursor.fetchone()['count'] # 监测点数量(标签数) cursor.execute(""" SELECT COUNT(DISTINCT tag_name) as tag_count FROM tag_data WHERE snapshot_id IN (SELECT id FROM data_snapshots WHERE unit_id = %s) """, (unit_id,)) tag_count = cursor.fetchone()['tag_count'] # 数据总量 cursor.execute(""" SELECT COUNT(*) as total FROM tag_data WHERE snapshot_id IN (SELECT id FROM data_snapshots WHERE unit_id = %s) """, (unit_id,)) data_total = cursor.fetchone()['total'] # 按设备类型统计标签数 cursor.execute(""" SELECT device_type, COUNT(DISTINCT tag_name) as cnt FROM tag_data WHERE snapshot_id IN (SELECT id FROM data_snapshots WHERE unit_id = %s) GROUP BY device_type """, (unit_id,)) device_stats = {row['device_type']: row['cnt'] for row in cursor.fetchall()} return jsonify({ 'success': True, 'data': { 'collect_count': collect_count, 'last_time': str(last_time) if last_time else None, 'today_count': today_count, 'tag_count': tag_count, 'data_total': data_total, 'device_stats': device_stats } }) finally: conn.close() except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 @api.route('/stats/all', methods=['GET']) def get_all_stats(): """获取所有工作面的总体统计信息""" try: conn = get_connection() try: with conn.cursor() as cursor: # 工作面数量 cursor.execute("SELECT COUNT(*) as count FROM mine_units") unit_count = cursor.fetchone()['count'] # 总采集次数 cursor.execute("SELECT COUNT(*) as count FROM data_snapshots") total_collects = cursor.fetchone()['count'] # 今日总采集次数 cursor.execute(""" SELECT COUNT(*) as count FROM data_snapshots WHERE DATE(snapshot_time) = CURDATE() """) today_collects = cursor.fetchone()['count'] # 总数据量 cursor.execute("SELECT COUNT(*) as count FROM tag_data") data_total = cursor.fetchone()['count'] # 总监测点数量(去重) cursor.execute("SELECT COUNT(DISTINCT tag_name) as count FROM tag_data") tag_count = cursor.fetchone()['count'] # 每个工作面的简要统计 cursor.execute(""" SELECT mu.unit_code, mu.unit_name, COUNT(DISTINCT ds.id) as collect_count, MAX(ds.snapshot_time) as last_time, COUNT(DISTINCT td.tag_name) as tag_count FROM mine_units mu LEFT JOIN data_snapshots ds ON mu.id = ds.unit_id LEFT JOIN tag_data td ON ds.id = td.snapshot_id GROUP BY mu.id, mu.unit_code, mu.unit_name ORDER BY mu.unit_name """) unit_stats = [] for row in cursor.fetchall(): unit_stats.append({ 'unit_code': row['unit_code'], 'unit_name': row['unit_name'], 'collect_count': row['collect_count'], 'last_time': str(row['last_time']) if row['last_time'] else None, 'tag_count': row['tag_count'] }) return jsonify({ 'success': True, 'data': { 'unit_count': unit_count, 'total_collects': total_collects, 'today_collects': today_collects, 'data_total': data_total, 'tag_count': tag_count, 'unit_stats': unit_stats } }) finally: conn.close() except Exception as e: return jsonify({ 'success': False, 'error': str(e) }), 500 # ==================== 健康检查 ==================== @api.route('/health', methods=['GET']) def health_check(): """健康检查""" return jsonify({ 'success': True, 'data': { 'status': 'ok', 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } })