完善牧安云哨-后端
This commit is contained in:
+138
-5
@@ -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)
|
||||
|
||||
@@ -3,9 +3,12 @@ import base64
|
||||
from fastapi import APIRouter
|
||||
|
||||
from config.app import F8_SERVER_USER_ID
|
||||
from db.postgres.sentinel import saveSentinelRecord
|
||||
from models.BaseResponse import BaseResponse
|
||||
from models.F8ImageRequest import F8ImageRequest
|
||||
from models.F8ImageRequestV2 import F8ImageRequestV2
|
||||
from models.SentinelRecordRequest import SentinelRecordRequest
|
||||
from service.RabbitMQ import sentinel_new_analysis
|
||||
from service.vision import (
|
||||
process_ticket_image,
|
||||
process_license_image,
|
||||
@@ -78,3 +81,12 @@ async def recognize_silkworm_cocoon(data: F8ImageRequest):
|
||||
return BaseResponse(data=json_data)
|
||||
except Exception as e:
|
||||
return BaseResponse(status=False, message=f"解析失败: {str(e)}", data=None)
|
||||
|
||||
|
||||
@publicRouter.post("/sentinel-record-analytics")
|
||||
async def delete_sentinel_record(data: SentinelRecordRequest):
|
||||
# 保存部分数据到数据库
|
||||
data.Id = saveSentinelRecord(data)
|
||||
# 发送请求给RabbitMQ
|
||||
res = await sentinel_new_analysis(data)
|
||||
return BaseResponse(data=res)
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from models.AnalysisRequest import AnalysisRequest
|
||||
from service.Analyze import mq_new_analysis
|
||||
|
||||
rqRouter = APIRouter()
|
||||
|
||||
|
||||
@rqRouter.post("/analyze")
|
||||
def send_analysis_request(req: AnalysisRequest):
|
||||
mq_new_analysis(req)
|
||||
return {"status": "queued"}
|
||||
# @rqRouter.post("/analyze")
|
||||
# def send_analysis_request(req: AnalysisRequest):
|
||||
# mq_new_analysis(req)
|
||||
# return {"status": "queued"}
|
||||
|
||||
@@ -17,7 +17,7 @@ serviceRouter = APIRouter()
|
||||
|
||||
# 对话列表
|
||||
@serviceRouter.get("/sessionsForService")
|
||||
def getSessions(user_id: UUID = Depends(get_user_id_from_token)):
|
||||
async def getSessions(user_id: UUID = Depends(get_user_id_from_token)):
|
||||
if not user_id:
|
||||
return {"error": "userId is required"}
|
||||
return BaseResponse(data=pg.get_sessions(user_id, "service"))
|
||||
@@ -25,7 +25,7 @@ def getSessions(user_id: UUID = Depends(get_user_id_from_token)):
|
||||
|
||||
# 对话
|
||||
@serviceRouter.post("/chatForService")
|
||||
def chat(req: ChatRequest, user_id: UUID = Depends(get_user_id_from_token)):
|
||||
async def chat(req: ChatRequest, user_id: UUID = Depends(get_user_id_from_token)):
|
||||
if not user_id:
|
||||
return {"error": "userId is required"}
|
||||
if not req.aiId:
|
||||
|
||||
@@ -113,6 +113,9 @@ async def menu_list(plat_id: int, user_id: UUID = Depends(get_user_id_from_token
|
||||
m["createTime"] = format_datetime(m.get("created_at"))
|
||||
m["updateTime"] = format_datetime(m.get("updated_at"))
|
||||
m["children"] = []
|
||||
# 删除created_at updated_at
|
||||
m.pop("createTime", None)
|
||||
m.pop("updateTime", None)
|
||||
|
||||
# 5. 构建菜单树
|
||||
tree = build_menu_tree(menus)
|
||||
|
||||
@@ -162,7 +162,7 @@ def getIVASCUploadToken(
|
||||
):
|
||||
# 生成唯一文件名,避免覆盖
|
||||
object_name = f"raw/{uuid.uuid4()}"
|
||||
return BaseResponse(data=get_upload_token(user_id, "video-sca", object_name))
|
||||
return BaseResponse(data=get_upload_token("video-sca", object_name))
|
||||
|
||||
|
||||
@visionRouter.get("/getScVideoList")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Query
|
||||
from starlette.websockets import WebSocket, WebSocketDisconnect
|
||||
|
||||
from config.security import get_user_id_from_token_from_ws
|
||||
from db.postgres import get_dept_id_by_user_id
|
||||
from db.postgres.ws_manager import ConnectionManager
|
||||
|
||||
ws_manager = ConnectionManager()
|
||||
@@ -10,8 +12,13 @@ iot_ws_router = APIRouter()
|
||||
|
||||
|
||||
@iot_ws_router.websocket("/device-status")
|
||||
async def websocket_device_status(websocket: WebSocket):
|
||||
await ws_manager.connect(websocket)
|
||||
async def websocket_device_status(
|
||||
websocket: WebSocket,
|
||||
token: str = Query(...),
|
||||
):
|
||||
user_id = get_user_id_from_token_from_ws(token)
|
||||
dept_id = get_dept_id_by_user_id(user_id) # 查数据库或缓存
|
||||
await ws_manager.connect(websocket, user_id, dept_id, 0)
|
||||
print("[WS] client connected")
|
||||
|
||||
try:
|
||||
@@ -21,3 +28,22 @@ async def websocket_device_status(websocket: WebSocket):
|
||||
except WebSocketDisconnect:
|
||||
await ws_manager.disconnect(websocket)
|
||||
print("[WS] client disconnected")
|
||||
|
||||
|
||||
@iot_ws_router.websocket("/sentinel_record")
|
||||
async def websocket_sentinel_record(
|
||||
websocket: WebSocket,
|
||||
token: str = Query(...),
|
||||
):
|
||||
user_id = get_user_id_from_token_from_ws(token)
|
||||
dept_id = get_dept_id_by_user_id(user_id) # 查数据库或缓存
|
||||
print("user_id:", user_id)
|
||||
print("dept_id:", dept_id)
|
||||
print("已接入")
|
||||
await ws_manager.connect(websocket, user_id, dept_id, 1)
|
||||
|
||||
try:
|
||||
while True:
|
||||
await websocket.receive_text()
|
||||
except WebSocketDisconnect:
|
||||
await ws_manager.disconnect(websocket)
|
||||
|
||||
Reference in New Issue
Block a user