# 成就徽章体系 - 后端对接文档 > 前端已将成就体系从 5 个扩充到 17 个,本文档供后端对齐成就定义、解锁判定与接口返回格式。 > 前端本地已实现完整兜底计算,后端接口未就绪或返回为空时不影响前端展示。 --- ## 一、成就清单(共 17 个) ### 1. 打卡次数类(total_records) | id | 名称 | 描述 | 解锁条件 | |----|------|------|----------| | first_checkin | 初来乍到 | 完成第一次打卡,开启你的饮酒记录之旅 | 累计打卡 ≥ 1 次 | | records_10 | 小有心得 | 累计打卡10次,记录已成习惯 | 累计打卡 ≥ 10 次 | | records_50 | 记录老手 | 累计打卡50次,每一杯都有迹可循 | 累计打卡 ≥ 50 次 | | records_100 | 酒档宗师 | 累计打卡100次,个人酒档初具规模 | 累计打卡 ≥ 100 次 | ### 2. 连续打卡类(streak_days) | id | 名称 | 描述 | 解锁条件 | |----|------|------|----------| | streak_7 | 七日之约 | 连续打卡7天,一周不落 | 连续打卡 ≥ 7 天 | | streak_30 | 签到王者 | 连续打卡30天,自律的记录者 | 连续打卡 ≥ 30 天 | | streak_100 | 百日坚持 | 连续打卡100天,毅力惊人 | 连续打卡 ≥ 100 天 | ### 3. 标准杯累计类(total_standard_cups) | id | 名称 | 描述 | 解锁条件 | |----|------|------|----------| | cups_10 | 微醺初体验 | 累计饮用10标准杯 | 累计标准杯 ≥ 10 | | cups_50 | 五十杯之路 | 累计饮用50标准杯,微醺是最好的状态 | 累计标准杯 ≥ 50 | | hundred_cups | 百杯不醉 | 累计饮用100标准杯 | 累计标准杯 ≥ 100 | | cups_300 | 海量传说 | 累计饮用300标准杯,记得理性饮酒哦 | 累计标准杯 ≥ 300 | ### 4. 酒类探索类(category_variety) | id | 名称 | 描述 | 解锁条件 | |----|------|------|----------| | variety_3 | 探索起步 | 尝试过3种不同类型的酒 | 酒类品种数 ≥ 3 | | tasting_master | 品鉴师 | 尝试过5种以上不同类型的酒 | 酒类品种数 ≥ 5 | | variety_8 | 酒类百科 | 尝试过8种以上不同类型的酒,尝遍百味 | 酒类品种数 ≥ 8 | ### 5. 单一酒类深耕类(category_records) | id | 名称 | 描述 | 解锁条件 | |----|------|------|----------| | baijiu_master | 白酒达人 | 白酒累计打卡20次 | category=baijiu,打卡 ≥ 20 次 | | beer_master | 啤酒之王 | 啤酒累计打卡20次,冰爽永不停 | category=beer,打卡 ≥ 20 次 | | wine_master | 红酒绅士 | 红酒累计打卡20次,品味微酸的美好 | category=wine,打卡 ≥ 20 次 | --- ## 二、条件类型说明 | type | 说明 | value 含义 | 附加字段 | |------|------|-----------|----------| | total_records | 总打卡次数 | 次数 | 无 | | streak_days | 连续打卡天数 | 天数 | 无 | | total_standard_cups | 累计标准杯 | 杯数 | 无 | | category_variety | 尝试过的酒类品种数 | 品种数 | 无 | | category_records | 某酒类打卡次数 | 次数 | `category`(baijiu / beer / wine,后续可扩展其他酒类) | **判定规则统一为 `>=`**(达到阈值即解锁,解锁后不回退)。 --- ## 三、接口要求 ### 3.1 GET /achievements 响应格式 ```json { "achievements": [ { "id": "first_checkin", "name": "初来乍到", "desc": "完成第一次打卡,开启你的饮酒记录之旅", "unlocked": true, "unlockedAt": "2026-08-10T20:00:00.000Z", "condition": { "type": "total_records", "value": 1 }, "progress": 1.0 } ] } ``` **字段约定:** | 字段 | 类型 | 必填 | 说明 | |------|------|:---:|------| | id | string | 是 | 成就ID,必须与本文档清单一致 | | name / desc | string | 是 | 名称与描述,与清单保持一致 | | unlocked | boolean | 是 | 是否已解锁 | | unlockedAt | string/null | 否 | 解锁时间,ISO 8601 | | condition | object | 是 | 与清单一致;category_records 需带 category | | progress | number | 否 | 进度 0~1(当前值/目标值,封顶 1) | | icon | string | 否 | **后端无需返回有效图标**,前端忽略该字段,统一使用本地预置图标 | ### 3.2 注意事项 1. **必须返回全量 17 条**(含未解锁的),前端按列表顺序渲染成就墙;不要只返回已解锁项 2. **icon 字段可不返回**:后端返回的图片路径前端不会使用,图标由前端本地控制 3. **progress 计算**:`min(当前累计值 / value, 1)`;category_records 按对应酒类的打卡次数计算 4. **解锁检测时机**:建议在创建打卡记录后异步触发(避免阻塞主流程),比对全部成就条件并写入解锁记录 5. **幂等**:同一成就解锁只记录一次,`unlocked_at` 以首次解锁时间为准 6. **id 兼容**:前端取值顺序为 `achievement_id || id`,两者返回其一即可 ### 3.3 存储建议 ```sql CREATE TABLE user_achievements ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(32) NOT NULL, achievement_id VARCHAR(32) NOT NULL, unlocked_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE INDEX idx_user_achievement (user_id, achievement_id) ); ``` --- ## 四、前端兜底策略(供后端了解) - 接口失败或返回空数组时,前端用本地统计(totalRecords / totalCups / streak / categoryVariety)对照上述条件自行计算解锁状态 - 本地计算 `category_records` 类成就依赖 `stats.categoryRecords`(各酒类打卡次数映射),统计接口若能返回该字段可提升兜底准确度 - 因此后端即使暂未实现,前端也不会报错,只是解锁以本地统计为准