瀏覽代碼

代码提交

SGTY 4 天之前
父節點
當前提交
9ab64a36dd

+ 62 - 1
src/knowledge/main.py

@@ -1,14 +1,75 @@
 # 导入FastAPI及相关模块
 # 导入FastAPI及相关模块
 import uvicorn
 import uvicorn
+from fastapi import FastAPI
 from fastapi_mcp import FastApiMCP
 from fastapi_mcp import FastApiMCP
 from py_tools.logging import logger
 from py_tools.logging import logger
 
 
+from src.knowledge.middlewares.base import register_middlewares
 from .settings import base_setting
 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 = FastApiMCP(app)
 mcp.mount()
 mcp.mount()
 mcp.setup_server()
 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():
 def main():
     logger.info(f"project run {base_setting.server_host}:{base_setting.server_port}")
     logger.info(f"project run {base_setting.server_host}:{base_setting.server_port}")
     uvicorn.run(
     uvicorn.run(

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

@@ -2,6 +2,24 @@ from pydantic import BaseModel
 from typing import Any, Optional
 from typing import Any, Optional
 
 
 class StandardResponse(BaseModel):
 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
     success: bool
     requestId: Optional[str] = None
     requestId: Optional[str] = None
     errorCode: Optional[int] = 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 fastapi import APIRouter, Depends, HTTPException
 from pydantic import BaseModel
 from pydantic import BaseModel
 from typing import List, Optional, Dict, Any
 from typing import List, Optional, Dict, Any
+
+from ..main import app
 from ..model.response import StandardResponse
 from ..model.response import StandardResponse
 from ..db.session import get_db
 from ..db.session import get_db
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import Session
 import logging
 import logging
 
 
-from ..server import app
+
 from ..service.search_service import SearchBusiness
 from ..service.search_service import SearchBusiness
 
 
 router = APIRouter(prefix="/medical", tags=["Medical Knowledge API"])
 router = APIRouter(prefix="/medical", tags=["Medical Knowledge API"])
@@ -23,7 +25,16 @@ class DiseaseInfoRequest(BaseModel):
     query: str
     query: str
     type: Optional[str] = None
     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(
 async def get_symptom_diseases(
     request: SymptomDiseasesRequest
     request: SymptomDiseasesRequest
 ):
 ):
@@ -36,35 +47,35 @@ async def get_symptom_diseases(
         logger.exception(f"获取症状相关疾病失败: {str(e)}")
         logger.exception(f"获取症状相关疾病失败: {str(e)}")
         raise HTTPException(500, detail=StandardResponse.error(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,
     response_model=StandardResponse,
     operation_id="get_disease_symptoms",
     operation_id="get_disease_symptoms",
     summary="获取疾病相关症状",
     summary="获取疾病相关症状",
@@ -153,8 +164,8 @@ class SimilarConceptsRequest(BaseModel):
     concept_id: str
     concept_id: str
     top_k: int = 5
     top_k: int = 5
 
 
-@router.post("/search_concept", 
-    operation_id="医学概念搜索", 
+@router.post("/search_concept",
+    operation_id="医学概念搜索",
     summary="根据名称和类型搜索医学概念",
     summary="根据名称和类型搜索医学概念",
     description="""根据概念名称和类型搜索医学概念节点。
     description="""根据概念名称和类型搜索医学概念节点。
     该接口主要用于医疗知识图谱中的概念搜索场景,例如:
     该接口主要用于医疗知识图谱中的概念搜索场景,例如:
@@ -190,8 +201,8 @@ async def search_concept(
         logger.error(f"搜索医学概念失败: {str(e)}")
         logger.error(f"搜索医学概念失败: {str(e)}")
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
 
 
-@app.post("/get_relations", 
-    operation_id="医学概念关系查询", 
+@app.post("/get_relations",
+    operation_id="医学概念关系查询",
     summary="根据概念ID查询相关关系",
     summary="根据概念ID查询相关关系",
     description="""根据概念ID查询该概念的相关关系。
     description="""根据概念ID查询该概念的相关关系。
     该接口主要用于医疗知识图谱中的关系查询场景,例如:
     该接口主要用于医疗知识图谱中的关系查询场景,例如:
@@ -228,7 +239,7 @@ async def get_relations(
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
         raise HTTPException(500, detail=StandardResponse.error(str(e)))
 
 
 @app.post("/get_similar_concepts", response_model=StandardResponse,
 @app.post("/get_similar_concepts", response_model=StandardResponse,
-    operation_id="相似概念查询", 
+    operation_id="相似概念查询",
     summary="根据概念ID查询相似概念",
     summary="根据概念ID查询相似概念",
     description="""根据概念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小于阈值的疾病
         # 按count降序排列并过滤掉count小于阈值的疾病
         sorted_diseases = sorted(disease_dict.values(), key=lambda x: x["count"], reverse=True)
         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]
         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
         return filtered_diseases
 
 
     def get_disease_symptoms(self, src_id):
     def get_disease_symptoms(self, src_id):