牧安云哨-后端

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
+1
View File
@@ -1,6 +1,7 @@
from .aimessage import *
from .aiprofile import *
from .aisession import *
from .iot import *
from .knowledge import *
from .license import *
from .report import *
+203
View File
@@ -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 = []
# ---- 设备 IDuuid----
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
+261
View File
@@ -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
+303 -50
View File
@@ -2,6 +2,7 @@ import json
from uuid import UUID
from config.pgDb import pg_pool
from utils import MyUtils
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,
"name": name,
"remark": remark,
"created_at": created_at,
"created_at": format_datetime(created_at),
"permissions": menu_ids or [],
}
)
@@ -411,9 +412,9 @@ def get_user_list_db_page(
params = []
# ---- 用户 ID,必须是 UUID,否则忽略 ----
if uid and is_valid_uuid(uid):
conditions.append("u.id = %s")
params.append(uid)
if uid:
conditions.append("u.id::text LIKE %s")
params.append(f"%{uid}%")
# ---- 用户名模糊搜索 ----
if username:
@@ -506,19 +507,11 @@ def get_user_list_db_page(
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 conn.cursor() as cursor:
if id:
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,)
)
cursor.execute("SELECT COUNT(*) FROM users WHERE email = %s;", (username,))
return cursor.fetchone()[0] > 0
@@ -554,40 +547,6 @@ def insert_user(data: dict) -> str:
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
def patch_user_db(id: str, data: dict) -> int:
with pg_pool.getConn() as conn:
@@ -603,6 +562,7 @@ def patch_user_db(id: str, data: dict) -> int:
"name": "username",
"email": "email",
"phone": "phone",
"password_hash": "password_hash",
"status": "is_active",
"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,
auth_code, type, meta, created_at, updated_at
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;
"""
# 转换 uuid 列表为 str 列表
@@ -697,3 +657,296 @@ def build_menu_tree(items):
else:
tree.append(item)
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
+25
View File
@@ -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)