1091 lines
No EOL
30 KiB
JavaScript
1091 lines
No EOL
30 KiB
JavaScript
// API客户端工具类 - 真实API版本
|
||
const config = require('../config/config.js');
|
||
|
||
class ApiClient {
|
||
constructor() {
|
||
this.baseUrl = config.api.baseUrl;
|
||
this.timeout = config.api.timeout || 15000;
|
||
this.token = null;
|
||
|
||
// 自动从本地存储获取token
|
||
try {
|
||
const userInfo = wx.getStorageSync('userInfo');
|
||
if (userInfo && userInfo.token) {
|
||
this.token = userInfo.token;
|
||
console.log('API客户端初始化,已加载token');
|
||
}
|
||
} catch (error) {
|
||
console.error('初始化时获取token失败:', error);
|
||
}
|
||
|
||
// 请求拦截器
|
||
this.requestInterceptors = [];
|
||
this.responseInterceptors = [];
|
||
|
||
console.log('API客户端初始化:', {
|
||
baseUrl: this.baseUrl,
|
||
timeout: this.timeout,
|
||
configLoaded: !!config?.api
|
||
});
|
||
}
|
||
|
||
// 设置token
|
||
setToken(token) {
|
||
this.token = token;
|
||
console.log('Token已设置');
|
||
}
|
||
|
||
// 清除token
|
||
clearToken() {
|
||
this.token = null;
|
||
console.log('Token已清除');
|
||
}
|
||
|
||
// 获取token
|
||
getToken() {
|
||
// 如果没有token,尝试从存储中获取
|
||
if (!this.token) {
|
||
try {
|
||
const userInfo = wx.getStorageSync('userInfo');
|
||
if (userInfo && userInfo.token) {
|
||
this.token = userInfo.token;
|
||
console.log('从存储中获取token成功,用户customId:', userInfo.user?.customId);
|
||
} else {
|
||
console.log('存储中没有找到有效的token');
|
||
}
|
||
} catch (error) {
|
||
console.error('从存储获取token失败:', error);
|
||
}
|
||
}
|
||
return this.token;
|
||
}
|
||
|
||
// 获取设备信息
|
||
async getDeviceInfo() {
|
||
try {
|
||
// 使用新的API替代废弃的wx.getSystemInfoSync
|
||
const [windowInfo, deviceInfo, appBaseInfo] = await Promise.all([
|
||
this.getWindowInfo(),
|
||
this.getDeviceInfo_new(),
|
||
this.getAppBaseInfo()
|
||
]);
|
||
|
||
const systemInfo = { ...windowInfo, ...deviceInfo, ...appBaseInfo };
|
||
|
||
return {
|
||
deviceId: deviceInfo.deviceId || systemInfo.deviceId || 'unknown',
|
||
deviceModel: deviceInfo.model || systemInfo.model || 'unknown',
|
||
deviceType: 'miniprogram', // 标识为小程序
|
||
appVersion: config?.appVersion || '1.0.0',
|
||
platform: deviceInfo.platform || systemInfo.platform || 'unknown',
|
||
system: deviceInfo.system || systemInfo.system || 'unknown'
|
||
};
|
||
} catch (error) {
|
||
console.error('获取设备信息失败,使用兜底方案:', error);
|
||
// 兜底使用旧API
|
||
try {
|
||
const systemInfo = wx.getSystemInfoSync();
|
||
return {
|
||
deviceId: systemInfo.deviceId || 'unknown',
|
||
deviceModel: systemInfo.model || 'unknown',
|
||
deviceType: 'miniprogram',
|
||
appVersion: config?.appVersion || '1.0.0',
|
||
platform: systemInfo.platform || 'unknown',
|
||
system: systemInfo.system || 'unknown'
|
||
};
|
||
} catch (fallbackError) {
|
||
console.error('兜底方案也失败:', fallbackError);
|
||
return {
|
||
deviceId: 'unknown',
|
||
deviceModel: 'unknown',
|
||
deviceType: 'miniprogram',
|
||
appVersion: config?.appVersion || '1.0.0',
|
||
platform: 'unknown',
|
||
system: 'unknown'
|
||
};
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取窗口信息
|
||
getWindowInfo() {
|
||
return new Promise((resolve) => {
|
||
try {
|
||
const windowInfo = wx.getWindowInfo();
|
||
resolve(windowInfo);
|
||
} catch (error) {
|
||
resolve({});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 获取设备信息(新API)
|
||
getDeviceInfo_new() {
|
||
return new Promise((resolve) => {
|
||
try {
|
||
const deviceInfo = wx.getDeviceInfo();
|
||
resolve(deviceInfo);
|
||
} catch (error) {
|
||
resolve({});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 获取应用基础信息
|
||
getAppBaseInfo() {
|
||
return new Promise((resolve) => {
|
||
try {
|
||
const appBaseInfo = wx.getAppBaseInfo();
|
||
resolve(appBaseInfo);
|
||
} catch (error) {
|
||
resolve({});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 编码查询参数
|
||
encodeParams(params) {
|
||
if (!params) return '';
|
||
|
||
return Object.keys(params)
|
||
.filter(key => params[key] !== null && params[key] !== undefined)
|
||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||
.join('&');
|
||
}
|
||
|
||
// 通用请求方法
|
||
async request(method, url, data = null, options = {}) {
|
||
const token = this.getToken();
|
||
const fullUrl = url.startsWith('http') ? url : `${this.baseUrl}${url}`;
|
||
|
||
console.log('发起API请求:', {
|
||
method,
|
||
url: fullUrl,
|
||
hasToken: !!token,
|
||
hasData: !!data
|
||
});
|
||
|
||
const requestOptions = {
|
||
url: fullUrl,
|
||
method: method.toUpperCase(),
|
||
timeout: this.timeout,
|
||
header: {
|
||
'Content-Type': 'application/json',
|
||
'X-Client-Version': `FindMe-MiniProgram/${config?.appVersion || '1.0.0'}`,
|
||
...options.headers
|
||
}
|
||
};
|
||
|
||
// 添加认证头
|
||
if (token) {
|
||
requestOptions.header['Authorization'] = `Bearer ${token}`;
|
||
}
|
||
|
||
// 处理请求数据
|
||
if (data) {
|
||
if (method.toUpperCase() === 'GET') {
|
||
// GET请求,将数据转换为查询参数
|
||
const queryString = this.encodeParams(data);
|
||
if (queryString) {
|
||
requestOptions.url += (requestOptions.url.includes('?') ? '&' : '?') + queryString;
|
||
}
|
||
} else {
|
||
// 其他请求,将数据作为请求体
|
||
requestOptions.data = data;
|
||
}
|
||
}
|
||
|
||
return new Promise((resolve, reject) => {
|
||
wx.request({
|
||
...requestOptions,
|
||
success: (res) => {
|
||
console.log('API响应:', {
|
||
url: fullUrl,
|
||
statusCode: res.statusCode,
|
||
data: res.data
|
||
});
|
||
|
||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||
// 检查业务状态码
|
||
if (res.data && typeof res.data === 'object') {
|
||
if (res.data.code === 0 || res.data.code === 200) {
|
||
resolve(res.data);
|
||
} else {
|
||
reject(new Error(`HTTP ${res.statusCode}: ${res.data.message || 'request:ok'}`));
|
||
}
|
||
} else {
|
||
resolve(res.data);
|
||
}
|
||
} else {
|
||
reject(new Error(`HTTP ${res.statusCode}: ${res.data?.message || 'request failed'}`));
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
console.error('API请求失败:', error);
|
||
reject(new Error(error.errMsg || '网络请求失败'));
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
// GET请求
|
||
async get(url, params = null, options = {}) {
|
||
return this.request('GET', url, params, options);
|
||
}
|
||
|
||
// POST请求
|
||
async post(url, data = null, options = {}) {
|
||
return this.request('POST', url, data, options);
|
||
}
|
||
|
||
// PUT请求
|
||
async put(url, data = null, options = {}) {
|
||
return this.request('PUT', url, data, options);
|
||
}
|
||
|
||
// DELETE请求
|
||
async delete(url, data = null, options = {}) {
|
||
return this.request('DELETE', url, data, options);
|
||
}
|
||
|
||
// 🔥 用户认证相关接口
|
||
|
||
// 发送验证码(生产级别实现,支持重试)
|
||
async sendVerifyCode(phone, retryCount = 0) {
|
||
try {
|
||
const response = await this.post('/api/v1/user/send-verify-code', { phone });
|
||
return response;
|
||
} catch (error) {
|
||
console.error('发送验证码失败:', error);
|
||
|
||
// 网络错误时自动重试(最多重试2次)
|
||
if (retryCount < 2 && this.isNetworkError(error)) {
|
||
console.log(`网络错误,正在重试... (${retryCount + 1}/2)`);
|
||
await this.delay(1000 * (retryCount + 1)); // 递增延迟
|
||
return this.sendVerifyCode(phone, retryCount + 1);
|
||
}
|
||
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 判断是否为网络错误
|
||
isNetworkError(error) {
|
||
return error.message && (
|
||
error.message.includes('网络') ||
|
||
error.message.includes('timeout') ||
|
||
error.message.includes('Network') ||
|
||
error.message.includes('Failed to fetch')
|
||
);
|
||
}
|
||
|
||
// 延迟函数
|
||
delay(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
// 用户登录
|
||
async login(phone, verifyCode) {
|
||
try {
|
||
// 获取设备信息
|
||
const deviceInfo = await this.getDeviceInfo();
|
||
|
||
const response = await this.post('/api/v1/user/login', {
|
||
phone,
|
||
verifyCode,
|
||
source: 'miniprogram', // 根据文档使用source字段(小写)
|
||
deviceId: deviceInfo.deviceId,
|
||
deviceType: deviceInfo.deviceType,
|
||
appVersion: deviceInfo.appVersion
|
||
});
|
||
|
||
// 不在这里保存数据,让认证管理器统一处理
|
||
console.log('登录API调用成功,设备信息:', deviceInfo);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('登录失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 微信登录
|
||
async wechatLogin(code, userInfo = null) {
|
||
try {
|
||
// 获取设备信息
|
||
const deviceInfo = await this.getDeviceInfo();
|
||
|
||
const loginData = {
|
||
code,
|
||
source: 'miniprogram', // 根据文档使用source字段(小写)
|
||
deviceId: deviceInfo.deviceId,
|
||
deviceType: deviceInfo.deviceType,
|
||
appVersion: deviceInfo.appVersion
|
||
};
|
||
|
||
if (userInfo) {
|
||
loginData.userInfo = userInfo;
|
||
}
|
||
|
||
const response = await this.post('/api/v1/user/wechat-login', loginData);
|
||
|
||
// 不在这里保存数据,让认证管理器统一处理
|
||
console.log('微信登录API调用成功,设备信息:', deviceInfo);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('微信登录失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取用户信息
|
||
async getUserInfo() {
|
||
try {
|
||
const response = await this.get('/api/v1/user/info');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取用户信息失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🔥 账号同步相关接口
|
||
|
||
// 绑定手机号
|
||
async bindPhone(phone, verifyCode, autoMerge = false) {
|
||
try {
|
||
// 获取设备信息
|
||
const deviceInfo = await this.getDeviceInfo();
|
||
|
||
const response = await this.post('/api/v1/user/bind-phone', {
|
||
phone,
|
||
verifyCode,
|
||
deviceId: deviceInfo.deviceId, // 根据文档保留deviceId
|
||
autoMerge
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('绑定手机号失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 检测可合并账号
|
||
async detectMerge(customId, autoMerge = false) {
|
||
try {
|
||
const response = await this.post('/api/v1/user/detect-merge', {
|
||
userCustomId: customId, // 根据文档使用userCustomId参数
|
||
autoMerge
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('检测可合并账号失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 合并账号
|
||
async mergeAccount(primaryCustomId, secondaryCustomId, mergeReason = '用户手动合并') {
|
||
try {
|
||
const response = await this.post('/api/v1/user/merge-account', {
|
||
primaryUserCustomId: primaryCustomId, // 根据文档使用primaryUserCustomId参数
|
||
secondaryUserCustomId: secondaryCustomId, // 根据文档使用secondaryUserCustomId参数
|
||
mergeReason
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('合并账号失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 更新用户资料
|
||
async updateUserProfile(data) {
|
||
try {
|
||
const response = await this.put('/api/v1/user/profile', data);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新用户资料失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取用户设置
|
||
async getUserSetting() {
|
||
try {
|
||
const response = await this.get('/api/v1/user/setting');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取用户设置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 刷新token
|
||
async refreshToken(refreshToken) {
|
||
try {
|
||
const response = await this.post('/api/v1/auth/refresh', {
|
||
refresh_token: refreshToken
|
||
});
|
||
|
||
if (response && response.code === 200 && response.data) {
|
||
this.setToken(response.data.access_token);
|
||
|
||
// 更新本地存储 - 保持字段名一致性
|
||
const userInfo = wx.getStorageSync('userInfo') || {};
|
||
userInfo.token = response.data.access_token;
|
||
userInfo.refreshToken = response.data.refresh_token; // 保持一致的字段名
|
||
userInfo.expiresAt = response.data.expires_at * 1000; // 转换为毫秒时间戳
|
||
wx.setStorageSync('userInfo', userInfo);
|
||
}
|
||
|
||
return response;
|
||
} catch (error) {
|
||
console.error('刷新token失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 用户登出
|
||
async logout() {
|
||
try {
|
||
const response = await this.post('/api/v1/user/logout');
|
||
|
||
// 清除本地token和用户信息
|
||
this.clearToken();
|
||
wx.removeStorageSync('userInfo');
|
||
|
||
return response;
|
||
} catch (error) {
|
||
console.error('登出失败:', error);
|
||
// 即使登出失败,也清除本地信息
|
||
this.clearToken();
|
||
wx.removeStorageSync('userInfo');
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🔥 位置相关接口
|
||
|
||
// 更新位置
|
||
async updateLocation(locationData) {
|
||
try {
|
||
const response = await this.post('/api/v1/location/update', locationData);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新位置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取好友位置
|
||
async getFriendsLocation() {
|
||
try {
|
||
const response = await this.get('/api/v1/location/friends');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取好友位置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取用户位置
|
||
async getUserLocation(userId) {
|
||
try {
|
||
const response = await this.get(`/api/v1/location/user/${userId}`);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取用户位置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取附近用户
|
||
async getNearbyUsers(params = {}) {
|
||
try {
|
||
const response = await this.get('/api/v1/location/nearby', params);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取附近用户失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取位置历史
|
||
async getLocationHistory(params = {}) {
|
||
try {
|
||
const response = await this.get('/api/v1/location/history', params);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取位置历史失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取位置隐私设置
|
||
async getLocationPrivacy() {
|
||
try {
|
||
const response = await this.get('/api/v1/location/privacy');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取位置隐私设置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 更新位置隐私设置
|
||
async updateLocationPrivacy(privacyData) {
|
||
try {
|
||
const response = await this.put('/api/v1/location/privacy', privacyData);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新位置隐私设置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取天气信息
|
||
async getWeatherInfo(latitude, longitude) {
|
||
try {
|
||
const response = await this.get('/api/v1/location/weather', {
|
||
latitude,
|
||
longitude
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取天气信息失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🔥 社交相关接口(根据好友功能API手册完整实现)
|
||
|
||
// 获取好友列表
|
||
async getFriends() {
|
||
try {
|
||
const response = await this.get('/api/v1/social/friends');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取好友列表失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取好友详细信息
|
||
async getFriendDetail(customId, lat = null, lng = null) {
|
||
try {
|
||
let url = `/api/v1/social/friends/${customId}/detail`;
|
||
const params = {};
|
||
if (lat !== null && lng !== null) {
|
||
params.lat = lat;
|
||
params.lng = lng;
|
||
}
|
||
|
||
const response = await this.get(url, params);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取好友详情失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 搜索用户
|
||
async searchUsers(query, searchType = 'all', page = 1, pageSize = 10) {
|
||
try {
|
||
if (!query || typeof query !== 'string') {
|
||
throw new Error('搜索关键词不能为空');
|
||
}
|
||
|
||
const validSearchTypes = ['nickname', 'custom_id', 'phone', 'all'];
|
||
if (!validSearchTypes.includes(searchType)) {
|
||
throw new Error('无效的搜索类型');
|
||
}
|
||
|
||
if (pageSize < 1 || pageSize > 50) {
|
||
throw new Error('每页数量必须在1-50之间');
|
||
}
|
||
|
||
const response = await this.post('/api/v1/social/users/search', {
|
||
query: query.trim(),
|
||
searchType: searchType,
|
||
page: Math.max(1, page),
|
||
pageSize: Math.min(50, pageSize)
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('搜索用户失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 添加好友
|
||
async addFriend(targetId, message = '') {
|
||
try {
|
||
if (!targetId || typeof targetId !== 'string') {
|
||
throw new Error('目标用户ID不能为空');
|
||
}
|
||
|
||
if (message && message.length > 100) {
|
||
throw new Error('好友申请留言不能超过100字符');
|
||
}
|
||
|
||
const response = await this.post('/api/v1/social/friend/add', {
|
||
targetId: targetId,
|
||
message: message.trim()
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('添加好友失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取好友请求列表
|
||
async getFriendRequests() {
|
||
try {
|
||
const response = await this.get('/api/v1/social/friend/requests');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取好友请求失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取好友请求数量
|
||
async getFriendRequestsCount() {
|
||
try {
|
||
const response = await this.get('/api/v1/social/friend/requests/count');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取好友请求数量失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 处理好友请求
|
||
async handleFriendRequest(requestId, accept) {
|
||
try {
|
||
if (!requestId || typeof requestId !== 'string') {
|
||
throw new Error('请求ID不能为空');
|
||
}
|
||
|
||
if (typeof accept !== 'boolean') {
|
||
throw new Error('accept参数必须是布尔值');
|
||
}
|
||
|
||
const response = await this.post('/api/v1/social/friend/handle-request', {
|
||
requestId: requestId,
|
||
accept: accept
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('处理好友请求失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 批量处理好友请求
|
||
async batchHandleFriendRequests(requestIds, accept) {
|
||
try {
|
||
if (!Array.isArray(requestIds) || requestIds.length === 0) {
|
||
throw new Error('请求ID列表不能为空');
|
||
}
|
||
|
||
if (requestIds.length > 20) {
|
||
throw new Error('一次最多处理20个请求');
|
||
}
|
||
|
||
if (typeof accept !== 'boolean') {
|
||
throw new Error('accept参数必须是布尔值');
|
||
}
|
||
|
||
const response = await this.post('/api/v1/social/friend/batch-handle-requests', {
|
||
requestIds: requestIds,
|
||
accept: accept
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('批量处理好友请求失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 更新好友关系
|
||
async updateFriendRelation(friendId, updateData) {
|
||
try {
|
||
if (!friendId) {
|
||
throw new Error('好友ID不能为空');
|
||
}
|
||
|
||
const validRelations = ['情侣', '家人', '兄弟', '姐妹', '闺蜜', '死党'];
|
||
if (updateData.relation && !validRelations.includes(updateData.relation)) {
|
||
throw new Error('无效的关系标签');
|
||
}
|
||
|
||
if (updateData.remark && updateData.remark.length > 50) {
|
||
throw new Error('好友备注不能超过50字符');
|
||
}
|
||
|
||
const response = await this.put('/api/v1/social/friend', {
|
||
friendId: friendId,
|
||
...updateData
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新好友关系失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 删除好友
|
||
async deleteFriend(friendCustomId) {
|
||
try {
|
||
if (!friendCustomId || typeof friendCustomId !== 'string') {
|
||
throw new Error('好友CustomID不能为空');
|
||
}
|
||
|
||
const response = await this.delete(`/api/v1/social/friend/${friendCustomId}`);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('删除好友失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 拉取好友通知
|
||
async pullFriendNotifications(limit = 10) {
|
||
try {
|
||
if (limit < 1 || limit > 50) {
|
||
throw new Error('拉取数量必须在1-50之间');
|
||
}
|
||
|
||
const response = await this.get('/api/v1/social/friend/notifications/pull', {
|
||
limit: limit
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('拉取好友通知失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取通知统计
|
||
async getFriendNotificationStats() {
|
||
try {
|
||
const response = await this.get('/api/v1/social/friend/notifications/stats');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取通知统计失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取群组列表
|
||
async getGroups() {
|
||
try {
|
||
const response = await this.get('/api/v1/social/groups');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取群组列表失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取群组数量
|
||
async getGroupsCount() {
|
||
try {
|
||
const response = await this.get('/api/v1/social/groups/count');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取群组数量失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🔥 聊天相关接口(根据WebSocket即时通讯接口文档完整实现)
|
||
|
||
// 获取会话列表
|
||
async getConversations() {
|
||
try {
|
||
const response = await this.get('/api/v1/chat/conversations');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取会话列表失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取聊天历史消息 - 根据API文档修正参数格式
|
||
async getChatMessages(targetId, chatType, params = {}) {
|
||
try {
|
||
const queryParams = {
|
||
receiverId: targetId, // 使用receiverId而不是conversationId
|
||
chatType: chatType, // 聊天类型:0=单聊, 1=群聊
|
||
limit: params.limit || 20,
|
||
direction: params.direction || 'before',
|
||
lastMsgId: params.lastMsgId, // 分页参数
|
||
...params
|
||
};
|
||
|
||
const response = await this.get('/api/v1/chat/history', queryParams);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取聊天消息失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🚫 发送消息已废弃 - 必须使用WebSocket
|
||
async sendMessage(targetId, content, msgType = 'text', chatType = 0) {
|
||
console.error('❌ HTTP发送消息已废弃!根据API文档,所有消息发送必须通过WebSocket');
|
||
throw new Error('消息发送必须使用WebSocket,HTTP发送接口已废弃');
|
||
}
|
||
|
||
// 批量标记消息已读
|
||
async batchMarkRead(conversationId, messageIds) {
|
||
try {
|
||
const response = await this.post('/api/v1/chat/batch-read', {
|
||
conversationId: conversationId,
|
||
messageIds: messageIds
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('批量标记已读失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 全部标记已读
|
||
async markAllRead(conversationId) {
|
||
try {
|
||
const response = await this.post('/api/v1/chat/mark-all-read', {
|
||
conversationId: conversationId
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('全部标记已读失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取总未读数
|
||
async getTotalUnreadCount() {
|
||
try {
|
||
const response = await this.get('/api/v1/chat/unread/total');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取总未读数失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 更新会话设置
|
||
async updateConversationSettings(conversationId, settings) {
|
||
try {
|
||
const response = await this.put(`/api/v1/chat/conversation/${conversationId}`, settings);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新会话设置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 删除会话
|
||
async deleteConversation(conversationId) {
|
||
try {
|
||
const response = await this.delete(`/api/v1/chat/conversation/${conversationId}`);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('删除会话失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取聊天设置
|
||
async getChatSettings() {
|
||
try {
|
||
const response = await this.get('/api/v1/chat/settings');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('获取聊天设置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 更新聊天设置
|
||
async updateChatSettings(settings) {
|
||
try {
|
||
const response = await this.put('/api/v1/chat/settings', settings);
|
||
return response;
|
||
} catch (error) {
|
||
console.error('更新聊天设置失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 拉取离线消息
|
||
async pullOfflineMessages(lastSeqId, limit = 50) {
|
||
try {
|
||
const response = await this.get('/api/v1/chat/sync/pull', {
|
||
lastSeqId: lastSeqId,
|
||
limit: limit
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('拉取离线消息失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 确认消息状态
|
||
async ackMessageStatus(messageId, status, timestamp) {
|
||
try {
|
||
const response = await this.post('/api/v1/chat/sync/ack', {
|
||
messageId: messageId,
|
||
status: status,
|
||
timestamp: timestamp
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('确认消息状态失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 标记消息已读(兼容方法)
|
||
async markMessagesRead(conversationId, messageIds) {
|
||
try {
|
||
if (Array.isArray(messageIds) && messageIds.length > 0) {
|
||
return await this.batchMarkRead(conversationId, messageIds);
|
||
} else {
|
||
return await this.markAllRead(conversationId);
|
||
}
|
||
} catch (error) {
|
||
console.error('标记消息已读失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 发送虚拟弹幕
|
||
async sendDanmaku(content, color = '#FFFFFF', size = 1, duration = 5000, latitude, longitude, radius = 100) {
|
||
try {
|
||
if (!content || content.length > 255) {
|
||
throw new Error('弹幕内容不能为空且不能超过255字符');
|
||
}
|
||
|
||
const response = await this.post('/api/v1/chat/danmaku', {
|
||
content: content,
|
||
color: color,
|
||
size: size,
|
||
duration: duration,
|
||
latitude: latitude,
|
||
longitude: longitude,
|
||
radius: radius
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('发送弹幕失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 查询附近弹幕
|
||
async getNearbyDanmaku(latitude, longitude, radius = 1000) {
|
||
try {
|
||
const response = await this.get('/api/v1/chat/danmaku/nearby', {
|
||
latitude: latitude,
|
||
longitude: longitude,
|
||
radius: radius
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error('查询附近弹幕失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 查询表情包
|
||
async getEmojiPackages() {
|
||
try {
|
||
const response = await this.get('/api/v1/chat/emoji/packages');
|
||
return response;
|
||
} catch (error) {
|
||
console.error('查询表情包失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🔥 文件上传接口
|
||
|
||
// 文件上传
|
||
async uploadFile(filePath, fileType = 'image', usageType = 'chat') {
|
||
try {
|
||
console.log('上传文件:', { filePath, fileType, usageType });
|
||
|
||
return new Promise((resolve, reject) => {
|
||
wx.uploadFile({
|
||
url: `${this.baseUrl}/api/v1/file/upload`,
|
||
filePath: filePath,
|
||
name: 'file',
|
||
formData: {
|
||
file_type: fileType,
|
||
usage_type: usageType
|
||
},
|
||
header: {
|
||
'Authorization': this.token ? `Bearer ${this.token}` : '',
|
||
'X-Client-Version': `FindMe-MiniProgram/${config?.appVersion || '1.0.0'}`
|
||
},
|
||
success: (res) => {
|
||
console.log('文件上传成功:', res);
|
||
try {
|
||
const data = JSON.parse(res.data);
|
||
if (data.code === 0) {
|
||
resolve(data);
|
||
} else {
|
||
reject(new Error(data.message || '上传失败'));
|
||
}
|
||
} catch (error) {
|
||
reject(new Error('响应解析失败'));
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
console.error('文件上传失败:', error);
|
||
reject(new Error(error.errMsg || '上传失败'));
|
||
}
|
||
});
|
||
});
|
||
} catch (error) {
|
||
console.error('文件上传异常:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 🔥 测试token有效性
|
||
async testTokenValidity() {
|
||
try {
|
||
const token = this.getToken();
|
||
|
||
console.log('开始测试token有效性:', {
|
||
hasToken: !!token,
|
||
tokenLength: token ? token.length : 0,
|
||
tokenStart: token ? token.substring(0, 20) + '...' : 'null'
|
||
});
|
||
|
||
if (!token) {
|
||
throw new Error('没有找到token');
|
||
}
|
||
|
||
const response = await this.getUserInfo();
|
||
console.log('Token有效性测试成功:', response);
|
||
return true;
|
||
|
||
} catch (error) {
|
||
console.error('Token有效性测试失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建全局实例
|
||
const apiClient = new ApiClient();
|
||
|
||
module.exports = apiClient;
|