问题背景: - 平台审核结论:页面未完整浏览、体验详情时即要求授权登录,属于不合规 - 用户应能先浏览页面内容,仅在执行操作(抽奖/兑换/购买)时才引导登录 根因分析: 1. api/appUser.js 中活动浏览类 API(getActivityDetail 等)使用 authRequest, 虽然后端接口是公开的,但同页面的 getGamePasses 等需认证接口返回 401 触发全局登录弹窗 2. getProductDetail 使用 authRequest 调用认证接口,未登录直接 401 3. 全局 401 拦截器不区分浏览请求和操作请求 修改内容: 1. api/appUser.js: 6 个浏览类 API 函数从 authRequest 改为 request - getActivityDetail, getActivityIssues, getActivityIssueRewards - getIssueDrawLogs, getMatchingCardTypes, getProductDetail 这些接口在后端均为公开路由,不需要携带 token 2. 活动页面 onLoad 中条件调用认证接口: - wuxianshang/index.vue: fetchPasses() 仅在已登录时调用 - yifanshang/index.vue: fetchPasses() 仅在已登录时调用 - duiduipeng/index.vue: fetchGamePasses() 仅在已登录时调用 次数卡(game passes)接口需要认证,未登录时跳过即可, 不影响页面浏览体验 3. utils/request.js: request() 函数增加 suppressAuthModal 参数 支持调用方按需静默 401 弹窗,作为安全兜底机制 验证场景: - 未登录 → 打开无限赏/一番赏/对对碰/商品详情 → 正常显示,无登录弹窗 - 未登录 → 点击抽奖/兑换按钮 → 弹出登录提示(符合平台规范) - 已登录 → 所有功能正常,次数卡信息正常加载
156 lines
4.5 KiB
JavaScript
Executable File
156 lines
4.5 KiB
JavaScript
Executable File
const BASE_URL = 'http://127.0.0.1:9991'
|
|
// const BASE_URL = 'https://kdy.1024tool.vip'
|
|
let authModalShown = false
|
|
|
|
function handleAuthExpired() {
|
|
if (authModalShown) return
|
|
authModalShown = true
|
|
uni.removeStorageSync('token')
|
|
uni.showModal({
|
|
title: '提示',
|
|
content: '账号登录已过期,请重新登录',
|
|
showCancel: true,
|
|
success: (res) => {
|
|
if (res.cancel) {
|
|
console.log('用户点击取消');
|
|
return
|
|
}
|
|
authModalShown = false
|
|
uni.navigateTo({ url: '/pages/login/index' })
|
|
}
|
|
})
|
|
}
|
|
|
|
// 不再脱敏,直接打印原始数据
|
|
|
|
export function request({ url, method = 'GET', data = {}, header = {}, suppressAuthModal = false }) {
|
|
return new Promise((resolve, reject) => {
|
|
const finalHeader = { ...buildDefaultHeaders(), ...header }
|
|
uni.request({
|
|
url: BASE_URL + url,
|
|
method,
|
|
data,
|
|
header: finalHeader,
|
|
timeout: 15000,
|
|
success: (res) => {
|
|
const code = res.statusCode
|
|
if (code >= 200 && code < 300) {
|
|
const body = res.data
|
|
resolve(body && body.data !== undefined ? body.data : body)
|
|
} else {
|
|
if (code === 401) {
|
|
const suppress = suppressAuthModal || finalHeader && (finalHeader['X-Suppress-Auth-Modal'] || finalHeader['x-suppress-auth-modal'])
|
|
if (!suppress) {
|
|
handleAuthExpired()
|
|
}
|
|
}
|
|
|
|
// 检查是否是商品缺货错误 (code: 20002)
|
|
// 仅当是商品详情接口时才弹窗
|
|
if (res.data && res.data.code === 20002 && url.startsWith('/api/app/products/')) {
|
|
uni.showModal({
|
|
title: '商品库存不足',
|
|
content: '当前商品库存不足,由于市场价格存在波动请联系客服核对价格,补充库存。',
|
|
showCancel: false
|
|
})
|
|
}
|
|
|
|
const msg = (res.data && (res.data.message || res.data.msg)) || '请求错误'
|
|
reject({ message: msg, statusCode: code, data: res.data })
|
|
}
|
|
},
|
|
fail: (err) => {
|
|
reject(err)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
export function authRequest(options) {
|
|
const token = uni.getStorageSync('token')
|
|
const base = buildDefaultHeaders()
|
|
const header = { ...base, ...(options.header || {}) }
|
|
// 设置Authorization头
|
|
if (token) {
|
|
header.Authorization = token
|
|
}
|
|
return request({ ...options, header })
|
|
}
|
|
|
|
export function redeemProductByPoints(user_id, product_id, quantity) {
|
|
return authRequest({
|
|
url: `/api/app/users/${user_id}/points/redeem-product`,
|
|
method: 'POST',
|
|
data: { product_id, quantity }
|
|
})
|
|
}
|
|
|
|
let cachedSystemInfo = null
|
|
|
|
function getSystemInfo() {
|
|
if (cachedSystemInfo) return cachedSystemInfo
|
|
try {
|
|
cachedSystemInfo = uni.getSystemInfoSync()
|
|
return cachedSystemInfo
|
|
} catch (_) {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
function getLanguage() {
|
|
return getSystemInfo().language || 'zh-CN'
|
|
}
|
|
|
|
function getPlatform() {
|
|
// 简单判断当前运行平台
|
|
if (typeof wx !== 'undefined') return 'mp-weixin'
|
|
return getSystemInfo().platform || 'unknown'
|
|
}
|
|
|
|
function getDeviceId() {
|
|
try {
|
|
let id = uni.getStorageSync('device_id')
|
|
if (!id) {
|
|
id = 'dev-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8)
|
|
uni.setStorageSync('device_id', id)
|
|
}
|
|
return id
|
|
} catch (_) { return 'dev-unknown' }
|
|
}
|
|
|
|
function buildDefaultHeaders() {
|
|
const info = getSystemInfo()
|
|
const lang = info.language || 'zh-CN'
|
|
const platform = info.platform || 'unknown'
|
|
const brand = info.brand || ''
|
|
const model = info.model || ''
|
|
const osName = info.system || ''
|
|
const osVersion = info.system ? info.system.split(' ')[1] || '' : ''
|
|
const screen = `${info.screenWidth}x${info.screenHeight}`
|
|
const pixelRatio = info.pixelRatio || 1
|
|
const windowSize = `${info.windowWidth}x${info.windowHeight}`
|
|
const headers = {
|
|
'Accept': 'application/json',
|
|
'content-type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'Accept-Language': lang,
|
|
'X-App-Client': 'uni-app',
|
|
'X-App-Platform': platform,
|
|
'X-Device-Id': getDeviceId(),
|
|
'X-Device-Brand': brand,
|
|
'X-Device-Model': model,
|
|
'X-OS-Name': osName,
|
|
'X-OS-Version': osVersion,
|
|
'X-Screen': screen,
|
|
'X-Pixel-Ratio': pixelRatio,
|
|
'X-Window': windowSize,
|
|
'Cache-Control': 'no-cache'
|
|
}
|
|
// 小程序环境禁止设置 User-Agent
|
|
// #ifndef MP
|
|
headers['User-Agent'] = `UniApp/${platform} (${brand} ${model}; ${osName} ${osVersion})`
|
|
// #endif
|
|
|
|
return headers
|
|
}
|