From 2a16d114745354cfb3fe3046e4e42a1fe2ee417e Mon Sep 17 00:00:00 2001 From: "97694732@qq.com" Date: Wed, 3 Jun 2026 17:58:44 +0800 Subject: [PATCH] =?UTF-8?q?401=E8=87=AA=E5=8A=A8=E9=87=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95:=E4=BB=93=E5=BA=93/=E8=B4=A7=E4=BD=8D=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A4=B1=E8=B4=A5=E8=87=AA=E5=8A=A8=E7=94=A8=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E8=B4=A6=E5=8F=B7=E7=99=BB=E5=BD=95=E5=90=8E=E9=87=8D?= =?UTF-8?q?=E8=AF=95,=E5=A4=B1=E8=B4=A5=E5=BC=B9=E7=AA=97=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E7=99=BB=E5=BD=95=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/login/login.vue | 2 + pages/upload/upload.vue | 29 +++++- utils/api.js | 190 +++++++++++++++++++++++++++++----------- 3 files changed, 168 insertions(+), 53 deletions(-) diff --git a/pages/login/login.vue b/pages/login/login.vue index ad62144..8f0444b 100644 --- a/pages/login/login.vue +++ b/pages/login/login.vue @@ -264,9 +264,11 @@ export default { const data = response.data // 登录成功,保存数据 uni.setStorageSync('token', data.access_token) + uni.setStorageSync('psi_token', data.access_token) uni.setStorageSync('openId', data.openid || '') uni.setStorageSync('userId', data.userId || '') uni.setStorageSync('phoneNumber', data.phoneNumber || this.formData.account) + uni.setStorageSync('remembered_password', this.formData.password) uni.setStorageSync('nickName', data.nickName || '') uni.setStorageSync('lastSubmitTime', Date.now()) uni.setStorageSync('agreedPrivacy', true) diff --git a/pages/upload/upload.vue b/pages/upload/upload.vue index d0ddc36..ed4f601 100644 --- a/pages/upload/upload.vue +++ b/pages/upload/upload.vue @@ -1123,7 +1123,6 @@ export default { try { const res = await getWarehouseList({ status: 1, page: 1, page_size: 100 }) console.log('仓库列表原始响应:', JSON.stringify(res)) - // 兼容多种响应格式 const list = res.data?.list || res.data?.records || res.list || res.records || [] if (list.length > 0) { this.popupWarehouseList = list @@ -1134,6 +1133,20 @@ export default { } } catch (e) { console.error('加载仓库失败:', e) + const errMsg = e.message || String(e) + if (errMsg.includes('NEED_LOGIN')) { + const displayMsg = errMsg.replace('NEED_LOGIN:', '') + uni.showModal({ + title: '登录已过期', + content: displayMsg, + confirmText: '去登录', + success: (res) => { + if (res.confirm) { + uni.navigateTo({ url: '/pages/login/login' }) + } + } + }) + } } finally { this.popupLoading = false } @@ -1167,6 +1180,20 @@ export default { } } catch (e) { console.error('加载货位失败:', e) + const errMsg = e.message || String(e) + if (errMsg.includes('NEED_LOGIN')) { + const displayMsg = errMsg.replace('NEED_LOGIN:', '') + uni.showModal({ + title: '登录已过期', + content: displayMsg, + confirmText: '去登录', + success: (res) => { + if (res.confirm) { + uni.navigateTo({ url: '/pages/login/login' }) + } + } + }) + } this.popupLocationList = this.popupAllLocationList this.popupLocHasMore = false } finally { diff --git a/utils/api.js b/utils/api.js index 5bd20c8..ce79999 100644 --- a/utils/api.js +++ b/utils/api.js @@ -136,77 +136,163 @@ function generateSimpleSignedUrl(baseUrl, params = {}) { } /** - * 获取token(从本地存储或登录接口) + * 获取token(优先从登录缓存获取,失败时尝试自动登录) */ function getAuthToken() { - // 使用curl中的PSI固定token + // 从登录页存储获取 + const token = uni.getStorageSync('token') + if (token) return token + // PSI专用key + const psiToken = uni.getStorageSync('psi_token') + if (psiToken) return psiToken + // 硬编码备用 return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6MjU1LCJ1c2VybmFtZSI6IjE4OTA0MDU2ODAwIiwiYWJvdXRfaWQiOjE5NjUyNTQ3NzQzMjc1MzM1NzAsImlzcyI6InBzaS1zeXN0ZW0iLCJleHAiOjE3ODA1NjY0NTYsIm5iZiI6MTc4MDQ4MDA1NiwiaWF0IjoxNzgwNDgwMDU2fQ.yWTRso0ps-z64iA7nSKK4t3EYOy54CYoLtATyzFxrqI' } +/** + * 尝试自动登录PSI,返回新token或null + */ +function autoPsiLogin() { + return new Promise((resolve) => { + const phone = uni.getStorageSync('phoneNumber') || '' + const password = uni.getStorageSync('remembered_password') || '' + if (!phone || !password) { + resolve(null) + return + } + console.log('【PSI自动登录】尝试使用存储账号', phone) + uni.request({ + url: 'https://api.buzhiyushu.cn/auth/interFaceLogin', + method: 'POST', + header: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ZWxhc3RpYzo1bVJESVVnNTJWQzBmcDE0bnctRg==' + }, + data: { + clientId: 'cec96a240989d1c6bcd55f86fca702b7', + phoneNumber: phone, + password: password + }, + success: (res) => { + const data = res.data + if (data && data.code === 200 && data.data && data.data.access_token) { + const newToken = data.data.access_token + console.log('【PSI自动登录】成功') + uni.setStorageSync('token', newToken) + uni.setStorageSync('psi_token', newToken) + resolve(newToken) + } else { + console.error('【PSI自动登录】失败:', JSON.stringify(data)) + resolve(null) + } + }, + fail: (err) => { + console.error('【PSI自动登录】网络错误:', JSON.stringify(err)) + resolve(null) + } + }) + }) +} + +/** + * 带自动登录重试的PSI API请求 + * @param {Function} requestFn - 实际发起请求的函数,接收token,返回Promise<响应数据> + * @param {string} apiName - 接口名称(用于日志) + */ +function requestWithRetry(requestFn, apiName) { + return requestFn(getAuthToken()).catch((err) => { + const errMsg = err.message || String(err) + // 只有401/无效令牌才尝试自动登录 + if (errMsg.includes('401') || errMsg.includes('无效的认证令牌')) { + console.log(`【${apiName}】令牌无效,尝试自动登录`) + return autoPsiLogin().then((newToken) => { + if (newToken) { + // 使用新token重试一次 + return requestFn(newToken) + } + // 自动登录失败,抛出需要跳转登录页的错误 + throw new Error('NEED_LOGIN:登录已过期,请重新登录') + }) + } + // 其他错误 + throw err + }) +} + /** * 获取仓库列表 */ export function getWarehouseList(params = {}) { - return new Promise((resolve, reject) => { - const url = generateSignedUrl(`${BASE_URL}/api/warehouse/list`, params) - console.log('【仓库列表】请求URL:', url) - console.log('【仓库列表】请求参数:', JSON.stringify(params)) - const token = getAuthToken() - console.log('【仓库列表】token:', token) - uni.request({ - url: url, - method: 'GET', - header: { - 'Authorization': 'Bearer ' + token - }, - success: (res) => { - console.log('【仓库列表】响应状态码:', res.statusCode) - console.log('【仓库列表】响应数据:', JSON.stringify(res.data)) - if (res.statusCode === 200) { - resolve(res.data) - } else { - reject(new Error(`请求失败: ${res.statusCode}`)) + return requestWithRetry((token) => { + return new Promise((resolve, reject) => { + const url = generateSignedUrl(`${BASE_URL}/api/warehouse/list`, params) + console.log('【仓库列表】请求URL:', url) + console.log('【仓库列表】请求参数:', JSON.stringify(params)) + uni.request({ + url: url, + method: 'GET', + header: { + 'Authorization': 'Bearer ' + token + }, + success: (res) => { + console.log('【仓库列表】响应状态码:', res.statusCode) + console.log('【仓库列表】响应数据:', JSON.stringify(res.data)) + if (res.statusCode === 200) { + resolve(res.data) + } else { + const data = res.data + if (data && data.error) { + reject(new Error(`${res.statusCode}:${data.error}`)) + } else { + reject(new Error(`请求失败: ${res.statusCode}`)) + } + } + }, + fail: (err) => { + console.error('【仓库列表】请求失败:', JSON.stringify(err)) + reject(err) } - }, - fail: (err) => { - console.error('【仓库列表】请求失败:', JSON.stringify(err)) - reject(err) - } + }) }) - }) + }, '仓库列表') } /** * 获取货位列表 */ export function getLocationList(params = {}) { - return new Promise((resolve, reject) => { - const url = generateSignedUrl(`${BASE_URL}/api/location/list`, params) - console.log('【货位列表】请求URL:', url) - console.log('【货位列表】请求参数:', JSON.stringify(params)) - const locToken = getAuthToken() - console.log('【货位列表】token:', locToken) - uni.request({ - url: url, - method: 'GET', - header: { - 'Authorization': 'Bearer ' + locToken - }, - success: (res) => { - console.log('【货位列表】响应状态码:', res.statusCode) - console.log('【货位列表】响应数据:', JSON.stringify(res.data)) - if (res.statusCode === 200) { - resolve(res.data) - } else { - reject(new Error(`请求失败: ${res.statusCode}`)) + return requestWithRetry((token) => { + return new Promise((resolve, reject) => { + const url = generateSignedUrl(`${BASE_URL}/api/location/list`, params) + console.log('【货位列表】请求URL:', url) + console.log('【货位列表】请求参数:', JSON.stringify(params)) + uni.request({ + url: url, + method: 'GET', + header: { + 'Authorization': 'Bearer ' + token + }, + success: (res) => { + console.log('【货位列表】响应状态码:', res.statusCode) + console.log('【货位列表】响应数据:', JSON.stringify(res.data)) + if (res.statusCode === 200) { + resolve(res.data) + } else { + const data = res.data + if (data && data.error) { + reject(new Error(`${res.statusCode}:${data.error}`)) + } else { + reject(new Error(`请求失败: ${res.statusCode}`)) + } + } + }, + fail: (err) => { + console.error('【货位列表】请求失败:', JSON.stringify(err)) + reject(err) } - }, - fail: (err) => { - console.error('【货位列表】请求失败:', JSON.stringify(err)) - reject(err) - } + }) }) - }) + }, '货位列表') } /**