75 lines
5.4 KiB
Plaintext
75 lines
5.4 KiB
Plaintext
---
|
||
description: soul-api 编码风格与规范(GORM、依赖使用、项目约定)
|
||
globs: soul-api/**/*.go
|
||
alwaysApply: false
|
||
---
|
||
|
||
# soul-api 编码规范
|
||
|
||
## 1. 数据访问:优先 ORM(GORM)
|
||
|
||
- **一律使用 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 推荐码绑定`)。
|