SGTY 4 dagar sedan
förälder
incheckning
9ab64a36dd

+ 62 - 1
src/knowledge/main.py

@@ -1,14 +1,75 @@
 # 导入FastAPI及相关模块
 import uvicorn
+from fastapi import FastAPI
 from fastapi_mcp import FastApiMCP
 from py_tools.logging import logger
 
+from src.knowledge.middlewares.base import register_middlewares
 from .settings import base_setting
-from .server import app
+
+from contextlib import asynccontextmanager
+from py_tools.logging import logger
+from datetime import datetime
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+    await startup(app)
+    yield
+    await shutdown()
+app = FastAPI(
+    description="知识图谱开放平台",
+    lifespan=lifespan,
+    middleware=register_middlewares(),  # 注册web中间件
+)
 mcp = FastApiMCP(app)
 mcp.mount()
 mcp.setup_server()
 
+async def startup(app: FastAPI):
+    """项目启动时准备环境"""
+    from py_tools.logging import logger
+    from pathlib import Path
+    from starlette.staticfiles import StaticFiles
+    from .config.site import SiteConfig
+    from .router.graph_api import graph_router
+    from .router.knowledge_nodes_api import knowledge_nodes_api_router
+    from .router.knowledge_saas import saas_kb_router
+    from .router.text_search import text_search_router
+    from .utils import log_util
+    
+    log_util.setup_logger()
+    
+    # 加载路由
+    app.include_router(knowledge_nodes_api_router)
+    app.include_router(text_search_router)
+    app.include_router(graph_router)
+    app.include_router(saas_kb_router)
+    
+    # 挂载静态文件目录
+    config = SiteConfig()
+    books_path = Path(config.get_config("BOOKS"))
+    app.mount("/books", StaticFiles(directory=books_path), name="books")
+    static_dir = Path(__file__).parent / "static"
+    app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
+    
+    logger.info("fastapi startup success")
+
+async def shutdown():
+    from py_tools.connections.http import AsyncHttpClient
+    from py_tools.logging import logger
+    await AsyncHttpClient.close()
+    logger.error("app shutdown")
+
+
+@app.get("/health")
+async def health_check():
+    """健康检查接口"""
+    return {
+        "status": "ok",
+        "timestamp": datetime.utcnow().isoformat(),
+        "service": "knowledge-graph"
+    }
+
 def main():
     logger.info(f"project run {base_setting.server_host}:{base_setting.server_port}")
     uvicorn.run(

+ 18 - 0
src/knowledge/model/response.py

@@ -2,6 +2,24 @@ from pydantic import BaseModel
 from typing import Any, Optional
 
 class StandardResponse(BaseModel):
+    """
+    标准API响应格式
+    
+    该类定义了项目中所有API接口的统一响应格式,包含以下字段:
+    - success: 表示请求是否成功处理
+    - requestId: 可选请求ID,用于追踪请求
+    - errorCode: 可选错误代码,用于标识特定错误类型
+    - vaildCode: 验证结果编码(0:成功 1:时间过期 2:并发过高 3:次数耗尽 9:系统异常)
+    - errorMsg: 可选错误信息,包含错误详情
+    - data: 可选响应数据,包含业务数据
+    
+    典型使用场景:
+    1. 成功响应:
+       StandardResponse(success=True, data={"key": "value"})
+    
+    2. 错误响应:
+       StandardResponse(success=False, errorCode=500, errorMsg="Internal error")
+    """
     success: bool
     requestId: Optional[str] = None
     errorCode: Optional[int] = None

+ 47 - 36
src/knowledge/router/medical_knowledge_api.py

@@ -1,12 +1,14 @@
 from fastapi import APIRouter, Depends, HTTPException
 from pydantic import BaseModel
 from typing import List, Optional, Dict, Any
+
+from ..main import app
 from ..model.response import StandardResponse
 from ..db.session import get_db
 from sqlalchemy.orm import Session
 import logging
 
-from ..server import app
+
 from ..service.search_service import SearchBusiness
 
 router = APIRouter(prefix="/medical", tags=["Medical Knowledge API"])
@@ -23,7 +25,16 @@ class DiseaseInfoRequest(BaseModel):
     query: str
     type: Optional[str] = None
 
-@router.post("/symptom_diseases", response_model=StandardResponse)
+@app.post("/symptom_diseases", response_model=StandardResponse, operation_id="症状相关疾病查询", summary="根据症状获取相关疾病列表",
+         description="""根据输入的症状列表,查询可能相关的疾病列表。
+         该接口主要用于医疗知识图谱查询场景,例如:通过输入症状名称,
+         返回可能相关的疾病信息。
+         典型应用场景包括:
+         - 初步诊断:输入患者症状获取可能疾病
+         - 鉴别诊断:通过症状缩小疾病范围
+         - 健康教育:了解症状可能对应的疾病""",
+         response_description="""返回疾病名称的字符串列表,格式为:
+         ["疾病名称1", "疾病名称2", ...]""")
 async def get_symptom_diseases(
     request: SymptomDiseasesRequest
 ):
@@ -36,35 +47,35 @@ async def get_symptom_diseases(
         logger.exception(f"获取症状相关疾病失败: {str(e)}")
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
 
-@app.post("/test", operation_id="医疗知识图谱目标节点查询", summary="根据医疗知识图谱获取医疗相关信息",
-         description="""根据三元组的起始节点名称和关系名称,查询目标节点列表。
-         该接口主要用于医疗知识图谱查询场景,例如:通过输入疾病名称和相关关系类型,
-         返回该疾病对应的相关症状、治疗方法等信息。
-         典型应用场景包括:
-         - 症状查询:输入疾病名称和"疾病相关症状"关系
-         - 诊断依据查询:输入疾病名称和"诊断依据"关系
-         - 鉴别诊断查询:输入疾病名称和"疾病相关鉴别诊断"关系""",
-         response_description="""返回目标节点名称的字符串列表,格式为:
-         ["节点名称1", "节点名称2", ...]""")
-async def test(node_name: str = Query(...,
-                  description="""知识图谱三元组的起始节点名称,通常是疾病名称。
-                 示例值:感冒、高血压、糖尿病等""",
-                  example="糖尿病"),
-                node_category: str = Query(...,
-                  description="""知识图谱三元组的起始节点类型,通常是疾病。
-                 示例值:疾病、症状等""",
-                  example="疾病"),
-                relation_name: str= Query(...,
-                  description="""知识图谱三元组的关系名称,描述节点间的关系类型。
-                 常见关系类型包括:
-                 - 疾病相关症状
-                 - 诊断依据
-                 - 疾病相关鉴别诊断""",
-                  example="疾病相关症状"),
-                db: Session = Depends(get_db)) -> list[str]:
-    return None
-
-@app.post("/disease_symptoms", 
+# @app.post("/test", operation_id="医疗知识图谱目标节点查询", summary="根据医疗知识图谱获取医疗相关信息",
+#          description="""根据三元组的起始节点名称和关系名称,查询目标节点列表。
+#          该接口主要用于医疗知识图谱查询场景,例如:通过输入疾病名称和相关关系类型,
+#          返回该疾病对应的相关症状、治疗方法等信息。
+#          典型应用场景包括:
+#          - 症状查询:输入疾病名称和"疾病相关症状"关系
+#          - 诊断依据查询:输入疾病名称和"诊断依据"关系
+#          - 鉴别诊断查询:输入疾病名称和"疾病相关鉴别诊断"关系""",
+#          response_description="""返回目标节点名称的字符串列表,格式为:
+#          ["节点名称1", "节点名称2", ...]""")
+# async def test(node_name: str = Query(...,
+#                   description="""知识图谱三元组的起始节点名称,通常是疾病名称。
+#                  示例值:感冒、高血压、糖尿病等""",
+#                   example="糖尿病"),
+#                 node_category: str = Query(...,
+#                   description="""知识图谱三元组的起始节点类型,通常是疾病。
+#                  示例值:疾病、症状等""",
+#                   example="疾病"),
+#                 relation_name: str= Query(...,
+#                   description="""知识图谱三元组的关系名称,描述节点间的关系类型。
+#                  常见关系类型包括:
+#                  - 疾病相关症状
+#                  - 诊断依据
+#                  - 疾病相关鉴别诊断""",
+#                   example="疾病相关症状"),
+#                 db: Session = Depends(get_db)) -> list[str]:
+#     return None
+
+@app.post("/disease_symptoms",
     response_model=StandardResponse,
     operation_id="get_disease_symptoms",
     summary="获取疾病相关症状",
@@ -153,8 +164,8 @@ class SimilarConceptsRequest(BaseModel):
     concept_id: str
     top_k: int = 5
 
-@router.post("/search_concept", 
-    operation_id="医学概念搜索", 
+@router.post("/search_concept",
+    operation_id="医学概念搜索",
     summary="根据名称和类型搜索医学概念",
     description="""根据概念名称和类型搜索医学概念节点。
     该接口主要用于医疗知识图谱中的概念搜索场景,例如:
@@ -190,8 +201,8 @@ async def search_concept(
         logger.error(f"搜索医学概念失败: {str(e)}")
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
 
-@app.post("/get_relations", 
-    operation_id="医学概念关系查询", 
+@app.post("/get_relations",
+    operation_id="医学概念关系查询",
     summary="根据概念ID查询相关关系",
     description="""根据概念ID查询该概念的相关关系。
     该接口主要用于医疗知识图谱中的关系查询场景,例如:
@@ -228,7 +239,7 @@ async def get_relations(
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
 
 @app.post("/get_similar_concepts", response_model=StandardResponse,
-    operation_id="相似概念查询", 
+    operation_id="相似概念查询",
     summary="根据概念ID查询相似概念",
     description="""根据概念ID查询该概念的相似概念。
     该接口主要用于医疗知识图谱中的相似概念查询场景,例如:

+ 0 - 120
src/knowledge/server.py

@@ -1,120 +0,0 @@
-from contextlib import asynccontextmanager
-from datetime import datetime
-from pathlib import Path
-
-from fastapi import FastAPI
-from py_tools.connections.http import AsyncHttpClient
-from py_tools.logging import logger
-
-from starlette.staticfiles import StaticFiles
-
-from .config.site import SiteConfig
-from .middlewares.base import register_middlewares
-
-from .router.graph_api import graph_router
-from .router.knowledge_nodes_api import knowledge_nodes_api_router
-from .router.knowledge_saas import saas_kb_router
-from .router.text_search import text_search_router
-from .router.medical_knowledge_api import medical_knowledge_router
-
-from .utils import log_util
-
-from fastapi.openapi.docs import (
-    get_redoc_html,
-    get_swagger_ui_html,
-    get_swagger_ui_oauth2_redirect_html,
-)
-
-
-
-@asynccontextmanager
-async def lifespan(app: FastAPI):
-    await startup()
-    yield
-    await shutdown()
-
-config = SiteConfig()
-app = FastAPI(
-    description="知识图谱开放平台",
-    lifespan=lifespan,
-    middleware=register_middlewares(),  # 注册web中间件
-    docs_url=None,
-    redoc_url=None
-)
-@app.get("/health")
-async def health_check():
-    """健康检查接口"""
-    return {
-        "status": "ok",
-        "timestamp": datetime.utcnow().isoformat(),
-        "service": "knowledge-graph"
-    }
-
-
-
-async def init_setup():
-    """初始化项目配置"""
-
-    log_util.setup_logger()
-    
-async def startup():
-    """项目启动时准备环境"""
-
-    await init_setup()
-
-    # 加载路由
-    app.include_router(knowledge_nodes_api_router)
-    app.include_router(text_search_router)
-    app.include_router(graph_router)
-    app.include_router(saas_kb_router)
-    app.include_router(medical_knowledge_router)
-
-    # 挂载静态文件目录,将/books路径映射到本地books文件夹
-
-    books_path = Path(config.get_config("BOOKS"))
-    #books_path = Path("E:\\project\\knowledge\\books")
-
-    app.mount("/books", StaticFiles(directory=books_path), name="books")
-    static_dir = Path(__file__).parent / "static"
-    app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
-
-    # 定义一个异步函数,用于返回自定义的Swagger UI HTML页面
-    @app.get("/docs", include_in_schema=False)
-    async def custom_swagger_ui_html():
-        # 调用get_swagger_ui_html函数,传入openapi_url、title、oauth2_redirect_url、swagger_js_url和swagger_css_url参数
-        return get_swagger_ui_html(
-            openapi_url=app.openapi_url,
-            title=app.title + " - Swagger UI",
-            oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
-            swagger_js_url="/static/swagger-ui-bundle.js",
-            swagger_css_url="/static/swagger-ui.css",
-        )
-
-    @app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
-    async def swagger_ui_redirect():
-        return get_swagger_ui_oauth2_redirect_html()
-
-    @app.get("/redoc", include_in_schema=False)
-    async def redoc_html():
-        return get_redoc_html(
-            openapi_url=app.openapi_url,
-            title=app.title + " - ReDoc",
-            redoc_js_url="/static/redoc.standalone.js",
-        )
-
-    # 需要拦截的 URL 列表(支持通配符)
-    INTERCEPT_URLS = {
-        "/v1/knowledge/*"
-    }
-
-    # 白名单 URL(不需要拦截的路径)
-    WHITE_LIST = {
-        "/books/*",
-        "/knowledge/*"
-    }
-    logger.info("fastapi startup success")
-
-
-async def shutdown():
-    await AsyncHttpClient.close()
-    logger.error("app shutdown")

+ 2 - 1
src/knowledge/service/search_service.py

@@ -188,7 +188,8 @@ class SearchBusiness:
         # 按count降序排列并过滤掉count小于阈值的疾病
         sorted_diseases = sorted(disease_dict.values(), key=lambda x: x["count"], reverse=True)
         filtered_diseases = [disease for disease in sorted_diseases if disease["count"] >= threshold]
-
+        #json打印filtered_diseases
+        print("filtered_diseases:",json.dumps(filtered_diseases, ensure_ascii=False, indent=4))
         return filtered_diseases
 
     def get_disease_symptoms(self, src_id):