function batchFindObjctValue (obj = {}, keys = []) { const values = {} for (let i = 0; i < keys.length; i++) { const key = keys[i] const keyPath = key.split('.') let currentKey = keyPath.shift() let result = obj while (currentKey) { if (!result) { break } result = result[currentKey] currentKey = keyPath.shift() } values[key] = result } return values } function getType (val) { return Object.prototype.toString.call(val).slice(8, -1).toLowerCase() } function hasOwn (obj, key) { return Object.prototype.hasOwnProperty.call(obj, key) } function isValidString (val) { return val && getType(val) === 'string' } function isPlainObject (obj) { return getType(obj) === 'object' } function isFn (fn) { // 务必注意AsyncFunction return typeof fn === 'function' } // 获取文件后缀,只添加几种图片类型供客服消息接口使用 const mime2ext = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/gif': 'gif', 'image/svg+xml': 'svg', 'image/bmp': 'bmp', 'image/webp': 'webp' } function getExtension (contentType) { return mime2ext[contentType] } const isSnakeCase = /_(\w)/g const isCamelCase = /[A-Z]/g function snake2camel (value) { return value.replace(isSnakeCase, (_, c) => (c ? c.toUpperCase() : '')) } function camel2snake (value) { return value.replace(isCamelCase, str => '_' + str.toLowerCase()) } function parseObjectKeys (obj, type) { let parserReg, parser switch (type) { case 'snake2camel': parser = snake2camel parserReg = isSnakeCase break case 'camel2snake': parser = camel2snake parserReg = isCamelCase break } for (const key in obj) { if (hasOwn(obj, key)) { if (parserReg.test(key)) { const keyCopy = parser(key) obj[keyCopy] = obj[key] delete obj[key] if (isPlainObject(obj[keyCopy])) { obj[keyCopy] = parseObjectKeys(obj[keyCopy], type) } else if (Array.isArray(obj[keyCopy])) { obj[keyCopy] = obj[keyCopy].map((item) => { return parseObjectKeys(item, type) }) } } } } return obj } function snake2camelJson (obj) { return parseObjectKeys(obj, 'snake2camel') } function camel2snakeJson (obj) { return parseObjectKeys(obj, 'camel2snake') } function getOffsetDate (offset) { return new Date( Date.now() + (new Date().getTimezoneOffset() + (offset || 0) * 60) * 60000 ) } function getDateStr (date, separator = '-') { date = date || new Date() const dateArr = [] dateArr.push(date.getFullYear()) dateArr.push(('00' + (date.getMonth() + 1)).substr(-2)) dateArr.push(('00' + date.getDate()).substr(-2)) return dateArr.join(separator) } function getTimeStr (date, separator = ':') { date = date || new Date() const timeArr = [] timeArr.push(('00' + date.getHours()).substr(-2)) timeArr.push(('00' + date.getMinutes()).substr(-2)) timeArr.push(('00' + date.getSeconds()).substr(-2)) return timeArr.join(separator) } function getFullTimeStr (date) { date = date || new Date() return getDateStr(date) + ' ' + getTimeStr(date) } function getDistinctArray (arr) { return Array.from(new Set(arr)) } /** * 拼接url * @param {string} base 基础路径 * @param {string} path 在基础路径上拼接的路径 * @returns */ function resolveUrl (base, path) { if (/^https?:/.test(path)) { return path } return base + path } function getVerifyCode (len = 6) { let code = '' for (let i = 0; i < len; i++) { code += Math.floor(Math.random() * 10) } return code } function coverMobile (mobile) { if (typeof mobile !== 'string') { return mobile } return mobile.slice(0, 3) + '****' + mobile.slice(7) } function getNonceStr (length = 16) { let str = '' while (str.length < length) { str += Math.random().toString(32).substring(2) } return str.substring(0, length) } function isMatchUserApp (userAppList, matchAppList) { if (userAppList === undefined || userAppList === null) { return true } if (getType(userAppList) !== 'array') { return false } if (userAppList.includes('*')) { return true } if (getType(matchAppList) === 'string') { matchAppList = [matchAppList] } return userAppList.some(item => matchAppList.includes(item)) } function checkIdCard (idCardNumber) { if (!idCardNumber || typeof idCardNumber !== 'string' || idCardNumber.length !== 18) return false const coefficient = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] const checkCode = [1, 0, 'x', 9, 8, 7, 6, 5, 4, 3, 2] const code = idCardNumber.substring(17) let sum = 0 for (let i = 0; i < 17; i++) { sum += Number(idCardNumber.charAt(i)) * coefficient[i] } return checkCode[sum % 11].toString() === code.toLowerCase() } function catchAwait (fn, finallyFn) { if (!fn) return [new Error('no function')] if (Promise.prototype.finally === undefined) { // eslint-disable-next-line no-extend-native Promise.prototype.finally = function (finallyFn) { return this.then( res => Promise.resolve(finallyFn()).then(() => res), error => Promise.resolve(finallyFn()).then(() => { throw error }) ) } } return fn .then((data) => [undefined, data]) .catch((error) => [error]) .finally(() => typeof finallyFn === 'function' && finallyFn()) } function dataDesensitization (value = '', options = {}) { const { onlyLast = false } = options const [firstIndex, middleIndex, lastIndex] = onlyLast ? [0, 0, -1] : [0, 1, -1] if (!value) return value const first = value.slice(firstIndex, middleIndex) const middle = value.slice(middleIndex, lastIndex) const last = value.slice(lastIndex) const star = Array.from(new Array(middle.length), (v) => '*').join('') return first + star + last } function getCurrentDateTimestamp (date = Date.now(), targetTimezone = 8) { const oneHour = 60 * 60 * 1000 return parseInt((date + targetTimezone * oneHour) / (24 * oneHour)) * (24 * oneHour) - targetTimezone * oneHour } module.exports = { getType, isValidString, batchFindObjctValue, isPlainObject, isFn, getDistinctArray, getFullTimeStr, resolveUrl, getOffsetDate, camel2snakeJson, snake2camelJson, getExtension, getVerifyCode, coverMobile, getNonceStr, isMatchUserApp, checkIdCard, catchAwait, dataDesensitization, getCurrentDateTimestamp }