diff --git a/graphResource2数据结构.xmind b/graphResource2数据结构.xmind index e675fa4..fb6d040 100644 Binary files a/graphResource2数据结构.xmind and b/graphResource2数据结构.xmind differ diff --git a/src/cache/index.js b/src/cache/index.js index 9cff8ce..1738e0b 100644 --- a/src/cache/index.js +++ b/src/cache/index.js @@ -10,10 +10,56 @@ // | -*-*-*- (时间--修改人--修改说明) -*-*-*- // | = // | ------------------------------------------------------------ -import { Op } from 'sequelize'; +import { DataTypes, Model, Op } from 'sequelize'; import { makeTreeForList } from '#common/tools/makeTree.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) { global.resourceCache = {}; // atomModelCache @@ -22,7 +68,7 @@ export async function createCatch(sequelize) { global.resourceCache.atomModelPool = atomModelPool; const atomModelPoolEndTime = performance.now(); logger.fatal( - `元分类/模型缓存加载完毕: atomModelPool - ${ + `${'元分类/模型缓存加载完毕: atomModelPool'.padEnd(30, ' ')} | ${ atomModelPoolEndTime - atomModelPoolStartTime } ms`, ); @@ -33,10 +79,21 @@ export async function createCatch(sequelize) { global.resourceCache.baseDictPool = baseDictPool; const baseDictPoolEndTime = performance.now(); logger.fatal( - `数据字典缓存加载完毕: baseDictPool - ${ + `${'数据字典缓存加载完毕: baseDictPool'.padEnd(30, ' ')} | ${ baseDictPoolEndTime - baseDictPoolStartTime } 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) { @@ -172,3 +229,432 @@ async function makeBaseDictCache(sequelize) { }; 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 + // ! 获取资源实体关系 +} diff --git a/src/common/database/dataModels/resourceClassBase.dataModel.js b/src/common/database/dataModels/resourceClassBase.dataModel.js index fb58daa..0f4ac9c 100644 --- a/src/common/database/dataModels/resourceClassBase.dataModel.js +++ b/src/common/database/dataModels/resourceClassBase.dataModel.js @@ -70,7 +70,7 @@ export function mountResourceClassBase(sequelize, DataTypes) { }, resourceClassBaseColor:{ type: DataTypes.STRING, - comment: '资源类名', + comment: '资源类颜色', }, resourceClassBaseAvatar:{ type: DataTypes.STRING, diff --git a/src/common/database/dataModels/resourceClassRelation.dataModel.js b/src/common/database/dataModels/resourceClassRelation.dataModel.js index 9bf13f6..25cf895 100644 --- a/src/common/database/dataModels/resourceClassRelation.dataModel.js +++ b/src/common/database/dataModels/resourceClassRelation.dataModel.js @@ -25,15 +25,16 @@ export function mountResourceClassRelation(sequelize, DataTypes) { allowNull: false, comment: '资源类关系ID' }, - resourceClassRelationTarget: { + resourceClassRelationFather: { type: DataTypes.INTEGER, allowNull: false, - comment: '目标资源类ID' + defaultValue: 0, + comment: '父资源类ID' }, - resourceClassRelationFather: { + resourceClassRelationTarget: { type: DataTypes.INTEGER, allowNull: false, - comment: '父资源类ID' + comment: '目标资源类ID' }, creator:{ type: DataTypes.STRING, diff --git a/src/common/database/dataModels/resourceEntityStruct.dataModel.js b/src/common/database/dataModels/resourceEntityStruct.dataModel.js index c2695db..0d95060 100644 --- a/src/common/database/dataModels/resourceEntityStruct.dataModel.js +++ b/src/common/database/dataModels/resourceEntityStruct.dataModel.js @@ -57,6 +57,7 @@ export function mountResourceEntityStruct(sequelize, DataTypes) { resourceEntityStructStorageType:{ type: DataTypes.INTEGER, allowNull: false, + default: 0, comment: '资源实体字段在数据库中存储的类型', }, resourceEntityStructStorageLength:{ @@ -99,12 +100,16 @@ export function mountResourceEntityStruct(sequelize, DataTypes) { }, resourceEntityStructRelationType:{ type: DataTypes.INTEGER, - comment: '资源实体字段关联类型', + comment: '资源实体字段关联类型:0是字典、1是资源实体,但是选择资源类', }, resourceEntityStructRelationValue:{ type: DataTypes.INTEGER, comment: '资源实体字段关联值', }, + resourceEntityStructDefaultValue:{ + type: DataTypes.STRING, + comment: "默认值" + }, creator:{ type: DataTypes.STRING, comment: "创建人" diff --git a/src/common/database/index.js b/src/common/database/index.js index b3f82da..c08c10b 100644 --- a/src/common/database/index.js +++ b/src/common/database/index.js @@ -28,7 +28,7 @@ import {mountResourceEntityRelation} from "#dataModels/resourceEntityRelation.da export default function createDatabase(logger) { const { database, username, password, host, port } = 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, { host, dialect: 'mysql', // 根据你的数据库类型修改 diff --git a/src/routes/graphResource2/baseDict/index.js b/src/routes/graphResource2/baseDict/index.js index 35fcef9..ea3e360 100644 --- a/src/routes/graphResource2/baseDict/index.js +++ b/src/routes/graphResource2/baseDict/index.js @@ -20,9 +20,9 @@ import { GetBaseDictStructForBaseDictIdDTO, GetBaseDictTreeDTO, } from '#routes/graphResource2/baseDict/index.dto.js'; -import { makeTreeForList } from '#common/tools/makeTree.js'; import binarySearch from '#common/tools/binarySearch.js'; import binarySearchMore from "#common/tools/binarySearchMore.js"; +import getNoSpacesStr from "#common/tools/getNoSpacesStr.js"; const baseDict = new Router(); @@ -202,7 +202,7 @@ baseDict.post('/createBaseDictItem', async (ctx, next) => { if (!verif.status) return ctx.throw(400, { e: verif.error.map((i) => i.message) }); 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 baseDictDescribe = ctx.request.body.baseDictDescribe; // 字典项描述 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) }); } 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 baseDictIdentify = ctx.request.body.baseDictIdentify; const RBP = resourceCache.baseDictPool; diff --git a/src/routes/graphResource2/index.js b/src/routes/graphResource2/index.js index d09b785..036a95a 100644 --- a/src/routes/graphResource2/index.js +++ b/src/routes/graphResource2/index.js @@ -16,6 +16,7 @@ import atomModel from "#routes/graphResource2/atomModel/index.js"; import testDTO from "#routes/index.dto.js"; import rootRouter from "#routes/index.js"; import baseDict from "#routes/graphResource2/baseDict/index.js"; +import resourceClass from "#routes/graphResource2/resourceClass/index.js"; const graphResource2 = new Router(); @@ -30,6 +31,7 @@ graphResource2.get('/', async (ctx) => { }); graphResource2.use('/atomModel', atomModel.routes()); graphResource2.use('/baseDict', baseDict.routes()); +graphResource2.use('/resourceClass', resourceClass.routes()) // graphResource2.use(async (ctx, next) => { diff --git a/src/routes/graphResource2/resourceClass/index.dto.js b/src/routes/graphResource2/resourceClass/index.dto.js new file mode 100644 index 0000000..ced858c --- /dev/null +++ b/src/routes/graphResource2/resourceClass/index.dto.js @@ -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); +} diff --git a/src/routes/graphResource2/resourceClass/index.js b/src/routes/graphResource2/resourceClass/index.js new file mode 100644 index 0000000..60172a6 --- /dev/null +++ b/src/routes/graphResource2/resourceClass/index.js @@ -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 diff --git a/test/makeClass.js b/test/makeClass.js new file mode 100644 index 0000000..22becfd --- /dev/null +++ b/test/makeClass.js @@ -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:{} +} + +// 第一步加载数据库数据 + + + diff --git a/test/testClassExtents.js b/test/testClassExtents.js new file mode 100644 index 0000000..633894e --- /dev/null +++ b/test/testClassExtents.js @@ -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)