import Request from './request.js'; import errorCode from './errorCode.js'; import {Base64} from "js-base64"; import { getDoctorWxEnv } from '@/config/app-env.js'; const { legacyApiBase } = getDoctorWxEnv(); export const config = { baseUrl: legacyApiBase } const arr = [ 'mobile', 'express_mobile', 'doctor', 'accept_tel', 'patient_mobile', 'id_card', 'idcard', 'password', 'phone' ] // 敏感数据(解密后做脱敏;需与下方 switch 分支一致) const sensitiveData = [ 'id_card', 'idcard', 'mobile', 'express_mobile', 'patient_mobile', 'phone' ] /** * 请求拦截器 * @param {*} options */ const reqInterceptor = async (options) => { // 加密 if (options.data != null && options.type !== 'upload') { options.data = getRes(options.data, false) } options.header = { ...options.header } const skipToken = options.header.isToken === false const isClinicAdminReq = options.header._clinicAdmin === '1' || (options.url && String(options.url).indexOf('clinic-admin') !== -1) const isPlatformAdminReq = options.header._platformAdmin === '1' || (options.url && String(options.url).indexOf('platform-admin') !== -1) const isSalespersonReq = options.header._salesperson === '1' || (options.url && String(options.url).indexOf('salesperson-') !== -1) const isClinicSalespersonReq = options.header._clinicSalesperson === '1' || (options.url && String(options.url).indexOf('clinic-salesperson-') !== -1) const isDoctorWxAuthReq = options.url && String(options.url).indexOf('doctor-wx-auth') !== -1 // 内部控制位,不得作为 HTTP 头发出 delete options.header.isToken delete options.header._clinicAdmin delete options.header._platformAdmin delete options.header._salesperson delete options.header._clinicSalesperson const clinicToken = uni.getStorageSync('clinic_admin_token') const platformToken = uni.getStorageSync('platform_admin_token') const salespersonToken = uni.getStorageSync('salesperson_token') const clinicSalespersonToken = uni.getStorageSync('clinic_salesperson_token') const doctorToken = uni.getStorageSync('token') const loginMode = uni.getStorageSync('loginMode') if (!skipToken) { if (isDoctorWxAuthReq) { if (loginMode === 'platform_admin' && platformToken) { options.header['authorization'] = `Bearer ${platformToken}` } else if (loginMode === 'salesperson' && salespersonToken) { options.header['authorization'] = `Bearer ${salespersonToken}` } else if (loginMode === 'clinic_salesperson' && clinicSalespersonToken) { options.header['authorization'] = `Bearer ${clinicSalespersonToken}` } else if (loginMode === 'clinic_admin' && clinicToken) { options.header['authorization'] = `Bearer ${clinicToken}` } else if (doctorToken) { options.header['authorization'] = `Bearer ${doctorToken}` } else if (platformToken) { options.header['authorization'] = `Bearer ${platformToken}` } else if (salespersonToken) { options.header['authorization'] = `Bearer ${salespersonToken}` } else if (clinicToken) { options.header['authorization'] = `Bearer ${clinicToken}` } } else if (isPlatformAdminReq && platformToken) { options.header['authorization'] = `Bearer ${platformToken}` } else if (isSalespersonReq && salespersonToken) { options.header['authorization'] = `Bearer ${salespersonToken}` } else if (isClinicSalespersonReq && clinicSalespersonToken) { options.header['authorization'] = `Bearer ${clinicSalespersonToken}` } else if (isClinicAdminReq && clinicToken) { options.header['authorization'] = `Bearer ${clinicToken}` } else if (doctorToken) { options.header['authorization'] = `Bearer ${doctorToken}` } } return options; } /** * 响应拦截器 */ const resInterceptor = (response, conf = {}) => { // 网络状态码 const statusCode = response.statusCode|| response.errcode; // 响应拦截器 if (statusCode >= 200 && statusCode < 300) { _responseLog(response, conf, "response 200-299") // 解密(Yii:业务在 data;Laravel jok:业务在 result) let res = response.data if (res == null || typeof res !== 'object') { return res } if (res.data != null) { res.data = getRes(res.data) } if (res.result != null) { res.result = getRes(res.result) } return res } else if (statusCode === 500) { uni.showToast({ icon: 'error', title: response['msg'] || errorCode[statusCode] }) _responseLog(response, conf, "response 500") // 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控 return { // 根据当前字段来判断是否reject wakaryReqToReject: true, // 下面可以配置其他返回信息,方便统一处理reject // 以下内容作为reject的返回,根据需求处理,返回具体错误信息 msg: "服务器错误", res: response } } else if (statusCode === 401) { uni.showToast({ icon: 'error', title: response['msg'] || response['message'] || errorCode[statusCode] }) const loginMode = uni.getStorageSync('loginMode') if (loginMode === 'platform_admin') { uni.removeStorageSync('platform_admin_token') uni.removeStorageSync('platform_admin_user') uni.removeStorageSync('loginMode') } else if (loginMode === 'salesperson') { uni.removeStorageSync('salesperson_token') uni.removeStorageSync('salesperson_user') uni.removeStorageSync('loginMode') } else if (loginMode === 'clinic_salesperson') { uni.removeStorageSync('clinic_salesperson_token') uni.removeStorageSync('clinic_salesperson_user') uni.removeStorageSync('loginMode') } else if (loginMode === 'clinic_admin') { uni.removeStorageSync('clinic_admin_token') uni.removeStorageSync('clinic_admin_user') uni.removeStorageSync('loginMode') } else { uni.removeStorageSync('token') uni.removeStorageSync('loginInfo') uni.removeStorageSync('identity') } setTimeout(() => { uni.reLaunch({ url: '/pages/login/index' }) }, 1000) _responseLog(response, conf, "response 401") // 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控 return { // 根据当前字段来判断是否reject wakaryReqToReject: true, // 下面可以配置其他返回信息,方便统一处理reject // 以下内容作为reject的返回,根据需求处理,返回具体错误信息 msg: response['msg'] || errorCode[statusCode], res: response } } else { uni.showToast({ icon: 'error', title: response['msg'] || errorCode[statusCode] }) _responseLog(response, conf, "response 300-499"); // 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控 return { // 根据当前字段来判断是否reject wakaryReqToReject: true, // 下面可以配置其他返回信息,方便统一处理reject // 以下内容作为reject的返回,根据需求处理,返回具体错误信息 msg: "response 300-499", res: response } } } /** * request log */ function _requestLog(req, describe = null) { if (process.env.NODE_ENV === 'development') {} } /** * response log */ function _responseLog(res, conf = {}, describe = null) { let _statusCode = res.statusCode; if (process.env.NODE_ENV === 'development') {} if (_statusCode === 500) { // save log to server } } function getRes(obj, isDecode = true) { if (obj == null || typeof obj !== 'object') { return obj } try { for (const key in obj) { if (Array.isArray(obj[key])) { obj[key] = getRes(obj[key]) } else if (typeof obj[key] === 'object' && obj[key] !== null) { obj[key] = getRes(obj[key]) } else { // 判断obj[key]是否在arr中 if (arr.indexOf(key) !== -1) { if (obj[key] == null || obj[key] === '') continue let aseFile = '' if (isDecode === true) { aseFile = customBase64Decode(obj[key]) } else { aseFile = customBase64Encode(obj[key]) } const isGarbled = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]|[\u{E000}-\u{F8FF}]/u.test( typeof aseFile === 'string' ? aseFile : '' ) if (isGarbled) { aseFile = obj[key] } if (isDecode === true) { if (sensitiveData.indexOf(key) !== -1) { // 数据脱敏,自动匹配姓名、手机号、身份证 switch (key) { case 'express_name': case 'express_region': case 'accept_name': case 'patient': // 保留前两个字符,其余用星号代替 aseFile = aseFile.slice(0, 1).padEnd(aseFile.length, '*'); break; case 'mobile': case 'express_mobile': case 'phone': // 保留前三位和后四位,中间用星号代替 aseFile = aseFile.slice(0, 3).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4); break; case 'id_card': case 'idcard': // 保留前六位和后四位,中间用星号代替 aseFile = aseFile.slice(0, 6).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4); break; default: // 默认情况下,全部用星号代替 break; } } } obj[key] = aseFile } } } return obj } catch (error) { console.error('错误', error) return obj } } function customBase64Decode(data) { if (data == null || data === '') return 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 const req = new Request(config, reqInterceptor, resInterceptor)