Files
soul-yongping/.cursor/rules/soul-api-coding.mdc

75 lines
5.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
description: soul-api 编码风格与规范GORM、依赖使用、项目约定
globs: soul-api/**/*.go
alwaysApply: false
---
# soul-api 编码规范
## 1. 数据访问:优先 ORMGORM
- **一律使用 GORM 进行数据读写**,通过 `database.DB()` 获取 `*gorm.DB`,操作集中在 `internal/model` 中定义的模型上。
- **禁止**在 handler 中手写 `db.Exec("INSERT ...")`、`db.Raw("SELECT ...")` 等裸 SQL除非满足下方「例外」。
- 常规 CRUD 必须用 GORM 链式 API
- 查询:`db.Where(...).First/Find/Count`、`db.Preload`、`db.Select`、`db.Order`
- 写入:`db.Create`、`db.Save`、`db.Model(...).Updates(map/struct)`、`db.Where(...).Delete`
- 原子更新:用 `gorm.Expr`,例如 `Update("pending_earnings", gorm.Expr("pending_earnings + ?", delta))`,而不是多行 Raw/Exec。
- **例外**(允许少量 Raw/Exec
- 单条复杂统计 SQL 且用 GORM 表达冗长时,可用 `db.Raw(...).Scan(&struct)`,并加简短注释说明原因。
- 必须用原生 SQL 的原子多列更新(如多字段 `SET a=a+?, b=b+?`)可保留 `db.Exec`,其余尽量改为 `Model().Update(Expr(...))`。
## 2. Model 与表结构
- 所有表对应结构体放在 `internal/model`,文件名与业务一致(如 `user.go`、`order.go`)。
- 结构体必须包含:
- `gorm` 标签:`column`、`primaryKey`、`type` 等;
- `json` 标签:对外 API 字段用小写驼峰(如 `openId`
- 实现 `TableName() string` 返回表名(若表名与默认规则不一致)。
- 不对外暴露的字段用 `json:"-"`(如 `SessionKey`、`PurchasedSections`)。
## 3. 依赖物尽其用
- **Gin**:入参用 `c.ShouldBindJSON(&req)` + `binding:"required"` 等做校验;统一用 `c.JSON(status, gin.H{...})` 或结构体返回;路由按功能挂在 `router.Setup` 的对应 Group如 `/admin` 用 `middleware.AdminAuth()`)。
- **GORM**能用链式条件、Scopes、预加载完成的不写 Raw事务用 `db.Transaction(func(tx *gorm.DB) error { ... })`。
- **配置**:仅通过 `internal/config` 的 `config.Load()` 读环境变量;业务代码不直接 `os.Getenv`;新配置项加到 `Config` 结构体并在 `Load()` 中解析。
- **中间件**:安全头用 `middleware.Secure()`,跨域用 `cors`,限流用 `middleware.NewRateLimiter(...).Middleware()`;新路由按需挂到已有或新 Group避免重复造轮子。
- **微信/支付**:小程序、支付、转账相关统一走 `internal/wechat` 封装handler 只做参数与结果转换。
## 4. 接口按使用方归类(小程序 vs 管理端)
新增或修改接口时,**必须先明确使用方**,再挂到对应路由组,避免混用:
| 使用方 | 路由组 | 路径前缀 | 鉴权 |
|--------|--------|----------|------|
| **管理端** | `admin := api.Group("/admin")` | `/api/admin/...` | `middleware.AdminAuth()` |
| **管理端(数据/配置)** | `db := api.Group("/db")` | `/api/db/...` | `middleware.AdminAuth()` |
| **小程序** | `miniprogram := api.Group("/miniprogram")` | `/api/miniprogram/...` | 按接口需要(如 token |
| **两端共用** | 在 `api` 下挂通用路径,并在 `miniprogram` 下挂同 path | `/api/xxx` 与 `/api/miniprogram/xxx` | 各自鉴权 |
**规则:**
- **仅管理端用的接口**:只挂在 `admin` 或 `db` 下,不要出现在 `miniprogram`。
- **仅小程序用的接口**:只挂在 `miniprogram` 下(如登录、支付、提现、小程序专属配置等)。
- **两端共用的接口**:在 `router.go` 里两处都注册同一 handler先写在 `api` 的对应区块(如「推荐」「用户」),再在 `// ----- 小程序组 -----` 里用 `miniprogram.GET/POST(... path, handler.XXX)` 挂一遍,保证小程序统一走 `/api/miniprogram/xxx`。
- handler 注释和路由注释中标明使用方,例如:`// GET /api/miniprogram/withdraw/records 小程序-提现记录`、`// GET /api/admin/withdrawals 管理端-提现列表`。
## 5. 目录与包约定
- `cmd/server/main.go`:入口,只做 config/database/wechat/router 的初始化与启停。
- `internal/handler`HTTP 处理函数,只做绑定、校验、调 DB/wechat、写响应不写复杂业务逻辑时可暂时放在 handler逻辑变复杂时再抽到 `internal/service`。
- `internal/router`注册路由与中间件不写业务逻辑新增路由时按「4. 接口按使用方归类」决定挂到 admin / db / miniprogram 或 api+miniprogram。
- `internal/database`:仅提供 `Init(dsn)` 与 `DB() *gorm.DB`。
- 新增接口时:先确定**使用方(小程序 / 管理端 / 共用)** → 再确定路由与 Group → 在对应 handler 中实现,数据库操作用 GORM + model。
## 6. 响应与错误
- 统一约定:成功 `gin.H{"success": true, "data": ...}` 或 `"message": "..."`;失败 `gin.H{"success": false, "error": "..."}`。
- 不吞掉错误DB/wechat 返回的 `err` 必须处理,并向前端返回明确错误信息或日志。
- HTTP 状态码:业务错误可用 200 + `success: false`(与现网一致);需要明确表达「未授权/禁止」时用 401/403。
## 7. 代码风格
- 遵循 `gofmt`,提交前保持格式一致。
- 包内命名:导出函数用 PascalCase内部用 camelCase与接口语义一致如 `ReferralBind`、`GetPublicDBConfig`)。
- 注释:公开 handler 或复杂逻辑处写清用途(如 `// POST /api/referral/bind 推荐码绑定`)。