FastAPI + Vite SaaS 脚手架技能
这套架构是什么
一个单仓双端的全栈应用骨架:Python 后端 + 前端 SPA 共住一个 git 仓库,通过多阶段 Docker 构建打成单一镜像,GitHub Actions 自动发版。核心定位:用一个 docker run 就能跑起来的自托管 SaaS。
技术选型(每一项都解释为什么):
| 层 |
选型 |
为什么 |
| Web framework |
FastAPI |
Pydantic v2 校验直接当 schema、async 原生、OpenAPI 自动生成 |
| ORM |
SQLAlchemy 2.x |
久经考验、async 支持成熟、Alembic 配套迁移成熟 |
| Migrations |
Alembic |
不要自己搞 schema_migrations 表 |
| Auth |
JWT(PyJWT)+ passlib + 可选 pyotp 2FA |
无状态、单镜像无 redis 依赖 |
| 调度 |
APScheduler BackgroundScheduler |
in-process,不引入额外服务;线程池避免阻塞事件循环 |
| 备份加密 |
pyzipper(AES-256 zip) |
stdlib zipfile 的 ZipCrypto 已破,pyzipper 是 drop-in 替换 |
| 对象存储 |
rclone subprocess |
一个二进制覆盖 S3/R2/WebDAV/B2/GDrive/OneDrive |
| Lint/Type/Test |
ruff + mypy + pytest |
标配,启动快 |
| Frontend |
pnpm workspace + Vite + (React/Vue 任选) |
apps/* + packages/* 拆分清晰 |
| 构建产物 |
单一 Docker 镜像 |
多阶段:Node build 前端 → Python 运行时拷 dist/ 当静态 |
| CI |
GitHub Actions |
ci.yml(test/lint/build)+ release.yml(tag 触发 → docker hub) |
| 编排 |
Makefile |
不引入 just/task,Makefile 一份所有人都会读 |
仓库布局(基线)
<project>/
├── .github/
│ └── workflows/
│ ├── ci.yml # PR/push: ruff + mypy + pytest + frontend build
│ └── release.yml # tag 'v*': build & push docker image
├── .docs/ # 内部设计文档(不发布)
├── docs/ # 用户向文档(随发版打入镜像/网站)
├── alembic/
│ ├── env.py
│ ├── script.py.mako
│ └── versions/
├── data/ # 运行时数据(gitignored,.gitkeep 占位)
│ └── .gitkeep
├── frontend/
│ ├── pnpm-workspace.yaml
│ ├── package.json # 仅 workspace 根
│ ├── apps/
│ │ └── web/ # Vite SPA
│ └── packages/
│ ├── api-client/ # 类型化 API client(从 OpenAPI 生成)
│ ├── ui/ # 共享组件
│ └── features/ # 跨页可复用业务组件
├── scripts/ # 运维脚本(seed/backup/grant-admin/...)
├── src/ # Python package(直接 import,不再嵌一层)
│ ├── __init__.py
│ ├── main.py # FastAPI app + lifespan
│ ├── config.py # pydantic-settings,所有 env 集中定义
│ ├── database.py # engine / session / Base
│ ├── deps.py # FastAPI Depends 工厂(get_db / get_user / ...)
│ ├── models.py # SQLAlchemy ORM(小项目放一起,大了再拆)
│ ├── schemas.py # Pydantic schemas(同上)
│ ├── security.py # 密码哈希 + JWT 签发/校验 + 可选 TOTP
│ ├── error_handling.py # 全局异常 → JSON 响应映射
│ ├── observability.py # structured logging + metrics
│ └── routers/
│ └── <group>/
│ ├── __init__.py # 聚合 APIRouter,main.py import 不变
│ ├── _shared.py # 共享 imports/helpers/router 实例;__all__ 显式
│ └── <entity>.py # 单一资源的 POST/PATCH/DELETE 或一组 GET
├── tests/ # pytest,与 src/ 对称
├── .env.example # 所有 env 的注释样本(.env 进 gitignore)
├── .gitignore
├── alembic.ini
├── docker-compose.yml # 生产 compose(用户拿去 deploy)
├── docker-compose.postgres.yml # overlay,可选切换 Postgres
├── Dockerfile # 多阶段:frontend-builder → python:3.12-slim
├── Makefile # 唯一的命令编排入口
├── mypy.ini
├── pytest.ini
├── README.md / README.en.md # 双语
├── requirements.txt # 不用 pyproject — 简单项目 requirements 更直白
├── ruff.toml
└── server.py # 极简 entry:from src.main import app
几个不显眼但关键的设计:
server.py 在仓根:让 uvicorn server:app 能直接跑。生产 Dockerfile 也用这个,本地 dev 也用这个,统一入口避免本地/生产两套启动方式。
src/ 不再嵌套包名:小项目里 <project>/src/<project>/foo.py 是没必要的间接层。直接 src/foo.py,Python path 设 .(PYTHONPATH=.)即可。
- 路由用包不用文件:每个
routers/<group>/ 是包,_shared.py 集中共享导入和 router 实例。新增 endpoint 只动单个 entity 文件,改共享逻辑只动 _shared.py。不要把业务塞进 __init__.py,它只做聚合。
/data 是唯一持久化目录:DB、备份、附件、用户上传 — 全部走 DATA_DIR env,默认 /data(docker)或 ./data(本地)。容器部署只挂一个 volume 就能整体备份。
.docs/ vs docs/:点前缀的不发布(内部设计/复盘),无点前缀的对外。明确分流。
用户工作流(Makefile 是核心)
scaffold 完成后,用户应该能直接:
make setup-backend # 建 venv + pip install + 拷 .env.example → .env
make migrate # alembic upgrade head
make seed-demo # 灌种子数据(可选)
make dev-api # uvicorn --reload --host 0.0.0.0
make dev-web # pnpm install + vite dev
make dev-db # docker compose up postgres(如果切到 PG)
make test # pytest
make lint # ruff check
make typecheck # mypy src
make wipe-local # 清本地 dev 数据(保留 .gitkeep 和指定的构建产物)
dev-api 必须 --host 0.0.0.0:模拟器/真机经 WiFi 用 IP 访问时,uvicorn 默认绑 127.0.0.1 会 Connection refused。这条踩过,scaffold 时直接写好。
scaffold 流程(AI 执行步骤)
当用户说"按 fastapi-vite-saas 起一个新项目叫 Foo"时,按以下顺序执行:
Step 1: 收集参数
如果用户没给全,问清楚(一次性问,不要来回往返):
- 项目名(kebab-case,如
foo-cloud)→ 同时确定 Python 包名(snake_case)、Docker image 名、env 前缀
- 目标路径(默认
~/code/mine/<project-name>)
- 前端框架:React / Vue / Svelte(默认 React,跟 BeeCount-Cloud 一致)
- 默认数据库:SQLite(单镜像零依赖)/ Postgres(团队/生产强一致需求)— 默认 SQLite,Postgres 通过 overlay compose 切换
- 是否需要 2FA(默认否,简化首版)
- 是否需要备份模块(rclone + 加密 zip,默认否,有需要再加)
- GitHub owner(用户名/组织名,写进 Dockerfile/README/CI)
Step 2: 创建目录树 + 生成基础文件
按上面的"仓库布局"创建空目录(包括 .gitkeep 占位)。
然后生成以下文件,所有 {{project_name}} / {{python_pkg}} / {{github_owner}} / {{image_name}} 替换成 Step 1 参数:
requirements.txt — 基线依赖(从这个 skill 同目录的 templates/requirements.txt 拷,见下面"模板文件")
src/main.py — FastAPI app 骨架,挂 CORS、健康检查 /healthz、版本端点 /api/v1/version
src/config.py — pydantic-settings,声明所有 env(DATABASE_URL, JWT_SECRET, DATA_DIR, CORS_ORIGINS, APP_ENV, APP_VERSION)
src/database.py — engine(根据 DATABASE_URL 自动判 sqlite/pg)+ SessionLocal + Base
src/deps.py — get_db / get_current_user 占位
src/security.py — passlib CryptContext + JWT encode/decode
src/models.py — User 模型占位
src/schemas.py — UserCreate/UserRead/Token 占位
src/error_handling.py — 把常见异常映射成 JSON(401/403/404/422/500)
src/routers/auth/__init__.py + _shared.py + register.py + login.py — 完整可跑的注册登录端点
alembic.ini + alembic/env.py + 第一个 migration(创建 users 表)
server.py — 一行:from src.main import app
Makefile — 全套 dev/test/lint/typecheck/wipe-local
Dockerfile — 多阶段构建(参考 BeeCount-Cloud,删掉 docs-index 那一段,普通项目不需要 RAG 索引)
docker-compose.yml — 单服务,挂 ./data:/data,暴露端口可参数化(默认 8080 → host)
docker-compose.postgres.yml — overlay,定义 db service + 覆盖 DATABASE_URL
.env.example — 所有 env 的注释样本,JWT_SECRET 写 change-me-32-bytes-strong-random
.gitignore — Python + Node + venv + .env + data/* (保留 .gitkeep) + IDE
ruff.toml / mypy.ini / pytest.ini — 直接抄 BeeCount-Cloud 的配置
.github/workflows/ci.yml — matrix: backend(ruff + mypy + pytest)+ frontend(pnpm install + build)
.github/workflows/release.yml — tag 触发,build docker → push docker hub(用 {{github_owner}}/{{image_name}})
README.md + README.en.md — 模板文档(项目简介、quickstart、deploy、env 说明)
frontend/pnpm-workspace.yaml + frontend/package.json
frontend/apps/web/ — Vite + React/Vue 模板(用 pnpm create vite 思路,生成最小可跑)
frontend/packages/api-client/ — 占位 + 注释说明"从 OpenAPI 用 openapi-typescript 生成"
frontend/packages/ui/ + frontend/packages/features/ — 占位 + 一个 hello 组件
Step 3: 初始化 git + 装依赖 + 跑一次自检
cd <target>
git init && git add -A && git commit -m "feat: fastapi-vite-saas 脚手架初始化"
make setup-backend
make migrate
make test # 期望:0 个测试,exit 0
cd frontend && pnpm install
pnpm -C apps/web build # 期望:dist/ 产出
任何一步失败,立即停下报错给用户,不要试图绕过。
Step 4: 提示下一步
最后给用户一份"接下来要干的 5 件事"清单:
- 改
.env.example 的 JWT_SECRET 为真随机串(给一行 openssl rand -hex 32 命令)
- 把仓库推到 GitHub:
gh repo create {{github_owner}}/{{project_name}} --public --source . --push
- 配 Docker Hub secrets(
DOCKERHUB_USERNAME / DOCKERHUB_TOKEN)让 release.yml 能 push 镜像
- 改 README 里的 placeholder(项目简介、screenshot)
- 起第一个业务实体:跟 AI 说"加一个 <实体名> 资源",AI 应该按 routers// + models.py + schemas.py + alembic migration 这套流程加
不要从 BeeCount-Cloud 抄过来的部分
以下是 BeeCount-Cloud 的领域逻辑,新项目里不要默认带上:
sync_changes + read_*_projection + sync_applier.py 那套多端同步架构(只在需要"移动端离线 + 多设备同步"时才搭)
- AI 文档 Q&A / RAG /
numpy cosine search / docs-index.*.sqlite(只在产品本身要做文档搜索时才搭)
mcp/ 目录 MCP server(只在要给 LLM 工具调用时才搭)
pyzipper / rclone / apscheduler 备份链(在用户明确说要做"自助备份到对象存储"时才搭)
pyotp / TOTP 2FA(只在 step 1 用户明确说要 2FA 时才搭)
openpyxl Excel 导入(域特定,不要默认装)
每一项都增加运行时复杂度。默认 scaffold 是最小可跑的 FastAPI + Vite + Docker,其它当 add-on 按需启用。
模板文件位置
与 SKILL.md 同目录,有一个 templates/ 子目录,里面是上面 Step 2 列出的所有文件的实际模板内容(用 {{var}} 占位)。AI 执行时:
- 读
templates/<filename> 内容
- 用 Step 1 收集的参数做字符串替换
- 写到
<target>/<filename>
模板暂未填充时,AI 可以基于 BeeCount-Cloud 对应文件现场改写(把 beecount / BeeCount / 8869 / sunxiao0721 等字面量替换成新项目参数)。后续版本会把模板固化进 templates/,减少现场改写的随机性。
反模式(已踩过的坑)
- ❌ 不要在
server.py 里写业务,它只能是 from src.main import app 一行 — 否则本地/生产入口分裂
- ❌ 不要用硬编码 SQL
CREATE TABLE ... AS SELECT 做迁移,全走 Alembic
- ❌ 不要在 Drift/Alembic 之外维护自己的
schema_migrations 表
- ❌ Dockerfile 里不要在 frontend builder 阶段就
COPY frontend/ 全量 — 先只拷 package.json + lock,装完依赖再 COPY 全量,Docker layer cache 才能命中
- ❌ 不要把
data/ 留在仓里跟构建产物混(docs-index.*.sqlite 是个例外:它是从兄弟仓 build 出来 commit 进去的索引)
- ❌ HEALTHCHECK 不要打
/api/v1/healthz,挂在根 /healthz,避免被前缀路由拦掉
升级与变体
- 切 Postgres:
docker-compose -f docker-compose.yml -f docker-compose.postgres.yml up,.env 改 DATABASE_URL=postgresql+psycopg://...
- 切 Vue/Svelte:Step 1 选项分支,生成
frontend/apps/web/ 时换模板
- 加 MCP server:用
mcp>=1.27.0 装 FastMCP,挂在 /api/v1/mcp,PAT 鉴权
- 加备份模块:装 pyzipper + apscheduler,起一个
src/backup/ 包,scripts/backup_sqlite.sh 兜底 CLI
1---2name: scaffold-fastapi-vite-saas3description: 当用户要起一个 FastAPI 后端 + Vite SPA 前端 + Docker + GitHub Actions 的单仓双端 SaaS 项目时使用。一个 docker run 就跑起来的自托管 SaaS / 内部工具骨架,脱胎于 BeeCount-Cloud 生产实践。触发关键词:"scaffold FastAPI + Vite 项目"、"新建一个 SaaS 单仓"、"起一个 FastAPI Docker monorepo"、"按 fastapi-vite-saas / BeeCount-Cloud 风格建项目"、"create a fastapi vite saas template"、"new selfhost monorepo with FastAPI and Vite"。不适用于纯前端项目、纯 CLI 工具、纯库项目。4---56# FastAPI + Vite SaaS 脚手架技能78## 这套架构是什么910一个**单仓双端**的全栈应用骨架:Python 后端 + 前端 SPA 共住一个 git 仓库,通过多阶段 Docker 构建打成单一镜像,GitHub Actions 自动发版。**核心定位:用一个 docker run 就能跑起来的自托管 SaaS。**1112技术选型(每一项都解释为什么):1314| 层 | 选型 | 为什么 |15|---|------|--------|16| Web framework | FastAPI | Pydantic v2 校验直接当 schema、async 原生、OpenAPI 自动生成 |17| ORM | SQLAlchemy 2.x | 久经考验、async 支持成熟、Alembic 配套迁移成熟 |18| Migrations | Alembic | 不要自己搞 schema_migrations 表 |19| Auth | JWT(PyJWT)+ passlib + 可选 pyotp 2FA | 无状态、单镜像无 redis 依赖 |20| 调度 | APScheduler BackgroundScheduler | in-process,不引入额外服务;线程池避免阻塞事件循环 |21| 备份加密 | pyzipper(AES-256 zip)| stdlib zipfile 的 ZipCrypto 已破,pyzipper 是 drop-in 替换 |22| 对象存储 | rclone subprocess | 一个二进制覆盖 S3/R2/WebDAV/B2/GDrive/OneDrive |23| Lint/Type/Test | ruff + mypy + pytest | 标配,启动快 |24| Frontend | pnpm workspace + Vite + (React/Vue 任选) | apps/* + packages/* 拆分清晰 |25| 构建产物 | 单一 Docker 镜像 | 多阶段:Node build 前端 → Python 运行时拷 dist/ 当静态 |26| CI | GitHub Actions | ci.yml(test/lint/build)+ release.yml(tag 触发 → docker hub)|27| 编排 | Makefile | 不引入 just/task,Makefile 一份所有人都会读 |2829## 仓库布局(基线)3031```32<project>/33├── .github/34│ └── workflows/35│ ├── ci.yml # PR/push: ruff + mypy + pytest + frontend build36│ └── release.yml # tag 'v*': build & push docker image37├── .docs/ # 内部设计文档(不发布)38├── docs/ # 用户向文档(随发版打入镜像/网站)39├── alembic/40│ ├── env.py41│ ├── script.py.mako42│ └── versions/43├── data/ # 运行时数据(gitignored,.gitkeep 占位)44│ └── .gitkeep45├── frontend/46│ ├── pnpm-workspace.yaml47│ ├── package.json # 仅 workspace 根48│ ├── apps/49│ │ └── web/ # Vite SPA50│ └── packages/51│ ├── api-client/ # 类型化 API client(从 OpenAPI 生成)52│ ├── ui/ # 共享组件53│ └── features/ # 跨页可复用业务组件54├── scripts/ # 运维脚本(seed/backup/grant-admin/...)55├── src/ # Python package(直接 import,不再嵌一层)56│ ├── __init__.py57│ ├── main.py # FastAPI app + lifespan58│ ├── config.py # pydantic-settings,所有 env 集中定义59│ ├── database.py # engine / session / Base60│ ├── deps.py # FastAPI Depends 工厂(get_db / get_user / ...)61│ ├── models.py # SQLAlchemy ORM(小项目放一起,大了再拆)62│ ├── schemas.py # Pydantic schemas(同上)63│ ├── security.py # 密码哈希 + JWT 签发/校验 + 可选 TOTP64│ ├── error_handling.py # 全局异常 → JSON 响应映射65│ ├── observability.py # structured logging + metrics66│ └── routers/67│ └── <group>/68│ ├── __init__.py # 聚合 APIRouter,main.py import 不变69│ ├── _shared.py # 共享 imports/helpers/router 实例;__all__ 显式70│ └── <entity>.py # 单一资源的 POST/PATCH/DELETE 或一组 GET71├── tests/ # pytest,与 src/ 对称72├── .env.example # 所有 env 的注释样本(.env 进 gitignore)73├── .gitignore74├── alembic.ini75├── docker-compose.yml # 生产 compose(用户拿去 deploy)76├── docker-compose.postgres.yml # overlay,可选切换 Postgres77├── Dockerfile # 多阶段:frontend-builder → python:3.12-slim78├── Makefile # 唯一的命令编排入口79├── mypy.ini80├── pytest.ini81├── README.md / README.en.md # 双语82├── requirements.txt # 不用 pyproject — 简单项目 requirements 更直白83├── ruff.toml84└── server.py # 极简 entry:from src.main import app85```8687**几个不显眼但关键的设计**:8889- **`server.py` 在仓根**:让 `uvicorn server:app` 能直接跑。生产 Dockerfile 也用这个,本地 dev 也用这个,**统一入口**避免本地/生产两套启动方式。90- **`src/` 不再嵌套包名**:小项目里 `<project>/src/<project>/foo.py` 是没必要的间接层。直接 `src/foo.py`,Python path 设 `.`(`PYTHONPATH=.`)即可。91- **路由用包不用文件**:每个 `routers/<group>/` 是包,`_shared.py` 集中共享导入和 router 实例。新增 endpoint 只动单个 entity 文件,改共享逻辑只动 `_shared.py`。**不要把业务塞进 `__init__.py`,它只做聚合。**92- **`/data` 是唯一持久化目录**:DB、备份、附件、用户上传 — 全部走 `DATA_DIR` env,默认 `/data`(docker)或 `./data`(本地)。容器部署只挂一个 volume 就能整体备份。93- **`.docs/` vs `docs/`**:点前缀的不发布(内部设计/复盘),无点前缀的对外。明确分流。9495## 用户工作流(Makefile 是核心)9697scaffold 完成后,用户应该能直接:9899```bash100make setup-backend # 建 venv + pip install + 拷 .env.example → .env101make migrate # alembic upgrade head102make seed-demo # 灌种子数据(可选)103make dev-api # uvicorn --reload --host 0.0.0.0104make dev-web # pnpm install + vite dev105make dev-db # docker compose up postgres(如果切到 PG)106make test # pytest107make lint # ruff check108make typecheck # mypy src109make wipe-local # 清本地 dev 数据(保留 .gitkeep 和指定的构建产物)110```111112**dev-api 必须 `--host 0.0.0.0`**:模拟器/真机经 WiFi 用 IP 访问时,uvicorn 默认绑 127.0.0.1 会 Connection refused。这条踩过,scaffold 时直接写好。113114## scaffold 流程(AI 执行步骤)115116当用户说"按 fastapi-vite-saas 起一个新项目叫 Foo"时,按以下顺序执行:117118### Step 1: 收集参数119120如果用户没给全,问清楚(一次性问,不要来回往返):121122- **项目名**(kebab-case,如 `foo-cloud`)→ 同时确定 Python 包名(snake_case)、Docker image 名、env 前缀123- **目标路径**(默认 `~/code/mine/<project-name>`)124- **前端框架**:React / Vue / Svelte(默认 React,跟 BeeCount-Cloud 一致)125- **默认数据库**:SQLite(单镜像零依赖)/ Postgres(团队/生产强一致需求)— 默认 SQLite,Postgres 通过 overlay compose 切换126- **是否需要 2FA**(默认否,简化首版)127- **是否需要备份模块**(rclone + 加密 zip,默认否,有需要再加)128- **GitHub owner**(用户名/组织名,写进 Dockerfile/README/CI)129130### Step 2: 创建目录树 + 生成基础文件131132按上面的"仓库布局"创建空目录(包括 `.gitkeep` 占位)。133134然后生成以下文件,**所有 `{{project_name}}` / `{{python_pkg}}` / `{{github_owner}}` / `{{image_name}}` 替换成 Step 1 参数**:1351361. `requirements.txt` — 基线依赖(从这个 skill 同目录的 `templates/requirements.txt` 拷,见下面"模板文件")1372. `src/main.py` — FastAPI app 骨架,挂 CORS、健康检查 `/healthz`、版本端点 `/api/v1/version`1383. `src/config.py` — pydantic-settings,声明所有 env(DATABASE_URL, JWT_SECRET, DATA_DIR, CORS_ORIGINS, APP_ENV, APP_VERSION)1394. `src/database.py` — engine(根据 DATABASE_URL 自动判 sqlite/pg)+ SessionLocal + Base1405. `src/deps.py` — get_db / get_current_user 占位1416. `src/security.py` — passlib CryptContext + JWT encode/decode1427. `src/models.py` — User 模型占位1438. `src/schemas.py` — UserCreate/UserRead/Token 占位1449. `src/error_handling.py` — 把常见异常映射成 JSON(401/403/404/422/500)14510. `src/routers/auth/__init__.py` + `_shared.py` + `register.py` + `login.py` — 完整可跑的注册登录端点14611. `alembic.ini` + `alembic/env.py` + 第一个 migration(创建 users 表)14712. `server.py` — 一行:`from src.main import app`14813. `Makefile` — 全套 dev/test/lint/typecheck/wipe-local14914. `Dockerfile` — 多阶段构建(参考 BeeCount-Cloud,删掉 docs-index 那一段,普通项目不需要 RAG 索引)15015. `docker-compose.yml` — 单服务,挂 `./data:/data`,暴露端口可参数化(默认 8080 → host)15116. `docker-compose.postgres.yml` — overlay,定义 `db` service + 覆盖 `DATABASE_URL`15217. `.env.example` — 所有 env 的注释样本,**JWT_SECRET 写 `change-me-32-bytes-strong-random`**15318. `.gitignore` — Python + Node + venv + .env + data/* (保留 .gitkeep) + IDE15419. `ruff.toml` / `mypy.ini` / `pytest.ini` — 直接抄 BeeCount-Cloud 的配置15520. `.github/workflows/ci.yml` — matrix: backend(ruff + mypy + pytest)+ frontend(pnpm install + build)15621. `.github/workflows/release.yml` — tag 触发,build docker → push docker hub(用 `{{github_owner}}/{{image_name}}`)15722. `README.md` + `README.en.md` — 模板文档(项目简介、quickstart、deploy、env 说明)15823. `frontend/pnpm-workspace.yaml` + `frontend/package.json`15924. `frontend/apps/web/` — Vite + React/Vue 模板(用 `pnpm create vite` 思路,生成最小可跑)16025. `frontend/packages/api-client/` — 占位 + 注释说明"从 OpenAPI 用 openapi-typescript 生成"16126. `frontend/packages/ui/` + `frontend/packages/features/` — 占位 + 一个 hello 组件162163### Step 3: 初始化 git + 装依赖 + 跑一次自检164165```bash166cd <target>167git init && git add -A && git commit -m "feat: fastapi-vite-saas 脚手架初始化"168make setup-backend169make migrate170make test # 期望:0 个测试,exit 0171cd frontend && pnpm install172pnpm -C apps/web build # 期望:dist/ 产出173```174175任何一步失败,**立即停下报错给用户**,不要试图绕过。176177### Step 4: 提示下一步178179最后给用户一份"接下来要干的 5 件事"清单:1801811. 改 `.env.example` 的 `JWT_SECRET` 为真随机串(给一行 `openssl rand -hex 32` 命令)1822. 把仓库推到 GitHub:`gh repo create {{github_owner}}/{{project_name}} --public --source . --push`1833. 配 Docker Hub secrets(`DOCKERHUB_USERNAME` / `DOCKERHUB_TOKEN`)让 release.yml 能 push 镜像1844. 改 README 里的 placeholder(项目简介、screenshot)1855. 起第一个业务实体:跟 AI 说"加一个 <实体名> 资源",AI 应该按 routers/<group>/ + models.py + schemas.py + alembic migration 这套流程加186187## 不要从 BeeCount-Cloud 抄过来的部分188189以下是 BeeCount-Cloud 的领域逻辑,**新项目里不要默认带上**:190191- `sync_changes` + `read_*_projection` + `sync_applier.py` 那套多端同步架构(只在需要"移动端离线 + 多设备同步"时才搭)192- AI 文档 Q&A / RAG / `numpy` cosine search / `docs-index.*.sqlite`(只在产品本身要做文档搜索时才搭)193- `mcp/` 目录 MCP server(只在要给 LLM 工具调用时才搭)194- `pyzipper` / `rclone` / `apscheduler` 备份链(在用户明确说要做"自助备份到对象存储"时才搭)195- `pyotp` / TOTP 2FA(只在 step 1 用户明确说要 2FA 时才搭)196- `openpyxl` Excel 导入(域特定,不要默认装)197198每一项都增加运行时复杂度。**默认 scaffold 是最小可跑的 FastAPI + Vite + Docker,其它当 add-on 按需启用。**199200## 模板文件位置201202与 SKILL.md 同目录,有一个 `templates/` 子目录,里面是上面 Step 2 列出的所有文件的实际模板内容(用 `{{var}}` 占位)。AI 执行时:2032041. 读 `templates/<filename>` 内容2052. 用 Step 1 收集的参数做字符串替换2063. 写到 `<target>/<filename>`207208模板暂未填充时,AI 可以**基于 BeeCount-Cloud 对应文件现场改写**(把 `beecount` / `BeeCount` / `8869` / `sunxiao0721` 等字面量替换成新项目参数)。后续版本会把模板固化进 `templates/`,减少现场改写的随机性。209210## 反模式(已踩过的坑)211212- ❌ 不要在 `server.py` 里写业务,它只能是 `from src.main import app` 一行 — 否则本地/生产入口分裂213- ❌ 不要用硬编码 SQL `CREATE TABLE ... AS SELECT` 做迁移,全走 Alembic214- ❌ 不要在 Drift/Alembic 之外维护自己的 `schema_migrations` 表215- ❌ Dockerfile 里不要在 frontend builder 阶段就 `COPY frontend/` 全量 — 先只拷 `package.json` + lock,装完依赖再 COPY 全量,Docker layer cache 才能命中216- ❌ 不要把 `data/` 留在仓里跟构建产物混(`docs-index.*.sqlite` 是个例外:它是从兄弟仓 build 出来 commit 进去的索引)217- ❌ HEALTHCHECK 不要打 `/api/v1/healthz`,挂在根 `/healthz`,避免被前缀路由拦掉218219## 升级与变体220221- **切 Postgres**:`docker-compose -f docker-compose.yml -f docker-compose.postgres.yml up`,`.env` 改 `DATABASE_URL=postgresql+psycopg://...`222- **切 Vue/Svelte**:Step 1 选项分支,生成 `frontend/apps/web/` 时换模板223- **加 MCP server**:用 `mcp>=1.27.0` 装 FastMCP,挂在 `/api/v1/mcp`,PAT 鉴权224- **加备份模块**:装 pyzipper + apscheduler,起一个 `src/backup/` 包,`scripts/backup_sqlite.sh` 兜底 CLI