- 后端分层架构: api(Controller)/services(业务)/repositories(数据)/models(ORM)
- 订单模块: 创建(契约对齐目标站{contract_id,drawing_data,pic_url})、搜索、
详情、更新、删除、状态机(意向单→正式单→生产中→完工/取消,非法迁移拒绝)、
7节点生产进度、审计日志
- 认证模块: JWT签发(HS256/24h)、PBKDF2密码、注册登录
- 样板库API: 分类树/模板搜索/模板详情, drawing_data 与采集样本同构
- 254张样板二维平面图全部采集并静态挂载 /static/windoor-images/
- 种子脚本: 管理员+分类树+254套样板入库
- 统一响应envelope与付费墙(code=10086)/权限(code=210)语义
- 端到端冒烟测试7/7通过, 更新API.md §12自研接口规范
74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
"""应用入口:FastAPI 装配、异常处理、静态资源挂载、开发服务器。"""
|
||
from pathlib import Path
|
||
|
||
from fastapi import FastAPI, Request
|
||
from fastapi.exceptions import RequestValidationError
|
||
from fastapi.staticfiles import StaticFiles
|
||
|
||
from huamenchuang.api.router import api_router
|
||
from huamenchuang.core.database import init_db
|
||
from huamenchuang.core.deps import ModuleLockedError, NotAuthedError
|
||
from huamenchuang.core.response import fail, ok, paywall
|
||
|
||
app = FastAPI(title="华门窗云平台", version="0.1.0")
|
||
|
||
|
||
@app.on_event("startup")
|
||
async def startup() -> None:
|
||
await init_db()
|
||
|
||
|
||
@app.exception_handler(NotAuthedError)
|
||
async def auth_handler(request: Request, exc: NotAuthedError):
|
||
return _json({"code": 401, "message": str(exc), "data": "", "other_data": "", "other_data2": ""})
|
||
|
||
|
||
@app.exception_handler(ModuleLockedError)
|
||
async def module_locked_handler(request: Request, exc: ModuleLockedError):
|
||
return _json(paywall(exc.module))
|
||
|
||
|
||
@app.exception_handler(RequestValidationError)
|
||
async def validation_handler(request: Request, exc: RequestValidationError):
|
||
"""参数校验失败 → 422 语义 envelope(列出字段,不泄漏内部细节)。"""
|
||
fields = [f"{'.'.join(str(x) for x in e['loc'][1:])}" for e in exc.errors()]
|
||
return _json(fail(f"参数错误: {', '.join(fields)}"))
|
||
|
||
|
||
@app.exception_handler(Exception)
|
||
async def unhandled_handler(request: Request, exc: Exception):
|
||
"""兜底异常:统一 500 envelope(不回栈信息)。"""
|
||
return _json(fail("服务器内部错误,请稍后再试"))
|
||
|
||
|
||
def _json(payload: dict):
|
||
from fastapi.responses import JSONResponse
|
||
return JSONResponse(payload)
|
||
|
||
|
||
@api_router.get("/health", tags=["系统"])
|
||
async def health():
|
||
return ok(data={"status": "up"}, message="ok")
|
||
|
||
|
||
app.include_router(api_router)
|
||
|
||
# 静态资源:样板平面图(data/windoor-images)
|
||
_imgdir = Path(__file__).resolve().parents[2] / "data" / "windoor-images"
|
||
if _imgdir.exists():
|
||
app.mount("/static/windoor-images", StaticFiles(directory=str(_imgdir)), name="windoor-images")
|
||
|
||
# 前端构建产物挂载(前后端规范:构建到后端目录)
|
||
_dist = Path(__file__).resolve().parents[2] / "frontend" / "dist"
|
||
if _dist.exists():
|
||
app.mount("/", StaticFiles(directory=str(_dist), html=True), name="frontend")
|
||
|
||
|
||
def main() -> None:
|
||
import uvicorn
|
||
uvicorn.run("huamenchuang.main:app", host="0.0.0.0", port=8000, reload=True)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|