common-utils.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. import logger from './logger'; // -----------------检测类型工具函数-----------------
  2. /**
  3. * 检测input类型是否为Map
  4. * @param {*} input 任意类型的输入
  5. * @returns {Boolean} true->map / false->not a map
  6. */
  7. export const isMap = function (input) {
  8. return getType(input) === 'map';
  9. };
  10. /**
  11. * 检测input类型是否为Set
  12. * @param {*} input 任意类型的输入
  13. * @returns {Boolean} true->set / false->not a set
  14. */
  15. export const isSet = function (input) {
  16. return getType(input) === 'set';
  17. };
  18. /**
  19. * 检测input类型是否为File
  20. * @param {*} input 任意类型的输入
  21. * @returns {Boolean} true->file / false->not a file
  22. */
  23. export const isFile = function (input) {
  24. return getType(input) === 'file';
  25. };
  26. /**
  27. * 检测input类型是否为number
  28. * @param {*} input 任意类型的输入
  29. * @returns {Boolean} true->number / false->not a number
  30. */
  31. export const isNumber = function (input) {
  32. return input !== null && (typeof input === 'number' && !isNaN(input - 0) || typeof input === 'object' && input.constructor === Number);
  33. };
  34. /**
  35. * 检测input类型是否为string
  36. * @param {*} input 任意类型的输入
  37. * @returns {Boolean} true->string / false->not a string
  38. */
  39. export const isString = function (input) {
  40. return typeof input === 'string';
  41. };
  42. export const isObject = function (input) {
  43. // null is object, hence the extra check
  44. return input !== null && typeof input === 'object';
  45. };
  46. /**
  47. * Checks to see if a value is an object and only an object
  48. * plain object: 没有标准定义,一般认为通过 {} 或者 new Object() 或者 Object.create(null) 方式创建的对象是纯粹对象
  49. * @param {*} input 任意类型的输入
  50. * @returns {Boolean} true->an object and only an object
  51. */
  52. export const isPlainObject = function (input) {
  53. // 注意不能使用以下方式判断,因为IE9/IE10下,对象的__proto__是undefined
  54. // return isObject(input) && input.__proto__ === Object.prototype;
  55. if (typeof input !== 'object' || input === null) {
  56. return false;
  57. }
  58. const proto = Object.getPrototypeOf(input);
  59. if (proto === null) {
  60. // edge case Object.create(null)
  61. return true;
  62. }
  63. let baseProto = proto;
  64. while (Object.getPrototypeOf(baseProto) !== null) {
  65. baseProto = Object.getPrototypeOf(baseProto);
  66. } // 2. 原型链第一个和最后一个比较
  67. return proto === baseProto;
  68. };
  69. /**
  70. * 检测input类型是否为数组
  71. * @param {*} input 任意类型的输入
  72. * @returns {Boolean} true->array / false->not an array
  73. */
  74. export const isArray = function (input) {
  75. if (typeof Array.isArray === 'function') {
  76. return Array.isArray(input);
  77. }
  78. return getType(input) === 'array';
  79. };
  80. /**
  81. * Checks to see if a value is undefined
  82. * @param {*} input 任意类型的输入
  83. * @returns {Boolean} true->input value is undefined
  84. */
  85. export const isUndefined = function (input) {
  86. return typeof input === 'undefined';
  87. };
  88. const isNativeClassRegex = /^class\s/;
  89. /**
  90. * Is ES6+ class
  91. * @param {*} input 任意类型的输入
  92. * @returns {Boolean} true->input is ES6+ class
  93. */
  94. export const isNativeClass = function (input) {
  95. return typeof input === 'function' && isNativeClassRegex.test(input.toString());
  96. };
  97. /**
  98. * 检测input类型是否为数组或者object
  99. * @param {*} input 任意类型的输入
  100. * @returns {Boolean} true->input is an array or an object
  101. */
  102. export const isArrayOrObject = function (input) {
  103. return isArray(input) || isObject(input);
  104. };
  105. /**
  106. * 检测input类型是否为function
  107. * @param {*} input 任意类型的输入
  108. * @returns {Boolean} true->input is a function
  109. */
  110. export const isFunction = function (input) {
  111. return typeof input === 'function';
  112. };
  113. export const isBoolean = function (input) {
  114. return typeof input === 'boolean';
  115. };
  116. /**
  117. * 检测input是否为Error的实例
  118. * @param {*} input 任意类型的输入
  119. * @returns {Boolean} true->input is an instance of Error
  120. */
  121. export const isInstanceOfError = function (input) {
  122. return input instanceof Error;
  123. };
  124. /**
  125. * Get the object type string
  126. * @param {*} input 任意类型的输入
  127. * @returns {String} the object type string
  128. */
  129. export const getType = function (input) {
  130. return Object.prototype.toString.call(input).match(/^\[object (.*)\]$/)[1].toLowerCase();
  131. };
  132. /**
  133. * ### 用于 request 参数 key 名称的合法性验证。
  134. * > 不合法的 key 有:
  135. * - 非 string 类型的 key ;
  136. * - 非字母和数字开头的 key ;
  137. * @param {string} key - 参数 key
  138. * @returns {boolean}
  139. * - true : 合法;
  140. * - false: 非法;
  141. */
  142. export const isValidRequestKey = function (key) {
  143. // 非 string 类型的 key
  144. if (typeof key !== 'string') {
  145. return false;
  146. }
  147. const firstCharactor = key[0]; // 非字母和数字开头的 key
  148. if (/[^a-zA-Z0-9]/.test(firstCharactor)) {
  149. return false;
  150. }
  151. return true;
  152. }; // -----------------获取时间工具函数,计算耗时用-----------------
  153. let baseTime = 0;
  154. if (!Date.now) {
  155. Date.now = function now() {
  156. return new Date().getTime();
  157. };
  158. }
  159. export const TimeUtil = {
  160. now() {
  161. if (baseTime === 0) {
  162. baseTime = Date.now() - 1;
  163. }
  164. const diff = Date.now() - baseTime;
  165. if (diff > 0xffffffff) {
  166. baseTime += 0xffffffff;
  167. return Date.now() - baseTime;
  168. }
  169. return diff;
  170. },
  171. utc() {
  172. return Math.round(Date.now() / 1000);
  173. }
  174. }; // -----------------深度合并工具函数-----------------
  175. /**
  176. * 深度 merge 两个对象。merge source to target.
  177. * @param {Object|Object[]} target 目标对象
  178. * @param {Object|Object[]} source 来源对象
  179. * @param {String[]} [keysIgnore] 要忽略的 keys。命中的 key 会在merge时被忽略
  180. * @param {*[]} [valuesIgnore] 要忽略的 values。命中的 value 会在merge时被忽略
  181. * @returns {Number} merge的次数(只有key相同value不同的时候才会merge),如果target和source既不是数组也不是object,则返回0
  182. */
  183. export const deepMerge = function (target, source, keysIgnore, valuesIgnore) {
  184. // 1. 非 Array 或 Object 类型则直接 return
  185. if (!(isArrayOrObject(target) && isArrayOrObject(source))) {
  186. return 0;
  187. }
  188. let mergedCount = 0;
  189. const keys = Object.keys(source);
  190. let tmpKey;
  191. for (let i = 0, len = keys.length; i < len; i++) {
  192. tmpKey = keys[i];
  193. if (isUndefined(source[tmpKey]) || keysIgnore && keysIgnore.includes(tmpKey)) {
  194. continue;
  195. }
  196. if (isArrayOrObject(target[tmpKey]) && isArrayOrObject(source[tmpKey])) {
  197. // 递归merge
  198. mergedCount += deepMerge(target[tmpKey], source[tmpKey], keysIgnore, valuesIgnore);
  199. } else {
  200. if (valuesIgnore && valuesIgnore.includes(source[tmpKey])) {
  201. continue;
  202. }
  203. if (target[tmpKey] !== source[tmpKey]) {
  204. target[tmpKey] = source[tmpKey];
  205. mergedCount += 1;
  206. }
  207. }
  208. }
  209. return mergedCount;
  210. }; // 简单的深拷贝实现
  211. export function deepCopy(val) {
  212. if (Object.prototype.toString.call(val) === '[object Array]' && Object.prototype.toString.call(val) === '[object Object]') {
  213. return val
  214. }
  215. let target = Array.isArray(val) ? [] : {} //数组兼容
  216. for (var k in val) {
  217. if (val.hasOwnProperty(k)) {
  218. if (typeof val[k] === 'object') {
  219. target[k] = deepCopy(val[k])
  220. } else {
  221. target[k] = val[k]
  222. }
  223. }
  224. }
  225. return target
  226. }
  227. /**
  228. * 序列化Error实例,只序列化Error实例的message和code属性(如果有的话)
  229. * @param {Error} error Error实例
  230. * @returns {String} 序列化后的内容
  231. */
  232. export const stringifyError = function (error) {
  233. return JSON.stringify(error, ['message', 'code']);
  234. };
  235. /**
  236. * 返回一个 ISO(ISO 8601 Extended Format)格式的字符串,如"2019-11-15T18:45:06.000+0800",用于记录sso上报时的时间
  237. * @returns {String}
  238. */
  239. export const date2ISOString = function () {
  240. const date = new Date(); // YYYY-MM-DDTHH:mm:ss.sssZ。时区总是UTC(协调世界时),加一个后缀“Z”标识。
  241. const tempISOString = date.toISOString(); // 返回协调世界时(UTC)相对于当前时区的时间差值,单位为分钟。
  242. // 如果本地时区晚于协调世界时,则该差值为正值,如果早于协调世界时则为负值
  243. const timezoneOffset = date.getTimezoneOffset();
  244. const currentTimeZoneInHour = timezoneOffset / 60;
  245. let replaceString = '';
  246. if (currentTimeZoneInHour < 0) {
  247. // 东区
  248. if (currentTimeZoneInHour > -10) {
  249. replaceString = `+0${Math.abs(currentTimeZoneInHour * 100)}`;
  250. } else {
  251. replaceString = `+${Math.abs(currentTimeZoneInHour * 100)}`;
  252. }
  253. } else {
  254. // 西区
  255. if (currentTimeZoneInHour >= 10) {
  256. replaceString = `-${currentTimeZoneInHour * 100}`;
  257. } else {
  258. replaceString = `-0${currentTimeZoneInHour * 100}`;
  259. }
  260. } // 不需要 encodeURIComponent 把 + 转成 %2B,kibana能识别
  261. return tempISOString.replace('Z', replaceString);
  262. };
  263. /**
  264. * 把map的内容转成字符串,
  265. * @param {Map} input 字典
  266. * @returns {String} 字典的内容
  267. */
  268. export const map2String = function (input) {
  269. if (!isMap(input)) {
  270. return 'not a map!!!';
  271. }
  272. let result = '';
  273. for (const [k, v] of input.entries()) {
  274. if (!isMap(v)) {
  275. result += `[k=${k} v=${v}] `;
  276. } else {
  277. return `[k=${k} -> ${map2String(v)}`;
  278. }
  279. }
  280. return result;
  281. };
  282. /**
  283. * 获取字符串占用的字节数
  284. * @param {String} string - 字符串
  285. * @returns {Number} 字符串的长度
  286. */
  287. export const stringSize = function (string) {
  288. if (string.length === 0) {
  289. return 0;
  290. }
  291. let i = 0;
  292. let char = '';
  293. let len = 0;
  294. let sizeStep = 1;
  295. const charSet = function () {
  296. if (typeof document !== 'undefined' && typeof document.characterSet !== 'undefined') {
  297. return document.characterSet;
  298. }
  299. return 'UTF-8';
  300. }();
  301. while (typeof string[i] !== 'undefined') {
  302. char = string[i++];
  303. if (char.charCodeAt[i] <= 255) {
  304. sizeStep = 1;
  305. } else {
  306. sizeStep = charSet === 'UTF-8' >= 0 ? 3 : 2;
  307. }
  308. len += sizeStep;
  309. }
  310. return len;
  311. };
  312. /**
  313. * 用于生成随机整数
  314. * @param {Number} level - 整数的级别 例如: 99, 999 , 9999 等
  315. * @returns {Number} 随机数字
  316. */
  317. export const randomInt = function (level) {
  318. const lv = level || 99999999;
  319. return Math.round(Math.random() * lv);
  320. };
  321. const CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  322. const CHARS_LENGTH = CHARS.length;
  323. /**
  324. * 获取[0,9],[a-z],[A,Z]拼成的随机字符串,长度32
  325. * @returns {String} 长度为32的随机字符串
  326. */
  327. export const randomString = function () {
  328. let result = '';
  329. for (let i = 32; i > 0; --i) {
  330. result += CHARS[Math.floor(Math.random() * CHARS_LENGTH)];
  331. }
  332. return result;
  333. }; // 判断传入的枚举值是否有效
  334. export const isValidType = function (obj, type) {
  335. for (const k in obj) {
  336. if (obj[k] === type) {
  337. return true;
  338. }
  339. }
  340. return false;
  341. };
  342. const AIIList = {};
  343. export const autoincrementIndex = function (key) {
  344. if (!key) {
  345. logger.error('autoincrementIndex(string: key) need key parameter');
  346. return false;
  347. }
  348. if (typeof AIIList[key] === 'undefined') {
  349. const dateInstance = new Date();
  350. let hoursString = `3${dateInstance.getHours()}`.slice(-2); // 小时数,用3占位,因为一天中不会出现30小时
  351. let minuteString = `0${dateInstance.getMinutes()}`.slice(-2);
  352. let secondString = `0${dateInstance.getSeconds()}`.slice(-2);
  353. AIIList[key] = parseInt([hoursString, minuteString, secondString, '0001'].join(''));
  354. hoursString = null;
  355. minuteString = null;
  356. secondString = null;
  357. logger.warn(`utils.autoincrementIndex() create new sequence : ${key} = ${AIIList[key]}`);
  358. }
  359. return AIIList[key]++;
  360. }; // 统一用HTTPS
  361. export const uniformHTTPS = function (url) {
  362. if (url.indexOf('http://') === -1 || url.indexOf('https://') === -1) {
  363. return `https://${url}`;
  364. }
  365. return url.replace(/https|http/, 'https');
  366. };
  367. /**
  368. * 使用转JSON方式克隆一个对象
  369. * @param {Object} data 要克隆的对象
  370. * @returns {Object} 克隆后的对象
  371. */
  372. export const cloneBaseOnJSON = function (data) {
  373. try {
  374. return JSON.parse(JSON.stringify(data));
  375. } catch (error) {
  376. logger.error('cloneBaseOnJSON Error: ', error);
  377. return data;
  378. }
  379. };
  380. /**
  381. * 深度克隆
  382. * @param {Object} data 要克隆的对象
  383. * @returns {Object} 克隆后的对象
  384. */
  385. export const clone = function (data) {
  386. if (Object.getOwnPropertyNames(data).length === 0) {
  387. return Object.create(null);
  388. }
  389. const newObject = Array.isArray(data) ? [] : Object.create(null);
  390. let type = ''; // eslint-disable-next-line guard-for-in
  391. for (const key in data) {
  392. // null 是一个特殊的对象,优先处理掉
  393. if (data[key] === null) {
  394. newObject[key] = null;
  395. continue;
  396. } // undefined 也优先处理掉
  397. if (data[key] === undefined) {
  398. newObject[key] = undefined;
  399. continue;
  400. }
  401. type = typeof data[key];
  402. if (['string', 'number', 'function', 'boolean'].indexOf(type) >= 0) {
  403. newObject[key] = data[key];
  404. continue;
  405. } // 只剩对象,递归
  406. newObject[key] = clone(data[key]);
  407. }
  408. return newObject;
  409. };
  410. /**
  411. * 更新自定义字段,如资料自定义字段,群组自定义字段,群成员自定义字段
  412. * @param {Object[]} target 待更新的自定义字段数组 [{key,value}, {key,value}, ...]
  413. * @param {Object[]} source 最新的自定义字段数组 [{key,value}, {key,value}, ...]
  414. */
  415. export function updateCustomField(target, source) {
  416. if (!isArray(target) || !isArray(source)) {
  417. logger.warn('updateCustomField target 或 source 不是数组,忽略此次更新。');
  418. return;
  419. }
  420. source.forEach(({
  421. key,
  422. value
  423. }) => {
  424. const customField = target.find(item => item.key === key);
  425. if (customField) {
  426. customField.value = value;
  427. } else {
  428. target.push({
  429. key,
  430. value
  431. });
  432. }
  433. });
  434. }
  435. /**
  436. * 类似 lodash.mapKeys 功能,并具备过滤 keys 的功能
  437. * @export
  438. * @param {*} obj
  439. * @param {*} iteratee
  440. */
  441. export function filterMapKeys(obj, iteratee) {
  442. const newObj = Object.create(null);
  443. Object.keys(obj).forEach(key => {
  444. const nextKey = iteratee(obj[key], key); // 回调返回 false 则过滤 key
  445. if (nextKey) {
  446. newObj[nextKey] = obj[key];
  447. }
  448. });
  449. return newObj;
  450. } // -----------------类lodash.mapKeys函数-----------------
  451. export function mapKeys(obj, iteratee) {
  452. const newObj = {};
  453. Object.keys(obj).forEach(key => {
  454. newObj[iteratee(obj[key], key)] = obj[key];
  455. });
  456. return newObj;
  457. } // -----------------类lodash.mapValues函数-----------------
  458. export function mapValues(obj, iteratee) {
  459. const newObj = {};
  460. Object.keys(obj).forEach(key => {
  461. newObj[key] = iteratee(obj[key], key);
  462. });
  463. return newObj;
  464. }