feat(api): 添加成就系统并升级SDK

- 升级 HaveADrink SDK 从 1.0.16 到 1.0.17 版本
- 新增 GetAchievements API 接口及相关数据类型定义
- 添加 17 个成就定义,包括打卡次数、连续打卡、标准杯累计、酒类探索等类别
- 实现成就解锁逻辑计算,支持 category_records 类型成就判断
- 创建成就徽章对接文档,定义接口格式和存储建议
- 在用户资料页面集成成就展示功能,优化图标匹配逻辑
This commit is contained in:
cg
2026-08-17 23:07:23 +08:00
parent 06ec10cf10
commit 18e7b4fd50
12 changed files with 644 additions and 29 deletions
+3 -3
View File
@@ -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"
}
}
+49 -1
View File
@@ -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项及解锁进度
<font color="green">GET</font> `/api/have_a_drink/v1/achievements/achievements`
#### 请求参数
|名称|类型|校验规则|说明|
|:-|:-|:-|:-|
#### 返回值
|名称|类型|说明|
|:-|:-:|:-|
|achievements|`Array<AchievementItem>`| 全量 17 个成就(含未解锁)<br>|
**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(...)
### 更新用户偏好
**已废弃:** 未使用
<font color="green">PUT</font> `/api/have_a_drink/v1/user/user/preferences`
+178
View File
@@ -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<option>;
static getLabel(v: number|string): string;
}
/**
* 酒类分类
*/
@@ -939,6 +968,146 @@ export declare class AchievementsData {
static fromObject(o: Object): AchievementsData;
}
/**
* 空请求(用于无参接口)
*/
export declare class GetAchievementListReq {
[key: string]: any;
/**
*/
constructor();
/**
* 从对象创建 GetAchievementListReq
*
* @param o Object
*/
static fromObject(o: Object): GetAchievementListReq;
}
/**
* 成就条件详情
*/
export declare class Condition {
[key: string]: any;
/**
* 条件类型
*/
type: ConditionType;
/**
* 目标阈值
*/
value: number;
/**
* 酒类分类(仅当 Type = category_records 时有效)
*/
category,omitempty: string;
/**
* @param type ConditionType 条件类型
* @param value number 目标阈值
* @param category,omitempty string 酒类分类(仅当 Type = category_records 时有效)
*/
constructor(type: ConditionType,value: number,category,omitempty: string,);
/**
* 从对象创建 Condition
*
* @param o Object
* - type: ConditionType, // 条件类型
* - value: number, // 目标阈值
* - category,omitempty: string, // 酒类分类(仅当 Type = category_records 时有效)
*/
static fromObject(o: Object): Condition;
}
/**
* 单个成就项
*/
export declare class AchievementItem {
[key: string]: any;
/**
* 成就唯一标识(如 first_checkin)
*/
id: string;
/**
* 成就名称
*/
name: string;
/**
* 成就描述
*/
desc: string;
/**
* 是否已解锁
*/
unlocked: boolean;
/**
* 解锁时间(ISO 8601),未解锁时为 null
*/
unlockedAt: string;
/**
* 解锁条件
*/
condition: Condition;
/**
* 进度 (0~1)
*/
progress: number;
/**
* 图标资源(前端忽略该字段,后端可省略)
*/
icon,omitempty: string;
/**
* @param id string 成就唯一标识(如 first_checkin)
* @param name string 成就名称
* @param desc string 成就描述
* @param unlocked boolean 是否已解锁
* @param unlockedAt string 解锁时间(ISO 8601),未解锁时为 null
* @param condition Condition 解锁条件
* @param progress number 进度 (0~1)
* @param icon,omitempty string 图标资源(前端忽略该字段,后端可省略)
*/
constructor(id: string,name: string,desc: string,unlocked: boolean,unlockedAt: string,condition: Condition,progress: number,icon,omitempty: string,);
/**
* 从对象创建 AchievementItem
*
* @param o Object
* - id: string, // 成就唯一标识(如 first_checkin)
* - name: string, // 成就名称
* - desc: string, // 成就描述
* - unlocked: boolean, // 是否已解锁
* - unlockedAt: string, // 解锁时间(ISO 8601),未解锁时为 null
* - condition: Condition, // 解锁条件
* - progress: number, // 进度 (0~1)
* - icon,omitempty: string, // 图标资源(前端忽略该字段,后端可省略)
*/
static fromObject(o: Object): AchievementItem;
}
/**
* 获取成就列表响应
*/
export declare class GetAchievementListResp {
[key: string]: any;
/**
* 全量 17 个成就(含未解锁)
*/
achievements: Array<AchievementItem>;
/**
* @param achievements Array<AchievementItem> 全量 17 个成就(含未解锁)
*/
constructor(achievements: Array<AchievementItem>,);
/**
* 从对象创建 GetAchievementListResp
*
* @param o Object
* - achievements: Array<AchievementItem>, // 全量 17 个成就(含未解锁)
*/
static fromObject(o: Object): GetAchievementListResp;
}
/**
* 朋友圈动态项
*/
@@ -5152,6 +5321,14 @@ export default class HaveADrink {
*/
public GetAchievements(req: GetAchievementsReq) : Promise<GetAchievementsResp>;
/**
* 获取用户所有成就列表。需登录认证,返回全量17项及解锁进度
*
* @param req GetAchievementListReq 空请求(用于无参接口)
* @return GetAchievementListResp 获取成就列表响应
*/
public GetAchievementList(req: GetAchievementListReq) : Promise<GetAchievementListResp>;
/**
* 获取朋友圈动态
*
@@ -5254,6 +5431,7 @@ export default class HaveADrink {
/**
* 更新用户偏好
*
* @deprecated 未使用
* @param req UpdatePreferencesReq 更新用户偏好请求
* @return UpdatePreferencesResp 更新用户偏好响应
*/
+158
View File
@@ -76,6 +76,51 @@ export class AchievementConditionType {
}
}
/**
* 成就条件类型
*/
export class ConditionType {
/**
* 总打卡次数
*/
static TotalRecords = "total_records";
/**
* 连续打卡天数
*/
static StreakDays = "streak_days";
/**
* 累计标准杯数
*/
static TotalStandardCups = "total_standard_cups";
/**
* 尝试过的酒类品种数
*/
static CategoryVariety = "category_variety";
/**
* 某酒类打卡次数(需配合 category 字段)
*/
static CategoryRecords = "category_records";
static entries() {
return [
{ value: ConditionType.TotalRecords, label: '总打卡次数' },
{ value: ConditionType.StreakDays, label: '连续打卡天数' },
{ value: ConditionType.TotalStandardCups, label: '累计标准杯数' },
{ value: ConditionType.CategoryVariety, label: '尝试过的酒类品种数' },
{ value: ConditionType.CategoryRecords, label: '某酒类打卡次数(需配合 category 字段)' },
];
}
static getLabel(v) {
switch(v) {
case ConditionType.TotalRecords: return '总打卡次数';
case ConditionType.StreakDays: return '连续打卡天数';
case ConditionType.TotalStandardCups: return '累计标准杯数';
case ConditionType.CategoryVariety: return '尝试过的酒类品种数';
case ConditionType.CategoryRecords: return '某酒类打卡次数(需配合 category 字段)';
}
}
}
/**
* 酒类分类
*/
@@ -936,6 +981,86 @@ export class AchievementsData {
}
}
/**
* 空请求(用于无参接口)
*/
export class GetAchievementListReq {
/**
*/
constructor() {
}
static fromObject(o) {
return new GetAchievementListReq();
}
}
/**
* 成就条件详情
*/
export class Condition {
/**
* @param type: ConditionType 条件类型
* @param value: number 目标阈值
* @param category,omitempty: string 酒类分类(仅当 Type = category_records 时有效)
*/
constructor(type,value,category,omitempty,) {
this.type = type;
this.value = value;
this.category,omitempty = category,omitempty;
}
static fromObject(o) {
return new Condition(o.type,o.value,o.category,omitempty,);
}
}
/**
* 单个成就项
*/
export class AchievementItem {
/**
* @param id: string 成就唯一标识(如 first_checkin)
* @param name: string 成就名称
* @param desc: string 成就描述
* @param unlocked: boolean 是否已解锁
* @param unlockedAt: string 解锁时间(ISO 8601),未解锁时为 null
* @param condition: Condition 解锁条件
* @param progress: number 进度 (0~1)
* @param icon,omitempty: string 图标资源(前端忽略该字段,后端可省略)
*/
constructor(id,name,desc,unlocked,unlockedAt,condition,progress,icon,omitempty,) {
this.id = id;
this.name = name;
this.desc = desc;
this.unlocked = unlocked;
this.unlockedAt = unlockedAt;
this.condition = condition;
this.progress = progress;
this.icon,omitempty = icon,omitempty;
}
static fromObject(o) {
return new AchievementItem(o.id,o.name,o.desc,o.unlocked,o.unlockedAt,o.condition,o.progress,o.icon,omitempty,);
}
}
/**
* 获取成就列表响应
*/
export class GetAchievementListResp {
/**
* @param achievements: Array<AchievementItem> 全量 17 个成就(含未解锁)
*/
constructor(achievements,) {
this.achievements = achievements;
}
static fromObject(o) {
return new GetAchievementListResp(o.achievements,);
}
}
/**
* 朋友圈动态项
*/
@@ -3736,6 +3861,39 @@ export default class HaveADrink {
});
}
/**
* 获取用户所有成就列表。需登录认证,返回全量17项及解锁进度
*/
GetAchievementList(req) {
return new Promise((reslove, reject)=>{
let data = req;
let url = `${this.host}/api/have_a_drink/v1/achievements/achievements`;
const query = Object.keys(data).map((x)=>(`${x}=${data[x]}`)).join('&');
this.http_request(url, {
uri: '/api/have_a_drink/v1/achievements/achievements',
method: 'GET',
query: query,
data: data,
responseType: 'json',
headers: {
'X-Token': this.token,
},
}).then((data)=>{
if (data.hasOwnProperty("fail")) {
if (data.fail) {
reject(data.msg);
} else {
reslove(data.data);
}
} else {
reslove(data);
}
}).catch((err)=>reject(err));
});
}
/**
* 获取朋友圈动态
*/
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "HaveADrink",
"type": "module",
"version": "v1.0.16",
"version": "v1.0.17",
"description": "喝酒了么 API 服务",
"main": "index.js",
"scripts": {