This commit is contained in:
2025-09-05 22:43:39 +08:00
parent 913c2ba036
commit dba965344a
9 changed files with 432 additions and 396 deletions
+27 -50
View File
@@ -6,7 +6,7 @@
const ENV_CONFIG = {
// 开发环境
development: {
baseURL: 'https://dev-api.pet-ai.com',
baseURL: 'http://localhost:8888',
timeout: 30000
},
// 测试环境
@@ -57,32 +57,7 @@ export const HTTP_CONFIG = {
*/
export const NO_AUTH_APIS = [
// 用户认证相关
'/auth/login',
'/auth/register',
'/auth/wx-login',
'/auth/wx-phone-login', // 微信手机号登录
'/auth/refresh',
'/auth/reset-password', // 重置密码
// 短信验证相关
'/sms/send', // 发送短信验证码
'/sms/verify', // 验证短信验证码
// 公开浏览的接口
'/ai/knowledge', // AI知识库
'/adoption/pets', // 浏览待领养宠物
'/adoption/pets/search', // 搜索宠物
'/adoption/pets/filter', // 筛选宠物
'/adoption/organizations', // 领养机构列表
'/reviews', // 查看评价
'/reviews/*', // 查看特定对象的评价
// 系统相关
'/system/config', // 系统配置
'/system/version', // 版本信息
'/system/check-update', // 检查更新
'/common/regions', // 地区数据
'/common/upload-config' // 上传配置
'/wechat/user/mini/login', // 微信小程序登录
]
/**
@@ -90,30 +65,7 @@ export const NO_AUTH_APIS = [
* 这些接口在调用时如果未登录会自动跳转到登录页面
*/
export const AUTH_REQUIRED_APIS = [
// 用户个人信息相关
'/profile/info', // 获取用户信息
'/profile/update', // 更新用户信息
'/profile/avatar', // 更新头像
'/profile/stats', // 用户统计信息
// 宠物管理相关
'/pets/create', // 创建宠物
'/pets/update', // 更新宠物信息
'/pets/delete', // 删除宠物
'/pets/my', // 我的宠物列表
// 领养相关
'/adoption/apply', // 申请领养
'/adoption/my-applications', // 我的申请
'/adoption/my-pets', // 我发布的宠物
// 收藏和关注
'/favorites/*', // 收藏相关
'/follows/*', // 关注相关
// 消息和通知
'/messages/*', // 消息相关
'/notifications/*' // 通知相关
]
/**
@@ -142,6 +94,31 @@ export function checkApiAuth(url) {
return true
}
/**
* 检查接口是否需要强制登录
* @param {String} url 接口URL
* @returns {Boolean} 是否需要强制登录
*/
export function checkAuthRequiredApi(url) {
// 移除查询参数,只保留路径
const path = url.split('?')[0]
// 检查是否在需要强制登录的列表中
for (const authApi of AUTH_REQUIRED_APIS) {
// 支持通配符匹配
if (authApi.includes('*')) {
const regex = new RegExp('^' + authApi.replace(/\*/g, '.*') + '$')
if (regex.test(path)) {
return true
}
} else if (path === authApi || path.startsWith(authApi + '/')) {
return true
}
}
return false
}
/**
* 添加不需要鉴权的接口
* @param {String|Array} apis 接口路径或接口路径数组
+133 -22
View File
@@ -1,7 +1,109 @@
// HTTP请求配置和拦截器
// 基于uView-next的luch-request库实现
import { HTTP_CONFIG, checkApiAuth } from './config.js'
import { HTTP_CONFIG, checkApiAuth, AUTH_REQUIRED_APIS } from './config.js'
/**
* 清理认证数据
*/
function clearAuthData() {
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
uni.removeStorageSync('loginStep')
uni.removeStorageSync('wxLoginCode')
uni.removeStorageSync('wxUserInfo')
uni.removeStorageSync('loginDate')
}
/**
* 检查接口是否需要强制登录
* @param {String} url 接口URL
* @returns {Boolean} 是否需要强制登录
*/
function checkAuthRequiredApi(url) {
// 移除查询参数,只保留路径
const path = url.split('?')[0]
// 检查是否在需要强制登录的列表中
for (const authApi of AUTH_REQUIRED_APIS) {
// 支持通配符匹配
if (authApi.includes('*')) {
const regex = new RegExp('^' + authApi.replace(/\*/g, '.*') + '$')
if (regex.test(path)) {
return true
}
} else if (path === authApi || path.startsWith(authApi + '/')) {
return true
}
}
return false
}
/**
* 检查用户是否已完成登录
* @returns {Boolean} 是否已完成登录
*/
function isUserLoggedIn() {
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
const loginStep = uni.getStorageSync('loginStep')
// 检查是否有有效的token和完成的登录流程
return !!(token && (loginStep === 'profile_completed' || loginStep === 'phone_authed'))
}
/**
* 尝试自动登录
* @returns {Promise<Boolean>} 是否登录成功
*/
async function tryAutoLogin() {
try {
return await autoLogin()
} catch (error) {
console.error('自动登录失败:', error)
return false
}
}
/**
* 处理需要鉴权的情况
*/
function handleAuthRequired() {
const loginStep = uni.getStorageSync('loginStep')
// 根据当前登录状态决定跳转页面
let targetPage = '/pages/login/login'
let toastMessage = '请先登录'
switch (loginStep) {
case 'wx_logged':
targetPage = '/pages/auth/phone-auth'
toastMessage = '请完成手机号授权'
break
case 'phone_authed':
case 'phone_skipped':
targetPage = '/pages/profile/user-info?mode=setup'
toastMessage = '请完善个人信息'
break
default:
targetPage = '/pages/login/login'
toastMessage = '请先登录'
}
// 显示提示并跳转
uni.showToast({
title: toastMessage,
icon: 'none',
duration: 1500
})
setTimeout(() => {
uni.navigateTo({ url: targetPage }).catch(() => {
uni.reLaunch({ url: targetPage })
})
}, 1500)
}
/**
* 初始化HTTP配置
@@ -24,20 +126,34 @@ export default (vm) => {
config.data = config.data || {}
config.custom = config.custom || {}
// 自动检查接口是否需要鉴权(如果没有明确指定)
if (config.custom.auth === undefined) {
config.custom.auth = checkApiAuth(config.url)
}
// 根据custom参数中配置的是否需要token,添加对应的请求头
if (config.custom.auth) {
// 从本地存储获取token
// 检查是否为需要强制登录的接口
const isAuthRequiredApi = checkAuthRequiredApi(config.url)
// 如果是需要强制登录的接口,检查登录状态
if (isAuthRequiredApi) {
if (!isUserLoggedIn()) {
console.log('访问需要鉴权的接口但未完成登录,跳转到登录流程:', config.url)
handleAuthRequired()
return Promise.reject({
message: '需要登录',
code: 'AUTH_REQUIRED',
url: config.url
})
}
}
// 添加Authorization请求头
if (config.custom.auth !== false) {
const token = uni.getStorageSync(HTTP_CONFIG.storageKeys.token)
if (token) {
config.header.Authorization = `Bearer ${token}`
} else {
// 如果需要token但没有token,可以跳转到登录页
console.warn('需要token但未找到,请先登录')
}
}
@@ -52,6 +168,11 @@ export default (vm) => {
return config
}, config => {
// 请求错误处理
// 如果是AUTH_REQUIRED错误,不显示loading
if (config.code === 'AUTH_REQUIRED') {
// 不显示loading,因为会跳转页面
return Promise.reject(config)
}
return Promise.reject(config)
})
@@ -76,13 +197,9 @@ export default (vm) => {
// 特殊状态码处理
if (data.code === 401) {
// token过期,清除本地token并跳转到登录页
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
uni.reLaunch({
url: HTTP_CONFIG.loginPage
})
// token过期,清除本地认证信息并跳转到登录流程
clearAuthData()
handleAuthRequired()
return Promise.reject(data)
}
@@ -116,14 +233,8 @@ export default (vm) => {
break
case 401:
errorMessage = '未授权,请重新登录'
// 清除本地认证信息
uni.removeStorageSync(HTTP_CONFIG.storageKeys.token)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.refreshToken)
uni.removeStorageSync(HTTP_CONFIG.storageKeys.userInfo)
// 跳转到登录页
uni.reLaunch({
url: HTTP_CONFIG.loginPage
})
clearAuthData()
handleAuthRequired()
break
case 403:
errorMessage = '拒绝访问'