You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
11 KiB

import json
import traceback
from app import app
from robyn import jsonify, Response
from service.OperationService import OperationService
from urllib.parse import unquote
# 实例化业务逻辑对象
operation_service = OperationService()
# --- 核心工具函数 ---
def create_response(status_code, data_dict):
"""
统一响应格式封装强制使用 UTF-8 防止中文乱码
"""
return Response(
status_code=status_code,
description=jsonify(data_dict),
headers={"Content-Type": "application/json; charset=utf-8"}
)
def parse_request_body(req):
"""
解析器适配 Robyn 框架确保能准确拿到前端传来的 IDnodeIdname label
"""
try:
body = getattr(req, "body", None)
if not body:
return {}
# 1. 处理 bytes 类型
if isinstance(body, (bytes, bytearray)):
body = body.decode('utf-8')
# 2. 如果已经是字典
if isinstance(body, dict):
return body
# 3. 处理字符串
if isinstance(body, str):
try:
data = json.loads(body)
# 处理双层 JSON 字符串转义的情况
if isinstance(data, str):
data = json.loads(data)
return data
except json.JSONDecodeError:
try:
from urllib.parse import parse_qs
params = parse_qs(body)
return {k: v[0] for k, v in params.items()}
except:
return {}
return {}
except Exception as e:
print(f"Request Body Parse Error: {e}")
return {}
def get_query_param(req, key, default=""):
"""
提取 URL 查询参数
"""
try:
data_source = getattr(req, "queries", None)
if data_source is None or (isinstance(data_source, dict) and not data_source):
data_source = getattr(req, "query_params", {})
if hasattr(data_source, "to_dict"):
data_source = data_source.to_dict()
val = data_source.get(key)
if val is None:
return default
raw_val = str(val[0]) if isinstance(val, list) else str(val)
return unquote(raw_val).strip()
except Exception as e:
print(f"Get Param Error ({key}): {e}")
return default
# --- 0. 数据治理修复接口 ---
@app.post("/api/kg/admin/fix-ids")
def fix_node_ids(req):
"""
手动触发修复数据库中 nodeId 为空或为 0 的存量数据
"""
try:
result = operation_service.fix_all_missing_node_ids()
return create_response(200, {
"code": 200 if result.get("success") else 500,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"修复接口异常: {str(e)}"})
# --- 1. 获取全量动态标签 (节点管理用) ---
@app.get("/api/kg/labels")
def get_labels(req):
try:
labels = operation_service.get_all_labels()
return create_response(200, {"code": 200, "data": labels, "msg": "success"})
except Exception as e:
traceback.print_exc()
return create_response(200, {"code": 500, "msg": f"获取标签失败: {str(e)}"})
# --- 新增:获取全量动态关系类型 (关系管理用) ---
@app.get("/api/kg/relationship-types")
def get_rel_types(req):
"""
从数据库动态获取所有关系类型 type 及其 label 映射
"""
try:
rel_types = operation_service.get_all_relationship_types()
return create_response(200, {"code": 200, "data": rel_types, "msg": "success"})
except Exception as e:
traceback.print_exc()
return create_response(200, {"code": 500, "msg": f"获取关系类型失败: {str(e)}"})
# --- 2. 输入联想建议 ---
@app.get("/api/kg/node/suggest")
def suggest_node(req):
try:
clean_keyword = get_query_param(req, "keyword", "")
suggestions = operation_service.suggest_nodes(clean_keyword)
return create_response(200, {"code": 200, "data": suggestions, "msg": "success"})
except Exception as e:
return create_response(200, {"code": 500, "msg": str(e)})
# --- 3. 获取分页节点列表 ---
@app.get("/api/kg/nodes")
def get_nodes(req):
try:
name_raw = get_query_param(req, "name", "")
label_raw = get_query_param(req, "label", "")
page_str = get_query_param(req, "page", "1")
size_str = get_query_param(req, "pageSize", "20")
page = int(page_str) if page_str.isdigit() else 1
page_size = int(size_str) if size_str.isdigit() else 20
name = name_raw if name_raw else None
label = label_raw if (label_raw and label_raw != "全部") else None
res_data = operation_service.get_nodes_subset(page, page_size, name=name, label=label)
return create_response(200, {"code": 200, "data": res_data, "msg": "success"})
except Exception as e:
traceback.print_exc()
return create_response(200, {"code": 500, "msg": f"获取节点失败: {str(e)}"})
# --- 4. 获取分页关系列表 ---
@app.get("/api/kg/relationships")
def get_relationships(req):
try:
source_raw = get_query_param(req, "source", "")
target_raw = get_query_param(req, "target", "")
type_raw = get_query_param(req, "type", "")
page_str = get_query_param(req, "page", "1")
size_str = get_query_param(req, "pageSize", "20")
page = int(page_str) if page_str.isdigit() else 1
page_size = int(size_str) if size_str.isdigit() else 20
source = source_raw if source_raw else None
target = target_raw if target_raw else None
rel_type = type_raw if (type_raw and type_raw != "全部") else None
res_data = operation_service.get_relationships_subset(page, page_size, source, target, rel_type)
return create_response(200, {"code": 200, "data": res_data, "msg": "success"})
except Exception as e:
traceback.print_exc()
return create_response(200, {"code": 500, "msg": f"获取关系失败: {str(e)}"})
# --- 5. 新增节点 ---
@app.post("/api/kg/node/add")
def add_node(req):
try:
body = parse_request_body(req)
label = str(body.get("label", "Drug")).strip()
name = str(body.get("name", "")).strip()
if not name:
return create_response(200, {"code": 400, "msg": "名称不能为空"})
result = operation_service.add_node(label, name)
return create_response(200, {
"code": 200 if result.get("success") else 400,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"新增异常: {str(e)}"})
# --- 6. 修改节点 ---
@app.post("/api/kg/node/update")
def update_node(req):
try:
body = parse_request_body(req)
node_id = body.get("id")
name = str(body.get("name", "")).strip()
label = str(body.get("label", "")).strip()
if not node_id or not name:
return create_response(200, {"code": 400, "msg": "参数缺失: 修改必须包含ID和名称"})
result = operation_service.update_node(node_id, name, label)
return create_response(200, {
"code": 200 if result.get("success") else 400,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"更新异常: {str(e)}"})
# --- 7. 新增关系 ---
@app.post("/api/kg/rel/add")
def add_relationship(req):
try:
body = parse_request_body(req)
source = str(body.get("source", "")).strip()
target = str(body.get("target", "")).strip()
rel_type = str(body.get("type", "")).strip()
rel_label = str(body.get("label", "")).strip() or rel_type
if not all([source, target, rel_type]):
return create_response(200, {"code": 400, "msg": "参数缺失: 起点、终点和类型为必填项"})
result = operation_service.add_relationship(source, target, rel_type, rel_label)
return create_response(200, {
"code": 200 if result.get("success") else 400,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"新增关系异常: {str(e)}"})
# --- 8. 修改关系 ---
@app.post("/api/kg/rel/update")
def update_rel(req):
try:
body = parse_request_body(req)
rel_id = body.get("id")
source = str(body.get("source", "")).strip()
target = str(body.get("target", "")).strip()
rel_type = str(body.get("type", "")).strip()
rel_label = str(body.get("label", "")).strip() or rel_type
if not rel_id:
return create_response(200, {"code": 400, "msg": "修改失败:关系ID缺失"})
result = operation_service.update_relationship(rel_id, source, target, rel_type, rel_label)
return create_response(200, {
"code": 200 if result.get("success") else 400,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"修改关系异常: {str(e)}"})
# --- 9. 删除节点 ---
@app.post("/api/kg/node/delete")
def delete_node(req):
try:
body = parse_request_body(req)
node_id = body.get("id")
if not node_id:
return create_response(200, {"code": 400, "msg": "删除失败: 未指定节点系统ID"})
result = operation_service.delete_node(node_id)
return create_response(200, {
"code": 200 if result.get("success") else 400,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"删除节点异常: {str(e)}"})
# --- 10. 删除关系 ---
@app.post("/api/kg/rel/delete")
def delete_rel(req):
try:
body = parse_request_body(req)
rel_id = body.get("id")
if not rel_id:
return create_response(200, {"code": 400, "msg": "删除失败: 未指定关系系统ID"})
result = operation_service.delete_relationship(rel_id)
return create_response(200, {
"code": 200 if result.get("success") else 400,
"msg": result.get("msg")
})
except Exception as e:
return create_response(200, {"code": 500, "msg": f"删除关系异常: {str(e)}"})
# --- 11. 获取图谱全局统计数据 ---
@app.get("/api/kg/stats")
def get_kg_stats(req):
try:
result = operation_service.get_kg_stats()
if result and result.get("success"):
return create_response(200, {"code": 200, "data": result.get("data"), "msg": "success"})
else:
msg = result.get("msg") if result else "未能获取统计数据"
return create_response(200, {"code": 400, "msg": msg})
except Exception as e:
traceback.print_exc()
return create_response(200, {"code": 500, "msg": f"统计数据异常: {str(e)}"})