Files
video/service/device_danger_service.py
2025-09-16 20:17:48 +08:00

267 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from datetime import date
from fastapi import APIRouter, Query, HTTPException, Path
from mysql.connector import Error as MySQLError
from ds.db import db
from encryption.encrypt_decorator import encrypt_response
from schema.device_danger_schema import (
DeviceDangerCreateRequest, DeviceDangerResponse, DeviceDangerListResponse
)
from schema.response_schema import APIResponse
# 路由初始化(前缀与设备管理相关,标签区分功能)
router = APIRouter(
prefix="/api/devices/dangers",
tags=["设备管理-危险记录"]
)
# ------------------------------
# 内部工具方法 - 检查设备是否存在(复用设备表逻辑)
# ------------------------------
def check_device_exist(client_ip: str) -> bool:
"""
检查指定IP的设备是否在devices表中存在
:param client_ip: 设备IP地址
:return: 存在返回True不存在返回False
"""
if not client_ip:
raise ValueError("设备IP不能为空")
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,))
return cursor.fetchone() is not None
except MySQLError as e:
raise Exception(f"检查设备存在性失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 内部工具方法 - 创建设备危险记录(核心插入逻辑)
# ------------------------------
def create_danger_record(danger_data: DeviceDangerCreateRequest) -> DeviceDangerResponse:
"""
内部工具方法向device_danger表插入新的危险记录
:param danger_data: 危险记录创建请求数据
:return: 创建成功的危险记录模型对象
"""
# 先检查设备是否存在
if not check_device_exist(danger_data.client_ip):
raise ValueError(f"IP为 {danger_data.client_ip} 的设备不存在,无法创建危险记录")
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 插入危险记录id自增时间自动填充
insert_query = """
INSERT INTO device_danger
(client_ip, type, result, created_at, updated_at)
VALUES (%s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
"""
cursor.execute(insert_query, (
danger_data.client_ip,
danger_data.type,
danger_data.result
))
conn.commit()
# 获取刚创建的记录用自增ID查询
danger_id = cursor.lastrowid
cursor.execute("SELECT * FROM device_danger WHERE id = %s", (danger_id,))
new_danger = cursor.fetchone()
return DeviceDangerResponse(**new_danger)
except MySQLError as e:
if conn:
conn.rollback()
raise Exception(f"插入危险记录失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 接口1创建设备危险记录
# ------------------------------
@router.post("/add", response_model=APIResponse, summary="创建设备危险记录")
@encrypt_response()
async def add_device_danger(danger_data: DeviceDangerCreateRequest):
try:
# 调用内部方法创建记录
new_danger = create_danger_record(danger_data)
return APIResponse(
code=200,
message=f"设备[{danger_data.client_ip}]危险记录创建成功",
data=new_danger
)
except ValueError as e:
# 设备不存在等业务异常
raise HTTPException(status_code=400, detail=str(e)) from e
except Exception as e:
# 数据库异常等系统错误
raise HTTPException(status_code=500, detail=str(e)) from e
# ------------------------------
# 接口2获取危险记录列表支持多条件筛选+分页)
# ------------------------------
@router.get("/", response_model=APIResponse, summary="获取设备危险记录列表(多条件筛选)")
@encrypt_response()
async def get_danger_list(
page: int = Query(1, ge=1, description="页码默认第1页"),
page_size: int = Query(10, ge=1, le=100, description="每页条数1-100之间"),
client_ip: str = Query(None, max_length=100, description="按设备IP筛选"),
danger_type: str = Query(None, max_length=50, alias="type", description="按危险类型筛选"),
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)
# 构建筛选条件
where_clause = []
params = []
if client_ip:
where_clause.append("client_ip = %s")
params.append(client_ip)
if danger_type:
where_clause.append("type = %s")
params.append(danger_type)
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"))
# 1. 统计符合条件的总记录数
count_query = "SELECT COUNT(*) AS total FROM device_danger"
if where_clause:
count_query += " WHERE " + " AND ".join(where_clause)
cursor.execute(count_query, params)
total = cursor.fetchone()["total"]
# 2. 分页查询记录(按创建时间倒序,最新的在前)
offset = (page - 1) * page_size
list_query = "SELECT * FROM device_danger"
if where_clause:
list_query += " WHERE " + " AND ".join(where_clause)
list_query += " ORDER BY created_at DESC LIMIT %s OFFSET %s"
params.extend([page_size, offset]) # 追加分页参数
cursor.execute(list_query, params)
danger_list = cursor.fetchall()
# 转换为响应模型
return APIResponse(
code=200,
message="获取危险记录列表成功",
data=DeviceDangerListResponse(
total=total,
dangers=[DeviceDangerResponse(**item) for item in danger_list]
)
)
except MySQLError as e:
raise HTTPException(status_code=500, detail=f"查询危险记录失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 接口3获取单个设备的所有危险记录
# ------------------------------
@router.get("/device/{client_ip}", response_model=APIResponse, summary="获取单个设备的所有危险记录")
# @encrypt_response()
async def get_device_dangers(
client_ip: str = Path(..., max_length=100, description="设备IP地址"),
page: int = Query(1, ge=1, description="页码默认第1页"),
page_size: int = Query(10, ge=1, le=100, description="每页条数1-100之间")
):
# 先检查设备是否存在
if not check_device_exist(client_ip):
raise HTTPException(status_code=404, detail=f"IP为 {client_ip} 的设备不存在")
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 1. 统计该设备的危险记录总数
count_query = "SELECT COUNT(*) AS total FROM device_danger WHERE client_ip = %s"
cursor.execute(count_query, (client_ip,))
total = cursor.fetchone()["total"]
# 2. 分页查询该设备的危险记录
offset = (page - 1) * page_size
list_query = """
SELECT * FROM device_danger
WHERE client_ip = %s
ORDER BY created_at DESC
LIMIT %s OFFSET %s
"""
cursor.execute(list_query, (client_ip, page_size, offset))
danger_list = cursor.fetchall()
return APIResponse(
code=200,
message=f"获取设备[{client_ip}]危险记录成功(共{total}条)",
data=DeviceDangerListResponse(
total=total,
dangers=[DeviceDangerResponse(**item) for item in danger_list]
)
)
except MySQLError as e:
raise HTTPException(status_code=500, detail=f"查询设备[{client_ip}]危险记录失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)
# ------------------------------
# 接口4根据ID获取单个危险记录详情
# ------------------------------
@router.get("/{danger_id}", response_model=APIResponse, summary="根据ID获取单个危险记录详情")
@encrypt_response()
async def get_danger_detail(
danger_id: int = Path(..., ge=1, description="危险记录ID")
):
conn = None
cursor = None
try:
conn = db.get_connection()
cursor = conn.cursor(dictionary=True)
# 查询单个危险记录
query = "SELECT * FROM device_danger WHERE id = %s"
cursor.execute(query, (danger_id,))
danger = cursor.fetchone()
if not danger:
raise HTTPException(status_code=404, detail=f"ID为 {danger_id} 的危险记录不存在")
return APIResponse(
code=200,
message="获取危险记录详情成功",
data=DeviceDangerResponse(**danger)
)
except MySQLError as e:
raise HTTPException(status_code=500, detail=f"查询危险记录详情失败: {str(e)}") from e
finally:
db.close_connection(conn, cursor)