Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
酒库管理系统 — 项目规则与 Orchestrator
必读
开始任何任务前,先读 docs/context/project.md 了解项目全貌。
Orchestrator:如何自动选择 Agent
根据用户请求的关键词和意图,按以下规则判断调用哪些 Agent:
新功能开发(完整流水线)
触发词:「新增」「开发」「实现」「做一个」+ 功能描述
串行:requirements-analyst → architect
并行:api-designer + db-designer(同时进行,都依赖架构但互不依赖)
→ ui-designer 判断复杂度:
简单变更:直接输出规范 → 继续下一步
复杂设计:输出 Figma 需求文档 → 暂停等用户确认设计稿
并行:backend-coder + flutter-coder(都依赖 API 设计,互不依赖)
串行:test-engineer → linter → code-reviewer → security-auditor
仅后端修改
触发词:「修复后端」「后端接口」「API」+ 具体描述
backend-coder → test-engineer → linter → code-reviewer
仅前端修改
触发词:「UI」「界面」「Flutter」「页面」
flutter-coder → linter
Bug 修复
触发词:「bug」「报错」「修复」「问题」
1. 先判断 bug 来源(后端/前端/数据库)
2. 调用对应的 coder agent 修复
3. test-engineer 验证修复
安全/代码质量检查
触发词:「安全」「漏洞」「review」「审查」「代码质量」
并行:security-auditor + code-reviewer + linter
线上问题
触发词:「线上」「故障」「宕机」「慢」「报错」
sre(优先级最高,立即响应)
→ 根据 sre 诊断结果,再决定是否调用 backend-coder 修复
文档需求
触发词:「文档」「说明」「手册」「README」
doc-writer
Agent 边界规则(强制执行)
| Agent | 可以写 | 不可以写 |
|---|---|---|
| requirements-analyst | docs/requirements/ | 任何代码 |
| architect | docs/architecture/ | 任何代码 |
| api-designer | docs/api/ | 任何代码 |
| db-designer | backend/schema/ | 业务代码、测试代码 |
| backend-coder | backend/internal/, backend/main.go | *_test.go, schema/ |
| ui-designer | docs/design/ | 任何代码 |
| flutter-coder | client/ | backend/ |
| test-engineer | _test.go, docs/review/-bugs.md | 业务代码 |
| linter | 格式自动修复(gofmt/dart format),docs/review/lint-report.md | 逻辑代码 |
| code-reviewer | docs/review/*-review.md | 任何代码 |
| security-auditor | docs/security/ | 任何代码 |
| devops | deploy/, .gitea/workflows/, scripts/ci/ | 业务代码 |
| sre | docs/runbooks/ | 任何代码 |
| doc-writer | docs/(除 context/ 外) | 任何代码 |
Agent 间通信协议
文件传递(主要方式):
- 每个 Agent 完成后将结果写入约定文件
- 下游 Agent 开始前读取上游产出文件
- 文件格式:混合模式(中文叙述 + YAML/代码块)
短链式调用(反馈循环):
- test-engineer 发现 bug → 直接将错误信息和
docs/review/{功能}-bugs.md路径传给 backend-coder - security-auditor 发现 Critical 问题 → 直接告知 backend-coder 修复,不等下次迭代
并行 Agent 调度规则
当多个 Agent 可以并行时,在同一条消息中发起多个 Agent 调用:
# 可以并行的情况(互不依赖):
- api-designer + db-designer
- backend-coder + flutter-coder
- security-auditor + code-reviewer + linter
# 必须串行的情况(有依赖):
- requirements-analyst 完成后 → architect 才能开始
- architect 完成后 → api-designer 和 db-designer 才能并行开始
- backend-coder 完成后 → test-engineer 才能开始
Git 提交规范
每个 Agent 完成工作后提交,commit message 格式:
{类型}({模块}): {简短说明}
类型:feat | fix | test | docs | chore | security | refactor
模块:backend | client | db | deploy | docs
示例:
feat(backend): 新增财务报表接口
test(backend): 财务报表模块测试用例
docs(api): 财务报表 API 接口文档
security: 修复入库单多租户隔离漏洞
开发完成标准(Definition of Done)
任何功能或修复,必须满足以下全部条件才算完成:
后端变更
# 1. 编译通过
export PATH="/opt/homebrew/bin:$PATH"
cd backend && go build ./...
# 2. 所有测试通过(不得有 FAIL)
cd backend && go test ./...
# 3. 无编译警告(vet 检查)
cd backend && go vet ./...
前端变更
# 1. 静态分析无 error(warning/info 允许存在)
cd client && flutter analyze --no-fatal-infos --no-fatal-warnings
# 2. 所有测试通过
cd client && flutter test
通用要求
- 代码已按 Git 提交规范提交(feat/fix/test/...)
- 涉及新接口:已在对应测试文件中覆盖核心路径
- 涉及数据库变更:schema.sql 和 model 同步更新
- 多租户隔离未被破坏(所有查询含 shop_id 条件)
测试未通过前,禁止提交代码、禁止打 tag 发版。
代码质量门禁
以下情况不得提交:
go build ./...失败go test ./...有失败用例- 存在 security-auditor 标记的 Critical 级别问题
- 存在 code-reviewer 标记的 blocking 级别问题
项目强制规则
多租户隔离
shop_id永远从 JWT token 中提取,使用middleware.GetShopID(c)- 绝不从请求参数、URL 或请求体中读取
shop_id - 所有数据库查询必须带
WHERE shop_id = ?条件
库存变更
- 入库/出库审核通过时,必须在同一事务中同时完成:
- 更新
inventories表数量 - 写入
inventory_logs流水记录
- 更新
- 出库前必须校验库存充足,不足时返回错误并回滚
Schema 管理
- 表结构变更:修改
backend/schema/schema.sql+backend/internal/model/对应 model - 启动时自动 AutoMigrate,无单独迁移文件
- 禁止为每个新业务字段修改表结构,使用
custom_fields JSON动态扩展
种子数据
- 测试数据以 SQL 文件形式维护:
backend/seeds/<shop_code>.sql - 通过
sh scripts/dev.sh seed <shop_code>执行(内部用docker exec注入 MySQL 容器) - 每个门店一个文件,SQL 文件顶部包含 TRUNCATE,每次执行完整重建
Go 命令
- 所有 Go 命令前须确保 PATH:
export PATH="/opt/homebrew/bin:$PATH" - 或直接使用
sh scripts/dev.sh脚本(已内置 PATH)
前端 URL 配置
- 所有后端 URL 必须通过
AppConfig读取(client/lib/core/config/app_config.dart) - 禁止在任何 provider / repository / widget 中硬编码
localhost:8080或任何 IP/域名 - 使用:
AppConfig.apiBaseUrl、AppConfig.healthUrl、AppConfig.versionUrl
前端平台判断
- 使用
dart:io的Platform类前,必须先检查kIsWeb(import 'package:flutter/foundation.dart') - Web 平台不支持
dart:io,直接使用会在 Web 构建时崩溃
表格列头筛选
- 可筛选列使用
FilterableColumnHeader(来自widgets/multi_select_dropdown.dart) - 不要在 toolbar 放独立的筛选按钮,筛选入口应内嵌在列头
- 列定义使用
ColDef,可隐藏列用ColumnToggleButton控制
响应式 / 移动端适配
- 断点判定统一用
context.isMobile(client/lib/core/responsive/responsive.dart,宽度 < 600 为窄屏/手机),禁止在各处散落MediaQuery.width < 600这类魔法数 - 列表屏:用
DataTableCard时必须同时传mobileCards(窄屏渲染卡片流),卡片复用MobileListCard/MobileCardField(widgets/mobile_list_card.dart);宽屏仍走原表格,行为不变 - 弹窗固定宽度:一律用
context.dialogWidth(X)(≤ 屏宽 92%),禁止裸写width: <固定值>导致窄屏溢出 - 导航:窄屏(手机)用 Drawer 抽屉(
app_shell.dart),宽屏保持侧边栏;新增页面无需单独处理,挂在 shell 下即可 - 平台判断仍遵守上面「前端平台判断」规则(
kIsWeb先于dart:io)
权限中间件(已全局挂载)
middleware.ReadOnly():已挂载到主api路由组,只读用户(role=readonly)所有写操作自动返回 403middleware.AdminOnly():挂载在/users路由组,仅管理员可管理用户- 新增写操作路由时,默认受
ReadOnly()保护;若仅管理员可用,需加入AdminOnly()子组
并发安全(数据库操作)
- 在同一事务内读后写的操作,必须用
FOR UPDATE锁行:tx.Set("gorm:query_option", "FOR UPDATE").Where(...).First(&model) - 适用场景:单号生成(
number_rules)、库存扣减(inventories)、任何 check-then-act 模式
发版流程
使用 /release <version> slash command:
/release 1.0.2
执行顺序:本地 build → test → 更新 CHANGELOG.md → git commit → tag → push main+tag。
CI/CD(Forgejo,.gitea/workflows/deploy.yml)收到 tag 后自动:编译 → 测试 → 创建 Release → 部署 EC2 → Telegram 通知。
多平台构建矩阵(脚本在 scripts/ci/):
- mac runner(容量 1)串行链:
build-linux-web(后端+Web+官网)→build-macos→build-android→build-ios→release-deploy - windows runner 并行:
build-windows(Inno Setup 打包 setup.exe) - 分发:桌面(Win/macOS) 发布到下载页
/downloads/+ 应用内更新;Android APK 同样挂/downloads/,需ANDROID_*签名 secrets(见docs/android-signing.md);iOS 走 TestFlight,需IOS_*/APPSTORE_*secrets(见docs/ios-signing.md) - Android/iOS 的 job 在对应 secrets 未配置时优雅跳过(exit 0,不阻塞发版);本地无
client/android/key.properties时 APK 自动回退 debug 签名
CHANGELOG 格式(Keep a Changelog):
## [1.0.2] - YYYY-MM-DD
### 新功能
- ...
### 改进
- ...
### 修复
- ...
只保留有内容的分类。/release 会检查 CHANGELOG 是否已有该版本节,没有则从 git log 自动生成。
异常上报
客户端异常已在两处统一捕获,无需在每个业务层重复处理:
main.dart:FlutterError.onError+runZonedGuarded捕获所有未处理异常api_client.dart:Dio 拦截器自动上报所有 HTTP 5xx 错误
需要手动调用 reportError(e, st) 的场景:
- 技术性异常(JSON 解析失败、第三方 SDK 崩溃等)
- 不需要上报:
AppException及其子类(已知业务错误)
} catch (e, st) {
reportError(e, st); // 一行,fire-and-forget
rethrow;
}
项目 TODO 管理
唯一待办系统:任务执行过程中如发现需要做的事项,必须用 /todo add <描述> 记入项目 todo 系统,禁止使用 TaskCreate/TodoWrite 等其它 todo 工具,保持单一真相源。
- 真相源:
todo/todo.json(结构化 JSON,含 id/标题/重要度/平台标签/创建时间/完成版本) - HTML:
todo/todo.html为生成物,每次写操作自动重渲染 - 重要度:
high(阻断/紧急)/mid(重要)/low(一般/优化)三级 - 改动等级:
tier 1(接口/schema/跨 2+ 模块)/tier 2(5+ 文件)/tier 3(小改/bugfix);一级改动须先进 plan 模式经用户批准,完成后同步更新设计文档,详见/todo - 状态:
open(待开始)→doing(开发中)→done(待验收)→accepted(已验收) - 完成即标记:开发完成后
/todo status <id> done,验收通过后用户运行/todo done <id>记入版本
常用命令:
/todo — 查看 summary(按状态分组)
/todo add 修复... — 新增待办(Claude 自动推断级别/标签)
/todo status <id> doing — 标记开发中(Claude 开始处理时调用)
/todo status <id> done — 标记待验收(Claude 完成开发时调用)
/todo done <id> — 用户验收通过,自动记入版本号
/todo reject <id> <原因> — 用户拒绝验收,退回 open 并升为最高优先级
/todo reopen <id> — 重新开启(清空验收/拒绝记录)
/todo rm <id> — 删除
底层脚本:node todo/todo.mjs <子命令> [参数]
搜索实现(拼音)
商品名搜索同时支持汉字、全拼、首字母:
products表有name_pinyin(全拼)和name_initials(首字母)两列- Create / Update / FindOrCreate 写入时自动调用
util.ToPinyin()生成,无需手动维护 - 启动时对
name_pinyin = ''的存量数据自动回填(backfillPinyin) - 库存搜索 SQL 同时 LIKE 匹配 name、code、name_pinyin、name_initials
- 新增其他需要拼音搜索的实体时,复用
backend/internal/util/pinyin.go的ToPinyin()