| name | webapp-gen |
| description | 完整前后台 Web 应用生成技能包;1. 当用户需要快速生成一个 Web 应用时,触发此技能 |
webapp-gen
全栈 Web 应用快速生成工具,完整的从需求到上线的工作流自动化。
技术栈: React + TypeScript (前端) | Go + Gorm + Gin + MySQL (后端)
STEP 0:初始化项目(强制前置,必须在一切之前执行)
在创建任何任务、写任何文档之前,先完成本步骤。后续所有步骤(docs/requirement.md、docs/frontend_design.md、前后端编码)都依赖这一步生成的目录结构,跳过会直接失败。
执行流程:
- 从用户一句话需求中提炼英文 kebab-case 项目名(例如"做个博客系统" →
blog-system)。如无法确定,用 Ask 工具向用户确认项目名后再继续。
- 在当前工作目录执行:
mkdir <project_name> && cd <project_name> && git clone https://github.com/LingoJack/proj_template.git .
- 验证初始化成功:确认当前目录下存在
backend/、frontend/、Makefile。若不存在,必须停下来排查,不得继续后续步骤。
- 后续所有工作都在
<project_name>/ 目录内进行(包括写 docs/*.md)。
会创建基于 react + ts + tailwindcss v4 + go 的项目脚示例项目
目录结构如:
➜ proj_template git:(main) ✗ tree . -I node_modules
.
├── backend
│ ├── cmd
│ │ └── server
│ │ ├── main.go
│ │ ├── wire_gen.go
│ │ └── wire.go
│ ├── config
│ │ ├── config.go
│ │ └── config.yaml
│ ├── controller
│ │ ├── health_test.go
│ │ ├── health.go
│ │ └── post_controller.go
│ ├── Dockerfile
│ ├── docs
│ │ └── docs.go
│ ├── go.mod
│ ├── go.sum
│ ├── middleware
│ │ ├── auth.go
│ │ ├── cors.go
│ │ ├── logger.go
│ │ ├── passthrough.go
│ │ ├── rate_limit.go
│ │ ├── recover.go
│ │ └── request_id.go
│ ├── model
│ │ └── post.go
│ ├── pkg
│ │ ├── database
│ │ │ └── database.go
│ │ ├── logger
│ │ │ └── logger.go
│ │ ├── response
│ │ │ └── response.go
│ │ └── validator
│ │ └── validator.go
│ ├── repository
│ │ ├── post_repository_test.go
│ │ └── post_repository.go
│ ├── router
│ │ └── router.go
│ ├── service
│ │ ├── post_service_test.go
│ │ └── post_service.go
│ └── tool
│ ├── aes.go
│ ├── chinese_to_letter.go
│ ├── concurrent.go
│ ├── conf
│ │ └── conf_loader.go
│ ├── copy.go
│ ├── cos.go
│ ├── custom.go
│ ├── encode.go
│ ├── env.go
│ ├── file.go
│ ├── format.go
│ ├── hash.go
│ ├── id.go
│ ├── ip.go
│ ├── json_fix.go
│ ├── json_schema.go
│ ├── jwt.go
│ ├── llm_json_extract.go
│ ├── ptr.go
│ ├── snowflask.go
│ ├── str.go
│ └── template_render.go
├── docker-compose.yml
├── frontend
│ ├── dist
│ │ ├── assets
│ │ │ ├── index-CiJpUzvu.css
│ │ │ └── index-vvgvxU9P.js
│ │ ├── favicon.svg
│ │ ├── icons.svg
│ │ └── index.html
│ ├── Dockerfile
│ ├── eslint.config.js
│ ├── index.html
│ ├── nginx.conf
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.svg
│ │ └── icons.svg
│ ├── README.md
│ ├── src
│ │ ├── api
│ │ │ ├── client.ts
│ │ │ └── posts.ts
│ │ ├── App.tsx
│ │ ├── assets
│ │ │ ├── hero.png
│ │ │ ├── react.svg
│ │ │ └── vite.svg
│ │ ├── components
│ │ │ └── Layout.tsx
│ │ ├── hooks
│ │ ├── index.css
│ │ ├── main.tsx
│ │ ├── pages
│ │ │ ├── Home.tsx
│ │ │ └── Posts.tsx
│ │ ├── stores
│ │ │ └── postStore.ts
│ │ └── types
│ │ ├── api.ts
│ │ └── post.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.node.json
│ └── vite.config.ts
└── Makefile
初始化工作流
重要:先完成上面的 STEP 0 项目初始化,再使用 (action='create') 工具创建以下任务。任务清单的第一项必须是"项目初始化",缺失就是流程错误。
预期工作流为:
项目初始化(git clone 模板,见 STEP 0,必须第一项)
需求分析
api 设计
前端设计
原型生成
原型反馈
根据最终原型完善api设计和前端设计
开始后端设计
后端编码实现
接口黑盒测试(scripts/api-test.sh 覆盖所有接口,本地环境先过一遍)
前端编码实现
容器化启动与验收(podman compose,跑完再用黑盒脚本验证容器环境,最终一项)
容器化原则
- 编码期本地化:前后端开发、原型反馈、单测都在本地跑(
make run-frontend / make run-backend),避免每次改代码都 rebuild 镜像拖慢迭代。
- 依赖服务容器化:后端编码/测试阶段需要 MySQL 时,只起 mysql 一个服务(见"后端编码实现"节),backend 仍在宿主机跑。
- 最终全栈容器化:所有功能完成后,用
podman compose up -d --build 跑完整链路做验收(见"容器化启动与验收"节)。
- 容器运行时统一用 podman:模板的
docker-compose.yml 是标准 compose v3 格式,podman compose 原生兼容。Makefile 里的 docker-up 目标基于 docker compose,本 skill 一律改走 podman compose 命令(不要执行 make docker-up)。
开始任何写文件动作前,先 pwd 或 ls 确认当前已在 <project_name>/ 目录下,且存在 backend/、frontend/。否则回到 STEP 0。
需求分析阶段
进入 PLAN MODE
根据用户的一句话需求进行详细扩写
需求文档必须包含如下内容:
- 标题:需求名称
- 一些预期的用例场景 Use Case
- 非功能性需求
直到用户认为计划完善
将任务输出到 docs/requirement.md
api设计阶段
产出 docs/api-design.md,作为前后端唯一契约。后续前端 mock、后端实现、黑盒测试全部以这份文档为准——文档没定义的字段/错误码不允许出现在代码里。
开发原则
- 虽然数据是 mock 的,但必须遵守 "数据从 API 中来" 的规律
- 原型必须满足「闭环理论」
闭环理论:
- 数据的产生和消费的逻辑成对存在,不存在只有生产、无消费或只有消费、无生产的数据
- 非特定情况,程序不能存在出现过但是无法触发的交互元素(如:不能点击或点击了无反应的按钮等)
统一响应格式
所有 HTTP 200 响应体统一用下面的信封,前端只按 code 判断业务成败,不看 HTTP 状态码(除 401/403 由中间件直接拦截):
{
"code": 0,
"message": "ok",
"data": { ... }
}
code: number —— 0 表示业务成功;非 0 表示业务失败。message 给人看,data 给机器用
message: string —— 成功时固定 "ok";失败时是可直接展示给用户的中文短句
data: T | null —— 成功返回业务数据;失败时必须为 null(不要塞空对象,便于前端 if (res.data) 判断)
- 字段命名统一
snake_case(与 Go+GORM 默认 JSON tag 对齐;前端在映射层转 camelCase)
- 时间字段统一 RFC3339 字符串(
2025-05-04T10:00:00+08:00),不要用毫秒时间戳
- ID 字段统一用字符串(Go
uint64 JSON 序列化为 string,避免 JS 精度丢失)
统一错误码区间
错误码在 docs/api-design.md 开头列一张总表,后端和前端都按表实现/展示。推荐区间:
| 区间 | 含义 | 举例 |
|---|
0 | 成功 | "ok" |
1000-1999 | 参数校验失败 | 1001 缺失必填、1002 格式错误 |
2000-2999 | 业务规则失败 | 2001 用户名已存在、2002 余额不足 |
3000-3999 | 资源不存在 | 3001 资源 ID 不存在 |
4000-4999 | 鉴权/权限 | 4001 未登录、4002 无权访问 |
5000-5999 | 服务端异常 | 5001 数据库错误、5002 外部依赖失败 |
每个具体接口的"可能错误"段必须列出它会返回的全部非 0 code,前端据此渲染错误分支。
统一分页约定
列表类接口必须支持分页,请求参数和响应结构固定:
- 请求(Query):
page 从 1 起,page_size 默认 20 上限 100;过滤/排序用 keyword、sort_by、order(asc/desc)
- 响应:
{
"code": 0,
"message": "ok",
"data": {
"items": [...],
"total": 123,
"page": 1,
"page_size": 20
}
}
鉴权约定
- 登录接口返回
data.token(JWT),前端放 localStorage,后续请求带 Authorization: Bearer <token>
- 未带 token 或 token 过期,中间件直接返回 HTTP 401 +
{"code": 4001, "message": "未登录或登录已过期", "data": null}
- 无权限返回 HTTP 403 +
{"code": 4002, ...}
- 公开接口(
/api/v1/public/*)和私有接口(/api/v1/*)路径分组,中间件按前缀匹配
每个接口的文档条目必须包含
- 方法 + 路径:
GET /api/v1/posts
- 是否需鉴权:公开 / 需登录 / 需 xxx 权限
- 请求参数:Query / Path / Body 分段列出,每个字段带类型、是否必填、约束(如
page_size: number, 选填, 1-100)
- 成功响应示例:完整 JSON(包含信封),
data 字段填真实示例值而非 "string" 占位
- 可能错误:列出所有非 0 code + message + 触发场景
- 业务说明:幂等性、副作用、限流(如有)
模板示例(放在文档里供所有接口参考)
### POST /api/v1/posts 创建文章
- **鉴权**:需登录
- **请求 Body**:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 是 | 1-100 字符 |
| content | string | 是 | 1-50000 字符 |
| tags | string[] | 否 | 最多 5 个,每个 1-20 字符 |
- **成功响应** (200):
```json
{
"code": 0,
"message": "ok",
"data": {
"id": "1234567890",
"title": "我的第一篇文章",
"content": "正文内容...",
"tags": ["rust", "rocket"],
"created_at": "2025-05-04T10:00:00+08:00"
}
}
- 可能错误:
1001 title/content 缺失或超长
2001 该用户 24h 内已创建过同标题文章
4001 未登录
#### 完成标准
- `docs/api-design.md` 包含所有接口条目 + 错误码总表 + 分页约定
- 任取 3 个接口随机检查:每个都能找到上面 6 项内容
- 前端根据此文档写 mock 数据 / TypeScript 类型;后端根据此文档实现;黑盒测试根据此文档断言字段名和 code
### 前端设计阶段
阅读需求文档 `docs/requirement.md` 内容,严格按照以下步骤开发:
进入 PLAN MODE,首先思考计划清楚以下内容
- 确定项目结构
- 列出页面组件
- 定义状态管理方案
- 选择 UI 库
- 确定路由规划
- 列出 API 接口定义
- 定义全局样式规范
- 确定响应式断点
- 确定主题色板
- 确定图标系统
- 确定组件库规范
### 原型生成阶段
按照 `docs/frontend_design.md` 实现原型,接口返回的数据可以先 mock,注意必须是 mock 接口返回的数据
运行以下命令检查前端项目构建
```bash
npx tsc --noEmit 2>& 1
make check-frontend
原型反馈阶段
若检查通过,使用 Bash 后台运行 make run-frontend 启动开发服务器
用 Ask 询问改进意见并按照用户要求优化,直到用户确认该原型满足要求
根据最终原型完善api设计和前端设计
后端设计阶段
根据原型和需求文档,以及前期设计的接口文档,设计数据表
写到 docs/backend-design.md 文档
通过 @skill:sql-to-go-struct-and-dao 生成 model 和 dao 层代码
数据库设计经验(重要):
-
禁止使用外键约束
- MySQL 初始化 SQL 中不要定义
FOREIGN KEY 约束
- 外键会导致表创建顺序依赖问题(必须先创建被引用的表)
- 外键影响数据删除/更新的灵活性,增加运维复杂度
- 应用层通过代码逻辑保证数据一致性即可
-
中文编码必须显式声明
-
Schema 文件修改注意事项
- 不要用
sed 命令批量修改 SQL 文件,会破坏文件结构(逗号、括号位置)
- SQL 文件结构敏感,应使用
Write 工具完整重写
- 每张表的字段定义之间用逗号分隔,最后一行字段前不要逗号
- 索引定义与字段定义同级,最后一项索引前不要逗号
后台开发阶段
实现后台接口以及逻辑
MySQL 依赖准备(首次接触数据库前必须执行):
项目根目录下模板已自带 docker-compose.yml,内含 mysql 8.0 服务(库名 appdb,用户 appuser/apppassword,root 密码 rootpassword,暴露 3306,含 healthcheck)。只需起 mysql 这一个服务,backend 仍在本地 go run 跑:
make podman-mysql-up
重要:模板 backend/config/config.yaml 默认 DSN 是占位值 user:pass@tcp(localhost:3306)/appdb,与 compose 里 mysql 服务的实际账号不一致,backend 本地跑会连接失败。第一次起后端前必须把 DSN 改成:
appuser:apppassword@tcp(127.0.0.1:3306)/appdb?charset=utf8mb4&parseTime=True&loc=Local
(注意是本地 127.0.0.1,不是 compose 内网的 mysql。compose 内 backend 服务用的 DSN 由 docker-compose.yml 的环境变量覆盖,不影响 config.yaml。)
跑后端测试前同理:make podman-mysql-up 再 make test-backend。
清理:make podman-down(保留数据卷)或 make podman-clean(连数据一起删)。
后端编码经验(重要):
-
模板代码冲突排查
- 模板自带
pkg/tool/ 目录下有多个工具文件,部分文件(如 aes.go、conf_loader.go)声明为 package tool,部分声明为 package conf
- 如果出现
found packages tool (aes.go) and conf (conf_loader.go) in same directory 编译错误,需检查包声明是否一致
- 模板中
conf_loader.go 如果独立为 package conf,应将其移到 pkg/tool/conf/ 子目录,或统一改为 package tool
-
Go Model 定义规范
-
Wire 依赖注入
cmd/server/wire.go 中声明所有 Provider 和 Injector
- 每新增一个 Service/Controller/Repository,都需在
wire.go 中添加对应的 wire.NewSet()
- 修改
wire.go 后必须运行 wire ./cmd/server/ 重新生成 wire_gen.go
- 如果 Wire 生成失败,检查:参数类型是否匹配、是否有循环依赖、接口绑定是否正确
-
路由注册
router/router.go 中按模块分组注册路由
- 公开路由(无需认证)和私有路由(需 JWT 认证)分开注册
- 中间件顺序:
Recovery → Logger → RequestID → CORS → RateLimit → Auth(仅私有路由)
-
接口黑盒测试脚本(必须交付)
后台接口写完后,必须在项目根目录下写一个端到端黑盒测试脚本(scripts/api-test.sh 或 scripts/api-test.py),覆盖所有实现的后端接口,验证部署链路真的能用。
脚本要求:
- 纯 HTTP 调用(
curl 或 requests),不依赖任何 Go 代码,验证的是已部署的服务
- 覆盖完整 CRUD:每个资源的 create → read → update → delete 一条龙,带中文数据(验证字符集)
- 覆盖鉴权:未登录访问私有接口应 401;登录后拿 token,带 token 访问应 200
- 覆盖边界:非法参数返回 400,查不存在资源返回 404
- 断言响应结构:
code、message、data 字段存在,code == 0 为成功
- 失败时打印完整请求/响应,exit code 非 0,便于 CI 集成
- 脚本开头支持
BASE_URL 环境变量,默认 http://localhost:8080,这样可以针对不同环境跑同一份测试
验证矩阵(三种环境都要过一遍,避免"本地好好的,容器里炸了"):
make podman-mysql-up && make run-backend &
BASE_URL=http://localhost:8080 bash scripts/api-test.sh
make podman-down && make podman-up
BASE_URL=http://localhost:8080 bash scripts/api-test.sh
make podman-clean && make podman-up
BASE_URL=http://localhost:8080 bash scripts/api-test.sh
任何一个环境不通过,就是该环境的配置问题(DSN、字符集、网络别名、端口映射),必须修到三个环境都过才算完成。
前端开发阶段
原型修改,替换为调用实际的后台接口
前端 API 对接经验(重要):
-
字段名大小写映射问题
- 后端 Go+GORM 默认返回 snake_case JSON 字段名(如
created_at、user_id)
- 前端 TypeScript 接口定义和映射函数必须使用 snake_case 接收后端数据
- 错误示例:
interface BackendUser {
Id: number;
CreatedAt: string;
}
- 正确示例:
interface BackendUser {
id: number;
created_at: string;
}
function mapUser(raw: BackendUser): User {
return { id: raw.id, createdAt: raw.created_at };
}
-
API 响应结构统一
- 响应信封、错误码区间、分页约定全部以
docs/api-design.md 为准,不在这里重新定义
- 前端 API 层统一做三件事:拆信封 → 非 0
code 抛异常(携带 code 和 message)→ snake_case 转 camelCase
- 错误分支按
docs/api-design.md 错误码总表对号入座,不要硬编码 message 做判断
-
前后端联调检查清单
- 启动后端:
cd backend && go run ./cmd/server/
- 启动前端:
cd frontend && npm run dev
- 验证代理:前端 vite.config.ts 的 proxy 应指向
http://localhost:8080
- 验证接口:
curl http://localhost:8080/api/v1/xxx 直接测试后端返回
- 检查控制台:浏览器开发者工具 Network 面板查看实际请求/响应
- 检查是否所有前端用到的接口都正常或图片是否正确加载
容器化启动与验收阶段
前后端功能全部完成后,做最终的全栈容器化验收。目标:一条命令起整个项目,证明部署链路通。
先把开发阶段的 mysql 单容器停掉(避免端口冲突):
make podman-down
再起全栈:
make podman-up
make podman-ps
make podman-logs
验收检查清单:
mysql / backend / frontend 三个容器都 running
- 浏览器访问
http://localhost:5173 前端可打开
- 前端调用的后端接口(走
http://localhost:8080 或 nginx 反代)返回正常数据
make podman-down && make podman-up 能复现一致行为(数据卷持久化生效)
停止:make podman-down。彻底清理(含 mysql 数据卷):make podman-clean。
容器化验收经验(重要):
-
MySQL 初始化顺序
docker/mysql-init/ 目录下的 SQL 文件按字母序执行
- 命名规范:
01_schema.sql(建表)、02_seed_data.sql(初始数据)
- 如果 schema.sql 中引用了其他表(如外键),必须确保被引用的表先创建
- 建议不用外键,避免表创建顺序问题
-
数据卷清理
-
容器健康检查
- MySQL 容器有 healthcheck,但 backend 启动时不依赖它(depends_on 只保证启动顺序)
- 后端启动失败时,检查日志:
podman logs <container_name>
- 常见问题:DSN 配置错误、数据库未完成初始化、端口冲突
-
前后端容器网络
docker-compose.yml 中服务名即为主机名
- backend 连接 MySQL 用服务名
mysql(不是 localhost)
- frontend 通过 nginx 反代访问 backend,nginx 配置中用
http://backend:8080