3e265bd837
- 还原 /v1/public/user/invite_sales 接口及 /invite/sales 别名(与 invite_records 并存) 逻辑/handler/types 与197fed7d删除前版本完全一致,按fefbd4f5新 routes 结构注册 - 新增迁移 02155_promo_schema_fix: 幂等修复 subscribe_promo / order 列类型与索引偏差 - 同步 etc/ppanel.yaml 数据库连接配置 - 补齐相关需求与设计文档
5.8 KiB
5.8 KiB
用户端提现列表 API — 订阅字段现状调研
调研日期:2026-05-27 调研范围:用户端「提现记录列表」接口当前返回字段,重点关注是否包含订阅相关信息
一、接口信息
| 项目 | 值 |
|---|---|
| 方法 | GET |
| 路径 | /v1/public/user/withdrawal_log |
| 认证 | JWT Token(AuthMiddleware + DeviceMiddleware) |
| 分组 | apis/public/user.api |
二、文件定位
| 层 | 路径 |
|---|---|
| API DSL | apis/public/user.api:118 (WithdrawalLog) / apis/public/user.api:368 (路由) |
| Handler | internal/handler/public/user/queryWithdrawalLogHandler.go |
| Logic | internal/logic/public/user/queryWithdrawalLogLogic.go:30 |
| 类型生成 | internal/types/types.go(WithdrawalLog、QueryWithdrawalLogListRequest、QueryWithdrawalLogListResponse) |
| 数据模型 | internal/model/user/user.go:167 (Withdrawal,表名 withdrawals) |
三、请求参数
QueryWithdrawalLogListRequest {
Page int `form:"page"`
Size int `form:"size"`
}
- 默认值:
page=1、size=10(在 logic 内兜底)
四、响应结构
4.1 顶层响应
QueryWithdrawalLogListResponse {
List []WithdrawalLog `json:"list"`
Total int64 `json:"total"`
}
4.2 列表项 WithdrawalLog
WithdrawalLog {
Id int64 `json:"id"`
UserId int64 `json:"user_id"`
Amount int64 `json:"amount"` // 单位:分
Content string `json:"content"` // 收款附加信息
Status uint8 `json:"status"` // 0:Pending 1:Approved 2:Rejected 3:Cancelled
Reason string `json:"reason,omitempty"` // 拒绝原因
Method uint8 `json:"method"` // 0:其他 1:支付宝 2:微信 3:USDT
Account string `json:"account"` // 收款账号
QrCodeUrl string `json:"qr_code_url"` // 收款码图片 URL
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
五、底层数据模型 Withdrawal
type Withdrawal struct {
Id int64
UserId int64 // index:idx_user_id
Amount int64
Content string // type:text
Status uint8 // 0:Pending 1:Approved 2:Rejected 3:Cancelled
Reason string // varchar(500)
Method uint8 // 0:其他 1:支付宝 2:微信 3:USDT
Account string // varchar(255)
QrCodeUrl string // varchar(500)
CreatedAt time.Time
UpdatedAt time.Time
}
表名:
withdrawals,与用户关联仅靠user_id外键,无任何订阅 ID / 订阅快照字段。
六、订阅字段现状(核心结论)
6.1 当前结论
| 维度 | 是否包含订阅信息 |
|---|---|
API 响应(WithdrawalLog) |
❌ 无 |
数据库表(withdrawals) |
❌ 无 |
| Logic 查询逻辑 | ❌ 无 JOIN、无附加查询 user_subscribe |
提现记录与订阅之间完全没有关联。原因:佣金来源于多次订单累计,提现是从「佣金余额(user.commission)」整体扣减,不绑定到任何具体订阅。
6.2 Logic 当前实现要点
// internal/logic/public/user/queryWithdrawalLogLogic.go:46-72
query := l.svcCtx.DB.WithContext(l.ctx).
Model(&user.Withdrawal{}).
Where("user_id = ?", u.Id)
// 仅按 user_id 过滤 + 分页 + 倒序,无任何 Preload / Join
七、已发现的隐患(与本次需求关联)
7.1 时间戳违反项目约定 ⚠️
queryWithdrawalLogLogic.go:70-71:
CreatedAt: row.CreatedAt.UnixMilli(),
UpdatedAt: row.UpdatedAt.UnixMilli(),
- 项目约定:后端统一返回秒级 Unix 时间戳(前端
formatDate已按数字 × 1000处理) - 当前实现返回毫秒级,前端会解析为约公元 +55000 年的日期,展示必然异常
- 修复方式:改为
.Unix()
该问题独立于「订阅字段」需求,但属于同一接口,建议同批修复。
八、可选扩展方向(待业务确认)
若产品希望在提现列表中展示订阅相关信息,可选方案如下:
| 方案 | 字段示意 | 实现成本 | 适用场景 |
|---|---|---|---|
| A. 当前生效订阅摘要 | current_subscribe: { id, name, expire_at } |
中(每行额外查 user_subscribe) |
想让用户看到「我提的是哪个订阅产生的佣金对应的余额」 |
| B. 用户全部订阅列表 | subscribes: [{ id, name, expire_at }] |
高(N+1 风险) | 极少场景,需评估必要性 |
| C. 仅订阅 ID 数组 | subscribe_ids: [int64] |
低 | 仅前端跳详情用 |
| D. 不加,保持现状 | — | 0 | 若业务上提现与订阅本就无关 |
推荐先与产品确认动机:提现是佣金余额提现,与订阅本身没有直接业务关系,加字段前需明确「让用户看到订阅信息要解决什么问题」。
九、相关接口(一并列出,便于对照)
| 接口 | 方法 | 路径 | 说明 |
|---|---|---|---|
| 提交提现 | POST | /v1/public/user/commission_withdraw |
入参 CommissionWithdrawRequest,返回 WithdrawalLog |
| 取消提现 | POST | /v1/public/user/withdrawal_cancel |
入参 CancelWithdrawalRequest,返回 WithdrawalLog |
| 提现记录列表 | GET | /v1/public/user/withdrawal_log |
本文主角 |
三个接口共用
WithdrawalLog类型,任何字段变更需统一同步,否则前端类型会错位。
十、后续动作建议
- 产品确认:是否真的需要在提现列表里返回订阅字段?目的是什么?
- 若需新增:在
apis/public/user.api修改WithdrawalLog,运行 goctl 重新生成,再补 Logic 查询。 - 顺手修复:将
UnixMilli()改为Unix()(独立小 PR 即可)。 - 如新增订阅字段:注意三个接口(list / cancel / withdraw)的返回结构同步,避免前端类型联动断裂。