完善权限系统

This commit is contained in:
BBIT-Kai
2025-12-08 18:11:48 +08:00
parent c53926afd6
commit dbdc222541
1503 changed files with 132197 additions and 885 deletions
+5 -4
View File
@@ -5,7 +5,6 @@ from fastapi.middleware.cors import CORSMiddleware
from uvicorn import Config, Server
from config.yolo import YOLOSingleton
from mcp_local.mcp_pipe import init_mcp_server
from routers.Bot import botRouter
from routers.Chat import chatRouter
from routers.Datasource import reportDataRouter
@@ -14,6 +13,7 @@ from routers.Public import publicRouter
from routers.RabbitMQ import rqRouter
from routers.Report import reportRouter
from routers.Service import serviceRouter
from routers.System import systemRouter
from routers.Vision import visionRouter
@@ -45,6 +45,7 @@ async def ai_lab():
for r in routers:
app.include_router(r, prefix="/llm", tags=["llm"])
app.include_router(visionRouter, prefix="/cv", tags=["cv"])
app.include_router(systemRouter, prefix="/system", tags=["system"])
app.include_router(publicRouter, prefix="/api/public", tags=["api"])
config = Config(app=app, host="0.0.0.0", port=13011, log_level="debug")
server = Server(config)
@@ -63,14 +64,14 @@ async def main():
# task_mcp1 = asyncio.create_task(init_mcp_server(endpoint_url_ai_lab))
# MCP服务-ql
endpoint_url_ql = "wss://ai.ronsunny.cn:8090/aimcp/mcp_endpoint/mcp/?token=8ZmCzp7FzsbxwHOg2%2FvBQkxrC3QWJiI%2B4iTfouExinjcT8ZgLwQfFUtgcMInI7St"
task_mcp2 = asyncio.create_task(init_mcp_server(endpoint_url_ql))
# endpoint_url_ql = "wss://ai.ronsunny.cn:8090/aimcp/mcp_endpoint/mcp/?token=8ZmCzp7FzsbxwHOg2%2FvBQkxrC3QWJiI%2B4iTfouExinjcT8ZgLwQfFUtgcMInI7St"
# task_mcp2 = asyncio.create_task(init_mcp_server(endpoint_url_ql))
# RabbitMQ服务
# task_mq = asyncio.create_task(mq_pull_analysis_async())
# await asyncio.gather(task_api, task_mcp1, task_mcp2, task_mq)
await asyncio.gather(task_api, task_mcp2)
await asyncio.gather(task_api)
if __name__ == "__main__":
-849
View File
@@ -1,849 +0,0 @@
from typing import List, Dict
from langchain_postgres import PostgresChatMessageHistory
from config.pgDb import pg_pool
# ————————————————————————————————————————————————————AI角色———————————————————————————————
database_name = "ai_chat_history"
def get_ai_personality(ai_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT ai_personality FROM ai_chat_profiles WHERE id = %s", (ai_id,)
)
row = cur.fetchone()
if row:
return row[0]
else:
return "你是一个乐于助人的AI助手,请保持中文简洁回答用户。"
def get_description(ai_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT description FROM ai_chat_profiles WHERE id = %s", (ai_id,)
)
row = cur.fetchone()
if row:
return row[0]
else:
return "你是一个乐于助人的AI助手,请保持中文简洁回答用户。"
def get_ai_available_kn_bases(ai_id: str) -> List[str]:
with pg_pool.getConn() as conn:
result = conn.execute(
"SELECT available_kn_bases FROM ai_chat_profiles WHERE id = %s", (ai_id,)
)
return result.fetchone()[0]
def get_all_ai_bot(user_id: str, module: str) -> List[Dict]:
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
# 查询用户角色
cur.execute("SELECT roles FROM users WHERE id = %s", (user_id,))
role_row = cur.fetchone()
if not role_row:
return [] # 用户不存在
user_roles = role_row[0]
# 查询 AI 角色 JSON 字段包含用户角色
cur.execute(
"""
SELECT id, title, description, welcome_words, ai_personality, available_report_tables, available_kn_bases
FROM ai_chat_profiles
WHERE available_module = %s
AND is_active = TRUE
AND available_roles::jsonb ?| %s
""",
(module, user_roles),
)
rows = cur.fetchall()
result = []
for row in rows:
# row 索引对应 SELECT 字段顺序
(
id_,
title,
description,
welcome_words,
ai_personality,
available_report_tables,
available_kn_bases,
) = row
# 解析 JSON
roles_json = ai_personality if ai_personality else {}
result.append(
{
"id": id_,
"title": title,
"description": description,
"welcome_words": welcome_words,
"name": roles_json.get("名字", ""),
"role": roles_json.get("性格", ""),
"service": roles_json.get("业务", ""),
"available_report_tables": available_report_tables,
"available_kn_bases": available_kn_bases,
}
)
return result
# ————————————————————————————————————————————————————消息———————————————————————————————
def insert_message(session_id: str, isAI: bool, content: str):
with pg_pool.getConn() as conn:
history = PostgresChatMessageHistory(
database_name, session_id, sync_connection=conn
)
if isAI:
history.add_ai_message(content)
else:
history.add_user_message(content)
def get_history(session_id: str):
simplified = []
with pg_pool.getConn() as conn:
history = PostgresChatMessageHistory(
database_name, session_id, sync_connection=conn
)
for msg in history.messages:
simplified.append({"type": msg.type, "content": msg.content})
return simplified
def get_history_with_time(session_id: str, number: int):
simplified = []
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
f"SELECT message, created_at FROM ai_chat_history WHERE session_id = '{session_id}' ORDER BY created_at DESC LIMIT {number}"
)
rows = cur.fetchall()
simplified = []
for row in rows:
msg_dict = row[0]
simplified.append(
{
"type": msg_dict.get("type"),
"created_at": row[1].isoformat(),
"content": msg_dict.get("data", {}).get("content"),
}
)
return simplified
# ————————————————————————————————————————————————————会话———————————————————————————————
def insert_session(
user_id: str, ai_id: str, session_id: str, session_title: str, available_module
):
with pg_pool.getConn() as coon:
with coon.cursor() as cur:
cur.execute(
"INSERT INTO ai_chat_sessions (id ,user_id, ai_id, title, available_module, created_at, updated_at) VALUES (%s, %s, %s, %s,%s, NOW(), NOW())",
(session_id, user_id, ai_id, session_title, available_module),
)
coon.commit()
def update_session_updated_at(session_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"UPDATE ai_chat_sessions SET updated_at = NOW() WHERE id = %s",
(session_id,),
)
conn.commit()
def get_sessions(user_id: str, available_module: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT id, title, updated_at "
"FROM ai_chat_sessions "
"WHERE user_id = %s AND available_module = %s "
"ORDER BY updated_at DESC",
(user_id, available_module),
)
sessions = cur.fetchall()
return [
{"id": row[0], "title": row[1], "updated_at": row[2]}
for row in sessions
]
# ————————————————————————————————————————————————————报表———————————————————————————————
def get_reports(user_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT id, title FROM ai_reports WHERE created_by = %s AND is_masked = TRUE ORDER BY created_at DESC",
(user_id,),
)
reports = cur.fetchall()
return [{"id": row[0], "title": row[1]} for row in reports]
def save_report(id: str, user_id: str, title: str, sql: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO ai_reports (id, title, sql, created_at, created_by , is_masked) VALUES (%s, %s, %s, NOW(), %s, FALSE) RETURNING id",
(id, title, sql, user_id),
)
report_id = cur.fetchone()[0]
conn.commit()
return report_id
def maked_report(report_id: str, title: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"UPDATE ai_reports SET title = %s, is_masked = TRUE WHERE id = %s",
(title, report_id),
)
conn.commit()
def getSQL(reportId: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute("SELECT sql FROM ai_reports WHERE id = %s", (reportId,))
row = cur.fetchone()
if row:
return row[0]
else:
return ""
def get_available_tables_str(aiId: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
# 1. 先取 AI 可用的数据库表
cur.execute(
"SELECT available_report_tables FROM ai_chat_profiles WHERE id = %s",
(aiId,),
)
role_row = cur.fetchone()
if not role_row:
return "无数据库表可用"
available_tables = role_row[0] # 假设是列表
if not available_tables:
return "无数据库表可用"
# 2. 构造 IN 查询占位符
placeholders = ",".join(["%s"] * len(available_tables))
sql_query = f"""
SELECT id, name, description
FROM ai_reports_tables
WHERE id IN ({placeholders}) AND is_active = TRUE
"""
cur.execute(sql_query, available_tables)
tableIds = cur.fetchall()
# 3. 查询这些表的字段
result = ""
for table in tableIds:
cur.execute(
"SELECT name, type, description FROM ai_reports_fields WHERE table_id = %s AND is_active = TRUE",
(table[0],),
)
columns = cur.fetchall()
result += f"{table[1]}{table[2]}\n"
result += "字段名,数据类型,描述\n"
for column in columns:
result += f"{column[0]},{column[1]}, {column[2]}\n"
result += "\n"
return result
# -------------------报表数据源------------------
# 获取表
def get_available_tables():
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"SELECT id, name, description,is_active FROM ai_reports_tables",
)
return [
{
"id": row[0],
"name": row[1],
"description": row[2],
"is_active": row[3],
}
for row in cursor.fetchall()
]
# 新增表
def add_table(name, description, user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO ai_reports_tables (name, description, create_by)
VALUES (%s, %s, %s)
RETURNING id
""",
(name, description, user_id),
)
new_id = cursor.fetchone()[0] # 取返回的 id
return new_id
# 获取字段
def get_fields_by_table_id(table_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"SELECT id, name, type, description, is_active FROM ai_reports_fields WHERE table_id = %s",
(table_id,),
)
return [
{
"id": row[0],
"name": row[1],
"type": row[2],
"description": row[3],
"is_active": row[4],
}
for row in cursor.fetchall()
]
# 新增字段
def add_field(name, type, description, is_active, table_id, user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"INSERT INTO ai_reports_fields (name,type,description, is_active, create_by, table_id) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id",
(name, type, description, is_active, user_id, table_id),
)
new_id = cursor.fetchone()[0] # 取返回的 id
return new_id
# 新增报表智能体
def insert_bot(
title: str,
description: str,
welcome_words: str,
ai_personality: str,
available_module: str,
available_report_tables: str,
available_kn_bases: str,
user_id: str,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
available_roles = json.dumps(["user"])
cursor.execute(
"""
INSERT INTO ai_chat_profiles
(available_module,available_roles, title, description, welcome_words, ai_personality, available_report_tables, available_kn_bases, created_by, created_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, now())
RETURNING id
""",
(
available_module,
available_roles,
title,
description,
welcome_words,
ai_personality,
available_report_tables,
available_kn_bases,
user_id,
),
)
report_id = cursor.fetchone()[0]
return report_id
# 更新报表智能体
def update_bot(
id: str,
title: str,
description: str,
welcome_words: str,
ai_personality: str,
available_module: str,
available_report_tables: str,
available_kn_bases: str,
user_id: str,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
UPDATE ai_chat_profiles
SET title = %s,
description = %s,
ai_personality = %s,
welcome_words = %s,
available_report_tables = %s,
available_kn_bases = %s,
available_module = %s,
updated_at = NOW(),
updated_by = %s
WHERE id = %s
""",
(
title,
description,
ai_personality,
welcome_words,
available_report_tables,
available_kn_bases,
available_module,
user_id,
id,
),
)
# ————————————————————————————————————————————————————知识库———————————————————————————————
def get_available_knowledge_bases(available_module: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, name, description, is_active
FROM ai_knowledge
WHERE available_module::jsonb @> %s::jsonb
""",
(f'["{available_module}"]',),
)
return [
{
"id": row[0],
"name": row[1],
"description": row[2],
"is_active": row[3],
}
for row in cursor.fetchall()
]
def add_knowledge_base(name, description, user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO ai_knowledge (name, description, created_by, created_at)
VALUES (%s, %s, %s, now())
RETURNING id
""",
(name, description, user_id),
)
new_id = cursor.fetchone()[0] # 取返回的 id
return new_id
# ————————————————————————————————————————————————————仪评指标联识别———————————————————————————————
from config.minIO import get_temp_url
import utils.MyUtils as MyUtils
def get_ticket_image_list(user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT created_at, file_name, resolution, size, name,
moisture_content, cocoon_weight, defective_pupa_count,
fresh_shell_weight, sample_count, barcode, oss,
net_weight_total, evaluator, reviewer,id ,dead_pupa_count
FROM ticket_images
WHERE created_by = %s
""",
(user_id,),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"created_at": MyUtils.format_datetime(row[0]),
"file_name": row[1],
"resolution": row[2],
"size": round(row[3] / 1024, 2),
"name": row[4],
"moisture_content": row[5],
"cocoon_weight": row[6],
"defective_pupa_count": row[7],
"fresh_shell_weight": row[8],
"sample_count": row[9],
"barcode": row[10],
"oss_url": get_temp_url("image-ticket", row[11]),
"net_weight_total": row[12],
"evaluator": row[13],
"reviewer": row[14],
"id": row[15],
"dead_pupa_count": row[16],
}
)
return result
def insert_ticket_image(
created_by,
file_name,
resolution,
size,
name,
moisture_content,
cocoon_weight,
defective_pupa_count,
dead_pupa_count,
fresh_shell_weight,
sample_count,
barcode,
oss,
net_weight_total,
evaluator,
reviewer,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO ticket_images (
created_by, file_name, resolution, size, name,
moisture_content, cocoon_weight, defective_pupa_count, dead_pupa_count,
fresh_shell_weight, sample_count, barcode, oss,
net_weight_total, evaluator, reviewer, created_at
)
VALUES (%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, NOW())
RETURNING id
""",
(
created_by,
file_name,
resolution,
size,
name,
moisture_content,
cocoon_weight,
defective_pupa_count,
dead_pupa_count,
fresh_shell_weight,
sample_count,
barcode,
oss,
net_weight_total,
evaluator,
reviewer,
),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
import json
# ————————————————————————————————————————————————————证件照片识别———————————————————————————————
def insert_license_image(
created_by, file_name, resolution, size, name, oss, type, content
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO license_images (
created_by, created_at, file_name, resolution, size, name, oss,
type, content
)
VALUES (%s, NOW(), %s, %s, %s, %s, %s, %s, %s )
RETURNING id
""",
(created_by, file_name, resolution, size, name, oss, type, content),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
def get_license_image_list(user_id, page=1, page_size=10):
"""
获取用户已分析图片列表,带分页
"""
offset = (page - 1) * page_size
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 1️⃣ 查询总条数
cursor.execute(
"""
SELECT COUNT(*)
FROM license_images
WHERE created_by = %s
""",
(user_id,),
)
total = cursor.fetchone()[0]
# 2️⃣ 查询当前页数据
cursor.execute(
"""
SELECT created_at, file_name, resolution, size, name, oss, id, type, content
FROM license_images
WHERE created_by = %s
ORDER BY created_at DESC
LIMIT %s OFFSET %s
""",
(user_id, page_size, offset),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"created_at": MyUtils.format_datetime(row[0]),
"file_name": row[1],
"resolution": row[2],
"size": round(row[3] / 1024, 2),
"name": row[4],
"oss_url": get_temp_url("image-license", row[5]),
"id": row[6],
"type": row[7],
"content": row[8],
}
)
return total, result
# ————————————————————————————————————————————————————蚕茧质量识别———————————————————————————————
def insert_sca_image(
file_name,
resolution,
size,
cocoon_count,
max_confidence,
min_confidence,
average_confidence,
other_info,
preprocess_time_ms,
inference_time_ms,
postprocess_time_ms,
name,
image_pre,
image_after,
created_by,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
other_info = json.dumps(other_info)
cursor.execute(
"""
INSERT INTO sca_images (
upload_datetime, file_name, resolution, size, cocoon_count, max_confidence, min_confidence,
average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms, name, image_pre, image_after, created_by
)
VALUES (NOW(), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )
RETURNING id
""",
(
file_name,
resolution,
size,
cocoon_count,
max_confidence,
min_confidence,
average_confidence,
other_info,
preprocess_time_ms,
inference_time_ms,
postprocess_time_ms,
name,
image_pre,
image_after,
created_by,
),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
def get_sca_image_list(user_id, name, page=1, page_size=10):
"""
获取用户已分析图片列表,带分页
"""
offset = (page - 1) * page_size
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 1️⃣ 查询总条数
# ✅ 改进版:支持 name 为空时统计全部,不为空时模糊统计
cursor.execute(
"""
SELECT COUNT(*)
FROM sca_images
WHERE created_by = %s
AND (%s = '' OR name LIKE '%%' || %s || '%%')
""",
(user_id, name, name),
)
total = cursor.fetchone()[0]
# 2️⃣ 查询当前页数据
# ✅ 改进版
cursor.execute(
"""
SELECT id, name, upload_datetime, file_name, image_pre, image_after, resolution,
size, cocoon_count, max_confidence, min_confidence, average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms
FROM sca_images
WHERE created_by = %s
AND (%s = '' OR name LIKE '%%' || %s || '%%')
ORDER BY upload_datetime DESC
LIMIT %s OFFSET %s
""",
(user_id, name, name, page_size, offset),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"id": row[0],
"name": row[1],
"upload_datetime": MyUtils.format_datetime(row[2]),
"file_name": row[3],
"image_pre": get_temp_url("image-sca", "raw/" + row[4]),
"image_after": get_temp_url("image-sca", "ai/" + row[5]),
"resolution": row[6],
"size": MyUtils.safe_round(row[7] / 1024, 2),
"cocoon_count": row[8],
"max_confidence": row[9],
"min_confidence": row[10],
"average_confidence": row[11],
"other_info": row[12],
"preprocess_time_ms": MyUtils.safe_round(row[13], 4),
"inference_time_ms": MyUtils.safe_round(row[14], 4),
"postprocess_time_ms": MyUtils.safe_round(row[15], 4),
}
)
return total, result
def get_sca_video_list(name, page=1, page_size=10):
"""
获取用户已分析视频列表,带分页
"""
offset = (page - 1) * page_size
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 1️⃣ 查询总条数
cursor.execute(
"""
SELECT COUNT(*)
FROM sca_videos
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
""",
(name, name),
)
total = cursor.fetchone()[0]
# 2️⃣ 查询当前页数据
cursor.execute(
"""
SELECT id, name, raw_object_name, ai_object_name, duration, size, video_codec, audio_codec,
overall_bit_rate, resolution, sc_analysis_time, sc_analysis_total_count, sc_analysis_max_count,
sc_analysis_primary_type, sc_analysis_secondary_type, other_info, created_at
FROM sca_videos
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
ORDER BY created_at DESC
LIMIT %s OFFSET %s
""",
(name, name, page_size, offset),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"id": row[0],
"name": row[1],
"raw_video_url": get_temp_url("video-sca", "raw/" + row[2]),
"ai_video_url": get_temp_url("video-sca", "ai/" + row[3]),
"duration": MyUtils.safe_round(row[4], 2),
"size_kb": MyUtils.safe_round(row[5] / 1024, 2),
"video_codec": row[6],
"audio_codec": row[7],
"overall_bit_rate": row[8],
"resolution": row[9],
"sc_analysis_time": MyUtils.safe_round(row[10], 2),
"sc_analysis_total_count": row[11],
"sc_analysis_max_count": row[12],
"sc_analysis_primary_type": row[13],
"sc_analysis_secondary_type": row[14],
"other_info": json.loads(row[15]),
"created_at": MyUtils.format_datetime(row[16]),
}
)
return total, result
def get_sca_video_details(v_id):
"""
获取指定视频的分析明细列表
"""
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, v_id, time_stamp, other_info
FROM sca_video_details
WHERE v_id = %s
ORDER BY time_stamp ASC
""",
(v_id,),
)
rows = cursor.fetchall()
result = []
for row in rows:
# other_info 从 JSON 字符串解析回字典
result.append(
{
"id": row[0],
"v_id": row[1],
"time_stamp": row[2],
"other_info": row[3],
}
)
return result
+10
View File
@@ -0,0 +1,10 @@
from .aimessage import *
from .aiprofile import *
from .aisession import *
from .knowledge import *
from .license import *
from .report import *
from .sca_image import *
from .sca_video import *
from .system import *
from .ticket import *
+50
View File
@@ -0,0 +1,50 @@
from langchain_postgres import PostgresChatMessageHistory
from config.pgDb import pg_pool
database_name = "ai_chat_history"
def insert_message(session_id: str, isAI: bool, content: str):
with pg_pool.getConn() as conn:
history = PostgresChatMessageHistory(
database_name, session_id, sync_connection=conn
)
if isAI:
history.add_ai_message(content)
else:
history.add_user_message(content)
def get_history(session_id: str):
simplified = []
with pg_pool.getConn() as conn:
history = PostgresChatMessageHistory(
database_name, session_id, sync_connection=conn
)
for msg in history.messages:
simplified.append({"type": msg.type, "content": msg.content})
return simplified
def get_history_with_time(session_id: str, number: int):
simplified = []
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
f"SELECT message, created_at FROM ai_chat_history WHERE session_id = '{session_id}' ORDER BY created_at DESC LIMIT {number}"
)
rows = cur.fetchall()
simplified = []
for row in rows:
msg_dict = row[0]
simplified.append(
{
"type": msg_dict.get("type"),
"created_at": row[1].isoformat(),
"content": msg_dict.get("data", {}).get("content"),
}
)
return simplified
+92
View File
@@ -0,0 +1,92 @@
from typing import List, Dict
from config.pgDb import pg_pool
def get_ai_personality(ai_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT ai_personality FROM ai_chat_profiles WHERE id = %s", (ai_id,)
)
row = cur.fetchone()
if row:
return row[0]
else:
return "你是一个乐于助人的AI助手,请保持中文简洁回答用户。"
def get_description(ai_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT description FROM ai_chat_profiles WHERE id = %s", (ai_id,)
)
row = cur.fetchone()
if row:
return row[0]
else:
return "你是一个乐于助人的AI助手,请保持中文简洁回答用户。"
def get_ai_available_kn_bases(ai_id: str) -> List[str]:
with pg_pool.getConn() as conn:
result = conn.execute(
"SELECT available_kn_bases FROM ai_chat_profiles WHERE id = %s", (ai_id,)
)
return result.fetchone()[0]
def get_all_ai_bot(user_id: str, module: str) -> List[Dict]:
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
# 查询用户角色
cur.execute("SELECT roles FROM users WHERE id = %s", (user_id,))
role_row = cur.fetchone()
if not role_row:
return [] # 用户不存在
user_roles = role_row[0]
# 查询 AI 角色 JSON 字段包含用户角色
cur.execute(
"""
SELECT id, title, description, welcome_words, ai_personality, available_report_tables, available_kn_bases
FROM ai_chat_profiles
WHERE available_module = %s
AND is_active = TRUE
AND available_roles::jsonb ?| %s
""",
(module, user_roles),
)
rows = cur.fetchall()
result = []
for row in rows:
# row 索引对应 SELECT 字段顺序
(
id_,
title,
description,
welcome_words,
ai_personality,
available_report_tables,
available_kn_bases,
) = row
# 解析 JSON
roles_json = ai_personality if ai_personality else {}
result.append(
{
"id": id_,
"title": title,
"description": description,
"welcome_words": welcome_words,
"name": roles_json.get("名字", ""),
"role": roles_json.get("性格", ""),
"service": roles_json.get("业务", ""),
"available_report_tables": available_report_tables,
"available_kn_bases": available_kn_bases,
}
)
return result
+41
View File
@@ -0,0 +1,41 @@
from config.pgDb import pg_pool
def insert_session(
user_id: str, ai_id: str, session_id: str, session_title: str, available_module
):
with pg_pool.getConn() as coon:
with coon.cursor() as cur:
cur.execute(
"INSERT INTO ai_chat_sessions (id ,user_id, ai_id, title, available_module, created_at, updated_at) VALUES (%s, %s, %s, %s,%s, NOW(), NOW())",
(session_id, user_id, ai_id, session_title, available_module),
)
coon.commit()
def update_session_updated_at(session_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"UPDATE ai_chat_sessions SET updated_at = NOW() WHERE id = %s",
(session_id,),
)
conn.commit()
def get_sessions(user_id: str, available_module: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT id, title, updated_at "
"FROM ai_chat_sessions "
"WHERE user_id = %s AND available_module = %s "
"ORDER BY updated_at DESC",
(user_id, available_module),
)
sessions = cur.fetchall()
return [
{"id": row[0], "title": row[1], "updated_at": row[2]}
for row in sessions
]
+39
View File
@@ -0,0 +1,39 @@
from config.pgDb import pg_pool
def get_available_knowledge_bases(available_module: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, name, description, is_active
FROM ai_knowledge
WHERE available_module::jsonb @> %s::jsonb
""",
(f'["{available_module}"]',),
)
return [
{
"id": row[0],
"name": row[1],
"description": row[2],
"is_active": row[3],
}
for row in cursor.fetchall()
]
def add_knowledge_base(name, description, user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO ai_knowledge (name, description, created_by, created_at)
VALUES (%s, %s, %s, now())
RETURNING id
""",
(name, description, user_id),
)
new_id = cursor.fetchone()[0] # 取返回的 id
return new_id
+75
View File
@@ -0,0 +1,75 @@
from config.minIO import get_temp_url
from config.pgDb import pg_pool
from utils import MyUtils
def insert_license_image(
created_by, file_name, resolution, size, name, oss, type, content
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO license_images (
created_by, created_at, file_name, resolution, size, name, oss,
type, content
)
VALUES (%s, NOW(), %s, %s, %s, %s, %s, %s, %s )
RETURNING id
""",
(created_by, file_name, resolution, size, name, oss, type, content),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
def get_license_image_list(user_id, page=1, page_size=10):
"""
获取用户已分析图片列表,带分页
"""
offset = (page - 1) * page_size
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 1️⃣ 查询总条数
cursor.execute(
"""
SELECT COUNT(*)
FROM license_images
WHERE created_by = %s
""",
(user_id,),
)
total = cursor.fetchone()[0]
# 2️⃣ 查询当前页数据
cursor.execute(
"""
SELECT created_at, file_name, resolution, size, name, oss, id, type, content
FROM license_images
WHERE created_by = %s
ORDER BY created_at DESC
LIMIT %s OFFSET %s
""",
(user_id, page_size, offset),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"created_at": MyUtils.format_datetime(row[0]),
"file_name": row[1],
"resolution": row[2],
"size": round(row[3] / 1024, 2),
"name": row[4],
"oss_url": get_temp_url("image-license", row[5]),
"id": row[6],
"type": row[7],
"content": row[8],
}
)
return total, result
+233
View File
@@ -0,0 +1,233 @@
from config.pgDb import pg_pool
def get_reports(user_id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"SELECT id, title FROM ai_reports WHERE created_by = %s AND is_masked = TRUE ORDER BY created_at DESC",
(user_id,),
)
reports = cur.fetchall()
return [{"id": row[0], "title": row[1]} for row in reports]
def save_report(id: str, user_id: str, title: str, sql: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"INSERT INTO ai_reports (id, title, sql, created_at, created_by , is_masked) VALUES (%s, %s, %s, NOW(), %s, FALSE) RETURNING id",
(id, title, sql, user_id),
)
report_id = cur.fetchone()[0]
conn.commit()
return report_id
def maked_report(report_id: str, title: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute(
"UPDATE ai_reports SET title = %s, is_masked = TRUE WHERE id = %s",
(title, report_id),
)
conn.commit()
def getSQL(reportId: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
cur.execute("SELECT sql FROM ai_reports WHERE id = %s", (reportId,))
row = cur.fetchone()
if row:
return row[0]
else:
return ""
def get_available_tables_str(aiId: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cur:
# 1. 先取 AI 可用的数据库表
cur.execute(
"SELECT available_report_tables FROM ai_chat_profiles WHERE id = %s",
(aiId,),
)
role_row = cur.fetchone()
if not role_row:
return "无数据库表可用"
available_tables = role_row[0] # 假设是列表
if not available_tables:
return "无数据库表可用"
# 2. 构造 IN 查询占位符
placeholders = ",".join(["%s"] * len(available_tables))
sql_query = f"""
SELECT id, name, description
FROM ai_reports_tables
WHERE id IN ({placeholders}) AND is_active = TRUE
"""
cur.execute(sql_query, available_tables)
tableIds = cur.fetchall()
# 3. 查询这些表的字段
result = ""
for table in tableIds:
cur.execute(
"SELECT name, type, description FROM ai_reports_fields WHERE table_id = %s AND is_active = TRUE",
(table[0],),
)
columns = cur.fetchall()
result += f"{table[1]}{table[2]}\n"
result += "字段名,数据类型,描述\n"
for column in columns:
result += f"{column[0]},{column[1]}, {column[2]}\n"
result += "\n"
return result
# -------------------报表数据源------------------
# 获取表
def get_available_tables():
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"SELECT id, name, description,is_active FROM ai_reports_tables",
)
return [
{
"id": row[0],
"name": row[1],
"description": row[2],
"is_active": row[3],
}
for row in cursor.fetchall()
]
# 新增表
def add_table(name, description, user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO ai_reports_tables (name, description, create_by)
VALUES (%s, %s, %s)
RETURNING id
""",
(name, description, user_id),
)
new_id = cursor.fetchone()[0] # 取返回的 id
return new_id
# 获取字段
def get_fields_by_table_id(table_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"SELECT id, name, type, description, is_active FROM ai_reports_fields WHERE table_id = %s",
(table_id,),
)
return [
{
"id": row[0],
"name": row[1],
"type": row[2],
"description": row[3],
"is_active": row[4],
}
for row in cursor.fetchall()
]
# 新增字段
def add_field(name, type, description, is_active, table_id, user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"INSERT INTO ai_reports_fields (name,type,description, is_active, create_by, table_id) VALUES (%s, %s, %s, %s, %s, %s) RETURNING id",
(name, type, description, is_active, user_id, table_id),
)
new_id = cursor.fetchone()[0] # 取返回的 id
return new_id
# 新增报表智能体
def insert_bot(
title: str,
description: str,
welcome_words: str,
ai_personality: str,
available_module: str,
available_report_tables: str,
available_kn_bases: str,
user_id: str,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
available_roles = json.dumps(["user"])
cursor.execute(
"""
INSERT INTO ai_chat_profiles
(available_module,available_roles, title, description, welcome_words, ai_personality, available_report_tables, available_kn_bases, created_by, created_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, now())
RETURNING id
""",
(
available_module,
available_roles,
title,
description,
welcome_words,
ai_personality,
available_report_tables,
available_kn_bases,
user_id,
),
)
report_id = cursor.fetchone()[0]
return report_id
# 更新报表智能体
def update_bot(
id: str,
title: str,
description: str,
welcome_words: str,
ai_personality: str,
available_module: str,
available_report_tables: str,
available_kn_bases: str,
user_id: str,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
UPDATE ai_chat_profiles
SET title = %s,
description = %s,
ai_personality = %s,
welcome_words = %s,
available_report_tables = %s,
available_kn_bases = %s,
available_module = %s,
updated_at = NOW(),
updated_by = %s
WHERE id = %s
""",
(
title,
description,
ai_personality,
welcome_words,
available_report_tables,
available_kn_bases,
available_module,
user_id,
id,
),
)
+122
View File
@@ -0,0 +1,122 @@
import json
from config.minIO import get_temp_url
from config.pgDb import pg_pool
from utils import MyUtils
def insert_sca_image(
file_name,
resolution,
size,
cocoon_count,
max_confidence,
min_confidence,
average_confidence,
other_info,
preprocess_time_ms,
inference_time_ms,
postprocess_time_ms,
name,
image_pre,
image_after,
created_by,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
other_info = json.dumps(other_info)
cursor.execute(
"""
INSERT INTO sca_images (
upload_datetime, file_name, resolution, size, cocoon_count, max_confidence, min_confidence,
average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms, name, image_pre, image_after, created_by
)
VALUES (NOW(), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s )
RETURNING id
""",
(
file_name,
resolution,
size,
cocoon_count,
max_confidence,
min_confidence,
average_confidence,
other_info,
preprocess_time_ms,
inference_time_ms,
postprocess_time_ms,
name,
image_pre,
image_after,
created_by,
),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
def get_sca_image_list(user_id, name, page=1, page_size=10):
"""
获取用户已分析图片列表,带分页
"""
offset = (page - 1) * page_size
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 1️⃣ 查询总条数
# ✅ 改进版:支持 name 为空时统计全部,不为空时模糊统计
cursor.execute(
"""
SELECT COUNT(*)
FROM sca_images
WHERE created_by = %s
AND (%s = '' OR name LIKE '%%' || %s || '%%')
""",
(user_id, name, name),
)
total = cursor.fetchone()[0]
# 2️⃣ 查询当前页数据
# ✅ 改进版
cursor.execute(
"""
SELECT id, name, upload_datetime, file_name, image_pre, image_after, resolution,
size, cocoon_count, max_confidence, min_confidence, average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms
FROM sca_images
WHERE created_by = %s
AND (%s = '' OR name LIKE '%%' || %s || '%%')
ORDER BY upload_datetime DESC
LIMIT %s OFFSET %s
""",
(user_id, name, name, page_size, offset),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"id": row[0],
"name": row[1],
"upload_datetime": MyUtils.format_datetime(row[2]),
"file_name": row[3],
"image_pre": get_temp_url("image-sca", "raw/" + row[4]),
"image_after": get_temp_url("image-sca", "ai/" + row[5]),
"resolution": row[6],
"size": MyUtils.safe_round(row[7] / 1024, 2),
"cocoon_count": row[8],
"max_confidence": row[9],
"min_confidence": row[10],
"average_confidence": row[11],
"other_info": row[12],
"preprocess_time_ms": MyUtils.safe_round(row[13], 4),
"inference_time_ms": MyUtils.safe_round(row[14], 4),
"postprocess_time_ms": MyUtils.safe_round(row[15], 4),
}
)
return total, result
+99
View File
@@ -0,0 +1,99 @@
import json
from config.minIO import get_temp_url
from config.pgDb import pg_pool
from utils import MyUtils
def get_sca_video_list(name, page=1, page_size=10):
"""
获取用户已分析视频列表,带分页
"""
offset = (page - 1) * page_size
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 1️⃣ 查询总条数
cursor.execute(
"""
SELECT COUNT(*)
FROM sca_videos
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
""",
(name, name),
)
total = cursor.fetchone()[0]
# 2️⃣ 查询当前页数据
cursor.execute(
"""
SELECT id, name, raw_object_name, ai_object_name, duration, size, video_codec, audio_codec,
overall_bit_rate, resolution, sc_analysis_time, sc_analysis_total_count, sc_analysis_max_count,
sc_analysis_primary_type, sc_analysis_secondary_type, other_info, created_at
FROM sca_videos
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
ORDER BY created_at DESC
LIMIT %s OFFSET %s
""",
(name, name, page_size, offset),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"id": row[0],
"name": row[1],
"raw_video_url": get_temp_url("video-sca", "raw/" + row[2]),
"ai_video_url": get_temp_url("video-sca", "ai/" + row[3]),
"duration": MyUtils.safe_round(row[4], 2),
"size_kb": MyUtils.safe_round(row[5] / 1024, 2),
"video_codec": row[6],
"audio_codec": row[7],
"overall_bit_rate": row[8],
"resolution": row[9],
"sc_analysis_time": MyUtils.safe_round(row[10], 2),
"sc_analysis_total_count": row[11],
"sc_analysis_max_count": row[12],
"sc_analysis_primary_type": row[13],
"sc_analysis_secondary_type": row[14],
"other_info": json.loads(row[15]),
"created_at": MyUtils.format_datetime(row[16]),
}
)
return total, result
def get_sca_video_details(v_id):
"""
获取指定视频的分析明细列表
"""
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, v_id, time_stamp, other_info
FROM sca_video_details
WHERE v_id = %s
ORDER BY time_stamp ASC
""",
(v_id,),
)
rows = cursor.fetchall()
result = []
for row in rows:
# other_info 从 JSON 字符串解析回字典
result.append(
{
"id": row[0],
"v_id": row[1],
"time_stamp": row[2],
"other_info": row[3],
}
)
return result
+699
View File
@@ -0,0 +1,699 @@
import json
from uuid import UUID
from config.pgDb import pg_pool
from utils.MyUtils import format_datetime, is_valid_uuid
def get_all_depts():
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, parent_id, name, comment, created_at
FROM sys_dept
ORDER BY created_at ASC;
"""
)
rows = cursor.fetchall()
# cursor.description 可用于映射字段名
columns = [desc[0] for desc in cursor.description]
return [dict(zip(columns, row)) for row in rows]
def insert_dept(parent_id, name, comment):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO sys_dept (parent_id, name, comment, created_at)
VALUES (%s, %s, %s, NOW())
RETURNING id;
""",
(parent_id, name, comment),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
def update_dept(id, parent_id, name, comment):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
UPDATE sys_dept
SET parent_id = %s,
name = %s,
comment = %s
WHERE id = %s;
""",
(parent_id, name, comment, id),
)
conn.commit()
return cursor.rowcount # >0表示成功
def has_child_dept(id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(*) FROM sys_dept WHERE parent_id = %s;
""",
(id,),
)
return cursor.fetchone()[0] > 0
def delete_dept(id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute("""DELETE FROM sys_dept WHERE id = %s;""", (id,))
conn.commit()
return cursor.rowcount
def get_menus(plat_id: int):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, pid, name, path, component, redirect,
auth_code, type, meta, created_at, updated_at
FROM sys_menu
WHERE plat_id = %s
ORDER BY created_at ASC;
""",
(plat_id,),
)
rows = cursor.fetchall()
columns = [desc[0] for desc in cursor.description]
return [dict(zip(columns, row)) for row in rows]
def get_all_menus():
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT id, pid, name, path, component, redirect,
auth_code, type, meta, created_at, updated_at
FROM sys_menu
ORDER BY created_at ASC;
""",
)
rows = cursor.fetchall()
columns = [desc[0] for desc in cursor.description]
return [dict(zip(columns, row)) for row in rows]
def insert_menu(data: dict, plat_id: int):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO sys_menu
(pid, name, path, component, redirect,
auth_code, type, meta, created_at, updated_at, plat_id)
VALUES (%s, %s, %s, %s, %s,
%s, %s, %s, NOW(), NOW(), %s)
RETURNING id;
""",
(
data.get("pid"),
data.get("name"),
data.get("path"),
data.get("component"),
data.get("redirect"),
data.get("auth_code"),
data.get("type"),
json.dumps(data.get("meta") or {}),
plat_id,
),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
def update_menu_db(id: str, data: dict):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
UPDATE sys_menu
SET pid = %s,
name = %s,
path = %s,
component = %s,
redirect = %s,
auth_code = %s,
type = %s,
meta = %s,
updated_at = NOW()
WHERE id = %s;
""",
(
data.get("pid"),
data.get("name"),
data.get("path"),
data.get("component"),
data.get("redirect"),
data.get("auth_code"),
data.get("type"),
json.dumps(data.get("meta") or {}),
id,
),
)
conn.commit()
return cursor.rowcount
def db_menu_name_exists(name: str, id: str | None, plat_id: int):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
if id:
cursor.execute(
"""
SELECT COUNT(*) FROM sys_menu
WHERE name = %s AND id <> %s AND plat_id = %s;
""",
(name, id, plat_id),
)
else:
cursor.execute(
"""
SELECT COUNT(*) FROM sys_menu WHERE name = %s;
""",
(name,),
)
return cursor.fetchone()[0] > 0
def db_menu_path_exists(path: str, id: str | None, plat_id: int):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
if id:
cursor.execute(
"""
SELECT COUNT(*) FROM sys_menu
WHERE path = %s AND id <> %s AND plat_id = %s;
""",
(path, id, plat_id),
)
else:
cursor.execute(
"""
SELECT COUNT(*) FROM sys_menu WHERE path = %s;
""",
(path,),
)
return cursor.fetchone()[0] > 0
def menu_has_children(id: str):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT COUNT(*) FROM sys_menu WHERE pid = %s;
""",
(id,),
)
return cursor.fetchone()[0] > 0
def delete_menu_db(id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute("""DELETE FROM sys_menu WHERE id = %s;""", (id,))
conn.commit()
return cursor.rowcount
# 检查角色名是否存在
def db_role_name_exists(name: str, id: str | None = None) -> bool:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
if id:
cursor.execute(
"SELECT COUNT(*) FROM sys_role WHERE name = %s AND id <> %s;",
(name, id),
)
else:
cursor.execute(
"SELECT COUNT(*) FROM sys_role WHERE name = %s;", (name,)
)
return cursor.fetchone()[0] > 0
# 插入角色
def insert_role(data: dict) -> str:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"INSERT INTO sys_role (name, remark) VALUES (%s, %s) RETURNING id;",
(data.get("name"), data.get("remark")),
)
role_id = cursor.fetchone()[0]
# 插入角色对应菜单关系
menu_ids = data.get("permissions", [])
if menu_ids:
for menu_id in menu_ids:
cursor.execute(
"INSERT INTO sys_role_menu (role_id, menu_id) VALUES (%s, %s);",
(role_id, menu_id),
)
conn.commit()
return role_id
# 更新角色
def update_role_db(id: str, data: dict) -> int:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"UPDATE sys_role SET name = %s, remark = %s WHERE id = %s;",
(data.get("name"), data.get("remark"), id),
)
# 更新角色菜单关系:先删除旧的,再插入新的
cursor.execute("DELETE FROM sys_role_menu WHERE role_id = %s;", (id,))
menu_ids = data.get("permissions", [])
if menu_ids:
for menu_id in menu_ids:
cursor.execute(
"INSERT INTO sys_role_menu (role_id, menu_id) VALUES (%s, %s);",
(id, menu_id),
)
conn.commit()
return cursor.rowcount
# 删除角色
def delete_role_db(id: str) -> int:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
# 删除角色菜单关联
cursor.execute("DELETE FROM sys_role_menu WHERE role_id = %s;", (id,))
# 删除角色
cursor.execute("DELETE FROM sys_role WHERE id = %s;", (id,))
conn.commit()
return cursor.rowcount
# 查询角色列表
def get_role_list_db() -> list[dict]:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT id, name, remark, created_at FROM sys_role;")
roles = cursor.fetchall()
result = []
for r in roles:
role_id, name, remark, created_at = r
# 查询角色对应菜单
cursor.execute(
"SELECT menu_id FROM sys_role_menu WHERE role_id = %s;", (role_id,)
)
result.append(
{
"id": role_id,
"title": name,
}
)
return result
def get_role_list_db_page(page: int, page_size: int, rid=None, name=None, remark=None):
offset = (page - 1) * page_size
conditions = []
params = []
# ---- id(必须是 UUID,否则不作为条件)----
if rid and is_valid_uuid(rid):
conditions.append("r.id = %s")
params.append(rid)
# ---- name(模糊搜索)----
if name:
conditions.append("r.name LIKE %s")
params.append(f"%{name}%")
# ---- remark(模糊搜索)----
if remark:
conditions.append("r.remark LIKE %s")
params.append(f"%{remark}%")
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 sys_role r {where_clause};"
cursor.execute(count_sql, params)
total = cursor.fetchone()[0]
# ---- 分页数据 ----
list_sql = f"""
SELECT
r.id,
r.name,
r.remark,
r.created_at,
array_remove(array_agg(rm.menu_id::varchar), NULL) AS menu_ids
FROM sys_role r
LEFT JOIN sys_role_menu rm ON r.id = rm.role_id
{where_clause}
GROUP BY r.id
ORDER BY r.created_at DESC
LIMIT %s OFFSET %s;
"""
cursor.execute(list_sql, params + [page_size, offset])
rows = cursor.fetchall()
roles = []
for r in rows:
role_id, name, remark, created_at, menu_ids = r
roles.append(
{
"id": role_id,
"name": name,
"remark": remark,
"created_at": created_at,
"permissions": menu_ids or [],
}
)
return roles, total
def get_user_list_db_page(
page: int,
page_size: int,
uid=None,
username=None,
status=None,
dept_id=None,
startTime=None,
endTime=None,
):
offset = (page - 1) * page_size
conditions = []
params = []
# ---- 用户 ID,必须是 UUID,否则忽略 ----
if uid and is_valid_uuid(uid):
conditions.append("u.id = %s")
params.append(uid)
# ---- 用户名模糊搜索 ----
if username:
conditions.append("u.username LIKE %s")
params.append(f"%{username}%")
# ---- 状态 0/1 ----
if status is not None:
conditions.append("u.is_active = %s")
params.append(status == 1)
# ---- 部门 ID(也是 uuid----
if dept_id and is_valid_uuid(dept_id):
conditions.append("u.dept_id = %s")
params.append(dept_id)
# ---- 时间过滤 ----
if startTime:
conditions.append("u.created_at >= %s")
params.append(startTime)
if endTime:
conditions.append("u.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 users u {where_clause};"
cursor.execute(count_sql, params)
total = cursor.fetchone()[0]
# ---- 分页查询 ----
list_sql = f"""
SELECT
u.id,
u.username,
u.email,
u.phone,
u.is_active,
u.dept_id,
d.name AS dept_name,
u.created_at
FROM users u
LEFT JOIN sys_dept d ON u.dept_id = d.id
{where_clause}
ORDER BY u.created_at DESC
LIMIT %s OFFSET %s;
"""
cursor.execute(list_sql, params + [page_size, offset])
rows = cursor.fetchall()
result = []
for r in rows:
(
user_id,
username,
email,
phone,
is_active,
d_id,
d_name,
created_at,
) = r
cursor.execute(
"SELECT role_id FROM sys_user_role WHERE user_id = %s;",
(user_id,),
)
role_ids = [rr[0] for rr in cursor.fetchall()]
result.append(
{
"id": user_id,
"name": username,
"email": email,
"phone": phone,
"status": 1 if is_active else 0,
"dept_id": d_id,
"dept_name": d_name,
"roles": role_ids,
"created_at": format_datetime(created_at),
}
)
return result, total
# 检查用户名是否存在
def db_user_name_exists(username: str, id: str | None = None) -> 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,)
)
return cursor.fetchone()[0] > 0
# 插入用户
def insert_user(data: dict) -> str:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO users (username, email, phone, is_active, dept_id, password_hash)
VALUES (%s, %s, %s, %s, %s, %s) RETURNING id;
""",
(
data.get("name"),
data.get("email"),
data.get("phone"),
bool(data.get("status", 1)),
data.get("dept_id"),
data.get("password_hash", ""),
),
)
user_id = cursor.fetchone()[0]
# 插入用户角色
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);",
(user_id, role_id),
)
conn.commit()
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:
with conn.cursor() as cursor:
# ------------------------
# 1. 构建动态 SQL
# ------------------------
fields = []
params = []
mapping = {
"name": "username",
"email": "email",
"phone": "phone",
"status": "is_active",
"dept_id": "dept_id",
}
for k, column in mapping.items():
if k in data:
if k == "status":
fields.append(f"{column} = %s")
params.append(bool(data[k]))
else:
fields.append(f"{column} = %s")
params.append(data[k])
# 如果有需要更新的字段
if fields:
sql = f"UPDATE users SET {', '.join(fields)} WHERE id = %s"
params.append(id)
cursor.execute(sql, tuple(params))
# ------------------------
# 2. roles 单独处理
# ------------------------
if "roles" in data:
cursor.execute("DELETE FROM sys_user_role WHERE user_id=%s;", (id,))
roles = data.get("roles") or []
for role_id in roles:
cursor.execute(
"INSERT INTO sys_user_role (user_id, role_id) VALUES (%s, %s)",
(id, role_id),
)
conn.commit()
return cursor.rowcount
# 删除用户
def delete_user_db(id: str) -> int:
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute("DELETE FROM sys_user_role WHERE user_id=%s;", (id,))
cursor.execute("DELETE FROM users WHERE id=%s;", (id,))
conn.commit()
return cursor.rowcount
def get_role_ids_by_user(user_id: UUID):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"SELECT role_id FROM sys_user_role WHERE user_id = %s;", (str(user_id),)
)
rows = cursor.fetchall()
return [row[0] for row in rows]
def get_menu_ids_by_roles(role_ids: list):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
sql = f"SELECT DISTINCT menu_id FROM sys_role_menu WHERE role_id = ANY(%s);"
cursor.execute(sql, (role_ids,))
rows = cursor.fetchall()
return [row[0] for row in rows]
def get_menus_by_ids(menu_ids: list, plat_id: int):
if not menu_ids:
return []
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
sql = """
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
ORDER BY created_at ASC;
"""
# 转换 uuid 列表为 str 列表
menu_ids_str = [str(mid) for mid in menu_ids]
cursor.execute(sql, (menu_ids_str, plat_id))
rows = cursor.fetchall()
columns = [desc[0] for desc in cursor.description]
return [dict(zip(columns, row)) for row in rows]
def build_menu_tree(items):
item_map = {item["id"]: item for item in items}
tree = []
for item in items:
pid = item["pid"]
if pid and pid in item_map:
item_map[pid]["children"].append(item)
else:
tree.append(item)
return tree
+101
View File
@@ -0,0 +1,101 @@
import utils.MyUtils as MyUtils
from config.minIO import get_temp_url
from config.pgDb import pg_pool
def get_ticket_image_list(user_id):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT created_at, file_name, resolution, size, name,
moisture_content, cocoon_weight, defective_pupa_count,
fresh_shell_weight, sample_count, barcode, oss,
net_weight_total, evaluator, reviewer,id ,dead_pupa_count
FROM ticket_images
WHERE created_by = %s
""",
(user_id,),
)
rows = cursor.fetchall()
result = []
for row in rows:
result.append(
{
"created_at": MyUtils.format_datetime(row[0]),
"file_name": row[1],
"resolution": row[2],
"size": round(row[3] / 1024, 2),
"name": row[4],
"moisture_content": row[5],
"cocoon_weight": row[6],
"defective_pupa_count": row[7],
"fresh_shell_weight": row[8],
"sample_count": row[9],
"barcode": row[10],
"oss_url": get_temp_url("image-ticket", row[11]),
"net_weight_total": row[12],
"evaluator": row[13],
"reviewer": row[14],
"id": row[15],
"dead_pupa_count": row[16],
}
)
return result
def insert_ticket_image(
created_by,
file_name,
resolution,
size,
name,
moisture_content,
cocoon_weight,
defective_pupa_count,
dead_pupa_count,
fresh_shell_weight,
sample_count,
barcode,
oss,
net_weight_total,
evaluator,
reviewer,
):
with pg_pool.getConn() as conn:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO ticket_images (
created_by, file_name, resolution, size, name,
moisture_content, cocoon_weight, defective_pupa_count, dead_pupa_count,
fresh_shell_weight, sample_count, barcode, oss,
net_weight_total, evaluator, reviewer, created_at
)
VALUES (%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, NOW())
RETURNING id
""",
(
created_by,
file_name,
resolution,
size,
name,
moisture_content,
cocoon_weight,
defective_pupa_count,
dead_pupa_count,
fresh_shell_weight,
sample_count,
barcode,
oss,
net_weight_total,
evaluator,
reviewer,
),
)
new_id = cursor.fetchone()[0]
conn.commit()
return new_id
+32 -18
View File
@@ -1,33 +1,33 @@
from models.ChatRequest import ChatRequest
from models.ChatWithReportRequest import ChatWithReportRequest
from models.BaseResponse import BaseResponse
# import db.postgres as pg
import uuid
import db.postgres as pg
import db.sqlserver as sqlserver
import uuid
import threading
from fastapi import APIRouter, Depends
from uuid import UUID
from fastapi import APIRouter, Depends
import db.postgres as pg
from config.security import get_user_id_from_token
from models.BaseResponse import BaseResponse
from models.ChatRequest import ChatRequest
chatRouter = APIRouter()
from agent.dataAgent import get_graph_output
from llm.chatLLM import get_chat_response
from llm.titleChain import get_title
from llm.sqlLLM import get_sql_description_response,get_chat_sql_response,get_chat_sql_improve_response
from models.SaveReportRequest import SaveReportRequest
from models.AIProfilesRequest import AIProfilesRequest
import json
# 对话历史记录
@chatRouter.get("/history")
def getHistory(sessionId: str):
return BaseResponse(data=pg.get_history(sessionId))
# 对话列表
@chatRouter.get("/sessionsForBot")
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,'bot'))
return BaseResponse(data=pg.get_sessions(user_id, "bot"))
@chatRouter.post("/chatForBot")
def chat(req: ChatRequest, user_id: UUID = Depends(get_user_id_from_token)):
@@ -40,7 +40,7 @@ def chat(req: ChatRequest, user_id: UUID = Depends(get_user_id_from_token)):
if not req.sessionId:
is_new_session = True
req.sessionId = str(uuid.uuid4())
pg.insert_session(user_id,req.aiId, req.sessionId, session_name,"bot")
pg.insert_session(user_id, req.aiId, req.sessionId, session_name, "bot")
else:
is_new_session = False
pg.update_session_updated_at(req.sessionId)
@@ -51,11 +51,25 @@ def chat(req: ChatRequest, user_id: UUID = Depends(get_user_id_from_token)):
# 调用 LLM
if req.aiId == "9d157dd1-921b-c768-5b90-3e903b50f6f9":
# 数据专家AI
answer = get_graph_output(aiRole=pg.get_ai_personality(req.aiId),history=pg.get_history(req.sessionId), userInput= req.userInput)
answer = get_graph_output(
aiRole=pg.get_ai_personality(req.aiId),
history=pg.get_history(req.sessionId),
userInput=req.userInput,
)
else:
answer = get_chat_response(aiRole=pg.get_ai_personality(req.aiId),history=pg.get_history(req.sessionId), userInput= req.userInput).content
answer = get_chat_response(
aiRole=pg.get_ai_personality(req.aiId),
history=pg.get_history(req.sessionId),
userInput=req.userInput,
).content
# 插入 AI 回复
pg.insert_message(req.sessionId, True, answer)
return BaseResponse(data={"session_name":session_name,"isNewSession":is_new_session,"content":answer,"sessionId": req.sessionId})
return BaseResponse(
data={
"session_name": session_name,
"isNewSession": is_new_session,
"content": answer,
"sessionId": req.sessionId,
}
)
+377
View File
@@ -0,0 +1,377 @@
from fastapi import APIRouter, Depends
from config.security import get_user_id_from_token
from db.postgres.system import *
from models.BaseResponse import BaseResponse
from utils.MyUtils import build_dept_tree, format_datetime, build_menu_tree
systemRouter = APIRouter()
@systemRouter.get("/dept/list")
async def dept_list(user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
depts = get_all_depts()
# 字段映射(关键)
for d in depts:
# 映射字段
d["pid"] = d.pop("parent_id") or 0 # !!!null → 0
d["remark"] = d.pop("comment")
d["createTime"] = format_datetime(d.pop("created_at"))
# 前端示例格式要求有 status 字段
d["status"] = 1
# children 初始化
d["children"] = []
# 构建树
tree = build_dept_tree(depts)
return BaseResponse(data=tree)
@systemRouter.post("/dept/add")
async def dept_add(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
parent_id = data.get("pid")
name = data.get("name")
comment = data.get("comment")
if not name:
return BaseResponse(status=False, message="部门名不能为空", data=None)
new_id = insert_dept(parent_id, name, comment)
return BaseResponse(data={"id": new_id})
@systemRouter.put("/dept/{id}")
async def dept_update(
id: str, data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
parent_id = data.get("pid")
name = data.get("name")
comment = data.get("comment")
rowcount = update_dept(id, parent_id, name, comment)
if rowcount == 0:
return BaseResponse(status=False, message="部门不存在", data=None)
return BaseResponse(data=True)
@systemRouter.delete("/dept/{id}")
async def dept_dept(id: str, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
if has_child_dept(id):
return BaseResponse(status=False, message="禁止删除:请先删除子部门", data=None)
deleted = delete_dept(id)
if deleted == 0:
return BaseResponse(status=False, message="部门不存在", data=None)
return BaseResponse(data=True)
from fastapi import Depends
from uuid import UUID
@systemRouter.get("/menu/list/{plat_id}")
async def menu_list(plat_id: int, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
# 1. 根据用户ID获取角色ID列表
role_ids = get_role_ids_by_user(user_id)
if not role_ids:
BaseResponse(data=[]) # 用户没有角色,则返回空列表
# 2. 根据角色ID获取菜单ID列表
menu_ids = get_menu_ids_by_roles(role_ids)
if not menu_ids:
BaseResponse(data=[]) # 角色没有菜单权限,则返回空列表
# 3. 查询菜单信息
menus = get_menus_by_ids(menu_ids, plat_id)
# 4. 字段映射,兼容前端
for m in menus:
m["pid"] = m.get("pid") or "0"
m["meta"] = m.get("meta") or {}
m["createTime"] = format_datetime(m.get("created_at"))
m["updateTime"] = format_datetime(m.get("updated_at"))
m["children"] = []
# 5. 构建菜单树
tree = build_menu_tree(menus)
return BaseResponse(data=tree)
@systemRouter.get("/menu/list/all/{plat_id}")
async def menu_list_all(plat_id: int, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
if plat_id == -1:
menus = get_all_menus()
else:
menus = get_menus(plat_id)
# 字段映射
for m in menus:
m["pid"] = m.pop("pid") or "0"
m["meta"] = m["meta"] or {}
m["createTime"] = format_datetime(m.pop("created_at"))
m["updateTime"] = format_datetime(m.pop("updated_at"))
m["children"] = []
# 构建树,与 build_dept_tree 同逻辑
tree = build_menu_tree(menus)
return BaseResponse(data=tree)
@systemRouter.get("/menu/name-exists/{plat_id}")
async def menu_name_exists(
plat_id: int,
name: 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_menu_name_exists(name, id, plat_id)
return BaseResponse(data=exists)
@systemRouter.get("/menu/path-exists/{plat_id}")
async def menu_path_exists(
plat_id: int,
path: 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_menu_path_exists(path, id, plat_id)
return BaseResponse(data=exists)
@systemRouter.post("/menu/{plat_id}")
async def create_menu(
plat_id: int, data: dict, user_id: UUID = Depends(get_user_id_from_token)
):
if not user_id:
return {"error": "userId is required"}
new_id = insert_menu(data, plat_id)
return BaseResponse(data={"id": new_id})
@systemRouter.put("/menu/{id}")
async def update_menu(
id: str,
data: dict,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
count = update_menu_db(id, data)
if count == 0:
return BaseResponse(status=False, message="菜单不存在", data=None)
return BaseResponse(data=True)
@systemRouter.delete("/menu/{id}")
async def delete_menu(
id: str,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
if menu_has_children(id):
return BaseResponse(status=False, message="禁止删除:请先删除子菜单", data=None)
deleted = delete_menu_db(id)
if deleted == 0:
return BaseResponse(status=False, message="菜单不存在", data=None)
return BaseResponse(data=True)
@systemRouter.get("/role/name-exists")
async def role_name_exists(
name: 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_role_name_exists(name, id)
return BaseResponse(data=exists)
@systemRouter.get("/role/all")
async def get_role_list(user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
roles = get_role_list_db()
return BaseResponse(data=roles)
@systemRouter.get("/role/list")
async def get_role_list(
page: int = 1,
pageSize: int = 10,
id: str | None = None,
name: str | None = None,
remark: str | None = None,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
roles, total = get_role_list_db_page(page, pageSize, id, name, remark)
return BaseResponse(data={"list": roles, "total": total})
@systemRouter.post("/role")
async def create_role(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
new_id = insert_role(data)
return BaseResponse(data={"id": new_id})
@systemRouter.put("/role/{id}")
async def update_role(
id: str,
data: dict,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
count = update_role_db(id, data)
if count == 0:
return BaseResponse(status=False, message="角色不存在", data=None)
return BaseResponse(data=True)
@systemRouter.delete("/role/{id}")
async def delete_role(
id: str,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
deleted = delete_role_db(id)
if deleted == 0:
return BaseResponse(status=False, message="角色不存在", data=None)
return BaseResponse(data=True)
@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)
return BaseResponse(data=exists)
@systemRouter.get("/user/list")
async def get_user_list(
page: int = 1,
pageSize: int = 10,
id: str | None = None,
name: str | None = None,
status: 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"}
users, total = get_user_list_db_page(
page, pageSize, id, name, status, dept_id, startTime, endTime
)
return BaseResponse(data={"list": users, "total": total})
@systemRouter.post("/user")
async def create_user(data: dict, user_id: UUID = Depends(get_user_id_from_token)):
if not user_id:
return {"error": "userId is required"}
new_id = insert_user(data)
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,
data: dict,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
count = patch_user_db(id, data)
if count == 0:
return BaseResponse(status=False, message="用户不存在", data=None)
return BaseResponse(data=True)
@systemRouter.delete("/user/{id}")
async def delete_user(
id: str,
user_id: UUID = Depends(get_user_id_from_token),
):
if not user_id:
return {"error": "userId is required"}
deleted = delete_user_db(id)
if deleted == 0:
return BaseResponse(status=False, message="用户不存在", data=None)
return BaseResponse(data=True)
+41
View File
@@ -24,3 +24,44 @@ def format_datetime(dt: datetime, tz="Asia/Shanghai"):
def safe_round(value, ndigits=2, default=None):
return round(value, ndigits) if value is not None else default
def build_dept_tree(depts):
dept_map = {d["id"]: d for d in depts}
roots = []
for d in depts:
pid = d["pid"]
if pid and pid in dept_map:
dept_map[pid]["children"].append(d)
else:
# pid 为 0 或不存在 → 顶层
roots.append(d)
return roots
def build_menu_tree(items):
item_map = {item["id"]: item for item in items}
tree = []
for item in items:
pid = item["pid"]
if pid and pid in item_map:
item_map[pid]["children"].append(item)
else:
tree.append(item)
return tree
from uuid import UUID
def is_valid_uuid(value: str):
try:
UUID(value)
return True
except:
return False