import {Base64} from "js-base64"; import {checkDev} from "@/utils/utils"; import {isGuestMode, getGuestStoreId} from "@/common/utils/auth.js"; const arr = [ 'mobile', 'express_mobile', 'doctor', 'accept_tel', 'patient_mobile', 'id_card', 'idcard', 'password' ] // 敏感数据:响应解密后保留明文原字段,脱敏值写入 {field}_tm const sensitiveData = [ 'id_card', 'idcard', 'guardian_id_card' ] // 仅展示脱敏、编辑需明文的字段 const displayMaskFields = [ 'mobile', 'express_mobile', 'patient_mobile', ] /** * 脱敏展示值(写入 field_tm) */ function maskFieldValue(key, plainText) { if (plainText == null || plainText === '') { return plainText } const str = String(plainText) switch (key) { case 'express_name': case 'express_region': case 'accept_name': case 'patient': return str.slice(0, 1).padEnd(str.length, '*') case 'mobile': case 'express_mobile': case 'patient_mobile': case 'guardian_mobile': if (str.length <= 7) { return str } return str.slice(0, 3).padEnd(str.length - 4, '*') + str.slice(-4) case 'id_card': case 'idcard': case 'guardian_id_card': if (str.length <= 10) { return str } return str.slice(0, 6).padEnd(str.length - 4, '*') + str.slice(-4) default: return str } } /** 只读展示:优先 field_tm */ export function displayTm(obj, field) { if (obj == null) { return '' } const tm = obj[field + '_tm'] if (tm != null && tm !== '') { return tm } return obj[field] ?? '' } const isDev = checkDev('dev'); // 环境URL配置 const ENV_URLS = { // 生产环境 prod: { main: "https://app.xiaokang88.com/member", xkApi: "https://api.xiaokang88.com/api/mobile", shop: "https://shop.xiaokang88.com/member", im: "https://api.ws.g.xiaokang88.com/api", }, // 测试环境 test: { main: "https://xk.test.saas.api-yii.nailaoyun.cn/member", xkApi: "https://xk.test.saas.api.nailaoyun.cn/api/mobile", shop: "https://test.xk.shop.nailaoyun.cn/member1", im: "https://xk.ws.nailaoyun.cn/api" }, // 本地开发环境 local: { main: "http://127.0.0.1:18000/member", xkApi: "http://127.0.0.1:18001/api/mobile", shop: "http://127.0.0.1:18018/member", im: "http://127.0.0.1:12080/api" } }; // 获取当前环境配置 function getEnvUrls() { // 如果是本地开发环境,使用本地配置 if (isDev === true) { return ENV_URLS.local; } // 检查是否开启了开发者测试模式 const devMode = checkDev('test'); return devMode ? ENV_URLS.test : ENV_URLS.prod; } function request(url, params, method = 0) { const envUrls = getEnvUrls(); let baseURL = envUrls.main; // 判断是否为guest模式且未登录 const guestMode = isGuestMode(); const token = uni.getStorageSync("token"); const isGuestRequest = guestMode && !token; // 如果是guest模式且是xkApi请求,转换为guest路由 if (isGuestRequest && url.indexOf('/xkApi') !== -1) { if (url.indexOf('/xkApi/online-consultation/get-default-doctor-id') !== -1) { url = url.replace('/xkApi/online-consultation/get-default-doctor-id', '/guest/guest-home/get-default-doctor-id'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/product/drug-list-by-salesperson') !== -1) { url = url.replace('/xkApi/product/drug-list-by-salesperson', '/guest/guest-product/drug-list-by-salesperson'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/product/home-top-products') !== -1) { url = url.replace('/xkApi/product/home-top-products', '/guest/guest-product/home-top-products'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/product/home-zones') !== -1) { url = url.replace('/xkApi/product/home-zones', '/guest/guest-product/home-zones'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/platform/config') !== -1) { url = url.replace('/xkApi/platform/config', '/guest/guest-platform/config'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/platform/qualifications') !== -1) { url = url.replace('/xkApi/platform/qualifications', '/guest/guest-platform/qualifications'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/store/info') !== -1) { url = url.replace('/xkApi/store/info', '/guest/guest-home/store-info'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/store/list') !== -1) { url = url.replace('/xkApi/store/list', '/guest/guest-home/store-list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/xkApi/special-prescription/') !== -1) { url = url.replace('/xkApi/special-prescription/', '/guest/guest-special-prescription/'); baseURL = envUrls.xkApi; } } // 如果是guest模式且是oldApi请求,转换为guest路由(需要在替换之前处理) if (isGuestRequest && url.indexOf('/oldApi') !== -1) { // 将 /oldApi/v1/xxx 转换为 /guest/guest-xxx/xxx // 注意:路由前缀是 api/mobile,所以完整路径是 api/mobile/guest/guest-doctor/list if (url.indexOf('/oldApi/v1/doctor/all-list') !== -1) { url = url.replace('/oldApi/v1/doctor/all-list', '/guest/guest-doctor/list'); baseURL = envUrls.xkApi; // guest路由使用xkApi的baseURL } else if (url.indexOf('/oldApi/v1/doctor/detail') !== -1) { url = url.replace('/oldApi/v1/doctor/detail', '/guest/guest-doctor/detail'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/doctor/comment-list') !== -1) { url = url.replace('/oldApi/v1/doctor/comment-list', '/guest/guest-doctor/comment-list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/patient/list') !== -1) { url = url.replace('/oldApi/v1/patient/list', '/guest/guest-patient/list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/store/info') !== -1) { url = url.replace('/oldApi/v1/store/info', '/guest/guest-home/store-info'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/store/detail') !== -1) { url = url.replace('/oldApi/v1/store/detail', '/guest/guest-home/store-detail'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/store/list') !== -1) { url = url.replace('/oldApi/v1/store/list', '/guest/guest-home/store-list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/register/doc-register-info') !== -1) { url = url.replace('/oldApi/v1/register/doc-register-info', '/guest/guest-doctor/doc-register-info'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/doctor/office-list') !== -1) { url = url.replace('/oldApi/v1/doctor/office-list', '/guest/guest-doctor/office-list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/info/categories') !== -1) { url = url.replace('/oldApi/v1/info/categories', '/guest/guest-info/categories'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/info/article-list') !== -1) { url = url.replace('/oldApi/v1/info/article-list', '/guest/guest-info/article-list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/oldApi/v1/info/article-info') !== -1) { url = url.replace('/oldApi/v1/info/article-info', '/guest/guest-info/article-info'); baseURL = envUrls.xkApi; } } // 如果是guest模式且是member/v1请求,也需要转换为guest路由 if (isGuestRequest && (url.indexOf('/member/v1') !== -1 || url.indexOf('/v1/') !== -1)) { if (url.indexOf('/v1/doctor/comment-list') !== -1 || url.indexOf('/member/v1/doctor/comment-list') !== -1) { url = url.replace(/\/member\/v1\/doctor\/comment-list|\/v1\/doctor\/comment-list/, '/guest/guest-doctor/comment-list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/v1/patient/list') !== -1 || url.indexOf('/member/v1/patient/list') !== -1) { url = url.replace(/\/member\/v1\/patient\/list|\/v1\/patient\/list/, '/guest/guest-patient/list'); baseURL = envUrls.xkApi; } else if (url.indexOf('/v1/register/doc-register-info') !== -1 || url.indexOf('/member/v1/register/doc-register-info') !== -1) { url = url.replace(/\/member\/v1\/register\/doc-register-info|\/v1\/register\/doc-register-info/, '/guest/guest-doctor/doc-register-info'); baseURL = envUrls.xkApi; } else if (url.indexOf('/v1/store/info') !== -1 || url.indexOf('/member/v1/store/info') !== -1) { url = url.replace(/\/member\/v1\/store\/info|\/v1\/store\/info/, '/guest/guest-home/store-info'); baseURL = envUrls.xkApi; } else if (url.indexOf('/v1/store/list') !== -1 || url.indexOf('/member/v1/store/list') !== -1) { url = url.replace(/\/member\/v1\/store\/list|\/v1\/store\/list/, '/guest/guest-home/store-list'); baseURL = envUrls.xkApi; } } if (url.split('/').indexOf('imApi') !== -1) { baseURL = envUrls.im; } if (url.split('/').indexOf('newApi') !== -1) { baseURL = envUrls.shop; } if (url.split('/').indexOf('xkApi') !== -1) { baseURL = envUrls.xkApi; // 如果是guest模式且是xkApi请求,添加/guest/前缀(但避免重复添加) // 转诊相关接口需要登录,不转换为guest路由 if (isGuestRequest && url.indexOf('/guest/') === -1 && url.indexOf('/transfer-prescription') === -1) { url = url.replace('/xkApi', '/guest/xkApi'); } } url = url.replace('/oldApi', ''); url = url.replace('/newApi', ''); url = url.replace('/imApi', ''); url = url.replace('/xkApi', ''); // 加密 if (params != null) { if (!params.data) { params.data = {}; } params.data = getRes(params.data, false); const isProductApi = url.indexOf('/product/') !== -1; if (isProductApi && !params.data.store_id) { const localStoreId = uni.getStorageSync('store_id'); params.data.store_id = localStoreId || getGuestStoreId(); } // 如果是guest模式,设置store_id(优先使用用户传入或本地存储的值) if (isGuestRequest && params.data) { // 优先使用用户传入的 store_id if (!params.data.store_id) { // 如果用户没传,尝试从本地存储获取 const localStoreId = uni.getStorageSync('store_id'); if (localStoreId) { params.data.store_id = localStoreId; } else { // 如果本地存储也没有,使用默认值(保持向后兼容) params.data.store_id = getGuestStoreId(); } } // 如果用户已经传了 store_id,就使用用户传入的值,不做任何修改 } } let num = 0; // promise导出 return new Promise((resolve, reject) => { uni.request({ // 基地址 + 传过来的地址 url: baseURL + url, timeout: 8000, // 8秒超时时间,单位ms // 展开传过来的数据 请求方式 所需数据 wxfc279dcf921a38f7 // store_id: '11001' ...params, // 请求头 header: { "authorization": 'Bearer ' + uni.getStorageSync("token"), "Content-Type": method === 0? "application/x-www-form-urlencoded": 'application/json' }, // 成功钩子 success(res) { if (res.data.errcode === 401) { const currentPage = getCurrentPages()[getCurrentPages().length - 1]; const currentRoute = currentPage ? currentPage.route : ''; const isLoginPage = currentRoute === 'pages/login/login'; // 如果当前不在登录页,则跳转到登录页 if (!isLoginPage) { uni.removeStorageSync('token') uni.removeStorageSync('userinfo') uni.removeStorageSync('token_h5') uni.removeStorageSync('user_id') uni.reLaunch({url: '/pages/login/login'}) } } if (res.data?.data != null) { res.data.data = getRes(res.data.data) } // 处理 xkApi 返回的 result 格式 if (res.data?.result != null) { res.data.result = getRes(res.data.result) } resolve(res) }, // 失败钩子 fail(err) { reject(err) uni.showToast({ title: "请求失败", duration: 1500, mask: false, icon: "error" }) }, // 成功失败都会执行的钩子 complete() { // 成功或者失败停止下拉刷新 uni.stopPullDownRefresh() } }) }) } function getRes(obj, isDecode = true) { try { if (obj == null || typeof obj !== 'object') { return obj } for (const key in obj) { if (key.endsWith('_tm')) { continue } if (Array.isArray(obj[key])) { obj[key] = getRes(obj[key], isDecode) } else if (obj[key] !== null && typeof obj[key] === 'object') { obj[key] = getRes(obj[key], isDecode) } else if (arr.indexOf(key) !== -1) { let plainValue = '' if (isDecode === true) { plainValue = customBase64Decode(obj[key]) } else { plainValue = customBase64Encode(obj[key]) } const isGarbled = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]|[\u{E000}-\u{F8FF}]/u.test( typeof plainValue === 'string' ? plainValue : '' ) if (isGarbled) { plainValue = obj[key] } if (isDecode === true) { const needTm = sensitiveData.indexOf(key) !== -1 || displayMaskFields.indexOf(key) !== -1 obj[key] = plainValue if (needTm) { obj[key + '_tm'] = maskFieldValue(key, plainValue) } } else { obj[key] = plainValue } } } return obj } catch (error) { console.error('错误:', error) return obj } } function customBase64Decode(data) { // return data try { // 第一次Base64解码 const decodedFirst = Base64.decode(data) // 截取从第30个字符开始往后的内容 const subStr = decodedFirst.slice(30) // 第二次Base64解码 return Base64.decode(subStr) != '' ? Base64.decode(subStr) : data } catch (error) { console.error(`解码Base64字符串【${data}】时发生错误`, error) return data } } // 加密 function customBase64Encode(data) { if (data == null || data === '') return data // return data try { // 和加密方法差不多但是是要生成30个随机字符 let randomString = '' for (let i = 0; i < 30; i++) { const randomChar = String.fromCharCode(Math.floor(Math.random() * (126 - 32 + 1)) + 32) randomString += randomChar } const encoded = Base64.encode(data) return Base64.encode(randomString + encoded) } catch (error) { console.error(`加密字符串【${data}】时发生错误`, error) return data } } // 导出 export default request