/* 碰盏日记 - API 服务层 * 基于 HaveADrink SDK 封装后端接口 */ import HaveADrink from 'HaveADrink' // ========================================== // 后端服务地址(切换环境只需改这里) // ========================================== const API_HOST = 'https://dev.wash-painting.cn' // ========================================== // HTTP 请求函数(供 SDK 内部调用) // 参考 goodBooth 项目的 fetchsomething // ========================================== function httpRequest(url, params) { // 支持静默模式(如后台刷新token),不弹 toast 不跳转 const silent = params.silent === true return new Promise((resolve, reject) => { // 自动注入 Authorization token const headers = Object.assign({}, params.headers || {}) const token = uni.getStorageSync('auth_token') if (token) { headers['Authorization'] = `Bearer ${token}` } uni.request({ url: url, method: params.method, params: params.query || undefined, data: params.data, header: headers, 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 ) { // token/refresh_token 过期或无效,清除登录态 uni.removeStorageSync('auth_token') uni.removeStorageSync('refresh_token') uni.removeStorageSync('is_logged_in') if (!silent) { uni.reLaunch({ url: '/pages/login/login' }) uni.showToast({ title: '登录已过期,请重新登录', icon: 'none', duration: 2500 }) } reject({ msg: '登录已过期,请重新登录', code }) } else { const rawMsg = (res.data && res.data.msg) || (res.data && res.data.errmsg) || '请求失败' const errMsg = `[${code}] ${rawMsg}` if (!silent) { uni.showToast({ title: errMsg, icon: 'none', duration: 2500 }) } reject({ msg: errMsg, code }) } }, fail: (err) => { const msg = (err && err.errMsg) || '网络请求失败' if (!silent) { uni.showToast({ title: msg, icon: 'none', duration: 2500 }) } reject({ msg }) } }) }) } // ========================================== // 文件上传函数(供 SDK 内部调用) // ========================================== function uploadRequest(url, params) { return new Promise((resolve, reject) => { const headers = Object.assign({}, params.headers || {}) const token = uni.getStorageSync('auth_token') if (token) { headers['Authorization'] = `Bearer ${token}` } uni.uploadFile({ url: url, filePath: params.data.image || params.data.fileName || params.data.filePath, name: params.data.name || 'image', header: headers, formData: params.data.formData || {}, success: (uploadRes) => { try { const data = typeof uploadRes.data === 'string' ? JSON.parse(uploadRes.data) : uploadRes.data resolve(data) } catch (e) { resolve(uploadRes.data) } }, fail: (err) => { reject({ msg: err.errMsg || '上传失败' }) } }) }) } /** * 使用 uni-app WebSocket API 实现的 WebSocket 类 * 符合之前定义的 WebsocketInterface(无类型约束) */ class UniAppWebSocket { /** * 构造函数:传入 WebSocket 服务器地址 * @param {string} host - 完整的 WebSocket URL,例如 'wss://example.com/ws' */ constructor(host) { this.host = host; // 事件回调属性(外部可赋值) this.close = null; // 可赋值为函数,同时也可用主动关闭连接 this.onopen = null; this.onmessage = null; // 内部 SocketTask 实例 this.socketTask = null; // 初始化连接 this._initSocket(); } /** * 初始化 WebSocket 连接(私有方法,约定以 _ 开头) */ _initSocket() { const self = this; // 创建 SocketTask this.socketTask = uni.connectSocket({ url: this.host, success() { // 连接创建成功(但尚未打开) }, fail(err) { // 连接创建失败,触发错误事件 const errorEvent = { type: 'error', error: err, target: self }; self.onerror(errorEvent); } }); // 监听打开事件 this.socketTask.onOpen((res) => { if (self.onopen) { // 构造一个简单的事件对象 const event = { type: 'open', target: self, ...res }; self.onopen(event); } }); // 监听消息事件 this.socketTask.onMessage((res) => { if (self.onmessage) { // res.data 是消息数据 const event = { type: 'message', data: res.data, target: self }; self.onmessage(event); } }); // 监听关闭事件 this.socketTask.onClose((res) => { // 构造关闭事件 const event = { type: 'close', code: res.code, reason: res.reason, wasClean: res.wasClean, target: self }; // 调用 onclose 方法(可被重写) self.onclose(event); }); // 监听错误事件 this.socketTask.onError((err) => { const errorEvent = { type: 'error', error: err, target: self }; self.onerror(errorEvent); }); // 将 close 属性设置为一个可调用函数,用于主动断开连接 // 同时也满足接口的 “回调类型” 定义 this.close = function(ev) { if (self.socketTask) { const code = ev && ev.code ? ev.code : 1000; const reason = ev && ev.reason ? ev.reason : ''; self.socketTask.close({ code, reason }); } // 如果作为回调函数,返回 null 即可(无实际意义) return null; }; } /** * 关闭事件处理方法(可被外部重写) * @param {Object} event - 关闭事件对象 */ onclose(event) { // 默认空实现,你可以在外部覆盖此方法 // console.log('连接已关闭', event); } /** * 错误事件处理方法(可被外部重写) * @param {Object} event - 错误事件对象 */ onerror(event) { // 默认空实现 // console.error('连接错误', event); } /** * 发送数据 * @param {string|ArrayBuffer|Blob} data - 要发送的数据 * @throws {Error} 如果 WebSocket 未初始化 */ send(data) { if (!this.socketTask) { throw new Error('WebSocket 未初始化,无法发送数据'); } this.socketTask.send({ data: data, success() { // 发送成功,可添加日志 }, fail(err) { // 发送失败,触发错误事件 const errorEvent = { type: 'error', error: err, target: this }; this.onerror(errorEvent); } }); } } // ========================================== // 初始化 SDK 实例 // ========================================== const client = new HaveADrink({ host: API_HOST, http_request: httpRequest, upload: uploadRequest, websocket: UniAppWebSocket, token: uni.getStorageSync('auth_token') || '' }) // ========================================== // 导出 SDK 实例(各页面直接 import 使用) // ========================================== export default client // ========================================== // Token 管理辅助函数 // ========================================== /** 登录成功后保存 token 并同步到 SDK */ export function saveAuthTokens(data) { if (data.token) { uni.setStorageSync('auth_token', data.token) client.setToken(data.token) } if (data.refresh_token) { uni.setStorageSync('refresh_token', data.refresh_token) } } /** 清除所有认证信息 */ export function clearAuth() { uni.removeStorageSync('auth_token') uni.removeStorageSync('refresh_token') uni.removeStorageSync('is_logged_in') uni.removeStorageSync('is_guest') uni.removeStorageSync('user_info') client.setToken('') } /** 尝试用 refresh_token 刷新 token(静默,不弹提示不跳转) */ export async function refreshAuthToken() { const refreshToken = uni.getStorageSync('refresh_token') if (!refreshToken) return false try { const res = await client.RefreshToken({ refresh_token: refreshToken, silent: true }) if (res && res.token) { saveAuthTokens(res) return true } return false } catch (e) { // 静默失败:refresh token 无效时清除残留凭证 uni.removeStorageSync('refresh_token') return false } } // ========================================== // 酒友圈 API(基于 HaveADrink SDK 真实接口) // ========================================== /** 获取动态信息流(游标分页) */ export async function GetCircleFeeds({ lastId = 0, pageSize = 10 } = {}) { const res = await client.GetFeeds({ lastId, pageSize }) return { data: res } } /** 点赞动态 */ export async function LikeFeed({ feedId }) { const res = await client.LikeFeed({ id: feedId }) return { data: res } } /** 取消点赞 */ export async function UnlikeFeed({ feedId }) { const res = await client.UnlikeFeed({ id: feedId }) return { data: res } } /** 获取动态评论 */ export async function GetFeedComments({ feedId }) { const res = await client.GetFeedComments({ id: feedId }) return { data: res } } /** 发表评论 */ export async function AddComment({ feedId, content }) { const res = await client.AddComment({ id: feedId, content }) return { data: res } } /** 发布动态 */ export async function PublishFeed({ text, images, recordId, visibility }) { const res = await client.PublishFeed({ text, images, recordId, visibility }) return { data: res } } /** 获取酒友列表 */ export async function GetFriends({ keyword = '' } = {}) { const res = await client.GetFriends({ keyword }) return { data: res } } /** 获取好友请求 */ export async function GetFriendRequests() { const res = await client.GetFriendRequests({}) return { data: res } } /** 发送好友请求 */ export async function SendFriendRequest({ userId, message = '' }) { const res = await client.SendFriendRequest({ userId, message }) return { data: res } } /** 接受好友请求 */ export async function AcceptFriendRequest({ requestId }) { const res = await client.AcceptFriendRequest({ id: requestId }) return { data: res } } /** 删除酒友 */ export async function RemoveFriend({ userId }) { const res = await client.RemoveFriend({ userId }) return { data: res } } /** 获取酒局列表 */ export async function GetEvents({ status = '' } = {}) { const res = await client.GetEvents({ status }) return { data: res } } /** 获取酒局详情 */ export async function GetEventDetail({ eventId }) { const res = await client.GetEventDetail({ id: eventId }) return { data: res } } /** 发起酒局 */ 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 async function JoinEvent({ eventId }) { const res = await client.JoinEvent({ id: eventId }) return { data: res } } /** 取消报名 */ export async function QuitEvent({ eventId }) { const res = await client.QuitEvent({ id: eventId }) return { data: res } } /** 酒局签到 */ export async function CheckInEvent({ eventId }) { const res = await client.CheckInEvent({ id: eventId }) return { data: res } } // ========================================== // 私信 API(基于 HaveADrink SDK 真实接口) // ========================================== /** 获取会话列表 */ export async function GetConversations() { const res = await client.GetConversations({}) return { data: res } } /** 获取历史消息(游标分页) */ export async function GetMessages({ conversationId, lastID = 0, pageSize = 20 } = {}) { const res = await client.GetMessages({ conversationId, lastID, pageSize }) return { data: res } } /** 标记会话已读 */ export async function MarkRead({ conversationId }) { const res = await client.MarkRead({ id: conversationId }) return { data: res } } /** 获取未读消息总数 */ 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 } }