From 18e7b4fd5035144198a9126fb11558039745312e Mon Sep 17 00:00:00 2001 From: cheng <545895878@qq.com> Date: Mon, 17 Aug 2026 23:07:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20=E6=B7=BB=E5=8A=A0=E6=88=90?= =?UTF-8?q?=E5=B0=B1=E7=B3=BB=E7=BB=9F=E5=B9=B6=E5=8D=87=E7=BA=A7SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 升级 HaveADrink SDK 从 1.0.16 到 1.0.17 版本 - 新增 GetAchievements API 接口及相关数据类型定义 - 添加 17 个成就定义,包括打卡次数、连续打卡、标准杯累计、酒类探索等类别 - 实现成就解锁逻辑计算,支持 category_records 类型成就判断 - 创建成就徽章对接文档,定义接口格式和存储建议 - 在用户资料页面集成成就展示功能,优化图标匹配逻辑 --- common/api.js | 13 +- common/constants.js | 109 ++++++++++++++-- common/utils.js | 5 +- docs/成就徽章对接文档.md | 127 +++++++++++++++++++ node_modules/.package-lock.json | 6 +- node_modules/HaveADrink/README.md | 50 +++++++- node_modules/HaveADrink/index.d.ts | 178 +++++++++++++++++++++++++++ node_modules/HaveADrink/index.js | 158 ++++++++++++++++++++++++ node_modules/HaveADrink/package.json | 2 +- package-lock.json | 8 +- package.json | 2 +- pages/profile/profile.vue | 15 ++- 12 files changed, 644 insertions(+), 29 deletions(-) create mode 100644 docs/成就徽章对接文档.md diff --git a/common/api.js b/common/api.js index 6e783b4..ea31dc6 100644 --- a/common/api.js +++ b/common/api.js @@ -1,7 +1,7 @@ /* 碰盏日记 - API 服务层 * 基于 HaveADrink SDK 封装后端接口 */ -import HaveADrink from 'HaveADrink' +import HaveADrink, { GetAchievementsReq } from 'HaveADrink' import UniAppWebSocket from './uni-websocket' // ========================================== @@ -343,6 +343,17 @@ export async function GetUnreadCount() { return { data: res } } +// ========================================== +// 成就 API +// ========================================== + +/** 获取成就列表(使用 SDK GetAchievementsReq 请求实例) */ +export async function GetAchievements() { + const req = new GetAchievementsReq() + const res = await client.GetAchievements(req) + return { data: res } +} + // ========================================== // 文件上传 API // ========================================== diff --git a/common/constants.js b/common/constants.js index f3852ac..21b9e15 100644 --- a/common/constants.js +++ b/common/constants.js @@ -179,6 +179,7 @@ export const DRINK_QUOTES = [ // 成就定义 export const ACHIEVEMENTS = [ + // 打卡次数类 { id: 'first_checkin', name: '初来乍到', @@ -187,11 +188,33 @@ export const ACHIEVEMENTS = [ condition: { type: 'total_records', value: 1 } }, { - id: 'hundred_cups', - name: '百杯不醉', - desc: '累计饮用100标准杯', - icon: '🏆', - condition: { type: 'total_standard_cups', value: 100 } + id: 'records_10', + name: '小有心得', + desc: '累计打卡10次,记录已成习惯', + icon: '📖', + condition: { type: 'total_records', value: 10 } + }, + { + id: 'records_50', + name: '记录老手', + desc: '累计打卡50次,每一杯都有迹可循', + icon: '✒️', + condition: { type: 'total_records', value: 50 } + }, + { + id: 'records_100', + name: '酒档宗师', + desc: '累计打卡100次,个人酒档初具规模', + icon: '🏵️', + condition: { type: 'total_records', value: 100 } + }, + // 连续打卡类 + { + id: 'streak_7', + name: '七日之约', + desc: '连续打卡7天,一周不落', + icon: '📅', + condition: { type: 'streak_days', value: 7 } }, { id: 'streak_30', @@ -201,11 +224,48 @@ export const ACHIEVEMENTS = [ condition: { type: 'streak_days', value: 30 } }, { - id: 'baijiu_master', - name: '白酒达人', - desc: '白酒累计打卡20次', - icon: '/static/icons/baijiu.svg', - condition: { type: 'category_records', category: 'baijiu', value: 20 } + id: 'streak_100', + name: '百日坚持', + desc: '连续打卡100天,毅力惊人', + icon: '💯', + condition: { type: 'streak_days', value: 100 } + }, + // 标准杯累计类 + { + id: 'cups_10', + name: '微醺初体验', + desc: '累计饮用10标准杯', + icon: '🥂', + condition: { type: 'total_standard_cups', value: 10 } + }, + { + id: 'cups_50', + name: '五十杯之路', + desc: '累计饮用50标准杯,微醺是最好的状态', + icon: '🍻', + condition: { type: 'total_standard_cups', value: 50 } + }, + { + id: 'hundred_cups', + name: '百杯不醉', + desc: '累计饮用100标准杯', + icon: '🏆', + condition: { type: 'total_standard_cups', value: 100 } + }, + { + id: 'cups_300', + name: '海量传说', + desc: '累计饮用300标准杯,记得理性饮酒哦', + icon: '🌊', + condition: { type: 'total_standard_cups', value: 300 } + }, + // 酒类探索类 + { + id: 'variety_3', + name: '探索起步', + desc: '尝试过3种不同类型的酒', + icon: '🧭', + condition: { type: 'category_variety', value: 3 } }, { id: 'tasting_master', @@ -213,6 +273,35 @@ export const ACHIEVEMENTS = [ desc: '尝试过5种以上不同类型的酒', icon: '🌈', condition: { type: 'category_variety', value: 5 } + }, + { + id: 'variety_8', + name: '酒类百科', + desc: '尝试过8种以上不同类型的酒,尝遍百味', + icon: '📚', + condition: { type: 'category_variety', value: 8 } + }, + // 单一酒类深耕类 + { + id: 'baijiu_master', + name: '白酒达人', + desc: '白酒累计打卡20次', + icon: '/static/icons/baijiu.svg', + condition: { type: 'category_records', category: 'baijiu', value: 20 } + }, + { + id: 'beer_master', + name: '啤酒之王', + desc: '啤酒累计打卡20次,冰爽永不停', + icon: '🍺', + condition: { type: 'category_records', category: 'beer', value: 20 } + }, + { + id: 'wine_master', + name: '红酒绅士', + desc: '红酒累计打卡20次,品味微酸的美好', + icon: '🍷', + condition: { type: 'category_records', category: 'wine', value: 20 } } ] diff --git a/common/utils.js b/common/utils.js index 1a7800b..4596558 100644 --- a/common/utils.js +++ b/common/utils.js @@ -388,8 +388,9 @@ export function checkAchievements(stats, achievements) { unlocked = stats.categoryVariety >= value break case 'category_records': - // 需要更细粒度的数据,这里简化处理 - unlocked = false + // 依赖 stats.categoryRecords({ baijiu: n, beer: n, ... }); + // 统计中无该字段时保守判为未解锁 + unlocked = ((stats.categoryRecords && stats.categoryRecords[category]) || 0) >= value break } diff --git a/docs/成就徽章对接文档.md b/docs/成就徽章对接文档.md new file mode 100644 index 0000000..8ccb885 --- /dev/null +++ b/docs/成就徽章对接文档.md @@ -0,0 +1,127 @@ +# 成就徽章体系 - 后端对接文档 + +> 前端已将成就体系从 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`(各酒类打卡次数映射),统计接口若能返回该字段可提升兜底准确度 +- 因此后端即使暂未实现,前端也不会报错,只是解锁以本地统计为准 diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 1dc2c4e..b127061 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -4,9 +4,9 @@ "requires": true, "packages": { "node_modules/HaveADrink": { - "version": "1.0.16", - "resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.16.tgz", - "integrity": "sha512-NKaV3AZHHVYT23Eo4WGTzZ7H+FVfQoHa8EE3k5SnHCi9yxGAxnExBqRYde5AEFI1pJNe7OzqzbxAykxN757LXQ==", + "version": "1.0.17", + "resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.17.tgz", + "integrity": "sha512-7aKlBtul4YCZLlGEF72OtaGsNzWX4nJlPvvxt7iJ7hu/x7MLD6yQWuc/IshlEf94A7aQmDxJubOdcrBcoLQjWw==", "license": "ISC" } } diff --git a/node_modules/HaveADrink/README.md b/node_modules/HaveADrink/README.md index d994092..1498d25 100644 --- a/node_modules/HaveADrink/README.md +++ b/node_modules/HaveADrink/README.md @@ -14,7 +14,7 @@ * 6. 社交模块 - 朋友圈动态、点赞 -**版本:** v1.0.16 +**版本:** v1.0.17 ## 安装 @@ -220,6 +220,53 @@ const req = new GetAchievementsReq() client.GetAchievements(req).then(...).catch(...) ``` +### 获取用户所有成就列表。需登录认证,返回全量17项及解锁进度 + + + +GET `/api/have_a_drink/v1/achievements/achievements` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|achievements|`Array`| 全量 17 个成就(含未解锁)
| + + + +**AchievementItem** +|名称|类型|说明| +|:-|:-|:-| +|id|`string`| 成就唯一标识(如 first_checkin)| +|name|`string`| 成就名称| +|desc|`string`| 成就描述| +|unlocked|`boolean`| 是否已解锁| +|unlockedAt|`string`| 解锁时间(ISO 8601),未解锁时为 null| +|condition|`Condition`| 解锁条件| +|progress|`number`| 进度 (0~1)| +|icon,omitempty|`string`| 图标资源(前端忽略该字段,后端可省略)| + + +**Condition** +|名称|类型|说明| +|:-|:-|:-| +|type|`ConditionType`| 条件类型| +|value|`number`| 目标阈值| +|category,omitempty|`string`| 酒类分类(仅当 Type = category_records 时有效)| + + + +```javascript +const req = new GetAchievementListReq() +client.GetAchievementList(req).then(...).catch(...) +``` + ### 获取朋友圈动态 @@ -1092,6 +1139,7 @@ client.GetUserProfile(req).then(...).catch(...) ### 更新用户偏好 +**已废弃:** 未使用 PUT `/api/have_a_drink/v1/user/user/preferences` diff --git a/node_modules/HaveADrink/index.d.ts b/node_modules/HaveADrink/index.d.ts index 5f36209..2d7cac0 100644 --- a/node_modules/HaveADrink/index.d.ts +++ b/node_modules/HaveADrink/index.d.ts @@ -65,6 +65,35 @@ export declare class AchievementConditionType extends Enum { static getLabel(v: number|string): string; } +/** + * 成就条件类型 + */ +export declare class ConditionType extends Enum { + /** + * 总打卡次数 + */ + static TotalRecords: ConditionType; + /** + * 连续打卡天数 + */ + static StreakDays: ConditionType; + /** + * 累计标准杯数 + */ + static TotalStandardCups: ConditionType; + /** + * 尝试过的酒类品种数 + */ + static CategoryVariety: ConditionType; + /** + * 某酒类打卡次数(需配合 category 字段) + */ + static CategoryRecords: ConditionType; + + static entries(): Array