完成构建类关系

下一步:构建实体关系,相关接口调试。
main
expressgy 10 months ago
parent f952e55e20
commit eaeedc0080
  1. BIN
      graphResource2数据结构.xmind
  2. 492
      src/cache/index.js
  3. 2
      src/common/database/dataModels/resourceClassBase.dataModel.js
  4. 9
      src/common/database/dataModels/resourceClassRelation.dataModel.js
  5. 7
      src/common/database/dataModels/resourceEntityStruct.dataModel.js
  6. 2
      src/common/database/index.js
  7. 6
      src/routes/graphResource2/baseDict/index.js
  8. 2
      src/routes/graphResource2/index.js
  9. 657
      src/routes/graphResource2/resourceClass/index.dto.js
  10. 137
      src/routes/graphResource2/resourceClass/index.js
  11. 131
      test/makeClass.js
  12. 36
      test/testClassExtents.js

Binary file not shown.

492
src/cache/index.js vendored

@ -10,10 +10,56 @@
// | -*-*-*- (时间--修改人--修改说明) -*-*-*- // | -*-*-*- (时间--修改人--修改说明) -*-*-*-
// | = // | =
// | ------------------------------------------------------------ // | ------------------------------------------------------------
import { Op } from 'sequelize'; import { DataTypes, Model, Op } from 'sequelize';
import { makeTreeForList } from '#common/tools/makeTree.js'; import { makeTreeForList } from '#common/tools/makeTree.js';
import makeObject from '#common/tools/makeObject.js'; import makeObject from '#common/tools/makeObject.js';
// 数据库字段类型对照
const SQLType = {
0: {
name: '字符串',
identify: 'string',
length: [0, 4096],
sequelizeType: DataTypes.STRING,
},
1: {
name: '布尔值',
identify: 'boolean',
length: null,
sequelizeType: DataTypes.BOOLEAN,
},
2: {
name: '整数',
identify: 'integer',
length: null,
sequelizeType: DataTypes.INTEGER,
},
3: {
name: '数字',
identify: 'double',
length: null,
sequelizeType: DataTypes.DOUBLE,
},
};
// @ excludeFieldName - Object - 描述: 数据库排除字段
const excludeFieldName = [
[
'entityid',
'uniquereference',
'originnode',
'creator',
'isdelete',
'entityname',
'createtimestamp',
'updatetimestamp',
],
];
// @ sequelizeFindAllType - Object - 描述: 查询参数
const sequelizeFindAllType = {
raw: true, // 原始数据
mapToModel: true, // 将下划线变成驼峰
};
export async function createCatch(sequelize) { export async function createCatch(sequelize) {
global.resourceCache = {}; global.resourceCache = {};
// atomModelCache // atomModelCache
@ -22,7 +68,7 @@ export async function createCatch(sequelize) {
global.resourceCache.atomModelPool = atomModelPool; global.resourceCache.atomModelPool = atomModelPool;
const atomModelPoolEndTime = performance.now(); const atomModelPoolEndTime = performance.now();
logger.fatal( logger.fatal(
`元分类/模型缓存加载完毕: atomModelPool - ${ `${'元分类/模型缓存加载完毕: atomModelPool'.padEnd(30, ' ')} | ${
atomModelPoolEndTime - atomModelPoolStartTime atomModelPoolEndTime - atomModelPoolStartTime
} ms`, } ms`,
); );
@ -33,10 +79,21 @@ export async function createCatch(sequelize) {
global.resourceCache.baseDictPool = baseDictPool; global.resourceCache.baseDictPool = baseDictPool;
const baseDictPoolEndTime = performance.now(); const baseDictPoolEndTime = performance.now();
logger.fatal( logger.fatal(
`数据字典缓存加载完毕: baseDictPool - ${ `${'数据字典缓存加载完毕: baseDictPool'.padEnd(30, ' ')} | ${
baseDictPoolEndTime - baseDictPoolStartTime baseDictPoolEndTime - baseDictPoolStartTime
} ms`, } ms`,
); );
// resourceCache
const resourcePoolStartTime = performance.now();
const resourcePool = await makeResourceCache(sequelize);
global.resourceCache.resourcePool = resourcePool;
const resourcePoolEndTime = performance.now();
logger.fatal(
`${'资源数据缓存加载完毕: resourcePool'.padEnd(30, ' ')} | ${
resourcePoolEndTime - resourcePoolStartTime
} ms`,
);
} }
async function makeAtomModelCache(sequelize) { async function makeAtomModelCache(sequelize) {
@ -172,3 +229,432 @@ async function makeBaseDictCache(sequelize) {
}; };
return baseDictPool; return baseDictPool;
} }
async function makeResourceCache(sequelize) {
// @ RAP - Object - 描述:元分类/模型缓存池
const RAP = resourceCache.atomModelPool;
// @ RBP - Object - 描述:字典缓存池
const RBP = resourceCache.baseDictPool;
// ! 获取资源类信息 =============================================================================
// region 获取资源类信息
const classList =
await sequelize.models.ResourceClassBase.findAll(sequelizeFindAllType);
// @ classIdList - Array[number] - 描述:资源类ID列表
const classIdList = [];
// @ classObject - Object[resourceClassBase] - 描述:资源类对象
const classObject = {};
for (let i = 0; i < classList.length; i++) {
// 资源类对象
const classItem = classList[i];
// 资源类ID
const classId = classList[i].resourceClassBaseId;
// 将资源类ID放入资源类ID列表
classIdList.push(classId);
// 将资源类对象放入集合中
classObject[classId] = classItem;
// 读取atomModel,元分类/模型信息
const atomModelId = classItem.atomModel;
// 从原分类/模型中拿到其名称
classItem.atomModelName = RAP.objectData[atomModelId].atomModelName;
// 读取resourceClassBaseDefine, 资源类定义catch提的,目前缺省
// | 待补充
// 读取resourceClassBaseType,资源类型定义,0实体、1虚拟、2管理层级
classItem.resourceClassBaseTypeName = {
0: '实体资源类',
1: '虚拟资源类',
2: '层级管理资源类',
}[classItem.resourceClassBaseType];
}
// console.log(classIdList)
// console.log(classObject)
// endregion
// ! 获取拓展字段信息 ===========================================================================
// region 获取拓展字段信息
const classExpandList =
await sequelize.models.ResourceClassExpandField.findAll(
sequelizeFindAllType,
);
// @ classExpandIdList - Array[number] - 描述:资源类拓展字段ID列表
const classExpandIdList = [];
// @ classExpandObject - Object[resourceClassExpandField] - 描述:资源类拓展字段对象
const classExpandObject = {};
// @ classExpandForClassIdObject - Object - 描述:基于资源类ID的资源拓展字段对象集合
const classExpandForClassIdObject = {};
for (let i = 0; i < classExpandList.length; i++) {
const expandField = classExpandList[i];
if (expandField.isDelete) {
// 如果删除
continue;
}
const expandFieldId = expandField.resourceClassExpandFieldId;
if (expandField.resourceClassExpandFieldRelationType === 0) {
// ! 如果是字典值
expandField.resourceClassExpandFieldRelation =
RBP.atomObject[
expandField.resourceClassExpandFieldValue
].baseDictName;
} else if (expandField.resourceClassExpandFieldRelationType === 1) {
// ! 如果是资源类
expandField.resourceClassExpandFieldRelation =
classObject[
expandField.resourceClassExpandFieldIdentify
].resourceClassBaseName;
} else {
expandField.resourceClassExpandFieldRelation = null;
}
classExpandIdList.push(expandFieldId);
classExpandObject[expandFieldId] = expandField;
// 拓展字段中的资源类ID
const classId = expandField.resourceClassBase;
if (classExpandForClassIdObject[classId] === undefined) {
classExpandForClassIdObject[classId] = {
[expandField.resourceClassExpandFieldIdentify]: expandField,
};
} else {
classExpandForClassIdObject[classId][
expandField.resourceClassExpandFieldIdentify
] = expandField;
}
}
// console.log(classExpandForClassIdObject)
// endregion
// ! 获取实体结构表 =============================================================================
// region 获取实体结构表
const entityStructList =
await sequelize.models.ResourceEntityStruct.findAll(
sequelizeFindAllType,
);
// @ entityStructIdList - Array[number] - 描述:资源实体结构id列表
const entityStructIdList = [];
// @ entityStructObject - Object[resourceEntityStruct] - 描述:资源实体结构对象集合
const entityStructObject = {};
// @ entityStructForClassBaseObject - Object - 描述:基于资源类ID的资源实体字段对象集合
const entityStructForClassBaseObject = {};
for (let i = 0; i < entityStructList.length; i++) {
const entityStruct = entityStructList[i];
if (entityStruct.isDelete) {
// 如果删除
continue;
}
const entityStructId = entityStruct.resourceEntityStructId;
entityStructIdList.push(entityStructId);
entityStructObject[entityStructId] = entityStruct;
// 获取存储类型和长度
entityStruct.storageTypeValue =
SQLType[entityStruct.resourceEntityStructStorageType];
// 这里的关联值不做处理
// 拓展字段中的资源类ID
const classId = entityStruct.resourceClassBase;
if (entityStructForClassBaseObject[classId] === undefined) {
entityStructForClassBaseObject[classId] = {
[entityStruct.resourceEntityStructIdentify]: entityStruct,
};
} else {
entityStructForClassBaseObject[classId][
entityStruct.resourceEntityStructIdentify
] = entityStruct;
}
}
// endregion
// ! 获取资源类关系表 ===========================================================================
// region 获取资源类关系表
const classRelationList =
await sequelize.models.ResourceClassRelation.findAll(
sequelizeFindAllType,
);
// @ classRelationIdList - String - 描述:获取资源类关系ID列表
const classRelationIdList = [];
// @ classRelationObject - String - 描述:获取资源类关系对象集合
const classRelationObject = {};
// @ classRelationObjectForTargetClass - String - 描述:获取资源类关系对象集合继续目标资源类
const classRelationObjectForTargetClass = {};
for (let i = 0; i < classRelationList.length; i++) {
const classRelationItem = classRelationList[i];
const classRelationItemId = classRelationItem.resourceClassRelationId;
const targetId = classRelationItem.resourceClassRelationTarget;
classRelationIdList.push(classRelationItem);
classRelationObject[classRelationItemId] = classRelationItem;
if (classRelationObjectForTargetClass[targetId] === undefined) {
classRelationObjectForTargetClass[targetId] = [classRelationItem];
} else {
classRelationObjectForTargetClass[targetId].push(classRelationItem);
}
}
// endregion
// | 这里生成基础数据模型 =============================================================================================
// region 这里生成基础数据模型
class makeClass {
// 基础数据
classId;
classIdentify;
atomModel;
className;
classBase;
classExpand;
entityStruct;
sequelizeModel;
constructor(classItem) {
// 设置基础信息值
const classId = classItem.resourceClassBaseId;
this.classId = classId;
const classIdentify = classItem.resourceClassBaseIdentify;
this.classIdentify = classIdentify;
const atomModel = classItem.atomModel;
this.atomModel = atomModel;
const className = classItem.resourceClassBaseName;
this.className = className;
this.classBase = classItem;
this.classExpand = classExpandForClassIdObject[classId];
if (this.classExpand === undefined) {
this.classExpand = {};
}
this.entityStruct = entityStructForClassBaseObject[classId];
if (this.entityStruct === undefined) {
this.entityStruct = {};
}
this.classRelationList = classRelationObjectForTargetClass[classId];
if (this.classRelationList == undefined) {
this.classRelationList = [];
}
this.#createCLassRelationIdentify();
this.#createSequelizeModel();
}
get classId() {
return this.classId;
}
get classIdentify() {
return this.classIdentify;
}
get atomModel() {
return this.atomModel;
}
get className() {
return this.className;
}
get classBase() {
return this.classBase;
}
get classExpand() {
return this.classExpand;
}
get entityStruct() {
return this.entityStruct;
}
#createSequelizeModel() {
class resourceEntity extends Model {}
// 定义资源实体名称字段
const entityName = {
type: DataTypes.STRING(255),
allowNull: false,
comment: '资源实体名称',
};
// 定义资源实体名称字段在数据库的备注
if (this.entityStruct.entityName !== undefined) {
entityName.comment =
this.entityStruct.entityName.resourceEntityStructName +
'-' +
this.entityStruct.entityName.resourceEntityStructDescribe;
}
// 定义字段
const defineField = {
// 在这里定义模型属性
entityId: {
type: DataTypes.STRING(64),
primaryKey: true,
allowNull: false,
comment: '资源实体ID',
},
// 自增列
uniqueReference: {
type: DataTypes.INTEGER,
autoIncrement: true,
allowNull: false,
unique: true, // 使得自增值是唯一的
comment: '唯一参考',
},
entityName,
// 所在节点
originNode: {
type: DataTypes.STRING,
allowNull: false,
comment: '所在的资源类节点',
},
creator: {
type: DataTypes.STRING,
comment: '创建人',
},
isDelete: {
type: DataTypes.STRING(64),
comment: '删除时间',
},
};
// 定义数据表
const defineTable = {
// 这是其他模型参数
sequelize, // 我们需要传递连接实例
modelName: this.classIdentify.toLowerCase(), // 我们需要选择模型名称
tableName: 'entity_' + this.classIdentify.toLowerCase(),
comment: '元分类/模型表',
timestamps: true, // 不要忘记启用时间戳!
createdAt: 'createTimestamp', // 不想要 createdAt
// 想要 updatedAt 但是希望名称叫做 updateTimestamp
updatedAt: 'updateTimestamp',
underscored: true, // 改成下划线格式
};
for (let fieldName in this.entityStruct) {
// 排除固定字段
if (excludeFieldName.includes(fieldName.toLowerCase())) {
continue;
}
// 拿到字段详细信息
const oneEntityStruct = this.entityStruct[fieldName];
// 建立数据库表字段标准数据
const oneField = {
type: DataTypes.STRING, // 对于意外数据,设置默认长度255的字符串
comment:
oneEntityStruct.resourceEntityStructName +
'-' +
oneEntityStruct.resourceEntityStructDescribe,
};
// 拿到自定义的存储类型
const fieldStorageType =
this.entityStruct[fieldName]
.resourceEntityStructStorageType;
if (
Object.keys(SQLType).includes(fieldStorageType.toString())
) {
// 如果该自定义存储类型在预设的存储类型中,设置自定义的存储类型
oneField.type = SQLType[fieldStorageType].sequelizeType;
if (
fieldStorageType == 0 &&
oneEntityStruct.resourceEntityStructStorageLength < 4096
) {
// 如果是字符串,并且长度在0 > 4096,设置其长度
oneField.type = oneField.type(
parseInt(
oneEntityStruct.resourceEntityStructStorageLength,
),
);
}
}
// 将该字段放入数据库字段定义对象中
defineField[fieldName] = oneField;
}
resourceEntity.init(defineField, defineTable);
// 同步数据库
resourceEntity.sync(); // {alter: true}是修改,现在是如果存在就不更改表,后面修改表明,字段名,字段长度会在更新操作时添加
this.sequelizeModel = resourceEntity;
}
#createCLassRelationIdentify() {
this.relationIdList = [];
if (this.classRelationList.length != 0) {
for (let classRelationItem of this.classRelationList) {
this.relationIdList.push(
classRelationItem.resourceClassRelationId,
);
}
}
}
}
// @ classModelObject - Object - 描述:资源类数据模型对象集合
const classModelObject = {};
for (let classId of classIdList) {
if (classObject[classId].isDelete) {
// 排除删除的
continue;
}
classModelObject[classId] = new makeClass(classObject[classId]);
}
// console.log(classModelObject)
// console.log((await sequelize.models.omencust.create({
// entityId: 'omencust' + crypto.randomUUID(),
// originNode: '0-0-1',
// entityName: '中天合创',
// address: '鄂尔多斯',
// util: '54',
// project: '1'
// })).dataValues)
// endregion
// | 这里生成关系节点数据模型 ==========================================================================================
// 给每个类生成关联列表
const classNodeObject = {
0: {
classModel:{},
nodeId:0,
children:[],
father: null
}
};
class makeClassNode {
constructor(classModel, classRelationId) {
this.classModel = classModel;
this.children = []
this.nodeId =classRelationId;
this.father = classRelationObject[classRelationId].resourceClassRelationFather
}
}
// @ 生成类关系节点
for (let classId of Object.keys(classModelObject)) {
const classModel = classModelObject[classId];
const relationIdList = classModel.relationIdList
if(relationIdList.length == 0){
// @ 在数据库添加这条关系
const newClassRelation = await sequelize.models.ResourceClassRelation.create({
resourceClassRelationFather: 0,
resourceClassRelationTarget: classId
})
const newClassRelationData = newClassRelation.dataValues;
const newClassRelationDataId = newClassRelationData.resourceClassRelationId
classRelationIdList.push(newClassRelationData)
classRelationObject[newClassRelationDataId] = newClassRelationData
classRelationObjectForTargetClass[classId] = newClassRelationData
relationIdList.push(newClassRelationDataId)
classNodeObject[newClassRelationDataId] = new makeClassNode(
classModel,
newClassRelationDataId,
);
}
for(let i = 0; i< relationIdList.length; i++){
const classRelationId = relationIdList[i];
classNodeObject[classRelationId] = new makeClassNode(
classModel,
classRelationId,
);
}
}
// @ 构建类关系
for(let i = 0; i < classRelationIdList.length; i++){
const nodeId = classRelationIdList[i].resourceClassRelationId;
const classNode = classNodeObject[nodeId]
const fatherId = classNodeObject[nodeId].father
classNodeObject[fatherId].children.push(classNode)
}
console.log(classNodeObject)
// ! 获取资源实体 ===============================================================================
// region 获取资源实体
// @ entityListForClassId - Object[Array[Object[entity]]] - 描述:实体列表根据资源类ID区分的对象
const entityListForClassId = {};
// @ entityListForClassIdentify - Object[Array[Object[entity]]] - 描述:实体列表根据资源类Identify区分的对象
const entityListForClassIdentify = {};
for (let i of Object.keys(classModelObject)) {
const classModel = classModelObject[i];
const classId = classModel.classId;
const classIdentify = classModel.classIdentify;
const entityList = await classModel.sequelizeModel.findAll({
...sequelizeFindAllType,
where: {
isDelete: {
[Op.is]: null,
},
},
});
entityListForClassId[classId] = entityList;
entityListForClassIdentify[classIdentify] = entityList;
}
// endregion
// ! 获取资源实体关系
}

@ -70,7 +70,7 @@ export function mountResourceClassBase(sequelize, DataTypes) {
}, },
resourceClassBaseColor:{ resourceClassBaseColor:{
type: DataTypes.STRING, type: DataTypes.STRING,
comment: '资源类', comment: '资源类颜色',
}, },
resourceClassBaseAvatar:{ resourceClassBaseAvatar:{
type: DataTypes.STRING, type: DataTypes.STRING,

@ -25,15 +25,16 @@ export function mountResourceClassRelation(sequelize, DataTypes) {
allowNull: false, allowNull: false,
comment: '资源类关系ID' comment: '资源类关系ID'
}, },
resourceClassRelationTarget: { resourceClassRelationFather: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
comment: '目标资源类ID' defaultValue: 0,
comment: '父资源类ID'
}, },
resourceClassRelationFather: { resourceClassRelationTarget: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
comment: '资源类ID' comment: '目标资源类ID'
}, },
creator:{ creator:{
type: DataTypes.STRING, type: DataTypes.STRING,

@ -57,6 +57,7 @@ export function mountResourceEntityStruct(sequelize, DataTypes) {
resourceEntityStructStorageType:{ resourceEntityStructStorageType:{
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: false,
default: 0,
comment: '资源实体字段在数据库中存储的类型', comment: '资源实体字段在数据库中存储的类型',
}, },
resourceEntityStructStorageLength:{ resourceEntityStructStorageLength:{
@ -99,12 +100,16 @@ export function mountResourceEntityStruct(sequelize, DataTypes) {
}, },
resourceEntityStructRelationType:{ resourceEntityStructRelationType:{
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
comment: '资源实体字段关联类型', comment: '资源实体字段关联类型:0是字典、1是资源实体,但是选择资源类',
}, },
resourceEntityStructRelationValue:{ resourceEntityStructRelationValue:{
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
comment: '资源实体字段关联值', comment: '资源实体字段关联值',
}, },
resourceEntityStructDefaultValue:{
type: DataTypes.STRING,
comment: "默认值"
},
creator:{ creator:{
type: DataTypes.STRING, type: DataTypes.STRING,
comment: "创建人" comment: "创建人"

@ -28,7 +28,7 @@ import {mountResourceEntityRelation} from "#dataModels/resourceEntityRelation.da
export default function createDatabase(logger) { export default function createDatabase(logger) {
const { database, username, password, host, port } = const { database, username, password, host, port } =
global.config.database.mysql; global.config.database.mysql;
console.log(database, username, password, host, port); // console.log(database, username, password, host, port);
const sequelize = new Sequelize(database, username, password, { const sequelize = new Sequelize(database, username, password, {
host, host,
dialect: 'mysql', // 根据你的数据库类型修改 dialect: 'mysql', // 根据你的数据库类型修改

@ -20,9 +20,9 @@ import {
GetBaseDictStructForBaseDictIdDTO, GetBaseDictStructForBaseDictIdDTO,
GetBaseDictTreeDTO, GetBaseDictTreeDTO,
} from '#routes/graphResource2/baseDict/index.dto.js'; } from '#routes/graphResource2/baseDict/index.dto.js';
import { makeTreeForList } from '#common/tools/makeTree.js';
import binarySearch from '#common/tools/binarySearch.js'; import binarySearch from '#common/tools/binarySearch.js';
import binarySearchMore from "#common/tools/binarySearchMore.js"; import binarySearchMore from "#common/tools/binarySearchMore.js";
import getNoSpacesStr from "#common/tools/getNoSpacesStr.js";
const baseDict = new Router(); const baseDict = new Router();
@ -202,7 +202,7 @@ baseDict.post('/createBaseDictItem', async (ctx, next) => {
if (!verif.status) if (!verif.status)
return ctx.throw(400, { e: verif.error.map((i) => i.message) }); return ctx.throw(400, { e: verif.error.map((i) => i.message) });
const atomModelId = ctx.request.body.atomModelId; // 元分类/模型ID const atomModelId = ctx.request.body.atomModelId; // 元分类/模型ID
const baseDictName = ctx.request.body.baseDictName; // 字典项名称 const baseDictName = getNoSpacesStr(ctx.request.body.baseDictName); // 字典项名称
const baseDictFather = ctx.request.body.baseDictFather; // 父节点ID const baseDictFather = ctx.request.body.baseDictFather; // 父节点ID
const baseDictDescribe = ctx.request.body.baseDictDescribe; // 字典项描述 const baseDictDescribe = ctx.request.body.baseDictDescribe; // 字典项描述
const baseDictIdentify = ctx.request.body.baseDictIdentify; // 字典项标志 const baseDictIdentify = ctx.request.body.baseDictIdentify; // 字典项标志
@ -354,7 +354,7 @@ baseDict.post('/editBaseDictItem', async (ctx, next) => {
return ctx.throw(400, { e: verif.error.map((i) => i.message) }); return ctx.throw(400, { e: verif.error.map((i) => i.message) });
} }
const baseDictId = ctx.request.body.baseDictId; const baseDictId = ctx.request.body.baseDictId;
const baseDictName = ctx.request.body.baseDictName; const baseDictName = getNoSpacesStr(ctx.request.body.baseDictName);
const baseDictDescribe = ctx.request.body.baseDictDescribe; const baseDictDescribe = ctx.request.body.baseDictDescribe;
const baseDictIdentify = ctx.request.body.baseDictIdentify; const baseDictIdentify = ctx.request.body.baseDictIdentify;
const RBP = resourceCache.baseDictPool; const RBP = resourceCache.baseDictPool;

@ -16,6 +16,7 @@ import atomModel from "#routes/graphResource2/atomModel/index.js";
import testDTO from "#routes/index.dto.js"; import testDTO from "#routes/index.dto.js";
import rootRouter from "#routes/index.js"; import rootRouter from "#routes/index.js";
import baseDict from "#routes/graphResource2/baseDict/index.js"; import baseDict from "#routes/graphResource2/baseDict/index.js";
import resourceClass from "#routes/graphResource2/resourceClass/index.js";
const graphResource2 = new Router(); const graphResource2 = new Router();
@ -30,6 +31,7 @@ graphResource2.get('/', async (ctx) => {
}); });
graphResource2.use('/atomModel', atomModel.routes()); graphResource2.use('/atomModel', atomModel.routes());
graphResource2.use('/baseDict', baseDict.routes()); graphResource2.use('/baseDict', baseDict.routes());
graphResource2.use('/resourceClass', resourceClass.routes())
// graphResource2.use(async (ctx, next) => { // graphResource2.use(async (ctx, next) => {

@ -0,0 +1,657 @@
// | ------------------------------------------------------------
// | @版本: version 0.1
// | @创建人: 【Nie-x7129】
// | @E-mail: x71291@outlook.com
// | @所在项目: graphResource2
// | @文件描述: index.dto.js -
// | @创建时间: 2023-12-03 15:30
// | @更新时间: 2023-12-03 15:30
// | @修改记录:
// | -*-*-*- (时间--修改人--修改说明) -*-*-*-
// | =
// | ------------------------------------------------------------
import makeDTO from '#common/dto/index.js';
export function GetBaseDictTreeDTO(data) {
const schema = {
type: 'object',
properties: {
atomModelId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '元分类/模型ID必须是整数',
minimum: '元分类/模型ID是一个正整数',
},
},
reduce: {
type: ['integer'],
minimum: 0,
default: 1,
errorMessage: {
type: '元分类/模型ID必须是整数',
minimum: '元分类/模型ID是一个正整数',
},
},
},
};
return makeDTO(data, schema);
}
// @ 获取资源类列表
export function GetResourceClassListDTO(data) {
const schema = {
type: 'object',
properties: {
atomModelId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '元分类/模型ID必须是整数',
minimum: '元分类/模型ID是一个正整数',
},
},
searchData: {
type: 'string',
maxLength: 255,
errorMessage: {
type: '查询内容必须是一个字符串',
maxLength: '查询内容长度超出限制255',
},
},
},
required: ['atomModelId'],
errorMessage: {
required: {
atomModelId: '元分类/模型ID为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 获取资源类分页
export function GetResourceClassPageDTO(data) {
const schema = {
type: 'object',
properties: {
atomModelId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '元分类/模型ID必须是整数',
minimum: '元分类/模型ID是一个正整数',
},
},
searchData: {
type: 'string',
maxLength: 255,
errorMessage: {
type: '查询内容必须是一个字符串',
maxLength: '查询内容长度超出限制255',
},
},
pageSize: {
type: 'integer',
minimum: 1,
maximum: 200,
default: 10,
errorMessage: {
type: '每页数量必须是一个数字',
minimum: '每页数量最少是1',
maximum: '每页数量最多是200',
},
},
pageNum: {
type: 'integer',
minimum: 1,
default: 1,
errorMessage: {
type: '页码必须是数字类型',
minimum: '页码最小是1',
},
},
},
required: ['atomModelId'],
errorMessage: {
required: {
atomModelId: '元分类/模型ID为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 获取资源类树
export function GetResourceClassTreeDTO(data) {
const schema = {
type: 'object',
properties: {
atomModelId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '元分类/模型ID必须是整数',
minimum: '元分类/模型ID是一个正整数',
},
},
searchData: {
type: 'string',
maxLength: 255,
errorMessage: {
type: '查询内容必须是一个字符串',
maxLength: '查询内容长度超出限制255',
},
},
},
required: ['atomModelId'],
errorMessage: {
required: {
atomModelId: '元分类/模型ID为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 添加资源类
export function CreateResourceClassItemDTO(data) {
const schema = {
type: 'object',
properties: {
atomModelId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '元分类/模型ID必须是整数',
minimum: '元分类/模型ID是一个正整数',
},
},
resourceClassBaseDefine: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类来源必须是整数',
minimum: '资源类来源是一个正整数',
},
},
resourceClassBaseName: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类名必须是一个字符串',
maxLength: '资源类名长度超出限制255',
minLength: '资源类名长度过短',
},
},
resourceClassBaseDescribe: {
type: 'string',
minLength: 1,
maxLength: 2048,
errorMessage: {
type: '资源类描述必须是一个字符串',
maxLength: '资源类描述长度超出限制2048',
minLength: '资源类描述长度过短',
},
},
resourceClassBaseType: {
type: ['integer'],
minimum: 0,
default: 0,
errorMessage: {
type: '资源类业务类型必须是整数',
minimum: '资源类业务类型是一个正整数',
},
},
resourceClassBaseIdentify: {
type: 'string',
minLength: 8,
maxLength: 8,
pattern: '^[a-zA-Z\\s]+$',
errorMessage: {
type: '资源类标识必须是一个字符串',
maxLength: '资源类标识是一个8位字符串',
minLength: '资源类标识是一个8位字符串',
pattern: '资源类标识必须为纯英文',
},
},
resourceClassBaseColor: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类颜色描述必须是一个字符串',
maxLength: '资源类颜色描述长度超出限制255',
minLength: '资源类颜色描述长度过短',
},
},
resourceClassBaseAvatar: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类图标必须是一个字符串',
maxLength: '资源类图标长度超出限制255',
minLength: '资源类图标长度过短',
},
},
resourceClassBaseFather: {
type: ['integer'],
minimum: 0,
default: 0,
errorMessage: {
type: '父资源类必须是整数',
minimum: '父资源类是一个正整数',
},
},
},
required: [
'atomModelId',
'resourceClassBaseIdentify',
'resourceClassBaseDescribe',
'resourceClassBaseName',
],
errorMessage: {
required: {
atomModelId: '元分类/模型ID为必填项',
resourceClassBaseIdentify: '资源类标识为必填项',
resourceClassBaseDescribe: '资源类描述为必填项',
resourceClassBaseName: '资源类名称为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 编辑资源类
export function EditResourceClassItemDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassBaseId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类ID必须是整数',
minimum: '资源类ID是一个正整数',
},
},
resourceClassBaseDefine: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类来源必须是整数',
minimum: '资源类来源是一个正整数',
},
},
resourceClassBaseName: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类名必须是一个字符串',
maxLength: '资源类名长度超出限制255',
minLength: '资源类名长度过短',
},
},
resourceClassBaseDescribe: {
type: 'string',
minLength: 1,
maxLength: 2048,
errorMessage: {
type: '资源类描述必须是一个字符串',
maxLength: '资源类描述长度超出限制2048',
minLength: '资源类描述长度过短',
},
},
resourceClassBaseType: {
type: ['integer'],
minimum: 0,
default: 0,
errorMessage: {
type: '资源类业务类型必须是整数',
minimum: '资源类业务类型是一个正整数',
},
},
resourceClassBaseIdentify: {
type: 'string',
minLength: 8,
maxLength: 8,
pattern: '^[a-zA-Z\\s]+$',
errorMessage: {
type: '资源类标识必须是一个字符串',
maxLength: '资源类标识是一个8位字符串',
minLength: '资源类标识是一个8位字符串',
pattern: '资源类标识必须为纯英文',
},
},
resourceClassBaseColor: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类颜色描述必须是一个字符串',
maxLength: '资源类颜色描述长度超出限制255',
minLength: '资源类颜色描述长度过短',
},
},
resourceClassBaseAvatar: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类图标必须是一个字符串',
maxLength: '资源类图标长度超出限制255',
minLength: '资源类图标长度过短',
},
},
},
required: [
'resourceClassBaseId',
'resourceClassBaseIdentify',
'resourceClassBaseDescribe',
'resourceClassBaseName',
],
errorMessage: {
required: {
resourceClassBaseId: '资源类ID为必填项',
resourceClassBaseIdentify: '资源类标识为必填项',
resourceClassBaseDescribe: '资源类描述为必填项',
resourceClassBaseName: '资源类名称为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 删除资源类
export function DeleteResourceClassItemDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassBaseId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类ID必须是整数',
minimum: '资源类ID是一个正整数',
},
},
},
required: ['resourceClassBaseId'],
errorMessage: {
required: {
resourceClassBaseId: '资源类ID为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 创建资源类关联
export function CreateResourceClassRelationDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassRelationFather: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '父级资源类ID必须是整数',
minimum: '父级资源类ID是一个正整数',
},
},
resourceClassRelationTarget: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '父级资源类ID必须是整数',
minimum: '目标资源类ID是一个正整数',
},
},
},
required: [
'resourceClassRelationFather',
'resourceClassRelationTarget',
],
errorMessage: {
required: {
resourceClassRelationFather: '父级资源类ID为必填项',
resourceClassRelationTarget: '目标资源类ID为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 删除资源类关联
export function DeleteResourceClassRelationDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassRelationId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类关联ID必须是整数',
minimum: '资源类关联ID是一个正整数',
},
},
},
required: ['resourceClassRelationId'],
errorMessage: {
required: {
resourceClassRelationId: '资源类关联ID为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 获取资源类完整信息
export function GetResourceClassCompleteInfoDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassBaseId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类关联ID必须是整数',
minimum: '资源类关联ID是一个正整数',
},
},
resourceClassBaseIdentify: {
type: 'string',
minLength: 8,
maxLength: 8,
pattern: '^[a-zA-Z\\s]+$',
errorMessage: {
type: '资源类标识必须是一个字符串',
maxLength: '资源类标识是一个8位字符串',
minLength: '资源类标识是一个8位字符串',
pattern: '资源类标识必须为纯英文',
},
},
},
};
return makeDTO(data, schema);
}
// @ 创建资源类拓展字段
export function CreateResourceClassExpandFieldItemDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassBaseId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类ID必须是整数',
minimum: '资源类ID是一个正整数',
},
},
resourceClassExpandFieldName: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类拓展字段名必须是一个字符串',
maxLength: '资源类拓展字段名长度超出限制255',
minLength: '资源类拓展字段名长度过短',
},
},
resourceClassExpandFieldIdentify: {
type: 'string',
minLength: 1,
maxLength: 64,
pattern: '^[a-zA-Z\\s]+$',
errorMessage: {
type: '资源类拓展字段标识必须是一个字符串',
maxLength: '资源类拓展字段标识是一个8位字符串',
minLength: '资源类拓展字段标识是一个8位字符串',
pattern: '资源类拓展字段标识必须为纯英文',
},
},
resourceClassExpandFieldDisplayType: {
type: ['integer'],
minimum: 0,
default: 0,
errorMessage: {
type: '拓展字段显示类型必须是整数',
minimum: '拓展字段显示类型是一个正整数',
},
},
resourceClassExpandFieldRelationType: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '拓展字段关联类型必须是整数',
minimum: '拓展字段关联类型是一个正整数',
},
},
resourceClassExpandFieldValue: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类拓展字段值必须是一个字符串',
maxLength: '资源类拓展字段值长度超出限制255',
minLength: '资源类拓展字段值长度过短',
},
},
},
required: ['resourceClassBaseId', 'resourceClassExpandFieldName', 'resourceClassExpandFieldIdentify', 'resourceClassExpandFieldValue'],
errorMessage: {
required: {
resourceClassBaseId: '资源类ID为必填项',
resourceClassExpandFieldName: '资源类拓展字段名为必填项',
resourceClassExpandFieldIdentify: '资源类拓展字段标识为必填项',
resourceClassExpandFieldValue: '资源类拓展字段值为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 编辑资源类拓展字段
export function EditResourceClassExpandFieldItemDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassExpandFieldId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类拓展字段ID必须是整数',
minimum: '资源类拓展字段ID是一个正整数',
},
},
resourceClassExpandFieldName: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类拓展字段名必须是一个字符串',
maxLength: '资源类拓展字段名长度超出限制255',
minLength: '资源类拓展字段名长度过短',
},
},
resourceClassExpandFieldIdentify: {
type: 'string',
minLength: 1,
maxLength: 64,
pattern: '^[a-zA-Z\\s]+$',
errorMessage: {
type: '资源类拓展字段标识必须是一个字符串',
maxLength: '资源类拓展字段标识是一个8位字符串',
minLength: '资源类拓展字段标识是一个8位字符串',
pattern: '资源类拓展字段标识必须为纯英文',
},
},
resourceClassExpandFieldDisplayType: {
type: ['integer'],
minimum: 0,
default: 0,
errorMessage: {
type: '拓展字段显示类型必须是整数',
minimum: '拓展字段显示类型是一个正整数',
},
},
resourceClassExpandFieldRelationType: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '拓展字段关联类型必须是整数',
minimum: '拓展字段关联类型是一个正整数',
},
},
resourceClassExpandFieldValue: {
type: 'string',
minLength: 1,
maxLength: 255,
errorMessage: {
type: '资源类拓展字段值必须是一个字符串',
maxLength: '资源类拓展字段值长度超出限制255',
minLength: '资源类拓展字段值长度过短',
},
},
},
required: ['resourceClassExpandFieldId', 'resourceClassExpandFieldName', 'resourceClassExpandFieldIdentify', 'resourceClassExpandFieldValue'],
errorMessage: {
required: {
resourceClassExpandFieldId: '资源类拓展字段ID为必填项',
resourceClassExpandFieldName: '资源类拓展字段名为必填项',
resourceClassExpandFieldIdentify: '资源类拓展字段标识为必填项',
resourceClassExpandFieldValue: '资源类拓展字段值为必填项',
},
},
};
return makeDTO(data, schema);
}
// @ 删除资源类拓展字段
export function DeleteResourceClassExpandFieldItemDTO(data) {
const schema = {
type: 'object',
properties: {
resourceClassExpandFieldId: {
type: ['integer'],
minimum: 0,
errorMessage: {
type: '资源类拓展字段ID必须是整数',
minimum: '资源类拓展字段ID是一个正整数',
},
},
},
required: ['resourceClassExpandFieldId', ],
errorMessage: {
required: {
resourceClassExpandFieldId: '资源类拓展字段ID为必填项',
},
},
};
return makeDTO(data, schema);
}

@ -0,0 +1,137 @@
// | ------------------------------------------------------------
// | @版本: version 0.1
// | @创建人: 【Nie-x7129】
// | @E-mail: x71291@outlook.com
// | @所在项目: graphResource2
// | @文件描述: index.js -
// | @创建时间: 2023-12-03 15:24
// | @更新时间: 2023-12-03 15:24
// | @修改记录:
// | -*-*-*- (时间--修改人--修改说明) -*-*-*-
// | =
// | ------------------------------------------------------------
import Router from "koa-router";
import {
CreateResourceClassExpandFieldItemDTO,
CreateResourceClassItemDTO,
CreateResourceClassRelationDTO, DeleteResourceClassExpandFieldItemDTO, DeleteResourceClassItemDTO,
DeleteResourceClassRelationDTO, EditResourceClassExpandFieldItemDTO, EditResourceClassItemDTO,
GetResourceClassCompleteInfoDTO,
GetResourceClassListDTO,
GetResourceClassPageDTO,
GetResourceClassTreeDTO
} from "#routes/graphResource2/resourceClass/index.dto.js";
const resourceClass = new Router()
// @ 获取资源类列表
resourceClass.get('/getResourceClassList', async (ctx, next) => {
const verif = GetResourceClassListDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 1
})
// @ 获取资源类分页
resourceClass.get('/getResourceClassPage', async (ctx, next) => {
const verif = GetResourceClassPageDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 2
})
// @ 获取资源类树
resourceClass.get('/getResourceClassTree', async (ctx, next) => {
const verif = GetResourceClassTreeDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 3
})
// @ 添加资源类
resourceClass.post('/createResourceClassItem', async (ctx, next) => {
const verif = CreateResourceClassItemDTO(ctx.request.body)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 4
})
// @ 编辑资源类
resourceClass.post('/editResourceClassItem', async (ctx, next) => {
const verif = EditResourceClassItemDTO(ctx.request.body)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 5
})
// @ 删除资源类
resourceClass.delete('/deleteResourceClassItem', async (ctx, next) => {
const verif = DeleteResourceClassItemDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 6
})
// @ 创建资源类关联
resourceClass.post('/createResourceClassRelation', async (ctx, next) => {
const verif = CreateResourceClassRelationDTO(ctx.request.body)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 7
})
// @ 删除资源类关联
resourceClass.delete('/deleteResourceClassRelation', async (ctx, next) => {
const verif = DeleteResourceClassRelationDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 8
})
// @ 获取资源类完整信息
resourceClass.get('/getResourceClassCompleteInfo', async (ctx, next) => {
const verif = GetResourceClassCompleteInfoDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 9
})
// @ 创建资源类拓展字段
resourceClass.post('/createResourceClassExpandFieldItem', async (ctx, next) => {
const verif = CreateResourceClassExpandFieldItemDTO(ctx.request.body)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 10
})
// @ 编辑资源类拓展字段
resourceClass.post('/editResourceClassExpandFieldItem', async (ctx, next) => {
const verif = EditResourceClassExpandFieldItemDTO(ctx.request.body)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 11
})
// @ 删除资源类拓展字段
resourceClass.delete('/deleteResourceClassExpandFieldItem', async (ctx, next) => {
const verif = DeleteResourceClassExpandFieldItemDTO(ctx.query)
if (!verif.status) {
return ctx.throw(400, { e: verif.error.map((i) => i.message) });
}
ctx.body = 12
})
export default resourceClass

@ -0,0 +1,131 @@
// | ------------------------------------------------------------
// | @版本: version 0.1
// | @创建人: 【Nie-x7129】
// | @E-mail: x71291@outlook.com
// | @所在项目: graphResource2
// | @文件描述: makeClass.js -
// | @创建时间: 2023-12-03 16:41
// | @更新时间: 2023-12-03 16:41
// | @修改记录:
// | -*-*-*- (时间--修改人--修改说明) -*-*-*-
// | =
// | ------------------------------------------------------------
const obj = {
id: 1,
name: '何希',
age: 25
}
const f1 = {
id: 2,
name: '何希的妈妈',
age: 45
}
const f2 = {
id: 3,
name: '何希的爸爸',
age: 45
}
class resourceClass{
baseInfo;
expandInfo;
fatherObject = {}
constructor(baseInfo, expandInfo = {}) {
this.baseInfo = baseInfo;
this.expandInfo = expandInfo
}
addFather(obj){
this.fatherObject[obj.id] = obj
}
delFather(obj){
delete this.fatherObject[obj.id]
}
get baseInfo(){
return this.baseInfo
}
get expandInfo(){
return this.expandInfo
}
get fatherId(){
return Object.keys(this.fatherObject)
}
get fatherObj(){
return this.fatherObj
}
get fatherList(){
return Object.keys(this.fatherObj).map(i => this.fatherObject[i])
}
}
function makeClass(obj1, obj2 = {}){
return new resourceClass(obj1, obj2)
}
const R1 = makeClass(obj);
const R2 = makeClass(obj);
obj.age++
console.log(R1.baseInfo)
console.log(R1)
console.log(R2.baseInfo)
const resourceCache = {
// 基础数据库信息
baseData:{
// 基础资源类信息
resourceClassList: [],
resourceClassObject:{},
// 拓展资源类信息
resourceClassExpandFieldList: [],
resourceClassExpandFieldObject: {},
// 实体字段信息
resourceEntityStructList: [],
resourceEntityStructObject: {},
// 资源实体信息
resourceEntityList: [],
resourceEntityObject: {},
// 资源类关系信息
resourceClassRelationList: [],
resourceClassRelationObject: {}
},
// 合成资源信息,计算数据
computedData:{
// 资源类完整数据 - 继承于baseData-resourceClassObject
resourceClassObject: {
resourceClassId: {
// 基础资源类信息,查找字典值
baseClassData: {
},
// 拓展资源类信息,查找字典值
expandClassData:{
},
// 资源实体结构转化为对象格式
entityStructData:{
},
// 资源类的父节点,及其关联下的子节点
relationClass:{
fatherId:['childId']
}
}
}
},
// 资源关联信息
relationData:{}
}
// 第一步加载数据库数据

@ -0,0 +1,36 @@
// | ------------------------------------------------------------
// | @版本: version 0.1
// | @创建人: 【Nie-x7129】
// | @E-mail: x71291@outlook.com
// | @所在项目: graphResource2
// | @文件描述: testClassExtents.js -
// | @创建时间: 2023-12-03 17:58
// | @更新时间: 2023-12-03 17:58
// | @修改记录:
// | -*-*-*- (时间--修改人--修改说明) -*-*-*-
// | =
// | ------------------------------------------------------------
console.log('START')
console.time(1)
let list = []
const a1 = performance.now();
for(let i = 0; i < 100000000; i ++){
list.push({
name: '何希',
age: 35,
love: 'GY',
key: parseInt(Math.random() * 100000000)
})
}
console.log('A')
const a2 = performance.now();
const data = list.filter(i => {
return (i.name + i.love + i.key).includes('GY21')
})
const a3 = performance.now();
console.log('makeData', a2-a1)
console.log('filterData', a3-a2)
console.log(data.length)
console.timeEnd(1)
Loading…
Cancel
Save