完善牧安云哨-后端

This commit is contained in:
BBIT-Kai
2025-12-29 16:30:36 +08:00
parent cd7aa35960
commit b9b8d30ebf
23 changed files with 1074 additions and 41 deletions
+138 -5
View File
@@ -1,17 +1,19 @@
import uuid
from uuid import UUID
from fastapi import APIRouter
from fastapi import Depends
from config.redis import RedisClient
from config.emqx import mqtt_publish
from config.minIO import get_upload_token
from config.redis import redis_client
from db.postgres.iot import *
from models.BaseResponse import BaseResponse
from models.EMQXWebhook import EMQXWebhook
from models.IotDeviceCommandRequest import IotDeviceCommandRequest
from routers.WS import ws_manager
iot_router = APIRouter()
redis_client = RedisClient()
from config.security import get_user_id_from_token
# -------------------- 设备接口 --------------------
@@ -25,14 +27,14 @@ async def emqx_webhook(data: EMQXWebhook):
if event == "client.connected":
redis_client.set_online(device_id)
await ws_manager.broadcast({"deviceId": device_id, "online": True})
await ws_manager.noticeOnlineStatus({"deviceId": device_id, "online": True})
print(f"[ONLINE] {device_id}")
elif event == "client.disconnected":
redis_client.set_offline(device_id)
await ws_manager.broadcast({"deviceId": device_id, "online": False})
await ws_manager.noticeOnlineStatus({"deviceId": device_id, "online": False})
print(f"[OFFLINE] {device_id}")
@@ -68,6 +70,19 @@ async def get_device_list(
device_id = d["name"] # 账号
d["online"] = redis_client.is_device_online(device_id) == 1
info_json = redis_client.get_device_info(device_id)
d["version"] = info_json.get("version", "")
d["ip"] = info_json.get("ip", "")
d["hostname"] = info_json.get("hostname", "")
d["mac"] = info_json.get("mac", "")
d["os"] = info_json.get("os", "")
d["cpu"] = info_json.get("cpu", "")
d["memory_total"] = info_json.get("memory_total", "")
d["disk_total"] = info_json.get("disk_total", "")
d["last_seen"] = info_json.get("last_seen", "")
d["project"] = info_json.get("project", "")
d["device_type"] = info_json.get("deviceType", "")
return BaseResponse(data={"list": devices, "total": total})
@@ -121,3 +136,121 @@ async def delete_device(
if deleted == 0:
return BaseResponse(status=False, message="设备不存在", data=None)
return BaseResponse(data=True)
@iot_router.get("/common/update/list")
async def get_update_list(
page: int = 1,
pageSize: int = 10,
id: str | None = None,
code: str | None = None,
dept_id: str | None = None,
startTime: str | None = None,
endTime: str | None = None,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
if code == "" or code is None:
code = None
else:
code = int(code)
updates, total = get_update_list_db_page(
page, pageSize, id, code, dept_id, startTime, endTime
)
return BaseResponse(data={"list": updates, "total": total})
@iot_router.post("/common/update")
async def create_update(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
dept_id = data.get("dept_id")
if not dept_id:
return {"error": "dept_id is required"}
# 前端传来的版本号
try:
new_code = int(data.get("code", 0))
except (TypeError, ValueError):
return BaseResponse(
status=False,
message="无效的版本号",
data=None,
)
# 获取该组织当前最大版本号
max_code = getMaxCodeByDeptId(dept_id)
if new_code <= max_code:
return BaseResponse(
status=False,
message=f"新版本号必须大于当前最大版本号 {max_code}",
data=None,
)
# 插入数据库
new_id = insert_update(data)
return BaseResponse(data={"id": new_id})
@iot_router.delete("/common/update/{id}")
async def delete_update(
id: str,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
deleted = delete_update_db(id)
if deleted == 0:
return BaseResponse(status=False, message="更新记录不存在", data=None)
return BaseResponse(data=True)
@iot_router.get("/common/update/getUploadUrl")
def getUploadUrl(
user_id: UUID = Depends(get_user_id_from_token),
):
# 生成唯一文件名,避免覆盖
object_name = f"{uuid.uuid4()}"
return BaseResponse(
data={
"uploadUrl": get_upload_token("iot-update", object_name),
"id": object_name,
}
)
@iot_router.get("/common/update/getMaxCodeByDeptId")
def updateGetMaxCodeByDeptId(
user_id: UUID = Depends(get_user_id_from_token),
dept_id: str | None = None,
):
# 生成唯一文件名,避免覆盖
return BaseResponse(data=getMaxCodeByDeptId(dept_id))
@iot_router.get("/common/update/check")
def getUploadUrl(
deviceID: str | None = None,
):
# 生成唯一文件名,避免覆盖
return BaseResponse(data=get_update_package(deviceID))
@iot_router.post("/common/device/command")
async def command(
data: IotDeviceCommandRequest, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
await mqtt_publish(
data.project, "cmd", data.device_type, data.id, data.command, "{}"
)
return BaseResponse(data=None)