优化提现功能,新增静默请求选项以支持无弹窗请求,提升用户体验。同时,更新微信转账逻辑,确保在未初始化转账客户端时正确处理状态,增强系统的灵活性和可维护性。调整API响应字段,确保一致性,提升代码可读性。

This commit is contained in:
乘风
2026-02-09 19:26:19 +08:00
parent fc57938bfe
commit ef1cb8cabd
20 changed files with 858 additions and 1378 deletions

View File

@@ -0,0 +1,74 @@
---
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 推荐码绑定`)。