牧安云哨-后端

This commit is contained in:
BBIT-Kai
2025-12-23 09:55:06 +08:00
parent f018d96e60
commit b5a9714025
16 changed files with 1219 additions and 75 deletions
+123
View File
@@ -0,0 +1,123 @@
from uuid import UUID
from fastapi import APIRouter
from fastapi import Depends
from config.redis import RedisClient
from db.postgres.iot import *
from models.BaseResponse import BaseResponse
from models.EMQXWebhook import EMQXWebhook
from routers.WS import ws_manager
iot_router = APIRouter()
redis_client = RedisClient()
from config.security import get_user_id_from_token
# -------------------- 设备接口 --------------------
@iot_router.post("/common/webhook")
async def emqx_webhook(data: EMQXWebhook):
device_id = data.clientid
event = data.event
if event == "client.connected":
redis_client.set_online(device_id)
await ws_manager.broadcast({"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})
print(f"[OFFLINE] {device_id}")
else:
# 其他事件直接忽略
print(f"[IGNORE] {event}")
return {"ok": True}
@iot_router.get("/common/device/list")
async def get_device_list(
page: int = 1,
pageSize: int = 10,
id: str | None = None,
name: str | None = None,
status: int | None = None,
is_superuser: int | 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"}
devices, total = get_device_list_db_page(
page, pageSize, id, name, status, is_superuser, dept_id, startTime, endTime
)
# ===== 👇 核心:补在线状态 =====
for d in devices:
device_id = d["name"] # 账号
d["online"] = redis_client.is_device_online(device_id) == 1
return BaseResponse(data={"list": devices, "total": total})
@iot_router.post("/common/device")
async def create_device(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
new_id = insert_device(data)
return BaseResponse(data={"id": new_id})
@iot_router.put("/common/device/{id}")
async def update_device(
id: str,
data: dict,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
count = update_device_db(id, data)
if count == 0:
return BaseResponse(status=False, message="设备不存在", data=None)
return BaseResponse(data=True)
@iot_router.patch("/common/device/{id}")
async def patch_device(
id: str,
data: dict,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
count = patch_device_db(id, data)
if count == 0:
return BaseResponse(status=False, message="设备不存在", data=None)
return BaseResponse(data=True)
@iot_router.delete("/common/device/{id}")
async def delete_device(
id: str,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
deleted = delete_device_db(id)
if deleted == 0:
return BaseResponse(status=False, message="设备不存在", data=None)
return BaseResponse(data=True)
+98
View File
@@ -0,0 +1,98 @@
from uuid import UUID
from fastapi import Depends, APIRouter
from config.security import get_user_id_from_token
from db.postgres import get_dept_ids_by_user_id, get_dept_id_by_user_id
from db.postgres.sentinel import *
from models.BaseResponse import BaseResponse
# -------------------- 设备接口 --------------------
sentinel_router = APIRouter()
@sentinel_router.get("/record/list")
async def get_sentinel_record_list(
page: int = 1,
page_size: int = 10,
id: str | None = None,
license_plate: str | None = None,
vehicle_type: str | None = None,
is_inspected: int | None = None,
livestock_type: str = None,
livestock_source: str | None = None,
start_time: str | None = None,
end_time: str | None = None,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
dept_ids = get_dept_ids_by_user_id(user_id)
print(dept_ids) # 输出所有部门的 dept_id 列表
records, total = get_sentinel_record_list_db_page(
page,
page_size,
id,
license_plate,
vehicle_type,
is_inspected,
livestock_source,
livestock_type,
dept_ids,
start_time,
end_time,
)
return BaseResponse(data={"list": records, "total": total})
@sentinel_router.post("/record")
async def create_sentinel_record(
data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
dept_id = get_dept_id_by_user_id(user_id)
new_id = insert_sentinel_record(data, dept_id)
return BaseResponse(data={"id": new_id})
@sentinel_router.put("/record/{id}")
async def update_sentinel_record(
id: str, data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
count = update_sentinel_record_db(id, data)
if count == 0:
return BaseResponse(status=False, message="记录不存在", data=None)
return BaseResponse(data=True)
@sentinel_router.patch("/record/{id}")
async def patch_sentinel_record(
id: str, data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
count = patch_sentinel_record_db(id, data)
if count == 0:
return BaseResponse(status=False, message="记录不存在", data=None)
return BaseResponse(data=True)
@sentinel_router.delete("/record/{id}")
async def delete_sentinel_record(
id: str, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
deleted = delete_sentinel_record_db(id)
if deleted == 0:
return BaseResponse(status=False, message="记录不存在", data=None)
return BaseResponse(data=True)
+118 -19
View File
@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, Query
from config.security import get_user_id_from_token
from db.postgres.system import *
@@ -60,7 +60,7 @@ async def dept_update(
parent_id = data.get("pid")
name = data.get("name")
comment = data.get("comment")
comment = data.get("remark")
rowcount = update_dept(id, parent_id, name, comment)
if rowcount == 0:
@@ -294,12 +294,11 @@ async def delete_role(
@systemRouter.get("/user/name-exists")
async def user_name_exists(
username: str,
id: str | None = None,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
exists = db_user_name_exists(username, id)
exists = db_user_name_exists(username)
return BaseResponse(data=exists)
@@ -333,21 +332,6 @@ async def create_user(data: dict, user_id: UUID = Depends(get_user_id_from_token
return BaseResponse(data={"id": new_id})
@systemRouter.put("/user/{id}")
async def update_user(
id: str,
data: dict,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
count = update_user_db(id, data)
if count == 0:
return BaseResponse(status=False, message="用户不存在", data=None)
return BaseResponse(data=True)
@systemRouter.patch("/user/{id}")
async def patch_user(
id: str,
@@ -375,3 +359,118 @@ async def delete_user(
if deleted == 0:
return BaseResponse(status=False, message="用户不存在", data=None)
return BaseResponse(data=True)
@systemRouter.get("/dict/list")
def getScVideoList(
user_id: UUID = Depends(get_user_id_from_token),
name: str = "",
page: int = Query(1, ge=1),
page_size: int = Query(10, ge=1, le=100),
):
if not user_id:
return {"error": "userId is required"}
total, items = get_dict_list(name, page=page, page_size=page_size)
return BaseResponse(
data={
"total": total,
"items": items,
}
)
@systemRouter.post("/dict")
def create_dict_api(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
"""
创建字典接口
data = { "key": str, "name": str, "remark": str }
"""
if not user_id:
return {"error": "userId is required"}
new_id = db_create_dict(data.get("key"), data.get("name"), data.get("remark"))
return BaseResponse(data={"id": new_id})
@systemRouter.put("/dict/{id}")
def update_dict_api(
id: str, data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
"""
更新字典接口
"""
if not user_id:
return {"error": "userId is required"}
db_update_dict(id, data.get("key"), data.get("name"), data.get("remark"))
return BaseResponse(data={"id": id})
@systemRouter.delete("/dict/{id}")
def delete_dict_api(id: str, user_id: UUID = Depends(get_user_id_from_token)):
"""
删除字典接口
"""
if not user_id:
return {"error": "userId is required"}
db_delete_dict(id)
return BaseResponse(data={"id": id})
@systemRouter.get("/dict/detail/list")
def get_dict_detail_api(dictId: str, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
items = db_get_dict_detail(dictId)
return BaseResponse(data={"list": items})
@systemRouter.post("/dict/detail")
def create_dict_detail_api(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
new_id = db_create_dict_detail(
value=data.get("value"),
dict_id=data.get("dict_id"),
sort=data.get("sort", 0),
pid=data.get("pid"),
remark=data.get("remark"),
)
return BaseResponse(data={"id": new_id})
@systemRouter.put("/dict/detail/{id}")
def update_dict_detail_api(
id: str, data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
db_update_dict_detail(
id=id,
value=data.get("value"),
sort=data.get("sort"),
pid=data.get("pid"),
remark=data.get("remark"),
)
return BaseResponse(data={"id": id})
@systemRouter.delete("/dict/detail/{id}")
def delete_dict_detail_api(id: str, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
db_delete_dict_detail(id)
return BaseResponse(data={"id": id})
@systemRouter.get("/dict/getValue")
def get_dict_detail_by_key(
key: str = Query(..., min_length=1),
):
if not key:
return {"error": "key is required"}
items = get_dict_detail_list_by_key(key)
return BaseResponse(data=items)
+23
View File
@@ -0,0 +1,23 @@
from fastapi import APIRouter
from starlette.websockets import WebSocket, WebSocketDisconnect
from db.postgres.ws_manager import ConnectionManager
ws_manager = ConnectionManager()
iot_ws_router = APIRouter()
@iot_ws_router.websocket("/device-status")
async def websocket_device_status(websocket: WebSocket):
await ws_manager.connect(websocket)
print("[WS] client connected")
try:
while True:
# 这里不需要接收任何消息
await websocket.receive_text()
except WebSocketDisconnect:
await ws_manager.disconnect(websocket)
print("[WS] client disconnected")