Files
ZYZ/shared/pubUtils/util.ts
2021-08-30 16:54:31 +08:00

637 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { STATUS } from './../consts/statusCode';
import { HeroModel, HeroType } from '../db/Hero';
import fs = require('fs');
import path = require('path');
import { HERO_CE_RATIO, ABI_STAGE, GACHA_TO_FLOOR, REFRESH_TIME, ROBOT_SYS_TYPE, } from '../consts';
import { findIndex } from 'underscore';
import { getTimeFunM } from './timeUtil';
import { Floor } from '../domain/activityField/gachaField';
import { WhiteListModel } from '../db/WhiteList';
import { UserModel } from '../db/User';
const randomName = require("chinese-random-name");
const moment = require('moment');
const crypto = require('crypto');
export function aesEncrypt(data, key, iv) {
const cipher = crypto.createCipheriv('aes-192-cbc', key, iv);
let crypted = cipher.update(data, 'utf8', 'hex');
crypted += cipher.final('hex');
return crypted;
}
export function aesDecrypt(data, key, iv) {
const decipher = crypto.createDecipheriv('aes-192-cbc', key, iv);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
export function aesEncryptcfb(data, key, iv) {
const cipher = crypto.createCipheriv('aes-192-cfb', key, iv);
let crypted = cipher.update(data, 'utf8', 'hex');
crypted += cipher.final('hex');
// console.log('****aesEncryptcfb', data, crypted)
return crypted;
}
export function aesDecryptcfb(data, key, iv) {
if (data) {
const decipher = crypto.createDecipheriv('aes-192-cfb', key, iv);
let decrypted = decipher.update(data, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} else {
return ''
}
}
export function genCode(len) {
const chars = '123456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijklmnopqrstuvwxyz';
const charArr = chars.split('');
let code = '';
for (let i = 0; i < len; i++) {
code += charArr[Math.floor(Math.random() * charArr.length)];
}
return code;
}
export function generateNum(len: number) {
let code = '';
for (let i = 0; i < len; i++) {
code += parseInt(`${Math.random() * 10}`);
}
return code;
}
/**
* 将 | 分隔的字符串解析为数组a|b|c 解析为[a, b, c]
* @param str 要解析的字符串
*/
export function decodeArrayStr(str: string, splitForm = '|') {
let last = str.substr(str.length - 1, 1);
if (last == '&') str = str.substr(0, str.length - 1);
if (str == '') {
return new Array<string>()
} else {
return str.split(splitForm);
}
}
/**
* 将 | 和 & 分隔的字符串解析为 Mapa&b&c|c&d&f 解析为 [[a,b,c], [c,d,f]]
* @param str 要解析的字符串
*/
export function decodeArrayListStr(str: string) {
return decodeArrayStr(str).map(cur => decodeArrayStr(cur, '&'));
}
/**
* 将 | 和 & 分隔的字符串解析为 Mapa&b|c&d|e&f 解析为 Map {a=>b, c=>d, e=>f}
* @param str 要解析的字符串
*/
export function decodeIdCntArrayStr(str: string, multi: number) {
const strArr = decodeArrayStr(str);
// console.log('decodeIdCntArrayStr: ', strArr);
const strMap = new Map<string, number>();
strArr.forEach(item => {
const kv = item.split('&');
strMap.set(kv[0], multi ? Math.ceil(parseInt(kv[1]) * multi) : parseInt(kv[1]));
});
return strMap;
}
// 计算当前武将战力
export async function calculateSumCE(roleId: string, type: number, param: { num?: number, heroes?: Array<number> }) {
let sum: number;
if (type == 1) { // 最高num人历史最高战力和
sum = await HeroModel.sumTopHeroCe(roleId, param.num || 0);
} else if (type == 2) { // 所有人战力和
sum = await HeroModel.sumHeroCe(roleId);
}
sum = reduceCe(sum);
return sum;
}
/**
* 传入两个时间,返回按照时间差计算,第二个时间比第一个晚几天
* @param preTime 之前的时间
* @param proTime 之后的时间
*/
export function deltaDays(preTime: Date, proTime: Date): number {
return moment(proTime).diff(moment(preTime), "days");
}
/**
* 计算按照每 x 天 y 点刷新一次,是否应该刷新
* @param preTime 基准时间
* @param curTime 当前时间
* @param hour 几点刷新
* @param deltaDay 间隔几天刷新,默认每天刷新(deltaDay = 1)
*/
export function shouldRefresh(preTime: Date, now: Date, hour: number = REFRESH_TIME, deltaDay = 1): boolean {
if (!preTime) return true;
let curTime = new Date(now.getTime());
let refreshTime = new Date(now.getTime());
refreshTime.setHours(hour, 0, 0, 0);
let refeshTime = refreshTime.getTime();
if (refeshTime - preTime.getTime() > (deltaDay >= 1 ? deltaDay - 1 : 0) * 24 * 60 * 60 * 1000 && curTime.getTime() >= refeshTime) {
return true;
}
return false;
}
/**
* 计算按照每 x 天 y 点刷新一次,是否应该刷新
* @param preTime 基准时间
* @param curTime 当前时间
* @param day 周几刷新
* @param hour 几点刷新
* @param deltaDay 间隔几天刷新,默认每天刷新(deltaDay = 1)
*/
export function shouldRefreshWeek(preTime: Date, now: Date, day: number = 1, hour: number = REFRESH_TIME, deltaWeek = 1): boolean {
if (!preTime) return true;
let curTime = new Date(now.getTime());
let refreshTime = <number>getTimeFunM(now).getTimeWithWeek(day, hour);
if (refreshTime - preTime.getTime() > (deltaWeek >= 1 ? deltaWeek - 1 : 0) * 7 * 24 * 60 * 60 * 1000 && curTime.getTime() >= refreshTime) {
return true;
}
return false;
}
/**
* 从一个数组中随机返回不重复的 cnt 个元素数组
* @param source 原数组
* @param cnt 返回随机元素个数
*/
export function getRandEelm<T>(source: Array<T> = [], cnt = 1): Array<T> {
if (cnt == 0) return [];
if (cnt >= source.length) return source;
let idxs = new Set();
while (1) {
let rand = Math.floor(Math.random() * source.length);
idxs.add(rand);
if (idxs.size >= cnt) {
break;
}
}
return source.filter((_item, idx) => idxs.has(idx));
}
/**
* 从一个数组中随机返回1元素
* @param source
*/
export function getRandSingleEelm<T>(source: Array<T>): T {
let len = source.length;
return source[Math.floor(Math.random() * len)]
}
/**
* 随机数组中一个index
* @param len
*/
export function getRandSingleIndex(len: number) {
return Math.floor(Math.random() * len);
}
/**
* 从一个数组根据权重随机一个元素
* @param randomList
*/
export function getRandEelmWithWeight<T extends { weight: number }>(randomList: T[]): { dic: T, index: number } {
let len = randomList.reduce((pre, cur) => {
return pre + cur.weight || 1;
}, 0);
let index = Math.floor(Math.random() * len);
let result = { dic: null, index: -1 };
for (let i = 0; i < randomList.length; i++) {
let { weight = 0 } = randomList[i];
if (index < weight) {
result.dic = randomList[i];
result.index = i;
break;
}
index -= weight;
}
return result
}
/**
* 不改变原数组长度,将内部元素打乱
* @param source
*/
export function sortArrRandom(source = []) {
let arr = deepCopy(source);
return arr.sort(() => { return Math.random() - 0.5; });
}
/**
* 在给定数值的浮动范围中随机一个值
* @param base 基础数值
* @param ratio 随机范围base 值上下浮动 ratio(小于 1 的整数)
* @param decimal 返回值保留的小数位数
*/
export function getRandValue(base: number, ratio: number, decimal = 2): number {
return parseFloat((base * (1 - ratio + Math.random() * ratio * 2)).toFixed(decimal));
}
/**
* 在给定最大最小值中随机一个值
* @param min 最小
* @param max 最大
* @param decimal 返回值保留的小数位数
*/
export function getRandValueByMinMax(min: number, max: number, decimal = 2): number {
let pow = Math.pow(10, decimal);
return Math.floor((min + (max - min) * Math.random()) * pow)/pow;
}
export function resResult(status: { code: number, simStr: string }, data = null, customMsg = '') {
const { code, simStr } = status;
if (code !== STATUS.SUCCESS.code) {
console.log(`normal err, code: ${code}, des: ${customMsg || simStr}`);
}
return { code, msg: customMsg || simStr, data };
}
export function getResStr(status: { code: number, simStr: string }) {
return status.simStr;
}
// 消除 js 浮点计算bug
export const cal = {
add: function (a: number, b: number) {
var c: number, d: number, e: number;
try {
c = a.toString().split(".")[1].length;
} catch (f) {
c = 0;
}
try {
d = b.toString().split(".")[1].length;
} catch (f) {
d = 0;
}
e = Math.pow(10, Math.max(c, d));
return (this.mul(a, e) + this.mul(b, e)) / e;
},
sub: function sub(a: number, b: number) {
var c: number, d: number, e: number;
try {
c = a.toString().split(".")[1].length;
} catch (f) {
c = 0;
}
try {
d = b.toString().split(".")[1].length;
} catch (f) {
d = 0;
}
e = Math.pow(10, Math.max(c, d));
return (this.mul(a, e) - this.mul(b, e)) / e;
},
mul: function mul(a: number, b: number) {
var c = 0,
d = a.toString(),
e = b.toString();
try {
c += d.split(".")[1].length;
} catch (f) { }
try {
c += e.split(".")[1].length;
} catch (f) { }
return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c);
},
div: function div(a: number, b: number) {
var c: number, d: number, e = 0,
f = 0;
try {
e = a.toString().split(".")[1].length;
} catch (g) { }
try {
f = b.toString().split(".")[1].length;
} catch (g) { }
c = Number(a.toString().replace(".", ""));
d = Number(b.toString().replace(".", ""));
return this.mul(c / d, Math.pow(10, f - e));
}
};
//计算公式
// export function calculateNum(ratio: { A: number, B: number }, params: { num: number }, defaultVal = 0) {
// // result = a * num + b
// try {
// let { A, B } = ratio;
// let { num } = params;
// let result = A * num + B;
// if (isNaN(result)) {
// console.error('calculate wrong: ', A, B, num)
// return defaultVal;
// } else {
// return result;
// }
// } catch (e) {
// console.error(e);
// return defaultVal;
// }
// }
export function ratioReward(reward: {id: number, count: number}[], ratio: number): {id: number, count: number}[] {
return reward.map(cur => {
return {id: cur.id, count: cur.count * ratio}
});
}
export function getItems(str: string) {
let arr = new Array<{ id: number, count: number }>();
let strArr = str.split('|');
for (let item of strArr) {
var itemArr = item.split('&');
arr.push({
id: parseInt(itemArr[0]),
count: parseInt(itemArr[1])
});
}
return arr;
}
export function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
var target;
if (obj instanceof Array) {
target = [];
obj.forEach(element => {
target.push(deepCopy(element));
});
} else {
target = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const element = obj[key];
if (!!element || element == 0) {
target[key] = deepCopy(element);
}
}
}
}
return target;
};
export function readJsonFile(fileName: string) {
const folder = 'jsons';
return fs.readFileSync(path.resolve(__dirname, `../resource/${folder}/${fileName}.json`)).toString('utf8').replace(/^\uFEFF/, '');
}
export function readWarJsonFileList() {
const folder = 'warJsons';
return fs.readdirSync(__dirname + `/../resource/${folder}`)
.filter(cur => { return cur.indexOf('.') != 0 })
.map(file => {
return {
name: file,
str: fs.readFileSync(path.resolve(__dirname, `../resource/${folder}/${file}`)).toString('utf8')
}
});
}
export function readTsFile(fileName: string) {
try {
let file = fs.readFileSync(path.resolve(__dirname, `../pubUtils/${fileName}.ts`)).toString('utf8').replace(/^\uFEFF/, '');
return file;
} catch(e) {
return null
}
}
export function readFileAndParse(fileName: string) {
try {
let readResult = readJsonFile(fileName);
return JSON.parse(readResult);
} catch(e) {
throw new Error(`${fileName}格式错误:${e.message}`);
}
}
export function readWarJsonFileAndParse() {
let warJsons = readWarJsonFileList();
let result = [];
for(let { name, str } of warJsons) {
try {
let json = JSON.parse(str);
result.push(json);
} catch(e) {
throw new Error(`${name}格式错误:${e.message}`);
}
}
return result;
}
// 字典表常用解析方法
// 解析物品 {"id": number, "count": number} 格式
export function parseGoodStr(str: string) {
let result = new Array<{ id: number, count: number }>();
if (!str) return result;
let decodeArr = decodeArrayListStr(str);
for (let [id, count] of decodeArr) {
if (isNaN(parseInt(id)) || isNaN(parseInt(count))) {
throw new Error('data table format wrong');
}
result.push({ id: parseInt(id), count: parseInt(count) });
}
return result
}
// 根据类型解析物品 {"type":number, "id": number, "count": number} 格式
//type 1.英雄2.物品
export function parseGoodStrWithType(str: string) {
let result = new Array<{ type: number, id: number, count: number }>();
if (!str) return result;
let decodeArr = decodeArrayListStr(str);
for (let [type, id, count] of decodeArr) {
if (isNaN(parseInt(type)) || isNaN(parseInt(id)) || isNaN(parseInt(count))) {
throw new Error('data table format wrong');
}
result.push({ type: parseInt(type), id: parseInt(id), count: parseInt(count) });
}
return result
}
// 根据类型解析物品 {"type":number, "hid": number, "count": number} 格式
//type 1.英雄2.物品
export function parseHeroStrWithType(str: string) {
let result = new Array<{ type: number, hid: number, count: number }>();
if (!str) return result;
let decodeArr = decodeArrayListStr(str);
for (let [type, hid, count] of decodeArr) {
if (isNaN(parseInt(type)) || isNaN(parseInt(hid)) || isNaN(parseInt(count))) {
throw new Error('data table format wrong');
}
result.push({ type: parseInt(type), hid: parseInt(hid), count: parseInt(count) });
}
return result
}
// 根据类型解析物品 {"type":number, "id": number, "count": number} 格式
//type 1.英雄2.物品, 3.RMB
export function parseResStr(str: string) {
let result = new Array<{ type: number, id: number, count: number }>();
if (!str) return result;
let decodeArr = decodeArrayListStr(str);
for (let [type, id, count] of decodeArr) {
if (isNaN(parseInt(type)) || isNaN(parseInt(id)) || isNaN(parseInt(count))) {
throw new Error('data table format wrong');
}
result.push({ type: parseInt(type), id: parseInt(id), count: parseInt(count) });
}
return result
}
// 数字列表
export function parseNumberList(str: string) {
let res = new Array<number>();
if (!str) return res;
let arr = decodeArrayStr(str, '&');
for (let g of arr) {
if (g === "") continue;
res.push(parseFloat(g));
}
return res;
}
export function mergeSameGoods(goods: Array<{ id: number, count: number }>) {
let resGoods = [];
for (let { id, count } of goods) {
let index = findIndex(resGoods, { id });
if (index > -1) {
resGoods[index].count += count;
} else {
resGoods.push({ id, count });
}
}
return resGoods;
}
export function returnHeroCeRatio(hero: HeroType) {
let ce = reduceCe(hero.ce);
return Object.assign(hero, { ce });
}
// 缩小战力
export function reduceCe(ce: number = 0) {
return Math.floor(ce / HERO_CE_RATIO / HERO_CE_RATIO)
}
// 获取全部属性
export function getAllAttrStage() {
let attrs = new Array<number>(); // 有升级的属性 1-hp 2-atk 3-def 4-mdef 5-agi 6-luk
for (let stage = ABI_STAGE.START + 1; stage <= ABI_STAGE.END; stage++) {
attrs.push(stage)
};
return attrs;
}
export function getChineseName() {
return randomName.generate()
}
export function getRobotInfo(sysType = ROBOT_SYS_TYPE.COM_BTL) {
return {
robotRoleName: getChineseName(),
robotRoleId: makeRobotId(genCode(8), sysType),
}
}
// 根据roleId判断是不是机器人
export function checkRoleIsRobot(roleId: string) {
return !!roleId.match(/_r/);
}
// 将一般的roleId转为带_r的
export function makeRobotId(roleId: string, sysType = ROBOT_SYS_TYPE.COM_BTL) {
if(sysType) {
return `${sysType}|${roleId}_r`;
} else {
return `${roleId}_r`;
}
}
// 获取来源系统
export function getRobotSysType(roleId: string) {
let type = roleId.split('|')[0];
if(isNaN(parseInt(type))) {
return 0
} else {
return parseInt(type);
}
}
// 将一般的roleId去掉_r
export function robotIdComBack(robotRoleId: string) {
return robotRoleId.replace(/_r/, '')
}
export function splitString(dataString: string, key: string) {
if (!dataString) {
return [];
}
let array = dataString.split(key).filter(obj => { return obj && obj != '' });
let numberArray = [];
for (let num of array) {
numberArray.push(Number(num));
}
return numberArray;
}
/**
* @description 获取抽卡保底状态
* @param gachaId 招募类型
* @param floor 玩家保底
*/
export function getFloorStatus(gachaId: number, floor: Floor[]) {
let floorMap = new Map<number, number>();
for (let { id, count } of floor) {
floorMap.set(id, count);
}
let dicFloorType = GACHA_TO_FLOOR.get(gachaId);
return dicFloorType.map(id => {
return {
id,
count: floorMap.get(id) || 0
}
});
}
export async function checkWhiteListWithUid(uid: number) {
let user = await UserModel.findUserByUid(uid);
if(!user) return false;
let { tel, ip, auth } = user;
return await checkWhiteList(tel, ip, auth);
}
export async function checkWhiteList(tel: string, ip: string, auth: number) {
if(auth == 1) {
return true;
}
if(tel) {
let result = await WhiteListModel.checkTel(tel);
if(!!result) return true;
}
if(ip) {
let result = await WhiteListModel.checkIp(ip);
if(!!result) return true;
}
return false
}