调整数据库表名,增加催青阶段分析演示模块
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
from .aimessage import *
|
from .aimessage import *
|
||||||
from .aiprofile import *
|
from .aiprofile import *
|
||||||
from .aisession import *
|
from .aisession import *
|
||||||
|
from .image_ysa import *
|
||||||
from .iot import *
|
from .iot import *
|
||||||
from .knowledge import *
|
from .knowledge import *
|
||||||
from .license import *
|
from .license import *
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def get_all_ai_bot(user_id: str, module: str) -> List[Dict]:
|
|||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
# 查询用户角色
|
# 查询用户角色
|
||||||
cur.execute("SELECT roles FROM users WHERE id = %s", (user_id,))
|
cur.execute("SELECT roles FROM sys_users WHERE id = %s", (user_id,))
|
||||||
role_row = cur.fetchone()
|
role_row = cur.fetchone()
|
||||||
if not role_row:
|
if not role_row:
|
||||||
return [] # 用户不存在
|
return [] # 用户不存在
|
||||||
|
|||||||
@@ -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_ysa_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 image_sca (
|
||||||
|
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_ysa_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 image_ysa
|
||||||
|
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, silkworm_count, max_confidence, min_confidence, average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms
|
||||||
|
FROM image_ysa
|
||||||
|
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-ysa", "raw/" + row[4]),
|
||||||
|
"image_after": get_temp_url("image-ysa", "ai/" + row[5]),
|
||||||
|
"resolution": row[6],
|
||||||
|
"size": MyUtils.safe_round(row[7] / 1024, 2),
|
||||||
|
"silkworm_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
|
||||||
@@ -10,7 +10,7 @@ def insert_license_image(
|
|||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO license_images (
|
INSERT INTO image_license (
|
||||||
created_by, created_at, file_name, resolution, size, name, oss,
|
created_by, created_at, file_name, resolution, size, name, oss,
|
||||||
type, content
|
type, content
|
||||||
)
|
)
|
||||||
@@ -36,7 +36,7 @@ def get_license_image_list(user_id, page=1, page_size=10):
|
|||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM license_images
|
FROM image_license
|
||||||
WHERE created_by = %s
|
WHERE created_by = %s
|
||||||
""",
|
""",
|
||||||
(user_id,),
|
(user_id,),
|
||||||
@@ -47,7 +47,7 @@ def get_license_image_list(user_id, page=1, page_size=10):
|
|||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT created_at, file_name, resolution, size, name, oss, id, type, content
|
SELECT created_at, file_name, resolution, size, name, oss, id, type, content
|
||||||
FROM license_images
|
FROM image_license
|
||||||
WHERE created_by = %s
|
WHERE created_by = %s
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT %s OFFSET %s
|
LIMIT %s OFFSET %s
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ def insert_sca_image(
|
|||||||
other_info = json.dumps(other_info)
|
other_info = json.dumps(other_info)
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO sca_images (
|
INSERT INTO image_sca (
|
||||||
upload_datetime, file_name, resolution, size, cocoon_count, max_confidence, min_confidence,
|
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
|
average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms, name, image_pre, image_after, created_by
|
||||||
)
|
)
|
||||||
@@ -70,7 +70,7 @@ def get_sca_image_list(user_id, name, page=1, page_size=10):
|
|||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM sca_images
|
FROM image_sca
|
||||||
WHERE created_by = %s
|
WHERE created_by = %s
|
||||||
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||||
""",
|
""",
|
||||||
@@ -85,7 +85,7 @@ def get_sca_image_list(user_id, name, page=1, page_size=10):
|
|||||||
"""
|
"""
|
||||||
SELECT id, name, upload_datetime, file_name, image_pre, image_after, resolution,
|
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
|
size, cocoon_count, max_confidence, min_confidence, average_confidence, other_info, preprocess_time_ms, inference_time_ms, postprocess_time_ms
|
||||||
FROM sca_images
|
FROM image_sca
|
||||||
WHERE created_by = %s
|
WHERE created_by = %s
|
||||||
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
AND (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||||
ORDER BY upload_datetime DESC
|
ORDER BY upload_datetime DESC
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def get_sca_video_list(name, page=1, page_size=10):
|
|||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM sca_videos
|
FROM video_sca
|
||||||
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
|
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||||
""",
|
""",
|
||||||
(name, name),
|
(name, name),
|
||||||
@@ -30,7 +30,7 @@ def get_sca_video_list(name, page=1, page_size=10):
|
|||||||
SELECT id, name, raw_object_name, ai_object_name, duration, size, video_codec, audio_codec,
|
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,
|
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
|
sc_analysis_primary_type, sc_analysis_secondary_type, other_info, created_at
|
||||||
FROM sca_videos
|
FROM video_sca
|
||||||
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
|
WHERE (%s = '' OR name LIKE '%%' || %s || '%%')
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT %s OFFSET %s
|
LIMIT %s OFFSET %s
|
||||||
@@ -67,7 +67,7 @@ def get_sca_video_list(name, page=1, page_size=10):
|
|||||||
return total, result
|
return total, result
|
||||||
|
|
||||||
|
|
||||||
def get_sca_video_details(v_id):
|
def get_video_sca_details(v_id):
|
||||||
"""
|
"""
|
||||||
获取指定视频的分析明细列表
|
获取指定视频的分析明细列表
|
||||||
"""
|
"""
|
||||||
@@ -76,7 +76,7 @@ def get_sca_video_details(v_id):
|
|||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT id, v_id, time_stamp, other_info
|
SELECT id, v_id, time_stamp, other_info
|
||||||
FROM sca_video_details
|
FROM video_sca_details
|
||||||
WHERE v_id = %s
|
WHERE v_id = %s
|
||||||
ORDER BY time_stamp ASC
|
ORDER BY time_stamp ASC
|
||||||
""",
|
""",
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ def get_user_list_db_page(
|
|||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
|
|
||||||
# ---- 统计总数 ----
|
# ---- 统计总数 ----
|
||||||
count_sql = f"SELECT COUNT(*) FROM users u {where_clause};"
|
count_sql = f"SELECT COUNT(*) FROM sys_users u {where_clause};"
|
||||||
cursor.execute(count_sql, params)
|
cursor.execute(count_sql, params)
|
||||||
total = cursor.fetchone()[0]
|
total = cursor.fetchone()[0]
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ def get_user_list_db_page(
|
|||||||
u.dept_id,
|
u.dept_id,
|
||||||
d.name AS dept_name,
|
d.name AS dept_name,
|
||||||
u.created_at
|
u.created_at
|
||||||
FROM users u
|
FROM sys_users u
|
||||||
LEFT JOIN sys_dept d ON u.dept_id = d.id
|
LEFT JOIN sys_dept d ON u.dept_id = d.id
|
||||||
{where_clause}
|
{where_clause}
|
||||||
ORDER BY u.created_at DESC
|
ORDER BY u.created_at DESC
|
||||||
@@ -511,7 +511,9 @@ def get_user_list_db_page(
|
|||||||
def db_user_name_exists(username: str) -> 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:
|
||||||
cursor.execute("SELECT COUNT(*) FROM users WHERE email = %s;", (username,))
|
cursor.execute(
|
||||||
|
"SELECT COUNT(*) FROM sys_users WHERE email = %s;", (username,)
|
||||||
|
)
|
||||||
return cursor.fetchone()[0] > 0
|
return cursor.fetchone()[0] > 0
|
||||||
|
|
||||||
|
|
||||||
@@ -521,7 +523,7 @@ def insert_user(data: dict) -> str:
|
|||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO users (username, email, phone, is_active, dept_id, password_hash)
|
INSERT INTO sys_users (username, email, phone, is_active, dept_id, password_hash)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s) RETURNING id;
|
VALUES (%s, %s, %s, %s, %s, %s) RETURNING id;
|
||||||
""",
|
""",
|
||||||
(
|
(
|
||||||
@@ -578,7 +580,7 @@ def patch_user_db(id: str, data: dict) -> int:
|
|||||||
|
|
||||||
# 如果有需要更新的字段
|
# 如果有需要更新的字段
|
||||||
if fields:
|
if fields:
|
||||||
sql = f"UPDATE users SET {', '.join(fields)} WHERE id = %s"
|
sql = f"UPDATE sys_users SET {', '.join(fields)} WHERE id = %s"
|
||||||
params.append(id)
|
params.append(id)
|
||||||
cursor.execute(sql, tuple(params))
|
cursor.execute(sql, tuple(params))
|
||||||
|
|
||||||
@@ -603,7 +605,7 @@ def delete_user_db(id: str) -> int:
|
|||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute("DELETE FROM sys_user_role WHERE user_id=%s;", (id,))
|
cursor.execute("DELETE FROM sys_user_role WHERE user_id=%s;", (id,))
|
||||||
cursor.execute("DELETE FROM users WHERE id=%s;", (id,))
|
cursor.execute("DELETE FROM sys_users WHERE id=%s;", (id,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
return cursor.rowcount
|
return cursor.rowcount
|
||||||
|
|
||||||
@@ -882,7 +884,7 @@ def get_dept_ids_by_user_id(user_id: UUID) -> list:
|
|||||||
# 第一步:通过 user_id 查找其所属的 dept_id
|
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
cursor.execute("SELECT dept_id FROM sys_users WHERE id = %s", (user_id,))
|
||||||
dept_id = cursor.fetchone()
|
dept_id = cursor.fetchone()
|
||||||
|
|
||||||
if not dept_id:
|
if not dept_id:
|
||||||
@@ -914,7 +916,7 @@ def get_dept_ids_by_user_id(user_id: UUID) -> list:
|
|||||||
# 第一步:通过 user_id 查找其所属的 dept_id
|
# 第一步:通过 user_id 查找其所属的 dept_id
|
||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
cursor.execute("SELECT dept_id FROM sys_users WHERE id = %s", (user_id,))
|
||||||
dept_id = cursor.fetchone()
|
dept_id = cursor.fetchone()
|
||||||
|
|
||||||
if not dept_id:
|
if not dept_id:
|
||||||
@@ -946,7 +948,7 @@ def get_dept_id_by_user_id(user_id: str) -> str:
|
|||||||
# 通过 user_id 查找其所属的 dept_id
|
# 通过 user_id 查找其所属的 dept_id
|
||||||
with pg_pool.getConn() as conn:
|
with pg_pool.getConn() as conn:
|
||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute("SELECT dept_id FROM users WHERE id = %s", (user_id,))
|
cursor.execute("SELECT dept_id FROM sys_users WHERE id = %s", (user_id,))
|
||||||
dept_id = cursor.fetchone()
|
dept_id = cursor.fetchone()
|
||||||
dept_id = dept_id[0]
|
dept_id = dept_id[0]
|
||||||
return str(dept_id)
|
return str(dept_id)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ def get_ticket_image_list(user_id):
|
|||||||
moisture_content, cocoon_weight, defective_pupa_count,
|
moisture_content, cocoon_weight, defective_pupa_count,
|
||||||
fresh_shell_weight, sample_count, barcode, oss,
|
fresh_shell_weight, sample_count, barcode, oss,
|
||||||
net_weight_total, evaluator, reviewer,id ,dead_pupa_count
|
net_weight_total, evaluator, reviewer,id ,dead_pupa_count
|
||||||
FROM ticket_images
|
FROM image_ticket
|
||||||
WHERE created_by = %s
|
WHERE created_by = %s
|
||||||
""",
|
""",
|
||||||
(user_id,),
|
(user_id,),
|
||||||
@@ -66,7 +66,7 @@ def insert_ticket_image(
|
|||||||
with conn.cursor() as cursor:
|
with conn.cursor() as cursor:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"""
|
"""
|
||||||
INSERT INTO ticket_images (
|
INSERT INTO image_ticket (
|
||||||
created_by, file_name, resolution, size, name,
|
created_by, file_name, resolution, size, name,
|
||||||
moisture_content, cocoon_weight, defective_pupa_count, dead_pupa_count,
|
moisture_content, cocoon_weight, defective_pupa_count, dead_pupa_count,
|
||||||
fresh_shell_weight, sample_count, barcode, oss,
|
fresh_shell_weight, sample_count, barcode, oss,
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from sqlalchemy import text
|
|
||||||
from sqlalchemy import text
|
|
||||||
from config.pgDb import pg_pool
|
from config.pgDb import pg_pool
|
||||||
from config.ssDb import mssql_pool
|
from config.ssDb import mssql_pool
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
def executeSQL(sql: str):
|
def executeSQL(sql: str):
|
||||||
"""
|
"""
|
||||||
@@ -14,11 +13,14 @@ def executeSQL(sql: str):
|
|||||||
# SQLAlchemy 2.x 返回 Row 对象,转成 dict
|
# SQLAlchemy 2.x 返回 Row 对象,转成 dict
|
||||||
return [dict(row._mapping) for row in result]
|
return [dict(row._mapping) for row in result]
|
||||||
|
|
||||||
|
|
||||||
def get_company_list(user_id: str):
|
def get_company_list(user_id: str):
|
||||||
# 1️⃣ 从 PostgreSQL 获取 tenant_id
|
# 1️⃣ 从 PostgreSQL 获取 tenant_id
|
||||||
with pg_pool.getConn() as pg_conn:
|
with pg_pool.getConn() as pg_conn:
|
||||||
with pg_conn.cursor() as cur:
|
with pg_conn.cursor() as cur:
|
||||||
cur.execute("SELECT bbit_tenant_id FROM users WHERE id = %s", (user_id,))
|
cur.execute(
|
||||||
|
"SELECT bbit_tenant_id FROM sys_users WHERE id = %s", (user_id,)
|
||||||
|
)
|
||||||
row = cur.fetchone()
|
row = cur.fetchone()
|
||||||
tenant_id = row[0] if row else None
|
tenant_id = row[0] if row else None
|
||||||
|
|
||||||
@@ -33,5 +35,3 @@ def get_company_list(user_id: str):
|
|||||||
with mssql_pool.getConn() as mssql_conn:
|
with mssql_pool.getConn() as mssql_conn:
|
||||||
result = mssql_conn.execute(query, params)
|
result = mssql_conn.execute(query, params)
|
||||||
return [{"id": str(row[0]), "name": row[1]} for row in result.fetchall()]
|
return [{"id": str(row[0]), "name": row[1]} for row in result.fetchall()]
|
||||||
|
|
||||||
|
|
||||||
@@ -153,6 +153,25 @@ def getSilkwormCocoonAnalysisTasks(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ————————————————————————————————催青阶段分析————————————————————————————————————————————————
|
||||||
|
@visionRouter.get("/getPromoteStageAnalysisTasks")
|
||||||
|
def getPromoteStageAnalysisTasks(
|
||||||
|
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 = pg.get_ysa_image_list(user_id, name, page=page, page_size=page_size)
|
||||||
|
return BaseResponse(
|
||||||
|
data={
|
||||||
|
"total": total,
|
||||||
|
"items": items,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ————————————————————————————————蚕茧视频识别任务————————————————————————————————————————————————
|
# ————————————————————————————————蚕茧视频识别任务————————————————————————————————————————————————
|
||||||
|
|
||||||
|
|
||||||
@@ -190,4 +209,4 @@ def getAnalyticsDetailBySCVideoId(
|
|||||||
):
|
):
|
||||||
if not user_id:
|
if not user_id:
|
||||||
return {"error": "userId is required"}
|
return {"error": "userId is required"}
|
||||||
return BaseResponse(data=pg.get_sca_video_details(vId))
|
return BaseResponse(data=pg.get_video_sca_details(vId))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
|||||||
import org.jetbrains.exposed.v1.datetime.timestamp
|
import org.jetbrains.exposed.v1.datetime.timestamp
|
||||||
import org.jetbrains.exposed.v1.json.json
|
import org.jetbrains.exposed.v1.json.json
|
||||||
|
|
||||||
object UsersTable : UUIDTable("users") {
|
object UsersTable : UUIDTable("sys_users") {
|
||||||
// 用户名
|
// 用户名
|
||||||
val username = varchar("username", length = 50).nullable()
|
val username = varchar("username", length = 50).nullable()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
|
|||||||
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
||||||
|
|
||||||
|
|
||||||
object VideoAnalyticsDetailsTable : UUIDTable("video_analytics_details") {
|
object VideoAnalyticsDetailsTable : UUIDTable("video_act_details") {
|
||||||
val vId = uuid("v_id").references(VideosTable.id)
|
val vId = uuid("v_id").references(VideosTable.id)
|
||||||
val aTimeStamp = integer("time_stamp")
|
val aTimeStamp = integer("time_stamp")
|
||||||
val aTotalPeople = integer("total_people")
|
val aTotalPeople = integer("total_people")
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
|
|||||||
import org.jetbrains.exposed.v1.datetime.datetime
|
import org.jetbrains.exposed.v1.datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
object VideosTable : UUIDTable("videos") {
|
object VideosTable : UUIDTable("video_act") {
|
||||||
val vName = varchar("name", 255)
|
val vName = varchar("name", 255)
|
||||||
val vObjectName = varchar("object_name", 255)
|
val vObjectName = varchar("object_name", 255)
|
||||||
val vFileName = varchar("file_name", 255)
|
val vFileName = varchar("file_name", 255)
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ export * from './license';
|
|||||||
export * from './sca';
|
export * from './sca';
|
||||||
export * from './sca2';
|
export * from './sca2';
|
||||||
export * from './ticket';
|
export * from './ticket';
|
||||||
|
export * from './ysa';
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { pyRequestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已分析的图片列表
|
||||||
|
*/
|
||||||
|
export async function getSilkwormCocoonAnalysisTasks(
|
||||||
|
name = '',
|
||||||
|
page = 1,
|
||||||
|
pageSize = 9,
|
||||||
|
) {
|
||||||
|
return pyRequestClient.get('/cv/getPromoteStageAnalysisTasks', {
|
||||||
|
params: { name, page, page_size: pageSize },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传图片分析任务
|
||||||
|
*/
|
||||||
|
export async function createSilkwormCocoonAnalysisTask(formData: FormData) {
|
||||||
|
return pyRequestClient.post('/cv/createPromoteStageAnalysisTask', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,390 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { EchartsUIType } from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
computed,
|
||||||
|
onActivated,
|
||||||
|
onDeactivated,
|
||||||
|
onMounted,
|
||||||
|
ref,
|
||||||
|
watch,
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||||
|
|
||||||
|
import { Button, Form, Input, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import * as api from '#/api';
|
||||||
|
import { createSilkwormCocoonAnalysisTask } from '#/api';
|
||||||
|
|
||||||
|
const list = ref<any[]>([]);
|
||||||
|
const error = ref<null | string>(null);
|
||||||
|
const filterKeyword = ref('');
|
||||||
|
const selectedItem = ref<any>(null);
|
||||||
|
async function loadList() {
|
||||||
|
error.value = null;
|
||||||
|
const res = await api.getSilkwormCocoonAnalysisTasks(
|
||||||
|
filterKeyword.value,
|
||||||
|
page.value,
|
||||||
|
pageSize.value,
|
||||||
|
);
|
||||||
|
list.value = res.items || [];
|
||||||
|
total.value = res.total;
|
||||||
|
}
|
||||||
|
function refreshList() {
|
||||||
|
filterKeyword.value = '';
|
||||||
|
loadList();
|
||||||
|
message.success('列表加载完成');
|
||||||
|
}
|
||||||
|
function createTask() {
|
||||||
|
modalApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectItem(item: any) {
|
||||||
|
selectedItem.value = item;
|
||||||
|
refreshLineChart();
|
||||||
|
}
|
||||||
|
// 监听关键词变化,调用防抖接口
|
||||||
|
watch(filterKeyword, () => {
|
||||||
|
loadList();
|
||||||
|
});
|
||||||
|
watch(selectedItem, () => {});
|
||||||
|
onMounted(() => {
|
||||||
|
loadList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const showInfoStr = ref<Record<string, number | string>>({});
|
||||||
|
|
||||||
|
const chartRef1 = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts: renderEcharts1 } = useEcharts(chartRef1);
|
||||||
|
|
||||||
|
const chartRef2 = ref<EchartsUIType>();
|
||||||
|
const { renderEcharts: renderEcharts2 } = useEcharts(chartRef2);
|
||||||
|
|
||||||
|
function refreshLineChart() {
|
||||||
|
const data = selectedItem.value;
|
||||||
|
showInfoStr.value = {
|
||||||
|
项目名: data.name,
|
||||||
|
项目上传时间: data.upload_datetime,
|
||||||
|
文件名: data.file_name,
|
||||||
|
文件大小: `${data.size} MB`,
|
||||||
|
分辨率: data.resolution,
|
||||||
|
最高置信度: data.max_confidence,
|
||||||
|
最低置信度: data.min_confidence,
|
||||||
|
平均置信度: data.average_confidence,
|
||||||
|
预处理时间: `${data.preprocess_time_ms} ms`,
|
||||||
|
推理时间: `${data.inference_time_ms} ms`,
|
||||||
|
后处理时间: `${data.postprocess_time_ms} ms`,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderEcharts1({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
data: Object.keys(data.other_info),
|
||||||
|
axisTick: {
|
||||||
|
alignWithLabel: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '数量',
|
||||||
|
type: 'bar',
|
||||||
|
barWidth: '30%',
|
||||||
|
data: Object.values(data.other_info).map(Number),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
renderEcharts2({
|
||||||
|
legend: { top: '5%', left: 'center' },
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
animationDelay() {
|
||||||
|
return Math.random() * 100;
|
||||||
|
},
|
||||||
|
animationEasing: 'exponentialInOut',
|
||||||
|
animationType: 'scale',
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
|
||||||
|
data: Object.entries(data.other_info).map(([key, val]) => ({
|
||||||
|
name: key,
|
||||||
|
value: Number(val),
|
||||||
|
})),
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 10,
|
||||||
|
borderWidth: 3,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
position: 'center',
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
padAngle: 5,
|
||||||
|
name: '蚕茧分类',
|
||||||
|
radius: ['10%', '70%'],
|
||||||
|
type: 'pie',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeactivated(() => {
|
||||||
|
// 离开路由时清理状态
|
||||||
|
selectedItem.value = null;
|
||||||
|
showInfoStr.value = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
// 回来的时候重新刷新一次列表
|
||||||
|
loadList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const projectName = ref('');
|
||||||
|
const fileName = ref('');
|
||||||
|
const selectedFile = ref<File | null>(null);
|
||||||
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
title: '新建幼蚕分析任务',
|
||||||
|
class: 'w-[600px]',
|
||||||
|
onCancel() {
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
if (!selectedFile.value) {
|
||||||
|
message.warning('请选择幼蚕图片');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uploadFile();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function uploadFile() {
|
||||||
|
// 先关闭弹窗
|
||||||
|
modalApi.close();
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', selectedFile.value!);
|
||||||
|
formData.append('projectName', projectName.value);
|
||||||
|
await createSilkwormCocoonAnalysisTask(formData).then(() => {
|
||||||
|
// 清空表单
|
||||||
|
projectName.value = '';
|
||||||
|
fileName.value = '';
|
||||||
|
selectedFile.value = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 接口完成后再触发事件
|
||||||
|
message.success('分析完成');
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFile() {
|
||||||
|
fileInputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFileChange(event: Event) {
|
||||||
|
const files = (event.target as HTMLInputElement).files;
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
selectedFile.value = files[0];
|
||||||
|
fileName.value = files[0].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页参数
|
||||||
|
const page = ref(1);
|
||||||
|
const pageSize = ref(9);
|
||||||
|
const total = ref(0); // 总条数
|
||||||
|
// 计算总页数
|
||||||
|
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
|
||||||
|
|
||||||
|
function changePage(newPage) {
|
||||||
|
page.value = newPage;
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="flex h-[90dvh] w-full flex-col">
|
||||||
<h1>正在开发中,敬请期待</h1>
|
<Modal>
|
||||||
|
<Form layout="vertical">
|
||||||
|
<Form.Item label="任务名称">
|
||||||
|
<Input v-model:value="projectName" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label="上传幼蚕图片*" required>
|
||||||
|
<div
|
||||||
|
@click="selectFile"
|
||||||
|
style="
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ fileName || '点击选择文件' }}
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
ref="fileInputRef"
|
||||||
|
@change="handleFileChange"
|
||||||
|
style="display: none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
<div class="flex h-full w-full bg-gray-50">
|
||||||
|
<!-- 左侧:筛选 + 列表 -->
|
||||||
|
<div class="flex w-64 flex-col border-r bg-white p-4">
|
||||||
|
<!-- 按钮组 -->
|
||||||
|
<div class="mb-4 flex justify-between space-x-2">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
@click="createTask"
|
||||||
|
class="flex-1"
|
||||||
|
>
|
||||||
|
新建任务
|
||||||
|
</Button>
|
||||||
|
<Button @click="refreshList" class="flex-1"> 刷新列表 </Button>
|
||||||
|
</div>
|
||||||
|
<!-- 筛选框 -->
|
||||||
|
<input
|
||||||
|
v-model="filterKeyword"
|
||||||
|
placeholder="筛选分析任务"
|
||||||
|
class="mb-4 rounded-md border px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary"
|
||||||
|
/>
|
||||||
|
<!-- 列表 -->
|
||||||
|
<div class="flex-1 space-y-2 overflow-auto">
|
||||||
|
<div
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.v_id"
|
||||||
|
@click="selectItem(item)"
|
||||||
|
class="cursor-pointer rounded border p-3 hover:bg-gray-100"
|
||||||
|
:class="{ 'bg-gray-100': item.id === selectedItem?.id }"
|
||||||
|
>
|
||||||
|
<div class="text-base font-medium">{{ item.name }}</div>
|
||||||
|
<div class="text-sm text-gray-400">{{ item.upload_datetime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="mt-2 flex justify-center space-x-2">
|
||||||
|
<button
|
||||||
|
:disabled="page === 1"
|
||||||
|
@click="changePage(page - 1)"
|
||||||
|
class="rounded border px-1"
|
||||||
|
>
|
||||||
|
上一页
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<span>第 {{ page }} 页 / 共 {{ totalPages }} 页</span>
|
||||||
|
|
||||||
|
<button
|
||||||
|
:disabled="page === totalPages"
|
||||||
|
@click="changePage(page + 1)"
|
||||||
|
class="rounded border px-1"
|
||||||
|
>
|
||||||
|
下一页
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:Tab 内容区 -->
|
||||||
|
<div class="flex flex-1 flex-col overflow-hidden p-6">
|
||||||
|
<div
|
||||||
|
v-if="!selectedItem"
|
||||||
|
class="flex h-full items-center justify-center text-gray-400"
|
||||||
|
>
|
||||||
|
请先选择左侧列表中的分析任务
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div class="flex h-full flex-col gap-4">
|
||||||
|
<!-- 主内容区域:左右结构 -->
|
||||||
|
<div class="flex flex-1 gap-4">
|
||||||
|
<!-- 左侧 -->
|
||||||
|
<div class="flex w-72 flex-col gap-4">
|
||||||
|
<!-- 视频基础信息展示 -->
|
||||||
|
<div
|
||||||
|
class="w-full rounded border bg-white p-4"
|
||||||
|
id="video_base_info"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(value, key) in showInfoStr"
|
||||||
|
:key="key"
|
||||||
|
class="mb-2 flex text-sm text-gray-700"
|
||||||
|
>
|
||||||
|
<div class="w-32 font-medium text-gray-900">
|
||||||
|
{{ key }}:
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 break-all text-gray-600">
|
||||||
|
{{ value || '—' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 下:空白卡片 -->
|
||||||
|
<div class="flex-1 rounded border bg-white p-4">
|
||||||
|
<EchartsUI ref="chartRef2" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧 -->
|
||||||
|
<div class="flex flex-1 flex-col gap-4">
|
||||||
|
<!-- 上:左右两个图片显示 -->
|
||||||
|
<div class="flex flex-1 gap-4">
|
||||||
|
<!-- 左图 -->
|
||||||
|
<div
|
||||||
|
class="flex flex-1 items-center justify-center rounded border bg-white p-4"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="selectedItem?.image_pre"
|
||||||
|
alt=""
|
||||||
|
class="h-[30dvh] w-full object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- 右图 -->
|
||||||
|
<div
|
||||||
|
class="flex flex-1 items-center justify-center rounded border bg-white p-4"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
:src="selectedItem?.image_after"
|
||||||
|
alt=""
|
||||||
|
class="h-[30dvh] w-full object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 下:柱状图区域 -->
|
||||||
|
<div class="flex flex-1 flex-col rounded border bg-white p-4">
|
||||||
|
<EchartsUI ref="chartRef1" class="flex-1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user