xy-server/game/gear/SKDataUtil.ts
2025-04-23 09:34:08 +08:00

923 lines
27 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.

/**
* 数据工具类
* @author BrightLi
* @since 2020/5/3
*/
import path from "path";
import fs from "fs";
import SKLogger from "./SKLogger";
import {util} from "protobufjs";
import float = util.float;
export default class SKDataUtil {
// 生成UUID
static uuid(): string {
if (typeof (window) !== "undefined" && typeof (window.crypto) !== "undefined" && typeof (window.crypto.getRandomValues) !== "undefined") {
let buf: Uint16Array = new Uint16Array(8);
window.crypto.getRandomValues(buf);
let result = (this.pad4(buf[0]) + this.pad4(buf[1]) + this.pad4(buf[2]) + this.pad4(buf[3]) + this.pad4(buf[4]) + this.pad4(buf[5]) + this.pad4(buf[6]) + this.pad4(buf[7]));
return result;
} else {
let result = this.random4() + this.random4() + this.random4() + this.random4()+
this.random4() + this.random4() + this.random4() + this.random4();
return result;
}
}
static primaryKey(): string {
let uuid = SKDataUtil.uuid();
let count = SKDataUtil.random(1,5);
for (let i = 0; i < count; i++) {
let index = SKDataUtil.random(1, uuid.length)
let split = uuid.split("");
split.splice(index, 1);
uuid = split.join("");
}
return uuid;
}
static rolePrimaryKey(number: any): any {
let num = '';
for (let i = 0; i < number; i++) {
if (i == 0) {
num += Math.floor(Math.random() * 9 + 1);
} else {
num += Math.floor(Math.random() * 10);
}
}
return parseInt(num);
}
private static pad4(num: number): string {
let ret: string = num.toString(16);
while (ret.length < 4) {
ret = "0" + ret;
}
return ret;
}
private static random4(): string {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
// 随机数
static random(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 从数组中随机抽取一项
static randomList(value: any[]): any {
if (value == null || value.length < 1) {
return null;
}
let index = this.random(0, value.length - 1);
return value[index];
}
// 数组洗牌
static shuffle(value: any[]): any[] {
if (!Array.isArray(value)) {
return null;
}
let result = this.clone(value);
let j: number;
for (let i = result.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
static randomListBy(list: any[], total: number): any[] {
if (list == null || list.length < total) {
return [];
}
list = this.clone(list);
let result = [];
for (let i = 0; i < total; i++) {
let random = Math.floor(Math.random() * list.length);
result.push(list.splice(random, 1)[0]);
}
return result;
}
// 替换指定位置的字符
static replaceChar(value: string, index: number, char: string): string {
return value.substr(0, index) + char + value.substr(index + char.length);
}
// 从前查找相同字符出现的最后一个位置
static sameIndex(value: string, char: string): number {
var result: number = -1;
for (var i = 0; i < value.length; i++) {
if (value[i] == char) {
if (result == -1) {
result = i;
} else {
result++;
}
} else if (result != -1) {
break;
}
}
return result;
}
// 从后查找相同字符出现的最后一个位置
static sameLastIndex(value: string, char: string): number {
var result: number = -1;
for (var i = value.length - 1; i > 0; i--) {
if (value[i] == char) {
if (result == -1) {
result = i;
} else {
result--;
}
} else if (result != -1) {
break;
}
}
return result;
}
// 格式化数字不足位补0
static prefixInteger(num: number, length: number): string {
let result = (Array(length).join('0') + num).slice(-length);
return result;
}
// "YYYY-mm-dd HH:MM"
static formatDate(format: string, value: string): string {
let now = new Date(value);
let opt: any = {
"Y+": now.getFullYear().toString(),
"m+": (now.getMonth() + 1).toString(),
"d+": now.getDate().toString(),
"H+": now.getHours().toString(),
"M+": now.getMinutes().toString(),
"S+": now.getSeconds().toString(),
}
let result: RegExpExecArray;
for (let key in opt) {
result = new RegExp(`(${key})`).exec(format);
if (result) {
format = format.replace(result[1], (result[1].length == 1) ? (opt[key]) : (opt[key].padStart(result[1].length, "0")));
}
}
return format;
}
// 根据ID生成5位邀请码
static encodeInvite(value: number) {
let key = 'E50CDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ';
let result = "";
while (value > 0) {
let mod = value % 35;
value = (value - mod) / 35;
result = key[mod] + result;
}
if (result.length < 5) {
result = "F" + result;
let len = result.length;
let min = 0;
let max = key.length - 1;
for (let i = len; i < 5; i++) {
let index = Math.floor(Math.random() * (max - min + 1)) + min;
result = key[index] + result;
}
}
return result;
}
// 解码邀请码
static decodeInvite(value: string): number {
let key = 'E50CDG3HQA4B1NOPIJ2RSTUV67MWX89KLYZ';
let index = value.indexOf("F");
if (index != -1) {
value = value.slice(index + 1, value.length);
}
let result = 0;
let p = 0;
for (let i = value.length - 1; i >= 0; i--) {
let char = value[i];
index = key.indexOf(char);
if (index != -1) {
result += index * Math.pow(35, p);
p++;
}
}
return result;
}
// 从数组中获得匹配
static numberByList(list: any[], key: string, value: number, targetKey: string): any {
for (let item of list) {
let temp = item[key];
if (!temp) {
continue;
}
if (temp == value) {
let result = item[targetKey];
return result;
}
}
return null;
}
// 从数组中获得匹配
static datalist(list: any[], key: string): any {
for (let item of list) {
if (item == key) {
return item;
}
}
return null;
}
//随机从数组中取出一条数据
static radom(array: any,start: number = 1){
let end: number = array.length; // end不传为数组长度
start--;
const index = start + Math.floor(Math.random() * (end - start));
return array[index];
}
// 是否存在KV
static hasKVByList(list: any[], key: string, value: any): boolean {
for (let item of list) {
if (item[key] == value) {
return true;
}
}
return false;
}
// 是否有互斥
static hasMutex(target: any[], mutex: any[], mutexKey: string, key: string): boolean {
for (let item of target) {
let temp = item[key];
if (!temp) {
continue;
}
// 如果是互斥数组
if (Array.isArray(temp)) {
for (let b of mutex) {
for (let a of temp) {
if (item[key] == b) {
return true;
}
}
}
} else {
for (let b of mutex) {
if (temp == b) {
return true;
}
}
}
}
return false;
}
// 获得二维数组中的数值
static intOf2Array(list: number[][], one: number, two: number, valid: number): number {
if (!list) {
return valid;
}
if (one < 0 || one >= list.length) {
return valid;
}
let temp = list[one] as number[];
if (!temp) {
return valid;
}
if (two < 0 || two >= temp.length) {
return valid;
}
return temp[two];
}
// 根据概率返回真假
static probability(min: number, max: number): boolean {
let result = SKDataUtil.random(min, max);
if (result <= min) {
return true;
} else {
return false;
}
}
static dictByDict(target: any, sheet: any, key: any): any {
if (!target) {
return null;
}
let dict = target[sheet];
if (!dict) {
return null;
}
let result = dict[key];
return result;
}
static findByDict(target: any, targetKey: any, value: any, resultKey: any, valid: any): any {
if (!target) {
return valid;
}
for (let key in target) {
let item = target[key];
if (item[targetKey] == value) {
return item[resultKey];
}
}
return valid;
}
static numberByString(target: string, splitter: string, index: number, valid: number = 0): number {
if (!target) {
return valid;
}
let temp = target.split(splitter);
if (temp.length < index) {
return valid;
}
let result = parseInt(temp[index]);
return result;
}
static readJson(filename: string): any {
let file = path.join(__dirname, filename);
let data = fs.readFileSync(file);
let result: any = SKDataUtil.jsonBy(data.toString());
return result;
}
// JSON解析
static jsonBy(value: any): any {
if (value == null) {
return null;
}
if (!this.isString(value)) {
return value;
}
if (this.isEmptyString(value)) {
return null;
}
let result: any = null;
try {
result = JSON.parse(value);
} catch (error) {
SKLogger.warn(`JSON解析:${value},失败:${error}`);
} finally {
return result;
}
}
// 转换成JSON字符串
static toJson(value: any, valid: string): string {
if (value == null) {
return valid;
}
if (this.isString(value)) {
return value;
}
let result: string = valid;
try {
result = JSON.stringify(value);
} catch (error) {
SKLogger.warn(`JSON转换:${value},失败:${error}`);
} finally {
return result;
}
}
// 是否为数字
static isNumber(value: any): boolean {
if (value == null) {
return false;
}
let type = typeof value;
if (type !== 'number') {
return false;
}
return true;
}
// 转换成数字
static numberBy(value: any, valid: number = NaN): number {
if (this.isNumber(value)) {
return value;
}
if (value == "undefined") {
return valid;
}
if (this.isString(value)) {
let result = parseFloat(value);
return result;
}
return valid;
}
// 转换成字符串
static stringBy(value: any): string {
if (value == null) {
return "";
}
if (this.isString(value)) {
return value;
}
let result = `${value}`;
return result;
}
// 中字符串超长作固定长度加省略号(...)处理
static beautySub(str: any, len: number) {
var reg = /[\u4e00-\u9fa5]/g, //专业匹配中文
slice = str.substring(0, len),
chineseCharNum = (~~(slice.match(reg) && slice.match(reg).length)),
realen = slice.length * 2 - chineseCharNum;
return str.substr(0, realen) + (realen < str.length ? "..." : "");
}
// 数组去重
static unique(quan_lst: any) {
//去掉重复选取的数据
for (let i = 0; i < quan_lst.length; i++) {
for (let j = i + 1; j < quan_lst.length;) {
if (quan_lst[i][0] == quan_lst[j][0]) {
quan_lst.splice(j, 1);//去除重复的对象;
} else {
j++;
}
}
}
return quan_lst;
}
// sort方法根据数组中对象的某一个属性值进行排序
static compare(property: any){
return function(a: any, b: any){
let value_one = a[property];
let value_two = b[property];
return value_one - value_two;
}
}
// 版本号比较
static checkVersion(versionA: string, versionB: string): number {
let vA = versionA.split('.');
let vB = versionB.split('.');
for (let i = 0; i < vA.length; ++i) {
let a = parseInt(vA[i]);
let b = parseInt(vB[i] || '0');
if (a === b) {
continue;
} else {
return a - b;
}
}
if (vB.length > vA.length) {
return -1;
}
return 0;
}
// 是否为数组
static isArray(value: any): boolean {
if (value == null) {
return false;
}
let result = Array.isArray(value);
return result;
}
// 是否为对象
static isObject(value: any): boolean {
if (value == null) {
return false;
}
let type = typeof value;
if (type === "object") {
return true;
}
return false;
}
// 是否为字符串
static isString(value: any): boolean {
let result = typeof (value);
if (result == "string") {
return true;
}
return false;
}
// 判断对象是否为空
static isEmptyObject(value: any): boolean {
let keys = Object.keys(value);
if (keys.length < 1) {
return true;
}
return false;
}
// 是否为空字符串
static isEmptyString(value: any): boolean {
if (value == null) {
return true;
}
if (!this.isString(value)) {
return true;
}
let result = <string>value;
if (result.length < 1) {
return true;
}
return false;
}
// 是否为空数组
static isEmptyArray(value: any): boolean {
if (value == null) {
return true;
}
if (!this.isArray(value)) {
return true;
}
let result = <any[]>value;
if (result.length < 1) {
return true;
}
return false;
}
/**
* 判断点与圆心之间的距离和圆半径的关系
* @param pointX 圆的中心点 X
* @param pointY 圆的中心点 Y
* @param circleX 判断位置的中心点 X
* @param circleY 判断位置的中心点 Y
* @param radius 半径
*/
static confineTo(pointX: number, pointY: number, circleX: number, circleY: number, radius: number): boolean {
let dist: number = Math.hypot((pointX - circleX), (pointY - circleY));
let range: number = radius;
if (dist > range) {
return false;
} else if (dist < range) {
return true;
} else {
return false;
}
}
/**
* 在一定范围随机生成经纬度
* @param MinLon 最小经度
* @param MaxLon 最大经度
* @param MinLat 最小纬度
* @param MaxLat 最大纬度
*/
static randomLonLat(MinLon: number, MaxLon: number, MinLat: number, MaxLat: number): any{
let lon: number = Math.floor(Math.random() * (MaxLon - MinLon) + MinLon);
let lat: number = Math.floor(Math.random() * (MaxLat - MinLat) + MinLat);
let site: any = [lon,lat];
return site;
}
// 正则测试
static testRegExp(value: string, regexp: RegExp, min: number, max: number, tip: string) {
if (value.length < min) {
return `长度不能少于${min}`;
}
if (value.length > max) {
return `最多只能${max}`;
}
let temp = regexp.test(value);
if (!temp) {
return tip;
}
return "";
}
// 获得键值
static valueForKey(target: any, key: any): any {
if (!target) {
console.warn(`$警告:获得键值,对象不存在`);
return null;
}
if (key == null) {
console.warn(`$警告:获得键值,对象${target}KEY值不存在`);
return null;
}
if (this.isArray(target)) {
return target[key];
}
if (!this.isObject(target)) {
console.warn(`$警告:获得键值,${target}[${key}]不是一个对象`);
return null;
}
let temp: any = <Object>target;
if (!temp.hasOwnProperty(key)) {
return null;
}
return temp[key];
}
// 检查是否合法
static checkValid(value: string): string {
if (value == null || value.length < 1) {
return "不能为空!";
}
let temp = value.trim();
if (value.length != temp.length) {
return "两侧不能有空格";
}
let list = [{ char: "\"", text: "双引号" },
{ char: "\'", text: "单引号" }];
for (let item of list) {
if (value.indexOf(item.char) != -1) {
return `不能包含${item.text}`;
}
}
return "";
}
// 检查MYSQL转义
static checkMYSQL(value: string): string {
if (value == null) {
return "";
}
value.replace(/g\'/g, "\'");
value.replace(/g\"/g, "\"");
return value;
}
static trim(value: string): string {
if (value == null || value.length < 1) {
return "";
}
value = value.trim();
return value;
}
// 获得最大值经验值
static maxExp(config: any, exp: number): any {
let result = exp;
for (let key in config) {
let item = config[key];
if (exp < item.exp) {
result = item.exp;
break;
}
}
return result;
}
// 获得当前阶段的经验值与最大值
static currentExp(config: any, exp: number): any {
let result: any = { value: 0, max: 0 };
for (let key in config) {
let item = config[key];
let level = item.id;
let max_exp = item.exp;
if (exp < max_exp) {
if (level > 1) {
let prev = config[`${level - 1}`];
result.value = exp - prev.exp;
result.max = max_exp - prev.exp;
} else {
result.value = exp;
result.max = max_exp;
}
break;
}
}
return result;
}
// 是否有属性
static hasProperty(target: any, key: string | number | symbol): boolean {
if (target == null) {
return false;
}
if (!this.isObject(target)) {
return false;
}
let result = <Object>target;
if (result.hasOwnProperty(key)) {
return true;
}
return false;
}
// 获得字典长度
static getLength(value: any): number {
if (value == null) {
return 0;
}
if (Array.isArray(value)) {
return value.length;
}
let length = 0;
for (let _key in value) {
length++;
}
return length;
}
// 对键求和
static getKeyTotal(value: any): number {
let total: number = 0;
for (let key in value) {
let count = parseInt(key);
total += count;
}
return total;
}
// 深度拷贝
static clone(value: any): any {
if (value == null) {
return null;
}
let result: any = Array.isArray(value) ? [] : {};
try {
result = SKDataUtil.jsonBy(SKDataUtil.toJson(value, null));
} catch (error) {
let info = `警告:深拷贝对象:${value},失败:${error}`
console.warn(info);
} finally {
return result;
}
}
// 克隆类实例
static cloneClass(value: any): any {
if (value == null) {
return null;
}
let result = Object.create(
Object.getPrototypeOf(value),
Object.getOwnPropertyDescriptors(value)
);
return result;
}
// 是否在范围内
static atRange(target: number, range: number[]): boolean {
if (this.isEmptyArray(range)) {
return false;
}
for (let item of range) {
if (target == item) {
return true;
}
}
return false;
}
// 是否在范围内
static atRangeByString(target: string, keys: string[]): boolean {
for (let key of keys) {
if (target == key) {
return true;
}
}
return false;
}
static clamp(value: number, min: number, max: number): number {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
// 去除重复
static removeRepeat(list: any[], key: string): any[] {
if (list == null || !this.isArray(list) || list.length < 1) {
return [];
}
let filters = [];
let result: any[] = [];
for (let i = 0; i < list.length; i++) {
let item = list[i]
let value = item[key];
if (value) {
if (filters.indexOf(value) == -1) {
filters.push(value);
} else {
list.splice(i, 1);
i--;
result.push(item);
}
}
}
return result;
}
// 获得数组元素
static getItemBy(list: any[], index: number): any {
if (!this.isArray(list)) {
return null;
}
if (index < 0 || index >= list.length) {
return null;
}
let result = list[index];
return result;
}
// 保留2位小数
static toDecimal2(value: number): number {
let temp = this.numberBy(value);
if (isNaN(temp)) {
return 0;
}
temp = Math.round(value * 100) / 100;
return temp;
}
// 从字典中随机抽取一项
static randomDict(value: any[]): any {
if (value == null) {
return null;
}
let length = this.getLength(value);
if (length < 1) {
return null;
}
let index = this.random(0, length - 1);
let data = this.dataOfDictByIndex(value, index);
return data;
}
// 获得字典第几个数据
static dataOfDictByIndex(dict: any, index: number): any {
if (index < 0 || index >= SKDataUtil.getLength(dict)) {
return null;
}
let count = -1;
for (let key in dict) {
count++;
if (count == index) {
return dict[key];
}
}
return null;
}
// 获得定义值
static getDefault(value: any, valid: any): any {
if (typeof value == "undefined")
return valid;
return value;
}
// 转换Long到53位安全数字
static fixedLong(data: any): any {
let type = typeof data;
if (type === "object") {
for (let key in data) {
let value = data[key];
let type = value.constructor.name;
if (type === "object") {
data[key] = this.fixedLong(value);
continue;
}
if (type === "array") {
let index = 0;
for (let item of value) {
value[index] = this.fixedLong(item);
index++;
}
}
if (type === "Long") {
let temp = value.toNumber();
if (!Number.isSafeInteger(temp)) {
temp = Number.MAX_SAFE_INTEGER;
}
data[key] = temp;
continue;
}
}
}
return data;
}
//阿拉伯数字转中文数字
static noToChinese(num: any) {
if (!/^\d*(\.\d*)?$/.test(num)) {
alert("Number is wrong!");
return "Number is wrong!";
}
let AA = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
let BB = new Array("", "十", "百", "千", "万", "亿", "点", "");
let a: any = ("" + num).replace(/(^0*)/g, "").split("."),
k: any = 0,
re: any = "";
for (let i = a[0].length - 1; i >= 0; i--) {
switch (k) {
case 0:
re = BB[7] + re;
break;
case 4:
if (!new RegExp("0{4}\\d{" + (a[0].length - i - 1) + "}$").test(a[0]))
re = BB[4] + re;
break;
case 8:
re = BB[5] + re;
BB[7] = BB[5];
k = 0;
break;
}
if (k % 4 == 2 && a[0].charAt(i + 2) != 0 && a[0].charAt(i + 1) == 0) re = AA[0] + re;
if (a[0].charAt(i) != 0) re = AA[a[0].charAt(i)] + BB[k % 4] + re;
k++;
}
if (a.length > 1) //加上小数部分(如果有小数部分)
{
re += BB[6];
for (let i = 0; i < a[1].length; i++) re += AA[a[1].charAt(i)];
}
return re;
}
static filter(text: any) {
// 红色 蓝色 绿色 分数 紫色
let list: any = [
{reg: new RegExp('#R'),color: `</c ><color=#ff0000 >`},
{reg: new RegExp('#L'),color: `</c ><color=#0000ff >`},
{reg: new RegExp('#G'),color: `</c ><color=#228b22 >`},
{reg: new RegExp('#P'),color: `</c ><color=#ff00ff >`},
{reg: new RegExp('#V'),color: `</c ><color=#8a2be2 >`}
];
for (let i = 0; i < list.length; i++) {
text = text.replace(list[i].reg, list[i].color);//替换HTML标签$&
}
if (text.split("</c >").length > 1){
text = text.slice(5) + "</c >"
}
return text;
};
}