hyapp-server/docs/flutter对接/成就徽章Flutter对接.md
2026-05-14 17:44:16 +08:00

358 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 成就徽章 Flutter 对接
## 地址
Base URL
```text
https://<gateway-host>
```
本地开发:
```text
http://127.0.0.1:13000
```
| 功能 | Method | Path |
| --- | --- | --- |
| 我的成就列表 | `GET` | `/api/v1/achievements` |
| 我的徽章展示 | `GET` | `/api/v1/badges/me` |
| 设置方形徽章展示槽 | `POST` | `/api/v1/badges/display` |
## 通用请求
Headers
| Header | 必填 | 说明 |
| --- | --- | --- |
| `Authorization` | 是 | `Bearer <access_token>` |
| `Content-Type` | POST 必填 | `application/json` |
| `X-App-Code` | 否 | App 编码,默认 `lalu` |
| `X-App-Package` | 否 | 包名解析 App 时使用 |
| `X-App-Platform` | 否 | `android` / `ios` |
统一返回:
```json
{
"code": "OK",
"message": "ok",
"request_id": "req_xxx",
"data": {}
}
```
失败返回:
```json
{
"code": "INVALID_ARGUMENT",
"message": "invalid argument",
"request_id": "req_xxx"
}
```
## 请求参数
### `GET /api/v1/achievements`
Query
| 字段 | 必填 | 默认 | 说明 |
| --- | --- | --- | --- |
| `collection_type` | 否 | 全部 | 普通成就传 `ordinary` |
| `collection_id` | 否 | 全部 | 成就集合 ID |
| `page` | 否 | `1` | 页码,从 1 开始 |
| `page_size` | 否 | `20` | 每页数量 |
### `GET /api/v1/badges/me`
无 query。
### `POST /api/v1/badges/display`
Body
```json
{
"slot": "profile_tile",
"command_id": "cmd_badge_display_1710000000000",
"items": [
{
"position": 1,
"resource_id": 8001,
"entitlement_id": "ent_xxx"
}
]
}
```
| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `slot` | 是 | 当前只允许 `profile_tile` |
| `command_id` | 是 | 客户端本次操作幂等 ID同一次重试使用同一个值 |
| `items` | 是 | 展示槽完整替换;最多 6 个 |
| `items[].position` | 否 | 从 1 开始;不传按数组顺序生成 |
| `items[].resource_id` | 是 | 必须来自用户已获得的可展示徽章 |
| `items[].entitlement_id` | 否 | 有则透传 |
## 返回值
以下 JSON 均为统一 envelope 的 `data`
### 我的成就列表
```json
{
"achievements": [
{
"definition": {
"achievement_id": "ach_first_gift",
"collection_id": "ordinary",
"collection_type": "ordinary",
"achievement_type": "ordinary",
"title": "First Gift",
"description": "",
"status": "active",
"version": 1,
"primary_badge_resource_id": 8001,
"primary_badge_resource": {
"resource_id": 8001,
"resource_code": "badge_first_gift",
"resource_type": "badge",
"name": "First Gift",
"status": "active",
"grantable": true,
"grant_strategy": "new_entitlement",
"usage_scopes": ["badge"],
"asset_url": "https://cdn.example/badge.png",
"preview_url": "",
"animation_url": "",
"metadata_json": "{\"badge_form\":\"tile\"}",
"sort_order": 10,
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"reward_resource_group_id": 9001,
"reward_resource_group": {
"group_id": 9001,
"group_code": "ach_first_gift_reward",
"name": "First Gift Reward",
"status": "active",
"description": "",
"sort_order": 10,
"items": [
{
"group_item_id": 1,
"item_type": "resource",
"resource_id": 8001,
"resource": {
"resource_id": 8001,
"resource_code": "badge_first_gift",
"resource_type": "badge",
"name": "First Gift",
"status": "active",
"grantable": true,
"grant_strategy": "new_entitlement",
"usage_scopes": ["badge"],
"asset_url": "https://cdn.example/badge.png",
"preview_url": "",
"animation_url": "",
"metadata_json": "{\"badge_form\":\"tile\"}",
"sort_order": 10,
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"quantity": 1,
"duration_ms": 0,
"sort_order": 10
}
],
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"auto_pin_policy": "if_empty",
"honor_visibility": true,
"effective_from_ms": 0,
"effective_to_ms": 0,
"sort_order": 10,
"display_config_json": "{}",
"conditions": [
{
"condition_id": "cond_xxx",
"achievement_id": "ach_first_gift",
"version": 1,
"metric_type": "send_gift_count",
"target_value": 1,
"target_unit": "count",
"dimension_filter_json": "{}"
}
],
"created_by_admin_id": 1,
"updated_by_admin_id": 1,
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"cycle_key": "lifetime",
"progress_value": 1,
"target_value": 1,
"user_status": "unlocked",
"unlocked_at_ms": 1710000000000,
"reward_status": "granted"
}
],
"total": 1,
"page": 1,
"page_size": 20,
"server_time_ms": 1710000000000
}
```
字段值:
| 字段 | 值 |
| --- | --- |
| `achievement_type` | `ordinary` / `activity_limited` |
| `collection_type` | `ordinary` |
| `user_status` | 空字符串表示未完成;`unlocked` 表示已达成 |
| `reward_status` | 空字符串 / `pending` / `running` / `granted` / `failed` |
| `auto_pin_policy` | `none` / `if_empty` / `always` |
| `primary_badge_resource` | 成就主徽章素材,可直接渲染 |
| `reward_resource_group.items[].resource` | 成就奖励资源素材,可直接渲染 |
### 我的徽章展示
```json
{
"strip_badges": [
{
"slot": "profile_strip",
"position": 1,
"badge_form": "strip",
"resource_id": 8101,
"resource": {
"resource_id": 8101,
"resource_code": "vip_gold_strip",
"resource_type": "badge",
"name": "VIP Gold",
"status": "active",
"grantable": true,
"grant_strategy": "new_entitlement",
"usage_scopes": ["badge"],
"asset_url": "https://cdn.example/vip-strip.png",
"preview_url": "",
"animation_url": "",
"metadata_json": "{\"badge_form\":\"strip\"}",
"sort_order": 10,
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"entitlement_id": "ent_strip_1",
"source_type": "vip",
"source_id": "vip_gold",
"pin_mode": "system",
"updated_at_ms": 1710000000000
}
],
"profile_tile_badges": [
{
"slot": "profile_tile",
"position": 1,
"badge_form": "tile",
"resource_id": 8001,
"resource": {
"resource_id": 8001,
"resource_code": "badge_first_gift",
"resource_type": "badge",
"name": "First Gift",
"status": "active",
"grantable": true,
"grant_strategy": "new_entitlement",
"usage_scopes": ["badge"],
"asset_url": "https://cdn.example/badge.png",
"preview_url": "",
"animation_url": "",
"metadata_json": "{\"badge_form\":\"tile\"}",
"sort_order": 10,
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"entitlement_id": "ent_tile_1",
"source_type": "achievement",
"source_id": "ach_first_gift",
"pin_mode": "user",
"updated_at_ms": 1710000000000
}
],
"honor_badges": [
{
"slot": "honor_wall",
"position": 1,
"badge_form": "tile",
"resource_id": 8001,
"resource": {
"resource_id": 8001,
"resource_code": "badge_first_gift",
"resource_type": "badge",
"name": "First Gift",
"status": "active",
"grantable": true,
"grant_strategy": "new_entitlement",
"usage_scopes": ["badge"],
"asset_url": "https://cdn.example/badge.png",
"preview_url": "",
"animation_url": "",
"metadata_json": "{\"badge_form\":\"tile\"}",
"sort_order": 10,
"created_at_ms": 1710000000000,
"updated_at_ms": 1710000000000
},
"entitlement_id": "ent_tile_1",
"source_type": "achievement",
"source_id": "ach_first_gift",
"pin_mode": "auto",
"updated_at_ms": 1710000000000
}
],
"server_time_ms": 1710000000000
}
```
字段值:
| 字段 | 说明 |
| --- | --- |
| `strip_badges` | 长徽章身份、等级、VIP 等系统展示 |
| `profile_tile_badges` | 用户主页方形展示槽;用户可通过 `POST /badges/display` 调整 |
| `honor_badges` | 荣誉墙方形徽章;成就或系统赠送 |
| `badge_form` | `strip` 长徽章 / `tile` 方形徽章 |
| `slot` | `profile_strip` / `profile_tile` / `honor_wall` |
| `source_type` | `achievement` / `system_grant` / `level` / `vip` / `identity` |
| `resource` | 徽章素材,可直接渲染 |
## 错误处理
| HTTP | code | 处理 |
| --- | --- | --- |
| `200` | `OK` | 读取 `data` |
| `400` | `INVALID_JSON` | 请求体不是合法 JSON |
| `400` | `INVALID_ARGUMENT` | 参数错误;不自动重试 |
| `401` | `UNAUTHORIZED` | 刷新 token失败则重新登录 |
| `403` | `PROFILE_REQUIRED` | 跳转资料补全 |
| `403` | `PERMISSION_DENIED` | 停止当前操作 |
| `404` | `NOT_FOUND` | 资源不存在或徽章不可展示 |
| `429` | `RATE_LIMITED` | 延迟重试 |
| `500` | `INTERNAL_ERROR` | 提示重试,记录 `request_id` |
| `502` | `UPSTREAM_ERROR` | 提示重试,记录 `request_id` |
`POST /api/v1/badges/display`
| 场景 | 处理 |
| --- | --- |
| 网络超时 | 使用同一个 `command_id` 重试 |
| 用户重新调整排序 | 生成新的 `command_id` |
| `NOT_FOUND` | 当前用户未获得该 `resource_id` 对应徽章,刷新 `/api/v1/badges/me` |
| `INVALID_ARGUMENT` | 检查 `slot``items.length <= 6``resource_id > 0`、重复 `resource_id` |