2025-09-02 18:51:50 +08:00
|
|
|
|
import json
|
2025-09-03 13:52:24 +08:00
|
|
|
|
|
2025-09-04 22:59:27 +08:00
|
|
|
|
from fastapi import APIRouter, Query, HTTPException,Request
|
2025-09-02 18:51:50 +08:00
|
|
|
|
from mysql.connector import Error as MySQLError
|
|
|
|
|
|
|
|
|
|
from ds.db import db
|
2025-09-04 12:29:27 +08:00
|
|
|
|
from schema.device_schema import DeviceCreateRequest, DeviceResponse, DeviceListResponse
|
2025-09-02 18:51:50 +08:00
|
|
|
|
from schema.response_schema import APIResponse
|
2025-09-02 21:42:09 +08:00
|
|
|
|
|
2025-09-02 18:51:50 +08:00
|
|
|
|
router = APIRouter(
|
|
|
|
|
prefix="/devices",
|
|
|
|
|
tags=["设备管理"]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-09-04 12:29:27 +08:00
|
|
|
|
# ------------------------------
|
|
|
|
|
# 内部工具方法 - 通过客户端IP增加设备报警次数
|
|
|
|
|
# ------------------------------
|
|
|
|
|
def increment_alarm_count_by_ip(client_ip: str) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
通过客户端IP增加设备的报警次数(内部服务方法)
|
|
|
|
|
|
|
|
|
|
:param client_ip: 客户端IP地址
|
|
|
|
|
:return: 操作是否成功
|
|
|
|
|
"""
|
|
|
|
|
if not client_ip:
|
|
|
|
|
raise ValueError("客户端IP不能为空")
|
|
|
|
|
|
|
|
|
|
conn = None
|
|
|
|
|
cursor = None
|
2025-09-02 21:42:09 +08:00
|
|
|
|
try:
|
2025-09-04 12:29:27 +08:00
|
|
|
|
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():
|
|
|
|
|
raise ValueError(f"客户端IP为 {client_ip} 的设备不存在")
|
|
|
|
|
|
2025-09-08 17:34:23 +08:00
|
|
|
|
# 报警次数加1、并更新时间戳
|
2025-09-04 12:29:27 +08:00
|
|
|
|
update_query = """
|
|
|
|
|
UPDATE devices
|
|
|
|
|
SET alarm_count = alarm_count + 1,
|
|
|
|
|
updated_at = CURRENT_TIMESTAMP
|
|
|
|
|
WHERE client_ip = %s
|
|
|
|
|
"""
|
|
|
|
|
cursor.execute(update_query, (client_ip,))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
except MySQLError as e:
|
|
|
|
|
if conn:
|
|
|
|
|
conn.rollback()
|
2025-09-08 17:34:23 +08:00
|
|
|
|
raise Exception(f"更新报警次数失败: {str(e)}") from e
|
2025-09-04 12:29:27 +08:00
|
|
|
|
finally:
|
|
|
|
|
db.close_connection(conn, cursor)
|
2025-09-02 21:42:09 +08:00
|
|
|
|
|
|
|
|
|
|
2025-09-02 18:51:50 +08:00
|
|
|
|
# ------------------------------
|
2025-09-04 12:29:27 +08:00
|
|
|
|
# 内部工具方法 - 通过客户端IP更新设备在线状态
|
|
|
|
|
# ------------------------------
|
|
|
|
|
def update_online_status_by_ip(client_ip: str, online_status: int) -> bool:
|
|
|
|
|
"""
|
|
|
|
|
通过客户端IP更新设备的在线状态(内部服务方法)
|
|
|
|
|
|
|
|
|
|
:param client_ip: 客户端IP地址
|
|
|
|
|
:param online_status: 在线状态(1-在线、0-离线)
|
|
|
|
|
:return: 操作是否成功
|
|
|
|
|
"""
|
|
|
|
|
if not client_ip:
|
|
|
|
|
raise ValueError("客户端IP不能为空")
|
|
|
|
|
|
|
|
|
|
# 验证状态值有效性
|
|
|
|
|
if online_status not in (0, 1):
|
|
|
|
|
raise ValueError("在线状态必须是0(离线)或1(在线)")
|
|
|
|
|
|
|
|
|
|
conn = None
|
|
|
|
|
cursor = None
|
|
|
|
|
try:
|
|
|
|
|
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():
|
|
|
|
|
raise ValueError(f"客户端IP为 {client_ip} 的设备不存在")
|
|
|
|
|
|
|
|
|
|
# 更新在线状态和时间戳
|
|
|
|
|
update_query = """
|
|
|
|
|
UPDATE devices
|
|
|
|
|
SET device_online_status = %s,
|
|
|
|
|
updated_at = CURRENT_TIMESTAMP
|
|
|
|
|
WHERE client_ip = %s
|
|
|
|
|
"""
|
|
|
|
|
cursor.execute(update_query, (online_status, client_ip))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
except MySQLError as e:
|
|
|
|
|
if conn:
|
|
|
|
|
conn.rollback()
|
2025-09-08 17:34:23 +08:00
|
|
|
|
raise Exception(f"更新设备在线状态失败: {str(e)}") from e
|
2025-09-04 12:29:27 +08:00
|
|
|
|
finally:
|
|
|
|
|
db.close_connection(conn, cursor)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------
|
|
|
|
|
# 原有接口保持不变
|
2025-09-02 18:51:50 +08:00
|
|
|
|
# ------------------------------
|
|
|
|
|
@router.post("/add", response_model=APIResponse, summary="创建设备信息")
|
2025-09-04 22:59:27 +08:00
|
|
|
|
async def create_device(device_data: DeviceCreateRequest, request: Request): # 注入Request对象
|
2025-09-04 12:29:27 +08:00
|
|
|
|
# 原有代码保持不变
|
2025-09-02 18:51:50 +08:00
|
|
|
|
conn = None
|
|
|
|
|
cursor = None
|
|
|
|
|
try:
|
|
|
|
|
conn = db.get_connection()
|
|
|
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
|
|
2025-09-02 21:42:09 +08:00
|
|
|
|
cursor.execute("SELECT * FROM devices WHERE client_ip = %s", (device_data.ip,))
|
2025-09-02 18:51:50 +08:00
|
|
|
|
existing_device = cursor.fetchone()
|
|
|
|
|
if existing_device:
|
2025-09-04 12:29:27 +08:00
|
|
|
|
# 更新设备状态为在线
|
|
|
|
|
update_online_status_by_ip(client_ip=device_data.ip, online_status=1)
|
|
|
|
|
# 返回信息
|
2025-09-02 21:42:09 +08:00
|
|
|
|
return APIResponse(
|
|
|
|
|
code=200,
|
2025-09-08 17:34:23 +08:00
|
|
|
|
message=f"设备IP {device_data.ip} 已存在、返回已有设备信息",
|
2025-09-04 22:59:27 +08:00
|
|
|
|
data=DeviceResponse(** existing_device)
|
2025-09-02 21:42:09 +08:00
|
|
|
|
)
|
|
|
|
|
|
2025-09-04 22:59:27 +08:00
|
|
|
|
# 直接使用注入的request对象获取用户代理
|
2025-09-02 18:51:50 +08:00
|
|
|
|
user_agent = request.headers.get("User-Agent", "").lower()
|
|
|
|
|
|
|
|
|
|
if user_agent == "default":
|
2025-09-04 12:29:27 +08:00
|
|
|
|
device_type = device_data.params.get("os") if (
|
|
|
|
|
device_data.params and isinstance(device_data.params, dict)) else "unknown"
|
2025-09-02 18:51:50 +08:00
|
|
|
|
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"
|
|
|
|
|
|
2025-09-04 12:29:27 +08:00
|
|
|
|
device_params_json = json.dumps(device_data.params) if device_data.params else None
|
2025-09-02 21:42:09 +08:00
|
|
|
|
|
2025-09-02 18:51:50 +08:00
|
|
|
|
insert_query = """
|
|
|
|
|
INSERT INTO devices
|
2025-09-04 12:29:27 +08:00
|
|
|
|
(client_ip, hostname, device_online_status, device_type, alarm_count, params)
|
|
|
|
|
VALUES (%s, %s, %s, %s, %s, %s)
|
2025-09-02 18:51:50 +08:00
|
|
|
|
"""
|
|
|
|
|
cursor.execute(insert_query, (
|
|
|
|
|
device_data.ip,
|
|
|
|
|
device_data.hostname,
|
|
|
|
|
1,
|
|
|
|
|
device_type,
|
|
|
|
|
0,
|
|
|
|
|
device_params_json
|
|
|
|
|
))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
device_id = cursor.lastrowid
|
|
|
|
|
cursor.execute("SELECT * FROM devices WHERE id = %s", (device_id,))
|
2025-09-04 12:29:27 +08:00
|
|
|
|
new_device = cursor.fetchone()
|
|
|
|
|
|
2025-09-02 18:51:50 +08:00
|
|
|
|
return APIResponse(
|
|
|
|
|
code=200,
|
2025-09-04 12:29:27 +08:00
|
|
|
|
message="设备创建成功",
|
|
|
|
|
data=DeviceResponse(**new_device)
|
2025-09-02 18:51:50 +08:00
|
|
|
|
)
|
2025-09-04 12:29:27 +08:00
|
|
|
|
|
2025-09-02 18:51:50 +08:00
|
|
|
|
except MySQLError as e:
|
|
|
|
|
if conn:
|
|
|
|
|
conn.rollback()
|
2025-09-08 17:34:23 +08:00
|
|
|
|
raise Exception(f"创建设备失败: {str(e)}") from e
|
2025-09-02 18:51:50 +08:00
|
|
|
|
except json.JSONDecodeError as e:
|
2025-09-08 17:34:23 +08:00
|
|
|
|
raise Exception(f"设备详细信息JSON序列化失败: {str(e)}") from e
|
2025-09-02 18:51:50 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
if conn:
|
|
|
|
|
conn.rollback()
|
|
|
|
|
raise e
|
|
|
|
|
finally:
|
|
|
|
|
db.close_connection(conn, cursor)
|
|
|
|
|
|
2025-09-04 12:29:27 +08:00
|
|
|
|
@router.get("/", response_model=APIResponse, summary="获取设备列表(支持筛选分页)")
|
2025-09-02 18:51:50 +08:00
|
|
|
|
async def get_device_list(
|
2025-09-08 17:34:23 +08:00
|
|
|
|
page: int = Query(1, ge=1, description="页码、默认第1页"),
|
|
|
|
|
page_size: int = Query(10, ge=1, le=100, description="每页条数、1-100之间"),
|
2025-09-04 12:29:27 +08:00
|
|
|
|
device_type: str = Query(None, description="按设备类型筛选"),
|
|
|
|
|
online_status: int = Query(None, ge=0, le=1, description="按在线状态筛选")
|
2025-09-02 18:51:50 +08:00
|
|
|
|
):
|
2025-09-04 12:29:27 +08:00
|
|
|
|
# 原有代码保持不变
|
2025-09-02 18:51:50 +08:00
|
|
|
|
conn = None
|
|
|
|
|
cursor = None
|
|
|
|
|
try:
|
|
|
|
|
conn = db.get_connection()
|
|
|
|
|
cursor = conn.cursor(dictionary=True)
|
|
|
|
|
|
|
|
|
|
where_clause = []
|
|
|
|
|
params = []
|
|
|
|
|
|
|
|
|
|
if device_type:
|
|
|
|
|
where_clause.append("device_type = %s")
|
|
|
|
|
params.append(device_type)
|
|
|
|
|
if online_status is not None:
|
|
|
|
|
where_clause.append("device_online_status = %s")
|
|
|
|
|
params.append(online_status)
|
|
|
|
|
|
2025-09-04 12:29:27 +08:00
|
|
|
|
count_query = "SELECT COUNT(*) AS total FROM devices"
|
2025-09-02 18:51:50 +08:00
|
|
|
|
if where_clause:
|
|
|
|
|
count_query += " WHERE " + " AND ".join(where_clause)
|
|
|
|
|
cursor.execute(count_query, params)
|
|
|
|
|
total = cursor.fetchone()["total"]
|
|
|
|
|
|
|
|
|
|
offset = (page - 1) * page_size
|
2025-09-04 12:29:27 +08:00
|
|
|
|
list_query = "SELECT * FROM devices"
|
2025-09-02 18:51:50 +08:00
|
|
|
|
if where_clause:
|
2025-09-04 12:29:27 +08:00
|
|
|
|
list_query += " WHERE " + " AND ".join(where_clause)
|
|
|
|
|
list_query += " ORDER BY id DESC LIMIT %s OFFSET %s"
|
2025-09-02 18:51:50 +08:00
|
|
|
|
params.extend([page_size, offset])
|
|
|
|
|
|
2025-09-04 12:29:27 +08:00
|
|
|
|
cursor.execute(list_query, params)
|
|
|
|
|
device_list = cursor.fetchall()
|
2025-09-02 18:51:50 +08:00
|
|
|
|
|
|
|
|
|
return APIResponse(
|
|
|
|
|
code=200,
|
|
|
|
|
message="获取设备列表成功",
|
2025-09-04 12:29:27 +08:00
|
|
|
|
data=DeviceListResponse(
|
|
|
|
|
total=total,
|
|
|
|
|
devices=[DeviceResponse(**device) for device in device_list]
|
2025-09-02 18:51:50 +08:00
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
except MySQLError as e:
|
2025-09-08 17:34:23 +08:00
|
|
|
|
raise Exception(f"获取设备列表失败: {str(e)}") from e
|
2025-09-02 18:51:50 +08:00
|
|
|
|
finally:
|
|
|
|
|
db.close_connection(conn, cursor)
|