Files
olons_server/docs/移动端任务与账户平台前后端对接文档.md
T

44 KiB
Raw Blame History

移动端任务与账户平台前后端对接文档

版本:V1.0
更新日期:2026-10-09
适用项目:olons_server 数据源:MySQL olons_admin 中的 hg_* 业务表

1. 文档说明

本文档用于移动端与 Olons Server 后端联调。内容依据《移动端任务与账户平台需求文档》、当前代码实现和现有数据库表结构整理。

接口分为两类:

  • 已实现:当前代码已经开放,可以直接联调。
  • 待实现:依据需求和现有数据模型定义的目标契约,不表示服务当前已经开放。

当前认证、个人中心、首页内容、任务订单和手工商城接口已实现。其余接口应按本文档契约逐步开发,实施时如果字段或状态发生调整,必须同步更新本文档后再联调。

2. 能力范围

领域 状态 说明
注册、登录 已实现 创建会员、资料、CNY 钱包并签发登录会话
退出 已实现 撤销当前 Redis 会话
个人资料、密码 已实现 会员概要、头像、登录密码和交易密码
首页、内容、Banner、等级 已实现 只返回已发布且已生效的配置和内容
任务、订单、任务提交 已实现 大厅、资格预检、领单、查询和凭证提交;审核结算由后台负责
钱包、账户明细 已实现 钱包余额随会员资料返回;账户明细读取不可变流水表并支持分页、业务类型和收支方向筛选
收款账户、取出 已实现(沙箱) 加密绑定、试算、申请、资金冻结和记录查询;上线前仍需支付与合规评审
储入 待实现 需在支付与合规评审后开放
手工商城 已实现 已发布商品列表、分类筛选和详情;当前只提供客服咨询动作
客服、通知、上传 待实现 部分数据表已存在

3. 公共协议

3.1 基础路径

{BASE_URL}/v1

接口不再包含应用名路径段,旧前缀不再提供兼容路由。

生产和测试环境均应使用 HTTPS。

3.2 公共请求头

Header 必填 说明
Content-Type: application/json JSON 请求必填 请求编码
Accept: application/json 建议 响应编码
X-Request-ID 建议 客户端生成 UUID,用于链路追踪
X-Device-ID 建议 安装级随机标识,不使用 IMEI 等硬件标识
Authorization: Bearer <token> 受保护接口必填 登录成功后取得的用户会话
Idempotency-Key 取出提交必填 8–80 位请求唯一键;相同业务重试必须复用,不同请求禁止复用

tenant_id 由后端部署配置确定。客户端不得在 Header、Query 或 Body 中传入租户身份。

3.3 登录会话

当前 Token 是 32 字节随机数的 Base64URL 编码,不是 JWT。前端不得解析 Token 或从中推断用户信息。

  • App 使用系统安全存储;Web 不在普通 LocalStorage 中长期保存敏感会话。
  • Token 不得进入 URL、埋点、普通日志或错误上报。
  • HTTP 401:清除本地会话,保存当前页面地址并跳转登录。
  • HTTP 403:展示无权限或账户访问受限,不直接按登录过期处理。
  • “记住密码”只属于客户端安全存储策略,不作为接口字段上传,严禁明文保存密码。

3.4 时间、金额和比例

  • 时间统一为 RFC 3339 UTC,例如 2026-10-09T10:00:00.123Z。
  • 金额统一使用最小货币单位整数。CNY 的 amount=29800 表示 298.00 元。
  • 比例统一使用万分比。rate=500 表示 5%。
  • 前端不得使用浮点数参与资金计算,页面展示值不参与后端结算。

金额对象示例:

{
  "amount": 29800,
  "currency": "CNY"
}

3.5 成功响应

{
  "code": 0,
  "data": {},
  "request_id": "29ece743-7474-4f32-8141-31f994da68e9",
  "server_time": "2026-10-09T10:00:00.123Z"
}

data 可为对象、列表或 null。前端同时判断 HTTP 状态码和业务 code。

3.6 失败响应

{
  "code": 30000,
  "reason": "PHONE_INVALID_FORMAT",
  "message": "validation failed",
  "details": {
    "field": "phone"
  },
  "request_id": "29ece743-7474-4f32-8141-31f994da68e9",
  "server_time": "2026-10-09T10:00:00.123Z"
}
字段 用途
code 稳定业务码,用于程序分支
reason 稳定英文原因,用于本地化文案映射
message 后端降级文案
details 错误字段、等待时间等结构化详情
request_id 客服和日志排查依据

3.7 分页

GET /v1/task-orders?page=1&page_size=20&status=pending
  • page 从 1 开始,默认 1。
  • page_size 默认 20,最大 100。
  • 默认按创建时间倒序。
  • 排序字段必须使用后端白名单。
{
  "code": 0,
  "data": {
    "items": [],
    "page": 1,
    "page_size": 20,
    "total": 0,
    "has_next": false
  },
  "request_id": "uuid",
  "server_time": "2026-10-09T10:00:00Z"
}

3.8 重复提交

  • 注册接口不使用 Idempotency-Key,由账户名、手机号等数据库唯一约束最终防重。
  • 任务领取、任务提交、储入和取出等高风险写操作必须由后端使用业务单号、状态机和账务约束防止重复执行。
  • 前端在写请求执行中禁用按钮;发生超时后先查询业务单状态,不盲目再次提交。
  • 钱包入账以业务单号及 hg_wallet_ledger.idempotency_key 等后端约束为准。

4. 认证接口

4.1 注册(已实现)

POST /v1/auth/register
Content-Type: application/json

未登录可访问。

{
  "username": "alice01",
  "phone": "13800138000",
  "password": "Secret123",
  "referral_code": "123456",
  "privacy_accepted": true
}
字段 类型 必填 规则
username string 是 1–10 位小写字母或数字;后端去空格并转小写
phone string 是 中国大陆 11 位手机号,不带 +86
password string 是 6–72 字节,不做 MD5
referral_code string 是 6 位数字,必须匹配正常会员邀请码
privacy_accepted boolean 是 必须为 true

注册事务同时写入:

  • hg_app_member:会员主档。
  • hg_app_member_profile:会员资料。
  • hg_wallet_account:CNY 零余额钱包。

成功响应:

{
  "code": 0,
  "data": {
    "member_id": 9,
    "member_no": "MABCDEFGHIJKLMNOPQRST",
    "username": "alice01",
    "phone": "13800138000",
    "invite_code": "654321",
    "token": "random-base64url-token",
    "token_type": "Bearer",
    "expires_at": "2026-10-23T10:00:00Z"
  },
  "request_id": "uuid",
  "server_time": "2026-10-09T10:00:00Z"
}

注册成功即自动登录。前端保存 Token 后直接进入首页,不再调用登录接口。

4.2 登录(已实现)

POST /v1/auth/login
Content-Type: application/json
{
  "phone": "13800138000",
  "password": "Secret123"
}

成功响应与注册相同。连续登录失败达到 10 次时,该账号在 15 分钟窗口内被限流。

4.3 退出(已实现)

POST /v1/auth/logout
Authorization: Bearer <token>

后端删除当前会话,响应 data=null。缺少、格式错误或已失效的 Token 返回 HTTP 401 和 AUTH_REQUIRED。前端无论请求是否成功都清除本地凭证。

4.4 认证错误

HTTP reason 说明
400 MALFORMED_REQUEST JSON 不合法、字段类型错误或存在未知字段
400 USERNAME_INVALID_FORMAT 账户名格式错误
400 PHONE_INVALID_FORMAT 手机号格式错误
400 PASSWORD_INVALID_FORMAT 密码格式错误
400 REFERRAL_CODE_INVALID 邀请码为空、格式错误或不存在
400 PRIVACY_POLICY_NOT_ACCEPTED 未同意隐私政策
401 INVALID_CREDENTIALS 手机号或密码错误
409 ACCOUNT_ALREADY_REGISTERED 账户名、手机号或其他唯一字段冲突
429 RATE_LIMITED 登录失败次数过多
503 SERVICE_UNAVAILABLE Redis 或敏感数据密钥未正确配置

5. 个人中心(已实现)

5.1 当前会员概要(已实现)

GET /v1/me
{
  "code": 0,
  "data": {
    "member_id": 9,
    "member_no": "MABCDEFGHIJKLMNOPQRST",
    "username": "alice01",
    "phone": "13800138000",
    "avatar_url": "",
    "invite_code": "654321",
    "level": {
      "id": 1,
      "code": "normal",
      "name": "普通会员",
      "icon_url": "",
      "commission_rate": 180
    },
    "member_status": "active",
    "member_category": "real",
    "credit_score": 100,
    "wallet": {
      "available_amount": 0,
      "frozen_amount": 0,
      "total_income": 0,
      "total_expense": 0,
      "currency": "CNY",
      "status": "active",
      "version": 0
    }
  },
  "request_id": "uuid",
  "server_time": "2026-10-09T10:00:00Z"
}

5.2 更换头像(已实现)

PATCH /v1/me/avatar
{
  "attachment_id": 101
}

附件必须是 app_id=olons_server、归属当前会员、状态有效且 MIME 类型为图片的 hg_sys_attachment 记录。上传接口未实现前,前端不能自行生成可用附件 ID。

成功响应 data:

{
  "attachment_id": 101,
  "avatar_url": "https://cdn.example.com/avatar.png"
}

5.3 修改登录密码(已实现)

PUT /v1/me/password
{
  "old_password": "OldSecret123",
  "new_password": "NewSecret456"
}

新密码为 6–72 字节且不能与当前密码相同。修改成功后撤销该会员在密码修改前签发的所有会话,包括当前会话;前端收到成功响应后清理 Token 并返回登录页。

5.4 设置或修改交易密码(已实现)

PUT /v1/me/trade-password
{
  "login_password": "Secret123",
  "old_trade_password": "123456",
  "new_trade_password": "654321"
}

交易密码固定为 6 位数字。首次设置时不传 old_trade_password;已设置时必须传入正确的旧交易密码。每次操作都需要 login_password 二次确认。

5.5 个人中心错误

HTTP reason 说明
400 AVATAR_ATTACHMENT_INVALID 附件无效、非图片或不属于当前会员
400 CURRENT_PASSWORD_INVALID 当前登录密码错误
400 LOGIN_PASSWORD_INVALID 设置交易密码时二次确认的登录密码错误
400 NEW_PASSWORD_INVALID 新登录密码格式错误或与原密码相同
400 OLD_TRADE_PASSWORD_REQUIRED 已设置交易密码但未传旧密码
400 OLD_TRADE_PASSWORD_INVALID 旧交易密码错误
400 TRADE_PASSWORD_INVALID 新交易密码不是 6 位数字或与原密码相同
404 ACCOUNT_NOT_FOUND 会员不存在或已停用
409 ACCOUNT_CHANGED 会员安全信息已被并发修改,需重新操作

6. 首页和内容(已实现)

6.1 首页聚合(已实现)

GET /v1/home
{
  "code": 0,
  "data": {
    "profile": {
      "username": "alice01",
      "avatar_url": "",
      "level_name": "普通会员"
    },
    "wallet": {
      "available_amount": 100000,
      "frozen_amount": 0,
      "currency": "CNY"
    },
    "features": {
      "task_enabled": true,
      "recharge_enabled": false,
      "withdrawal_enabled": false,
      "manual_mall_enabled": true
    },
    "banners": [],
    "notices": [],
    "task_summary": {
      "today_count": 0,
      "today_commission_amount": 0,
      "yesterday_commission_amount": 0
    }
  },
  "request_id": "uuid",
  "server_time": "2026-10-09T10:00:00Z"
}

features 由当前已发布的 hg_site_config_version.config_json 生成,前端不得写死。关闭的功能入口应显示后端提供的原因。

功能开关支持位于 features 对象内或配置根节点。找不到已发布且已到生效时间的配置时,所有开关默认为 false。

task_summary.today_count 按当日创建的任务订单统计;今日和昨日佣金仅统计 hg_task_submission.submission_status=4 的已结算数据。

6.2 Banner(已实现)

GET /v1/banners?position=home_top

position 默认为 home_top,只允许小写字母、数字、下划线和连字符。接口只返回已发布、处于投放时间内且未删除的 Banner。

Banner 跳转类型:

数据库值 API target_type 处理
1 none 不跳转,不返回 target_value
2 internal_route 只接受单斜线开头的站内路由
3 external_url 只接受带有有效域名的 HTTPS URL

不安全或不识别的跳转值降级为 none。

6.3 内容详情(已实现)

GET /v1/contents/{content_key}

privacy_policy 和 user_agreement 允许未登录访问,其他内容需要 Bearer Token。目前数据库中尚无这两个公开内容键,在后台发布前请求会返回 404 RESOURCE_NOT_FOUND。

内容详情建议字段:

{
  "content_key": "privacy_policy",
  "content_type": "policy",
  "title": "用户隐私政策",
  "summary": "",
  "body_html": "<p>...</p>",
  "body_text": "...",
  "version": 3,
  "published_at": "2026-10-09T08:00:00Z"
}

asset_ids 始终返回数组。body_html 来自后台已清洗的发布版本;前端仍需禁止脚本、事件属性和危险 URL scheme。

6.4 会员等级(已实现)

GET /v1/member-levels

只返回已启用等级及其当前已发布、已生效版本,按后台 sort 排序。

列表项:

{
  "id": 1,
  "code": "VIP1",
  "name": "普通会员",
  "icon_url": "",
  "upgrade_amount": 50000,
  "gift_amount": 2000,
  "daily_order_limit": 10,
  "commission_rate": 180,
  "daily_withdraw_limit": 4,
  "rules": {},
  "version": 1
}

6.5 荣誉榜(已实现)

GET /v1/leaderboard

接口要求登录,返回当前租户内已完成任务数量排名前 50 的有效会员。排名只统计 hg_trade_order.order_type=2 AND order_status=4 的真实任务订单;昵称由服务端脱敏,前端不得还原或展示手机号。

{
  "code": 0,
  "data": [
    {
      "rank": 1,
      "username": "风***淡",
      "avatar_url": "https://cdn.example.com/avatar.png",
      "completed_count": 12580,
      "status": "领取成功"
    }
  ]
}

没有已完成任务时返回空数组,不使用模拟排行数据填充页面。

6.6 首页和内容错误

HTTP reason 说明
400 BANNER_POSITION_INVALID Banner 位置参数格式错误
400 CONTENT_KEY_INVALID 内容键格式错误
404 RESOURCE_NOT_FOUND 内容不存在、未发布或未生效

7. 任务和订单(已实现)

7.1 接口清单

方法 接口 用途
GET /v1/task-halls 当前等级可访问的任务大厅
GET /v1/tasks/eligibility?hall_id=1 领取资格预检
GET /v1/tasks/stats 总资产、今日/昨日佣金、领取数量
POST /v1/task-orders/claim 领取或匹配任务
GET /v1/task-orders 订单分页
GET /v1/task-orders/{order_id} 订单详情
POST /v1/task-orders/{order_id}/submissions 提交任务证据

本章接口全部要求登录。数据来源为 hg_task_hall、hg_task、hg_trade_order、hg_task_submission、hg_wallet_account 和 hg_wallet_ledger,仅处理 order_type=2 的任务订单,不会混入手工商城订单。

任务开关和领单规则读取当前已发布且已生效的 hg_site_config_version.config_json。支持以下配置键(可放在根节点或嵌套对象中):

配置键 含义 未配置时
task_enabled 任务功能开关 false,关闭领单
task_minimum_amount 最低可用余额,单位分 0
task_business_start_at 营业开始时间,格式 HH:mm 或 HH:mm:ss 00:00:00
task_business_end_at 营业结束时间,格式 HH:mm 或 HH:mm:ss 23:59:59
task_deadline_minutes 订单完成时限,分钟 15

开关采用失败关闭策略:找不到有效配置或没有 task_enabled=true 时,可以读取大厅、统计及历史订单,但资格预检返回 TASK_DISABLED,领单被拒绝。

7.2 领取资格预检

GET /v1/tasks/eligibility?hall_id=1
{
  "code": 0,
  "data": {
    "eligible": false,
    "reason": "INSUFFICIENT_BALANCE",
    "message": "可用余额不足",
    "available_amount": 10000,
    "minimum_amount": 50000,
    "today_count": 2,
    "daily_limit": 5,
    "pending_order_id": 0,
    "business_start_at": "09:00:00+08:00",
    "business_end_at": "22:00:00+08:00"
  },
  "request_id": "uuid",
  "server_time": "2026-10-09T10:00:00Z"
}

预检包括营业时间、会员状态、钱包状态、最低余额、当日数量、未完成订单及等级范围。预检只供展示,领取时后端必须在事务内再次校验。

判断顺序为:任务开关、营业时间、钱包状态、最低余额、等级每日上限、未完成订单。risk_status 是会员类别(1=真人、2=假人),不是风控状态,不能据此拒绝领单。

7.3 领取任务

POST /v1/task-orders/claim
{
  "hall_id": 1
}

成功返回订单摘要,包括 id、order_no、status、product、total_amount、commission_amount、currency 和 deadline_at。

领单事务会锁定当前会员和钱包记录,重新校验大厅等级、账户状态、最低余额、会员等级每日领取次数和未完成订单。校验通过后先创建 status=matching、order_type=2 的派单中订单,此时商品、数量、金额和佣金均为空或 0,与后台订单列表的“派单中/匹配订单”原型一致。会员的 auto_dispatch=1 时,达到网站设置的 autoOrderDelaySeconds 延时后由系统自动匹配:从当前已发布、在有效期内、币种为 CNY,且价格同时满足会员可用余额与 autoOrderMinAmount/autoOrderMaxAmount 区间的 hg_product 中随机选取商品,按会员当前等级版本计算佣金,并将订单原子更新为 status=pending。匹配成功时在同一事务内从可用余额扣除订单本金并转入冻结余额,同时生成可用余额支出和冻结余额收入流水。订单完成时释放冻结本金、返回可用余额并另行入账佣金;订单超时时也必须释放并退回本金。auto_dispatch=2 或自动匹配暂时找不到商品时,订单保持“派单中”,管理员可在后台通过“匹配订单”人工指定商品,人工匹配也执行相同的本金扣除和冻结流程。自动和人工匹配共用同一状态条件,并发时只有一方能更新成功。新注册会员默认开启自动派单,管理员可在会员编辑弹窗修改派单模式。

HTTP reason 说明
409 PENDING_ORDER_EXISTS 存在未完成订单
422 OUTSIDE_BUSINESS_HOURS 不在营业时间
422 INSUFFICIENT_BALANCE 可用余额不足
422 DAILY_LIMIT_REACHED 已达当日上限
422 LEVEL_NOT_ELIGIBLE 等级不符合
422 ACCOUNT_RESTRICTED 账户状态受限
422 TASK_DISABLED 任务开关未开启或没有已生效配置
503 NO_TASK_AVAILABLE 当前没有可匹配任务

7.4 订单列表

GET /v1/task-orders?page=1&page_size=20&status=pending

status 可选:all、matching、pending、processing、completed、expired、frozen、cancelled、failed。

{
  "items": [
    {
      "id": 101,
      "order_no": "TO202610090001A1B2C3D4",
      "task_id": 1,
      "product": {
        "id": 20,
        "name": "示例商品",
        "cover_url": "https://cdn.example.com/product.png"
      },
      "quantity": 1,
      "unit_price_amount": 29800,
      "total_amount": 29800,
      "commission_amount": 1490,
      "currency": "CNY",
      "status": "pending",
      "status_text": "待处理",
      "message": "请在截止时间前完成",
      "created_at": "2026-10-09T10:00:00Z",
      "updated_at": "2026-10-09T10:01:00Z",
      "deadline_at": "2026-10-09T10:15:00Z"
    }
  ],
  "page": 1,
  "page_size": 20,
  "total": 1
}

7.5 订单详情与提交

订单详情额外返回任务说明、商品和规则快照、状态时间线、失败原因、关联账变及 allowed_actions。倒计时使用响应 server_time 与 deadline_at 的差值。

POST /v1/task-orders/{order_id}/submissions
{}

成功返回 submission_no、status=settled、submitted_at 和 order_status=completed。重复提交不得生成多笔提交记录或佣金流水。

正常情况下仅 pending 订单允许提交。前端按原型提供“立即提交”确认操作,不要求用户填写凭证。接口仍兼容可选的 evidence_text 和 evidence_attachment_ids:文字最长 2000 字符、附件最多 9 个;附件必须属于当前租户和当前会员、app_id=olons_server、状态有效,且类型为图片或 PDF。提交事务对订单、钱包加锁,超时订单自动置为 expired;有效提交创建唯一的已结算记录、佣金流水并增加可用余额,最后把订单置为 completed。后台审核页保留已结算记录供查询,冻结、强制处理和人工补偿只作为异常兜底。

订单详情返回:

  • task:任务标题、说明和详情 HTML;
  • rule_snapshot:领单时固化的佣金、等级和截止时间;
  • submission:凭证、审核状态与驳回原因;
  • timeline:创建、匹配、提交、审核、结算、完成时间点;
  • ledgers:以订单号或提交号关联的钱包流水;
  • allowed_actions:待处理为 submit、contact_support,超时/冻结/失败仅为 contact_support;提交成功后不再提供重复提交操作。

7.6 任务统计

GET /v1/tasks/stats
{
  "available_amount": 100000,
  "frozen_amount": 29800,
  "total_asset_amount": 129800,
  "today_commission_amount": 1490,
  "yesterday_commission_amount": 1000,
  "today_claim_count": 2,
  "today_completed_count": 1
}

佣金只统计 hg_task_submission.submission_status=4 的已结算记录,今日领取和完成数量只统计任务订单。日期边界按数据库 UTC 日期计算。

7.7 通用校验错误

HTTP reason 说明
400 HALL_ID_INVALID 大厅 ID 缺失或格式错误
400 ORDER_ID_INVALID 订单 ID 格式错误
400 PAGE_INVALID / PAGE_SIZE_INVALID 分页参数错误,page_size 最大 100
400 ORDER_STATUS_INVALID 状态筛选值不在允许范围内
400 EVIDENCE_INVALID 凭证内容或附件不符合要求
404 ORDER_NOT_FOUND 订单不存在或不属于当前会员
409 ORDER_STATE_INVALID 当前状态不允许提交或已经提交过
409 ORDER_EXPIRED 提交时订单已超时

8. 手工商城(已实现)

方法 接口 说明
GET /v1/manual-products?page=1&page_size=20&category_id=0 product_type=2 的已发布商品
GET /v1/manual-products/{product_id} 商品详情和可执行动作

两个接口均要求登录,只返回同时满足以下条件的商品:

  • hg_product.product_type=2;
  • 商品 publish_status=3,未被软删除且在展示有效期内;
  • 所属 hg_product_category.status=1,并且分类未被软删除。

8.1 商品列表

GET /v1/manual-products?page=1&page_size=20&category_id=0

category_id=0 或不传表示全部启用分类,指定正整数时按分类筛选。推荐商品优先,再按后台 sort 和商品 ID 排序。响应示例:

{
  "items": [
    {
      "id": 4,
      "product_no": "DEMO-P-03",
      "category": {
        "id": 2,
        "code": "DEMO_DAILY",
        "name": "演示日用百货"
      },
      "name": "已发布演示商品",
      "shop_name": "Olons 演示店",
      "price_amount": 12900,
      "currency": "CNY",
      "cover_url": "https://cdn.example.com/product.png",
      "summary": "商品说明摘要",
      "claimed_count": 1,
      "is_recommended": true,
      "is_limited": true,
      "start_at": "2026-10-01T00:00:00Z",
      "end_at": "2026-10-31T23:59:59Z",
      "available_actions": ["contact_support"]
    }
  ],
  "page": 1,
  "page_size": 20,
  "total": 1
}

金额单位为分。现有 hg_product 没有独立摘要字段,summary 由商品详情转换为纯文本后截取前 120 个字符。claimed_count 统计该商品 order_type=3 且状态不是已取消或失败的订单。is_limited 在商品配置了 end_at 时为 true,客户端使用该字段区分限时商品,不根据分类名称推断;start_at、end_at 为商品展示有效期。

8.2 商品详情

GET /v1/manual-products/4

详情在列表字段基础上增加:

{
  "payment_type": "balance",
  "detail_text": "已发布演示商品,用于展示。",
  "created_at": "2026-10-05T14:32:29Z",
  "updated_at": "2026-10-08T14:32:29Z"
}

当前版本只支持用户主动咨询,不创建商城订单,也不扣款或冻结余额,因此 available_actions 固定返回 ["contact_support"]。后续开放购买时必须另行增加下单接口、库存校验、幂等与资金事务,不能复用咨询动作直接扣款。

运营详情不会以原始 HTML 返回。后端移除标签、脚本和样式内容后输出 detail_text,避免持久型 XSS;cover_url 仅接受不包含用户凭据的 HTTPS 地址,不符合要求时返回空字符串。

8.3 错误码

HTTP reason 说明
400 CATEGORY_ID_INVALID 分类 ID 不是非负整数
400 PRODUCT_ID_INVALID 商品 ID 缺失或格式错误
400 PAGE_INVALID / PAGE_SIZE_INVALID 分页参数错误,page_size 最大 100
404 PRODUCT_NOT_FOUND 商品不存在、未发布、已过展示期、已删除或分类已停用

9. 钱包和明细(待实现)

9.1 钱包概要

GET /v1/wallet
{
  "code": 0,
  "data": {
    "available_amount": 100000,
    "frozen_amount": 29800,
    "total_income": 50000,
    "total_expense": 10000,
    "currency": "CNY",
    "status": "active",
    "version": 12
  },
  "request_id": "uuid",
  "server_time": "2026-10-09T10:00:00Z"
}

9.2 账户明细

GET /v1/wallet/ledgers?page=1&page_size=20&biz_type=task&direction=income
{
  "id": 1001,
  "ledger_no": "WL202610090001",
  "biz_type": "task",
  "biz_no": "TO202610090001",
  "entry_type": "commission",
  "direction": "income",
  "amount": 1490,
  "before_amount": 100000,
  "after_amount": 101490,
  "currency": "CNY",
  "remark": "任务佣金",
  "occurred_at": "2026-10-09T10:30:00Z",
  "resource": {
    "type": "task_order",
    "id": 101
  }
}

钱包流水只能由后端业务事务生成,移动端没有新增、修改或删除流水的接口。

10. 储入(待实现)

本组接口在支付通道、收款主体和合规评审完成前只允许沙箱联调。

方法 接口 用途
GET /v1/recharge/channels 可用通道、金额范围和手续费规则
POST /v1/recharge-orders 创建储入订单
GET /v1/recharge-orders 储入记录分页
GET /v1/recharge-orders/{order_id} 储入详情

创建请求:

{
  "channel_id": 1,
  "amount": 50000,
  "currency": "CNY",
  "voucher_attachment_ids": [],
  "remark": ""
}

响应包含 recharge_no、status、request_amount、payable_amount、currency、expire_at 和经过校验的支付指引。不通过客服聊天发送临时个人收款账户。

公开状态:pending_payment、reviewing、processing、succeeded、failed、expired、cancelled。当前人工审核流程创建后直接进入 reviewing;pending_payment 仅为后续接入真实支付通道和支付回调时保留。失败、驳回或过期必须返回 reason_code 和 reason_text。

11. 收款账户和取出(已实现,沙箱)

本组接口已完成代码和现有数据库的读接口联调。在支付通道、付款主体、实名要求、反洗钱策略和法务评审完成前,只允许沙箱使用,不得连接真实付款通道。

取出功能读取当前已发布且已生效的 hg_site_config_version.config_json。仅当根节点或嵌套对象中的 withdrawal_enabled=true 时允许试算和提交;没有有效配置时按失败关闭策略返回 WITHDRAWAL_DISABLED。收款账户查询与绑定不受该开关影响。

11.1 收款账户

GET /v1/payment-accounts
POST /v1/payment-accounts
POST /v1/attachments/images

先分别上传微信和支付宝二维码。图片接口使用 multipart/form-data,字段名为 file,仅接受不超过 5MB 的 JPG、PNG、WebP,返回附件 id 和可访问的 url。随后提交新增请求:

{
  "bank_name": "中国工商银行",
  "holder_name": "张三",
  "identity_no": "11010119900101123X",
  "account_no": "6222000000001234",
  "wechat_qr_attachment_id": 101,
  "alipay_qr_attachment_id": 102
}

绑卡页面按原型固定为银行卡账户,字段规则如下:

字段 是否必填 说明
holder_name 是 2–64 个字符
identity_no 是 15 位或 18 位身份证号,18 位末位允许 X
account_no 是 8–32 位数字,空格和连字符会被移除
bank_name 是 2–100 个字符
wechat_qr_attachment_id 是 当前会员上传的有效图片附件
alipay_qr_attachment_id 是 当前会员上传的有效图片附件,不能与微信二维码共用同一附件

姓名、身份证号和银行卡号使用 AES-256-GCM 独立随机 nonce 加密,身份证号和银行卡号另存 HMAC-SHA256 检索哈希。列表只返回脱敏信息,绝不返回明文或密文。绑定不要求支付密码;支付密码只在取出提交时校验。当前产品不设置银行卡后台审核流程,绑定成功后直接写入 verify_status=verified,可立即用于取出。

列表项示例:

{
  "id": 1,
  "account_type": "bank_card",
  "bank_code": "BANK",
  "bank_name": "中国工商银行",
  "holder_mask": "张*",
  "identity_no_mask": "1101**********123X",
  "account_no_mask": "6222********1234",
  "wechat_qr_attachment_id": 101,
  "alipay_qr_attachment_id": 102,
  "verify_status": "verified",
  "is_default": true,
  "created_at": "2026-10-09T10:00:00Z",
  "updated_at": "2026-10-09T10:00:00Z"
}

11.2 取出试算与提交

POST /v1/withdrawal-orders/quote
POST /v1/withdrawal-orders
GET  /v1/withdrawal-orders?page=1&page_size=20&status=all
GET  /v1/withdrawal-orders/{order_id}

试算请求:

{
  "payment_account_id": 1,
  "amount": 50000,
  "currency": "CNY"
}

试算根据已核验收款账户选择启用且支持取出的 hg_payment_channel。优先匹配账户类型对应通道,不存在时使用人工通道。手续费支持固定金额、万分比和两者相加三种模式;比例手续费向上取整到分。

试算返回金额、渠道、可用余额、当日次数、额度范围和 5 分钟后的 quote_expires_at,但不冻结余额:

{
  "payment_account_id": 1,
  "account_mask": "6222********1234",
  "channel_id": 3,
  "channel_code": "bank",
  "channel_name": "银行卡",
  "request_amount": 50000,
  "fee_amount": 100,
  "actual_amount": 49900,
  "currency": "CNY",
  "minimum_amount": 1,
  "maximum_amount": 100000,
  "available_amount": 100000,
  "today_count": 0,
  "daily_limit": 4,
  "quote_expires_at": "2026-10-09T10:05:00Z"
}

提交请求:

POST /v1/withdrawal-orders
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
  "payment_account_id": 1,
  "amount": 50000,
  "currency": "CNY",
  "trade_password": "654321"
}

后端不信任试算结果,提交时重新读取通道并计算手续费和到账金额。Idempotency-Key 会结合租户和会员派生稳定业务单号;相同请求重试返回原订单并标记 idempotent_replay=true,同一个键用于不同账户、金额或币种时返回 IDEMPOTENCY_CONFLICT,不依赖额外幂等记录表。

提交事务执行以下操作:

  1. 锁定会员,确认账户有效、交易密码未变化;
  2. 校验收款账户属于当前会员且已核验;
  3. 锁定并重新校验支付通道规则;
  4. 锁定 CNY 钱包,检查钱包状态、可用余额及会员等级每日取出次数;
  5. 创建 pending_review 取出订单和加密收款账户快照;
  6. 可用余额减少申请金额,冻结余额增加申请金额;
  7. 分别写入可用余额支出和冻结余额收入两条不可变流水。

订单、钱包和两条流水在同一数据库事务内提交,任一步失败全部回滚。提交阶段不增加 total_expense;后台付款成功时才把冻结金额转为最终支出,失败、驳回或取消时必须解冻退回。后台状态处理不属于本章客户端接口。

公开状态为 pending_review、reviewing、processing、succeeded、failed、rejected、cancelled。列表支持上述状态过滤和标准分页;详情、列表均返回脱敏收款快照、金额、手续费、状态、失败原因和各状态时间,不返回 account_snapshot_cipher。

11.3 错误码

HTTP reason 说明
400 ACCOUNT_TYPE_INVALID 收款账户类型不支持
400 PAYMENT_ACCOUNT_FIELDS_INVALID 动态必填字段或格式错误
400 TRADE_PASSWORD_INVALID 交易密码格式或内容错误
400 CURRENCY_INVALID 当前仅支持 CNY
400 IDEMPOTENCY_KEY_INVALID 取出提交未提供合规的幂等键
400 WITHDRAWAL_STATUS_INVALID 列表状态筛选值错误
409 PAYMENT_ACCOUNT_EXISTS 相同类型和账号已经绑定
409 IDEMPOTENCY_CONFLICT 同一幂等键被用于不同请求
422 TRADE_PASSWORD_NOT_SET 尚未设置交易密码
422 PAYMENT_ACCOUNT_INVALID 收款账户不存在、已删除或未核验
422 WITHDRAWAL_DISABLED 取出功能未开启
422 WITHDRAWAL_AMOUNT_INVALID 金额不在通道范围、超过余额或手续费不合理
422 WITHDRAWAL_DAILY_LIMIT_REACHED 已达到会员等级每日次数上限
422 INSUFFICIENT_BALANCE 可用余额不足
422 ACCOUNT_RESTRICTED 会员或钱包状态受限
429 RATE_LIMITED 交易密码连续错误次数过多,15 分钟后重试
503 WITHDRAWAL_CHANNEL_UNAVAILABLE 没有可用的取出通道

12. 客服、通知和上传

12.1 客服(已实现)

GET /v1/customer-services

响应字段包括 id、service_code、name、avatar_url、work_time、online_status、service_url、backup_contact 和 privacy_notice。

只返回 hg_customer_service.status=1 且未删除的客服,按 sort、id 排序。online_status 映射为 online、offline 或 busy。头像和客服地址仅返回不含用户凭据的 HTTPS URL,不符合要求时返回空字符串。

客服 URL 必须经服务端域名白名单校验。前端 WebView/iframe 只开启必要权限,并显示第三方服务来源和隐私提示。

12.2 通知

GET  /v1/notices?page=1&page_size=20
GET  /v1/notices/{notice_id}
POST /v1/notices/{notice_id}/read

当前已实现通知列表接口。列表包含当前会员通知和 member_id=0 的全体通知,只返回有效期内且未撤回记录,按创建时间倒序排列。

{
  "items": [
    {
      "id": 1,
      "title": "系统维护通知",
      "content": "通知内容",
      "source_type": "real",
      "start_at": "2026-10-13T00:00:00Z",
      "end_at": "2026-10-20T00:00:00Z",
      "created_at": "2026-10-13T00:00:00Z"
    }
  ],
  "page": 1,
  "page_size": 20,
  "total": 1
}

source_type 为 real(系统通知)或 operational_simulation(活动通知)。详情和已读接口尚未实现;当前数据库没有会员通知已读关系表,如需精确已读状态,实现前应补充数据模型。

12.3 上传

POST /v1/uploads
{
  "scene": "task_evidence",
  "file_name": "evidence.jpg",
  "mime_type": "image/jpeg",
  "size": 102400
}

推荐流程为“申请凭证 → 上传隔离区 → 安全扫描 → 绑定业务”。业务接口只接受扫描通过且属于当前会员的附件 ID。

场景 类型 建议限制
avatar JPEG、PNG、WebP 5 MiB,校验像素尺寸
task_evidence JPEG、PNG、WebP、PDF 单文件 10 MiB,最多 9 个
payment_qr JPEG、PNG、WebP 5 MiB,内容校验
recharge_voucher JPEG、PNG、WebP、PDF 单文件 10 MiB

13. 状态枚举

数据库数字状态是内部实现。API 对前端返回稳定字符串和 status_text,前端不得依赖数据库数值。

hg_app_member.risk_status 字段名沿用现有数据库,实际业务含义是会员类别:1 真人、2 假人。API 统一使用 member_category=real|fake,不将该字段用于判断账户是否可登录。

领域 API 状态
会员 active、disabled、cancelled
会员类别 real、fake
任务订单 matching、pending、processing、completed、expired、frozen、cancelled、failed
任务提交 reviewing、approved、rejected、settled、reversed
储入 pending_payment、reviewing、processing、succeeded、failed、expired、cancelled
取出 pending_review、reviewing、processing、succeeded、failed、rejected、cancelled
收款账户核验 pending、verified、rejected
客服 online、offline、busy

已实现领域按本表映射;待实现领域在开发前仍需与 HotGo 字典和正式状态流转确认。

14. 通用错误与重试

HTTP reason 前端处理
400 MALFORMED_REQUEST 根据 details.field 定位表单字段
401 AUTH_REQUIRED 清除会话并跳转登录
403 FORBIDDEN 显示无权限或账户访问受限
404 RESOURCE_NOT_FOUND 显示内容下线或记录不存在
405 METHOD_NOT_ALLOWED 作为开发错误记录,不重试
409 CONFLICT 刷新业务资源后让用户决定下一步
422 具体业务原因码 显示原因和恢复入口
429 RATE_LIMITED 按 retry_after_seconds 倒计时
500 INTERNAL 展示通用错误和 request_id
503 SERVICE_UNAVAILABLE 安全读请求可限次重试
  • GET 在网络错误或 503 时可指数退避,最多自动重试 2 次。
  • POST、PUT、PATCH 默认不自动重试;超时后先查询业务资源。
  • 400、401、403、404、409、422 不做网络层自动重试。

15. 页面与接口映射

页面 首屏接口 操作接口
/login — POST /auth/login
/register GET /contents/privacy_policy POST /auth/register
/home GET /home Banner、快捷入口、内容详情
/receive GET /tasks/eligibility、GET /tasks/stats POST /task-orders/claim
记录页 GET /task-orders 跳转订单详情
/orderDetail GET /task-orders/{id} POST /task-orders/{id}/submissions
/mall GET /manual-products 查看详情或客服
/customerService GET /customer-services 打开安全 WebView/iframe
/mine GET /me 钱包、收款账户、密码、通知、退出
/recharge GET /recharge/channels、GET /member-levels POST /recharge-orders
/withdrawal GET /wallet、GET /payment-accounts 试算、提交取出
/depositRecord GET /recharge-orders 查看详情
/withdrawalRecord GET /withdrawal-orders 查看详情
/bill GET /wallet/ledgers 跳转关联业务详情

表中省略公共前缀 /v1。

16. API 与数据表映射

领域 主表 关联表
认证、会员资料 hg_app_member hg_app_member_profile、hg_app_member_level
收款账户 hg_app_member_payment_account hg_sys_attachment
钱包 hg_wallet_account hg_wallet_ledger
首页配置 hg_site_config_version hg_banner、hg_content、hg_content_version
会员等级 hg_app_member_level hg_app_member_level_version
任务大厅 hg_task_hall hg_task_hall_level
任务订单 hg_task、hg_trade_order hg_task_level、hg_product、hg_task_submission、hg_wallet_ledger
手工商城 hg_product hg_product_category、hg_sys_attachment
储入 hg_recharge_order hg_payment_channel、hg_wallet_account、hg_wallet_ledger
取出 hg_withdrawal_order hg_app_member_payment_account、hg_wallet_account、hg_wallet_ledger
客服 hg_customer_service 第三方会话映射
通知 hg_app_member_notice 已读关系待补充

17. 前端实现要求

17.1 请求层

  • 集中注入 BASE_URL、X-Request-ID、X-Device-ID 和 Authorization。
  • 集中处理统一响应、401、429、超时和断网。
  • 搜索、分页和页签切换时取消过期请求,避免旧响应覆盖新状态。
  • 日志不记录密码、交易密码、Token、完整手机号、银行卡和收款码。

17.2 页面状态

每个页面至少覆盖:

  • 首次加载、下拉刷新、加载更多、空状态、可重试失败、不可重试业务失败。
  • 写请求期间按钮 loading 和防重复点击。
  • 服务端返回的状态文案、失败原因与恢复入口。
  • 320–430 像素宽度可用;键盘弹出时表单按钮可达;触控区域至少 44×44 像素。

17.3 语言和文案

  • reason、status 等枚举固定使用英文。
  • 前端语言包映射用户文案,不把后端 message 作为唯一文案源。
  • 本文档统一使用“储入”“取出”“领取”。如产品最终改为“充值”“提现”“接单”,前后端文档与语言包必须同时调整。

18. 联调顺序

  1. P0 认证:注册、登录、退出、会话过期和错误映射。
  2. P0 账户首页:/me、/home、Banner、内容、等级和钱包概要。
  3. P0 任务闭环:预检、领取、订单列表、详情、提交、审核状态和账变。
  4. P1 内容服务:手工商城、客服、通知和上传。
  5. P1 资金闭环:收款账户、储入、取出、回调、审核和对账;开始前必须完成支付与法务评审。

19. 联调验收清单

  • 未登录用户只能访问登录、注册和公开协议。
  • 注册成功后自动进入登录态,不重复调用登录。
  • 401 清理会话并返回登录;403 不误判为会话过期。
  • 页签快速切换时旧请求不会覆盖新数据。
  • 重复点击任务领取或提交不生成重复订单、提交或账变。
  • 订单、钱包、账变和统计口径一致。
  • 储入和取出失败、驳回、超时及取消均有原因和恢复路径。
  • 手机号、密码、交易密码、银行卡和 Token 不进入日志或埋点。
  • 富文本、商品名、公告和客服链接无可执行恶意内容。
  • 金额均按最小货币单位传输,无浮点精度差异。
  • 断网、超时、429 和 5xx 均有清晰恢复方式。

20. 待确认事项

  1. 登录注册是否加入图片验证码、短信验证或设备风控挑战。
  2. 是否新增刷新 Token、设备会话列表和“退出所有设备”。
  3. 任务、储入和取出数据库状态值的最终字典与允许转换。
  4. 任务领取是随机匹配、规则匹配还是用户选择,以及截止时间的正式存储位置。
  5. 会员等级版本中储入、取出、任务数和佣金规则的正式 JSON Schema。
  6. 支付通道、收款主体、实名要求、取出审核和适用地区的合规结论。
  7. 客服使用自建系统还是第三方 WebView/iframe,以及允许传递的用户信息。
  8. 通知是否要求逐会员已读记录;如需要,数据库应补充相应关系表。