import { immutable, excludedInJSON, circular, action } from '../decorators'; import { config, history, typeCheck, utils, Vertex, Service, LEVEL_ENUM, Interface, Param, Return, Variable, Logic, View, Schema, ActionOptions, ACTION_MODE } from '..'; import { logicService } from '../../service/logic'; import { assert } from 'console'; import { traverse } from '../utils'; import { vertexsMap } from '../cacheData'; import { getSchemaOfExpressionNode } from './tools'; import Structure from '../data/Structure'; export enum LOGIC_TYPE { // LogicNode Start = 'Start', ProcessOutcome = 'ProcessOutcome', End = 'End', IfStatement = 'IfStatement', SwitchStatement = 'SwitchStatement', SwitchCase = 'SwitchCase', ForEachStatement = 'ForEachStatement', WhileStatement = 'WhileStatement', AssignmentExpression = 'AssignmentExpression', Comment = 'Comment', CallLogic = 'CallLogic', CallInterface = 'CallInterface', CallInterParam = 'CallInterParam', CallGraphQL = 'CallGraphQL', DBQuery = 'DBQuery', CronJob = 'CronJob', JSONSerialize = 'JSONSerialize', JSONDeserialize = 'JSONDeserialize', CallMessageShow = 'CallMessageShow', CallConsoleLog = 'CallConsoleLog', JSBlock = 'JSBlock', TypeNote = 'TypeNote', CallQueryComponent = 'CallQueryComponent', // Expression Identifier = 'Identifier', NumericLiteral = 'NumericLiteral', BooleanLiteral = 'BooleanLiteral', StringLiteral = 'StringLiteral', NullLiteral = 'NullLiteral', UnaryExpression = 'UnaryExpression', BinaryExpression = 'BinaryExpression', LogicalExpression = 'LogicalExpression', MemberExpression = 'MemberExpression', CallExpression = 'CallExpression', ObjectExpression = 'ObjectExpression', ArrayExpression = 'ArrayExpression', Destination = 'Destination', DestinationParam = 'DestinationParam', BuiltInFunction = 'BuiltInFunction', BuiltInFuncParam = 'BuiltInFuncParam', Unparsed = 'Unparsed', QuerySelectExpression = 'QuerySelectExpression', QueryFromExpression = 'QueryFromExpression', QueryJoinExpression = 'QueryJoinExpression', QueryAggregateExpression = 'QueryAggregateExpression', } export function evaluate(node: LogicNode | ExpressionNode, finalCode = true): string { if (!node) return ''; if (node.type === 'Identifier') return node.code || node.name; if (node.type === 'NumericLiteral') return node.value; if (node.type === 'BooleanLiteral') return `${node.value}`; if (node.type === 'StringLiteral') return `'${node.value}'`; if (node.type === 'NullLiteral') return 'null'; if (node.type === 'BinaryExpression') { let left: string = evaluate(node.left, finalCode); if (!finalCode && node.left?.type === LOGIC_TYPE.MemberExpression) left = `(${left})`; let right: string = evaluate(node.right, finalCode); if (!finalCode && node.right?.type === LOGIC_TYPE.MemberExpression) right = `(${right})`; return `${left} ${node.operator} ${right}`; } if (node.type === 'LogicalExpression') { let left: string = evaluate(node.left, finalCode); if (!finalCode && node.left?.type === LOGIC_TYPE.MemberExpression) left = `(${left})`; let right: string = evaluate(node.right, finalCode); if (!finalCode && node.right?.type === LOGIC_TYPE.MemberExpression) right = `(${right})`; return `${left} ${node.operator} ${right}`; } if (node.type === LOGIC_TYPE.UnaryExpression && node.operator === '!') { const argument = (node).argument; const code = evaluate(argument, finalCode); const arr = [LOGIC_TYPE.BinaryExpression, LOGIC_TYPE.LogicalExpression]; if (!finalCode) arr.push(LOGIC_TYPE.MemberExpression); return arr.includes(argument?.type) ? `!(${code})` : `!${code}`; } if (node.type === 'Unparsed') { return node.code; } if (node.type === 'MemberExpression') { const object: string = evaluate((node).object, finalCode); const property = (node).property; if (finalCode || property.code?.includes('ID_ENUMVALUE_')) { if (property.type === 'Identifier') return `${object}.${property.code || property.name}`; else if (property.type === 'StringLiteral') return `${object}['${property.value}']`; else if (property.type === 'NumericLiteral') return `${object}[${property.value}]`; } else { const lastPart = object.split(' && ').pop(); if (property.type === 'Identifier') return `${object} && ${lastPart}.${property.code || property.name}`; else if (property.type === 'StringLiteral') return `${object} && ${lastPart}['${property.value}']`; else if (property.type === 'NumericLiteral') return `${object} && ${lastPart}[${property.value}]`; } } if (node.type === 'CallExpression') { const args = []; for (const argumentNode of (node).arguments) { args.push(evaluate(argumentNode, finalCode)); } const name = (node).callee.name; return `${name}(${args.join(', ')})`; } if (node.type === 'ObjectExpression') { const obj: { [prop: string]: any } = {}; for (let { key, value } of (node).properties) { let keyValue = evaluate(key, finalCode); if (/^"(.*)"$/.test(keyValue)) keyValue = RegExp.$1; const valueTemp = evaluate(value, finalCode); if (/^"(.*)"$/.test(valueTemp)) value = RegExp.$1; obj[keyValue] = value; } return JSON.stringify(obj); } if (node.type === 'ArrayExpression') { const arr = []; for (const elementNode of (node).elements) { arr.push(evaluate(elementNode, finalCode)); } return '[' + arr.join(', ') + ']'; } if (node.type === 'Destination') { const params = ((node as any).params || []) .filter((param: LogicNode) => param.pageParamKey && param.pageParamKeyValue) .map((param: LogicNode) => evaluate(param.pageParamKey, finalCode) + '=${' + evaluate(param.pageParamKeyValue, finalCode) + '}'); let result = node.code; if (params.length) { result = '`' + `${node.code}?${params.join('&')}` + '`'; } else { result = '`' + `${result}` + '`'; } return result.replace(/"/g, "'"); } if (node.type === 'BuiltInFunction') { const calleeCode = `$utils['${(node as LogicNode).calleeCode}']`; if (!calleeCode) return; // 需要保持params顺序 const params = ((node as any).builtInFuncParams || []) .map((param: any) => param.builtInFuncParamValue ? evaluate(param.builtInFuncParamValue, finalCode) : null); let paramResult = params.join(','); if (paramResult === ',') { paramResult = ''; } else { paramResult = params.join(','); } return `${calleeCode}(${paramResult})`.replace(/"/g, "'"); } if (node.type === 'TypeNote') { const { type, format } = node.schema; return `{ type: '${type}', format: '${format}' }`; } } export const logicItemKeyOfLogicItem: string[] = ['test', 'left', 'right', 'each', 'item', 'index', 'start', 'end', 'variables', 'taskId', 'processInstanceId', 'startedBy', 'errorMessage', 'callInterParamValue', 'builtInFuncParamValue', 'pageParamKey', 'pageParamKeyValue', 'argument', 'select', 'from', 'limit', 'orderElement', 'aggregateParam', 'order', 'pageElement', 'pageSizeElement', 'groupElement', ]; export const logicItemArrayKeyOfLogicItem: string[] = ['body', 'consequent', 'alternate', 'cases', 'params', 'arguments', 'builtInFuncParams', 'groupBy', 'orderBy', 'selectElementList', 'joinPartList', 'onExpressionList', 'where', 'having', ]; /** * 逻辑项类 */ export class LogicItem extends Vertex { /** * 概念类型 */ @immutable() public readonly level: LEVEL_ENUM = LEVEL_ENUM.logicItem; /** * Id */ @immutable() public readonly id: string = undefined; /** * 父级 Id */ @immutable() public readonly parentId: string = undefined; /** * 所属父级属性 */ @immutable() public readonly parentAttr: string = undefined; @excludedInJSON() @immutable() public readonly parent: LogicItem = undefined; @immutable() public readonly logicId: string = undefined; @immutable() public readonly logic: Logic = undefined; /** * 标题 */ @immutable() public readonly label: string = undefined; @immutable() public folded: boolean = false; @immutable() public placeholderName: string = undefined; /** * OffsetX */ @immutable() public readonly offsetX: number = undefined; /** * OffsetY */ @immutable() public readonly offsetY: number = undefined; /** * 所在位置 */ @immutable() public readonly _posIndex: number = undefined; /** * 标题 */ @immutable() public readonly type: string = undefined; /** * Ref */ @excludedInJSON() @immutable() public readonly refTarget: any = undefined; @immutable() public readonly callInterParam: any = undefined; @immutable() public readonly callInterParamValue: any = undefined; @immutable() public readonly builtInFuncParamValue: any = undefined; @immutable() public readonly pageParamKey: any = undefined; @immutable() public readonly pageParamKeyValue: any = undefined; /** * CallQueryComponent 对应的 structure 的 id */ @immutable() public readonly structureRef: string = undefined; @immutable() public readonly schema?: Schema = undefined; /** * 类型校验结果 */ @excludedInJSON() public typeCheckNote: any = undefined; /** * @param source 需要合并的部分参数 */ constructor(source?: Partial) { super(); source && this.assign(source); } /** * 纯前端添加 */ addIn(parent: LogicItem, parentId: string, parentAttr: string, _posIndex: number) { if (!parent) { if (!this.logic) return; if (this.logic.playgroundId === parentId) { // In playgroundId const index = _posIndex || this.logic.playground.length; this.logic.playground.splice(index, 0, this); } else { // In main body 这个后面让忠杰改成数组吧 const index = this.logic.body.findIndex((item) => item.id === parentId); ~index && this.logic.body.splice(index + 1, 0, this); this.logic.body.forEach((item, index) => { if (index) { item.assign({ parentId: this.logic.body[index - 1].id }); } }); } } else { this.assign({ parent, parentAttr }); const attr = parent[parentAttr as keyof LogicItem]; if (Array.isArray(attr)) { const index = _posIndex; ~index && attr.splice(index, 0, this); } else { if (!parentAttr) return; parent.assign({ [parentAttr]: this }); } } } /** * @temp 临时的 * @param body */ static preprocess(body: any) { traverse((current) => { delete current.node.editable; if (current.node.type !== 'ForEachStatement') delete current.node.index; // 处理 CallGraphQL if (current.node.type === 'CallGraphQL') { const body = current.node; delete body.paramsType; delete body.optionalParams; body.params && body.params.forEach((param: any, index: number) => { param._posIndex = index; }); delete body.querySchemaMap; delete body.singularResolverName; body.querySchemaList = JSON.stringify(body.querySchemaList || []); } }, { node: body }, { mode: 'anyObject' }); // 处理 CallInterface if (body.type === 'CallInterface') { delete body.paramsType; delete body.optionalParams; body.params.forEach((param: any, index: number) => { param._posIndex = index; }); } } /** * 创建 * @requires this.logic * @param parent * @param parentId * @param parentAttr * @param _posIndex * @param cache 在前端不处理节点 */ async create({ parent, parentId, parentAttr, _posIndex, cache, offsetX, offsetY, }: { parent: LogicItem, parentId: string, parentAttr: string, _posIndex?: number, cache?: boolean, offsetX?: number, offsetY?: number, }, actionOptions?: ActionOptions) { config.defaultApp?.emit('saving'); const prevParent = this.parent; if (parent) { const attr = parent[parentAttr as keyof LogicItem]; if (_posIndex === undefined && Array.isArray(attr)) _posIndex = attr.length; } offsetX = offsetX ?? this.offsetX; offsetY = offsetY ?? this.offsetY; const body = this.toJSON(); body.parentId = parentId; body.parentAttr = parentAttr; body._posIndex = _posIndex; body.offsetX = offsetX; body.offsetY = offsetY; LogicItem.preprocess(body); let result: LogicItem; if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) { utils.logger.debug(`在该逻辑节点的属性"${parentAttr}"上插入一个节点"`, body); body.logicId = this.logic && this.logic.id; result = await logicService.addItem({ headers: { appId: config.defaultApp?.id, serviceId: this.logic?.interface?.serviceId, operationAction: 'LogicItem.create', operationDesc: actionOptions?.actionDesc || `添加逻辑项"${this.label || this.type}"`, operationIgnore: actionOptions?.actionIgnore, }, body, }); this.deepPick(result, ['id', 'parentId', 'parentAttr', 'joinPartRef', 'structureRef']); } this.assign({ parent, parentId, parentAttr, offsetX, offsetY, logicId: this.logic && this.logic.id, }); LogicItem.from(this, this.logic, this.parent, false); !cache && this.addIn(parent, parentId, parentAttr, _posIndex); utils.traverse(({ node }) => { if (node.type === LOGIC_TYPE.CallQueryComponent) { // 兼容 流程-流程节点 内部逻辑 const service = this.logic.processComponent ? this.logic.processComponent.process?.service : this.logic.interface?.service; const structure = Structure.from({ id: node.structureRef, }, service); structure.loadPro(); } }, { node: this }, { mode: 'anyObject', excludedKeySet: this.JSON_EXCLUDED_KEYS, }); this.logic && this.logic.emit('change'); if (actionOptions?.checkTypeIgnore !== true) { this.checkType(); prevParent && prevParent.checkType(); } await config.defaultApp?.history.load(actionOptions?.actionMode !== ACTION_MODE.undoRedo && { operationAction: 'LogicItem.create', operationBeforeImage: null, operationAfterImage: JSON.parse(JSON.stringify(this)), operationDesc: `添加逻辑项"${this.label || this.type}"`, }); config.defaultApp?.emit('saved'); } /** * 创建或移动 * @requires this.logic * @param parent * @param parentId * @param parentAttr * @param _posIndex * @param cache 在前端不处理节点 */ async move({ parent, parentId, parentAttr, _posIndex, cache, offsetX, offsetY, }: { parent: LogicItem, parentId: string, parentAttr: string, _posIndex?: number, cache?: boolean, offsetX?: number, offsetY?: number, }, actionOptions?: ActionOptions) { config.defaultApp?.emit('saving'); const prevParent = this.parent; if (parentAttr && !parent) { parent = vertexsMap.get(parentId) as LogicItem; } if (parent) { const attr = parent[parentAttr as keyof LogicItem]; if (_posIndex === undefined && Array.isArray(attr)) _posIndex = attr.length; } const body = this.toJSON(); body.parentId = parentId; body.parentAttr = parentAttr; body._posIndex = _posIndex; LogicItem.preprocess(body); if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) { utils.logger.debug(`在该逻辑节点的属性"${parentAttr}"上插入一个节点"`, body); const result: LogicItem = await logicService.moveItem({ headers: { appId: config.defaultApp?.id, operationAction: 'LogicItem.move', operationDesc: actionOptions?.actionDesc || `移动逻辑项"${this.label || this.type}"`, }, body: { id: body.id, moveFromLogicItem: { parentId: this.parentId, parentAttr: this.parentAttr, _posIndex: this._posIndex, offsetX: this.offsetX, offsetY: this.offsetY, }, moveToLogicItem: { parentId, parentAttr, _posIndex, offsetX, offsetY, }, }, }); } if (!(this.parentId === parentId && this.parentId === this.logic.playgroundId && this.parentAttr === parentAttr)) { // 以下几句顺序不能变,注意! !cache && this.remove(); this.assign({ parent, parentId, parentAttr, offsetX, offsetY, }); !cache && this.addIn(parent, parentId, parentAttr, _posIndex); } else { this.assign({ offsetX, offsetY, }); } this.logic && this.logic.emit('change'); this.checkType(); prevParent && prevParent.checkType(); await config.defaultApp?.history.load(actionOptions?.actionMode !== ACTION_MODE.undoRedo && { operationAction: 'LogicItem.move', operationBeforeImage: null, operationAfterImage: null, // JSON.parse(JSON.stringify(this)), operationDesc: `移动逻辑项"${this.label || this.type}"`, }); config.defaultApp?.emit('saved'); } /** * 创建或移动 * @requires this.logic * @param parent * @param parentId * @param parentAttr * @param _posIndex * @param cache 在前端不处理节点 */ createOrMove(options: { parent: LogicItem, parentId: string, parentAttr: string, _posIndex?: number, cache?: boolean, offsetX?: number, offsetY?: number, }, actionOptions?: ActionOptions) { if (this.id) return this.move(options, actionOptions); else return this.create(options, actionOptions); } /** * 粘贴 * @param logicItems * @param targetType * @param targetId */ static async paste(logicItems: LogicItem[] | LogicItem, targetType: 'logic' | 'html' | 'processComponent', targetId: string) { if (!Array.isArray(logicItems)) logicItems = [logicItems]; logicItems.forEach((logicItem) => { LogicItem.preprocess(logicItem); }); const res = await logicService.paste({ body: { type: 'logicItem', logicItems, targetId, targetType, }, headers: { appId: config.defaultApp?.id, operationAction: 'LogicItem.paste', operationDesc: `粘贴逻辑项"${logicItems[0].label || logicItems[0].type}"`, }, }); LogicItem.redoPaste(res); } static async redoPaste(res: any) { const logic = vertexsMap.get(res.targetId) as Logic; res.logicItems.forEach((logicItem: LogicItem) => { logicItem = LogicItem.from(logicItem, logic, null); logic.playground.push(logicItem); logicItem.checkType(); utils.traverse(({ node }) => { if (node.type === LOGIC_TYPE.CallQueryComponent) { // 兼容 流程-流程节点 内部逻辑 const service = node.logic.processComponent ? node.logic.processComponent.process?.service : node.logic.interface?.service; const structure = Structure.from({ id: node.structureRef, }, service); structure.loadPro(); } }, { node: logicItem }, { mode: 'anyObject', excludedKeySet: logicItem.JSON_EXCLUDED_KEYS, }); }); await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); } static async undoPaste(res: any) { const logic = vertexsMap.get(res.targetId) as Logic; const { playground } = logic; for (let i = playground.length - 1; i >= 0; i--) { if (res.logicItems.find((item: any) => item.id === playground[i].id)) { typeCheck.delete(playground[i].id); playground.splice(i, 1); } } logic.interface?.service?.syncStructures(); // 处理 CallQueryComponent 嵌套的情形 await config.defaultApp?.history.load(); config.defaultApp?.emit('saved'); } /** * 粘贴预处理,用于粘贴到表达式输入弹窗。此处,粘贴没有立即提交到后端,点击确定按钮后才提交。 * @param logicItems * @param targetType * @param targetId */ static async pretreatmentBeforePaste(logicItems: LogicItem[] | LogicItem, targetType: 'logic' | 'html' | 'processComponent', targetId: string) { if (!Array.isArray(logicItems)) logicItems = [logicItems]; logicItems.forEach((logicItem) => { LogicItem.preprocess(logicItem); }); function removeId(logicItems: LogicItem[]) { return JSON.parse(JSON.stringify(logicItems, (key, value) => key === 'id' ? undefined : value)); } return logicService.pretreatmentBeforePaste({ body: { type: 'logicItem', logicItems, targetId, targetType, }, }).then((res: any) => { res.logicItems = removeId(res.logicItems); return res; }); } /** * 纯前端创建 */ virtualCreate(parent: LogicItem, parentId: string, parentAttr: string, _posIndex?: number, cache?: boolean) { if (parent) { const attr = parent[parentAttr as keyof LogicItem]; if (_posIndex === undefined && Array.isArray(attr)) _posIndex = attr.length; } this.addIn(parent, parentId, parentAttr, _posIndex); } /** * 纯前端 replaceWith */ async virtualReplaceWith(logicItem: LogicItem) { this.remove(); let _posIndex: number; if (this.parent) { const attr = this.parent[this.parentAttr as keyof LogicItem]; if (Array.isArray(attr)) _posIndex = attr.indexOf(this); } logicItem.virtualCreate(this.parent, this.parentId, this.parentAttr, _posIndex, false); this.assign(logicItem); return this; } /** * 纯前端删除 */ remove() { // 处理 old if (!this.parent) { if (!this.logic) return; if (this.logic.playgroundId === this.parentId) { // In playgroundId const index = this.logic.playground.indexOf(this); ~index && this.logic.playground.splice(index, 1); } else { // In main body 这个后面让忠杰改成数组吧 const index = this.logic.body.indexOf(this); ~index && this.logic.body.splice(index, 1); this.logic.body.forEach((item, index) => { if (index) { item.assign({ parentId: this.logic.body[index - 1].id }); } }); } } else { const attr = this.parent[this.parentAttr as keyof LogicItem]; if (Array.isArray(attr)) { const index = attr.indexOf(this); ~index && attr.splice(index, 1); } else { this.parent.assign({ [this.parentAttr]: null }); } } } /** * 删除逻辑节点 * @param cache 在前端不处理节点 */ @action('删除逻辑节点') async delete(cache?: boolean, actionOptions?: ActionOptions) { config.defaultApp?.emit('saving'); if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) { if (this.id) { const body = this.toPlainJSON(); /// @TODO: 不知道哪里来的 index delete body.index; /// try { await logicService.removeItem({ headers: { appId: config.defaultApp?.id, serviceId: this.logic?.interface?.serviceId, operationAction: 'LogicItem.delete', operationDesc: `删除逻辑项"${this.label || this.type}"`, }, body, }); } catch (err) { await config.defaultApp?.history.load(); throw err; } } } if (this.id) { typeCheck.delete(this.id); this.logic.interface?.service?.syncStructures(); // 处理 CallQueryComponent 嵌套的情形 const parent: LogicItem = this.parent; if (parent && parent.checkType) { parent.checkType(); } } !cache && this.remove(); this.destroy(); this.logic && this.logic.emit('change'); await config.defaultApp?.history.load(actionOptions?.actionMode !== ACTION_MODE.undoRedo && { operationAction: 'LogicItem.delete', operationBeforeImage: JSON.parse(JSON.stringify(this)), operationAfterImage: null, operationDesc: `删除逻辑项"${this.label || this.type}"`, }); config.defaultApp?.emit('saved'); } /** * LogicItem 有选择框的情况,为了减少变量变更,使用 replaceWith 的方式 */ async replaceWith(logicItem: LogicItem) { await this.delete(true); let _posIndex: number; if (this.parent) { const attr = this.parent[this.parentAttr as keyof LogicItem]; if (Array.isArray(attr)) _posIndex = attr.indexOf(this); } await logicItem.createOrMove({ parent: this.parent, parentId: this.parentId, parentAttr: this.parentAttr, _posIndex, cache: true, }); logicItem.assign({ offsetX: this.offsetX, offsetY: this.offsetY, }); this.assign(logicItem); this.logic && this.logic.emit('change'); return this; } /** * 修改逻辑项 */ async update(source?: LogicItem, actionOptions?: ActionOptions) { config.defaultApp?.emit('saving'); if (source) { // 处理子节点 if (Object.values(source).some((value) => typeof value === 'object')) { source = LogicItem.from(source, this.logic, this.parent); utils.clearObject(source); } this.assign(source); } // const body = this.toPlainJSON(); const body = this.toJSON(); body.params = (this as any).params && JSON.parse(JSON.stringify((this as any).params)); body.querySchemaList = (this as any).querySchemaList && Array.from((this as any).querySchemaList); body.returnSchema = (this as any).returnSchema; if (this.parentAttr === 'item') body.schema = (this as any).schema; if (this.type === 'TypeNote') body.schema = (this as any).schema; LogicItem.preprocess(body); utils.logger.debug('修改逻辑项', body); if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) { const result = await logicService.updateItem({ headers: { appId: config.defaultApp?.id, operationAction: 'LogicItem.update', operationDesc: `修改逻辑项"${this.label || this.type}"`, operationIgnore: actionOptions?.actionIgnore, }, body, }); // this.deepPick(result, ['id', 'parentId', 'parentAttr']); // 合并params里的id if (body.params && body.params.length) { const logicItem = LogicItem.from(result, this.logic, this.parent); this.assign({ params: (logicItem as any).params }); } if (body.builtInFuncParams && body.builtInFuncParams.length) { const logicItem = LogicItem.from(result, this.logic, this.parent); this.assign({ builtInFuncParams: (logicItem as any).builtInFuncParams }); } } // 处理 each 的问题 if (this.parentAttr === 'each') { const each = this; const item: any = (this.parent as LogicNode).item; let eachSchema = getSchemaOfExpressionNode(each); if (eachSchema) { eachSchema = eachSchema.schema || eachSchema; // eachSchema = convert2SchemaType(eachSchema); (item as any).schema = eachSchema.items; // 兼容老的items形式 if (eachSchema.type === 'genericType') { (item as any).schema = eachSchema.typeInstantiation.typeParams[0].typeParamValue; } if (!(item as any).schema) { (item as any).schema = eachSchema; } return item.update(undefined, { actionIgnore: true, }); } } this.emitVertexIdToNameChange(); this.logic && this.logic.emit('change'); this.checkType(); await config.defaultApp?.history.load(actionOptions?.actionMode !== ACTION_MODE.undoRedo && { operationAction: 'LogicItem.update', operationBeforeImage: null, // JSON.parse(JSON.stringify(this)), operationAfterImage: null, operationDesc: `修改逻辑项"${this.label || this.type}"`, }); config.defaultApp?.emit('saved'); return this; } /** * 设置逻辑项标题 * @param label 标题 */ @action('设置逻辑项标题') async setLabel(label: string) { this.assign({ label }); await this.update(undefined, { actionDesc: '设置逻辑项标题', }); } /** * 从后端 JSON 生成规范的 ExpressionNode 对象 * @param source JSON * @param logic 父级 logic */ public static from(source: any, logic: Logic, parent: LogicItem, shouldCreateNewItem = true) { if (source.level === LEVEL_ENUM.variable && source.type === LOGIC_TYPE.Identifier) source.code = `ID_${source.id}`; let logicItem: LogicItem; if (!shouldCreateNewItem && (source instanceof LogicItem)) logicItem = source; else logicItem = source.level === 'logicNode' ? new LogicNode(source) : new ExpressionNode(source); logicItem.assign({ logic, parent, }); if (logicItem.type === LOGIC_TYPE.CallGraphQL) { const querySchemaList = (logicItem as any).querySchemaList; logicItem.assign({ querySchemaList: querySchemaList && typeof querySchemaList === 'string' ? JSON.parse(querySchemaList || '[]') : querySchemaList }); } if (logicItem.type === LOGIC_TYPE.CallQueryComponent) { logicItem.assign({ logicName: logic.name, serviceId: logic.interface?.serviceId, }); } if (['QueryFromExpression', 'QueryJoinExpression', 'QueryAggregateExpression'].includes(logicItem.type)) { logicItem.assign({ serviceId: logic?.interface?.serviceId, }); } logicItemKeyOfLogicItem.forEach((key) => { if (source[key]) { if (source[key].level === LEVEL_ENUM.logicNode) logicItem.assign({ [key]: LogicNode.from(source[key], logic, logicItem) }); else if (source[key].level === LEVEL_ENUM.expressionNode || source[key].level === LEVEL_ENUM.variable) logicItem.assign({ [key]: ExpressionNode.from(source[key], logic, logicItem) }); else if (!Array.isArray(source[key]) && !Object.keys(source[key]).length) logicItem.assign({ [key]: null }); } }); logicItemArrayKeyOfLogicItem.forEach((key) => { if (Array.isArray(source[key])) logicItem.assign({ [key]: source[key].map((item: any) => LogicItem.from(item, logic, logicItem)) }); }); if ((logicItem as any).callInterParam) { logicItem.assign({ refTarget: LogicItem.getVertexByRef((logicItem as any).callInterParam) }); } else if ((logicItem as any).interfaceKey) { logicItem.assign({ refTarget: LogicItem.getVertexByRef((logicItem as any).interfaceKey) }); } return logicItem; } /** * 校验类型 */ async checkType() { let crt: LogicItem = this; const nodes = []; do { if ( !['builtInFuncParams'].includes(crt.parentAttr) && !(crt instanceof ExpressionNode && crt.parent instanceof LogicNode) && (crt instanceof ExpressionNode || !crt.parent) ) nodes.push(crt); // CallQueryComponent 内部的节点不用checkType if (crt.parent?.type === LOGIC_TYPE.CallQueryComponent) nodes.splice(0, nodes.length); crt = crt.parent; } while (crt instanceof LogicItem); for (const node of nodes) { await this._checkType(node); } } private async _checkType(node: LogicItem) { const res = await logicService.checkType({ query: { logicId: this.logic && this.logic.id, loItemId: node.id, }, }); if (res) { res.logicId = this.logic && this.logic.id; typeCheck.pushAll([res]); LogicItem.assignTypeCheckResult(node, res); } return res; } static assignTypeCheckResult(logicItem: LogicItem | LogicItem[], typeCheckResult: any) { if (Array.isArray(logicItem)) { const map = new Map(); for (const item of typeCheckResult) { map.set(item.id, item); } for (let i = 0; i < logicItem.length; i++) { LogicItem.assignTypeCheckResult(logicItem[i], map.get(logicItem[i].id)); } return; } if (!typeCheckResult || !logicItem || typeof typeCheckResult !== 'object') return; // if(logicItem.id === typeCheckResult.id) logicItem.typeCheckNote = typeCheckResult.typeCheckNote; for (const [key, object] of Object.entries(typeCheckResult)) { if (['id', 'typeCheckNote', 'processComponentId', 'attributes'].includes(key)) continue; LogicItem.assignTypeCheckResult((logicItem as any)[key], object); } } /** * 生成 JS 脚本 */ toScript() { return ''; } getLogicItem(type: LOGIC_TYPE) { let node: LogicItem = this; while (node) { if (node.type === LOGIC_TYPE[type]) return node; node = node.parent; } return undefined; } getSelectedTabOfCallQueryComponent(activeImage: any) { const map: { [key: string]: string } = { where: 'where', from: 'from', groupBy: 'groupBy', select: 'groupBy', having: 'groupBy', orderBy: 'orderBy', limit: 'orderBy', }; let node: LogicItem = this; while (node) { if (node.parent?.type === LOGIC_TYPE.CallQueryComponent) { const parentAttr = node.parentAttr; return { tab: map[parentAttr], parentAttr, }; } node = node.parent; } if (activeImage?.parentAttr && map[activeImage.parentAttr]) return { tab: map[activeImage.parentAttr], parentAttr: activeImage.parentAttr, }; } } export class LogicNode extends LogicItem { /** * 逻辑节点类型 */ @immutable() public readonly type: LOGIC_TYPE = undefined; @immutable() public readonly test: ExpressionNode = undefined; @immutable() public readonly consequent: Array = undefined; public readonly alternate: Array = undefined; public readonly cases: Array = undefined; @immutable() public readonly each: ExpressionNode = undefined; @immutable() public readonly item: ExpressionNode = undefined; @immutable() public readonly index: ExpressionNode = undefined; @immutable() public readonly start: ExpressionNode = undefined; @immutable() public readonly end: ExpressionNode = undefined; @immutable() public readonly body: Array = undefined; @immutable() public readonly operator: string = undefined; @immutable() public readonly left: LogicItem = undefined; @immutable() public readonly right: LogicItem = undefined; @immutable() public readonly arguments: Array = undefined; @immutable() public readonly params: Array = undefined; @immutable() public readonly value: string = undefined; @immutable() public readonly method: string = undefined; @immutable() public readonly entity: string = undefined; @immutable() public readonly action: string = undefined; @immutable() public readonly processDefinitionKey: string = undefined; @immutable() public readonly taskId: string = undefined; @immutable() public readonly finished: string = undefined; @immutable() public readonly processInstanceId: string = undefined; @immutable() public readonly schemaRef: string = undefined; @immutable() public readonly returnSchema: Schema = undefined; @immutable() public readonly cron: string = undefined; @immutable() public readonly page: string = undefined; @immutable() public readonly url: string = undefined; @immutable() public readonly code: string = undefined; @immutable() public readonly calleeCode: string = undefined; @immutable() public readonly interfaceKey: string = undefined; @immutable() public readonly name: string = undefined; @immutable() public readonly from: ExpressionNode = undefined; /** * @param source 需要合并的部分参数 */ constructor(source?: Partial) { super(); const hasInstantiated = source && source.id && vertexsMap.get(source.id) instanceof LogicNode; if (hasInstantiated) { console.warn(`LogicNode (${source.id}) 多次实例化`); // Object.assign(vertexsMap.get(source.id), source); // return vertexsMap.get(source.id) as LogicNode; } source && this.assign(source); } } export class ExpressionNode extends LogicItem { /** * 逻辑节点类型 */ @immutable() public readonly type: LOGIC_TYPE = undefined; @immutable() public readonly left: LogicItem = undefined; @immutable() public readonly right: LogicItem = undefined; @immutable() public readonly code: string = undefined; @immutable() public readonly name: string = undefined; @immutable() public readonly key: ExpressionNode = undefined; @immutable() public readonly value: string | ExpressionNode = undefined; @immutable() public readonly object: ExpressionNode = undefined; @immutable() public readonly property: ExpressionNode = undefined; @immutable() public readonly operator: string = undefined; @immutable() public readonly properties: Array = undefined; @immutable() public readonly elements: Array = undefined; @immutable() public readonly callee: ExpressionNode = undefined; @immutable() public readonly argument: ExpressionNode = undefined; /** * SchemaRef */ @immutable() public readonly schemaRef: string = undefined; @immutable() public readonly joinType: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL' = undefined; /** * @param source 需要合并的部分参数 */ constructor(source?: Partial) { super(); source && this.assign(source); } }