牧安云哨-后端
This commit is contained in:
@@ -8,13 +8,16 @@ from config.yolo import YOLOSingleton
|
|||||||
from routers.Bot import botRouter
|
from routers.Bot import botRouter
|
||||||
from routers.Chat import chatRouter
|
from routers.Chat import chatRouter
|
||||||
from routers.Datasource import reportDataRouter
|
from routers.Datasource import reportDataRouter
|
||||||
|
from routers.Iot import iot_router
|
||||||
from routers.Knowledge import knowledgeRouter
|
from routers.Knowledge import knowledgeRouter
|
||||||
from routers.Public import publicRouter
|
from routers.Public import publicRouter
|
||||||
from routers.RabbitMQ import rqRouter
|
from routers.RabbitMQ import rqRouter
|
||||||
from routers.Report import reportRouter
|
from routers.Report import reportRouter
|
||||||
|
from routers.Sentinel import sentinel_router
|
||||||
from routers.Service import serviceRouter
|
from routers.Service import serviceRouter
|
||||||
from routers.System import systemRouter
|
from routers.System import systemRouter
|
||||||
from routers.Vision import visionRouter
|
from routers.Vision import visionRouter
|
||||||
|
from routers.WS import iot_ws_router
|
||||||
|
|
||||||
|
|
||||||
async def ai_lab():
|
async def ai_lab():
|
||||||
@@ -46,6 +49,9 @@ async def ai_lab():
|
|||||||
app.include_router(r, prefix="/llm", tags=["llm"])
|
app.include_router(r, prefix="/llm", tags=["llm"])
|
||||||
app.include_router(visionRouter, prefix="/cv", tags=["cv"])
|
app.include_router(visionRouter, prefix="/cv", tags=["cv"])
|
||||||
app.include_router(systemRouter, prefix="/system", tags=["system"])
|
app.include_router(systemRouter, prefix="/system", tags=["system"])
|
||||||
|
app.include_router(iot_router, prefix="/iot", tags=["iot"])
|
||||||
|
app.include_router(sentinel_router, prefix="/iot/sentinel", tags=["iot_sentinel"])
|
||||||
|
app.include_router(iot_ws_router, prefix="/iot/ws", tags=["iot_ws"])
|
||||||
app.include_router(publicRouter, prefix="/api/public", tags=["api"])
|
app.include_router(publicRouter, prefix="/api/public", tags=["api"])
|
||||||
config = Config(app=app, host="0.0.0.0", port=13011, log_level="debug")
|
config = Config(app=app, host="0.0.0.0", port=13011, log_level="debug")
|
||||||
server = Server(config)
|
server = Server(config)
|
||||||
|
|||||||
@@ -29,6 +29,25 @@ def get_upload_token(user_id, bucket_name, object_name, xpires=timedelta(minutes
|
|||||||
|
|
||||||
|
|
||||||
def get_temp_url(bucket_name, object_name):
|
def get_temp_url(bucket_name, object_name):
|
||||||
|
# 如果 object_name 为 None 或空字符串,则返回默认图片
|
||||||
|
if not object_name or not bucket_name:
|
||||||
|
bucket_name = "system"
|
||||||
|
object_name = "favicon.ico"
|
||||||
|
|
||||||
|
# 使用 presigned_get_object 获取临时 URL
|
||||||
return minio_client.presigned_get_object(
|
return minio_client.presigned_get_object(
|
||||||
bucket_name, object_name, expires=timedelta(seconds=3600)
|
bucket_name, object_name, expires=timedelta(seconds=3600)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_temp_url_dict(bucket_name, object_dict, object_name):
|
||||||
|
# 如果 object_name 为 None 或空字符串,则返回默认图片
|
||||||
|
if not object_name:
|
||||||
|
bucket_name = "system"
|
||||||
|
object_dict = "default"
|
||||||
|
object_name = "favicon.ico"
|
||||||
|
|
||||||
|
# 使用 presigned_get_object 获取临时 URL
|
||||||
|
return minio_client.presigned_get_object(
|
||||||
|
bucket_name, object_dict + "/" + object_name, expires=timedelta(seconds=3600)
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import redis
|
||||||
|
|
||||||
|
|
||||||
|
class RedisClient:
|
||||||
|
|
||||||
|
def __init__(self, config_path="config.yaml"):
|
||||||
|
self.redis = redis.Redis(
|
||||||
|
"10.10.12.101",
|
||||||
|
6379,
|
||||||
|
0,
|
||||||
|
decode_responses=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_online(self, device_id: str):
|
||||||
|
key = f"device:online:{device_id}"
|
||||||
|
self.redis.set(key, 1)
|
||||||
|
|
||||||
|
def set_offline(self, device_id: str):
|
||||||
|
key = f"device:online:{device_id}"
|
||||||
|
self.redis.delete(key)
|
||||||
|
|
||||||
|
def is_device_online(self, device_id: str) -> bool:
|
||||||
|
key = f"device:online:{device_id}"
|
||||||
|
return self.redis.exists(key) == 1
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from .aimessage import *
|
from .aimessage import *
|
||||||
from .aiprofile import *
|
from .aiprofile import *
|
||||||
from .aisession import *
|
from .aisession import *
|
||||||
|
from .iot import *
|
||||||
from .knowledge import *
|
from .knowledge import *
|
||||||
from .license import *
|
from .license import *
|
||||||
from .report import *
|
from .report import *
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
from hashlib import sha256
|
||||||
|
|
||||||
|
from config.pgDb import pg_pool
|
||||||
|
from utils.MyUtils import format_datetime, is_valid_uuid
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_list_db_page(
|
||||||
|
page: int,
|
||||||
|
page_size: int,
|
||||||
|
device_id=None,
|
||||||
|
name=None,
|
||||||
|
status=None,
|
||||||
|
is_superuser=None,
|
||||||
|
dept_id=None,
|
||||||
|
startTime=None,
|
||||||
|
endTime=None,
|
||||||
|
):
|
||||||
|
offset = (page - 1) * page_size
|
||||||
|
|
||||||
|
conditions = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
# ---- 设备 ID(uuid)----
|
||||||
|
if device_id:
|
||||||
|
conditions.append("d.id::text LIKE %s")
|
||||||
|
params.append(f"%{device_id}%")
|
||||||
|
|
||||||
|
# ---- 设备名模糊搜索 ----
|
||||||
|
if name:
|
||||||
|
conditions.append("d.name LIKE %s")
|
||||||
|
params.append(f"%{name}%")
|
||||||
|
|
||||||
|
# ---- 状态 ----
|
||||||
|
if status is not None:
|
||||||
|
conditions.append("d.is_active = %s")
|
||||||
|
params.append(status == 1)
|
||||||
|
|
||||||
|
# ---- 状态 ----
|
||||||
|
if is_superuser is not None:
|
||||||
|
conditions.append("d.is_superuser = %s")
|
||||||
|
params.append(is_superuser == 1)
|
||||||
|
|
||||||
|
# ---- 部门 ----
|
||||||
|
if dept_id and is_valid_uuid(dept_id):
|
||||||
|
conditions.append("d.dept_id = %s")
|
||||||
|
params.append(dept_id)
|
||||||
|
|
||||||
|
# ---- 时间过滤 ----
|
||||||
|
if startTime:
|
||||||
|
conditions.append("d.created_at >= %s")
|
||||||
|
params.append(startTime)
|
||||||
|
|
||||||
|
if endTime:
|
||||||
|
conditions.append("d.created_at <= %s")
|
||||||
|
params.append(endTime)
|
||||||
|
|
||||||
|
where_clause = " WHERE " + " AND ".join(conditions) if conditions else ""
|
||||||
|
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
|
||||||
|
# ---- 统计总数 ----
|
||||||
|
count_sql = f"""
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM iot_users d
|
||||||
|
{where_clause};
|
||||||
|
"""
|
||||||
|
cursor.execute(count_sql, params)
|
||||||
|
total = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# ---- 分页查询 ----
|
||||||
|
list_sql = f"""
|
||||||
|
SELECT
|
||||||
|
d.id,
|
||||||
|
d.name,
|
||||||
|
d.remark,
|
||||||
|
d.is_active,
|
||||||
|
d.is_superuser,
|
||||||
|
d.dept_id,
|
||||||
|
sd.name AS dept_name,
|
||||||
|
d.created_at
|
||||||
|
FROM iot_users d
|
||||||
|
LEFT JOIN sys_dept sd ON d.dept_id = sd.id
|
||||||
|
{where_clause}
|
||||||
|
ORDER BY d.created_at DESC
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.execute(list_sql, params + [page_size, offset])
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in rows:
|
||||||
|
(
|
||||||
|
device_id,
|
||||||
|
name,
|
||||||
|
remark,
|
||||||
|
is_active,
|
||||||
|
is_superuser,
|
||||||
|
dept_id,
|
||||||
|
dept_name,
|
||||||
|
created_at,
|
||||||
|
) = r
|
||||||
|
|
||||||
|
result.append(
|
||||||
|
{
|
||||||
|
"id": device_id,
|
||||||
|
"name": name,
|
||||||
|
"remark": remark,
|
||||||
|
"status": 1 if is_active else 0,
|
||||||
|
"is_superuser": 1 if is_superuser else 0,
|
||||||
|
"dept_id": dept_id,
|
||||||
|
"dept_name": dept_name,
|
||||||
|
"created_at": format_datetime(created_at),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result, total
|
||||||
|
|
||||||
|
|
||||||
|
def insert_device(data: dict) -> str:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
name = data.get("name")
|
||||||
|
salt = name[-4:] if name else ""
|
||||||
|
password = data.get("password", "123456")
|
||||||
|
password_hash = sha256((salt + password).encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO iot_users (name, password_hash, salt, remark, dept_id, is_active, is_superuser)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
||||||
|
RETURNING id;
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
password_hash,
|
||||||
|
salt,
|
||||||
|
data.get("remark"),
|
||||||
|
data.get("dept_id"),
|
||||||
|
bool(data.get("status", 1)),
|
||||||
|
bool(data.get("is_superuser", 0)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
device_id = cursor.fetchone()[0]
|
||||||
|
conn.commit()
|
||||||
|
return device_id
|
||||||
|
|
||||||
|
|
||||||
|
def update_device_db(id: str, data: dict) -> int:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
UPDATE iot_users
|
||||||
|
SET remark=%s, is_active=%s, dept_id=%s, is_superuser=%s
|
||||||
|
WHERE id=%s;
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
data.get("remark"),
|
||||||
|
bool(data.get("status", 1)),
|
||||||
|
data.get("dept_id"),
|
||||||
|
bool(data.get("is_superuser", 0)),
|
||||||
|
id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
|
||||||
|
|
||||||
|
def patch_device_db(id: str, data: dict) -> int:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
fields = []
|
||||||
|
params = []
|
||||||
|
mapping = {
|
||||||
|
"remark": "remark",
|
||||||
|
"status": "is_active",
|
||||||
|
"is_superuser": "is_superuser",
|
||||||
|
"dept_id": "dept_id",
|
||||||
|
}
|
||||||
|
for k, column in mapping.items():
|
||||||
|
if k in data:
|
||||||
|
value = (
|
||||||
|
bool(data[k]) if k in ["status", "is_superuser"] else data[k]
|
||||||
|
)
|
||||||
|
fields.append(f"{column} = %s")
|
||||||
|
params.append(value)
|
||||||
|
|
||||||
|
if fields:
|
||||||
|
sql = f"UPDATE iot_users SET {', '.join(fields)} WHERE id = %s"
|
||||||
|
params.append(id)
|
||||||
|
cursor.execute(sql, tuple(params))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
|
||||||
|
|
||||||
|
def delete_device_db(id: str) -> int:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("DELETE FROM iot_users WHERE id=%s;", (id,))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
from config.minIO import get_temp_url_dict
|
||||||
|
from config.pgDb import pg_pool
|
||||||
|
from utils.MyUtils import format_datetime
|
||||||
|
|
||||||
|
|
||||||
|
def get_sentinel_record_list_db_page(
|
||||||
|
page: int,
|
||||||
|
page_size: int,
|
||||||
|
record_id=None,
|
||||||
|
license_plate=None,
|
||||||
|
vehicle_type=None,
|
||||||
|
is_inspected=None,
|
||||||
|
livestock_source=None,
|
||||||
|
livestock_type=None,
|
||||||
|
dept_ids=None,
|
||||||
|
start_time=None,
|
||||||
|
end_time=None,
|
||||||
|
):
|
||||||
|
offset = (page - 1) * page_size
|
||||||
|
|
||||||
|
conditions = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
# ---- 记录 ID ----
|
||||||
|
if record_id:
|
||||||
|
conditions.append("r.id::text LIKE %s")
|
||||||
|
params.append(f"%{record_id}%")
|
||||||
|
|
||||||
|
# ---- 车牌号 ----
|
||||||
|
if license_plate:
|
||||||
|
conditions.append("r.license_plate LIKE %s")
|
||||||
|
params.append(f"%{license_plate}%")
|
||||||
|
|
||||||
|
# ---- 车型 ----
|
||||||
|
if vehicle_type:
|
||||||
|
conditions.append("r.vehicle_type LIKE %s")
|
||||||
|
params.append(f"%{vehicle_type}%")
|
||||||
|
|
||||||
|
# ---- 是否检查 ----
|
||||||
|
if is_inspected is not None:
|
||||||
|
conditions.append("r.is_inspected = %s")
|
||||||
|
params.append(bool(is_inspected))
|
||||||
|
|
||||||
|
# ---- 来源 ----
|
||||||
|
if livestock_source is not None:
|
||||||
|
conditions.append("r.livestock_source LIKE %s")
|
||||||
|
params.append(f"%{livestock_source}%")
|
||||||
|
|
||||||
|
# ---- 种类 ----
|
||||||
|
if livestock_type is not None:
|
||||||
|
conditions.append("r.livestock_type LIKE %s")
|
||||||
|
params.append(livestock_type)
|
||||||
|
|
||||||
|
# ---- 部门 ----
|
||||||
|
if dept_ids:
|
||||||
|
conditions.append("r.dept_id = ANY(%s)")
|
||||||
|
params.append(dept_ids)
|
||||||
|
|
||||||
|
# ---- 时间过滤 ----
|
||||||
|
if start_time:
|
||||||
|
conditions.append("r.created_at >= %s")
|
||||||
|
params.append(start_time)
|
||||||
|
|
||||||
|
if end_time:
|
||||||
|
conditions.append("r.created_at <= %s")
|
||||||
|
params.append(end_time)
|
||||||
|
|
||||||
|
where_clause = " WHERE " + " AND ".join(conditions) if conditions else ""
|
||||||
|
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
|
||||||
|
# ---- 统计总数 ----
|
||||||
|
count_sql = f"""
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM sentinel_records r
|
||||||
|
{where_clause};
|
||||||
|
"""
|
||||||
|
cursor.execute(count_sql, params)
|
||||||
|
total = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# ---- 分页查询 ----
|
||||||
|
list_sql = f"""
|
||||||
|
SELECT
|
||||||
|
r.id,
|
||||||
|
r.license_plate,
|
||||||
|
r.vehicle_type,
|
||||||
|
r.license_plate_image,
|
||||||
|
r.vehicle_image,
|
||||||
|
r.livestock_type,
|
||||||
|
r.livestock_source,
|
||||||
|
r.is_inspected,
|
||||||
|
r.dept_id,
|
||||||
|
sd.name AS dept_name,
|
||||||
|
r.created_at,
|
||||||
|
r.updated_at,
|
||||||
|
r.remark
|
||||||
|
FROM sentinel_records r
|
||||||
|
LEFT JOIN sys_dept sd ON r.dept_id = sd.id
|
||||||
|
{where_clause}
|
||||||
|
ORDER BY r.created_at DESC
|
||||||
|
LIMIT %s OFFSET %s;
|
||||||
|
"""
|
||||||
|
|
||||||
|
cursor.execute(list_sql, params + [page_size, offset])
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for r in rows:
|
||||||
|
(
|
||||||
|
record_id,
|
||||||
|
license_plate,
|
||||||
|
vehicle_type,
|
||||||
|
license_plate_image,
|
||||||
|
vehicle_image,
|
||||||
|
livestock_type,
|
||||||
|
livestock_source,
|
||||||
|
is_inspected,
|
||||||
|
dept_id,
|
||||||
|
dept_name,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
remark,
|
||||||
|
) = r
|
||||||
|
|
||||||
|
result.append(
|
||||||
|
{
|
||||||
|
"id": record_id,
|
||||||
|
"license_plate": license_plate,
|
||||||
|
"vehicle_type": vehicle_type,
|
||||||
|
"license_plate_image": get_temp_url_dict(
|
||||||
|
"sentinel", "license_plate", license_plate_image
|
||||||
|
),
|
||||||
|
"vehicle_image": get_temp_url_dict(
|
||||||
|
"sentinel", "vehicle_image", vehicle_image
|
||||||
|
),
|
||||||
|
"livestock_type": livestock_type,
|
||||||
|
"livestock_source": livestock_source,
|
||||||
|
"is_inspected": 1 if is_inspected else 0,
|
||||||
|
"dept_id": dept_id,
|
||||||
|
"dept_name": dept_name,
|
||||||
|
"created_at": format_datetime(created_at),
|
||||||
|
"updated_at": format_datetime(updated_at),
|
||||||
|
"remark": remark,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result, total
|
||||||
|
|
||||||
|
|
||||||
|
def insert_sentinel_record(data: dict, dept_id) -> str:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO sentinel_records (
|
||||||
|
license_plate,
|
||||||
|
vehicle_type,
|
||||||
|
license_plate_image,
|
||||||
|
vehicle_image,
|
||||||
|
livestock_type,
|
||||||
|
livestock_source,
|
||||||
|
is_inspected,
|
||||||
|
dept_id,
|
||||||
|
remark,
|
||||||
|
created_by
|
||||||
|
)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
|
RETURNING id;
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
data.get("license_plate"),
|
||||||
|
data.get("vehicle_type"),
|
||||||
|
data.get("license_plate_image"),
|
||||||
|
data.get("vehicle_image"),
|
||||||
|
data.get("livestock_type"),
|
||||||
|
data.get("livestock_source"),
|
||||||
|
bool(data.get("is_inspected", False)),
|
||||||
|
dept_id,
|
||||||
|
data.get("remark"),
|
||||||
|
data.get("created_by"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
record_id = cursor.fetchone()[0]
|
||||||
|
conn.commit()
|
||||||
|
return record_id
|
||||||
|
|
||||||
|
|
||||||
|
def update_sentinel_record_db(id: str, data: dict) -> int:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
UPDATE sentinel_records
|
||||||
|
SET
|
||||||
|
license_plate = %s,
|
||||||
|
vehicle_type = %s,
|
||||||
|
license_plate_image = %s,
|
||||||
|
vehicle_image = %s,
|
||||||
|
livestock_type = %s,
|
||||||
|
livestock_source = %s,
|
||||||
|
is_inspected = %s,
|
||||||
|
remark = %s,
|
||||||
|
updated_at = now()
|
||||||
|
WHERE id = %s;
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
data.get("license_plate"),
|
||||||
|
data.get("vehicle_type"),
|
||||||
|
data.get("license_plate_image"),
|
||||||
|
data.get("vehicle_image"),
|
||||||
|
data.get("livestock_type"),
|
||||||
|
data.get("livestock_source"),
|
||||||
|
bool(data.get("is_inspected", False)),
|
||||||
|
data.get("remark"),
|
||||||
|
id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
|
||||||
|
|
||||||
|
def patch_sentinel_record_db(id: str, data: dict) -> int:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
fields = []
|
||||||
|
params = []
|
||||||
|
mapping = {
|
||||||
|
"license_plate": "license_plate",
|
||||||
|
"vehicle_type": "vehicle_type",
|
||||||
|
"license_plate_image": "license_plate_image",
|
||||||
|
"vehicle_image": "vehicle_image",
|
||||||
|
"livestock_type": "livestock_type",
|
||||||
|
"livestock_source": "livestock_source",
|
||||||
|
"is_inspected": "is_inspected",
|
||||||
|
"remark": "remark",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, column in mapping.items():
|
||||||
|
if k in data:
|
||||||
|
value = data[k]
|
||||||
|
# 如果字段是 "is_inspected",将其转换为布尔类型
|
||||||
|
if k == "is_inspected":
|
||||||
|
value = bool(value) # 转换为布尔值
|
||||||
|
fields.append(f"{column} = %s")
|
||||||
|
params.append(value)
|
||||||
|
|
||||||
|
if fields:
|
||||||
|
sql = f"UPDATE sentinel_records SET {', '.join(fields)} WHERE id = %s"
|
||||||
|
params.append(id)
|
||||||
|
cursor.execute(sql, tuple(params))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
|
||||||
|
|
||||||
|
def delete_sentinel_record_db(id: str) -> int:
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("DELETE FROM sentinel_records WHERE id=%s;", (id,))
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from config.pgDb import pg_pool
|
from config.pgDb import pg_pool
|
||||||
|
from utils import MyUtils
|
||||||
from utils.MyUtils import format_datetime, is_valid_uuid
|
from utils.MyUtils import format_datetime, is_valid_uuid
|
||||||
|
|
||||||
|
|
||||||
@@ -387,7 +388,7 @@ def get_role_list_db_page(page: int, page_size: int, rid=None, name=None, remark
|
|||||||
"id": role_id,
|
"id": role_id,
|
||||||
"name": name,
|
"name": name,
|
||||||
"remark": remark,
|
"remark": remark,
|
||||||
"created_at": created_at,
|
"created_at": format_datetime(created_at),
|
||||||
"permissions": menu_ids or [],
|
"permissions": menu_ids or [],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -411,9 +412,9 @@ def get_user_list_db_page(
|
|||||||
params = []
|
params = []
|
||||||
|
|
||||||
# ---- 用户 ID,必须是 UUID,否则忽略 ----
|
# ---- 用户 ID,必须是 UUID,否则忽略 ----
|
||||||
if uid and is_valid_uuid(uid):
|
if uid:
|
||||||
conditions.append("u.id = %s")
|
conditions.append("u.id::text LIKE %s")
|
||||||
params.append(uid)
|
params.append(f"%{uid}%")
|
||||||
|
|
||||||
# ---- 用户名模糊搜索 ----
|
# ---- 用户名模糊搜索 ----
|
||||||
if username:
|
if username:
|
||||||
@@ -506,19 +507,11 @@ def get_user_list_db_page(
|
|||||||
return result, total
|
return result, total
|
||||||
|
|
||||||
|
|
||||||
# 检查用户名是否存在
|
# 检查登录账号是否存在
|
||||||
def db_user_name_exists(username: str, id: str | None = None) -> bool:
|
def db_user_name_exists(username: str) -> bool:
|
||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
if id:
|
cursor.execute("SELECT COUNT(*) FROM users WHERE email = %s;", (username,))
|
||||||
cursor.execute(
|
|
||||||
"SELECT COUNT(*) FROM users WHERE username = %s AND id <> %s;",
|
|
||||||
(username, id),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
cursor.execute(
|
|
||||||
"SELECT COUNT(*) FROM users WHERE username = %s;", (username,)
|
|
||||||
)
|
|
||||||
return cursor.fetchone()[0] > 0
|
return cursor.fetchone()[0] > 0
|
||||||
|
|
||||||
|
|
||||||
@@ -554,40 +547,6 @@ def insert_user(data: dict) -> str:
|
|||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
|
|
||||||
# 更新用户
|
|
||||||
def update_user_db(id: str, data: dict) -> int:
|
|
||||||
with pg_pool.getConn() as conn:
|
|
||||||
with conn.cursor() as cursor:
|
|
||||||
cursor.execute(
|
|
||||||
"""
|
|
||||||
UPDATE users
|
|
||||||
SET username=%s, email=%s, phone=%s, is_active=%s, dept_id=%s
|
|
||||||
WHERE id=%s;
|
|
||||||
""",
|
|
||||||
(
|
|
||||||
data.get("name"),
|
|
||||||
data.get("email"),
|
|
||||||
data.get("phone"),
|
|
||||||
bool(data.get("status", 1)),
|
|
||||||
data.get("dept_id"),
|
|
||||||
id,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# 更新用户角色关系:先删除旧的,再插入新的
|
|
||||||
cursor.execute("DELETE FROM sys_user_role WHERE user_id=%s;", (id,))
|
|
||||||
role_ids = data.get("roles", [])
|
|
||||||
if role_ids:
|
|
||||||
for role_id in role_ids:
|
|
||||||
cursor.execute(
|
|
||||||
"INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s);",
|
|
||||||
(id, role_id),
|
|
||||||
)
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
return cursor.rowcount
|
|
||||||
|
|
||||||
|
|
||||||
# 局部更新用户(PATCH)
|
# 局部更新用户(PATCH)
|
||||||
def patch_user_db(id: str, data: dict) -> int:
|
def patch_user_db(id: str, data: dict) -> int:
|
||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
@@ -603,6 +562,7 @@ def patch_user_db(id: str, data: dict) -> int:
|
|||||||
"name": "username",
|
"name": "username",
|
||||||
"email": "email",
|
"email": "email",
|
||||||
"phone": "phone",
|
"phone": "phone",
|
||||||
|
"password_hash": "password_hash",
|
||||||
"status": "is_active",
|
"status": "is_active",
|
||||||
"dept_id": "dept_id",
|
"dept_id": "dept_id",
|
||||||
}
|
}
|
||||||
@@ -676,7 +636,7 @@ def get_menus_by_ids(menu_ids: list, plat_id: int):
|
|||||||
SELECT id, pid, name, path, component, redirect,
|
SELECT id, pid, name, path, component, redirect,
|
||||||
auth_code, type, meta, created_at, updated_at
|
auth_code, type, meta, created_at, updated_at
|
||||||
FROM sys_menu
|
FROM sys_menu
|
||||||
WHERE id = ANY(%s::varchar[]) AND plat_id = %s
|
WHERE id = ANY(%s::varchar[]) AND plat_id = %s AND type != 'button'
|
||||||
ORDER BY created_at ASC;
|
ORDER BY created_at ASC;
|
||||||
"""
|
"""
|
||||||
# 转换 uuid 列表为 str 列表
|
# 转换 uuid 列表为 str 列表
|
||||||
@@ -697,3 +657,296 @@ def build_menu_tree(items):
|
|||||||
else:
|
else:
|
||||||
tree.append(item)
|
tree.append(item)
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
|
||||||
|
def get_dict_list(keyword="", page=1, page_size=10):
|
||||||
|
"""
|
||||||
|
获取系统字典列表,支持分页和关键字搜索(key / name)
|
||||||
|
"""
|
||||||
|
offset = (page - 1) * page_size
|
||||||
|
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
# 1️⃣ 查询总条数
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT COUNT(*)
|
||||||
|
FROM sys_dict
|
||||||
|
WHERE (
|
||||||
|
%s = ''
|
||||||
|
OR key ILIKE '%%' || %s || '%%'
|
||||||
|
OR name ILIKE '%%' || %s || '%%'
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
(keyword, keyword, keyword),
|
||||||
|
)
|
||||||
|
total = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# 2️⃣ 查询当前页数据
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT id, key, name, remark, created_at
|
||||||
|
FROM sys_dict
|
||||||
|
WHERE (
|
||||||
|
%s = ''
|
||||||
|
OR key ILIKE '%%' || %s || '%%'
|
||||||
|
OR name ILIKE '%%' || %s || '%%'
|
||||||
|
)
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT %s OFFSET %s
|
||||||
|
""",
|
||||||
|
(keyword, keyword, keyword, page_size, offset),
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for row in rows:
|
||||||
|
result.append(
|
||||||
|
{
|
||||||
|
"id": row[0],
|
||||||
|
"key": row[1],
|
||||||
|
"name": row[2],
|
||||||
|
"remark": row[3],
|
||||||
|
"created_at": MyUtils.format_datetime(row[4]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return total, result
|
||||||
|
|
||||||
|
|
||||||
|
def db_create_dict(key: str, name: str, remark: str):
|
||||||
|
"""
|
||||||
|
在数据库中创建字典
|
||||||
|
"""
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO sys_dict (id, key, name, remark, created_at)
|
||||||
|
VALUES (gen_random_uuid(), %s, %s, %s, now())
|
||||||
|
RETURNING id
|
||||||
|
""",
|
||||||
|
(key, name, remark),
|
||||||
|
)
|
||||||
|
new_id = cursor.fetchone()[0]
|
||||||
|
return new_id
|
||||||
|
|
||||||
|
|
||||||
|
def db_update_dict(id: str, key: str, name: str, remark: str):
|
||||||
|
"""
|
||||||
|
在数据库中更新字典
|
||||||
|
"""
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
UPDATE sys_dict
|
||||||
|
SET key=%s, name=%s, remark=%s
|
||||||
|
WHERE id=%s
|
||||||
|
""",
|
||||||
|
(key, name, remark, id),
|
||||||
|
)
|
||||||
|
return id
|
||||||
|
|
||||||
|
|
||||||
|
def db_delete_dict(id: str):
|
||||||
|
"""
|
||||||
|
在数据库中删除字典
|
||||||
|
"""
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("DELETE FROM sys_dict WHERE id=%s", (id,))
|
||||||
|
return id
|
||||||
|
|
||||||
|
|
||||||
|
def db_get_dict_detail(dict_id: str):
|
||||||
|
"""
|
||||||
|
获取字典详情列表
|
||||||
|
"""
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT id, value, sort, pid, dict_id, remark, created_at, updated_at
|
||||||
|
FROM sys_dict_detail
|
||||||
|
WHERE dict_id=%s
|
||||||
|
ORDER BY sort ASC, created_at ASC
|
||||||
|
""",
|
||||||
|
(dict_id,),
|
||||||
|
)
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
result = []
|
||||||
|
for row in rows:
|
||||||
|
result.append(
|
||||||
|
{
|
||||||
|
"id": row[0],
|
||||||
|
"value": row[1],
|
||||||
|
"sort": row[2],
|
||||||
|
"pid": row[3],
|
||||||
|
"dict_id": row[4],
|
||||||
|
"remark": row[5],
|
||||||
|
"created_at": row[6].isoformat() if row[6] else None,
|
||||||
|
"updated_at": row[7].isoformat() if row[7] else None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def db_create_dict_detail(
|
||||||
|
value: str, dict_id: str, sort: int = 0, pid: str = None, remark: str = None
|
||||||
|
):
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO sys_dict_detail (id, value, dict_id, sort, pid, remark, created_at)
|
||||||
|
VALUES (gen_random_uuid(), %s, %s, %s, %s, %s, now())
|
||||||
|
RETURNING id
|
||||||
|
""",
|
||||||
|
(value, dict_id, sort, pid, remark),
|
||||||
|
)
|
||||||
|
new_id = cursor.fetchone()[0]
|
||||||
|
return new_id
|
||||||
|
|
||||||
|
|
||||||
|
def db_update_dict_detail(
|
||||||
|
id: str, value: str = None, sort: int = None, pid: str = None, remark: str = None
|
||||||
|
):
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
UPDATE sys_dict_detail
|
||||||
|
SET value=COALESCE(%s, value),
|
||||||
|
sort=COALESCE(%s, sort),
|
||||||
|
pid=COALESCE(%s, pid),
|
||||||
|
remark=COALESCE(%s, remark),
|
||||||
|
updated_at=now()
|
||||||
|
WHERE id=%s
|
||||||
|
""",
|
||||||
|
(value, sort, pid, remark, id),
|
||||||
|
)
|
||||||
|
return id
|
||||||
|
|
||||||
|
|
||||||
|
def db_delete_dict_detail(id: str):
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("DELETE FROM sys_dict_detail WHERE id=%s", (id,))
|
||||||
|
return id
|
||||||
|
|
||||||
|
|
||||||
|
def get_dict_detail_list_by_key(dict_key: str):
|
||||||
|
"""
|
||||||
|
通过字典 key 获取字典明细列表
|
||||||
|
"""
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
d.id,
|
||||||
|
d.value,
|
||||||
|
d.sort,
|
||||||
|
d.pid,
|
||||||
|
d.remark,
|
||||||
|
d.created_at
|
||||||
|
FROM sys_dict_detail d
|
||||||
|
JOIN sys_dict s ON d.dict_id = s.id
|
||||||
|
WHERE s.key = %s
|
||||||
|
ORDER BY d.sort ASC, d.created_at ASC
|
||||||
|
""",
|
||||||
|
(dict_key,),
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for row in rows:
|
||||||
|
result.append(
|
||||||
|
{
|
||||||
|
"id": row[0],
|
||||||
|
"value": row[1],
|
||||||
|
"sort": row[2],
|
||||||
|
"pid": row[3],
|
||||||
|
"remark": row[4],
|
||||||
|
"created_at": MyUtils.format_datetime(row[5]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_dept_ids_by_user_id(user_id: UUID) -> list:
|
||||||
|
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
||||||
|
dept_id = cursor.fetchone()
|
||||||
|
|
||||||
|
if not dept_id:
|
||||||
|
raise ValueError(
|
||||||
|
f"User with id {user_id} not found or has no department."
|
||||||
|
)
|
||||||
|
|
||||||
|
dept_id = dept_id[0]
|
||||||
|
|
||||||
|
# 第二步:通过 dept_id 获取所有下级部门的 dept_id
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
WITH RECURSIVE dept_hierarchy AS (
|
||||||
|
SELECT id FROM sys_dept WHERE id = %s
|
||||||
|
UNION
|
||||||
|
SELECT d.id FROM sys_dept d
|
||||||
|
INNER JOIN dept_hierarchy dh ON d.parent_id = dh.id
|
||||||
|
)
|
||||||
|
SELECT id FROM dept_hierarchy;
|
||||||
|
""",
|
||||||
|
(dept_id,),
|
||||||
|
)
|
||||||
|
dept_ids = [row[0] for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
return dept_ids
|
||||||
|
|
||||||
|
|
||||||
|
def get_dept_ids_by_user_id(user_id: UUID) -> list:
|
||||||
|
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
||||||
|
dept_id = cursor.fetchone()
|
||||||
|
|
||||||
|
if not dept_id:
|
||||||
|
raise ValueError(
|
||||||
|
f"User with id {user_id} not found or has no department."
|
||||||
|
)
|
||||||
|
|
||||||
|
dept_id = dept_id[0]
|
||||||
|
|
||||||
|
# 第二步:通过 dept_id 获取所有下级部门的 dept_id
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
WITH RECURSIVE dept_hierarchy AS (
|
||||||
|
SELECT id FROM sys_dept WHERE id = %s
|
||||||
|
UNION
|
||||||
|
SELECT d.id FROM sys_dept d
|
||||||
|
INNER JOIN dept_hierarchy dh ON d.parent_id = dh.id
|
||||||
|
)
|
||||||
|
SELECT id FROM dept_hierarchy;
|
||||||
|
""",
|
||||||
|
(dept_id,),
|
||||||
|
)
|
||||||
|
dept_ids = [row[0] for row in cursor.fetchall()]
|
||||||
|
|
||||||
|
return dept_ids
|
||||||
|
|
||||||
|
|
||||||
|
def get_dept_id_by_user_id(user_id: UUID) -> list:
|
||||||
|
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||||
|
with pg_pool.getConn() as conn:
|
||||||
|
with conn.cursor() as cursor:
|
||||||
|
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
||||||
|
dept_id = cursor.fetchone()
|
||||||
|
dept_id = dept_id[0]
|
||||||
|
return dept_id
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import asyncio
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import WebSocket
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.active_connections: List[WebSocket] = []
|
||||||
|
self.lock = asyncio.Lock()
|
||||||
|
|
||||||
|
async def connect(self, websocket: WebSocket):
|
||||||
|
await websocket.accept()
|
||||||
|
async with self.lock:
|
||||||
|
self.active_connections.append(websocket)
|
||||||
|
|
||||||
|
async def disconnect(self, websocket: WebSocket):
|
||||||
|
async with self.lock:
|
||||||
|
if websocket in self.active_connections:
|
||||||
|
self.active_connections.remove(websocket)
|
||||||
|
|
||||||
|
async def broadcast(self, message: dict):
|
||||||
|
async with self.lock:
|
||||||
|
for ws in self.active_connections:
|
||||||
|
await ws.send_json(message)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class EMQXWebhook(BaseModel):
|
||||||
|
event: str
|
||||||
|
clientid: str
|
||||||
|
username: Optional[str] = None
|
||||||
|
timestamp: Optional[int] = None
|
||||||
@@ -22,7 +22,7 @@ Pillow==11.3.0
|
|||||||
python-multipart==0.0.20
|
python-multipart==0.0.20
|
||||||
aio_pika==9.5.7
|
aio_pika==9.5.7
|
||||||
ultralytics==8.3.227
|
ultralytics==8.3.227
|
||||||
|
redis==7.1.0
|
||||||
# MCP服务
|
# MCP服务
|
||||||
python-dotenv>=1.0.0
|
python-dotenv>=1.0.0
|
||||||
websockets>=11.0.3
|
websockets>=11.0.3
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
@@ -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 config.security import get_user_id_from_token
|
||||||
from db.postgres.system import *
|
from db.postgres.system import *
|
||||||
@@ -60,7 +60,7 @@ async def dept_update(
|
|||||||
|
|
||||||
parent_id = data.get("pid")
|
parent_id = data.get("pid")
|
||||||
name = data.get("name")
|
name = data.get("name")
|
||||||
comment = data.get("comment")
|
comment = data.get("remark")
|
||||||
|
|
||||||
rowcount = update_dept(id, parent_id, name, comment)
|
rowcount = update_dept(id, parent_id, name, comment)
|
||||||
if rowcount == 0:
|
if rowcount == 0:
|
||||||
@@ -294,12 +294,11 @@ async def delete_role(
|
|||||||
@systemRouter.get("/user/name-exists")
|
@systemRouter.get("/user/name-exists")
|
||||||
async def user_name_exists(
|
async def user_name_exists(
|
||||||
username: str,
|
username: str,
|
||||||
id: str | None = None,
|
|
||||||
user_id: UUID = Depends(get_user_id_from_token),
|
user_id: UUID = Depends(get_user_id_from_token),
|
||||||
):
|
):
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return {"error": "userId is required"}
|
return {"error": "userId is required"}
|
||||||
exists = db_user_name_exists(username, id)
|
exists = db_user_name_exists(username)
|
||||||
return BaseResponse(data=exists)
|
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})
|
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}")
|
@systemRouter.patch("/user/{id}")
|
||||||
async def patch_user(
|
async def patch_user(
|
||||||
id: str,
|
id: str,
|
||||||
@@ -375,3 +359,118 @@ async def delete_user(
|
|||||||
if deleted == 0:
|
if deleted == 0:
|
||||||
return BaseResponse(status=False, message="用户不存在", data=None)
|
return BaseResponse(status=False, message="用户不存在", data=None)
|
||||||
return BaseResponse(data=True)
|
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)
|
||||||
|
|||||||
@@ -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")
|
||||||
@@ -8,11 +8,10 @@ import json
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
from websockets.server import WebSocketServerProtocol
|
|
||||||
from websockets.exceptions import ConnectionClosed
|
|
||||||
from utils.logger import get_logger
|
|
||||||
|
|
||||||
logger = get_logger()
|
from fastapi import logger
|
||||||
|
from websockets.exceptions import ConnectionClosed
|
||||||
|
from websockets.legacy.server import WebSocketServerProtocol
|
||||||
|
|
||||||
|
|
||||||
class RobotConnection:
|
class RobotConnection:
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ fun Application.User(config: AppConfig) {
|
|||||||
} else {
|
} else {
|
||||||
// 账号密码不匹配
|
// 账号密码不匹配
|
||||||
BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
|
BaseResponse(status = false, message = "账号密码不匹配,请重新登录", data = null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BaseResponse(status = false, message = "账号已被禁用,请联系管理员", data = null)
|
BaseResponse(status = false, message = "账号已被禁用,请联系管理员", data = null)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user