From 5915319f9fa805fc91b495227485fc1727a0a4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E5=8F=B2=E8=8D=92=E4=B8=98?= Date: Fri, 11 Sep 2026 10:47:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0main.py=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/deps.py | 14 +++++++++++++- api/router.py | 5 +++++ config/database/milvus.py | 12 +++++++++++- config/settings.py | 9 +++++++-- main.py | 12 +++++++++++- nl2sql/__init__.py | 0 requirements.txt | 15 +++++++++++++++ sql/schema.sql | 5 +++-- utils/response.py | 2 +- 9 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 nl2sql/__init__.py diff --git a/api/deps.py b/api/deps.py index 7d75ee1..94deffe 100644 --- a/api/deps.py +++ b/api/deps.py @@ -9,6 +9,9 @@ from service.auth import decode_token from utils.exceptions import AuthError, ForbiddenError +KNOWLEDGE_OPERATOR_ROLES = {"ADMIN", "KNOWLEDGE_ADMIN", "KNOWLEDGE_OPERATOR", "运营"} + + async def get_current_user( request: Request, db: AsyncSession = Depends(get_db) ) -> SysUser: @@ -24,4 +27,13 @@ async def get_current_user( raise AuthError("用户不存在") if user.status != "正常": raise ForbiddenError("账号已被禁用或冻结") - return user \ No newline at end of file + return user + + +async def require_knowledge_operator(user: SysUser = Depends(get_current_user)) -> SysUser: + """Allow only administrators or explicitly assigned knowledge operators.""" + if user.user_type == "ADMIN": + return user + if user.user_type != "EMPLOYEE" or user.employee_role not in KNOWLEDGE_OPERATOR_ROLES: + raise ForbiddenError("仅运营人员可以管理知识库") + return user diff --git a/api/router.py b/api/router.py index 3d132d6..42dbb97 100644 --- a/api/router.py +++ b/api/router.py @@ -3,9 +3,14 @@ """ from fastapi import APIRouter +from api.routers import auth +from api.routers import customer_agent +from api.routers import knowledge from api.routers import auth, product, questionnaire api_router = APIRouter() api_router.include_router(auth.router, prefix="/api", tags=["认证"]) +api_router.include_router(customer_agent.router, prefix="/api/agent/customer", tags=["客服Agent"]) +api_router.include_router(knowledge.router, prefix="/api/knowledge", tags=["知识库"]) api_router.include_router(product.router, prefix="/api", tags=["产品"]) api_router.include_router(questionnaire.router, prefix="/api", tags=["问卷"]) diff --git a/config/database/milvus.py b/config/database/milvus.py index 8004731..67329e6 100644 --- a/config/database/milvus.py +++ b/config/database/milvus.py @@ -24,6 +24,16 @@ def client() -> AsyncMilvusClient: return _client +async def ensure_database(milvus_client: AsyncMilvusClient | None = None) -> None: + """Create the configured project database only when it does not exist.""" + target = milvus_client or client() + database_name = settings.milvus.db_name + if not database_name: + raise RuntimeError("MILVUS_DB must be configured") + if database_name not in await target.list_databases(): + await target.create_database(database_name) + + async def init_db() -> None: client() # 急切建连;失败由注册表重试(config/database/__init__.py) @@ -36,4 +46,4 @@ async def dispose() -> None: async def check_health() -> None: - await client().get_server_version() \ No newline at end of file + await client().get_server_version() diff --git a/config/settings.py b/config/settings.py index 171da69..4349116 100644 --- a/config/settings.py +++ b/config/settings.py @@ -71,12 +71,17 @@ class MilvusCfg(BaseSettings): token: Optional[str] = None # 可缺省(无需鉴权时留空) user: Optional[str] = None password: Optional[str] = None - db_name: Optional[str] = None + db: Optional[str] = None connect_timeout: int # 建连/通道就绪超时(构造是急切连接,必须短) timeout: int # 数据操作超时,调用处可覆盖 model_config = SettingsConfigDict(env_prefix="MILVUS_", env_file=_ENV_FILE, extra="ignore") + @property + def db_name(self) -> Optional[str]: + """Compatibility name used by the Milvus client wrapper.""" + return self.db + class LLMCfg(BaseSettings): """大模型配置:本地 Ollama / OpenAI 兼容 API 双模式见 tool/llm.py。""" @@ -134,4 +139,4 @@ class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=_ENV_FILE, extra="ignore") -settings = Settings() \ No newline at end of file +settings = Settings() diff --git a/main.py b/main.py index b7e6be0..da752ab 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,10 @@ from fastapi import FastAPI from api.router import api_router from config import database +from service.customer_agent.bootstrap import ( + build_default_knowledge_upload_service, + build_default_runtime, +) from utils.exceptions import register_exception_handlers from utils.logger import setup_logging from utils.request_id import RequestIdMiddleware @@ -13,6 +17,8 @@ from utils.request_id import RequestIdMiddleware @asynccontextmanager async def lifespan(app: FastAPI): setup_logging() # 幂等:分级日志 + trace_id + 脱敏 + app.state.customer_agent_runtime = build_default_runtime() + app.state.knowledge_upload_service = build_default_knowledge_upload_service() # 四库会话懒创建:启动不连接任何库,首次访问才建,可手动预热: # asyncio.run(database.init_db()) yield @@ -28,4 +34,8 @@ app.include_router(api_router) @app.get("/") async def root(): - return {"message": "智能公募基金系统 API", "docs": "/docs"} \ No newline at end of file + return {"message": "智能公募基金系统 API", "docs": "/docs"} + +if __name__ == '__main__': + import uvicorn + uvicorn.run('main:app', host="127.0.0.1", port=8000) diff --git a/nl2sql/__init__.py b/nl2sql/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index 0fc9de2..1b34b21 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,18 @@ +fastapi +python-multipart +uvicorn +httpx +pyjwt +pydantic +pydantic-settings +sqlalchemy +aiomysql +redis +pymilvus +neo4j +python-dotenv +pypdf + fastapi~=0.141.1 sqlalchemy~=2.0.52 httpx~=0.28.1 diff --git a/sql/schema.sql b/sql/schema.sql index ac56327..a71c2a3 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -265,6 +265,7 @@ CREATE TABLE IF NOT EXISTS conversation_archive ( role VARCHAR(16) NOT NULL COMMENT 'user/assistant/system', content MEDIUMTEXT NULL COMMENT '对话内容', tool_calls JSON NULL COMMENT '工具调用记录 [{"tool":"nl2sql",...}]', + trace_id VARCHAR(64) NULL COMMENT '请求链路追踪ID', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_session (session_id), KEY idx_user_time (user_id, create_time), @@ -336,7 +337,7 @@ CREATE TABLE IF NOT EXISTS audit_log ( target VARCHAR(128) NULL COMMENT '操作对象(单号/ID)', detail TEXT NULL COMMENT '详情(JSON 字符串)', ip VARCHAR(64) NULL, - trace_id VARCHAR(32) NULL COMMENT '链路号', + trace_id VARCHAR(64) NULL COMMENT '链路号', status VARCHAR(8) NOT NULL DEFAULT '成功' COMMENT '成功/失败', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_id), @@ -436,4 +437,4 @@ CREATE TABLE IF NOT EXISTS portfolio_benchmark ( create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_risk_level (risk_level) -) COMMENT='组合基准配置表(投顾Agent 再平衡参照,运营可调整)'; \ No newline at end of file +) COMMENT='组合基准配置表(投顾Agent 再平衡参照,运营可调整)'; diff --git a/utils/response.py b/utils/response.py index f61ef0b..53a8062 100644 --- a/utils/response.py +++ b/utils/response.py @@ -15,7 +15,7 @@ class Code: SERVER_ERROR = 500 LLM_FAIL = 1001 # LLM 调用失败 KB_NO_RESULT = 1002 # 知识库检索无结果 - SQL_GEN_FAIL = 1003 # NL2SQL 生成失败 + SQL_GEN_FAIL = 1003 # nl2sql 生成失败 RISK_TRIGGERED = 1004 # 风控规则触发 / 交易被拦截 NOT_SUITABLE = 1005 # 适当性不匹配