Files
server/miniprogram/utils/api.js
2025-11-02 19:34:16 +08:00

489 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* API服务工具
* 统一处理网络请求、错误处理、token管理等
*/
const app = getApp();
// API配置
const API_CONFIG = {
baseUrl: 'https://api.xiaozhi.com', // 生产环境API地址
timeout: 10000,
retryCount: 3,
retryDelay: 1000
};
// 请求状态码
const STATUS_CODE = {
SUCCESS: 200,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
SERVER_ERROR: 500,
NETWORK_ERROR: -1
};
// 错误消息映射
const ERROR_MESSAGES = {
[STATUS_CODE.UNAUTHORIZED]: '登录已过期,请重新登录',
[STATUS_CODE.FORBIDDEN]: '没有访问权限',
[STATUS_CODE.NOT_FOUND]: '请求的资源不存在',
[STATUS_CODE.SERVER_ERROR]: '服务器内部错误',
[STATUS_CODE.NETWORK_ERROR]: '网络连接失败,请检查网络设置'
};
/**
* 基础请求方法
* @param {Object} options 请求配置
* @returns {Promise} 请求结果
*/
function request(options) {
return new Promise((resolve, reject) => {
const {
url,
method = 'GET',
data = {},
header = {},
needAuth = true,
showLoading = true,
loadingText = '加载中...',
retryCount = 0
} = options;
// 显示加载提示
if (showLoading) {
wx.showLoading({
title: loadingText,
mask: true
});
}
// 构建请求头
const requestHeader = {
'Content-Type': 'application/json',
...header
};
// 添加认证token
if (needAuth && app.globalData.token) {
requestHeader['Authorization'] = `Bearer ${app.globalData.token}`;
}
// 发起请求
wx.request({
url: `${API_CONFIG.baseUrl}${url}`,
method,
data,
header: requestHeader,
timeout: API_CONFIG.timeout,
success: (res) => {
if (showLoading) {
wx.hideLoading();
}
const { statusCode, data: responseData } = res;
// 请求成功
if (statusCode === STATUS_CODE.SUCCESS) {
if (responseData.code === 0) {
resolve(responseData);
} else {
// 业务错误
handleBusinessError(responseData, reject);
}
} else {
// HTTP错误
handleHttpError(statusCode, reject, options, retryCount);
}
},
fail: (error) => {
if (showLoading) {
wx.hideLoading();
}
console.error('Request failed:', error);
// 网络错误重试
if (retryCount < API_CONFIG.retryCount) {
setTimeout(() => {
request({
...options,
retryCount: retryCount + 1
}).then(resolve).catch(reject);
}, API_CONFIG.retryDelay);
} else {
handleNetworkError(error, reject);
}
}
});
});
}
/**
* 处理业务错误
* @param {Object} responseData 响应数据
* @param {Function} reject 拒绝函数
*/
function handleBusinessError(responseData, reject) {
const { code, message } = responseData;
// 特殊错误码处理
switch (code) {
case 401:
// token过期清除登录状态
app.clearLoginStatus();
wx.showToast({
title: '登录已过期',
icon: 'none'
});
// 跳转到登录页
wx.navigateTo({
url: '/pages/login/login'
});
break;
case 403:
wx.showToast({
title: '权限不足',
icon: 'none'
});
break;
default:
wx.showToast({
title: message || '操作失败',
icon: 'none'
});
}
reject({
code,
message,
type: 'business'
});
}
/**
* 处理HTTP错误
* @param {Number} statusCode 状态码
* @param {Function} reject 拒绝函数
* @param {Object} options 请求选项
* @param {Number} retryCount 重试次数
*/
function handleHttpError(statusCode, reject, options, retryCount) {
const message = ERROR_MESSAGES[statusCode] || '请求失败';
// 401错误特殊处理
if (statusCode === STATUS_CODE.UNAUTHORIZED) {
app.clearLoginStatus();
wx.navigateTo({
url: '/pages/login/login'
});
}
// 5xx错误重试
if (statusCode >= 500 && retryCount < API_CONFIG.retryCount) {
setTimeout(() => {
request({
...options,
retryCount: retryCount + 1
}).then(resolve).catch(reject);
}, API_CONFIG.retryDelay);
return;
}
wx.showToast({
title: message,
icon: 'none'
});
reject({
code: statusCode,
message,
type: 'http'
});
}
/**
* 处理网络错误
* @param {Object} error 错误对象
* @param {Function} reject 拒绝函数
*/
function handleNetworkError(error, reject) {
wx.showToast({
title: '网络连接失败',
icon: 'none'
});
reject({
code: STATUS_CODE.NETWORK_ERROR,
message: '网络连接失败',
type: 'network',
error
});
}
/**
* GET请求
* @param {String} url 请求地址
* @param {Object} params 请求参数
* @param {Object} options 其他选项
* @returns {Promise} 请求结果
*/
function get(url, params = {}, options = {}) {
const queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
const requestUrl = queryString ? `${url}?${queryString}` : url;
return request({
url: requestUrl,
method: 'GET',
...options
});
}
/**
* POST请求
* @param {String} url 请求地址
* @param {Object} data 请求数据
* @param {Object} options 其他选项
* @returns {Promise} 请求结果
*/
function post(url, data = {}, options = {}) {
return request({
url,
method: 'POST',
data,
...options
});
}
/**
* PUT请求
* @param {String} url 请求地址
* @param {Object} data 请求数据
* @param {Object} options 其他选项
* @returns {Promise} 请求结果
*/
function put(url, data = {}, options = {}) {
return request({
url,
method: 'PUT',
data,
...options
});
}
/**
* DELETE请求
* @param {String} url 请求地址
* @param {Object} options 其他选项
* @returns {Promise} 请求结果
*/
function del(url, options = {}) {
return request({
url,
method: 'DELETE',
...options
});
}
/**
* 文件上传
* @param {String} url 上传地址
* @param {String} filePath 文件路径
* @param {Object} formData 表单数据
* @param {Object} options 其他选项
* @returns {Promise} 上传结果
*/
function upload(url, filePath, formData = {}, options = {}) {
return new Promise((resolve, reject) => {
const {
name = 'file',
header = {},
needAuth = true,
showLoading = true
} = options;
if (showLoading) {
wx.showLoading({
title: '上传中...',
mask: true
});
}
// 构建请求头
const requestHeader = { ...header };
if (needAuth && app.globalData.token) {
requestHeader['Authorization'] = `Bearer ${app.globalData.token}`;
}
wx.uploadFile({
url: `${API_CONFIG.baseUrl}${url}`,
filePath,
name,
formData,
header: requestHeader,
success: (res) => {
if (showLoading) {
wx.hideLoading();
}
try {
const data = JSON.parse(res.data);
if (data.code === 0) {
resolve(data);
} else {
handleBusinessError(data, reject);
}
} catch (error) {
reject({
code: -1,
message: '响应数据解析失败',
type: 'parse',
error
});
}
},
fail: (error) => {
if (showLoading) {
wx.hideLoading();
}
handleNetworkError(error, reject);
}
});
});
}
// 会员相关API
const memberAPI = {
// 获取用户信息
getUserInfo() {
return get('/api/member/user/info');
},
// 微信登录
wxLogin(code) {
return post('/api/member/auth/wx-login', { code }, { needAuth: false });
},
// 获取会员信息
getMemberInfo() {
return get('/api/member/info');
},
// 绑定会员
bindMember(memberData) {
return post('/api/member/bind', memberData);
},
// 解绑会员
unbindMember() {
return post('/api/member/unbind');
},
// 获取会员权益
getMemberBenefits() {
return get('/api/member/benefits');
},
// 验证会员权益
verifyBenefit(benefitType) {
return post('/api/member/verify-benefit', { benefitType });
},
// 获取会员等级配置
getMemberLevels() {
return get('/api/member/levels');
},
// 升级会员
upgradeMember(targetLevel) {
return post('/api/member/upgrade', { targetLevel });
},
// 获取绑定历史
getBindHistory(page = 1, size = 10) {
return get('/api/member/bind-history', { page, size });
},
// 获取权益使用记录
getBenefitUsage(page = 1, size = 10) {
return get('/api/member/benefit-usage', { page, size });
},
// 邀请好友
inviteFriend(inviteCode) {
return post('/api/member/invite', { inviteCode });
},
// 获取邀请记录
getInviteHistory(page = 1, size = 10) {
return get('/api/member/invite-history', { page, size });
}
};
// 支付相关API
const paymentAPI = {
// 创建支付订单
createOrder(orderData) {
return post('/api/payment/create-order', orderData);
},
// 查询订单状态
queryOrder(orderId) {
return get(`/api/payment/order/${orderId}`);
},
// 取消订单
cancelOrder(orderId) {
return post(`/api/payment/order/${orderId}/cancel`);
},
// 申请退款
requestRefund(orderId, refundData) {
return post(`/api/payment/order/${orderId}/refund`, refundData);
},
// 查询退款状态
queryRefund(refundId) {
return get(`/api/payment/refund/${refundId}`);
}
};
// 通用API
const commonAPI = {
// 获取系统配置
getSystemConfig() {
return get('/api/common/config', {}, { needAuth: false });
},
// 上传图片
uploadImage(filePath) {
return upload('/api/common/upload/image', filePath);
},
// 发送验证码
sendVerifyCode(phone) {
return post('/api/common/send-verify-code', { phone }, { needAuth: false });
},
// 验证验证码
verifyCode(phone, code) {
return post('/api/common/verify-code', { phone, code }, { needAuth: false });
}
};
module.exports = {
request,
get,
post,
put,
del: del,
upload,
memberAPI,
paymentAPI,
commonAPI,
API_CONFIG,
STATUS_CODE
};