开始
This commit is contained in:
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Executable
+8
@@ -0,0 +1,8 @@
|
||||
from flask import render_template
|
||||
from flask_login import login_required
|
||||
|
||||
def init_about_routes(app):
|
||||
@app.route('/about')
|
||||
@login_required
|
||||
def about():
|
||||
return render_template('about.html')
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
from flask import render_template, request, redirect, url_for, flash
|
||||
from flask_login import login_user, login_required, logout_user, current_user
|
||||
from scripts.models import db, User
|
||||
|
||||
def init_auth_routes(app):
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
phone = request.form['phone']
|
||||
user = User.query.filter_by(phone=phone).first()
|
||||
if user:
|
||||
login_user(user)
|
||||
app.logger.info(f'用户 {user.name}({user.phone}) 登录成功')
|
||||
# 无论是统计员还是装维员,都跳转到仪表盘
|
||||
return redirect(url_for('dashboard'))
|
||||
else:
|
||||
app.logger.warning(f'登录失败:用户不存在 (手机号: {phone})')
|
||||
flash('用户不存在')
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
app.logger.info(f'用户 {current_user.name}({current_user.phone}) 登出')
|
||||
logout_user()
|
||||
return redirect(url_for('login'))
|
||||
Executable
+197
@@ -0,0 +1,197 @@
|
||||
from flask import render_template, flash, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from scripts.models import db, Device, WorkOrder
|
||||
from collections import defaultdict
|
||||
from sqlalchemy import func
|
||||
from utils.db_utils import cache_query, db_operation
|
||||
|
||||
def init_dashboard_routes(app):
|
||||
@app.route('/dashboard')
|
||||
@login_required
|
||||
def dashboard():
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问仪表盘页面')
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
try:
|
||||
app.logger.info(f'用户 {current_user.name} 访问仪表盘页面')
|
||||
|
||||
# 获取设备在线率数据
|
||||
overall_stats = get_overall_device_stats()
|
||||
branch_stats = get_branch_device_stats()
|
||||
town_stats = get_town_device_stats()
|
||||
|
||||
return render_template('dashboard.html',
|
||||
overall_stats=overall_stats,
|
||||
branch_stats=branch_stats,
|
||||
town_stats=town_stats)
|
||||
except Exception as e:
|
||||
app.logger.error(f"仪表盘页面加载失败: {str(e)}")
|
||||
flash('数据加载失败,请稍后重试')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
@cache_query(ttl=300) # 缓存5分钟
|
||||
def get_overall_device_stats():
|
||||
"""获取总体设备在线率统计"""
|
||||
stats = {}
|
||||
|
||||
# 获取设备类型列表
|
||||
device_types = db.session.query(Device.device_type).distinct().all()
|
||||
device_types = [d[0] for d in device_types if d[0]]
|
||||
|
||||
for device_type in device_types:
|
||||
# 获取该类型的设备总数
|
||||
total_devices = db.session.query(func.count(Device.device_id))\
|
||||
.filter(Device.device_type == device_type)\
|
||||
.scalar() or 0
|
||||
|
||||
# 获取该类型的派单数
|
||||
work_orders = db.session.query(func.count(WorkOrder.order_id))\
|
||||
.join(Device, WorkOrder.device_id == Device.device_id)\
|
||||
.filter(Device.device_type == device_type)\
|
||||
.scalar() or 0
|
||||
|
||||
# 计算在线率
|
||||
if total_devices > 0:
|
||||
online_rate = ((total_devices - work_orders) / total_devices) * 100
|
||||
else:
|
||||
online_rate = 0
|
||||
|
||||
stats[device_type] = {
|
||||
'total': total_devices,
|
||||
'work_orders': work_orders,
|
||||
'online_rate': round(online_rate, 2)
|
||||
}
|
||||
|
||||
return stats
|
||||
|
||||
@cache_query(ttl=300) # 缓存5分钟
|
||||
def get_branch_device_stats():
|
||||
"""获取各支局设备在线率统计"""
|
||||
stats = {}
|
||||
|
||||
# 获取所有支局
|
||||
branches = db.session.query(Device.branch).distinct().all()
|
||||
branches = [b[0] for b in branches if b[0]]
|
||||
|
||||
# 获取设备类型列表
|
||||
device_types = db.session.query(Device.device_type).distinct().all()
|
||||
device_types = [d[0] for d in device_types if d[0]]
|
||||
|
||||
for branch in branches:
|
||||
branch_stats = {}
|
||||
|
||||
for device_type in device_types:
|
||||
# 获取该支局该类型的设备总数
|
||||
total_devices = db.session.query(func.count(Device.device_id))\
|
||||
.filter(Device.branch == branch, Device.device_type == device_type)\
|
||||
.scalar() or 0
|
||||
|
||||
# 获取该支局该类型的派单数
|
||||
work_orders = db.session.query(func.count(WorkOrder.order_id))\
|
||||
.join(Device, WorkOrder.device_id == Device.device_id)\
|
||||
.filter(Device.branch == branch, Device.device_type == device_type)\
|
||||
.scalar() or 0
|
||||
|
||||
# 计算在线率
|
||||
if total_devices > 0:
|
||||
online_rate = ((total_devices - work_orders) / total_devices) * 100
|
||||
else:
|
||||
online_rate = 0
|
||||
|
||||
branch_stats[device_type] = {
|
||||
'total': total_devices,
|
||||
'work_orders': work_orders,
|
||||
'online_rate': round(online_rate, 2)
|
||||
}
|
||||
|
||||
# 计算该支局总体在线率
|
||||
total_branch_devices = db.session.query(func.count(Device.device_id))\
|
||||
.filter(Device.branch == branch)\
|
||||
.scalar() or 0
|
||||
|
||||
total_branch_orders = db.session.query(func.count(WorkOrder.order_id))\
|
||||
.join(Device, WorkOrder.device_id == Device.device_id)\
|
||||
.filter(Device.branch == branch)\
|
||||
.scalar() or 0
|
||||
|
||||
if total_branch_devices > 0:
|
||||
branch_online_rate = ((total_branch_devices - total_branch_orders) / total_branch_devices) * 100
|
||||
else:
|
||||
branch_online_rate = 0
|
||||
|
||||
branch_stats['总计'] = {
|
||||
'total': total_branch_devices,
|
||||
'work_orders': total_branch_orders,
|
||||
'online_rate': round(branch_online_rate, 2)
|
||||
}
|
||||
|
||||
stats[branch] = branch_stats
|
||||
|
||||
return stats
|
||||
|
||||
@cache_query(ttl=300) # 缓存5分钟
|
||||
def get_town_device_stats():
|
||||
"""获取各乡镇设备在线率统计"""
|
||||
stats = {}
|
||||
|
||||
# 获取所有乡镇
|
||||
towns = db.session.query(Device.town).distinct().all()
|
||||
towns = [t[0] for t in towns if t[0]]
|
||||
|
||||
# 获取设备类型列表
|
||||
device_types = db.session.query(Device.device_type).distinct().all()
|
||||
device_types = [d[0] for d in device_types if d[0]]
|
||||
|
||||
for town in towns:
|
||||
town_stats = {}
|
||||
|
||||
for device_type in device_types:
|
||||
# 获取该乡镇该类型的设备总数
|
||||
total_devices = db.session.query(func.count(Device.device_id))\
|
||||
.filter(Device.town == town, Device.device_type == device_type)\
|
||||
.scalar() or 0
|
||||
|
||||
# 获取该乡镇该类型的派单数
|
||||
work_orders = db.session.query(func.count(WorkOrder.order_id))\
|
||||
.join(Device, WorkOrder.device_id == Device.device_id)\
|
||||
.filter(Device.town == town, Device.device_type == device_type)\
|
||||
.scalar() or 0
|
||||
|
||||
# 计算在线率
|
||||
if total_devices > 0:
|
||||
online_rate = ((total_devices - work_orders) / total_devices) * 100
|
||||
else:
|
||||
online_rate = 0
|
||||
|
||||
town_stats[device_type] = {
|
||||
'total': total_devices,
|
||||
'work_orders': work_orders,
|
||||
'online_rate': round(online_rate, 2)
|
||||
}
|
||||
|
||||
# 计算该乡镇总体在线率
|
||||
total_town_devices = db.session.query(func.count(Device.device_id))\
|
||||
.filter(Device.town == town)\
|
||||
.scalar() or 0
|
||||
|
||||
total_town_orders = db.session.query(func.count(WorkOrder.order_id))\
|
||||
.join(Device, WorkOrder.device_id == Device.device_id)\
|
||||
.filter(Device.town == town)\
|
||||
.scalar() or 0
|
||||
|
||||
if total_town_devices > 0:
|
||||
town_online_rate = ((total_town_devices - total_town_orders) / total_town_devices) * 100
|
||||
else:
|
||||
town_online_rate = 0
|
||||
|
||||
town_stats['总计'] = {
|
||||
'total': total_town_devices,
|
||||
'work_orders': total_town_orders,
|
||||
'online_rate': round(town_online_rate, 2)
|
||||
}
|
||||
|
||||
stats[town] = town_stats
|
||||
|
||||
return stats
|
||||
Executable
+297
@@ -0,0 +1,297 @@
|
||||
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():
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
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)})
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
from flask import render_template, request, flash, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from scripts.models import db, WorkOrderHandling, ManualWorkOrder
|
||||
from datetime import datetime
|
||||
|
||||
def init_history_routes(app):
|
||||
@app.route('/history')
|
||||
@login_required
|
||||
def history():
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问历史记录')
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
app.logger.info(f'用户 {current_user.name} 访问历史记录页面')
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = 20
|
||||
search = request.args.get('search', '')
|
||||
|
||||
# 查询普通工单
|
||||
if current_user.role == '统计员':
|
||||
query1 = WorkOrderHandling.query
|
||||
query2 = ManualWorkOrder.query
|
||||
else:
|
||||
# 装维员只能看到自己处理的工单
|
||||
query1 = WorkOrderHandling.query.filter_by(handler=current_user.name)
|
||||
query2 = ManualWorkOrder.query.filter_by(handler=current_user.name)
|
||||
|
||||
# 添加搜索条件
|
||||
if search:
|
||||
query1 = query1.filter(
|
||||
db.or_(
|
||||
WorkOrderHandling.order_id.contains(search),
|
||||
WorkOrderHandling.device_id.contains(search),
|
||||
WorkOrderHandling.branch.contains(search),
|
||||
WorkOrderHandling.handler.contains(search),
|
||||
WorkOrderHandling.fault_type.contains(search),
|
||||
WorkOrderHandling.handle_description.contains(search),
|
||||
WorkOrderHandling.contact_person.contains(search),
|
||||
WorkOrderHandling.remark.contains(search)
|
||||
)
|
||||
)
|
||||
query2 = query2.filter(
|
||||
db.or_(
|
||||
ManualWorkOrder.order_id.contains(search),
|
||||
ManualWorkOrder.device_id.contains(search),
|
||||
ManualWorkOrder.branch.contains(search),
|
||||
ManualWorkOrder.handler.contains(search),
|
||||
ManualWorkOrder.fault_type.contains(search),
|
||||
ManualWorkOrder.handle_description.contains(search),
|
||||
ManualWorkOrder.contact_person.contains(search),
|
||||
ManualWorkOrder.remark.contains(search)
|
||||
)
|
||||
)
|
||||
|
||||
# 合并两种工单并按时间排序
|
||||
handlings1 = query1.all()
|
||||
handlings2 = query2.all()
|
||||
|
||||
# 将手工故障单转换为与普通工单相同的格式
|
||||
combined_handlings = []
|
||||
for h in handlings1:
|
||||
combined_handlings.append({
|
||||
'order_id': h.order_id,
|
||||
'branch': h.branch,
|
||||
'device_id': h.device_id,
|
||||
'handler': h.handler,
|
||||
'time': h.restore_time,
|
||||
'fault_type': h.fault_type,
|
||||
'type': 'normal'
|
||||
})
|
||||
|
||||
for h in handlings2:
|
||||
combined_handlings.append({
|
||||
'order_id': h.order_id,
|
||||
'branch': h.branch,
|
||||
'device_id': h.device_id,
|
||||
'handler': h.handler,
|
||||
'time': h.create_time,
|
||||
'fault_type': h.fault_type,
|
||||
'type': 'manual'
|
||||
})
|
||||
|
||||
# 按时间排序
|
||||
combined_handlings.sort(key=lambda x: x['time'], reverse=True)
|
||||
|
||||
# 手动分页
|
||||
total = len(combined_handlings)
|
||||
start = (page - 1) * per_page
|
||||
end = start + per_page
|
||||
current_page = combined_handlings[start:end]
|
||||
|
||||
# 创建分页对象
|
||||
class Pagination:
|
||||
def __init__(self, items, page, per_page, total):
|
||||
self.items = items
|
||||
self.page = page
|
||||
self.per_page = per_page
|
||||
self.total = total
|
||||
self.pages = (total + per_page - 1) // per_page
|
||||
self.has_prev = page > 1
|
||||
self.has_next = page < self.pages
|
||||
self.prev_num = page - 1
|
||||
self.next_num = page + 1
|
||||
|
||||
handlings = Pagination(current_page, page, per_page, total)
|
||||
|
||||
return render_template('history.html', handlings=handlings, search=search)
|
||||
|
||||
@app.route('/history/<string:order_id>')
|
||||
@login_required
|
||||
def history_detail(order_id):
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试查看工单详情 {order_id}')
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
app.logger.info(f'用户 {current_user.name} 查看工单 {order_id} 的详情')
|
||||
# 根据工单号前缀判断是普通工单还是手工故障单
|
||||
if order_id.startswith('M'):
|
||||
handling = ManualWorkOrder.query.get_or_404(order_id)
|
||||
else:
|
||||
handling = WorkOrderHandling.query.filter_by(order_id=order_id).first_or_404()
|
||||
|
||||
# 装维员只能查看自己处理的工单
|
||||
if current_user.role == '装维员' and handling.handler != current_user.name:
|
||||
flash('您没有权限查看此工单')
|
||||
return redirect(url_for('history'))
|
||||
|
||||
# 传递工单类型到模板
|
||||
order_type = 'manual' if order_id.startswith('M') else 'normal'
|
||||
|
||||
return render_template('history_detail.html', handling=handling, order_type=order_type)
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
from flask import render_template, request, jsonify, flash, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from scripts.models import db, Device, ManualWorkOrder
|
||||
from datetime import datetime
|
||||
import os
|
||||
from utils.file_utils import allowed_file, compress_image
|
||||
from utils.lzmx import send_manual_lzmx_message
|
||||
from config import Config
|
||||
|
||||
def init_manual_routes(app):
|
||||
@app.route('/manual', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def manual():
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问手工故障单页面')
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
if request.method == 'POST':
|
||||
app.logger.info(f'用户 {current_user.name} 开始创建手工故障单')
|
||||
# 处理文件上传
|
||||
photo_paths = []
|
||||
if 'photo' in request.files:
|
||||
files = request.files.getlist('photo')
|
||||
if len(files) > 5:
|
||||
flash('最多只能上传5张图片')
|
||||
return redirect(url_for('manual'))
|
||||
for file in files:
|
||||
if file and allowed_file(file.filename, Config.ALLOWED_EXTENSIONS): # 修改这里
|
||||
filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}"
|
||||
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
file.save(file_path)
|
||||
compress_image(file_path)
|
||||
photo_paths.append(filename)
|
||||
|
||||
# 生成工单号
|
||||
today = datetime.now().strftime('%Y%m%d')
|
||||
last_order = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.order_id.like(f'M{today}%')
|
||||
).order_by(ManualWorkOrder.order_id.desc()).first()
|
||||
|
||||
if last_order:
|
||||
last_number = int(last_order.order_id[-4:])
|
||||
new_number = f"M{today}{str(last_number + 1).zfill(4)}"
|
||||
else:
|
||||
new_number = f"M{today}0001"
|
||||
|
||||
# 创建手工故障单
|
||||
manual_order = ManualWorkOrder(
|
||||
order_id=new_number,
|
||||
project_type=request.form['project_type'],
|
||||
device_id=request.form['device_id'],
|
||||
device_location=request.form['device_location'],
|
||||
fault_time=datetime.strptime(request.form['fault_time'], '%Y-%m-%dT%H:%M'),
|
||||
handler=request.form['handler'],
|
||||
fault_type=request.form['fault_type'],
|
||||
handle_description=request.form['handle_description'],
|
||||
photo_paths=','.join(photo_paths) if photo_paths else None,
|
||||
contact_person=request.form['contact_person'],
|
||||
contact_phone=request.form['contact_phone'],
|
||||
distance_from_branch=request.form['distance_from_branch'],
|
||||
remark=request.form.get('remark', ''),
|
||||
branch=current_user.branch
|
||||
)
|
||||
|
||||
db.session.add(manual_order)
|
||||
db.session.commit()
|
||||
|
||||
# 发送量子密信通知
|
||||
send_manual_lzmx_message(
|
||||
Config.LZMX_WEBHOOK_URL, # 修改这里
|
||||
request.form['handler'],
|
||||
request.form['device_id'],
|
||||
request.form['fault_type'],
|
||||
request.form['distance_from_branch'],
|
||||
manual_order.fault_time,
|
||||
request.form['project_type'],
|
||||
request.form['device_location']
|
||||
)
|
||||
|
||||
app.logger.info(f'用户 {current_user.name} 创建手工故障单成功:{new_number}')
|
||||
flash('手工故障单创建成功')
|
||||
return redirect(url_for('history'))
|
||||
|
||||
return render_template('manual.html')
|
||||
Executable
+325
@@ -0,0 +1,325 @@
|
||||
from flask import render_template, flash, redirect, url_for, send_file
|
||||
from flask_login import login_required, current_user
|
||||
from scripts.models import db, WorkOrder, WorkOrderHandling, ManualWorkOrder
|
||||
from datetime import datetime, timedelta
|
||||
from collections import defaultdict
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, Alignment
|
||||
from utils.reward import get_reward_by_distance
|
||||
from scripts.models import Device
|
||||
import io
|
||||
|
||||
def init_statistics_routes(app):
|
||||
@app.route('/statistics')
|
||||
@login_required
|
||||
def statistics():
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问统计页面')
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
app.logger.info(f'用户 {current_user.name} 访问统计页面')
|
||||
# 获取当前时间
|
||||
now = datetime.now()
|
||||
|
||||
# 计算本周开始和结束时间
|
||||
week_start = now - timedelta(days=now.weekday())
|
||||
week_start = week_start.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
week_end = week_start + timedelta(days=7)
|
||||
|
||||
# 计算本月开始和结束时间
|
||||
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
if now.month == 12:
|
||||
month_end = now.replace(year=now.year + 1, month=1, day=1)
|
||||
else:
|
||||
month_end = now.replace(month=now.month + 1, day=1)
|
||||
|
||||
# 统计普通工单
|
||||
def get_work_order_stats(start_time, end_time):
|
||||
total = WorkOrder.query.filter(
|
||||
WorkOrder.dispatch_time >= start_time,
|
||||
WorkOrder.dispatch_time < end_time
|
||||
).count()
|
||||
|
||||
completed = WorkOrder.query.filter(
|
||||
WorkOrder.dispatch_time >= start_time,
|
||||
WorkOrder.dispatch_time < end_time,
|
||||
WorkOrder.status == '已处理'
|
||||
).count()
|
||||
|
||||
pending = total - completed
|
||||
return {'total': total, 'completed': completed, 'pending': pending}
|
||||
|
||||
# 统计手工故障单
|
||||
def get_manual_order_stats(start_time, end_time):
|
||||
total = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= start_time,
|
||||
ManualWorkOrder.create_time < end_time
|
||||
).count()
|
||||
return total
|
||||
|
||||
# 计算奖励金额
|
||||
def calculate_reward(start_time, end_time):
|
||||
rewards = defaultdict(float)
|
||||
|
||||
# 查询所有已处理的工单
|
||||
handlings = WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= start_time,
|
||||
WorkOrderHandling.restore_time < end_time
|
||||
).all()
|
||||
|
||||
# 查询所有手工故障单
|
||||
manual_orders = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= start_time,
|
||||
ManualWorkOrder.create_time < end_time
|
||||
).all()
|
||||
|
||||
# 计算普通工单奖励
|
||||
for handling in handlings:
|
||||
device = Device.query.get(handling.device_id)
|
||||
if device:
|
||||
distance = float(handling.distance_from_branch)
|
||||
reward = get_reward_by_distance(device.project_type, distance)
|
||||
rewards[device.project_type] += reward
|
||||
|
||||
# 计算手工故障单奖励
|
||||
for order in manual_orders:
|
||||
distance = float(order.distance_from_branch)
|
||||
reward = get_reward_by_distance(order.project_type, distance)
|
||||
rewards[order.project_type] += reward
|
||||
|
||||
return rewards
|
||||
|
||||
def get_handler_stats(start_time, end_time):
|
||||
handler_stats = defaultdict(lambda: {
|
||||
'work_orders': 0,
|
||||
'manual_orders': 0,
|
||||
'rewards': defaultdict(float)
|
||||
})
|
||||
|
||||
# 统计普通工单
|
||||
handlings = WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= start_time,
|
||||
WorkOrderHandling.restore_time < end_time
|
||||
).all()
|
||||
|
||||
for handling in handlings:
|
||||
handler = handling.handler
|
||||
handler_stats[handler]['work_orders'] += 1
|
||||
device = Device.query.get(handling.device_id)
|
||||
if device:
|
||||
distance = float(handling.distance_from_branch)
|
||||
reward = get_reward_by_distance(device.project_type, distance)
|
||||
handler_stats[handler]['rewards'][device.project_type] += reward
|
||||
|
||||
# 统计手工故障单
|
||||
manual_orders = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= start_time,
|
||||
ManualWorkOrder.create_time < end_time
|
||||
).all()
|
||||
|
||||
for order in manual_orders:
|
||||
handler = order.handler
|
||||
handler_stats[handler]['manual_orders'] += 1
|
||||
distance = float(order.distance_from_branch)
|
||||
reward = get_reward_by_distance(order.project_type, distance)
|
||||
handler_stats[handler]['rewards'][order.project_type] += reward
|
||||
|
||||
return handler_stats
|
||||
|
||||
# 获取统计数据
|
||||
week_work_stats = get_work_order_stats(week_start, week_end)
|
||||
month_work_stats = get_work_order_stats(month_start, month_end)
|
||||
|
||||
week_manual_stats = get_manual_order_stats(week_start, week_end)
|
||||
month_manual_stats = get_manual_order_stats(month_start, month_end)
|
||||
|
||||
week_rewards = calculate_reward(week_start, week_end)
|
||||
month_rewards = calculate_reward(month_start, month_end)
|
||||
|
||||
week_handler_stats = get_handler_stats(week_start, week_end)
|
||||
month_handler_stats = get_handler_stats(month_start, month_end)
|
||||
|
||||
# 如果是装维员,只显示自己的数据
|
||||
if current_user.role == '装维员':
|
||||
week_work_stats = {
|
||||
'completed': WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= week_start,
|
||||
WorkOrderHandling.restore_time < week_end,
|
||||
WorkOrderHandling.handler == current_user.name
|
||||
).count()
|
||||
}
|
||||
month_work_stats = {
|
||||
'completed': WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= month_start,
|
||||
WorkOrderHandling.restore_time < month_end,
|
||||
WorkOrderHandling.handler == current_user.name
|
||||
).count()
|
||||
}
|
||||
week_manual_stats = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= week_start,
|
||||
ManualWorkOrder.create_time < week_end,
|
||||
ManualWorkOrder.handler == current_user.name
|
||||
).count()
|
||||
month_manual_stats = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= month_start,
|
||||
ManualWorkOrder.create_time < month_end,
|
||||
ManualWorkOrder.handler == current_user.name
|
||||
).count()
|
||||
# 修改这里:只获取当前装维员的奖励统计
|
||||
week_rewards = calculate_reward(week_start, week_end)
|
||||
month_rewards = calculate_reward(month_start, month_end)
|
||||
week_handler_stats = {current_user.name: week_handler_stats.get(current_user.name, defaultdict(int))}
|
||||
month_handler_stats = {current_user.name: month_handler_stats.get(current_user.name, defaultdict(int))}
|
||||
|
||||
# 从 handler_stats 中提取当前用户的奖励数据
|
||||
week_rewards = week_handler_stats[current_user.name]['rewards']
|
||||
month_rewards = month_handler_stats[current_user.name]['rewards']
|
||||
|
||||
return render_template('statistics.html',
|
||||
week_work_stats=week_work_stats,
|
||||
month_work_stats=month_work_stats,
|
||||
week_manual_stats=week_manual_stats,
|
||||
month_manual_stats=month_manual_stats,
|
||||
week_rewards=week_rewards,
|
||||
month_rewards=month_rewards,
|
||||
week_handler_stats=week_handler_stats,
|
||||
month_handler_stats=month_handler_stats)
|
||||
@app.route('/export_monthly_stats/<int:year>/<int:month>')
|
||||
@login_required
|
||||
def export_monthly_stats(year, month):
|
||||
if current_user.role != '统计员':
|
||||
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试导出月度统计')
|
||||
flash('您没有权限执行此操作')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
app.logger.info(f'用户 {current_user.name} 导出 {year}年{month}月 统计报表')
|
||||
|
||||
# 计算指定月份的开始和结束时间
|
||||
month_start = datetime(year, month, 1, 0, 0, 0)
|
||||
if month == 12:
|
||||
month_end = datetime(year + 1, 1, 1, 0, 0, 0)
|
||||
else:
|
||||
month_end = datetime(year, month + 1, 1, 0, 0, 0)
|
||||
|
||||
# 检查是否有数据
|
||||
handlings = WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= month_start,
|
||||
WorkOrderHandling.restore_time < month_end
|
||||
).first()
|
||||
|
||||
if not handlings:
|
||||
flash(f'{year}年{month}月系统无检修工单')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
# 创建工作簿
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "运维计件奖励"
|
||||
|
||||
# 设置标题
|
||||
ws.merge_cells('A1:J1')
|
||||
title = ws.cell(1, 1, f"{year}年{month}月政企重点项目运维各支局检修激励汇总")
|
||||
title.font = Font(size=14, bold=True)
|
||||
title.alignment = Alignment(horizontal='center')
|
||||
|
||||
# 设置表头
|
||||
headers = ['序号', '检修单位名称', '检修项目', '检修设备总数(台)',
|
||||
'单位距支局维护人员所在地址(公里)', '检修设备每个单价(元)',
|
||||
'检修项目合计数(路)', '本月检修金额(元)', '支局', '检修人员','本月个人检修合计','检修时间']
|
||||
for col, header in enumerate(headers, 1):
|
||||
cell = ws.cell(2, col, header)
|
||||
cell.font = Font(bold=True)
|
||||
cell.alignment = Alignment(horizontal='center', wrap_text=True)
|
||||
|
||||
# 获取指定月份数据
|
||||
handlings = WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= month_start,
|
||||
WorkOrderHandling.restore_time < month_end
|
||||
).all()
|
||||
|
||||
# 查询本月所有手工故障单
|
||||
manual_orders = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= month_start,
|
||||
ManualWorkOrder.create_time < month_end
|
||||
).order_by(ManualWorkOrder.create_time).all()
|
||||
|
||||
# 填充数据
|
||||
row = 3
|
||||
idx = 0 # 初始化序号计数器
|
||||
|
||||
# 处理普通工单
|
||||
for handling in handlings:
|
||||
device = db.session.get(Device, handling.device_id)
|
||||
if not device:
|
||||
continue
|
||||
|
||||
idx += 1 # 增加序号
|
||||
ws.cell(row, 1, idx) # 序号
|
||||
ws.cell(row, 2, device.device_id) # 检修单位名称
|
||||
ws.cell(row, 3, device.project_type) # 检修项目
|
||||
ws.cell(row, 4, 1) # 检修设备总数
|
||||
ws.cell(row, 5, handling.distance_from_branch) # 距离
|
||||
|
||||
# 计算单价
|
||||
distance = float(handling.distance_from_branch)
|
||||
reward = get_reward_by_distance(device.project_type, distance)
|
||||
ws.cell(row, 6, reward) # 单价
|
||||
|
||||
ws.cell(row, 7, 1) # 检修项目合计数
|
||||
ws.cell(row, 8, reward) # 本月检修金额
|
||||
ws.cell(row, 9, handling.branch) # 支局
|
||||
ws.cell(row, 10, handling.handler) # 检修人员
|
||||
ws.cell(row, 12, handling.restore_time.strftime('%Y-%m-%d %H:%M:%S')) # 检修时间
|
||||
|
||||
row += 1
|
||||
|
||||
# 处理手工故障单
|
||||
for manual_order in manual_orders:
|
||||
idx += 1 # 增加序号
|
||||
ws.cell(row, 1, idx) # 序号
|
||||
ws.cell(row, 2, manual_order.device_location) # 检修单位名称
|
||||
ws.cell(row, 3, manual_order.project_type) # 检修项目
|
||||
ws.cell(row, 4, 1) # 检修设备总数
|
||||
ws.cell(row, 5, manual_order.distance_from_branch) # 距离
|
||||
|
||||
# 计算单价
|
||||
distance = float(manual_order.distance_from_branch)
|
||||
reward = get_reward_by_distance(manual_order.project_type, distance)
|
||||
ws.cell(row, 6, reward) # 单价
|
||||
|
||||
ws.cell(row, 7, 1) # 检修项目合计数
|
||||
ws.cell(row, 8, reward) # 本月检修金额
|
||||
ws.cell(row, 9, manual_order.branch) # 支局
|
||||
ws.cell(row, 10, manual_order.handler) # 检修人员
|
||||
ws.cell(row, 12, manual_order.create_time.strftime('%Y-%m-%d %H:%M:%S')) # 检修时间
|
||||
|
||||
row += 1
|
||||
idx += 1
|
||||
|
||||
# 添加合计行
|
||||
ws.cell(row, 7, f"合计")
|
||||
ws.cell(row, 8, f"=SUM(H3:H{row-1})")
|
||||
ws.cell(row, 8).font = Font(bold=True)
|
||||
|
||||
# 设置列宽
|
||||
ws.column_dimensions['B'].width = 20
|
||||
ws.column_dimensions['C'].width = 15
|
||||
ws.column_dimensions['E'].width = 15
|
||||
ws.column_dimensions['F'].width = 15
|
||||
ws.column_dimensions['H'].width = 15
|
||||
|
||||
# 保存到内存中
|
||||
excel_file = io.BytesIO()
|
||||
wb.save(excel_file)
|
||||
excel_file.seek(0)
|
||||
|
||||
# 设置文件名
|
||||
filename = f'{year}年{month}月政企重点项目运维各支局检修激励汇总.xlsx'
|
||||
|
||||
return send_file(
|
||||
excel_file,
|
||||
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
as_attachment=True,
|
||||
download_name=filename
|
||||
)
|
||||
Executable
+331
@@ -0,0 +1,331 @@
|
||||
from flask import render_template, request, redirect, url_for, flash, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from scripts.models import db, Device, WorkOrder, WorkOrderHandling
|
||||
from datetime import datetime
|
||||
import os
|
||||
from utils.file_utils import allowed_file, compress_image
|
||||
from utils.lzmx import send_lzmx_message
|
||||
from config import Config
|
||||
|
||||
def init_work_order_routes(app):
|
||||
# 添加获取支局列表的辅助函数
|
||||
def get_branches():
|
||||
branches = db.session.query(Device.branch).distinct().all()
|
||||
return [branch[0] for branch in branches if branch[0]]
|
||||
|
||||
# 派单平台路由
|
||||
@app.route('/dispatch', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def dispatch():
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
# 处理 POST 请求
|
||||
if request.method == 'POST':
|
||||
device_ids = request.form.getlist('device_ids')
|
||||
|
||||
if not device_ids:
|
||||
flash('请选择至少一个设备')
|
||||
return redirect(url_for('dispatch'))
|
||||
|
||||
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()
|
||||
|
||||
# 获取起始编号
|
||||
if last_work_order:
|
||||
last_number = int(last_work_order.order_id[-4:])
|
||||
start_number = last_number + 1
|
||||
else:
|
||||
start_number = 1
|
||||
|
||||
# 为每个设备生成唯一的工单号
|
||||
for index, device_id in enumerate(device_ids):
|
||||
device = db.session.get(Device, device_id)
|
||||
if not device:
|
||||
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_id,
|
||||
dispatch_time=datetime.now(),
|
||||
status='待处理',
|
||||
branch=device.branch
|
||||
)
|
||||
|
||||
db.session.add(work_order)
|
||||
device.is_normal = False
|
||||
db.session.add(device)
|
||||
|
||||
db.session.commit()
|
||||
flash('派单成功')
|
||||
return redirect(url_for('dispatch'))
|
||||
|
||||
# 处理 GET 请求
|
||||
# 获取分页参数
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 50, type=int) # 从请求参数中获取每页显示数量
|
||||
|
||||
# 获取搜索词和筛选条件
|
||||
search = request.args.get('search', '')
|
||||
selected_town = request.args.get('town', '')
|
||||
selected_branch = request.args.get('branch', '')
|
||||
|
||||
towns = db.session.query(Device.town).distinct().all()
|
||||
towns = [town[0] for town in towns if town[0]]
|
||||
|
||||
branches = db.session.query(Device.branch).distinct().all()
|
||||
branches = [branch[0] for branch in branches if branch[0]]
|
||||
|
||||
query = Device.query
|
||||
if search:
|
||||
query = query.filter(
|
||||
(Device.device_id.contains(search)) |
|
||||
(Device.town.contains(search)) |
|
||||
(Device.branch.contains(search)) |
|
||||
(Device.unit_name.contains(search)) |
|
||||
(Device.project_type.contains(search)) |
|
||||
(Device.device_type.contains(search)) |
|
||||
(Device.remark.contains(search))
|
||||
)
|
||||
|
||||
if selected_town:
|
||||
query = query.filter(Device.town == selected_town)
|
||||
|
||||
if selected_branch:
|
||||
query = query.filter(Device.branch == selected_branch)
|
||||
|
||||
# 分页时使用从请求获取的 per_page 参数
|
||||
devices = query.filter_by(in_use=True).paginate(page=page, per_page=per_page)
|
||||
|
||||
return render_template('dispatch.html', devices=devices, towns=towns, branches=branches)
|
||||
|
||||
@app.route('/update_device_remark', methods=['POST'])
|
||||
@login_required
|
||||
def update_device_remark():
|
||||
try:
|
||||
data = request.get_json()
|
||||
device_id = data.get('device_id')
|
||||
remark = data.get('remark', '')
|
||||
|
||||
device = Device.query.get(device_id)
|
||||
if not device:
|
||||
return jsonify({'success': False, 'message': '设备不存在'})
|
||||
|
||||
device.remark = remark
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'message': str(e)})
|
||||
|
||||
# 接单平台路由
|
||||
@app.route('/receive')
|
||||
@login_required
|
||||
def receive():
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 50, type=int)
|
||||
branch = request.args.get('branch', '')
|
||||
device_type = request.args.get('device_type', '')
|
||||
project_type = request.args.get('project_type', '')
|
||||
|
||||
# 基础查询
|
||||
query = WorkOrder.query.filter_by(status='待处理')
|
||||
|
||||
# 根据用户权限筛选工单
|
||||
if current_user.branch != '交付中心':
|
||||
query = query.join(Device).filter(Device.branch == current_user.branch)
|
||||
|
||||
# 获取总数统计
|
||||
total_orders = query.count()
|
||||
|
||||
# 获取各支局统计
|
||||
branch_stats = {}
|
||||
if current_user.branch == '交付中心':
|
||||
branch_counts = db.session.query(
|
||||
Device.branch,
|
||||
db.func.count(WorkOrder.order_id)
|
||||
).join(WorkOrder, WorkOrder.device_id == Device.device_id
|
||||
).filter(WorkOrder.status == '待处理'
|
||||
).group_by(Device.branch).all()
|
||||
|
||||
branch_stats = dict(branch_counts)
|
||||
|
||||
# 应用分支筛选(仅交付中心可用)
|
||||
if branch and current_user.branch == '交付中心':
|
||||
query = query.join(Device).filter(Device.branch == branch)
|
||||
|
||||
# 应用设备类型筛选
|
||||
if device_type:
|
||||
query = query.join(Device).filter(Device.device_type == device_type)
|
||||
|
||||
# 应用项目类型筛选
|
||||
if project_type:
|
||||
query = query.join(Device).filter(Device.project_type == project_type)
|
||||
|
||||
# 获取筛选选项
|
||||
device_types_query = db.session.query(Device.device_type).distinct().join(WorkOrder).filter(WorkOrder.status == '待处理')
|
||||
project_types_query = db.session.query(Device.project_type).distinct().join(WorkOrder).filter(WorkOrder.status == '待处理')
|
||||
|
||||
# 根据用户权限进一步筛选选项
|
||||
if current_user.branch != '交付中心':
|
||||
device_types_query = device_types_query.filter(Device.branch == current_user.branch)
|
||||
project_types_query = project_types_query.filter(Device.branch == current_user.branch)
|
||||
elif branch:
|
||||
device_types_query = device_types_query.filter(Device.branch == branch)
|
||||
project_types_query = project_types_query.filter(Device.branch == branch)
|
||||
|
||||
device_types = [dt[0] for dt in device_types_query.all() if dt[0]]
|
||||
project_types = [pt[0] for pt in project_types_query.all() if pt[0]]
|
||||
|
||||
# 获取分页数据
|
||||
work_orders = query.paginate(page=page, per_page=per_page)
|
||||
|
||||
# 计算派单时长
|
||||
for work_order in work_orders.items:
|
||||
dispatch_time = work_order.dispatch_time
|
||||
now = datetime.now()
|
||||
delta = now - dispatch_time
|
||||
work_order.duration = f"{delta.days}天{delta.seconds // 3600}小时{(delta.seconds % 3600) // 60}分钟"
|
||||
|
||||
return render_template('receive.html',
|
||||
work_orders=work_orders,
|
||||
total_orders=total_orders,
|
||||
branch_stats=branch_stats,
|
||||
show_filter=current_user.branch == '交付中心', # 只有交付中心可以看到筛选
|
||||
branches=get_branches() if current_user.branch == '交付中心' else [], # 只有交付中心可以看到支局列表
|
||||
selected_branch=branch,
|
||||
device_types=device_types,
|
||||
project_types=project_types
|
||||
)
|
||||
|
||||
@app.route('/recall_all_orders', methods=['POST'])
|
||||
@login_required
|
||||
def recall_all_orders():
|
||||
try:
|
||||
# 获取所有在途工单
|
||||
orders = WorkOrder.query.filter_by(status='待处理').all()
|
||||
count = len(orders)
|
||||
|
||||
# 恢复设备状态并删除工单
|
||||
for order in orders:
|
||||
# 恢复设备状态
|
||||
device = Device.query.get(order.device_id)
|
||||
if device:
|
||||
device.is_normal = True
|
||||
db.session.add(device)
|
||||
|
||||
# 直接删除工单
|
||||
db.session.delete(order)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'count': count
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
# 处理工单
|
||||
@app.route('/receive/<string:work_order_id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def receive_detail(work_order_id):
|
||||
if current_user.role not in ['装维员', '统计员']:
|
||||
flash('您没有权限访问此页面')
|
||||
return redirect(url_for('statistics'))
|
||||
|
||||
work_order = WorkOrder.query.get_or_404(work_order_id)
|
||||
device = Device.query.filter_by(device_id=work_order.device_id).first()
|
||||
|
||||
if request.method == 'POST':
|
||||
photo_paths = []
|
||||
if 'photo' in request.files:
|
||||
files = request.files.getlist('photo')
|
||||
if len(files) > 5:
|
||||
flash('最多只能上传5张图片')
|
||||
return redirect(url_for('receive_detail', work_order_id=work_order_id))
|
||||
for file in files:
|
||||
if file and allowed_file(file.filename, Config.ALLOWED_EXTENSIONS): # 修改这里
|
||||
filename = f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{file.filename}"
|
||||
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||
file.save(file_path)
|
||||
compress_image(file_path)
|
||||
photo_paths.append(filename)
|
||||
|
||||
# 创建处理工单信息
|
||||
handling_info = WorkOrderHandling(
|
||||
order_id=work_order.order_id,
|
||||
device_id=work_order.device_id,
|
||||
branch=device.branch if device else None,
|
||||
restore_time=datetime.now(),
|
||||
fault_type=request.form['fault_type'],
|
||||
contact_person=request.form['contact_person'],
|
||||
contact_phone=request.form['contact_phone'],
|
||||
handler=request.form['handler'],
|
||||
handle_description=request.form['handle_description'],
|
||||
photo_paths=','.join(photo_paths) if photo_paths else None,
|
||||
distance_from_branch=request.form['distance_from_branch'],
|
||||
remark=request.form.get('remark', '')
|
||||
)
|
||||
|
||||
# 更新工单状态为已处理
|
||||
work_order.status = '已处理'
|
||||
|
||||
# 更新设备状态为正常
|
||||
if device:
|
||||
device.is_normal = True
|
||||
|
||||
# 将所有更改添加到会话
|
||||
db.session.add(handling_info)
|
||||
db.session.add(work_order)
|
||||
if device:
|
||||
db.session.add(device)
|
||||
|
||||
# 一次性提交所有更改
|
||||
db.session.commit()
|
||||
|
||||
# 发送量子密信通知
|
||||
send_lzmx_message(
|
||||
Config.LZMX_WEBHOOK_URL, # 修改这里
|
||||
request.form['handler'],
|
||||
work_order.device_id,
|
||||
request.form['fault_type'],
|
||||
request.form['distance_from_branch'],
|
||||
datetime.now()
|
||||
)
|
||||
|
||||
flash('工单处理成功')
|
||||
return redirect(url_for('receive'))
|
||||
|
||||
return render_template('receive_detail.html', work_order=work_order, device=device)
|
||||
|
||||
@app.route('/recall_order/<order_id>')
|
||||
@login_required
|
||||
def recall_order(order_id):
|
||||
# 检查用户权限
|
||||
if current_user.role != '统计员':
|
||||
flash('没有权限执行此操作')
|
||||
return redirect(url_for('receive'))
|
||||
|
||||
# 获取工单
|
||||
work_order = WorkOrder.query.get_or_404(order_id)
|
||||
|
||||
# 更新设备状态
|
||||
device = Device.query.get(work_order.device_id)
|
||||
if device:
|
||||
device.is_normal = True
|
||||
db.session.add(device)
|
||||
|
||||
# 删除工单
|
||||
db.session.delete(work_order)
|
||||
db.session.commit()
|
||||
|
||||
flash('工单已成功追回')
|
||||
return redirect(url_for('receive'))
|
||||
Reference in New Issue
Block a user