# 成就和徽章体系架构 本文单独定义成就、徽章、长徽章、大徽章、系统赠送徽章和资料页展示的产品/后端边界。等级体系、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` 隔离。