Files
hi-server/doc/api-withdrawal-and-log.md
shanshanzhong147 b9192db042
Build docker and publish / build (20.15.1) (push) Failing after 8m29s
Build docker and publish / build (20.15.1) (pull_request) Successful in 8m3s
feat: 文件上传接口直接返回完整 URL + 提现 content 改为非必填
- FileUploadResponse / FileUploadCompleteResponse 精简为只返回 url
- S3Store.BuildObjectURL 拼接完整访问地址
- 提现 method=0 时 content 不再参与必填校验
- 新增用户端 API 接口文档
2026-05-27 00:57:21 -07:00

13 KiB
Raw Permalink Blame History

提现 & 文件上传 & 日志上报 — 用户端 API 接口文档

基于 ppanel-server 源码整理,所有时间戳均为秒级 Unix


目录


一、提现接口

认证方式: JWT(用户登录态)

路由前缀: /v1/public/user

1.1 申请提现

提交佣金提现申请,创建一条待审核的提现记录。

POST /v1/public/user/commission_withdraw

Request Body

字段 类型 必填 校验 说明
amount int64 提现金额(分)
method uint8 oneof=0 1 2 3 收款方式(见枚举表)
content string 提现备注
account string 条件必填 收款账号
qr_code_url string 条件必填 收款码图片 URL

各收款方式的必填字段

method 收款方式 必填字段
1 支付宝 qr_code_url 收款码图片
2 微信 qr_code_url 收款码图片
3 银行卡 account 收款账号
0 其他 account 必填

Request 示例

{
  "amount": 5000,
  "content": "提现到支付宝",
  "method": 1,
  "account": "user@example.com",
  "qr_code_url": "https://cdn.example.com/qrcode/alipay.png"
}

Response: WithdrawalLog


1.2 取消提现

用户取消自己的待审核提现申请,佣金退回账户。

POST /v1/public/user/withdrawal_cancel

Request Body

字段 类型 必填 校验 说明
withdrawal_id int64 required,gt=0 提现记录 ID

Request 示例

{
  "withdrawal_id": 123
}

Response: WithdrawalLog(状态已变为 3=已取消


1.3 查询提现记录

分页查询当前用户的提现记录(自动按 JWT 中的 userId 过滤)。

GET /v1/public/user/withdrawal_log

Query 参数

参数 类型 必填 说明
page int 页码,默认 1
size int 每页条数,默认 10

Request 示例

GET /v1/public/user/withdrawal_log?page=1&size=10

Response

{
  "list": [WithdrawalLog, ...],
  "total": 25
}

二、枚举值与状态流转

提现状态 (status)

说明
0 待审核
1 已通过
2 已拒绝
3 已取消

收款方式 (method)

说明
0 其他
1 支付宝
2 微信
3 银行卡

状态流转

                 ┌── 管理员通过 ──▶ 已通过 (1)
                 │
待审核 (0) ──────┼── 管理员拒绝 ──▶ 已拒绝 (2)
                 │
                 └── 用户取消 ───▶ 已取消 (3)

WithdrawalLog 对象

所有提现接口共用的响应结构:

{
  "id": 1,
  "user_id": 100,
  "amount": 5000,
  "content": "提现备注",
  "status": 0,
  "reason": "",
  "method": 1,
  "account": "user@example.com",
  "qr_code_url": "https://cdn.example.com/qrcode/alipay.png",
  "created_at": 1716700000,
  "updated_at": 1716700000
}
字段 类型 说明
id int64 提现记录 ID
user_id int64 用户 ID
amount int64 提现金额(分)
content string 提现备注
status uint8 状态(见枚举表)
reason string 拒绝原因(仅 status=2 时有值,其余 omitempty
method uint8 收款方式(见枚举表)
account string 收款账号
qr_code_url string 收款码图片 URL
created_at int64 创建时间(秒级 Unix
updated_at int64 更新时间(秒级 Unix

三、文件上传接口

认证方式: JWT + DeviceMiddleware(用户登录态 + 设备认证)

路由前缀: /v1/public/file

存储后端: S3 兼容(RustFS

提供两种上传方式:

方式 适用场景 流程
直传 小文件(收款码等) 1 次请求,multipart/form-data 直接上传
预签名 大文件 / 客户端直传 S3 init → 客户端 PUT 到预签名 URL → complete 确认

3.1 直传文件(小文件)

通过 multipart/form-data 直接上传文件到服务端,服务端转存至 S3。

POST /v1/public/file/upload
Content-Type: multipart/form-data

Form 参数

字段 类型 必填 说明
biz_type string 业务类型(如 withdrawal_qrcodeavatar 等)
file file 上传的文件(multipart

cURL 示例

curl -X POST /v1/public/file/upload \
  -H "Authorization: Bearer <token>" \
  -F "biz_type=withdrawal_qrcode" \
  -F "file=@/path/to/alipay_qr.png"

Response

{
  "file_id": "a1b2c3d4e5f678901234",
  "file_name": "alipay_qr.png",
  "object_key": "app-upload/2026/05/27/100/alipay_qr.png__a1b2c3d4e5f678901234",
  "size": 52480,
  "content_type": "image/png",
  "etag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
  "status": "completed"
}

FileUploadResponse 字段说明

字段 类型 说明
file_id string 文件唯一 ID24 字符 hex
file_name string 原始文件名
object_key string S3 对象路径
size int64 文件大小(字节)
content_type string MIME 类型
etag string S3 ETag
status string 状态,直传成功即 completed

3.2 初始化上传(大文件 — 预签名)

获取 S3 预签名 URL,客户端直接 PUT 到 S3,避免文件经过服务端。

POST /v1/public/file/upload/init

Request Body

字段 类型 必填 校验 说明
biz_type string required 业务类型
file_name string required 文件名
content_type string required MIME 类型(如 image/png
size int64 required 文件大小(字节)
sha256 string 文件 SHA256(可选校验)

Request 示例

{
  "biz_type": "withdrawal_qrcode",
  "file_name": "wechat_qr.png",
  "content_type": "image/png",
  "size": 102400,
  "sha256": "e3b0c44298fc1c149afbf4c8996fb924..."
}

Response

{
  "file_id": "b2c3d4e5f6789012345a",
  "object_key": "app-upload/2026/05/27/100/wechat_qr.png__b2c3d4e5f6789012345a",
  "upload_url": "https://s3.example.com/bucket/app-upload/...?X-Amz-Signature=...",
  "method": "PUT",
  "headers": {
    "Content-Type": "image/png"
  },
  "expired_at": 1716700300
}

FileUploadInitResponse 字段说明

字段 类型 说明
file_id string 文件唯一 ID
object_key string S3 对象路径
upload_url string 预签名上传 URL
method string HTTP 方法(PUT
headers map 上传时需携带的请求头
expired_at int64 预签名过期时间(秒级 Unix,默认 300 秒)

客户端上传流程

1. 调用 /upload/init 获取 upload_url
2. 用返回的 method + headers 直接上传文件到 upload_url
3. 上传成功后调用 /upload/complete 确认

3.3 确认上传完成

客户端通过预签名 URL 上传完成后,调用此接口确认文件状态。

POST /v1/public/file/upload/complete

Request Body

字段 类型 必填 校验 说明
file_id string required init 返回的 file_id

Request 示例

{
  "file_id": "b2c3d4e5f6789012345a"
}

Response

{
  "file_id": "b2c3d4e5f6789012345a",
  "object_key": "app-upload/2026/05/27/100/wechat_qr.png__b2c3d4e5f6789012345a",
  "size": 102400,
  "content_type": "image/png",
  "etag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
  "status": "completed"
}

FileUploadCompleteResponse 字段说明

字段 类型 说明
file_id string 文件唯一 ID
object_key string S3 对象路径
size int64 实际文件大小(S3 HeadObject 获取)
content_type string MIME 类型
etag string S3 ETag
status string completed

校验规则

  • 文件大小不能超过配置的 S3.MaxUploadSize
  • Content-Type 必须在配置的 S3.AllowedContentTypes 白名单内(若配置了)
  • complete 时会校验 S3 上的实际文件大小是否与 init 声明的一致
  • 只能确认自己发起的上传(userId 校验)

四、日志查询接口 (Admin)

认证方式: AuthMiddleware(管理员权限)

路由前缀: /v1/admin/log

数据来源: log_message 表(客户端上报的错误/崩溃日志)


4.1 错误日志列表

分页查询客户端上报的错误日志,支持多维度筛选。

GET /v1/admin/log/error_message/list

Query 参数

参数 类型 必填 说明
page int 页码
size int 每页条数
platform string 平台筛选(ios / android / windows / mac / harmony
level uint8 日志级别
user_id int64 用户 ID
device_id string 设备 ID
error_code string 错误码
keyword string 关键字搜索(匹配 message
start int64 开始时间(秒级 Unix
end int64 结束时间(秒级 Unix

Request 示例

GET /v1/admin/log/error_message/list?page=1&size=20&platform=ios&start=1716600000&end=1716700000

Response

{
  "total": 50,
  "list": [
    {
      "id": 1,
      "platform": "ios",
      "app_version": "2.1.0",
      "os_name": "iOS",
      "os_version": "17.5",
      "device_id": "A1B2C3D4",
      "user_id": 100,
      "session_id": "sess_xxx",
      "level": 3,
      "error_code": "VPN_CONNECT_FAIL",
      "message": "Failed to establish VPN tunnel",
      "created_at": 1716700000
    }
  ]
}

ErrorLogMessage 字段说明

字段 类型 说明
id int64 日志 ID
platform string 平台
app_version string 客户端版本
os_name string 操作系统名称
os_version string 操作系统版本
device_id string 设备 ID
user_id int64 用户 ID
session_id string 会话 ID
level uint8 日志级别
error_code string 错误码
message string 错误消息
created_at int64 创建时间(秒级 Unix

4.2 错误日志详情

获取单条错误日志的完整详情(列表字段 + 堆栈/IP/UA 等扩展信息)。

GET /v1/admin/log/error_message/detail

Response

{
  "id": 1,
  "platform": "ios",
  "app_version": "2.1.0",
  "os_name": "iOS",
  "os_version": "17.5",
  "device_id": "A1B2C3D4",
  "user_id": 100,
  "session_id": "sess_xxx",
  "level": 3,
  "error_code": "VPN_CONNECT_FAIL",
  "message": "Failed to establish VPN tunnel",
  "stack": "at VPNManager.connect() line 42\nat ...",
  "client_ip": "1.2.3.4",
  "user_agent": "PPanel/2.1.0 iOS/17.5",
  "locale": "zh-CN",
  "occurred_at": 1716700000,
  "created_at": 1716700000
}

相比列表额外返回的字段

字段 类型 说明
stack string 堆栈信息
client_ip string 客户端 IP
user_agent string User-Agent
locale string 客户端语言/地区
occurred_at int64 误发生时间(秒级 Unix

4.3 日志消息原始详情

获取单条 log_message 的完整原始数据(含 context、digest 等全量字段)。

GET /v1/admin/log/message/detail

Query 参数

参数 类型 必填 说明
id int64 日志消息 ID

Response

{
  "id": 1,
  "platform": "ios",
  "app_version": "2.1.0",
  "os_name": "iOS",
  "os_version": "17.5",
  "device_id": "A1B2C3D4",
  "user_id": 100,
  "session_id": "sess_xxx",
  "level": 3,
  "error_code": "VPN_CONNECT_FAIL",
  "message": "Failed to establish VPN tunnel",
  "stack": "at VPNManager.connect() line 42\nat ...",
  "context": { "server_id": 5, "protocol": "vmess" },
  "client_ip": "1.2.3.4",
  "user_agent": "PPanel/2.1.0 iOS/17.5",
  "locale": "zh-CN",
  "digest": "sha256_abc123...",
  "occurred_at": 1716700000,
  "created_at": 1716700000
}

相比详情额外返回的字段

字段 类型 说明
context any 附加上下文(原始 JSON
digest string 内容摘要(用于去重)