docs: 更新 MySQL 展示系统说明

This commit is contained in:
2026-05-11 20:51:43 +08:00
parent ddd077eb37
commit 6f627fe776
2 changed files with 147 additions and 285 deletions
+80 -84
View File
@@ -1,112 +1,108 @@
# fullstack-starter
# battery-soh
一个**单二进制**的全栈应用 starter——`bun run compile` 出来的 `./server` 文件就是你要部署的全部产物,自带 HTTP 服务、SSR、API、嵌入式 SQL 迁移,运行时不依赖 Node、不依赖源码、不依赖外部 migration 目录
一个基于 **Bun + TanStack Start + ORPC** 的电池健康展示系统。应用会被打包成单个二进制文件,前端页面、SSR 服务和 ORPC API 一起发布;业务数据只读接入甲方现有 MySQL 表 `ls_battery_info`,本项目不写入、不迁移、不修改甲方数据库
技术栈:Bun · TanStack Start (React 19 SSR) · ORPC(契约优先 API)· Drizzle ORM · PostgreSQL 18+ · Tailwind v4 · Biome。
## 数据源
## 为什么用这个
甲方提供的只读表结构:
- **部署最简**:发布只拷一个二进制文件。先 `./server migrate``./server`,完事。
- **契约优先**:在 `*.contract.ts` 用 Zod 定义一次,前端、后端、OpenAPI 文档自动同步。
- **类型严格**TypeScript strict,杜绝 `any` / `@ts-ignore` / `as any` 等类型逃逸。
- **开箱可跑**:路径别名、文件路由、ORPC 接线、Tailwind、热重载、错误页全部预接好。
| 字段名 | 数据类型 | 说明 |
| --- | --- | --- |
| `id` | `int(11)` 自增 | 主键 ID |
| `user_id` | `int(11)` | 用户 ID |
| `mac` | `varchar(50)` | 设备 MAC |
| `dev_model` | `varchar(20)` | 设备型号 |
| `dev_name` | `varchar(50)` | 设备名称 |
| `is_low_power` | `varchar(10)` | 是否低电量:`true` / `false` |
| `power_status` | `tinyint(4)` | `0` 未充电,`1` 正在充电,`2` 充电完成 |
| `power` | `tinyint(4)` | 当前电量 `0~100` |
| `create_time` | `datetime` | 创建时间 |
| `remark` | `varchar(500)` | 备注,可空 |
环境变量:
```bash
DATABASE_URL=mysql://user:password@host:3306/database
```
## 快速开始
> **需要 PostgreSQL 18+**——schema 用 PG 原生的 `uuidv7()` 生成主键(`compose.yaml` 已锁 `postgres:18-alpine`)。要兼容更老的 PG,把 `src/server/db/fields.ts` 里的 `default(sql\`uuidv7()\`)` 换成 `$defaultFn(() => Bun.randomUUIDv7())`,再跑 `bun run db:generate`。
```bash
cp .env.example .env # 把里面的 DATABASE_URL 改成你的 Postgres
cp .env.example .env
bun install
bun run db:push # 开发期:把 schema 直接同步到 DB(不写 migration 文件)
bun run dev # http://localhost:3000
bun run dev
```
打开浏览器
- `http://localhost:3000/` — Todo 示例页
- `http://localhost:3000/api/docs` — Scalar 渲染的 API 文档
## 目录结构(你需要关心的部分)
```
src/
├── routes/ # 文件路由:页面 + API 端点
├── server/
│ ├── api/
│ │ ├── contracts/ # Zod 契约(client / server 共享)
│ │ └── routers/ # 业务实现
│ └── db/ # Drizzle schema + 嵌入式 migrations
├── client/ # 前端 hooks、ORPC 客户端
└── components/ # UI 组件
```
## 加一个功能(以 `post` 为例)
每一步都很短,按顺序填即可:
1. **建表**`src/server/db/schema/post.ts` 定义 `postTable`,记得展开 `...generatedFields`(自动注入 `id` / `createdAt` / `updatedAt`)。
2. **导出表**:在 `src/server/db/schema/index.ts``export * from './post'`
3. **写契约**`src/server/api/contracts/post.contract.ts``drizzle-zod` 从表派生 Zod schema。
4. **挂契约**:在 `src/server/api/contracts/index.ts``post` 加进 `contract` 对象。
5. **写实现**`src/server/api/routers/post.router.ts` 实现 `os.post.*.handler(...)`
6. **挂路由**:在 `src/server/api/routers/index.ts``post` 加进 `router` 对象。
7. **写前端 hook**`src/client/queries/post.ts` 导出 `useInvalidatePosts` 等失效辅助。
8. **写页面**`src/routes/<page>.tsx``useSuspenseQuery` 读、`mutate` 写;mutation 的 `onSuccess` 调用第 7 步的 helper。
9. **生成 migration**`bun run db:generate` 把 SQL 写到 `./drizzle/` 并嵌入二进制。
完工。`bun run dev` 已自动热重载。
## 部署
**永远先 migrate 再 serve**。Migration 已嵌入二进制;部署只发一个 `./server` 文件。
```bash
./server migrate # 应用嵌入式 migration(用 $DATABASE_URL
./server # 启动 HTTP 服务(默认子命令)
./server --help # 列出所有子命令
```
仓库自带 `compose.yaml`(一次性 `migrate` 服务先跑完,再启动 `app`):
如果甲方暂时没有提供数据库连接,可以用 Docker Compose 启动本地 MySQL 并填充示例数据
```bash
docker compose up --build
```
Kubernetes 上:把 `./server migrate` 放进 initContainer 或 Helm `pre-upgrade` Job,主容器跑 `./server`
Compose 会启动三个服务:
## 脚本一览
- `db`:本地 MySQL 8.4
- `seed`:执行 `bun run seed`,用 Drizzle MySQL schema + `drizzle-seed` 重置本地 `ls_battery_info` 并写入示例数据
- `app`:使用同一个 `DATABASE_URL` 启动单二进制应用
也可以手动 seed 本地库:
```bash
DATABASE_URL=mysql://battery:battery@localhost:3306/battery_soh bun run seed
```
`seed` 是本地开发/验收脚本,会建表并重置示例数据;应用运行时仍然只执行 `SELECT`,不会写入数据库。
打开浏览器:
- `http://localhost:3000/`SoH 预测与风险洞察看板
- `http://localhost:3000/batteries`:设备电池实时状态
- `http://localhost:3000/api/docs`Scalar 渲染的 ORPC OpenAPI 文档
## 架构
```
src/
├── routes/ # TanStack Start 文件路由:页面 + API 端点
├── server/
│ ├── api/ # ORPC contract / router
│ └── battery/mysql.ts # 甲方 MySQL 只读查询
├── domain/battery.ts # 电池领域类型、归一化、展示聚合
├── client/orpc.ts # isomorphic ORPC client
└── styles.css # Tailwind v4 entry
```
接口保持模板里的 ORPC 模式:
- `battery.dashboard`:读取每个 `mac` 的最新记录并生成看板聚合数据
- `battery.batteries`:无 `mac` 时返回每台设备最新记录;带 `mac` 时返回该设备历史记录,按 `create_time desc` 限制 500 条
所有业务查询都是 `SELECT`,没有 mutation,也没有 mock/fallback 数据。
## 部署
```bash
bun run build
bun run compile
./out/server-<target>
```
Docker 镜像会在构建阶段产出单个 `./server` 二进制,运行阶段只需要配置 `DATABASE_URL`
## 脚本
| 命令 | 作用 |
| --- | --- |
| `bun run dev` | Vite 开发服务器(默认端口 3000 |
| `bun run build` | 构建到 `.output/``bun run compile` 会用到) |
| `bun run dev` | Vite 开发服务器 |
| `bun run build` | 构建到 `.output/` |
| `bun run compile` | 生成单二进制 `out/server-<target>` |
| `bun run seed` | 为本地 MySQL 创建甲方表并填充示例数据 |
| `bun run typecheck` | TypeScript 类型检查 |
| `bun run test` | 运行所有 `*.test.ts` |
| `bun run fix` | Biome 格式化 + lint + 整理 imports |
| `bun run db:push` | 开发期:直接同步 schema 到 DB(不写 migration 文件) |
| `bun run db:generate` | 写 SQL migration 到 `./drizzle/` 并嵌入二进制 |
| `bun run db:embed` | 仅重生 `migrations.gen.ts`(手改了 `./drizzle/*.sql` 后用) |
| `bun run db:migrate` | 通过 drizzle-kit 在本地应用 migration(开发便利) |
| `bun run db:studio` | Drizzle Studio(可视化 DB |
跨平台编译:`bun run compile:{linux,darwin,windows}[:arch]`
## 端点
| 路径 | 用途 |
| --- | --- |
| `/` | Todo 示例 UI |
| `/health` | 存活探针(不查 DB,纯文本 `ok` |
| `/api/rpc` | ORPC RPC 端点(client 直连) |
| `/api/docs` | Scalar 渲染的 API 文档 |
| `/api/spec.json` | OpenAPI spec |
## 提交前
## 验证
```bash
bun run fix && bun run typecheck && bun run test
bun run fix && bun run typecheck && bun run test && bun run build
```
没有 CI、没有 pre-commit hook——上面三条由你自觉跑。