diff --git a/common/api.js b/common/api.js index 990695f..692999f 100644 --- a/common/api.js +++ b/common/api.js @@ -32,8 +32,17 @@ function httpRequest(url, params) { success: (res) => { const code = res.statusCode if (code === 200 || code === 201 || code === 204) { + // 业务级错误:HTTP 200 但 fail:true + if (res.data && res.data.fail === true) { + const bizMsg = res.data.msg || '操作失败' + if (!silent) { + uni.showToast({ title: bizMsg, icon: 'none', duration: 2500 }) + } + reject({ msg: bizMsg, code }) + return + } resolve(res.data) - } else if (code === 401 || code === 418) { + } else if (code === 401 ) { // token/refresh_token 过期或无效,清除登录态 uni.removeStorageSync('auth_token') uni.removeStorageSync('refresh_token') @@ -76,8 +85,8 @@ function uploadRequest(url, params) { uni.uploadFile({ url: url, - filePath: params.data.fileName || params.data.filePath, - name: params.data.name || 'file', + filePath: params.data.image || params.data.fileName || params.data.filePath, + name: params.data.name || 'image', header: headers, formData: params.data.formData || {}, success: (uploadRes) => { @@ -156,137 +165,146 @@ export async function refreshAuthToken() { } // ========================================== -// 酒友圈 API(当前返回 Mock 数据,后端就绪后切换为 SDK 调用) +// 酒友圈 API(基于 HaveADrink SDK 真实接口) // ========================================== -import { MOCK_FEEDS, MOCK_COMMENTS, MOCK_FRIENDS, MOCK_FRIEND_REQUESTS, MOCK_EVENTS, MOCK_CONVERSATIONS, MOCK_MESSAGES } from './mock-data' -/** 模拟网络延迟 */ -function mockDelay(data, ms = 300) { - return new Promise(resolve => setTimeout(() => resolve({ data }), ms)) -} - -/** 获取动态信息流 */ -export function GetCircleFeeds({ page = 1, pageSize = 10 } = {}) { - // TODO: 后端就绪后替换为 client.GetCircleFeeds(...) - const start = (page - 1) * pageSize - const list = MOCK_FEEDS.slice(start, start + pageSize) - return mockDelay({ list, total: MOCK_FEEDS.length, hasMore: start + pageSize < MOCK_FEEDS.length }) +/** 获取动态信息流(游标分页) */ +export async function GetCircleFeeds({ lastId = 0, pageSize = 10 } = {}) { + const res = await client.GetFeeds({ lastId, pageSize }) + return { data: res } } /** 点赞动态 */ -export function LikeFeed({ feedId }) { - return mockDelay({ success: true }) +export async function LikeFeed({ feedId }) { + const res = await client.LikeFeed({ id: feedId }) + return { data: res } } /** 取消点赞 */ -export function UnlikeFeed({ feedId }) { - return mockDelay({ success: true }) +export async function UnlikeFeed({ feedId }) { + const res = await client.UnlikeFeed({ id: feedId }) + return { data: res } } /** 获取动态评论 */ -export function GetFeedComments({ feedId }) { - const list = MOCK_COMMENTS[feedId] || [] - return mockDelay({ list }) +export async function GetFeedComments({ feedId }) { + const res = await client.GetFeedComments({ id: feedId }) + return { data: res } } /** 发表评论 */ -export function AddComment({ feedId, content }) { - return mockDelay({ success: true, comment: { id: 'c_' + Date.now(), content, time: '刚刚' } }) +export async function AddComment({ feedId, content }) { + const res = await client.AddComment({ id: feedId, content }) + return { data: res } } /** 发布动态 */ -export function PublishFeed({ text, images, recordId, visibility }) { - return mockDelay({ success: true, feedId: 'feed_' + Date.now() }) +export async function PublishFeed({ text, images, recordId, visibility }) { + const res = await client.PublishFeed({ text, images, recordId, visibility }) + return { data: res } } /** 获取酒友列表 */ -export function GetFriends({ keyword = '' } = {}) { - const list = keyword - ? MOCK_FRIENDS.filter(f => f.nickname.includes(keyword)) - : MOCK_FRIENDS - return mockDelay({ list }) +export async function GetFriends({ keyword = '' } = {}) { + const res = await client.GetFriends({ keyword }) + return { data: res } } /** 获取好友请求 */ -export function GetFriendRequests() { - return mockDelay({ list: MOCK_FRIEND_REQUESTS }) +export async function GetFriendRequests() { + const res = await client.GetFriendRequests({}) + return { data: res } } /** 发送好友请求 */ -export function SendFriendRequest({ userId }) { - return mockDelay({ success: true }) +export async function SendFriendRequest({ userId, message = '' }) { + const res = await client.SendFriendRequest({ userId, message }) + return { data: res } } /** 接受好友请求 */ -export function AcceptFriendRequest({ requestId }) { - return mockDelay({ success: true }) +export async function AcceptFriendRequest({ requestId }) { + const res = await client.AcceptFriendRequest({ id: requestId }) + return { data: res } } /** 删除酒友 */ -export function RemoveFriend({ userId }) { - return mockDelay({ success: true }) +export async function RemoveFriend({ userId }) { + const res = await client.RemoveFriend({ userId }) + return { data: res } } /** 获取酒局列表 */ -export function GetEvents({ status = '' } = {}) { - const list = status ? MOCK_EVENTS.filter(e => e.status === status) : MOCK_EVENTS - return mockDelay({ list }) +export async function GetEvents({ status = '' } = {}) { + const res = await client.GetEvents({ status }) + return { data: res } } /** 获取酒局详情 */ -export function GetEventDetail({ eventId }) { - const evt = MOCK_EVENTS.find(e => e.id === eventId) || MOCK_EVENTS[0] - return mockDelay(evt) +export async function GetEventDetail({ eventId }) { + const res = await client.GetEventDetail({ id: eventId }) + return { data: res } } /** 发起酒局 */ -export function CreateEvent({ title, time, location, maxPeople, note }) { - return mockDelay({ success: true, eventId: 'evt_' + Date.now() }) +export async function CreateEvent({ title, time, location, maxPeople, note, latitude, longitude }) { + const geo = (latitude != null && longitude != null) ? { latitude, longitude } : undefined + const res = await client.CreateEvent({ title, time, location, maxPeople, geo, note }) + return { data: res } } /** 报名酒局 */ -export function JoinEvent({ eventId }) { - return mockDelay({ success: true }) +export async function JoinEvent({ eventId }) { + const res = await client.JoinEvent({ id: eventId }) + return { data: res } } /** 取消报名 */ -export function QuitEvent({ eventId }) { - return mockDelay({ success: true }) +export async function QuitEvent({ eventId }) { + const res = await client.QuitEvent({ id: eventId }) + return { data: res } } /** 酒局签到 */ -export function CheckInEvent({ eventId }) { - return mockDelay({ success: true }) +export async function CheckInEvent({ eventId }) { + const res = await client.CheckInEvent({ id: eventId }) + return { data: res } } // ========================================== -// 私信 API(当前返回 Mock 数据,后端就绪后切换为 SDK 调用) +// 私信 API(基于 HaveADrink SDK 真实接口) // ========================================== /** 获取会话列表 */ -export function GetConversations() { - // TODO: 后端就绪后替换为 client.GetConversations(...) - return mockDelay({ list: MOCK_CONVERSATIONS }) +export async function GetConversations() { + const res = await client.GetConversations({}) + return { data: res } } -/** 获取历史消息 */ -export function GetMessages({ conversationId, page = 1, pageSize = 20 } = {}) { - // TODO: 后端就绪后替换为 client.GetMessages(...) - const all = MOCK_MESSAGES[conversationId] || [] - const start = (page - 1) * pageSize - const list = all.slice(start, start + pageSize) - return mockDelay({ list, hasMore: start + pageSize < all.length }) +/** 获取历史消息(游标分页) */ +export async function GetMessages({ conversationId, lastID = 0, pageSize = 20 } = {}) { + const res = await client.GetMessages({ conversationId, lastID, pageSize }) + return { data: res } } /** 标记会话已读 */ -export function MarkRead({ conversationId }) { - // TODO: 后端就绪后替换为 client.MarkRead(...) - return mockDelay({ success: true }) +export async function MarkRead({ conversationId }) { + const res = await client.MarkRead({ id: conversationId }) + return { data: res } } /** 获取未读消息总数 */ -export function GetUnreadCount() { - // TODO: 后端就绪后替换为 client.GetUnreadCount(...) - const total = MOCK_CONVERSATIONS.reduce((sum, c) => sum + c.unread, 0) - return mockDelay({ total }) +export async function GetUnreadCount() { + const res = await client.GetUnreadCount({}) + return { data: res } +} + +// ========================================== +// 文件上传 API +// ========================================== + +/** 上传图片(返回 url) */ +export async function UploadImage({ filePath }) { + const res = await client.UploadImage({ image: filePath }) + return { data: res } } diff --git a/common/websocket.js b/common/websocket.js index ec73149..bd936e8 100644 --- a/common/websocket.js +++ b/common/websocket.js @@ -1,9 +1,29 @@ /* 碰盏日记 - WebSocket 管理器 * 单例模式,负责私信实时通信 + * 协议格式与 HaveADrink SDK ChatWebSocket 一致:{ mesgType, data } * 支持心跳、自动重连、消息队列 */ -const WS_HOST = 'wss://dev.wash-painting.cn/ws' +const WS_HOST = 'wss://dev.wash-painting.cn/api/have_a_drink/v1/chat/chat' + +// 客户端消息类型(与 SDK ClientMesgType 枚举值一致) +const ClientMesgType = { + Chat: 'chat', + Typing: 'typing', + Read: 'read', + Ping: 'ping' +} + +// 服务端消息类型(与 SDK ServerMesgType 枚举值一致) +const ServerMesgType = { + Chat: 'chat', + Typing: 'typing', + Read: 'read', + Ack: 'ack', + Pong: 'pong', + Connected: 'connected', + Kicked: 'kicked' +} class WebSocketManager { constructor() { @@ -21,6 +41,7 @@ class WebSocketManager { this.heartbeatWait = 60000 // 60s 无 pong 则重连 this.manualClose = false this.token = '' + this.userId = '' // 连接成功后服务端返回的用户ID } /** @@ -37,11 +58,14 @@ class WebSocketManager { this.manualClose = false this.isConnecting = true - const url = `${WS_HOST}?token=${this.token}` - console.log('[WS] 正在连接...', url) + const url = WS_HOST + console.log('[WS] 正在连接...') this.socketTask = uni.connectSocket({ url, + header: { + 'Authorization': `Bearer ${this.token}` + }, complete: () => {} }) @@ -95,17 +119,17 @@ class WebSocketManager { } /** - * 发送消息 - * @param {string} type - 消息类型: chat | typing | read | ping + * 发送消息(与 SDK writeClient* 方法格式一致) + * @param {string} mesgType - 消息类型: chat | typing | read | ping * @param {object} data - 消息数据 */ - send(type, data = {}) { - const payload = JSON.stringify({ type, data, ts: Date.now() }) + send(mesgType, data = {}) { + const payload = JSON.stringify({ mesgType, data }) if (this.isConnected && this.socketTask) { this.socketTask.send({ data: payload }) } else { - // 断线暂存队列 - if (type !== 'ping') { + // 断线暂存队列(ping 不暂存) + if (mesgType !== ClientMesgType.Ping) { this.messageQueue.push(payload) } // 尝试重连 @@ -117,7 +141,7 @@ class WebSocketManager { /** * 订阅事件 - * @param {string} event - message | typing | read | ack | connect | disconnect + * @param {string} event - message | typing | read | ack | connected | kicked | connect | disconnect * @param {function} callback */ on(event, callback) { @@ -143,27 +167,38 @@ class WebSocketManager { // ========== 内部方法 ========== - /** 处理收到的消息 */ + /** 处理收到的消息(SDK ServerMesgType 协议) */ _handleMessage(msg) { - const { type, data } = msg - switch (type) { - case 'pong': - this._onPong() - break - case 'chat': + const { mesgType, data } = msg + switch (mesgType) { + case ServerMesgType.Chat: this._emit('message', data) break - case 'typing': + case ServerMesgType.Typing: this._emit('typing', data) break - case 'read': + case ServerMesgType.Read: this._emit('read', data) break - case 'ack': + case ServerMesgType.Ack: this._emit('ack', data) break + case ServerMesgType.Pong: + this._onPong() + break + case ServerMesgType.Connected: + // 服务端验证 token 成功后推送,携带 userId + this.userId = data && data.userId ? data.userId : '' + console.log('[WS] 服务端确认连接, userId:', this.userId) + this._emit('connected', data) + break + case ServerMesgType.Kicked: + console.warn('[WS] 被踢下线') + this._emit('kicked', data) + this.disconnect() + break default: - console.log('[WS] 未知消息类型', type) + console.log('[WS] 未知消息类型', mesgType) } } @@ -181,7 +216,7 @@ class WebSocketManager { _startHeartbeat() { this._stopHeartbeat() this.heartbeatTimer = setInterval(() => { - this.send('ping') + this.send(ClientMesgType.Ping, {}) // 设置超时检测 this.heartbeatTimeout = setTimeout(() => { console.warn('[WS] 心跳超时,触发重连') diff --git a/components/EventCard.vue b/components/EventCard.vue index 5a63155..093353e 100644 --- a/components/EventCard.vue +++ b/components/EventCard.vue @@ -49,8 +49,9 @@ export default { emits: ['item-click'], computed: { statusLabel() { - const map = { open: '报名中', ongoing: '进行中', ended: '已结束' } - return map[this.event.status] || '报名中' + const val = String(this.event.status).toLowerCase() + const map = { open: '报名中', upcoming: '待开始', ongoing: '进行中', ended: '已结束' } + return map[val] || '报名中' } } } @@ -80,6 +81,9 @@ export default { &.status-open { background: rgba(94,198,160,0.12); } + &.status-upcoming { + background: rgba(139,133,184,0.12); + } &.status-ongoing { background: rgba(232,168,56,0.12); } @@ -93,6 +97,7 @@ export default { font-weight: $fw-medium; .status-open & { color: $mint; } + .status-upcoming & { color: $lavender; } .status-ongoing & { color: $amber; } .status-ended & { color: $text-tertiary; } } diff --git a/components/FeedCard.vue b/components/FeedCard.vue index 833e148..ef3e87b 100644 --- a/components/FeedCard.vue +++ b/components/FeedCard.vue @@ -73,8 +73,15 @@ export default { getCatIcon, isIconPath, feelingLabel(f) { - const map = { sober: '😌 清醒', buzzed: '😊 微醺', tipsy: '😄 小醉', drunk: '🥴 喝多了' } - return map[f] || '🍻 喝了点' + const val = String(f).toLowerCase() + const map = { + tipsy: '😌 微醺', + buzzed: '😊 到位', + drunk: '🥴 醉了', + blackout: '😵 断片', + sober: '😌 清醒' + } + return map[val] || '🍻 喝了点' } } } diff --git a/manifest.json b/manifest.json index 34a3d2d..581b39e 100644 --- a/manifest.json +++ b/manifest.json @@ -46,6 +46,7 @@ "desc" : "用于选择酒局地点和导航" } }, + "requiredPrivateInfos" : ["chooseLocation", "getLocation"], "optimization" : { "subPackages" : true } diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index f930d21..1c06d3e 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.4", - "resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.4.tgz", - "integrity": "sha512-kSSpTZrQGjRueftSAopurZcwXGMS3oKQ0mvP3/sePJh7odO5ev9FT31isXX0LLmL1HojzB4nrIeDb/PTdGhYuA==", + "version": "1.0.8", + "resolved": "https://npm.wash-painting.cn/HaveADrink/-/HaveADrink-1.0.8.tgz", + "integrity": "sha512-YOaqEZg+T1MKz1OtRJc09niyxEwm+52g91Yj0L2aGEy6bK6pTLJYlmREdneIStiniUGHVjL2/U5Qk57lTBAuFw==", "license": "ISC" } } diff --git a/node_modules/HaveADrink/README.md b/node_modules/HaveADrink/README.md index d0744e1..fbd048a 100644 --- a/node_modules/HaveADrink/README.md +++ b/node_modules/HaveADrink/README.md @@ -14,7 +14,7 @@ * 6. 社交模块 - 朋友圈动态、点赞 -**版本:** v1.0.4 +**版本:** v1.0.8 ## 安装 @@ -350,58 +350,6 @@ const req = new GetFeedReq() client.GetFeed(req).then(...).catch(...) ``` -### 点赞 - - - -POST `/api/have_a_drink/v1/communication/records/like` - -#### 请求参数 -|名称|类型|校验规则|说明| -|:-|:-|:-|:-| -|id|`string\|number`|required| 记录ID| - - - - -#### 返回值 -|名称|类型|说明| -|:-|:-:|:-| - - - - -```javascript -const req = new LikeRecordReq() -client.LikeRecord(req).then(...).catch(...) -``` - -### 取消点赞 - - - -POST `/api/have_a_drink/v1/communication/records/unlike` - -#### 请求参数 -|名称|类型|校验规则|说明| -|:-|:-|:-|:-| -|id|`string\|number`|required| 记录ID| - - - - -#### 返回值 -|名称|类型|说明| -|:-|:-:|:-| - - - - -```javascript -const req = new LikeRecordReq() -client.UnlikeRecord(req).then(...).catch(...) -``` - ### 上传照片 @@ -479,7 +427,7 @@ client.UploadPhotos(req).then(...).catch(...) **DrinkItem** |名称|类型|校验规则|说明| |:-|:-|:-|:-| -|category|`Category`|required| 酒类分类ID| +|category|`Category`|| 酒类分类ID| |brand|`string`|required| 品牌名称| |product|`string`|| 产品名| |amount|`number`|required,gt=0| 饮用量数值| @@ -532,7 +480,7 @@ client.UploadPhotos(req).then(...).catch(...) **FoodInfo** |名称|类型|校验规则|说明| |:-|:-|:-|:-| -|category|`FoodCategory`|required| 配餐分类ID| +|category|`FoodCategory`|| 配餐分类ID| |name|`string`|| 具体菜名| @@ -1193,3 +1141,809 @@ client.UpdatePreferences(req).then(...).catch(...) const req = new UploadImageReq() client.UploadImage(req).then(...).catch(...) ``` + +### 动态信息流 + + + +GET `/api/have_a_drink/v1/moments/feeds` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|lastId|`string\|number`|| 最后一条动态ID,用于分页| +|pageSize|`number`|| 每页数量| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 动态列表
| +|hasMore|`boolean`| 是否还有更多动态
| + + + +**Feed** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 动态ID| +|userId|`string\|number`| 用户ID| +|nickname|`string`| 昵称| +|avatar|`string`| 头像| +|time|`string`| 发布时间| +|text|`string`| 动态文本| +|drinks|`Array`| 酒水标签| +|feeling|`Feeling`| 自我感觉| +|images|`Array`| 图片列表| +|likes|`number`| 点赞数| +|liked|`boolean`| 是否点赞| +|comments|`number`| 评论数| + + +**DrinkTag** +|名称|类型|说明| +|:-|:-|:-| +|category|`Category`| 酒类分类| +|name|`string`| 酒类名称| +|amount|`number`| 酒类数量| + + +**Feeling** +|名称|值|类型|说明| +|:-|:-:|:-|:-| +|Tipsy|"tipsy"|`Feeling`|微醺| +|Buzzed|"buzzed"|`Feeling`|到位| +|Drunk|"drunk"|`Feeling`|醉了| +|Blackout|"blackout"|`Feeling`|断片| + + + +```javascript +const req = new GetFeedsReq() +client.GetFeeds(req).then(...).catch(...) +``` + +### 发布动态 + + + +POST `/api/have_a_drink/v1/moments/feeds` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|text|`string`|| 动态文本| +|images|`Array`|| 图片列表| +|recordId|`string\|number`|| 关联的记录ID| +|visibility|`Visibility`|公开: Visibility.Public = "public"
仅好友: Visibility.Friends = "friends"
仅自己: Visibility.Private = "private"| 可见性| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|success|`boolean`| 是否成功
| +|feedId|`string\|number`| 动态ID
| + + + + +```javascript +const req = new PublishFeedReq() +client.PublishFeed(req).then(...).catch(...) +``` + +### 点赞 + + + +POST `/api/have_a_drink/v1/moments/feeds/like` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 动态ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|ok|`boolean`| 是否成功
| + + + + +```javascript +const req = new LikeFeedReq() +client.LikeFeed(req).then(...).catch(...) +``` + +### 取消点赞 + + + +POST `/api/have_a_drink/v1/moments/feeds/unlike` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 动态ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|ok|`boolean`| 是否成功
| + + + + +```javascript +const req = new UnlikeFeedReq() +client.UnlikeFeed(req).then(...).catch(...) +``` + +### 获取评论 + + + +GET `/api/have_a_drink/v1/moments/feeds/comments` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 动态ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 评论列表
| + + + +**Comment** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 评论ID| +|userId|`string\|number`| 用户ID| +|nickname|`string`| 昵称| +|avatar|`string`| 头像| +|content|`string`| 评论内容| +|time|`string`| 评论时间| + + + +```javascript +const req = new GetFeedCommentsReq() +client.GetFeedComments(req).then(...).catch(...) +``` + +### 发表评论 + + + +POST `/api/have_a_drink/v1/moments/feeds/comments` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 动态ID| +|content|`string`|| 评论内容| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|id|`string\|number`| 评论ID
| +|userId|`string\|number`| 用户ID
| +|nickname|`string`| 昵称
| +|avatar|`string`| 头像
| +|content|`string`| 评论内容
| +|time|`string`| 评论时间
| + + + + +```javascript +const req = new AddCommentFullReq() +client.AddComment(req).then(...).catch(...) +``` + +### 酒友管理 + + + +GET `/api/have_a_drink/v1/friends/friends` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|keyword|`string`|| 搜索关键词| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 分页数据
| + + + +**Friend** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 酒友ID| +|nickname|`string`| 昵称| +|avatar|`string`| 头像| +|lastDrink|`string\|number`| 最近一次喝的酒| +|lastDrinkTime|`string`| 最近一次喝的酒时间| +|online|`boolean`| 是否在线| + + + +```javascript +const req = new GetFriendsReq() +client.GetFriends(req).then(...).catch(...) +``` + +### 好友请求管理 + + + +GET `/api/have_a_drink/v1/friends/friend-requests` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 分页数据
| + + + +**FriendRequest** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 好友请求ID| +|userId|`string\|number`| 酒友ID| +|nickname|`string`| 昵称| +|avatar|`string`| 头像| +|message|`string`| 消息| +|time|`string`| 创建时间| + + + +```javascript +const req = new GetFriendRequestsReq() +client.GetFriendRequests(req).then(...).catch(...) +``` + +### 发送好友请求 + + + +POST `/api/have_a_drink/v1/friends/friend-requests` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|userId|`string\|number`|| 酒友ID| +|message|`string`|| 消息| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| + + + + +```javascript +const req = new SendFriendRequestReq() +client.SendFriendRequest(req).then(...).catch(...) +``` + +### 接受好友请求 + + + +POST `/api/have_a_drink/v1/friends/friend-requests/accept` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 好友请求ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| + + + + +```javascript +const req = new AcceptFriendRequestReq() +client.AcceptFriendRequest(req).then(...).catch(...) +``` + +### 删除酒友 + + + +DELETE `/api/have_a_drink/v1/friends/friends` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|userId|`string\|number`|| 酒友ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| + + + + +```javascript +const req = new RemoveFriendReq() +client.RemoveFriend(req).then(...).catch(...) +``` + +### 约酒活动 + + + +GET `/api/have_a_drink/v1/events/events` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|status|`EventStatus`|开放报名: EventStatus.Open = 1
待开始: EventStatus.Upcoming = 2
进行中: EventStatus.Ongoing = 3
已结束: EventStatus.Ended = 4| 状态筛选| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 分页数据
| + + + +**Event** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 酒局ID| +|title|`string`| 酒局标题| +|organizer|`User`| 酒局组织者| +|time|`string`| 酒局时间| +|location|`string`| 酒局地点| +|maxPeople|`number`| 最大人数| +|joined|`number`| 已报名人数| +|participants|`Array`| 已报名用户| +|status|`EventStatus`| 酒局状态| +|note|`string`| 酒局备注| +|isJoined|`boolean`| 是否已报名| +|isOrganizer|`boolean`| 是否为组织者| + + +**User** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 用户ID| +|nickname|`string`| 用户昵称| +|avatar|`string`| 头像URL| +|joinedAt|`string`| 加入时间| +|preferences|`UserPreferences`| 用户偏好| + + +**UserPreferences** +|名称|类型|说明| +|:-|:-|:-| +|favoriteCategories|`Array`| 偏好酒类ID列表| +|frequency|`Frequency`| 饮酒频率| + + +**Frequency** +|名称|值|类型|说明| +|:-|:-:|:-|:-| +|Rarely|"rarely"|`Frequency`|很少| +|Sometimes|"sometimes"|`Frequency`|有时| +|Often|"often"|`Frequency`|经常| + + +**EventStatus** +|名称|值|类型|说明| +|:-|:-:|:-|:-| +|Open|1|`EventStatus`|开放报名| +|Upcoming|2|`EventStatus`|待开始| +|Ongoing|3|`EventStatus`|进行中| +|Ended|4|`EventStatus`|已结束| + + + +```javascript +const req = new GetEventsReq() +client.GetEvents(req).then(...).catch(...) +``` + +### 获取酒局详情 + + + +GET `/api/have_a_drink/v1/events/events/detail` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 酒局ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|id|`string\|number`| 酒局ID
| +|title|`string`| 酒局标题
| +|organizer|`User`| 酒局组织者
| +|time|`string`| 酒局时间
| +|location|`string`| 酒局地点
| +|maxPeople|`number`| 最大人数
| +|joined|`number`| 已报名人数
| +|participants|`Array`| 已报名用户
| +|status|`EventStatus`| 酒局状态
开放报名: EventStatus.Open = 1
待开始: EventStatus.Upcoming = 2
进行中: EventStatus.Ongoing = 3
已结束: EventStatus.Ended = 4| +|note|`string`| 酒局备注
| +|isJoined|`boolean`| 是否已报名
| +|isOrganizer|`boolean`| 是否为组织者
| + + + +**User** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 用户ID| +|nickname|`string`| 用户昵称| +|avatar|`string`| 头像URL| +|joinedAt|`string`| 加入时间| +|preferences|`UserPreferences`| 用户偏好| + + +**UserPreferences** +|名称|类型|说明| +|:-|:-|:-| +|favoriteCategories|`Array`| 偏好酒类ID列表| +|frequency|`Frequency`| 饮酒频率| + + + +```javascript +const req = new GetEventDetailReq() +client.GetEventDetail(req).then(...).catch(...) +``` + +### 发起酒局 + + + +POST `/api/have_a_drink/v1/events/events` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|title|`string`|| 酒局标题| +|time|`string`|| 酒局时间| +|location|`string`|| 酒局地点| +|maxPeople|`number`|| 最大人数| +|geo|`GeoPoint`|| 坐标信息| +|note|`string`|| 酒局备注| + + + +**GeoPoint** +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|latitude|`number`|| 纬度| +|longitude|`number`|| 经度| + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|eventId|`string\|number`| 酒局ID
| + + + + +```javascript +const req = new CreateEventReq() +client.CreateEvent(req).then(...).catch(...) +``` + +### 报名酒局 + + + +POST `/api/have_a_drink/v1/events/events/join` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 酒局ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|ok|`boolean`| 是否成功
| + + + + +```javascript +const req = new JoinEventReq() +client.JoinEvent(req).then(...).catch(...) +``` + +### 取消报名酒局 + + + +POST `/api/have_a_drink/v1/events/events/quit` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 酒局ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|ok|`boolean`| 是否成功
| + + + + +```javascript +const req = new QuitEventReq() +client.QuitEvent(req).then(...).catch(...) +``` + +### 签到酒局 + + + +POST `/api/have_a_drink/v1/events/events/checkin` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string\|number`|| 酒局ID| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|ok|`boolean`| 是否成功
| + + + + +```javascript +const req = new CheckInEventReq() +client.CheckInEvent(req).then(...).catch(...) +``` + +### 获取会话列表 + + + +GET `/api/have_a_drink/v1/chat/conversations` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 会话列表
| + + + +**Conversation** +|名称|类型|说明| +|:-|:-|:-| +|id|`string`| 会话ID| +|friendId|`string\|number`| 对方用户ID| +|nickname|`string`| 对方昵称| +|avatar|`string`| 对方头像URL(可为空)| +|lastMessage|`string`| 最后一条消息摘要| +|lastTime|`string`| 最后消息时间(ISO 8601)| +|unread|`number`| 未读消息数| +|online|`boolean`| 对方是否在线| + + + +```javascript +const req = new GetConversationsReq() +client.GetConversations(req).then(...).catch(...) +``` + +### 获取历史消息 + + + +GET `/api/have_a_drink/v1/chat/messages` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|conversationId|`string`|required| 会话ID| +|lastID|`string\|number`|| 游标分页| +|pageSize|`number`|gt=0,lte=100| 每页数量,默认20| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|list|`Array`| 消息列表
| +|hasMore|`boolean`| 是否还有更多消息
| + + + +**Message** +|名称|类型|说明| +|:-|:-|:-| +|id|`string\|number`| 消息ID| +|conversationId|`string`| 会话ID| +|senderId|`string\|number`| 发送者ID| +|receiverId|`string\|number`| 接收者ID| +|type|`MessageType`| 消息类型| +|content|`string`| 消息内容| +|timestamp|`number`| 发送时间(毫秒时间戳)| +|status|`MessageStatus`| 消息状态| + + +**MessageType** +|名称|值|类型|说明| +|:-|:-:|:-|:-| +|Text|"text"|`MessageType`|文本消息| +|Image|"image"|`MessageType`|图片消息| + + +**MessageStatus** +|名称|值|类型|说明| +|:-|:-:|:-|:-| +|Sending|"sending"|`MessageStatus`|发送中| +|Sent|"sent"|`MessageStatus`|已发送| +|Delivered|"delivered"|`MessageStatus`|已送达| +|Read|"read"|`MessageStatus`|已读| + + + +```javascript +const req = new GetMessagesReq() +client.GetMessages(req).then(...).catch(...) +``` + +### 标记会话已读 + + + +POST `/api/have_a_drink/v1/chat/conversations/read` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|id|`string`|required| 会话ID(路径参数)| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|success|`boolean`| 是否成功
| + + + + +```javascript +const req = new MarkReadReq() +client.MarkRead(req).then(...).catch(...) +``` + +### 获取未读消息总数 + + + +GET `/api/have_a_drink/v1/chat/unread-count` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|total|`number`| 未读总数
| + + + + +```javascript +const req = new GetUnreadCountReq() +client.GetUnreadCount(req).then(...).catch(...) +``` + +### 注意:连接建立后,服务端会先验证 token,然后推送 connected 事件 + + + +WS `/api/have_a_drink/v1/chat/chat` + +#### 请求参数 +|名称|类型|校验规则|说明| +|:-|:-|:-|:-| +|mesgType|`ClientMesgType`|发送消息: ClientMesgType.Chat = "chat"
输入状态: ClientMesgType.Typing = "typing"
已读回执: ClientMesgType.Read = "read"
心跳: ClientMesgType.Ping = "ping"| 消息类型| +|data|`ClientMessage`|| 消息数据| + + + + +#### 返回值 +|名称|类型|说明| +|:-|:-:|:-| +|mesgType|`ServerMesgType`| 消息类型
推送新消息: ServerMesgType.Chat = "chat"
对方输入状态: ServerMesgType.Typing = "typing"
对方已读回执: ServerMesgType.Read = "read"
消息送达确认: ServerMesgType.Ack = "ack"
心跳响应: ServerMesgType.Pong = "pong"
连接成功: ServerMesgType.Connected = "connected"
被踢下线: ServerMesgType.Kicked = "kicked"| +|data|`ServerMessage`| 消息数据
| + + + + +```javascript +const req = new ChatWebSocket() +client.ChatWebSocket(req).then(...).catch(...) +``` diff --git a/node_modules/HaveADrink/index.d.ts b/node_modules/HaveADrink/index.d.ts index 9cd4cd6..32e119d 100644 --- a/node_modules/HaveADrink/index.d.ts +++ b/node_modules/HaveADrink/index.d.ts @@ -263,6 +263,135 @@ export declare class Frequency extends Enum { static getLabel(v: number|string): string; } +/** + * 酒局状态 + */ +export declare class EventStatus extends Enum { + /** + * 开放报名 + */ + static Open: EventStatus; + /** + * 待开始 + */ + static Upcoming: EventStatus; + /** + * 进行中 + */ + static Ongoing: EventStatus; + /** + * 已结束 + */ + static Ended: EventStatus; + + static entries(): Array