2024-09-19 12:57:55 +08:00
|
|
|
|
import Request from './request.js';
|
|
|
|
|
|
import errorCode from './errorCode.js';
|
2025-02-13 10:31:01 +08:00
|
|
|
|
import {Base64} from "js-base64";
|
2026-05-14 14:29:51 +08:00
|
|
|
|
import { getDoctorWxEnv } from '@/config/app-env.js';
|
|
|
|
|
|
|
|
|
|
|
|
const { legacyApiBase } = getDoctorWxEnv();
|
|
|
|
|
|
|
2024-09-19 12:57:55 +08:00
|
|
|
|
export const config = {
|
2026-05-14 14:29:51 +08:00
|
|
|
|
baseUrl: legacyApiBase
|
2024-09-19 12:57:55 +08:00
|
|
|
|
}
|
2025-02-13 10:31:01 +08:00
|
|
|
|
|
|
|
|
|
|
const arr = [
|
|
|
|
|
|
'mobile',
|
|
|
|
|
|
'express_mobile',
|
|
|
|
|
|
'doctor',
|
|
|
|
|
|
'accept_tel',
|
|
|
|
|
|
'patient_mobile',
|
|
|
|
|
|
'id_card',
|
|
|
|
|
|
'idcard',
|
|
|
|
|
|
'password'
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-05-12 10:10:38 +08:00
|
|
|
|
// 敏感数据(解密后做脱敏;需与下方 switch 分支一致)
|
2025-02-13 10:31:01 +08:00
|
|
|
|
const sensitiveData = [
|
|
|
|
|
|
'id_card',
|
2026-05-12 10:10:38 +08:00
|
|
|
|
'idcard',
|
|
|
|
|
|
'mobile',
|
|
|
|
|
|
'express_mobile',
|
|
|
|
|
|
'patient_mobile'
|
2025-02-13 10:31:01 +08:00
|
|
|
|
]
|
2024-09-19 12:57:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 请求拦截器
|
2024-12-21 17:10:32 +08:00
|
|
|
|
* @param {*} options
|
2024-09-19 12:57:55 +08:00
|
|
|
|
*/
|
|
|
|
|
|
const reqInterceptor = async (options) => {
|
2025-02-13 10:31:01 +08:00
|
|
|
|
// 加密
|
2026-05-14 14:29:51 +08:00
|
|
|
|
if (options.data != null && options.type !== 'upload') {
|
2025-02-13 10:31:01 +08:00
|
|
|
|
options.data = getRes(options.data, false)
|
|
|
|
|
|
}
|
2024-09-19 12:57:55 +08:00
|
|
|
|
options.header = {
|
|
|
|
|
|
...options.header
|
|
|
|
|
|
}
|
|
|
|
|
|
const isToken = (options.header || {}).isToken === false
|
2026-05-30 13:52:44 +08:00
|
|
|
|
const isClinicAdminReq = (options.header || {})._clinicAdmin === '1'
|
|
|
|
|
|
|| (options.url && String(options.url).indexOf('clinic-admin') !== -1)
|
|
|
|
|
|
const clinicToken = uni.getStorageSync('clinic_admin_token')
|
|
|
|
|
|
const doctorToken = uni.getStorageSync('token')
|
|
|
|
|
|
if (!isToken) {
|
|
|
|
|
|
if (isClinicAdminReq && clinicToken) {
|
|
|
|
|
|
options.header['authorization'] = `Bearer ${clinicToken}`
|
|
|
|
|
|
} else if (doctorToken) {
|
|
|
|
|
|
options.header['authorization'] = `Bearer ${doctorToken}`
|
|
|
|
|
|
}
|
2024-09-19 12:57:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
return options;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 响应拦截器
|
|
|
|
|
|
*/
|
|
|
|
|
|
const resInterceptor = (response, conf = {}) => {
|
|
|
|
|
|
// 网络状态码
|
|
|
|
|
|
const statusCode = response.statusCode|| response.errcode;
|
|
|
|
|
|
// 响应拦截器
|
|
|
|
|
|
if (statusCode >= 200 && statusCode < 300) {
|
|
|
|
|
|
_responseLog(response, conf, "response 200-299")
|
2026-05-12 10:10:38 +08:00
|
|
|
|
// 解密(Yii:业务在 data;Laravel jok:业务在 result)
|
2025-02-13 10:31:01 +08:00
|
|
|
|
let res = response.data
|
2026-05-12 10:10:38 +08:00
|
|
|
|
if (res == null || typeof res !== 'object') {
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
2025-02-13 10:31:01 +08:00
|
|
|
|
if (res.data != null) {
|
|
|
|
|
|
res.data = getRes(res.data)
|
|
|
|
|
|
}
|
2026-05-12 10:10:38 +08:00
|
|
|
|
if (res.result != null) {
|
|
|
|
|
|
res.result = getRes(res.result)
|
|
|
|
|
|
}
|
2025-02-13 10:31:01 +08:00
|
|
|
|
return res
|
2024-09-19 12:57:55 +08:00
|
|
|
|
} 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',
|
2026-05-30 13:52:44 +08:00
|
|
|
|
title: response['msg'] || response['message'] || errorCode[statusCode]
|
2024-09-19 12:57:55 +08:00
|
|
|
|
})
|
2026-05-30 13:52:44 +08:00
|
|
|
|
const isClinic = uni.getStorageSync('loginMode') === 'clinic_admin'
|
|
|
|
|
|
if (isClinic) {
|
|
|
|
|
|
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)
|
2024-09-19 12:57:55 +08:00
|
|
|
|
_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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-13 10:31:01 +08:00
|
|
|
|
function getRes(obj, isDecode = true) {
|
2026-05-12 10:10:38 +08:00
|
|
|
|
if (obj == null || typeof obj !== 'object') {
|
|
|
|
|
|
return obj
|
|
|
|
|
|
}
|
2025-02-13 10:31:01 +08:00
|
|
|
|
try {
|
|
|
|
|
|
for (const key in obj) {
|
|
|
|
|
|
if (Array.isArray(obj[key])) {
|
|
|
|
|
|
obj[key] = getRes(obj[key])
|
2026-05-12 10:10:38 +08:00
|
|
|
|
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
2025-02-13 10:31:01 +08:00
|
|
|
|
obj[key] = getRes(obj[key])
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 判断obj[key]是否在arr中
|
|
|
|
|
|
if (arr.indexOf(key) !== -1) {
|
|
|
|
|
|
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':
|
|
|
|
|
|
// 保留前三位和后四位,中间用星号代替
|
|
|
|
|
|
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) {
|
|
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-09-19 12:57:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 请求方法封装
|
|
|
|
|
|
*/
|
2024-12-21 17:10:32 +08:00
|
|
|
|
|
2024-09-19 12:57:55 +08:00
|
|
|
|
export const req = new Request(config, reqInterceptor, resInterceptor)
|