可以成功动态更换yolo模型并重启服务生效

This commit is contained in:
2025-09-12 18:28:43 +08:00
parent 4be7f7bf14
commit 206652d6bb
6 changed files with 499 additions and 123 deletions

View File

@ -1,10 +1,14 @@
import json
from datetime import datetime, date
from fastapi import APIRouter, Query, HTTPException,Request
from fastapi import APIRouter, Query, HTTPException, Request, Path
from mysql.connector import Error as MySQLError
from ds.db import db
from schema.device_schema import DeviceCreateRequest, DeviceResponse, DeviceListResponse
from schema.device_schema import (
DeviceCreateRequest, DeviceResponse, DeviceListResponse,
DeviceStatusHistoryResponse, DeviceStatusHistoryListResponse
)
from schema.response_schema import APIResponse
router = APIRouter(
@ -13,12 +17,53 @@ router = APIRouter(
)
# ------------------------------
# 内部工具方法 - 记录设备状态变更历史
# ------------------------------
def record_status_change(client_ip: str, status: int) -> bool:
"""
记录设备状态变更历史(写入 device_action 表)
:param client_ip: 设备IP
:param status: 状态1-在线、0-离线)
:return: 操作是否成功
"""
if not client_ip:
raise ValueError("客户端IP不能为空")
if status not in (0, 1):
raise ValueError("状态必须是0离线或1在线")
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 插入状态变更记录到 device_action
insert_query = """
INSERT INTO device_action
(client_ip, action, created_at, updated_at)
VALUES (%s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
"""
cursor.execute(insert_query, (client_ip, status))
conn.commit()
return True
except MySQLError as e:
if conn:
conn.rollback()
raise Exception(f"记录设备状态变更失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 内部工具方法 - 通过客户端IP增加设备报警次数
# ------------------------------
def increment_alarm_count_by_ip(client_ip: str) -> bool:
"""
通过客户端IP增加设备的报警次数(内部服务方法)
通过客户端IP增加设备的报警次数
:param client_ip: 客户端IP地址
:return: 操作是否成功
@ -34,7 +79,8 @@ def increment_alarm_count_by_ip(client_ip: str) -> bool:
# 检查设备是否存在
cursor.execute("SELECT id FROM devices WHERE client_ip = %s", (client_ip,))
if not cursor.fetchone():
device = cursor.fetchone()
if not device:
raise ValueError(f"客户端IP为 {client_ip} 的设备不存在")
# 报警次数加1、并更新时间戳
@ -61,7 +107,7 @@ def increment_alarm_count_by_ip(client_ip: str) -> bool:
# ------------------------------
def update_online_status_by_ip(client_ip: str, online_status: int) -> bool:
"""
通过客户端IP更新设备的在线状态(内部服务方法)
通过客户端IP更新设备的在线状态
:param client_ip: 客户端IP地址
:param online_status: 在线状态1-在线、0-离线)
@ -70,7 +116,6 @@ def update_online_status_by_ip(client_ip: str, online_status: int) -> bool:
if not client_ip:
raise ValueError("客户端IP不能为空")
# 验证状态值有效性
if online_status not in (0, 1):
raise ValueError("在线状态必须是0离线或1在线")
@ -80,11 +125,16 @@ def update_online_status_by_ip(client_ip: str, online_status: int) -> bool:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 检查设备是否存在
cursor.execute("SELECT id FROM devices WHERE client_ip = %s", (client_ip,))
if not cursor.fetchone():
# 检查设备是否存在并获取设备ID
cursor.execute("SELECT id, device_online_status FROM devices WHERE client_ip = %s", (client_ip,))
device = cursor.fetchone()
if not device:
raise ValueError(f"客户端IP为 {client_ip} 的设备不存在")
# 状态无变化则不操作
if device['device_online_status'] == online_status:
return True
# 更新在线状态和时间戳
update_query = """
UPDATE devices
@ -93,8 +143,11 @@ def update_online_status_by_ip(client_ip: str, online_status: int) -> bool:
WHERE client_ip = %s
"""
cursor.execute(update_query, (online_status, client_ip))
conn.commit()
# 记录状态变更历史
record_status_change(client_ip, online_status)
conn.commit()
return True
except MySQLError as e:
if conn:
@ -105,46 +158,43 @@ def update_online_status_by_ip(client_ip: str, online_status: int) -> bool:
# ------------------------------
# 原有接口保持不变
# 创建设备信息接口
# ------------------------------
@router.post("/add", response_model=APIResponse, summary="创建设备信息")
async def create_device(device_data: DeviceCreateRequest, request: Request): # 注入Request对象
# 原有代码保持不变
async def create_device(device_data: DeviceCreateRequest, request: Request):
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 检查设备是否已存在
cursor.execute("SELECT * FROM devices WHERE client_ip = %s", (device_data.ip,))
existing_device = cursor.fetchone()
if existing_device:
# 更新设备状态为在线
# 更新设备为在线状态
update_online_status_by_ip(client_ip=device_data.ip, online_status=1)
# 返回信息
return APIResponse(
code=200,
message=f"设备IP {device_data.ip} 已存在、返回已有设备信息",
data=DeviceResponse(** existing_device)
data=DeviceResponse(**existing_device)
)
# 直接使用注入的request对象获取用户代理
# 通过 User-Agent 判断设备类型
user_agent = request.headers.get("User-Agent", "").lower()
device_type = "unknown"
if user_agent == "default":
device_type = device_data.params.get("os") if (
device_data.params and isinstance(device_data.params, dict)) else "unknown"
device_type = device_data.params.get("os") if (device_data.params and isinstance(device_data.params, dict)) else "unknown"
elif "windows" in user_agent:
device_type = "windows"
elif "android" in user_agent:
device_type = "android"
elif "linux" in user_agent:
device_type = "linux"
else:
device_type = "unknown"
device_params_json = json.dumps(device_data.params) if device_data.params else None
# 插入新设备
insert_query = """
INSERT INTO devices
(client_ip, hostname, device_online_status, device_type, alarm_count, params)
@ -160,10 +210,14 @@ async def create_device(device_data: DeviceCreateRequest, request: Request): #
))
conn.commit()
# 获取新设备并返回
device_id = cursor.lastrowid
cursor.execute("SELECT * FROM devices WHERE id = %s", (device_id,))
new_device = cursor.fetchone()
# 记录上线历史
record_status_change(device_data.ip, 1)
return APIResponse(
code=200,
message="设备创建成功",
@ -175,7 +229,7 @@ async def create_device(device_data: DeviceCreateRequest, request: Request): #
conn.rollback()
raise Exception(f"创建设备失败: {str(e)}") from e
except json.JSONDecodeError as e:
raise Exception(f"设备详细信息JSON序列化失败: {str(e)}") from e
raise Exception(f"设备参数JSON序列化失败: {str(e)}") from e
except Exception as e:
if conn:
conn.rollback()
@ -183,14 +237,17 @@ async def create_device(device_data: DeviceCreateRequest, request: Request): #
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 获取设备列表接口
# ------------------------------
@router.get("/", response_model=APIResponse, summary="获取设备列表(支持筛选分页)")
async def get_device_list(
page: int = Query(1, ge=1, description="页码默认第1页"),
page_size: int = Query(10, ge=1, le=100, description="每页条数1-100之间"),
page: int = Query(1, ge=1, description="页码默认第1页"),
page_size: int = Query(10, ge=1, le=100, description="每页条数1-100之间"),
device_type: str = Query(None, description="按设备类型筛选"),
online_status: int = Query(None, ge=0, le=1, description="按在线状态筛选")
):
# 原有代码保持不变
conn = None
cursor = None
try:
@ -207,12 +264,14 @@ async def get_device_list(
where_clause.append("device_online_status = %s")
params.append(online_status)
# 统计总数
count_query = "SELECT COUNT(*) AS total FROM devices"
if where_clause:
count_query += " WHERE " + " AND ".join(where_clause)
cursor.execute(count_query, params)
total = cursor.fetchone()["total"]
# 分页查询列表
offset = (page - 1) * page_size
list_query = "SELECT * FROM devices"
if where_clause:
@ -238,23 +297,140 @@ async def get_device_list(
db.close_connection(conn, cursor)
def get_unique_client_ips() -> list[str]:
"""
获取所有去重的客户端IP列表
:return: 去重后的客户端IP字符串列表如果没有数据则返回空列表
"""
# ------------------------------
# 获取设备上下线记录接口
# ------------------------------
@router.get("/{device_id}/status-history", response_model=APIResponse, summary="获取设备上下线记录")
async def get_device_status_history(
device_id: int = Path(..., description="设备ID"),
page: int = Query(1, ge=1, description="页码默认第1页"),
page_size: int = Query(10, ge=1, le=100, description="每页条数1-100之间"),
start_date: date = Query(None, description="开始日期格式YYYY-MM-DD"),
end_date: date = Query(None, description="结束日期格式YYYY-MM-DD")
):
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 检查设备是否存在并获取 client_ip
cursor.execute("SELECT id, client_ip FROM devices WHERE id = %s", (device_id,))
device = cursor.fetchone()
if not device:
raise HTTPException(status_code=404, detail=f"设备ID为 {device_id} 的设备不存在")
client_ip = device['client_ip']
where_clause = ["client_ip = %s"]
params = [client_ip]
# 日期筛选
if start_date:
where_clause.append("DATE(created_at) >= %s")
params.append(start_date.strftime("%Y-%m-%d"))
if end_date:
where_clause.append("DATE(created_at) <= %s")
params.append(end_date.strftime("%Y-%m-%d"))
# 统计记录总数
count_query = "SELECT COUNT(*) AS total FROM device_action WHERE " + " AND ".join(where_clause)
cursor.execute(count_query, params)
total = cursor.fetchone()["total"]
# 分页查询记录
offset = (page - 1) * page_size
list_query = f"""
SELECT * FROM device_action
WHERE {' AND '.join(where_clause)}
ORDER BY created_at DESC
LIMIT %s OFFSET %s
"""
params.extend([page_size, offset])
cursor.execute(list_query, params)
history_list = cursor.fetchall()
# 格式化为响应模型结构
formatted_history = []
for item in history_list:
formatted_item = {
"id": item["id"],
"device_id": device_id,
"client_ip": item["client_ip"],
"status": item["action"],
"status_time": item["created_at"]
}
formatted_history.append(formatted_item)
return APIResponse(
code=200,
message="获取设备上下线记录成功",
data=DeviceStatusHistoryListResponse(
total=total,
history=[DeviceStatusHistoryResponse(**item) for item in formatted_history]
)
)
except MySQLError as e:
raise Exception(f"获取设备上下线记录失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 手动更新设备在线状态接口
# ------------------------------
@router.put("/{device_id}/status", response_model=APIResponse, summary="更新设备在线状态")
async def update_device_status(
device_id: int = Path(..., description="设备ID"),
status: int = Query(..., ge=0, le=1, description="在线状态1-在线、0-离线)")
):
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 获取设备 client_ip
cursor.execute("SELECT id, client_ip FROM devices WHERE id = %s", (device_id,))
device = cursor.fetchone()
if not device:
raise HTTPException(status_code=404, detail=f"设备ID为 {device_id} 的设备不存在")
# 更新状态
success = update_online_status_by_ip(device['client_ip'], status)
if success:
status_text = "在线" if status == 1 else "离线"
return APIResponse(
code=200,
message=f"设备已更新为{status_text}状态",
data={"device_id": device_id, "status": status, "status_text": status_text}
)
return APIResponse(
code=500,
message="更新设备状态失败",
data=None
)
except MySQLError as e:
raise Exception(f"更新设备状态失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 获取所有去重的客户端IP列表
# ------------------------------
def get_unique_client_ips() -> list[str]:
"""获取所有去重的客户端IP列表"""
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 查询去重的客户端IP
query = "SELECT DISTINCT client_ip FROM devices WHERE client_ip IS NOT NULL"
cursor.execute(query)
# 提取结果并转换为字符串列表
results = cursor.fetchall()
return [item['client_ip'] for item in results]