主要改动: fix(docker/dify-deploy): 修复脚本核心功能 - heredoc 单引号 bug: 'ENVEOF' 改为 ENVEOF,变量正确展开 - 端口默认值 8083/8084/8085 对齐 .env.prod 生产配置 - 加 dc_cmd() 兼容 docker-compose v1/v2 plugin - openssl rand 生成强随机密码与 SECRET_KEY(42 字符) - install 跳过已存在 .env,保护用户配置(管理员密码/SECRET_KEY) - read -p < /dev/tty 兼容非 tty 环境(CI/CD) - show-config 改用 DIFY_NGINX_PORT(nginx 入口)而非 APP_WEB_PORT docs(mvp-design): 修正 §3.2 workflow inputs 描述 - 实际只有 query,删除错误的 user_id input 声明 - 节点序列图同步更新 feat(aiChatService): 新增 Dify 客户端与适配器 - service/dify_client.go: Dify Workflow 调用 + SSE 解析 - service/dify_adapter.go: 与现有 chat_service 桥接 - provider/ai_chat_provider.go: Dubbo 入口简化 - main.go: 装配 ConversationRepository + DifyClient feat(migrations): 新增 AI 搭子会话表 ai_chat.sql - ai_conversations / ai_messages 表 + 索引 docs: 新增 Dify 集成设计文档 - 2026-06-29-ai-chat-dify-mvp-design.md (MVP 实施级) - 2026-06-29-ai-chat-dify-integration-v2-design.md (V2 演进路线图) - docs/dify/角角.yml (Workflow DSL 导出) config: 更新 env 模板与 docker 配置 - backend/.env.example: DIFY_* 环境变量声明 - docker/.env.prod: DIFY_API_BASE 对齐 8083 - docker/build.sh: 微调 - CLAUDE.md: 项目规范补充 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
471 lines
21 KiB
Markdown
471 lines
21 KiB
Markdown
<!-- code-review-graph MCP tools -->
|
||
## MCP Tools: code-review-graph
|
||
|
||
**IMPORTANT: This project has a knowledge graph. ALWAYS use the
|
||
code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore
|
||
the codebase.** The graph is faster, cheaper (fewer tokens), and gives
|
||
you structural context (callers, dependents, test coverage) that file
|
||
scanning cannot.
|
||
|
||
### When to use graph tools FIRST
|
||
|
||
- **Exploring code**: `semantic_search_nodes` or `query_graph` instead of Grep
|
||
- **Understanding impact**: `get_impact_radius` instead of manually tracing imports
|
||
- **Code review**: `detect_changes` + `get_review_context` instead of reading entire files
|
||
- **Finding relationships**: `query_graph` with callers_of/callees_of/imports_of/tests_for
|
||
- **Architecture questions**: `get_architecture_overview` + `list_communities`
|
||
|
||
Fall back to Grep/Glob/Read **only** when the graph doesn't cover what you need.
|
||
|
||
### Key Tools
|
||
|
||
| Tool | Use when |
|
||
| ------ | ---------- |
|
||
| `detect_changes` | Reviewing code changes — gives risk-scored analysis |
|
||
| `get_review_context` | Need source snippets for review — token-efficient |
|
||
| `get_impact_radius` | Understanding blast radius of a change |
|
||
| `get_affected_flows` | Finding which execution paths are impacted |
|
||
| `query_graph` | Tracing callers, callees, imports, tests, dependencies |
|
||
| `semantic_search_nodes` | Finding functions/classes by name or keyword |
|
||
| `get_architecture_overview` | Understanding high-level codebase structure |
|
||
| `refactor_tool` | Planning renames, finding dead code |
|
||
|
||
### Workflow
|
||
|
||
1. The graph auto-updates on file changes (via hooks).
|
||
2. Use `detect_changes` for code review.
|
||
3. Use `get_affected_flows` to understand impact.
|
||
4. Use `query_graph` pattern="tests_for" to check coverage.
|
||
|
||
---
|
||
|
||
## 数据库操作规范
|
||
|
||
### PostgreSQL 序列同步规则(强制)
|
||
|
||
**问题背景**:项目使用 `BIGSERIAL` / `autoIncrement` 自增主键。当通过 SQL 手动 `INSERT` 指定 `id` 值时,PostgreSQL 序列不会自动跟进,导致后续 GORM 插入报 `duplicate key value violates unique constraint`。
|
||
|
||
**触发场景**:
|
||
- 测试数据脚本(如 `create_gallery_test_users.go`)硬编码 ID
|
||
- 数据迁移脚本手动插入记录
|
||
- DBeaver / psql 手动补数据
|
||
|
||
**规范要求**:
|
||
|
||
1. **任何手动指定 ID 的 INSERT 语句,末尾必须同步重置序列**:
|
||
```sql
|
||
-- 错误示例(会导致序列不同步)
|
||
INSERT INTO assets (id, name, ...) VALUES (1000, 'xxx', ...);
|
||
|
||
-- 正确示例
|
||
INSERT INTO assets (id, name, ...) VALUES (1000, 'xxx', ...);
|
||
SELECT setval('assets_id_seq', (SELECT MAX(id) FROM assets));
|
||
```
|
||
|
||
2. **脚本文件规范**:所有输出 SQL 的 Go 脚本(如 `backend/scripts/*.go`),必须在生成的 SQL 末尾包含序列重置语句:
|
||
```go
|
||
fmt.Printf(`SELECT setval('%s_id_seq', (SELECT MAX(id) FROM %s));\n`, tableName, tableName)
|
||
```
|
||
|
||
3. **新表创建时**:预留足够的序列起始值给测试数据:
|
||
```sql
|
||
CREATE SEQUENCE assets_id_seq START WITH 10000;
|
||
```
|
||
|
||
4. **定期检查**:环境部署后执行以下 SQL 确认序列健康:
|
||
```sql
|
||
SELECT
|
||
schemaname, sequencename, last_value,
|
||
(SELECT MAX(id) FROM assets) AS table_max_id,
|
||
last_value >= (SELECT MAX(id) FROM assets) AS is_healthy
|
||
FROM pg_sequences
|
||
WHERE sequencename = 'assets_id_seq';
|
||
```
|
||
|
||
**受影响表**(使用 autoIncrement 主键):
|
||
- `assets`
|
||
- `asset_registry`
|
||
- `users`
|
||
- `stars`
|
||
- `activity_assets`
|
||
- `collection_assets`
|
||
- `materials`
|
||
- `exhibitions`
|
||
- `galleries`
|
||
- 以及其他所有 `id BIGSERIAL PRIMARY KEY` 的表
|
||
|
||
**违规后果**:生产环境报 `duplicate key` 导致用户铸造/创建失败,需紧急修复序列。
|
||
|
||
---
|
||
|
||
## 前端开发规范(uniapp + vue3 · app 端)
|
||
|
||
### 技术栈基线
|
||
|
||
- **框架**:UniApp 3.x + **Vue 3 组合式 API**(`vueVersion: "3"`,`@vue/compiler-sfc ^3.5`),不要再写 Vue 2 Options API 或混用 `this`
|
||
- **状态管理**:Vuex 4(`store/index.js` + `store/modules/*`),跨页面状态走 Vuex,组件临时状态用 `ref` / `reactive`
|
||
- **复用逻辑**:放进 `composables/useXxx.js`(已有 `useHolographicPreview` / `useDashboardData` / `useLenticularStudioTilt` 等),**禁止**把可复用的逻辑写在单文件组件里
|
||
- **目标平台**:以 **app-plus(Android + iOS)为主**,H5 / 微信小程序等其他端仅在显式 `#ifdef` 支持时才能用
|
||
- **原生能力**:`plus.*`(陀螺仪、角标、Intent 跳转等)、UniPush、设备指纹、Socket 全都走 `utils/` 下专用封装,**不要**在组件里直接 `plus.*`
|
||
|
||
### 关键约定
|
||
|
||
1. **条件编译是硬约束**——涉及原生 API / 原生插件 / 平台差异代码必须包在 `// #ifdef APP-PLUS … // #endif`(或 `MP-WEIXIN` / `H5` 等):
|
||
```js
|
||
// 正确
|
||
// #ifdef APP-PLUS
|
||
plus.runtime.setBadgeNumber(0)
|
||
// #endif
|
||
|
||
// 错误(plus 在非 app 端是 undefined,会直接报错)
|
||
plus.runtime.setBadgeNumber(0)
|
||
```
|
||
**禁止**用 `if (typeof plus !== 'undefined')` 之类兜底代替条件编译。
|
||
|
||
2. **API 调用统一封装**——所有后端接口走 `utils/api.js`(设备注册、WebSocket token 上报等),组件层只调封装函数,**禁止**在 `.vue` 里直接 `uni.request`。
|
||
|
||
3. **路由与页面注册**——新页面**先在 [frontend/pages.json](frontend/pages.json) 注册再写 `.vue` 文件**;Tab 页面用 `uni.switchTab`,普通跳转用 `navigateTo`,**禁止**用 `redirectTo` 替代 `navigateBack` 制造假"返回"。
|
||
|
||
4. **权限申请必须给出口**——通知 / 相机 / 相册 / 定位等敏感权限,授权失败时必须给"去设置"按钮并调用系统设置页(参照 `App.vue#setPermissions` 的 Android Intent / iOS `app-settings:` 范式),**禁止**静默失败。
|
||
|
||
5. **性能基线**——长列表使用现有 [components/VirtualList.vue](frontend/components/VirtualList.vue),图片用 [components/LazyImage.vue](frontend/components/LazyImage.vue);自定义字体已知坑:部分 Android WebView 对部分 `.ttf` 会报 OTS / cmap 解析失败(如 `JDLTYuanTiJian.ttf`),新增字体前先在小内存 Android 机上验证。
|
||
|
||
6. **资源与产物隔离**——`unpackage/dist/*` 是编译产物,**禁止**手动修改;图标准备物放在 `unpackage/res/icons/`,源码改动放 `static/`。
|
||
|
||
### 禁止的反模式
|
||
|
||
- ❌ 在 Vue 3 项目里混用 `export default { data() { return {} } }` Options API
|
||
- ❌ 在非 `APP-PLUS` 分支直接调 `plus.*` / 原生插件 API
|
||
- ❌ 在组件里直接 `uni.request` / `uni.connectSocket`,绕过 `utils/api.js`
|
||
- ❌ 跨页面状态用 props 层层下传 / `getApp().globalData` 散落——必须走 Vuex
|
||
- ❌ 新增页面不写 `pages.json` 就提交
|
||
- ❌ 权限被拒后只 `console.warn` 不引导用户去开启
|
||
- ❌ 手动编辑 `unpackage/dist/` 下的任何文件
|
||
|
||
### 完成自检(提交前过一遍)
|
||
|
||
- [ ] 新组件用 `<script setup>` 或 `setup()` 组合式 API,无 `this`
|
||
- [ ] 所有原生 / 平台差异代码包了 `#ifdef`
|
||
- [ ] 接口调用走 `utils/api.js`,未在组件里裸写 `uni.request`
|
||
- [ ] 跨页面 / 跨组件状态走 Vuex
|
||
- [ ] 新页面已在 `pages.json` 注册
|
||
- [ ] 敏感权限失败有"去设置"出口
|
||
- [ ] 长列表 / 大图场景接入了虚拟滚动或懒加载
|
||
- [ ] 未改动 `unpackage/dist/` 编译产物
|
||
|
||
---
|
||
|
||
## Git 提交规范
|
||
|
||
### 核心规则:AI 不得主动 commit
|
||
|
||
**未经用户明确指示,AI 严禁执行 `git commit`。** 只有用户在消息中明确说出"帮我 commit"、"提交吧"、"commit 一下"等类似指令时,AI 才可以执行 commit 操作。除此之外,任何情况下都不允许 AI 自行提交。
|
||
|
||
**默认允许的 git 操作(只读 / 暂存)**:
|
||
- ✅ `git status`、`git diff`、`git log`、`git show` 等只读命令
|
||
- ✅ `git add <file>` 暂存指定文件(仅在用户明确要求时)
|
||
- ❌ `git commit` —— 必须由用户明确触发
|
||
- ❌ `git push` —— 必须由用户明确触发
|
||
- ❌ `git reset --hard`、`git push --force`、`git checkout .` 等破坏性操作 —— 一律禁止
|
||
|
||
### 附加规范
|
||
|
||
1. **commit 前确认**:执行 commit 前,先 `git status` 和 `git diff --staged` 确认暂存区内容与用户预期一致。
|
||
2. **commit 粒度**:一个 commit 只做一件事,不要把无关修改混在一起。
|
||
3. **默认分支保护**:当前在 `main` / `master` 时,不要直接修改,先提示用户创建 feature 分支。
|
||
4. **commit message**:使用 `Co-Authored-By: Claude <noreply@anthropic.com>` 结尾(仅在 AI 协助生成 commit 时)。
|
||
5. **不要自作主张**:即使代码写完、测试通过、用户长时间未回复,也**不要**主动 commit 等待用户确认。
|
||
|
||
**违规后果**:污染提交历史、丢失用户未保存的修改、误推到远端等难以撤销的问题。
|
||
|
||
---
|
||
|
||
## 自审与回归检查规范
|
||
|
||
### 核心规则:修复后必须做整体回归检查
|
||
|
||
**每次自审发现 bug 并完成修复后,必须对改动范围及周边逻辑做一次整体回归检查**,防止"修复 A 引入 B"的情况。
|
||
|
||
### 检查流程
|
||
|
||
1. **第一遍:定位并修复问题**
|
||
- 找到自审发现的问题点
|
||
- 实施修复
|
||
|
||
2. **第二遍:修复后整体回归(强制)**
|
||
- **改动文件本身**:确认修复没有破坏原有功能
|
||
- **直接调用方 / 被调用方**:通过 `query_graph pattern="callers_of"` / `callees_of"` 确认上下游不受影响
|
||
- **相邻模块**:同 package、同目录的相关文件需要扫一遍
|
||
- **测试用例**:如果项目有测试,跑一遍相关测试
|
||
- **依赖配置**:如果修改了 model/migration/route 等配置,确认下游消费方同步更新
|
||
|
||
3. **第三遍:交叉影响检查**
|
||
- 多个 bug 一起修时,要检查**修复之间**是否有冲突
|
||
- 公共函数 / 共享状态被多个修复点改动时,特别注意副作用
|
||
|
||
### 检查清单(自审模板)
|
||
|
||
修复完一个 bug 后,逐项过一遍:
|
||
|
||
- [ ] 修复是否解决了**原始问题**(不是新引入的问题)
|
||
- [ ] 修改的函数/方法的**所有调用方**是否仍能正常工作
|
||
- [ ] 相关单元测试是否通过 / 是否需要补充新用例
|
||
- [ ] 是否有**条件分支**(if/else、switch)只改了其中一支
|
||
- [ ] 错误处理 / 边界条件(空值、nil、超长、并发)是否被新逻辑覆盖
|
||
- [ ] 数据库 / API 字段变更是否同步更新了所有使用方
|
||
- [ ] 日志、错误信息、文档是否需要同步更新
|
||
|
||
### 违规后果
|
||
|
||
- 一次修复引入新 bug,调试时间翻倍
|
||
- 用户对代码质量失去信任
|
||
- 提交历史变成"反复横跳"的打补丁记录
|
||
|
||
### ★ 全局自审规则(强约束)
|
||
|
||
**自审必须是"全局审查",不能只读"我修改的部分"**。
|
||
|
||
#### 错误做法(局部自审,已多次踩坑)
|
||
|
||
- 只读修改过的章节,找修改自身的 bug
|
||
- 跳过未修改的章节,认为"我没动过所以没问题"
|
||
- 只检查修复后的代码,不检查修复是否破坏了其他章节的引用
|
||
|
||
#### 正确做法(全局自审,强制执行)
|
||
|
||
每次自审必须**从文档第一节读到最后一节**,按以下步骤:
|
||
|
||
1. **章节通读清单**:先列出文档所有章节(§1 到 §16),逐一阅读
|
||
2. **跨章节引用一致性检查**:
|
||
- §1.x 描述的事实 → 在 §5.x 实现了吗?
|
||
- §3 Dify 配置 → §5 后端是否一致?
|
||
- §10 目录 → §5 代码是否齐全?
|
||
- §11 部署 → §5 装配是否对齐?
|
||
3. **代码示例完整性**:
|
||
- Go import 块是否齐全(time/strings/redis 等常用包)
|
||
- 函数签名与调用点参数个数一致
|
||
- struct 字段与构造方法一致
|
||
4. **章节编号一致性**:§3.4 与 §3.5 不重复;§11.x 编号无错位
|
||
5. **架构图 vs 时序图 vs 代码**:物理时序、逻辑步骤、代码实现三者交叉验证
|
||
|
||
#### 失败案例(2026-06-29 V2 文档自审 6 轮)
|
||
|
||
- **第 1-4 轮自审漏发现的问题**:DifyProvider 用 `p.httpClient`/`p.redisClient`/`p.convRepo`,新设计应该用组件抽象,但旧代码没改干净
|
||
- **Workflow 双 mapping**(V1 设计 bug):Backend 维护 `star_dataset_mapping` + Workflow 维护 `STAR_DATASET_MAP`,6 轮自审都没看到,是架构评审指出的
|
||
- **§11 部署章节**持续出现角色分工错位、循环引用
|
||
|
||
**根因**:我之前的"自审"本质上是"局部自审",只看我改的部分。
|
||
|
||
#### 自审触发时机
|
||
|
||
- 写完设计方案文档后
|
||
- 完成一轮"修复 X 个 bug"后
|
||
- 实施编码 `go build` 前(最后一次文档校对)
|
||
|
||
#### 自审报告必须包含
|
||
|
||
1. **修改的章节**:列出本次修了哪些章节
|
||
2. **未改动的章节**:列出本次没动但通读了哪些章节(防止"我没看"的盲区)
|
||
3. **跨章节引用一致性**:列出所有发现的不一致
|
||
4. **Go 编译验证**:列出所有需要 `go build` 才能发现的潜在问题
|
||
5. **优先级**:P0/P1/P2 分类
|
||
|
||
---
|
||
|
||
## 文档维护规则(设计方案类)
|
||
|
||
### 设计方案文档必须包含的开头部分
|
||
|
||
任何设计方案文档(如 `docs/specs/*.md`)**必须在文档开头**包含:
|
||
|
||
1. **方案概述**(*必读*):
|
||
- **要解决的问题**:业务问题 + 技术问题(分类列出)
|
||
- **整体实现路径**:阶段划分 + 时间估算
|
||
- **关键决策**:核心设计选择的简要说明 + 指向详细章节
|
||
- **核心架构图**(TL;DR):一图说明整体结构
|
||
|
||
2. **文档说明**:
|
||
- 适用范围
|
||
- 工作量估算
|
||
- 前置版本/历史
|
||
- 目标读者
|
||
|
||
**目的**:让读者**5 分钟内**能判断这个文档"是不是我需要的" + "大概什么内容"。
|
||
|
||
### ★ MVP 先行原则(业务驱动,不是架构驱动)
|
||
|
||
**核心原则:MVP 阶段不实施"为未来 100 明星 + 多 AI 平台"准备的架构**。
|
||
|
||
**错误做法(已踩坑)**:
|
||
- 业务第一阶段只有 1 个明星,但设计文档规划了 Provider 抽象、ProviderFactory、ConversationStore 抽象
|
||
- 结果:MVP 阶段写了 1500-2000 行抽象代码,**80% 用不上**
|
||
- 后果:实施周期 4-5 周(应该 1 周),新人接手困难,运营被复杂架构拖慢
|
||
|
||
**正确做法**:
|
||
- **MVP 设计原则**:MVP 文档只描述当前业务需要的实现
|
||
- **Stage 1**: 1 个明星 + 1 个 Dify Workflow + 直接调 DifyClient(**没有 Provider 抽象**)
|
||
- **Stage 2-5**: 业务复杂度上来后,**按业务驱动**逐步加抽象
|
||
|
||
**设计文档结构(**双文档体系**)**:
|
||
|
||
| 文档 | 用途 | 实施阶段 |
|
||
|------|------|---------|
|
||
| **MVP 设计**(如 `*-mvp-design.md`) | MVP 实施级方案,1 周可落地 | MVP 阶段 |
|
||
| **完整架构**(如 `*-v2-design.md`) | 100 明星 + 多 AI 平台完整设计 | Stage 2+ 参考 |
|
||
|
||
**两个文档的关系**:
|
||
- MVP 文档开头要明确"★ MVP 优先"提示
|
||
- 完整架构文档开头要明确"⚠️ MVP 不实施,仅路线图"
|
||
- 完整架构文档的 §10"后续优化"映射到 MVP 的 Stage 1-5 演进路径
|
||
|
||
**判断要不要做架构的设计问题**:
|
||
|
||
| 问题 | 答案 |
|
||
|------|------|
|
||
| "MVP 只有 1 个 Provider,需要 AIProvider interface 吗?" | **不需要**(YAGNI) |
|
||
| "MVP 只有 1 个 Dataset,需要 star_dataset_mapping 吗?" | **不需要**(写死) |
|
||
| "MVP 流量小,需要 RedisLock 防并发吗?" | **不需要**(单实例部署) |
|
||
| "MVP 不会换 Dify,需要 MiniMax fallback 吗?" | **不需要**(错了就报错) |
|
||
| "MVP 想要对话跨天续接,需要 PostgreSQL 持久化吗?" | **需要**(追星场景长生命周期) |
|
||
| "MVP 想要拒答敏感词,需要 AuditService 吗?" | **需要**(V1 已有) |
|
||
|
||
**反面案例**(2026-06-29):
|
||
- V2 文档设计了 Provider 抽象、ProviderFactory、ConversationStore 抽象、DatasetResolver、WorkflowClient/HistoryClient 拆分、AIProfile 等
|
||
- 实际 MVP 只需要:JWT + ConversationRepository + DifyClient + StreamChat + 后置审核 + 保存消息
|
||
- 5 倍的复杂度,**0 业务价值**
|
||
|
||
### 设计方案文档的自审清单
|
||
|
||
每次完成/大改设计方案文档,必须做以下自审(除了上面"全局自审"通用规则外):
|
||
|
||
- [ ] 文档开头是否有"方案概述"?
|
||
- [ ] "方案概述"是否包含:要解决的问题、实现路径、关键决策、核心架构图?
|
||
- [ ] **是否违反 MVP 先行原则?**(设计的抽象/复杂度是否超过当前业务需要)
|
||
- [ ] 实现路径是否给了明确的时间估算和里程碑?
|
||
- [ ] 关键决策是否能通过超链接定位到详细章节?
|
||
- [ ] 核心架构图是否覆盖了所有关键组件?
|
||
- [ ] 完整架构文档开头是否标注"MVP 不实施,仅路线图"?
|
||
|
||
### 设计方案修改的同步原则
|
||
|
||
修改设计文档时,**必须同时检查**:
|
||
|
||
1. **未改章节的引用一致性**(用 grep 查找旧字段名)
|
||
2. **目录列表与实际文件**(§10 改了,§11 也要同步)
|
||
3. **代码示例的语法**(Go/Rust/Python 等)
|
||
4. **章节编号**(插入新章节后,后续编号是否需要顺延)
|
||
|
||
### 文档维护的"传染性"提醒
|
||
|
||
**修改一个章节会"传染"其他章节**:
|
||
|
||
| 修改 A 章节 | 必须同步检查的章节 |
|
||
|------------|---------------------|
|
||
| §3 Dify 配置 | §5 后端 + §11 部署 + §10 目录 |
|
||
| §5 代码 | §6 时序图 + §11 main.go 装配 + §10 目录 |
|
||
| §10 目录 | §11 部署任务清单 + §5 文件名引用 |
|
||
| §11 部署 | §5 装配代码 + §10 文件列表 |
|
||
| §1 关键决策 | §14 对比表 + §3.3 实施 |
|
||
| **新增强大架构** | **是否违反 MVP 先行原则?是否需要拆为 MVP + 演进双文档?** |
|
||
|
||
---
|
||
|
||
## 本地规则说明
|
||
|
||
**这些规则是 Claude 在本仓库工作时必须遵守的本地约定**:
|
||
|
||
- 不提交到 git(除非用户明确指示)
|
||
- 用户每次会话可能重复触发这些规则
|
||
- 修改 `CLAUDE.md` 内容需用户明确同意
|
||
- 规则优先级:用户消息 > CLAUDE.md > memory/ > 默认行为
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## 接口开发规范
|
||
|
||
### 核心规则:添加/修改接口必须使用工程化方式完成
|
||
|
||
**每次新增或修改 API 接口时,必须以工程化、标准化方式完成**,禁止"能跑就行"的临时拼凑。完成后必须能直接通过代码审查,不需要大改。
|
||
|
||
### 工程化要求清单
|
||
|
||
1. **分层架构**(强制):
|
||
- `handler`(控制器):接收请求、参数绑定与校验、调用 service、组装响应
|
||
- `service`(业务层):业务逻辑编排、事务控制、调用 repository
|
||
- `repository` / `dao`(数据层):纯数据库操作,不含业务逻辑
|
||
- handler 中**禁止**直接调用 repository / 直接写 SQL
|
||
- service 中**禁止**直接操作 HTTP 请求/响应对象
|
||
|
||
2. **请求/响应 DTO**:
|
||
- 入参和出参使用独立的结构体(`XxxRequest` / `XxxResponse`)
|
||
- **禁止**直接用 DB model 当作入参或返回值
|
||
- 字段命名遵循项目既有规范(snake_case / camelCase)
|
||
- 敏感字段(密码、手机号、token)在响应中**必须脱敏或排除**
|
||
|
||
3. **参数校验**:
|
||
- 使用 `binding` / `validate` tag 在 handler 层做必填、长度、格式、枚举校验
|
||
- 业务规则校验放在 service 层
|
||
- 校验失败的错误信息要明确指出哪个字段、什么问题
|
||
|
||
4. **错误处理**:
|
||
- 使用项目统一的错误码/错误类型(如 `ErrCodeXxx`)
|
||
- **禁止**把原始 error 直接返回给前端
|
||
- **禁止**用 `_` 吞掉错误
|
||
- 关键业务错误必须打 ERROR 级别日志(含 trace_id / request_id)
|
||
|
||
5. **日志规范**:
|
||
- 接口入口:记录 method、path、request_id、用户身份(脱敏)
|
||
- 业务关键节点:状态流转、跨服务调用、缓存命中/未命中
|
||
- 异常退出:必须记录 stack trace 或 error cause
|
||
|
||
6. **API 文档**:
|
||
- 同步更新 Swagger / OpenAPI 注释
|
||
- 包含:接口描述、请求参数、响应示例、错误码列表、权限要求
|
||
- 字段类型、是否必填、示例值都要写清楚
|
||
|
||
7. **数据库变更**:
|
||
- 表结构变更必须写 migration
|
||
- 索引、外键、唯一约束、默认值要显式声明
|
||
- 影响现有数据的变更要考虑兼容方案(默认值、backfill)
|
||
|
||
8. **缓存策略**:
|
||
- 是否需要缓存、用什么 key、过期时间、缓存更新/失效策略要明确
|
||
- **禁止**缓存与 DB 数据不一致的方案(如只 set 不 delete)
|
||
|
||
9. **测试**:
|
||
- service 层核心业务逻辑必须覆盖单元测试
|
||
- handler 层至少一个 happy path + 一个 error case
|
||
- 数据库相关测试考虑使用事务回滚或测试容器
|
||
|
||
10. **遵循项目既有约定**:
|
||
- 命名风格、目录结构、文件命名、错误码定义与项目保持一致
|
||
- 复用项目已有的工具函数、中间件、错误处理逻辑
|
||
- **禁止**引入与项目风格冲突的新写法(例如项目用 snake_case 却写 camelCase)
|
||
|
||
### 禁止的反模式
|
||
|
||
- ❌ 在 handler 里直接写 SQL / ORM 调用
|
||
- ❌ 把 DB model 直接作为 API 入参或返回值
|
||
- ❌ 复制粘贴老接口代码不做适配(路径、参数、错误处理不一致)
|
||
- ❌ 用 `if err != nil { return err }` 一把梭,没有业务错误码
|
||
- ❌ 缺少或忘记更新 API 文档
|
||
- ❌ 改了表结构但没写 migration
|
||
- ❌ 没有写测试或测试只覆盖了 happy path
|
||
- ❌ 临时引入新的库/框架(未和项目既有技术栈对齐)
|
||
|
||
### 完成自检
|
||
|
||
接口写完后,逐项确认:
|
||
|
||
- [ ] 分层结构正确(handler / service / repository 各司其职)
|
||
- [ ] 入参/出参是独立 DTO
|
||
- [ ] 参数校验完整(必填、长度、格式、边界)
|
||
- [ ] 错误处理统一(错误码 + 友好提示 + 日志)
|
||
- [ ] 关键路径有日志
|
||
- [ ] Swagger 文档已更新
|
||
- [ ] DB 变更已写 migration
|
||
- [ ] 相关测试已编写并通过
|
||
- [ ] 命名、风格与项目既有代码一致
|