开始
This commit is contained in:
Executable
+90
@@ -0,0 +1,90 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import UserMixin
|
||||
from datetime import datetime, timezone
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
# 用户表
|
||||
class User(UserMixin, db.Model):
|
||||
__tablename__ = 'user'
|
||||
phone = db.Column(db.String(20), primary_key=True, comment="手机号码")
|
||||
name = db.Column(db.String(50), comment="姓名")
|
||||
branch = db.Column(db.String(50), comment="所属支局")
|
||||
role = db.Column(db.String(20), comment="角色")
|
||||
def get_id(self):
|
||||
return self.phone # 使用手机号码作为唯一标识符
|
||||
|
||||
# 设备表
|
||||
class Device(db.Model):
|
||||
__tablename__ = 'device'
|
||||
device_id = db.Column(db.String(100), primary_key=True, comment="设备编号")
|
||||
town = db.Column(db.String(50), comment="所属乡镇")
|
||||
branch = db.Column(db.String(50), comment="所属支局")
|
||||
unit_name = db.Column(db.String(100), comment="单位名称")
|
||||
project_type = db.Column(db.String(50), comment="项目类型")
|
||||
account = db.Column(db.String(20), comment="关联宽带账号")
|
||||
device_type = db.Column(db.String(50), comment="设备类型")
|
||||
puid = db.Column(db.String(100), comment="通道标识") # 添加 puid 字段
|
||||
remark = db.Column(db.String(200), comment="备注")
|
||||
in_use = db.Column(db.Boolean, default=True, comment="是否在用")
|
||||
is_normal = db.Column(db.Boolean, default=True, comment="是否正常")
|
||||
|
||||
# 工单表
|
||||
class WorkOrder(db.Model):
|
||||
__tablename__ = 'work_order'
|
||||
order_id = db.Column(db.String(20), primary_key=True, comment="工单号(日期+编号)")
|
||||
device_id = db.Column(db.String(100), db.ForeignKey('device.device_id'), comment="设备编号") # 从20改为100
|
||||
dispatch_time = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), comment="派单时间")
|
||||
status = db.Column(db.String(20), default='待处理', comment="工单状态")
|
||||
branch = db.Column(db.String(50), comment="所属支局")
|
||||
# 与设备表的关系
|
||||
device = db.relationship('Device', backref='work_orders')
|
||||
|
||||
# 处理工单信息表
|
||||
class WorkOrderHandling(db.Model):
|
||||
__tablename__ = 'work_order_handling'
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
order_id = db.Column(db.String(20), db.ForeignKey('work_order.order_id'), comment="工单号")
|
||||
device_id = db.Column(db.String(100), db.ForeignKey('device.device_id'), comment="设备编号") # 从20改为100
|
||||
branch = db.Column(db.String(50), comment="所属支局")
|
||||
restore_time = db.Column(db.DateTime, comment="恢复时间")
|
||||
fault_type = db.Column(db.String(100), comment="故障类型")
|
||||
contact_person = db.Column(db.String(50), comment="故障对接人")
|
||||
contact_phone = db.Column(db.String(20), comment="联系电话")
|
||||
handler = db.Column(db.String(50), comment="故障处理人")
|
||||
handle_description = db.Column(db.String(500), comment="处理描述")
|
||||
photo_paths = db.Column(db.String(500), comment="现场图片路径")
|
||||
distance_from_branch = db.Column(db.Float, comment="距离支局公里数")
|
||||
remark = db.Column(db.String(500), comment="备注")
|
||||
# 与工单表的关系
|
||||
work_order = db.relationship('WorkOrder', backref='handling_info')
|
||||
|
||||
# 手工故障单表
|
||||
class ManualWorkOrder(db.Model):
|
||||
__tablename__ = 'manual_work_order'
|
||||
order_id = db.Column(db.String(20), primary_key=True, comment="工单号")
|
||||
project_type = db.Column(db.String(50), comment="项目类型")
|
||||
device_id = db.Column(db.String(100), comment="故障设备")
|
||||
device_location = db.Column(db.String(200), comment="设备位置")
|
||||
fault_time = db.Column(db.DateTime, comment="故障时间")
|
||||
handler = db.Column(db.String(50), comment="故障处理人")
|
||||
fault_type = db.Column(db.String(100), comment="故障类型")
|
||||
handle_description = db.Column(db.String(500), comment="处理描述")
|
||||
photo_paths = db.Column(db.String(500), comment="现场图片路径")
|
||||
contact_person = db.Column(db.String(50), comment="故障对接人")
|
||||
contact_phone = db.Column(db.String(20), comment="联系电话")
|
||||
distance_from_branch = db.Column(db.Float, comment="距离支局公里数")
|
||||
remark = db.Column(db.String(500), comment="备注")
|
||||
create_time = db.Column(db.DateTime, default=datetime.now, comment="创建时间")
|
||||
branch = db.Column(db.String(50), comment="所属支局")
|
||||
|
||||
class Channel(db.Model):
|
||||
__tablename__ = 'channels'
|
||||
|
||||
puid = db.Column(db.String(50), primary_key=True)
|
||||
pu_name = db.Column(db.String(200))
|
||||
status = db.Column(db.String(20))
|
||||
update_time = db.Column(db.String(50))
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Channel {self.puid}>'
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
+269
@@ -0,0 +1,269 @@
|
||||
import requests
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
import uuid
|
||||
from sqlalchemy import select, create_engine, Table, Column, String, MetaData, text
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
class LoginClient:
|
||||
def __init__(self):
|
||||
self.base_url = "https://116.10.244.81:28001"
|
||||
self.session = requests.Session()
|
||||
# 禁用SSL验证警告
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
|
||||
def get_captcha(self):
|
||||
"""获取验证码图片"""
|
||||
# 生成随机cid
|
||||
cid = uuid.uuid4().hex.encode()
|
||||
cid = hashlib.sha256(cid).hexdigest()
|
||||
|
||||
url = f"{self.base_url}/api/vss-auth-center/v1/captcha"
|
||||
headers = {
|
||||
"Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0",
|
||||
"Referer": f"{self.base_url}/vss-em-iui/"
|
||||
}
|
||||
params = {"cid": cid}
|
||||
|
||||
response = self.session.get(url, headers=headers, params=params, verify=False)
|
||||
|
||||
# 缓存验证码到本地
|
||||
captcha_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
|
||||
'static', 'captcha.png')
|
||||
with open(captcha_path, "wb") as f:
|
||||
f.write(response.content)
|
||||
|
||||
return cid
|
||||
|
||||
def login(self, account, password, validcode, cid):
|
||||
"""执行登录"""
|
||||
url = f"{self.base_url}/api/vss-auth-center/v1/user/login"
|
||||
|
||||
headers = {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0",
|
||||
"Referer": f"{self.base_url}/vss-em-iui/",
|
||||
"language": "zh-CN"
|
||||
}
|
||||
|
||||
params = {
|
||||
"cutype": "12"
|
||||
}
|
||||
|
||||
data = {
|
||||
"account": account,
|
||||
"password": password,
|
||||
"validcode": validcode,
|
||||
"cid": cid
|
||||
}
|
||||
|
||||
response = self.session.post(
|
||||
url,
|
||||
headers=headers,
|
||||
params=params,
|
||||
json=data,
|
||||
verify=False
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_online_info(self, token, page=1, page_size=1500):
|
||||
"""获取在线信息"""
|
||||
url = f"{self.base_url}/api/vss-user-business/v1/pus/queryPage/4109"
|
||||
|
||||
headers = {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
||||
"Authoration": token,
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"Content-Type": "application/json",
|
||||
"Referer": f"{self.base_url}/vss-em-iui/",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Edg/135.0.0.0",
|
||||
"language": "zh-CN"
|
||||
}
|
||||
|
||||
# 生成防伪标识
|
||||
forgery_defense = uuid.uuid4().hex
|
||||
|
||||
params = {
|
||||
"page_size": page_size,
|
||||
"page": page,
|
||||
"pu_name": "",
|
||||
"puid": "",
|
||||
"protocol": "",
|
||||
"nodeid": "",
|
||||
"popunitid": "",
|
||||
"status": "",
|
||||
"forgerydefense": forgery_defense,
|
||||
"csrftoken": forgery_defense
|
||||
}
|
||||
|
||||
response = self.session.get(
|
||||
url,
|
||||
headers=headers,
|
||||
params=params,
|
||||
verify=False
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_existing_channels(engine, channels_table):
|
||||
"""获取数据库中现有的通道状态"""
|
||||
with engine.connect() as conn:
|
||||
query = select(channels_table.c.puid, channels_table.c.status)
|
||||
result = conn.execute(query)
|
||||
return {row.puid: row.status for row in result}
|
||||
|
||||
def update_device_status(engine):
|
||||
"""更新设备状态"""
|
||||
with engine.connect() as conn:
|
||||
# 获取channels表和device表的数据
|
||||
channels_query = """
|
||||
SELECT puid, status, pu_name
|
||||
FROM channels
|
||||
"""
|
||||
channels_result = conn.execute(text(channels_query))
|
||||
channels_data = {row.puid: {'status': row.status, 'pu_name': row.pu_name} for row in channels_result}
|
||||
|
||||
# 获取device表中的puid
|
||||
device_query = """
|
||||
SELECT puid
|
||||
FROM device
|
||||
"""
|
||||
device_result = conn.execute(text(device_query))
|
||||
|
||||
# 更新设备状态
|
||||
updates = []
|
||||
for row in device_result:
|
||||
puid = row.puid
|
||||
if puid in channels_data:
|
||||
# 更新在线状态
|
||||
is_normal = 1 if channels_data[puid]['status'] == 'online' else 0
|
||||
|
||||
# 检查使用状态
|
||||
pu_name = channels_data[puid]['pu_name']
|
||||
in_use = 0 if any(keyword in pu_name for keyword in ['无学生', '丢失']) else 1
|
||||
|
||||
# 构建更新语句
|
||||
update_query = """
|
||||
UPDATE device
|
||||
SET is_normal = :is_normal, in_use = :in_use
|
||||
WHERE puid = :puid
|
||||
"""
|
||||
updates.append({
|
||||
'puid': puid,
|
||||
'is_normal': is_normal,
|
||||
'in_use': in_use
|
||||
})
|
||||
|
||||
if updates:
|
||||
update_stmt = text("""
|
||||
UPDATE device
|
||||
SET is_normal = :is_normal, in_use = :in_use
|
||||
WHERE puid = :puid
|
||||
""")
|
||||
conn.execute(update_stmt, updates)
|
||||
conn.commit()
|
||||
print(f"成功更新 {len(updates)} 个设备状态")
|
||||
|
||||
def main():
|
||||
client = LoginClient()
|
||||
|
||||
while True:
|
||||
cid = client.get_captcha()
|
||||
print("验证码已保存为captcha.png")
|
||||
validcode = input("请查看captcha.png并输入验证码: ")
|
||||
password = "Root1234#"
|
||||
hashed_password = hashlib.sha256(password.encode()).hexdigest()
|
||||
|
||||
result = client.login(
|
||||
account="DHDX001",
|
||||
password=hashed_password,
|
||||
validcode=validcode,
|
||||
cid=cid
|
||||
)
|
||||
|
||||
if result.get("result") == 0:
|
||||
token = result.get("token")
|
||||
|
||||
online_info = client.get_online_info(token)
|
||||
if online_info.get("result") == 0:
|
||||
rows = online_info.get("rows", [])
|
||||
filtered_rows = [
|
||||
row for row in rows
|
||||
if "大化4+N摄像头" not in row.get("pu_name", "")
|
||||
and "大化幼儿园监控" not in row.get("pu_name", "")
|
||||
]
|
||||
|
||||
engine = create_engine('mysql+pymysql://4an:GtbJp6Ai5azntnTB@10.10.10.253/4an')
|
||||
metadata = MetaData()
|
||||
channels = Table(
|
||||
'channels', metadata,
|
||||
Column('puid', String(50), primary_key=True),
|
||||
Column('pu_name', String(200)),
|
||||
Column('status', String(20)),
|
||||
Column('update_time', String(50))
|
||||
)
|
||||
metadata.create_all(engine)
|
||||
|
||||
existing_channels = get_existing_channels(engine, channels)
|
||||
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
update_data = []
|
||||
|
||||
for row in filtered_rows:
|
||||
puid = row.get('puid')
|
||||
current_status = row.get('status')
|
||||
|
||||
if puid not in existing_channels or existing_channels[puid] != current_status:
|
||||
|
||||
update_data.append({
|
||||
'puid': puid,
|
||||
'pu_name': row.get('pu_name'),
|
||||
'status': current_status,
|
||||
'update_time': current_time
|
||||
})
|
||||
|
||||
# 执行更新
|
||||
if update_data:
|
||||
with engine.connect() as conn:
|
||||
stmt = insert(channels).on_duplicate_key_update(
|
||||
pu_name=channels.c.pu_name,
|
||||
status=channels.c.status,
|
||||
update_time=channels.c.update_time
|
||||
)
|
||||
conn.execute(stmt, update_data)
|
||||
conn.commit()
|
||||
print(f"\n成功更新 {len(update_data)} 条记录")
|
||||
else:
|
||||
print("\n没有需要更新的记录")
|
||||
|
||||
# 更新设备状态
|
||||
print("\n开始更新设备状态...")
|
||||
update_device_status(engine)
|
||||
|
||||
return
|
||||
else:
|
||||
print("获取通道信息失败!")
|
||||
print(f"错误信息: {online_info.get('err_msg', '未知错误')}")
|
||||
return # 获取信息失败也退出程序
|
||||
|
||||
else:
|
||||
print("登录失败!")
|
||||
print(result)
|
||||
if result.get("result") == 12: # 验证码错误
|
||||
retry = input("验证码错误,是否重新获取验证码?(y/n): ")
|
||||
if retry.lower() != 'y':
|
||||
return
|
||||
else:
|
||||
return # 其他错误直接退出
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+110
@@ -0,0 +1,110 @@
|
||||
from datetime import datetime, timedelta
|
||||
from collections import defaultdict
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加项目根目录到 Python 路径
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from scripts.models import db, Device, WorkOrder, WorkOrderHandling, ManualWorkOrder
|
||||
from utils.lzmx import send_weekly_stats_message
|
||||
from flask import Flask
|
||||
|
||||
# 创建一个简单的 Flask 应用
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://4an:GtbJp6Ai5azntnTB@10.10.10.253/4an'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
LZMX_WEBHOOK_URL = "http://imtwo.zdxlz.com/im-external/v1/webhook/send?key=ef1dc605827f425e96491ce5725b5274"
|
||||
|
||||
# 初始化数据库
|
||||
db.init_app(app)
|
||||
|
||||
def get_reward_by_distance(project_type, distance):
|
||||
if project_type == '校园安防"4+N"项目':
|
||||
if distance <= 5: return 20
|
||||
elif distance <= 30: return 40
|
||||
elif distance <= 50: return 50
|
||||
else: return 60
|
||||
elif project_type == '综治视联网':
|
||||
if distance <= 10: return 30
|
||||
elif distance <= 30: return 40
|
||||
elif distance <= 50: return 50
|
||||
else: return 60
|
||||
elif project_type == '教育城域网':
|
||||
if distance <= 5: return 20
|
||||
elif distance <= 30: return 30
|
||||
elif distance <= 50: return 40
|
||||
else: return 50
|
||||
elif project_type == '其他':
|
||||
return 30
|
||||
return 0
|
||||
|
||||
def weekly_stats():
|
||||
with app.app_context():
|
||||
# 计算本周的开始和结束时间
|
||||
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 = now
|
||||
|
||||
# 统计工单数据
|
||||
total_orders = WorkOrder.query.filter(
|
||||
WorkOrder.dispatch_time >= week_start,
|
||||
WorkOrder.dispatch_time < week_end
|
||||
).count()
|
||||
|
||||
completed_orders = WorkOrder.query.filter(
|
||||
WorkOrder.dispatch_time >= week_start,
|
||||
WorkOrder.dispatch_time < week_end,
|
||||
WorkOrder.status == '已处理'
|
||||
).count()
|
||||
|
||||
manual_orders = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= week_start,
|
||||
ManualWorkOrder.create_time < week_end
|
||||
).count()
|
||||
|
||||
# 获取处理人统计
|
||||
handler_stats = defaultdict(float)
|
||||
|
||||
# 统计普通工单奖励
|
||||
handlings = WorkOrderHandling.query.filter(
|
||||
WorkOrderHandling.restore_time >= week_start,
|
||||
WorkOrderHandling.restore_time < week_end
|
||||
).all()
|
||||
|
||||
for handling in handlings:
|
||||
device = db.session.get(Device, handling.device_id) # 更新为新的方法
|
||||
if device:
|
||||
distance = float(handling.distance_from_branch)
|
||||
reward = get_reward_by_distance(device.project_type, distance)
|
||||
handler_stats[handling.handler] += reward
|
||||
|
||||
# 统计手工故障单奖励
|
||||
manual_orders_list = ManualWorkOrder.query.filter(
|
||||
ManualWorkOrder.create_time >= week_start,
|
||||
ManualWorkOrder.create_time < week_end
|
||||
).all()
|
||||
|
||||
for order in manual_orders_list:
|
||||
distance = float(order.distance_from_branch)
|
||||
reward = get_reward_by_distance(order.project_type, distance)
|
||||
handler_stats[order.handler] += reward
|
||||
|
||||
# 获取前三名
|
||||
top_handlers = sorted(handler_stats.items(), key=lambda x: x[1], reverse=True)[:3]
|
||||
|
||||
# 准备统计数据
|
||||
stats = {
|
||||
'total_orders': total_orders,
|
||||
'completed_orders': completed_orders,
|
||||
'pending_orders': total_orders - completed_orders,
|
||||
'manual_orders': manual_orders,
|
||||
'top_handlers': top_handlers
|
||||
}
|
||||
|
||||
# 发送统计消息
|
||||
send_weekly_stats_message(LZMX_WEBHOOK_URL, stats)
|
||||
|
||||
if __name__ == '__main__':
|
||||
weekly_stats()
|
||||
Reference in New Issue
Block a user