44 KiB
移动端任务与账户平台前后端对接文档
版本:V1.0
更新日期:2026-10-09
适用项目:olons_server数据源:MySQLolons_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,不依赖额外幂等记录表。
提交事务执行以下操作:
- 锁定会员,确认账户有效、交易密码未变化;
- 校验收款账户属于当前会员且已核验;
- 锁定并重新校验支付通道规则;
- 锁定 CNY 钱包,检查钱包状态、可用余额及会员等级每日取出次数;
- 创建
pending_review取出订单和加密收款账户快照; - 可用余额减少申请金额,冻结余额增加申请金额;
- 分别写入可用余额支出和冻结余额收入两条不可变流水。
订单、钱包和两条流水在同一数据库事务内提交,任一步失败全部回滚。提交阶段不增加 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. 联调顺序
- P0 认证:注册、登录、退出、会话过期和错误映射。
- P0 账户首页:
/me、/home、Banner、内容、等级和钱包概要。 - P0 任务闭环:预检、领取、订单列表、详情、提交、审核状态和账变。
- P1 内容服务:手工商城、客服、通知和上传。
- P1 资金闭环:收款账户、储入、取出、回调、审核和对账;开始前必须完成支付与法务评审。
19. 联调验收清单
- 未登录用户只能访问登录、注册和公开协议。
- 注册成功后自动进入登录态,不重复调用登录。
- 401 清理会话并返回登录;403 不误判为会话过期。
- 页签快速切换时旧请求不会覆盖新数据。
- 重复点击任务领取或提交不生成重复订单、提交或账变。
- 订单、钱包、账变和统计口径一致。
- 储入和取出失败、驳回、超时及取消均有原因和恢复路径。
- 手机号、密码、交易密码、银行卡和 Token 不进入日志或埋点。
- 富文本、商品名、公告和客服链接无可执行恶意内容。
- 金额均按最小货币单位传输,无浮点精度差异。
- 断网、超时、429 和 5xx 均有清晰恢复方式。
20. 待确认事项
- 登录注册是否加入图片验证码、短信验证或设备风控挑战。
- 是否新增刷新 Token、设备会话列表和“退出所有设备”。
- 任务、储入和取出数据库状态值的最终字典与允许转换。
- 任务领取是随机匹配、规则匹配还是用户选择,以及截止时间的正式存储位置。
- 会员等级版本中储入、取出、任务数和佣金规则的正式 JSON Schema。
- 支付通道、收款主体、实名要求、取出审核和适用地区的合规结论。
- 客服使用自建系统还是第三方 WebView/iframe,以及允许传递的用户信息。
- 通知是否要求逐会员已读记录;如需要,数据库应补充相应关系表。