604 lines
24 KiB
Markdown
604 lines
24 KiB
Markdown
# 成就和徽章体系架构
|
||
|
||
本文单独定义成就、徽章、长徽章、大徽章、系统赠送徽章和资料页展示的产品/后端边界。等级体系、VIP 体系和资源组体系已有独立文档,本文只说明它们和徽章展示的衔接方式。
|
||
|
||
## 目标
|
||
|
||
- 成就可以由服务端任务完成、事实指标达标或活动条件达成后自动解锁。
|
||
- 成就解锁后可以发放徽章,也可以只记录成就不发徽章。
|
||
- 系统可以直接赠送徽章,不要求生成一个伪造成就。
|
||
- 徽章分为长徽章和大徽章:
|
||
- 长徽章用于身份、等级、VIP、机构、职业、官方认证等横向标识。
|
||
- 大徽章用于成就、活动、荣誉墙等方形或近方形展示。
|
||
- App 可以在资料页顶部展示多个长徽章,在 Honor/荣誉区域展示多个大徽章。
|
||
- 房间资料、麦位、公屏和用户主页都只能读取服务端返回的徽章投影,客户端不能自报徽章。
|
||
- 后台可以配置成就条件、成就奖励徽章、徽章样式、徽章有效期、系统赠送和展示优先级。
|
||
|
||
## 非目标
|
||
|
||
- 不把成就体系和每日任务体系合并。每日任务是短周期任务和领奖;成就是长期或活动荣誉。
|
||
- 不把徽章配置写散到用户资料表。徽章仍然是 `wallet-service` 的资源权益。
|
||
- 不允许客户端提交“我完成了某成就”或“我拥有某徽章”。
|
||
- 不让 `room-service`、`wallet-service`、`user-service` 或 `game-service` 在主链路同步判断成就。
|
||
- 首版不做徽章交易、徽章合成、徽章回收兑换。
|
||
|
||
## 核心概念
|
||
|
||
| 概念 | 含义 | Owner |
|
||
| --- | --- | --- |
|
||
| 成就 `achievement` | 一套服务端条件和解锁事实,例如完成指定任务、送礼达到 10 万、活动内抽奖 10 次 | `activity-service` |
|
||
| 徽章资源 `badge resource` | 用户可以持有、过期、展示或佩戴的资源,`resources.resource_type=badge` | `wallet-service` |
|
||
| 长徽章 `strip badge` | 横向胶囊/铭牌样式,用于等级、VIP、身份、机构、认证等 | `wallet-service` 配置,来源状态由对应服务提供 |
|
||
| 大徽章 `tile badge` | 方形或近方形荣誉图标,用于成就、活动赠送、系统荣誉 | `wallet-service` 配置,成就来源由 `activity-service` 触发 |
|
||
| 徽章持有 `entitlement` | 用户已经获得某个徽章资源,可能永久,也可能限时 | `wallet-service` |
|
||
| 徽章展示 `display slot` | 用户资料页、荣誉墙、房间资料等位置上展示哪些徽章和排序 | `wallet-service` 或展示投影 |
|
||
|
||
关键关系:
|
||
|
||
- 成就解锁不等于徽章持有。成就是荣誉事实,徽章是资源权益。
|
||
- 徽章持有不等于正在展示。用户可以拥有很多大徽章,但资料页只展示前几个。
|
||
- 系统赠送徽章不等于成就。运营活动可以直接发一个大徽章,不必污染成就列表。
|
||
- 长徽章优先表达“当前身份状态”,大徽章优先表达“历史荣誉沉淀”。
|
||
|
||
## 徽章分类
|
||
|
||
### 按获取来源
|
||
|
||
| 来源 | source_type | 示例 | 是否生成成就 |
|
||
| --- | --- | --- | --- |
|
||
| 成就奖励 | `achievement` | 完成送礼达人成就后获得方形徽章 | 是 |
|
||
| 系统赠送 | `system_grant` | 运营活动、节日、补偿、人工授予徽章 | 否 |
|
||
| 等级身份 | `level` | 财富 M16、游戏 53、魅力 18 | 不一定,通常由等级投影计算 |
|
||
| VIP 身份 | `vip` | VIP1/VIP7 长徽章 | 否,由 VIP 状态决定 |
|
||
| 官方身份 | `identity` | 主播、公会、代理、币商、官方认证 | 否,由对应身份系统决定 |
|
||
| 关系身份 | `relationship` | CP、守护、家族关系等 | 可选,通常由关系事实决定 |
|
||
|
||
### 按展示形态
|
||
|
||
| 形态 | badge_form | 展示位置 | 设计规则 |
|
||
| --- | --- | --- | --- |
|
||
| 长徽章 | `strip` | 昵称下方、资料卡顶部、房间小卡、麦位名称旁 | 横向,图标 + 短文本或等级数字,优先级排序,数量受限 |
|
||
| 大徽章 | `tile` | Honor 页、荣誉墙、资料页精选荣誉 | 方形/近方形,强调图形资产,可被用户精选展示 |
|
||
|
||
长徽章不建议放进 Honor 网格里堆叠;大徽章不建议塞到昵称行里,否则会挤压昵称、短号和国家标识。
|
||
|
||
## 总体架构
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Task["activity task completed"] --> AchievementConsumer["activity achievement consumer"]
|
||
WalletFact["wallet/user/room/game facts"] --> AchievementConsumer
|
||
AchievementConsumer --> AchievementDB[("activity achievement_*")]
|
||
AchievementDB --> RewardJob["achievement reward job"]
|
||
RewardJob --> WalletGrant["wallet-service GrantResourceGroup"]
|
||
|
||
Admin["server/admin"] --> SystemGrant["wallet-service GrantResource/Group"]
|
||
WalletGrant --> BadgeEntitlement[("user_resource_entitlements")]
|
||
SystemGrant --> BadgeEntitlement
|
||
|
||
BadgeEntitlement --> BadgeDisplay["badge display slots / projection"]
|
||
Level["activity level read model"] --> BadgeDisplay
|
||
VIP["wallet VIP state"] --> BadgeDisplay
|
||
Identity["user/host/agency identity"] --> BadgeDisplay
|
||
|
||
App["App profile / Honor"] --> Gateway["gateway-service"]
|
||
Gateway --> BadgeDisplay
|
||
```
|
||
|
||
处理原则:
|
||
|
||
- 成就只消费服务端事实事件或服务端任务完成事件。
|
||
- 徽章发放统一走 `wallet-service` 资源或资源组发放能力。
|
||
- 长徽章里的等级/VIP/身份可以是“计算型展示”,不一定要求每次状态变化都发一个新资源。
|
||
- 大徽章必须有明确的徽章资源和持有记录,方便 Honor 页展示、过期过滤和后台审计。
|
||
- 高频房间资料展示需要轻量投影,不能实时扫用户全部资源和全部成就。
|
||
|
||
## 成就设计
|
||
|
||
成就分两类:
|
||
|
||
| 类型 | achievement_type | 示例 | 周期 |
|
||
| --- | --- | --- | --- |
|
||
| 普通成就 | `ordinary` | 完成 10 个每日任务、累计送礼 10 万、累计上麦 100 小时 | `lifetime` |
|
||
| 活动成就 | `activity_limited` | 活动期内完成指定游戏挑战、送指定礼物、登录满 7 天 | `activity:{collection_id}` |
|
||
|
||
成就触发来源:
|
||
|
||
| 触发来源 | metric_type 示例 | 说明 |
|
||
| --- | --- | --- |
|
||
| 每日/专属任务完成 | `task_completed_count`、`specific_task_completed` | task module 写出 `TaskCompleted` 服务端事件,成就系统消费 |
|
||
| 送礼/收礼 | `gift_spend_coin`、`gift_received_value` | 以 wallet 账务事实为准 |
|
||
| 游戏 | `game_spend_coin`、`game_win_coin`、`game_round_count` | 以游戏结算或钱包扣费事实为准 |
|
||
| 关系/社交 | `follow_count`、`followed_count`、`relationship_days` | 以 user-service 关系事实为准 |
|
||
| 活跃 | `active_day_count`、`consecutive_active_days` | 以服务端活跃事实为准 |
|
||
|
||
### 成就解锁规则
|
||
|
||
- 单条件成就:一个指标达到目标值后解锁。
|
||
- 多条件成就:多个条件全部达到后解锁,首版只支持 AND。
|
||
- 活动成就只统计 `occurred_at_ms >= start_ms && occurred_at_ms < end_ms` 的事件。
|
||
- 普通成就 `cycle_key=lifetime`;活动成就 `cycle_key=activity:{collection_id}`。
|
||
- 退款、撤销、解除关系等首版不自动回滚已解锁成就,后续通过风控或后台补偿处理。
|
||
|
||
### 成就奖励徽章
|
||
|
||
成就定义里不直接写图片 URL,只绑定资源组或主徽章资源:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `primary_badge_resource_id` | 该成就主展示徽章,通常是大徽章 |
|
||
| `reward_resource_group_id` | 解锁后发放的资源组,可以包含徽章、头像框、金币等 |
|
||
| `auto_pin_policy` | 是否自动放入资料页精选展示,例如 `none` / `if_empty` / `always` |
|
||
| `honor_visibility` | 是否进入 Honor 列表,默认进入 |
|
||
|
||
如果 `reward_resource_group_id` 内包含大徽章,必须保证对应资源的 `badge_form=tile`。如果成就只用于记录不发徽章,`primary_badge_resource_id=0` 且 `reward_resource_group_id=0`。
|
||
|
||
## 系统赠送徽章
|
||
|
||
系统赠送用于运营或后台直接授予徽章:
|
||
|
||
| 场景 | 推荐方式 |
|
||
| --- | --- |
|
||
| 活动参与奖 | 后台批量赠送资源组,`grant_source=activity_campaign` |
|
||
| 官方认证 | 授予长徽章,`grant_source=identity_verify`,通常限时或直到身份取消 |
|
||
| 补偿/人工荣誉 | 后台赠送大徽章,`grant_source=admin` |
|
||
| 节日限定 | 赠送限时大徽章,资源组 item 配置 `duration_ms` |
|
||
|
||
系统赠送只写资源授予事实,不写 `user_achievement_unlocks`。如果产品希望用户在 Honor 页看到“活动赠送”的记录,可以从 wallet 的 `ResourceGroupGranted/UserResourceChanged` 事件生成 `user_badge_grants` 投影,来源标记为 `system_grant`。
|
||
|
||
## 长徽章设计
|
||
|
||
长徽章用于资料头部的身份行,类似截图中昵称下方的 M16、等级数字、身份标识、VIP 等。
|
||
|
||
### 长徽章来源
|
||
|
||
| 类型 | 示例 | 展示来源 |
|
||
| --- | --- | --- |
|
||
| 等级长徽章 | 财富 M16、游戏 53、魅力 18 | `activity-service` 等级摘要 |
|
||
| VIP 长徽章 | VIP1/VIP7 | `wallet-service` VIP 当前状态 |
|
||
| 身份长徽章 | 主播、代理、币商、官方、家族 | 对应身份系统或后台授予 |
|
||
| 关系长徽章 | CP 天数、守护关系 | user-service 关系事实 |
|
||
|
||
长徽章有两种实现方式:
|
||
|
||
| 方式 | 使用场景 | 说明 |
|
||
| --- | --- | --- |
|
||
| 计算型长徽章 | 等级、VIP 这类随状态变化的标识 | 不需要每级发一个资源,查询时根据状态和配置生成展示项 |
|
||
| 资源型长徽章 | 官方认证、代理、币商等运营授予标识 | 通过 badge 资源发放,有有效期和审计 |
|
||
|
||
等级和 VIP 不建议每次升级都发一个新长徽章资源,否则会产生大量过期或废弃权益。更稳妥的做法是保存等级/VIP 状态,按 `badge_display_specs` 的模板渲染,例如 `M{level}`、`VIP{level}`。
|
||
|
||
### 展示规则
|
||
|
||
- 长徽章展示槽位建议为 `profile_strip`。
|
||
- 默认最多展示 6 个,超过数量按 `priority` 和业务权威排序截断。
|
||
- 推荐排序:官方/身份认证 > VIP > 等级 > 关系 > 活动临时身份。
|
||
- 长徽章文字必须短,避免昵称区域换行挤压。
|
||
- 可过期长徽章在 `expires_at_ms <= now` 后自动从展示投影移除。
|
||
- 用户不应能手动伪造等级/VIP/认证长徽章,只能在允许的资源型长徽章中选择是否展示。
|
||
|
||
## 大徽章设计
|
||
|
||
大徽章用于 Honor 页和资料页精选荣誉,类似截图中一排方形金色徽章。
|
||
|
||
### 大徽章来源
|
||
|
||
| 类型 | 示例 | 获取 |
|
||
| --- | --- | --- |
|
||
| 成就大徽章 | 送礼达人、麦上之星、连续活跃 30 天 | 成就解锁后发放 |
|
||
| 活动大徽章 | 生日活动、节日活动、游戏赛季 | 活动成就或系统赠送 |
|
||
| 官方大徽章 | 官方精选、赛事冠军、年度荣誉 | 系统赠送 |
|
||
|
||
### 展示规则
|
||
|
||
- 大徽章展示槽位建议为 `profile_tile` 和 `honor_wall`。
|
||
- Honor 页展示用户拥有的全部有效大徽章,按获得时间倒序或后台排序展示。
|
||
- 资料页顶部精选只展示 3 到 6 个,用户可以手动置顶;未设置时按稀有度、优先级、获得时间自动选择。
|
||
- 已过期的大徽章默认不展示;如果产品要展示历史已过期活动徽章,需要给资源配置 `historical_visible=true`,但不能再作为有效权益使用。
|
||
- 大徽章点击后可以进入详情页,展示来源、获得时间、有效期、对应成就进度或系统赠送原因。
|
||
|
||
## 数据模型
|
||
|
||
### 钱包资源侧
|
||
|
||
徽章本体继续复用 `resources`:
|
||
|
||
```sql
|
||
resources(
|
||
app_code, resource_id, resource_code, resource_type,
|
||
name, status, grantable, grant_strategy,
|
||
asset_url, preview_url, animation_url, metadata_json,
|
||
created_at_ms, updated_at_ms
|
||
);
|
||
```
|
||
|
||
约束:
|
||
|
||
- 徽章资源必须 `resource_type=badge`。
|
||
- 大徽章推荐 `grant_strategy=set_active_flag`,永久持有。
|
||
- 限时活动徽章可以使用 `expires_at_ms` 控制有效期。
|
||
- 徽章图片、动画和客户端样式从资源库读取,不允许成就配置直接填散落 URL。
|
||
|
||
建议新增徽章展示规格表:
|
||
|
||
```sql
|
||
badge_display_specs(
|
||
app_code VARCHAR(32) NOT NULL,
|
||
resource_id BIGINT NOT NULL,
|
||
badge_form VARCHAR(32) NOT NULL, -- strip / tile
|
||
badge_kind VARCHAR(32) NOT NULL, -- level / vip / achievement / system / identity
|
||
default_slot VARCHAR(32) NOT NULL, -- profile_strip / profile_tile / honor_wall
|
||
display_name VARCHAR(128) NOT NULL,
|
||
priority INT NOT NULL DEFAULT 0,
|
||
rarity VARCHAR(32) NOT NULL DEFAULT '',
|
||
text_template VARCHAR(128) NOT NULL DEFAULT '',
|
||
display_config_json JSON NULL,
|
||
status VARCHAR(32) NOT NULL,
|
||
created_at_ms BIGINT NOT NULL,
|
||
updated_at_ms BIGINT NOT NULL,
|
||
PRIMARY KEY(app_code, resource_id),
|
||
KEY idx_badge_specs_form(app_code, badge_form, status, priority)
|
||
);
|
||
```
|
||
|
||
如果首版不想加表,可以先把这些字段放进 `resources.metadata_json`;但后台要按长徽章/大徽章筛选、排序和校验时,独立表更清晰。
|
||
|
||
### 成就侧
|
||
|
||
```sql
|
||
achievement_collections(
|
||
app_code, collection_id, collection_type,
|
||
title, status, effective_from_ms, effective_to_ms,
|
||
sort_order, display_config_json,
|
||
created_at_ms, updated_at_ms
|
||
);
|
||
|
||
achievement_definitions(
|
||
app_code, achievement_id, collection_id,
|
||
achievement_type, title, description,
|
||
status, version,
|
||
primary_badge_resource_id,
|
||
reward_resource_group_id,
|
||
auto_pin_policy,
|
||
honor_visibility,
|
||
effective_from_ms, effective_to_ms,
|
||
sort_order,
|
||
created_by_admin_id, updated_by_admin_id,
|
||
created_at_ms, updated_at_ms
|
||
);
|
||
|
||
achievement_conditions(
|
||
app_code, condition_id, achievement_id, version,
|
||
metric_type, target_value, target_unit,
|
||
dimension_filter_json,
|
||
created_at_ms
|
||
);
|
||
|
||
user_achievement_progress(
|
||
app_code, user_id, achievement_id, condition_id, cycle_key,
|
||
progress_value, target_value, status,
|
||
updated_at_ms,
|
||
PRIMARY KEY(app_code, user_id, achievement_id, condition_id, cycle_key)
|
||
);
|
||
|
||
user_achievement_unlocks(
|
||
app_code, user_id, achievement_id, cycle_key,
|
||
version, status, unlocked_at_ms,
|
||
reward_job_id,
|
||
created_at_ms, updated_at_ms,
|
||
PRIMARY KEY(app_code, user_id, achievement_id, cycle_key)
|
||
);
|
||
```
|
||
|
||
### 徽章展示槽位
|
||
|
||
现有 `user_resource_equipment` 对 `resource_type=badge` 只能保存一个装备项,无法满足多个长徽章和多个大徽章同时展示。建议不要直接复用它做徽章墙,而是新增多槽位表:
|
||
|
||
```sql
|
||
user_badge_display_slots(
|
||
app_code VARCHAR(32) NOT NULL,
|
||
user_id BIGINT NOT NULL,
|
||
slot VARCHAR(32) NOT NULL, -- profile_strip / profile_tile / honor_wall / room_card
|
||
position INT NOT NULL,
|
||
badge_form VARCHAR(32) NOT NULL, -- strip / tile
|
||
resource_id BIGINT NOT NULL,
|
||
entitlement_id VARCHAR(96) NOT NULL DEFAULT '',
|
||
source_type VARCHAR(32) NOT NULL, -- achievement / system_grant / level / vip / identity
|
||
source_id VARCHAR(128) NOT NULL DEFAULT '',
|
||
pin_mode VARCHAR(32) NOT NULL DEFAULT '', -- auto / user / system
|
||
updated_at_ms BIGINT NOT NULL,
|
||
PRIMARY KEY(app_code, user_id, slot, position),
|
||
KEY idx_badge_display_user(app_code, user_id, slot, updated_at_ms),
|
||
KEY idx_badge_display_resource(app_code, resource_id)
|
||
);
|
||
```
|
||
|
||
校验:
|
||
|
||
- `profile_strip` 只允许 `badge_form=strip`。
|
||
- `profile_tile` 和 `honor_wall` 只允许 `badge_form=tile`。
|
||
- 资源型徽章必须校验用户持有有效 `user_resource_entitlements`。
|
||
- 计算型长徽章可以 `entitlement_id=''`,但必须能从等级/VIP/身份状态验证。
|
||
- 同一槽位同一 `resource_id` 不应重复展示。
|
||
|
||
### 资料展示投影
|
||
|
||
高频资料展示建议维护轻量投影:
|
||
|
||
```sql
|
||
user_badge_display_profiles(
|
||
app_code VARCHAR(32) NOT NULL,
|
||
user_id BIGINT NOT NULL,
|
||
strip_badges_json JSON NOT NULL,
|
||
tile_badges_json JSON NOT NULL,
|
||
honor_summary_json JSON NOT NULL,
|
||
updated_at_ms BIGINT NOT NULL,
|
||
PRIMARY KEY(app_code, user_id)
|
||
);
|
||
```
|
||
|
||
投影内容只放展示所需字段,例如 `resource_id/name/badge_form/source_type/asset_url/text/level/expires_at_ms/priority`。完整详情仍回源到成就和资源接口。
|
||
|
||
## 发放流程
|
||
|
||
### 成就获得大徽章
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant Source as Task/Wallet/User/Game Fact
|
||
participant Act as activity-service
|
||
participant DB as activity MySQL
|
||
participant Wallet as wallet-service
|
||
participant App as App
|
||
|
||
Source->>Act: TaskCompleted or metric event
|
||
Act->>DB: update achievement progress idempotently
|
||
Act->>DB: insert user_achievement_unlocks
|
||
Act->>DB: insert reward job
|
||
Act->>Wallet: GrantResourceGroup(command_id)
|
||
Wallet->>Wallet: create badge entitlement
|
||
Wallet-->>Act: grant_id
|
||
Act->>DB: mark reward granted
|
||
App->>Act: query achievements / badges
|
||
```
|
||
|
||
幂等键建议:
|
||
|
||
- 成就消费:`source_event_id:achievement:{achievement_id}:{user_id}:{cycle_key}`
|
||
- 奖励发放:`achievement_reward:{user_id}:{achievement_id}:{cycle_key}`
|
||
- 钱包命令:`wallet_grant:{reward_job_id}`
|
||
|
||
### 系统直接赠送徽章
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant Admin as server/admin
|
||
participant Wallet as wallet-service
|
||
participant Projection as badge projection
|
||
|
||
Admin->>Wallet: GrantResource/GrantResourceGroup(command_id, badge resource)
|
||
Wallet->>Wallet: write resource_grants and user_resource_entitlements
|
||
Wallet->>Projection: UserResourceChanged outbox
|
||
Projection->>Projection: update user_badge_display_profiles
|
||
```
|
||
|
||
系统赠送必须保留 `grant_source`、操作人、原因、目标用户、有效期和资源快照,方便客服和审计。
|
||
|
||
### 长徽章刷新
|
||
|
||
长徽章不一定经过资源发放:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
LevelChanged["GrowthLevelChanged"] --> Projection["badge display projection"]
|
||
VipChanged["VipMembershipChanged"] --> Projection
|
||
IdentityChanged["Host/Agency/Verify Changed"] --> Projection
|
||
Projection --> Profile["profile strip badges"]
|
||
```
|
||
|
||
例如用户财富等级从 M15 升到 M16,只需要等级投影变化后刷新 `strip_badges_json` 中的文本和样式,不需要给用户发一个新的 M16 徽章资源。
|
||
|
||
## App 接口
|
||
|
||
### 查询我的成就
|
||
|
||
```http
|
||
GET /api/v1/achievements?collection_type=ordinary
|
||
GET /api/v1/achievements?collection_id=act_202605
|
||
```
|
||
|
||
每个成就返回:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `achievement_id` | 成就 ID |
|
||
| `title` / `description` | 展示文案 |
|
||
| `status` | `locked` / `in_progress` / `unlocked` / `expired` |
|
||
| `progress_value` / `target_value` | 当前进度和目标 |
|
||
| `unlocked_at_ms` | 解锁时间 |
|
||
| `primary_badge` | 主大徽章资源摘要 |
|
||
| `reward_status` | `none` / `pending` / `granted` / `failed` |
|
||
| `effective_to_ms` | 活动成就结束时间 |
|
||
|
||
### 查询我的徽章
|
||
|
||
```http
|
||
GET /api/v1/badges/me
|
||
```
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"strip_badges": [],
|
||
"profile_tile_badges": [],
|
||
"honor_badges": [],
|
||
"server_time_ms": 1777996800000
|
||
}
|
||
```
|
||
|
||
`strip_badges` 是资料顶部长徽章;`profile_tile_badges` 是资料页精选大徽章;`honor_badges` 是 Honor 页全部大徽章。
|
||
|
||
### 设置资料页精选大徽章
|
||
|
||
```http
|
||
POST /api/v1/badges/display
|
||
```
|
||
|
||
请求:
|
||
|
||
```json
|
||
{
|
||
"slot": "profile_tile",
|
||
"items": [
|
||
{ "resource_id": 101, "entitlement_id": "ent_1", "position": 1 },
|
||
{ "resource_id": 102, "entitlement_id": "ent_2", "position": 2 }
|
||
],
|
||
"command_id": "cmd_badge_display_550e8400"
|
||
}
|
||
```
|
||
|
||
规则:
|
||
|
||
- 只能设置当前用户自己有效持有的大徽章。
|
||
- `profile_tile` 最多 6 个,具体数量由 App 设计配置决定。
|
||
- 长徽章中的等级、VIP、认证类标识不允许用户伪造或任意排序;只允许隐藏非强制展示的资源型长徽章。
|
||
- 重复 `command_id` + 相同 payload 返回同一结果;不同 payload 返回幂等冲突。
|
||
|
||
### 查询他人荣誉
|
||
|
||
```http
|
||
GET /api/v1/users/{user_id}/honors
|
||
```
|
||
|
||
返回用户公开可见的大徽章、成就摘要和精选展示。隐私或风控隐藏的徽章不返回。
|
||
|
||
### 房间资料展示
|
||
|
||
现有接口可继续承载:
|
||
|
||
```http
|
||
GET /api/v1/users/room-display-profiles:batch?user_ids=10001,10002
|
||
```
|
||
|
||
`badges` 字段建议返回已裁剪的展示数组,不返回用户全部徽章:
|
||
|
||
```json
|
||
{
|
||
"badges": [
|
||
{ "badge_form": "strip", "source_type": "vip", "text": "VIP7" },
|
||
{ "badge_form": "strip", "source_type": "level", "text": "M16" },
|
||
{ "badge_form": "tile", "source_type": "achievement", "resource_id": 101 }
|
||
]
|
||
}
|
||
```
|
||
|
||
## 后台管理
|
||
|
||
后台建议放在活动管理和资源管理两个区域:
|
||
|
||
```text
|
||
活动管理
|
||
- 成就配置
|
||
- 活动成就
|
||
|
||
资源管理
|
||
- 徽章资源
|
||
- 徽章赠送
|
||
- 徽章展示配置
|
||
```
|
||
|
||
### 成就配置
|
||
|
||
字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| 成就名称、描述 | App 展示 |
|
||
| 成就类型 | 普通 / 活动限时 |
|
||
| 条件类型 | 指定任务完成 / 指标累计 / 多条件 AND |
|
||
| 指标和目标值 | 例如 `task_completed_count >= 10` |
|
||
| 活动窗口 | 活动成就必填 |
|
||
| 主徽章资源 | 一般选择大徽章 |
|
||
| 奖励资源组 | 可选,解锁后发放 |
|
||
| 排序和状态 | draft / active / paused / archived |
|
||
|
||
### 徽章资源配置
|
||
|
||
字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| 徽章形态 | 长徽章 `strip` / 大徽章 `tile` |
|
||
| 徽章来源提示 | achievement / system / level / vip / identity |
|
||
| 资源素材 | icon、preview、animation |
|
||
| 文本模板 | 长徽章可用,例如 `M{level}`、`VIP{level}` |
|
||
| 默认槽位 | profile_strip / profile_tile / honor_wall |
|
||
| 展示优先级 | 数值越高越靠前 |
|
||
| 稀有度 | common / rare / epic / legendary 等展示字段 |
|
||
| 有效期策略 | 永久、固定天数、跟随 VIP/身份有效期 |
|
||
|
||
### 系统赠送
|
||
|
||
字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| 目标用户 | 单个用户或批量导入 |
|
||
| 徽章或资源组 | 从资源库选择 |
|
||
| 有效期 | 永久或指定天数 |
|
||
| 赠送原因 | 必填,进入审计 |
|
||
| grant_source | admin / activity_campaign / identity_verify |
|
||
| command_id | 前端按提交动作生成,重试复用 |
|
||
|
||
## 展示建议
|
||
|
||
资料页顶部可以按这个结构展示:
|
||
|
||
1. 头像、昵称、国家和基础身份。
|
||
2. 长徽章行:VIP、等级、身份、机构等,最多 6 个。
|
||
3. 精选大徽章行:用户置顶或系统自动挑选 3 到 6 个。
|
||
4. 数据行:访客、关注、粉丝等。
|
||
5. Tab:Honor / Relationship / Moment。
|
||
6. Honor 页:按分类展示全部大徽章和成就详情。
|
||
|
||
展示策略:
|
||
|
||
- 长徽章越靠近昵称,越应该是强身份和实时状态。
|
||
- 大徽章越靠近 Honor,越应该是历史荣誉和活动纪念。
|
||
- 资料页首屏只展示精选,不展示全部,避免像资源背包一样拥挤。
|
||
- Honor 页要能区分“成就获得”和“系统赠送”,否则用户不知道徽章来源。
|
||
|
||
## 异常与补偿
|
||
|
||
- 重复事件:成就进度和解锁必须用派生事件 ID 幂等。
|
||
- 钱包发放失败:成就仍保持已解锁,奖励任务重试,后台可手动补发。
|
||
- 徽章过期:展示投影按有效期过滤,不删除历史授予记录。
|
||
- 系统赠送撤销:首版建议新增后台禁用/撤销权益命令,不直接删表。
|
||
- 成就配置修改:active 成就修改条件必须生成新版本;历史是否重算走后台 rebuild job。
|
||
- 展示投影延迟:房间资料允许短暂延迟,但不能返回客户端自报徽章。
|
||
- 多 App 隔离:所有成就、资源、展示槽位必须带 `app_code`。
|
||
|
||
## 落地顺序
|
||
|
||
1. 在资源后台为 badge 增加 `badge_form`、`badge_kind`、默认槽位和优先级配置。
|
||
2. 在 `activity-service` 增加 achievement domain:定义、条件、进度、解锁、奖励任务。
|
||
3. 扩展 task module 事件,完成任务后写出 `TaskCompleted` 服务端事实,供成就消费。
|
||
4. 用 `wallet-service.GrantResourceGroup` 发放成就徽章和系统徽章。
|
||
5. 新增 `user_badge_display_slots`,支持多个长徽章和多个大徽章展示。
|
||
6. 新增 App 接口:成就列表、我的徽章、设置精选大徽章、他人荣誉。
|
||
7. 接入 `room-display-profiles:batch`,返回裁剪后的长徽章和精选大徽章。
|
||
8. 补后台:成就配置、徽章资源配置、系统赠送、授予记录查询。
|
||
|
||
## 验收点
|
||
|
||
- 用户完成指定任务后,成就只解锁一次,徽章奖励只发放一次。
|
||
- 系统直接赠送大徽章后,用户 Honor 页能看到该徽章,但成就列表不会多出伪造成就。
|
||
- 同一用户可以同时展示多个长徽章和多个大徽章。
|
||
- 等级/VIP 变化后,长徽章展示自动变化,不产生无意义的旧等级徽章堆积。
|
||
- 用户不能把未持有、已过期或形态不匹配的徽章设置到资料页。
|
||
- 资料页和房间资料只返回服务端投影,客户端不能自报徽章。
|
||
- 徽章资源、成就定义、系统赠送和展示设置都按 `app_code` 隔离。
|