feat: backend 架构基座(copier 预生成,与功能无关的横切层)

每个生成的 backend 第一天即有完整分层结构 + 能跑的横切基础设施:
- 空层包:domain / application/services / infrastructure/db(待功能填)
- routers:health(存活/就绪) + ws(WebSocket 代理骨架)
- middleware:request_id(注入+loguru 上下文)+ access_log
- exceptions:AppException 基类 + 统一 handler(标准错误响应)
- infrastructure/db/base:engine/session/Base(SQLite WAL+busy_timeout / PG 连接池)+ get_db
- utils:ids + files(work_dir/会话目录/防穿越)
- core/logging:加文件 sink 到 {WORK_DIR}/logs(运行时建,gitignore)
- main.py:装配中间件/异常/路由
- 运行时目录(logs、{uuid} 临时)由代码创建,不入库

验证:sqlite/postgresql 两配置 ruff+mypy 全过,import 成功,7 条路由就位。
This commit is contained in:
baozaotumao
2026-06-02 00:37:51 -07:00
parent 48bacdbed6
commit 434a4683ea
21 changed files with 283 additions and 8 deletions
@@ -0,0 +1,41 @@
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from loguru import logger
from .base import AppException
async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
request_id = getattr(request.state, "request_id", None)
logger.warning("AppException | code={} msg={}", exc.code, exc.message)
return JSONResponse(
status_code=exc.status_code,
content={
"error": {
"code": exc.code,
"message": exc.message,
"request_id": request_id,
}
},
)
async def global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
request_id = getattr(request.state, "request_id", None)
logger.exception("Unhandled exception")
return JSONResponse(
status_code=500,
content={
"error": {
"code": "INTERNAL_ERROR",
"message": "服务器内部错误",
"request_id": request_id,
}
},
)
def register_exception_handlers(app: FastAPI) -> None:
# handler 形参类型为具体异常,与 Starlette 宽签名不完全一致,忽略该告警
app.add_exception_handler(AppException, app_exception_handler) # type: ignore[arg-type]
app.add_exception_handler(Exception, global_exception_handler)