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.
193 lines
7.5 KiB
193 lines
7.5 KiB
|
3 months ago
|
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):
|
||
|
|
"""
|
||
|
|
通用请求体解析器:解决解析 bytes、字符串或多重序列化的情况。
|
||
|
|
"""
|
||
|
|
body = req.body
|
||
|
|
try:
|
||
|
|
if isinstance(body, (bytes, bytearray)):
|
||
|
|
body = json.loads(body.decode('utf-8'))
|
||
|
|
if isinstance(body, str):
|
||
|
|
body = json.loads(body)
|
||
|
|
return body if isinstance(body, dict) else {}
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Request Body Parse Error: {e}")
|
||
|
|
return {}
|
||
|
|
|
||
|
|
# --- 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(500, {"code": 500, "msg": f"获取标签失败: {str(e)}"})
|
||
|
|
|
||
|
|
# --- 2. 输入联想建议 ---
|
||
|
|
@app.get("/api/kg/node/suggest")
|
||
|
|
def suggest_node(req):
|
||
|
|
try:
|
||
|
|
query_data = getattr(req, "queries", getattr(req, "query_params", {}))
|
||
|
|
raw_keyword = query_data.get("keyword", "")
|
||
|
|
keyword = raw_keyword[0] if isinstance(raw_keyword, list) and raw_keyword else str(raw_keyword)
|
||
|
|
clean_keyword = unquote(keyword).strip()
|
||
|
|
|
||
|
|
suggestions = operation_service.suggest_nodes(clean_keyword)
|
||
|
|
return create_response(200, {"code": 200, "data": suggestions, "msg": "success"})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(500, {"code": 500, "msg": str(e)})
|
||
|
|
|
||
|
|
# --- 3. 获取分页节点列表 ---
|
||
|
|
@app.get("/api/kg/nodes")
|
||
|
|
def get_nodes(req):
|
||
|
|
try:
|
||
|
|
query_data = getattr(req, "queries", getattr(req, "query_params", {}))
|
||
|
|
page = int(query_data.get("page", "1"))
|
||
|
|
page_size = int(query_data.get("pageSize", "20"))
|
||
|
|
|
||
|
|
res_data = operation_service.get_nodes_subset(page, page_size)
|
||
|
|
return create_response(200, {"code": 200, "data": res_data, "msg": "success"})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(500, {"code": 500, "msg": f"获取节点列表失败: {str(e)}"})
|
||
|
|
|
||
|
|
# --- 4. 获取分页关系列表 ---
|
||
|
|
@app.get("/api/kg/relationships")
|
||
|
|
def get_relationships(req):
|
||
|
|
try:
|
||
|
|
query_data = getattr(req, "queries", getattr(req, "query_params", {}))
|
||
|
|
page = int(query_data.get("page", "1"))
|
||
|
|
page_size = int(query_data.get("pageSize", "20"))
|
||
|
|
|
||
|
|
res_data = operation_service.get_relationships_subset(page, page_size)
|
||
|
|
return create_response(200, {"code": 200, "data": res_data, "msg": "success"})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(500, {"code": 500, "msg": f"获取关系列表失败: {str(e)}"})
|
||
|
|
|
||
|
|
# --- 5. 新增节点 ---
|
||
|
|
@app.post("/api/kg/node/add")
|
||
|
|
def add_node(req):
|
||
|
|
try:
|
||
|
|
body = parse_request_body(req)
|
||
|
|
# 增加 strip() 防止空格导致的匹配失败
|
||
|
|
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["success"] else 400, "msg": result["msg"]})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
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或Name)"})
|
||
|
|
|
||
|
|
result = operation_service.update_node(node_id, name, label)
|
||
|
|
return create_response(200, {"code": 200 if result["success"] else 400, "msg": result["msg"]})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(200, {"code": 500, "msg": str(e)})
|
||
|
|
|
||
|
|
# --- 7. 新增关系 ---
|
||
|
|
@app.post("/api/kg/rel/add")
|
||
|
|
def add_relationship(req):
|
||
|
|
try:
|
||
|
|
body = parse_request_body(req)
|
||
|
|
# 强制格式化所有字符串,彻底杜绝数据类型导致的 MATCH 失败
|
||
|
|
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()
|
||
|
|
|
||
|
|
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["success"] else 400, "msg": result["msg"]})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
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()
|
||
|
|
|
||
|
|
if not all([rel_id, source, target, rel_type]):
|
||
|
|
return create_response(200, {"code": 400, "msg": "修改失败:必要参数(ID/Source/Target/Type)缺失"})
|
||
|
|
|
||
|
|
result = operation_service.update_relationship(rel_id, source, target, rel_type, rel_label)
|
||
|
|
return create_response(200, {"code": 200 if result["success"] else 400, "msg": result["msg"]})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(200, {"code": 500, "msg": 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不能为空"})
|
||
|
|
|
||
|
|
operation_service.delete_node(node_id)
|
||
|
|
return create_response(200, {"code": 200, "msg": "节点及其关联关系已成功删除"})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(200, {"code": 500, "msg": 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不能为空"})
|
||
|
|
|
||
|
|
operation_service.delete_relationship(rel_id)
|
||
|
|
return create_response(200, {"code": 200, "msg": "关系已成功从数据库移除"})
|
||
|
|
except Exception as e:
|
||
|
|
traceback.print_exc()
|
||
|
|
return create_response(200, {"code": 500, "msg": str(e)})
|