feat: 订单模块 v0.1 实现与样板二维平面图全量采集

- 后端分层架构: 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自研接口规范
This commit is contained in:
HHY
2026-09-20 08:16:03 -07:00
parent 7554f2e3c7
commit d3e6de585c
288 changed files with 1835 additions and 9 deletions
+73
View File
@@ -0,0 +1,73 @@
"""应用入口: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()