from flask import jsonify, request, send_file, session, current_app from flask_login import login_required, current_user from scripts.update.zte import LoginClient from scripts.models import db, Device, WorkOrder, Channel from datetime import datetime from sqlalchemy import text, create_engine import hashlib import pymysql import io import os def init_device_routes(app): @app.route('/sync_offline_devices', methods=['POST']) @login_required def sync_offline_devices(): allowed_roles = ['装维员', '统计员', '管理员', 'admin'] if current_user.role not in allowed_roles and current_user.name != 'Admin': app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试同步离线设备') return jsonify({'success': False, 'message': '没有权限执行此操作'}) try: app.logger.info(f'用户 {current_user.name} 开始同步离线设备') # 从配置中获取数据库连接信息 db_config = current_app.config['KEY110_DB'] conn = pymysql.connect(**db_config) cursor = conn.cursor() # 获取所有教学点的状态 cursor.execute(""" SELECT 教学点名称, 连通状态 FROM IP数据 """) schools = cursor.fetchall() # 分别处理在线和离线的教学点 offline_school_names = [school[0].strip().replace(' ', ' ') for school in schools if school[1] == '离线'] online_school_names = [school[0].strip().replace(' ', ' ') for school in schools if school[1] == '在线'] # 关闭1key110数据库连接 cursor.close() conn.close() # 处理恢复在线的设备 online_devices = Device.query.filter( Device.device_id.in_(online_school_names), Device.is_normal == False # 当前状态为故障的设备 ).all() # 删除恢复在线设备的在途工单 recovered_count = 0 for device in online_devices: # 查找并删除该设备的在途工单 pending_orders = WorkOrder.query.filter_by( device_id=device.device_id, status='待处理' ).all() for order in pending_orders: db.session.delete(order) recovered_count += 1 # 更新设备状态为正常 device.is_normal = True db.session.add(device) # 处理离线设备 offline_devices = Device.query.filter( Device.device_id.in_(offline_school_names), Device.in_use == True, Device.is_normal == True ).all() # 生成工单号 today = datetime.now().strftime('%Y%m%d') last_work_order = WorkOrder.query.filter( WorkOrder.order_id.like(f'{today}%') ).order_by(WorkOrder.order_id.desc()).first() start_number = int(last_work_order.order_id[-4:]) + 1 if last_work_order else 1 # 创建工单 created_count = 0 for index, device in enumerate(offline_devices): # 检查是否已有在途工单 existing_order = WorkOrder.query.filter_by( device_id=device.device_id, status='待处理' ).first() if existing_order: continue current_number = start_number + index new_number = f"{today}{str(current_number).zfill(4)}" work_order = WorkOrder( order_id=new_number, device_id=device.device_id, dispatch_time=datetime.now(), status='待处理', branch=device.branch ) db.session.add(work_order) device.is_normal = False db.session.add(device) created_count += 1 db.session.commit() app.logger.info(f'同步完成:创建了 {created_count} 个工单,恢复了 {recovered_count} 个设备') return jsonify({ 'success': True, 'count': created_count }) except Exception as e: app.logger.error(f'同步离线设备失败:{str(e)}') return jsonify({ 'success': False, 'message': str(e) }) @app.route('/check_existing_orders', methods=['POST']) @login_required def check_existing_orders(): device_ids = request.json.get('device_ids', []) app.logger.info(f'用户 {current_user.name} 检查设备工单状态,设备数量:{len(device_ids)}') # 查询设备状态 existing_devices = Device.query.filter( Device.device_id.in_(device_ids), Device.is_normal == False # 检查设备是否处于故障状态 ).with_entities(Device.device_id).all() # 提取设备ID列表 existing_device_ids = [device.device_id for device in existing_devices] app.logger.info(f'发现 {len(existing_device_ids)} 个设备存在故障工单') return jsonify({'existing_devices': existing_device_ids}) @app.route('/get_captcha', methods=['GET']) def get_captcha(): app.logger.info('用户请求验证码') try: client = LoginClient() cid = client.get_captcha() # 将 cid 存储在 session 中供后续使用 session['zte_cid'] = cid # 读取验证码图片并返回给前端 captcha_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static', 'captcha.png') with open(captcha_path, "rb") as f: app.logger.info('验证码获取成功') return send_file( io.BytesIO(f.read()), mimetype='image/png' ) except Exception as e: app.logger.error(f'获取验证码失败:{str(e)}') return jsonify({'success': False, 'message': '获取验证码失败'}) @app.route('/sync_camera_step1', methods=['POST']) def sync_camera_step1(): try: app.logger.info(f'用户 {current_user.name if current_user else "未登录"} 开始同步摄像头第一步') data = request.get_json() captcha = data.get('captcha') cid = session.get('zte_cid') if not captcha or not cid: app.logger.warning('同步摄像头失败:验证码或会话ID缺失') return jsonify({'success': False, 'message': 'Invalid request'}) client = LoginClient() # 登录 app.logger.info('尝试登录中兴平台') login_result = client.login( account="DHDX001", password=hashlib.sha256("Root1234#".encode()).hexdigest(), validcode=captcha, cid=cid ) if login_result.get("result") != 0: app.logger.warning('登录中兴平台失败:验证码错误') return jsonify({'success': False, 'message': 'INVALID_CAPTCHA'}) token = login_result.get("token") app.logger.info('登录成功,开始获取在线信息') # 获取在线信息 online_info = client.get_online_info(token) if online_info.get("result") == 0: rows = online_info.get("rows", []) updated_count = 0 offline_count = 0 app.logger.info(f'获取到 {len(rows)} 个设备信息,开始更新数据库') # 更新 channels 表 for row in rows: db.session.execute( text(""" INSERT INTO channels (puid, pu_name, status) VALUES (:puid, :pu_name, :status) ON DUPLICATE KEY UPDATE pu_name = :pu_name, status = :status """), { 'puid': row['puid'], 'pu_name': row['pu_name'], 'status': row['status'] } ) updated_count += 1 if row['status'] != 'online': offline_count += 1 db.session.commit() app.logger.info(f'数据库更新完成,更新了 {updated_count} 条记录,其中离线设备 {offline_count} 个') return jsonify({ 'success': True, 'updated_count': updated_count, 'offline_count': offline_count }) app.logger.error('获取在线信息失败') return jsonify({'success': False, 'message': 'Failed to get online info'}) except Exception as e: app.logger.error(f'同步摄像头步骤1失败:{str(e)}') return jsonify({'success': False, 'message': str(e)}) @app.route('/sync_camera_step2', methods=['POST']) def sync_camera_step2(): try: app.logger.info(f'用户 {current_user.name if current_user else "未登录"} 开始同步摄像头第二步') # 更新设备状态 devices = Device.query.filter( Device.puid.isnot(None), Device.in_use == True # 添加这个条件,只检查在用设备 ).all() app.logger.info(f'查询到 {len(devices)} 个需要同步的设备') offline_devices = [] for device in devices: channel = db.session.query(Channel).filter_by(puid=device.puid).first() if channel and channel.status != 'online': device.is_normal = 0 # 检查是否已有在途工单 existing_order = WorkOrder.query.filter_by( device_id=device.device_id, status='待处理' ).first() if not existing_order: # 只添加没有在途工单的设备 offline_devices.append(device) db.session.commit() app.logger.info(f'发现 {len(offline_devices)} 个离线设备需要创建工单') # 为离线设备创建工单 created_count = 0 if offline_devices: today = datetime.now().strftime('%Y%m%d') last_work_order = WorkOrder.query.filter( WorkOrder.order_id.like(f'{today}%') ).order_by(WorkOrder.order_id.desc()).first() start_number = int(last_work_order.order_id[-4:]) + 1 if last_work_order else 1 for index, device in enumerate(offline_devices): order_id = f"{today}{str(start_number + index).zfill(4)}" work_order = WorkOrder( order_id=order_id, device_id=device.device_id, status='待处理', dispatch_time=datetime.now(), branch=device.branch # 添加支局信息 ) db.session.add(work_order) created_count += 1 db.session.commit() app.logger.info(f'成功创建 {created_count} 个工单') return jsonify({ 'success': True, 'count': created_count }) except Exception as e: app.logger.error(f'同步摄像头步骤2失败:{str(e)}') return jsonify({'success': False, 'message': str(e)})