Files
4an-workorder/app/views/device.py
T

298 lines
12 KiB
Python
Executable File

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