diff --git a/README.en.md b/README.en.md index 20e7daa2..ab741309 100644 --- a/README.en.md +++ b/README.en.md @@ -38,10 +38,11 @@ English | [简体中文](./README.md) | I want to… | Go to | |------------|--------| -| **Run the project locally ASAP** | Section **Quick Start** → **“First-time local setup (in order)”** (env files, migrations, backend + frontend) | +| **Run the project locally ASAP** | **Quick Start** → **“First-time local setup (in order)”** (env, deps, run; **first backend start auto-inits DB schema & seed data**) | +| **Architecture diagram & default ports (5180 / 8001, …)** | **“Local Architecture & Default Ports”** (matches `.env*.example`) | | **See what the project offers** | **Built-in Functional Modules**, **Demo Environment** (credentials) | | **Extend / plugin development** | **Secondary Development Tutorial**; backend layout and CLI: [**backend/README.md**](backend/README.md) | -| **API docs** | After the backend is up: `http:///docs` (Swagger) or `/redoc` | +| **API docs** | With template env: **`http://127.0.0.1:8001/docs`** (see `SERVER_PORT`) | ## 🎯 Core Advantages @@ -83,6 +84,42 @@ FastapiAdmin └─ README.md # Chinese documentation ``` +## 🏗️ Local Architecture & Default Ports + +Aligned with **`backend/env/.env.dev.example`** and **`frontend/.env.development.example`**; if you changed `.env.dev` / `.env.development`, use your local values. + +```mermaid +flowchart LR + subgraph client[Browser] + U[User] + end + subgraph fe[frontend dev] + V[Vue3 + Vite] + end + subgraph be[backend] + A[FastAPI / Uvicorn] + end + subgraph data[Data] + DB[(Database)] + R[(Redis)] + end + U --> V + V -->|REST via VITE_API_BASE_URL| A + A --> DB + A --> R +``` + +| Component | Config key | Example default (dev template) | +|-------------|------------|--------------------------------| +| Web UI | `frontend/.env.development` → `VITE_APP_PORT` | **5180** → **`http://127.0.0.1:5180`** | +| Backend HTTP | `backend/env/.env.dev` → `SERVER_HOST` / `SERVER_PORT` | **`0.0.0.0:8001`** → **`http://127.0.0.1:8001`** | +| API base URL | `VITE_API_BASE_URL` | **`http://127.0.0.1:8001`** | +| API prefix | `ROOT_PATH` + `VITE_APP_BASE_API` | **`/api/v1`** on both sides | +| Swagger / Redoc | — | **`http://127.0.0.1:8001/docs`**, `/redoc` | +| WebSocket (optional) | `VITE_APP_WS_ENDPOINT` | e.g. **`ws://127.0.0.1:8001`** | +| DB port | `DATABASE_PORT` | Template uses MySQL **`3306`**; PostgreSQL often **`5432`** | +| Redis | `REDIS_HOST` / `REDIS_PORT` | Example **`localhost:6379`** | + ## 🛠️ Technology Stack Overview | Type | Technology Selection | Description | @@ -137,10 +174,12 @@ With **Pydantic v2** and **PostgreSQL (asyncpg)**, ORM writes expect native Pyth 1. **Install runtimes**: Python ≥ 3.10, Node.js ≥ 20, [pnpm](https://pnpm.io/), local **MySQL or PostgreSQL** (or SQLite if configured in `backend/env/.env.dev`), and **Redis** matching your `.env.dev`. 2. **Clone the repo**: see “Get the Code” below. 3. **Env files**: copy `backend/env/.env.dev.example` → `backend/env/.env.dev`, and `frontend/.env.development.example` → `frontend/.env.development`; fill in **DB, Redis, JWT secret**, etc. Create an empty database first. -4. **Backend deps + migrations**: `cd backend`, run **`uv sync`** (recommended), then **`uv run main.py upgrade --env=dev`** to **apply the schema** (required on first run). -5. **Start backend**: `uv run main.py run --env=dev`. +4. **Backend dependencies**: `cd backend`, run **`uv sync`** (recommended) or `pip install -r requirements.txt`. +5. **Start backend**: `uv run main.py run --env=dev`. **The first start automatically initializes tables and seed data**—you usually **do not** need to run `upgrade` first. 6. **Frontend**: `cd frontend`, `pnpm install`, `pnpm run dev`. -7. **Browser**: use the URL printed by Vite (port from `VITE_APP_PORT` in `frontend/.env.development`); log in with the admin account (same as [Demo Environment](#-demo-environment) unless you changed seed data). +7. **Browser**: with the template env, open **`http://127.0.0.1:5180`** (`VITE_APP_PORT=5180`); log in with the admin account (same as [Demo Environment](#-demo-environment) unless you changed seed data). + +> Use **`revision` / `upgrade`** only when you change ORM models and manage migrations with Alembic (see FAQ below). ### Environment Requirements @@ -173,12 +212,12 @@ git clone https://github.com/fastapiadmin/FastapiAdmin.git ```bash cd backend uv sync -uv run main.py upgrade --env=dev +# First start auto-inits schema & data; no need to run upgrade beforehand uv run main.py run --env=dev # uv run main.py run --env=prod ``` -> Without `uv`: `pip install -r requirements.txt`, then `python main.py upgrade --env=dev` and `python main.py run --env=dev`. +> Without `uv`: `pip install -r requirements.txt`, then `python main.py run --env=dev`. Use `upgrade` when you need Alembic after model changes. #### Using pip / venv @@ -188,7 +227,6 @@ python -m venv .venv # Windows: .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate pip install -r requirements.txt -python main.py upgrade --env=dev python main.py run --env=dev ``` @@ -203,11 +241,14 @@ pnpm run build ### After startup -| Service | Notes | -|---------|--------| -| Backend API | Default often `http://127.0.0.1:8000` (check logs and `backend/env/.env.dev`) | -| Swagger | `http:///docs` | -| Web UI | URL printed by Vite (`VITE_APP_PORT` in `frontend/.env.development`) | +When using **`.env.dev.example` / `.env.development.example`** as-is: + +| Service | URL (example) | +|---------|----------------| +| Web UI (Vite) | `http://127.0.0.1:5180` | +| Backend base | `http://127.0.0.1:8001` | +| Swagger | `http://127.0.0.1:8001/docs` | +| API prefix | `http://127.0.0.1:8001/api/v1` (matches `ROOT_PATH`) | ### 🐳 Docker Deployment @@ -458,7 +499,7 @@ async def get_detail( - **Code Generator**: Automatically generate front-end and back-end CRUD code - **API Documentation**: Automatically generate Swagger/Redoc API documentation -- **Database Migration**: Support for Alembic database migration +- **Database**: Auto schema & seed on first start; Alembic supported for schema evolution - **Log System**: Built-in log recording and query functions - **Monitoring System**: Built-in server monitoring and cache monitoring functions @@ -496,10 +537,13 @@ A: Configure Redis connection information in `backend/env/.env.dev` or `backend/ A: Use the command `python main.py revision --env=dev` to generate migration files. #### Q: How to apply database migrations? -A: Use the command `python main.py upgrade --env=dev` to apply migrations. +A: Run `python main.py upgrade --env=dev` (or `uv run ...`) when you **need Alembic migrations**. **First start usually does not require this**—the app initializes automatically. #### Q: How to start the development server? -A: From `backend`, run `uv run main.py run --env=dev` (or `python main.py run --env=dev`). On **first run**, install dependencies and run `upgrade` migrations first—see **“First-time local setup”** above. +A: From `backend`, run `uv run main.py run --env=dev` (or `python main.py run --env=dev`). **First start auto-initializes the database and seed data**; no manual `upgrade` is required beforehand. + +#### Q: Do I need to run migrations before the first start? +A: **Usually no.** The first backend start initializes schema and data. Use `revision` / `upgrade` only when you change models and use Alembic. #### Q: How to build the frontend production version? A: Use the command `pnpm run build` to build the frontend production version. diff --git a/README.md b/README.md index 660be730..9a2a9099 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,11 @@ | 你想…… | 去看 | |--------|------| -| **最快在本地跑起来** | 下文 **「快速开始」** → **「第一次本地运行(按顺序)」**(含复制环境文件、迁移、启动前后端) | +| **最快在本地跑起来** | 下文 **「快速开始」** → **「第一次本地运行(按顺序)」**(含复制环境文件、安装依赖、启动前后端;**首次启动后端会自动初始化库表与基础数据**) | +| **架构图与默认端口(5180 / 8001 等)** | **「本地架构与默认端口」**(与 `.env*.example` 一致) | | **先了解项目能做什么** | **「内置功能模块」**、**「演示环境」**(账号密码) | | **做二次开发 / 插件** | **「二开教程」**;后端目录与命令见 [**backend/README.md**](backend/README.md) | -| **接口文档** | 后端启动后浏览器打开 `http://<后端地址>/docs`(Swagger)或 `/redoc` | +| **接口文档** | 模板下 Swagger:**`http://127.0.0.1:8001/docs`**(端口以 `SERVER_PORT` 为准) | ## 🎯 核心优势 @@ -83,6 +84,42 @@ FastapiAdmin └─ README.md # 中文文档 ``` +## 🏗️ 本地架构与默认端口 + +与仓库内 **`backend/env/.env.dev.example`**、**`frontend/.env.development.example`** 保持一致;若你本地已改 `.env.dev` / `.env.development`,以实际文件为准。 + +```mermaid +flowchart LR + subgraph client[浏览器] + U[用户] + end + subgraph fe[frontend 开发] + V[Vue3 + Vite] + end + subgraph be[backend] + A[FastAPI / Uvicorn] + end + subgraph data[数据层] + DB[(数据库)] + R[(Redis)] + end + U --> V + V -->|REST 见 VITE_API_BASE_URL| A + A --> DB + A --> R +``` + +| 组件 | 配置项 | 示例默认值(开发模板) | +|------|--------|------------------------| +| 前端页面 | `frontend/.env.development` → `VITE_APP_PORT` | **5180**,即 **`http://127.0.0.1:5180`** | +| 后端 HTTP | `backend/env/.env.dev` → `SERVER_HOST` / `SERVER_PORT` | **`0.0.0.0:8001`**,本机访问 **`http://127.0.0.1:8001`** | +| 前端请求后端 | `VITE_API_BASE_URL` | **`http://127.0.0.1:8001`** | +| API 前缀 | `ROOT_PATH`(后端)+ `VITE_APP_BASE_API`(前端) | 后端 **`/api/v1`**;前端代理前缀 **`/api/v1`** | +| Swagger / Redoc | — | **`http://127.0.0.1:8001/docs`**、`/redoc` | +| WebSocket(可选) | `VITE_APP_WS_ENDPOINT` | 示例 **`ws://127.0.0.1:8001`** | +| 数据库端口 | `DATABASE_PORT` | 模板为 MySQL **`3306`**;PostgreSQL 常见 **`5432`** | +| Redis | `REDIS_HOST` / `REDIS_PORT` | 示例 **`localhost:6379`** | + ## 🛠️ 技术栈概览 | 类型 | 技术选型 | 描述 | @@ -139,10 +176,12 @@ FastapiAdmin 1. **安装运行时**:Python ≥ 3.10、Node.js ≥ 20、[pnpm](https://pnpm.io/zh/)(前端包管理)、本机 **MySQL 或 PostgreSQL**(或改用 SQLite 需在 `backend/env/.env.dev` 中配置)、**Redis**(与 `.env.dev` 中一致)。 2. **获取代码**:见下方「获取代码」。 3. **配置环境变量**:将 `backend/env/.env.dev.example` 复制为 `backend/env/.env.dev`,将 `frontend/.env.development.example` 复制为 `frontend/.env.development`,按注释填写 **数据库连接、Redis、JWT 密钥** 等(须先在本机创建空数据库)。 -4. **安装后端依赖并迁移**:进入 `backend`,推荐使用 `uv sync`(见下);然后执行 **`uv run main.py upgrade --env=dev`**(或 `python main.py upgrade --env=dev`)**应用数据库表结构**;首次部署不可跳过。 -5. **启动后端**:`uv run main.py run --env=dev`(默认开发环境)。 +4. **安装后端依赖**:进入 `backend`,推荐使用 **`uv sync`**(见下「后端启动」);或使用 `pip install -r requirements.txt`。 +5. **启动后端**:`uv run main.py run --env=dev`。**首次启动会自动初始化数据库表结构及基础数据**,一般**无需**事先执行 `upgrade` 迁移命令。 6. **安装前端依赖并启动**:进入 `frontend` 执行 `pnpm install` 与 `pnpm run dev`。 -7. **打开浏览器**:前端地址见终端输出(端口由 `frontend/.env.development` 中 `VITE_APP_PORT` 决定);使用管理员账号登录(与 [演示环境](#-演示环境) 一致,若你导入的是初始 SQL 则以后台为准)。 +7. **打开浏览器**:按模板一般为 **`http://127.0.0.1:5180`**(`VITE_APP_PORT=5180`);使用管理员账号登录(与 [演示环境](#-演示环境) 一致,若你导入的是初始 SQL 则以后台为准)。 + +> **说明**:若你**修改了 ORM 模型**并需用 Alembic 管理变更,再使用 `python main.py revision` / `upgrade`(见下文「常见问题」)。 ### 环境要求 @@ -174,17 +213,15 @@ git clone https://github.com/fastapiadmin/FastapiAdmin.git ```bash cd backend -# 创建虚拟环境并安装依赖(等价于根据 pyproject 安装) uv sync -# 首次或模型变更后:应用数据库迁移(不可省略) -uv run main.py upgrade --env=dev -# 启动:请先保证数据库已创建、Redis 已启动且与 .env.dev 一致 +# 启动:请先保证已创建空数据库、Redis 已启动且与 .env.dev 一致 +# 首次启动会自动初始化表与基础数据,无需先执行 upgrade uv run main.py run --env=dev # 生产环境示例 # uv run main.py run --env=prod ``` -> 若未使用 `uv`,也可用 `pip install -r requirements.txt` 安装依赖,再用 `python main.py upgrade --env=dev` 与 `python main.py run --env=dev`。 +> 若未使用 `uv`:`pip install -r requirements.txt` 后直接 `python main.py run --env=dev`。模型变更需走 Alembic 时再执行 `upgrade`。 #### 使用传统 pip / venv @@ -194,7 +231,6 @@ python -m venv .venv # Windows: .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate pip install -r requirements.txt -python main.py upgrade --env=dev python main.py run --env=dev ``` @@ -210,11 +246,14 @@ pnpm run build ### 启动后访问 -| 服务 | 说明 | -|------|------| -| 后端 API | 默认 `http://127.0.0.1:8000`(具体端口以启动日志与 `backend/env/.env.dev` 为准) | -| Swagger | `http://<后端地址>/docs` | -| 前端 Web | 终端中 Vite 输出的本地地址(端口见 `frontend/.env.development` 的 `VITE_APP_PORT`) | +与 **`.env.dev.example` / `.env.development.example`** 对齐时: + +| 服务 | 地址(示例) | +|------|----------------| +| 前端 Web(Vite) | `http://127.0.0.1:5180` | +| 后端 API 根 | `http://127.0.0.1:8001` | +| Swagger | `http://127.0.0.1:8001/docs` | +| 业务接口前缀 | `http://127.0.0.1:8001/api/v1`(与 `ROOT_PATH` 一致) | ### 🐳 Docker 部署 @@ -452,7 +491,7 @@ async def get_detail( - **代码生成器**:自动生成前后端CRUD代码 - **API文档**:自动生成Swagger/Redoc API文档 -- **数据库迁移**:支持Alembic数据库迁移 +- **数据库**:首次启动自动初始化表与基础数据;结构演进可配合 Alembic - **日志系统**:内置日志记录和查询功能 - **监控系统**:内置服务器监控和缓存监控功能 @@ -490,10 +529,13 @@ A:在 `backend/env/.env.dev` 或 `backend/env/.env.prod` 文件中配置Redis A:使用 `python main.py revision --env=dev` 命令生成迁移文件。 #### Q:如何应用数据库迁移? -A:使用 `python main.py upgrade --env=dev` 命令应用迁移。 +A:在**需要执行 Alembic 迁移**时使用 `python main.py upgrade --env=dev`(或 `uv run ...`)。**首次启动一般不必先执行**,由应用自动初始化。 #### Q:如何启动开发服务器? -A:在 `backend` 目录执行 `uv run main.py run --env=dev`(或 `python main.py run --env=dev`)。**首次**须先完成依赖安装与 `upgrade` 迁移,见上文 **「第一次本地运行」**。 +A:在 `backend` 目录执行 `uv run main.py run --env=dev`(或 `python main.py run --env=dev`)。**首次启动会自动初始化数据库与基础数据**,无需先手动执行 `upgrade`。 + +#### Q:首次启动要先做数据库迁移吗? +A:**一般不需要**。首次启动后端会自动完成库表与初始数据;仅在你**自行修改模型**并需用 Alembic 时再使用 `revision` / `upgrade`。 #### Q:如何构建前端生产版本? A:使用 `pnpm run build` 命令构建前端生产版本。 diff --git a/backend/README.md b/backend/README.md index 90a72016..6cedf265 100644 --- a/backend/README.md +++ b/backend/README.md @@ -2,7 +2,9 @@ 一个基于 FastAPI 框架构建企业级后端架构解决方案,为前端 Vue3 管理系统提供完整的 API 服务支持。 -> **和仓库根目录文档的关系**:**一键前后端启动、演示账号、Docker 部署、新手导航** 请以仓库根目录 [**README.md**](../README.md)(英文 [**README.en.md**](../README.en.md))为准;**本文档**侧重 `backend/` 目录结构、迁移命令与开发约定。 +> **和仓库根目录文档的关系**:**一键前后端启动、演示账号、Docker 部署、新手导航、Mermaid 架构图与默认端口(5180 / 8001 等)** 请以仓库根目录 [**README.md**](../README.md)(英文 [**README.en.md**](../README.en.md))为准;**本文档**侧重 `backend/` 目录结构、迁移命令与开发约定。 + +与 **`env/.env.dev.example`** 对齐时:**`SERVER_PORT=8001`**(本机 **`http://127.0.0.1:8001`**),**`ROOT_PATH=/api/v1`**,Swagger **`/docs`**;前端开发端口见 **`../frontend/.env.development.example`** 中的 **`VITE_APP_PORT=5180`**、`VITE_API_BASE_URL=http://127.0.0.1:8001`。 ## 🚀 项目特性 @@ -96,15 +98,16 @@ module_*/ 1. 复制 `env/.env.dev.example` → `env/.env.dev`,填写数据库、Redis 等(先在 DB 中建好空库)。 2. 在 **`backend/` 目录下** 安装依赖:推荐 **`uv sync`**;或 `pip install -r requirements.txt`。 -3. **应用迁移**:`uv run main.py upgrade --env=dev`(或 `python main.py upgrade --env=dev`)。首次运行不可跳过。 -4. **启动**:`uv run main.py run --env=dev`。接口文档:`http://:/docs`。 +3. **启动**:`uv run main.py run --env=dev`(或 `python main.py run --env=dev`)。**首次启动会自动初始化数据库表与基础数据**,一般**无需**先执行 `upgrade`。接口文档示例:`http://127.0.0.1:8001/docs`(端口见 `.env.dev` 中 `SERVER_PORT`)。 -### 数据库迁移命令 +### 数据库迁移命令(模型变更时使用) + +日常**首次启动不必手动执行**;当你**修改了 ORM 模型**并需用 Alembic 管理结构变更时再使用: ```bash -# 生成迁移文件(模型变更时) +# 生成迁移文件(模型变更后) python main.py revision --env=dev -# 应用迁移(首次启动、拉代码后必做) +# 应用迁移 python main.py upgrade --env=dev # 使用 uv 时 diff --git a/backend/app/core/discover.py b/backend/app/core/discover.py index 19971775..73ea07c3 100644 --- a/backend/app/core/discover.py +++ b/backend/app/core/discover.py @@ -1,10 +1,22 @@ """ 简化的动态路由发现与注册 -约定: -- 扫描 `app.plugin` 下所有以 `module_` 开头的顶级目录 -- 在各模块任意子目录下的 `controller.py` 中定义的 `APIRouter` 实例会自动被注册 -- 顶级目录 `module_xxx` 会映射为容器路由前缀 `/` +目录与命名规范(不满足则无法注册或导入失败): +- 插件必须放在 ``app/plugin`` 下,且**顶级目录名**必须以 ``module_`` 开头,例如 + ``module_example``、``module_yourfeature``(扫描模式:``module_*/**/controller.py``)。 +- 控制器文件名必须为 ``controller.py``(大小写敏感,Linux 上 ``Controller.py`` 无效)。 +- 从 ``module_xxx`` 到 ``controller.py`` 的**每一级目录名**须为合法 Python 标识符 + (仅字母数字下划线、不以数字开头;不要使用中划线、空格、中文目录名等)。 +- 每一级目录应可作为包导入:通常需有 ``__init__.py``(或符合 namespace package 规则)。 +- 在 ``controller.py`` 的**模块顶层**定义 ``APIRouter`` 实例并赋值给变量 + (如 ``DemoRouter = APIRouter(...)``);定义在函数内部的 router **不会被**扫描到。 + +路由前缀:顶级目录 ``module_xxx`` 映射为容器前缀 ``/xxx``(去掉前缀 ``module_`` 共 7 个字符)。 + +常见「路由没注册」原因: +- 目录不叫 ``module_*``,或 ``controller.py`` 不在该树下的任意子路径中。 +- 包无法导入:缺 ``__init__.py``、目录名非法、拼写不一致。 +- ``controller.py`` 无语法错误但模块内没有任何顶层 ``APIRouter`` 变量。 """ # 标准库导入 @@ -18,6 +30,35 @@ from fastapi import APIRouter from app.core.logger import log +def _import_failure_hint(exc: BaseException) -> str: + """根据异常类型给出简短排查提示(中文日志)。""" + if isinstance(exc, ModuleNotFoundError): + missing = getattr(exc, "name", None) or str(exc) + return ( + f"无法解析模块(ModuleNotFoundError: {missing})。" + "常见原因:① 从 app.plugin 到 controller 的某级目录缺少 __init__.py;" + "② 目录名不是合法 Python 标识符(禁用连字符、空格、中文等);" + "③ 磁盘路径与 import 路径不一致(大小写、子目录名拼写)。" + ) + if isinstance(exc, ImportError): + return ( + "导入失败(ImportError)。常见原因:controller 或其依赖模块循环导入、" + "第三方依赖未安装、或相对导入路径错误。" + ) + if isinstance(exc, SyntaxError): + return f"controller.py 存在语法错误:{exc.msg}(约第 {exc.lineno} 行)。" + if isinstance(exc, PermissionError): + return ( + "权限错误(PermissionError)。多见于受限环境(沙箱、部分 CI):" + "import 链上某模块初始化时调用了被禁止的系统能力(如进程池),与目录命名无关。" + "在完整操作系统下重试;若仍失败再结合堆栈排查。" + ) + return ( + f"未分类异常({type(exc).__name__})。请查看下方堆栈;" + "若与命名/包结构无关,可能是 controller 顶层 import 的依赖在加载时失败。" + ) + + def get_dynamic_router() -> APIRouter: """ 执行动态路由发现与注册,返回包含所有动态路由的根路由实例 @@ -57,7 +98,14 @@ def get_dynamic_router() -> APIRouter: top_module = path_parts[0] # 生成路由前缀 (module_xxx -> /xxx) - prefix = f"/{top_module[7:]}" + suffix = top_module[7:] if top_module.startswith("module_") else "" + if not suffix: + log.error( + f"❌ 跳过异常顶级目录名(须为 module_ 前缀且后面还有名称): {top_module!r}," + f"文件: {file}" + ) + continue + prefix = f"/{suffix}" # 获取或创建容器路由 if prefix not in container_routers: @@ -72,6 +120,7 @@ def get_dynamic_router() -> APIRouter: module = importlib.import_module(module_path) # 查找并注册所有APIRouter实例 + registered_here = 0 for attr_name in dir(module): attr_value = getattr(module, attr_name, None) @@ -81,20 +130,39 @@ def get_dynamic_router() -> APIRouter: if router_id not in seen_router_ids: seen_router_ids.add(router_id) container_router.include_router(attr_value) + registered_here += 1 + log.debug(f" ↳ 注册 APIRouter 变量 `{attr_name}` ← {module_path}") + + if registered_here == 0: + log.warning( + f"⚠️ 模块已加载但未注册任何路由: {module_path}\n" + f" 原因:该文件中未找到**顶层** APIRouter 实例。\n" + f" 规范:在 controller.py 模块顶层定义,例如 " + f"`XxxRouter = APIRouter(route_class=..., prefix=..., tags=[...])`," + f"不要仅在函数内创建 APIRouter。" + ) except Exception as e: - log.error(f"❌️ 处理模块 {module_path} 失败: {e!s}") + hint = _import_failure_hint(e) + log.exception( + f"❌ 处理模块失败: {module_path}\n {hint}\n 异常: {e!s}" + ) # 将所有容器路由注册到根路由 for prefix, container_router in sorted(container_routers.items()): + route_count = len(container_router.routes) root_router.include_router(container_router) - log.info(f"✅️ 注册容器: {prefix} (路由数: {len(container_router.routes)})") + if route_count == 0: + log.warning( + f"⚠️ 容器前缀 {prefix} 下未挂载任何子路由(可能该 module 下所有 controller 均未导出 APIRouter)" + ) + log.info(f"✅️ 注册容器: {prefix} (子路由数: {route_count})") - log.info(f"✅️ 动态路由发现完成: 注册了 {len(container_routers)} 个容器路由") + log.info(f"✅️ 动态路由发现完成: 共 {len(container_routers)} 个容器前缀") return root_router except Exception as e: - log.error(f"❌️ 动态路由发现失败: {e!s}") + log.exception(f"❌ 动态路由发现整体失败: {e!s}") # 如果失败,返回一个空的路由实例 return root_router