@lcap/asl
Version:
NetEase Application Specific Language
1,258 lines (1,147 loc) • 43.5 kB
text/typescript
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 <string>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(<LogicNode>node.left, finalCode);
if (!finalCode && node.left?.type === LOGIC_TYPE.MemberExpression)
left = `(${left})`;
let right: string = evaluate(<LogicNode>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(<LogicNode>node.left, finalCode);
if (!finalCode && node.left?.type === LOGIC_TYPE.MemberExpression)
left = `(${left})`;
let right: string = evaluate(<LogicNode>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 = (<ExpressionNode>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((<ExpressionNode>node).object, finalCode);
const property = (<ExpressionNode>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 (<LogicNode>node).arguments) {
args.push(evaluate(argumentNode, finalCode));
}
const name = (<ExpressionNode>node).callee.name;
return `${name}(${args.join(', ')})`;
}
if (node.type === 'ObjectExpression') {
const obj: {
[prop: string]: any
} = {};
for (let { key, value } of (<ExpressionNode>node).properties) {
let keyValue = evaluate(key, finalCode);
if (/^"(.*)"$/.test(keyValue))
keyValue = RegExp.$1;
const valueTemp = evaluate(<ExpressionNode>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 (<ExpressionNode>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 {
/**
* 概念类型
*/
()
public readonly level: LEVEL_ENUM = LEVEL_ENUM.logicItem;
/**
* Id
*/
()
public readonly id: string = undefined;
/**
* 父级 Id
*/
()
public readonly parentId: string = undefined;
/**
* 所属父级属性
*/
()
public readonly parentAttr: string = undefined;
()
()
public readonly parent: LogicItem = undefined;
()
public readonly logicId: string = undefined;
()
public readonly logic: Logic = undefined;
/**
* 标题
*/
()
public readonly label: string = undefined;
()
public folded: boolean = false;
()
public placeholderName: string = undefined;
/**
* OffsetX
*/
()
public readonly offsetX: number = undefined;
/**
* OffsetY
*/
()
public readonly offsetY: number = undefined;
/**
* 所在位置
*/
()
public readonly _posIndex: number = undefined;
/**
* 标题
*/
()
public readonly type: string = undefined;
/**
* Ref
*/
()
()
public readonly refTarget: any = undefined;
()
public readonly callInterParam: any = undefined;
()
public readonly callInterParamValue: any = undefined;
()
public readonly builtInFuncParamValue: any = undefined;
()
public readonly pageParamKey: any = undefined;
()
public readonly pageParamKeyValue: any = undefined;
/**
* CallQueryComponent 对应的 structure 的 id
*/
()
public readonly structureRef: string = undefined;
()
public readonly schema?: Schema = undefined;
/**
* 类型校验结果
*/
()
public typeCheckNote: any = undefined;
/**
* @param source 需要合并的部分参数
*/
constructor(source?: Partial<LogicItem>) {
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 在前端不处理节点
*/
('删除逻辑节点')
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 标题
*/
('设置逻辑项标题')
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 {
/**
* 逻辑节点类型
*/
()
public readonly type: LOGIC_TYPE = undefined;
()
public readonly test: ExpressionNode = undefined;
()
public readonly consequent: Array<LogicNode> = undefined;
public readonly alternate: Array<LogicNode> = undefined;
public readonly cases: Array<LogicNode> = undefined;
()
public readonly each: ExpressionNode = undefined;
()
public readonly item: ExpressionNode = undefined;
()
public readonly index: ExpressionNode = undefined;
()
public readonly start: ExpressionNode = undefined;
()
public readonly end: ExpressionNode = undefined;
()
public readonly body: Array<LogicNode> = undefined;
()
public readonly operator: string = undefined;
()
public readonly left: LogicItem = undefined;
()
public readonly right: LogicItem = undefined;
()
public readonly arguments: Array<ExpressionNode> = undefined;
()
public readonly params: Array<ExpressionNode> = undefined;
()
public readonly value: string = undefined;
()
public readonly method: string = undefined;
()
public readonly entity: string = undefined;
()
public readonly action: string = undefined;
()
public readonly processDefinitionKey: string = undefined;
()
public readonly taskId: string = undefined;
()
public readonly finished: string = undefined;
()
public readonly processInstanceId: string = undefined;
()
public readonly schemaRef: string = undefined;
()
public readonly returnSchema: Schema = undefined;
()
public readonly cron: string = undefined;
()
public readonly page: string = undefined;
()
public readonly url: string = undefined;
()
public readonly code: string = undefined;
()
public readonly calleeCode: string = undefined;
()
public readonly interfaceKey: string = undefined;
()
public readonly name: string = undefined;
()
public readonly from: ExpressionNode = undefined;
/**
* @param source 需要合并的部分参数
*/
constructor(source?: Partial<LogicNode>) {
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 {
/**
* 逻辑节点类型
*/
()
public readonly type: LOGIC_TYPE = undefined;
()
public readonly left: LogicItem = undefined;
()
public readonly right: LogicItem = undefined;
()
public readonly code: string = undefined;
()
public readonly name: string = undefined;
()
public readonly key: ExpressionNode = undefined;
()
public readonly value: string | ExpressionNode = undefined;
()
public readonly object: ExpressionNode = undefined;
()
public readonly property: ExpressionNode = undefined;
()
public readonly operator: string = undefined;
()
public readonly properties: Array<ExpressionNode> = undefined;
()
public readonly elements: Array<ExpressionNode> = undefined;
()
public readonly callee: ExpressionNode = undefined;
()
public readonly argument: ExpressionNode = undefined;
/**
* SchemaRef
*/
()
public readonly schemaRef: string = undefined;
()
public readonly joinType: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL' = undefined;
/**
* @param source 需要合并的部分参数
*/
constructor(source?: Partial<ExpressionNode>) {
super();
source && this.assign(source);
}
}