@steedos/service-metadata-objects 3.0.0-beta.9 → 3.0.0-beta.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/actionsHandler.js +45 -21
- package/lib/actionsHandler.js.map +1 -1
- package/lib/formula/core.js +35 -8
- package/lib/formula/core.js.map +1 -1
- package/lib/formula/formulaActionHandler.d.ts +1 -1
- package/lib/formula/formulaActionHandler.js +40 -23
- package/lib/formula/formulaActionHandler.js.map +1 -1
- package/lib/summary/summaryActionHandler.d.ts +2 -2
- package/lib/summary/summaryActionHandler.js +21 -11
- package/lib/summary/summaryActionHandler.js.map +1 -1
- package/package.json +5 -5
- package/src/actionsHandler.ts +335 -258
- package/src/formula/core.ts +44 -10
- package/src/formula/formulaActionHandler.ts +499 -379
- package/src/summary/summaryActionHandler.ts +279 -208
|
@@ -1,435 +1,555 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
SteedosFieldFormulaTypeConfig,
|
|
3
|
+
SteedosFieldFormulaQuoteTypeConfig,
|
|
4
|
+
SteedosFormulaVarTypeConfig,
|
|
5
|
+
SteedosFormulaVarPathTypeConfig,
|
|
6
|
+
FormulaUserKey,
|
|
7
|
+
FormulaUserSessionKey,
|
|
8
|
+
SteedosFormulaBlankValue,
|
|
9
|
+
} from "./type";
|
|
10
|
+
import { pickFormulaVars } from "./core";
|
|
3
11
|
// import { isCodeObject } from '../util'; TODO
|
|
4
|
-
import { Register } from
|
|
12
|
+
import { Register } from "@steedos/metadata-registrar";
|
|
5
13
|
|
|
6
|
-
import _ = require(
|
|
7
|
-
const clone = require(
|
|
14
|
+
import _ = require("lodash");
|
|
15
|
+
const clone = require("clone");
|
|
8
16
|
|
|
9
17
|
// TODO
|
|
10
|
-
const isCodeObject = (objectApiName)=>{
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const refMapName = '$formula_ref_maps';
|
|
18
|
+
const isCodeObject = (objectApiName) => {
|
|
19
|
+
return objectApiName ? false : true;
|
|
20
|
+
};
|
|
15
21
|
|
|
22
|
+
const refMapName = "$formula_ref_maps";
|
|
16
23
|
|
|
17
|
-
function getDynamicCalcMapCacherKey(
|
|
18
|
-
|
|
24
|
+
function getDynamicCalcMapCacherKey(
|
|
25
|
+
objectApiName: string,
|
|
26
|
+
mainObjectApiName: string,
|
|
27
|
+
): string {
|
|
28
|
+
return `$dynamic_calc_map.${objectApiName}.${mainObjectApiName}`;
|
|
19
29
|
}
|
|
20
30
|
|
|
21
|
-
export class FormulaActionHandler{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
export class FormulaActionHandler {
|
|
32
|
+
broker: any = null;
|
|
33
|
+
constructor(broker) {
|
|
34
|
+
this.broker = broker;
|
|
35
|
+
}
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
async deleteAll(objectConfig) {
|
|
38
|
+
try {
|
|
39
|
+
if (!objectConfig) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// console.log(`deleteAll formula`, `${this.cacherKey(objectConfig.name)}.*`)
|
|
43
|
+
// await this.broker.call('metadata.fuzzyDelete', {key: `${this.cacherKey(objectConfig.name)}.*`}, {meta: {}})
|
|
44
|
+
await Register.fuzzyDelete(
|
|
45
|
+
this.broker,
|
|
46
|
+
`${this.cacherKey(objectConfig.name)}.*`,
|
|
47
|
+
);
|
|
48
|
+
return true;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
this.broker.logger.error(error);
|
|
40
51
|
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
55
|
+
async getObjectConfig(objectApiName: string) {
|
|
56
|
+
const data = await this.broker.call("objects.get", {
|
|
57
|
+
objectApiName: objectApiName,
|
|
58
|
+
});
|
|
59
|
+
return data ? data.metadata : null;
|
|
60
|
+
}
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
addFieldFormulaQuotesConfig(
|
|
63
|
+
quote: SteedosFieldFormulaQuoteTypeConfig,
|
|
64
|
+
quotes: Array<SteedosFieldFormulaQuoteTypeConfig>,
|
|
65
|
+
) {
|
|
66
|
+
if (quote.field_name === "_id") {
|
|
67
|
+
// _id字段不记为引用关系,因为其值不会变化,相关记录属性变更时不需要重算被引用的字段公式
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
let existQuote = quotes.find((item) => {
|
|
71
|
+
return (
|
|
72
|
+
item.field_name === quote.field_name &&
|
|
73
|
+
item.object_name === quote.object_name
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
if (!existQuote) {
|
|
77
|
+
quotes.push(quote);
|
|
58
78
|
}
|
|
79
|
+
}
|
|
59
80
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
/**
|
|
82
|
+
* 把公式中a.b.c,比如account.website这样的变量转为SteedosFieldFormulaQuoteTypeConfig和SteedosFieldFormulaVarTypeConfig追加到quotes和vars中
|
|
83
|
+
* 因为getObjectConfigs拿到的对象肯定不包括被禁用和假删除的对象,所以不需要额外判断相关状态
|
|
84
|
+
* @param formulaVar 公式中的单个变量,比如account.website
|
|
85
|
+
* @param fieldConfig
|
|
86
|
+
* @param objectConfigs
|
|
87
|
+
* @param quotes
|
|
88
|
+
*/
|
|
89
|
+
async computeFormulaVarAndQuotes(
|
|
90
|
+
formulaVar: string,
|
|
91
|
+
objectConfig: any,
|
|
92
|
+
quotes: Array<SteedosFieldFormulaQuoteTypeConfig>,
|
|
93
|
+
vars: Array<SteedosFormulaVarTypeConfig>,
|
|
94
|
+
) {
|
|
95
|
+
let isSimpleVar = objectConfig === null; //明确传入null时表示要计算的是普通变量。
|
|
96
|
+
// global.user.profile这种 amis 全局变量不参与变量计算,直接跳过
|
|
97
|
+
let isAmisGlobalVar = /^global\./.test(formulaVar);
|
|
98
|
+
if (isAmisGlobalVar) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// 公式变量以FormulaUserSessionKey(即$user)值开头,说明是user变量
|
|
102
|
+
let isUserVar = new RegExp(
|
|
103
|
+
`^${FormulaUserKey.replace("$", "\\$")}\\b`,
|
|
104
|
+
).test(formulaVar);
|
|
105
|
+
let isUserSessionVar = new RegExp(
|
|
106
|
+
`^${FormulaUserSessionKey.replace("$", "\\$")}\\b`,
|
|
107
|
+
).test(formulaVar);
|
|
108
|
+
let varItems = formulaVar.split(".");
|
|
109
|
+
let paths: Array<SteedosFormulaVarPathTypeConfig> = [];
|
|
110
|
+
let formulaVarItem: SteedosFormulaVarTypeConfig = {
|
|
111
|
+
key: formulaVar,
|
|
112
|
+
paths: paths,
|
|
113
|
+
};
|
|
114
|
+
if (isUserVar || isUserSessionVar) {
|
|
115
|
+
// user var不可以按普通变量处理,因为其变量值依赖paths属性
|
|
116
|
+
isSimpleVar = false;
|
|
117
|
+
objectConfig = "space_users";
|
|
118
|
+
}
|
|
119
|
+
if (isSimpleVar) {
|
|
120
|
+
// 只要不是user var则标记为普通变量,后续取参数值时直接通过key取值,而不用走变量上的paths属性。
|
|
121
|
+
// 普通变量直接跳过后面所有代码即可,因为不需要处理paths和公式关系链等逻辑。
|
|
122
|
+
// 注意普通公式也是支持$user全局变量的,但是isSimpleVar是false,所以走的是后面的逻辑
|
|
123
|
+
formulaVarItem.is_simple_var = true;
|
|
124
|
+
vars.push(formulaVarItem);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (isUserSessionVar) {
|
|
128
|
+
// $userSession变量直接跳后后面所有代码即可,因为不需要处理paths和公式关系链等逻辑。
|
|
129
|
+
formulaVarItem.is_user_session_var = true;
|
|
130
|
+
vars.push(formulaVarItem);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (isUserVar) {
|
|
134
|
+
// 如果是user变量,则不需要计算quotes引用,但是其paths值需要正常记录下来
|
|
135
|
+
formulaVarItem.is_user_var = true;
|
|
136
|
+
// vars.push(formulaVarItem);
|
|
137
|
+
// return;
|
|
138
|
+
} else {
|
|
139
|
+
if (formulaVar.startsWith("$")) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`computeFormulaVarAndQuotes:The formula var '${formulaVar}' is starts with '$' but not a user session var that starts with $user.`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (!objectConfig) {
|
|
145
|
+
// 不是$user变量时,需要传入objectConfig参数
|
|
146
|
+
throw new Error(
|
|
147
|
+
`computeFormulaVarAndQuotes:The 'objectConfig' is required for the formula var '${formulaVar}'`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
let tempObjectConfig = objectConfig;
|
|
152
|
+
let i = -1;
|
|
153
|
+
for await (let varItem of varItems) {
|
|
154
|
+
i++;
|
|
155
|
+
// let i:number = _index as unknown as number;
|
|
156
|
+
// let varItem = varItems[i];
|
|
157
|
+
let tempFieldConfig: any;
|
|
158
|
+
const isUserKey = varItem === FormulaUserKey;
|
|
159
|
+
if (varItem === "_id") {
|
|
160
|
+
// 支持_id属性
|
|
161
|
+
tempFieldConfig = {
|
|
162
|
+
name: varItem,
|
|
163
|
+
type: "text",
|
|
164
|
+
};
|
|
165
|
+
} else if (isUserKey) {
|
|
166
|
+
// 如果是$user变量,则特殊处理下
|
|
167
|
+
tempFieldConfig = {
|
|
168
|
+
name: varItem,
|
|
169
|
+
type: "lookup",
|
|
170
|
+
reference_to: "space_users",
|
|
171
|
+
};
|
|
172
|
+
} else {
|
|
173
|
+
tempFieldConfig = tempObjectConfig.fields[varItem];
|
|
174
|
+
}
|
|
175
|
+
if (!tempFieldConfig) {
|
|
176
|
+
// 不是对象上的字段,则直接退出,这里注意公式中引用零代码中的字段的话,公式中字段名需要手动加上__c后缀(因为用户填写的api名称不带__c,是内核会自动加后缀),否则会找不到
|
|
177
|
+
// throw new Error(`computeFormulaVarAndQuotes:Can't find the field '${varItem}' on the object '${tempObjectConfig.name}' for the formula var '${formulaVar}'`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
let isFormulaType = tempFieldConfig.type === "formula";
|
|
181
|
+
if (!isUserKey) {
|
|
182
|
+
let tempFieldFormulaVarPath: SteedosFormulaVarPathTypeConfig = {
|
|
183
|
+
field_name: varItem,
|
|
184
|
+
reference_from: tempObjectConfig.name,
|
|
78
185
|
};
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
isSimpleVar = false;
|
|
82
|
-
objectConfig = "space_users";
|
|
186
|
+
if (isFormulaType) {
|
|
187
|
+
tempFieldFormulaVarPath.is_formula = true;
|
|
83
188
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
189
|
+
// 当是$user时,不需要把第一个path记录下来,只需要记录其后续的路径即可
|
|
190
|
+
paths.push(tempFieldFormulaVarPath);
|
|
191
|
+
}
|
|
192
|
+
if (!isUserVar) {
|
|
193
|
+
// $user变量不需要记录引用关系,因为没法确定每条record当时对应的当前用户ID是多少
|
|
194
|
+
// 自己可以引用自己,i大于0就是其他对象上的引用
|
|
195
|
+
let tempFieldFormulaQuote: SteedosFieldFormulaQuoteTypeConfig = {
|
|
196
|
+
object_name: tempObjectConfig.name,
|
|
197
|
+
field_name: tempFieldConfig.name,
|
|
198
|
+
};
|
|
199
|
+
if (i === 0 && i === varItems.length - 1) {
|
|
200
|
+
// 是引用的本对象上自身的字段,即自己引用自己
|
|
201
|
+
tempFieldFormulaQuote.is_own = true;
|
|
91
202
|
}
|
|
92
|
-
if(
|
|
93
|
-
|
|
94
|
-
formulaVarItem.is_user_session_var = true;
|
|
95
|
-
vars.push(formulaVarItem);
|
|
96
|
-
return;
|
|
203
|
+
if (isFormulaType) {
|
|
204
|
+
tempFieldFormulaQuote.is_formula = true;
|
|
97
205
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
206
|
+
this.addFieldFormulaQuotesConfig(tempFieldFormulaQuote, quotes);
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
tempFieldConfig.type === "lookup" ||
|
|
210
|
+
tempFieldConfig.type === "master_detail"
|
|
211
|
+
) {
|
|
212
|
+
// 引用类型字段
|
|
213
|
+
if (tempFieldConfig.multiple) {
|
|
214
|
+
// TODO:暂时不支持数组的解析,见:公式字段中要实现lookup关联到数组字段的情况 #783
|
|
215
|
+
throw new Error(
|
|
216
|
+
`computeFormulaVarAndQuotes:The field '${tempFieldConfig.name}' for the formula var '${formulaVar}' is a multiple ${tempFieldConfig.type} type, it is not supported yet.`,
|
|
217
|
+
);
|
|
103
218
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
219
|
+
// if (i === varItems.length - 1) {
|
|
220
|
+
// // 引用类型字段后面必须继续引用该字段的相关属性,否则直接报错
|
|
221
|
+
// throw new Error(`computeFormulaVarAndQuotes:The field '${tempFieldConfig.name}' for the formula var '${formulaVar}' is a ${tempFieldConfig.type} type, so you must add more property after it.`);
|
|
222
|
+
// }
|
|
223
|
+
if (tempFieldConfig.reference_to_field && paths.length) {
|
|
224
|
+
paths[paths.length - 1].reference_to_field =
|
|
225
|
+
tempFieldConfig.reference_to_field;
|
|
112
226
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else if(isUserKey){
|
|
129
|
-
// 如果是$user变量,则特殊处理下
|
|
130
|
-
tempFieldConfig = {
|
|
131
|
-
name: varItem,
|
|
132
|
-
type: "lookup",
|
|
133
|
-
reference_to: "space_users"
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else{
|
|
137
|
-
tempFieldConfig = tempObjectConfig.fields[varItem];
|
|
138
|
-
}
|
|
139
|
-
if (!tempFieldConfig) {
|
|
140
|
-
// 不是对象上的字段,则直接退出,这里注意公式中引用零代码中的字段的话,公式中字段名需要手动加上__c后缀(因为用户填写的api名称不带__c,是内核会自动加后缀),否则会找不到
|
|
141
|
-
// throw new Error(`computeFormulaVarAndQuotes:Can't find the field '${varItem}' on the object '${tempObjectConfig.name}' for the formula var '${formulaVar}'`);
|
|
142
|
-
return ;
|
|
143
|
-
}
|
|
144
|
-
let isFormulaType = tempFieldConfig.type === "formula";
|
|
145
|
-
if(!isUserKey){
|
|
146
|
-
let tempFieldFormulaVarPath: SteedosFormulaVarPathTypeConfig = {
|
|
147
|
-
field_name: varItem,
|
|
148
|
-
reference_from: tempObjectConfig.name
|
|
149
|
-
};
|
|
150
|
-
if (isFormulaType) {
|
|
151
|
-
tempFieldFormulaVarPath.is_formula = true;
|
|
152
|
-
}
|
|
153
|
-
// 当是$user时,不需要把第一个path记录下来,只需要记录其后续的路径即可
|
|
154
|
-
paths.push(tempFieldFormulaVarPath);
|
|
155
|
-
}
|
|
156
|
-
if (!isUserVar) {
|
|
157
|
-
// $user变量不需要记录引用关系,因为没法确定每条record当时对应的当前用户ID是多少
|
|
158
|
-
// 自己可以引用自己,i大于0就是其他对象上的引用
|
|
159
|
-
let tempFieldFormulaQuote: SteedosFieldFormulaQuoteTypeConfig = {
|
|
160
|
-
object_name: tempObjectConfig.name,
|
|
161
|
-
field_name: tempFieldConfig.name
|
|
162
|
-
};
|
|
163
|
-
if(i === 0 && i === varItems.length - 1){
|
|
164
|
-
// 是引用的本对象上自身的字段,即自己引用自己
|
|
165
|
-
tempFieldFormulaQuote.is_own = true;
|
|
166
|
-
}
|
|
167
|
-
if (isFormulaType) {
|
|
168
|
-
tempFieldFormulaQuote.is_formula = true;
|
|
169
|
-
}
|
|
170
|
-
this.addFieldFormulaQuotesConfig(tempFieldFormulaQuote, quotes);
|
|
171
|
-
}
|
|
172
|
-
if (tempFieldConfig.type === "lookup" || tempFieldConfig.type === "master_detail") {
|
|
173
|
-
// 引用类型字段
|
|
174
|
-
if (tempFieldConfig.multiple) {
|
|
175
|
-
// TODO:暂时不支持数组的解析,见:公式字段中要实现lookup关联到数组字段的情况 #783
|
|
176
|
-
throw new Error(`computeFormulaVarAndQuotes:The field '${tempFieldConfig.name}' for the formula var '${formulaVar}' is a multiple ${tempFieldConfig.type} type, it is not supported yet.`);
|
|
177
|
-
}
|
|
178
|
-
// if (i === varItems.length - 1) {
|
|
179
|
-
// // 引用类型字段后面必须继续引用该字段的相关属性,否则直接报错
|
|
180
|
-
// throw new Error(`computeFormulaVarAndQuotes:The field '${tempFieldConfig.name}' for the formula var '${formulaVar}' is a ${tempFieldConfig.type} type, so you must add more property after it.`);
|
|
181
|
-
// }
|
|
182
|
-
if(tempFieldConfig.reference_to_field && paths.length){
|
|
183
|
-
paths[paths.length - 1].reference_to_field = tempFieldConfig.reference_to_field;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
// 不是引用类型字段,则直接退出
|
|
188
|
-
if (i < varItems.length - 1) {
|
|
189
|
-
// 提前找到非跨对象字段,说明varItems中后面没计算的变量是多余错误的,因为.后面肯定是跨对象引用出来的字段(除非是$user等全局变量)
|
|
190
|
-
throw new Error(`computeFormulaVarAndQuotes:The field '${tempFieldConfig.name}' for the formula var '${formulaVar}' is not a lookup/master_detail type, so you can't get more property for it.`);
|
|
191
|
-
}
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
if (typeof tempFieldConfig.reference_to !== "string") {
|
|
195
|
-
// 暂时只支持reference_to为字符的情况,其他类型直接跳过
|
|
196
|
-
throw new Error(`Field ${tempFieldConfig.name} in formula ${formulaVar} does not define the "Reference Object" property or its "Reference Object" property is a function.`);
|
|
197
|
-
}
|
|
198
|
-
tempObjectConfig = await this.getObjectConfig(tempFieldConfig.reference_to);
|
|
199
|
-
if (!tempObjectConfig) {
|
|
200
|
-
// 没找到相关引用对象,直接退出
|
|
201
|
-
// 如果不是零代码对象,直接报错,否则直接返回,待相关零代码对象加载进来时,会再进入该函数
|
|
202
|
-
if(isCodeObject(tempFieldConfig.reference_to)){
|
|
203
|
-
throw new Error(`computeFormulaVarAndQuotes:Can't find the object reference_to '${tempFieldConfig.reference_to}' by the field '${tempFieldConfig.name}' for the formula var '${formulaVar}'`);
|
|
204
|
-
}
|
|
205
|
-
else{
|
|
206
|
-
// await this.broker.call('metadata.add', {key: this.cacherKey(getDynamicCalcMapCacherKey(tempFieldConfig.reference_to, objectConfig.name)), data: {objectApiName: objectConfig.name}}, {meta: {}})
|
|
207
|
-
await Register.add(this.broker, {key: this.cacherKey(getDynamicCalcMapCacherKey(tempFieldConfig.reference_to, objectConfig.name)), data: {objectApiName: objectConfig.name}}, {});
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
227
|
+
} else {
|
|
228
|
+
// 不是引用类型字段,则直接退出
|
|
229
|
+
if (i < varItems.length - 1) {
|
|
230
|
+
// 考虑到 amis 公式,比如 ${name.length} 或 ${owner.name.length} 这种,name不是lookup字段,不需要进一步处理引用关系,但是也不能 throw new Error
|
|
231
|
+
// 此时 formulaVarItem.key 中应该是name 或 owner.name,而不是name.length 或 owner.name.length,即key中要把结尾的.length去除
|
|
232
|
+
formulaVarItem.key = formulaVarItem.key.replace(
|
|
233
|
+
new RegExp(`((.+)?${varItem})..+`),
|
|
234
|
+
"$1",
|
|
235
|
+
);
|
|
236
|
+
// // 提前找到非跨对象字段,说明varItems中后面没计算的变量是多余错误的,因为.后面肯定是跨对象引用出来的字段(除非是$user等全局变量)
|
|
237
|
+
// throw new Error(
|
|
238
|
+
// `computeFormulaVarAndQuotes:The field '${tempFieldConfig.name}' for the formula var '${formulaVar}' is not a lookup/master_detail type, so you can't get more property for it.`,
|
|
239
|
+
// );
|
|
211
240
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
if (typeof tempFieldConfig.reference_to !== "string") {
|
|
244
|
+
// 暂时只支持reference_to为字符的情况,其他类型直接跳过
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Field ${tempFieldConfig.name} in formula ${formulaVar} does not define the "Reference Object" property or its "Reference Object" property is a function.`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
tempObjectConfig = await this.getObjectConfig(
|
|
250
|
+
tempFieldConfig.reference_to,
|
|
251
|
+
);
|
|
252
|
+
if (!tempObjectConfig) {
|
|
253
|
+
// 没找到相关引用对象,直接退出
|
|
254
|
+
// 如果不是零代码对象,直接报错,否则直接返回,待相关零代码对象加载进来时,会再进入该函数
|
|
255
|
+
if (isCodeObject(tempFieldConfig.reference_to)) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
`computeFormulaVarAndQuotes:Can't find the object reference_to '${tempFieldConfig.reference_to}' by the field '${tempFieldConfig.name}' for the formula var '${formulaVar}'`,
|
|
258
|
+
);
|
|
259
|
+
} else {
|
|
260
|
+
// await this.broker.call('metadata.add', {key: this.cacherKey(getDynamicCalcMapCacherKey(tempFieldConfig.reference_to, objectConfig.name)), data: {objectApiName: objectConfig.name}}, {meta: {}})
|
|
261
|
+
await Register.add(
|
|
262
|
+
this.broker,
|
|
263
|
+
{
|
|
264
|
+
key: this.cacherKey(
|
|
265
|
+
getDynamicCalcMapCacherKey(
|
|
266
|
+
tempFieldConfig.reference_to,
|
|
267
|
+
objectConfig.name,
|
|
268
|
+
),
|
|
269
|
+
),
|
|
270
|
+
data: { objectApiName: objectConfig.name },
|
|
271
|
+
},
|
|
272
|
+
{},
|
|
273
|
+
);
|
|
274
|
+
return;
|
|
226
275
|
}
|
|
227
|
-
|
|
228
|
-
throw new Error(`pickFormulaVars:Catch an error "${ex}" while pick vars from the formula "${formula}" for "${JSON.stringify(objectConfig)}"`);
|
|
229
|
-
}
|
|
230
|
-
for await(const formulaVar of formulaVars) {
|
|
231
|
-
await this.computeFormulaVarAndQuotes(formulaVar, objectConfig, quotes, vars);
|
|
232
|
-
}
|
|
233
|
-
return { quotes, vars };
|
|
276
|
+
}
|
|
234
277
|
}
|
|
278
|
+
vars.push(formulaVarItem);
|
|
279
|
+
}
|
|
235
280
|
|
|
236
|
-
|
|
237
|
-
|
|
281
|
+
/**
|
|
282
|
+
* 根据公式内容,取出其中{}中的变量,返回计算后的公式引用集合
|
|
283
|
+
* @param formula
|
|
284
|
+
* @param fieldConfig
|
|
285
|
+
*/
|
|
286
|
+
async computeFormulaVarsAndQuotes(formula: string, objectConfig: any) {
|
|
287
|
+
let quotes: Array<SteedosFieldFormulaQuoteTypeConfig> = [];
|
|
288
|
+
let vars: Array<SteedosFormulaVarTypeConfig> = [];
|
|
289
|
+
let formulaVars: any = [];
|
|
290
|
+
try {
|
|
291
|
+
formulaVars = pickFormulaVars(formula);
|
|
292
|
+
} catch (ex) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
`pickFormulaVars:Catch an error "${ex}" while pick vars from the formula "${formula}" for "${JSON.stringify(objectConfig)}"`,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
for await (const formulaVar of formulaVars) {
|
|
298
|
+
await this.computeFormulaVarAndQuotes(
|
|
299
|
+
formulaVar,
|
|
300
|
+
objectConfig,
|
|
301
|
+
quotes,
|
|
302
|
+
vars,
|
|
303
|
+
);
|
|
238
304
|
}
|
|
305
|
+
return { quotes, vars };
|
|
306
|
+
}
|
|
239
307
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
let formulaConfig: SteedosFieldFormulaTypeConfig = {
|
|
244
|
-
_id: `${objectConfig.name}.${fieldConfig.name}`,
|
|
245
|
-
object_name: objectConfig.name,
|
|
246
|
-
field_name: fieldConfig.name,
|
|
247
|
-
formula: formula,
|
|
248
|
-
data_type: fieldConfig.data_type,
|
|
249
|
-
formula_blank_value: <SteedosFormulaBlankValue>fieldConfig.formula_blank_value,
|
|
250
|
-
quotes: result.quotes,
|
|
251
|
-
vars: result.vars
|
|
252
|
-
};
|
|
308
|
+
cacherKey(APIName: string): string {
|
|
309
|
+
return `$steedos.#formula.${APIName}`;
|
|
310
|
+
}
|
|
253
311
|
|
|
254
|
-
|
|
255
|
-
|
|
312
|
+
async getObjectFieldFormulaConfig(fieldConfig: any, objectConfig: any) {
|
|
313
|
+
const formula = fieldConfig.formula;
|
|
314
|
+
let result = await this.computeFormulaVarsAndQuotes(formula, objectConfig);
|
|
315
|
+
let formulaConfig: SteedosFieldFormulaTypeConfig = {
|
|
316
|
+
_id: `${objectConfig.name}.${fieldConfig.name}`,
|
|
317
|
+
object_name: objectConfig.name,
|
|
318
|
+
field_name: fieldConfig.name,
|
|
319
|
+
formula: formula,
|
|
320
|
+
data_type: fieldConfig.data_type,
|
|
321
|
+
formula_blank_value: <SteedosFormulaBlankValue>(
|
|
322
|
+
fieldConfig.formula_blank_value
|
|
323
|
+
),
|
|
324
|
+
quotes: result.quotes,
|
|
325
|
+
vars: result.vars,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
return formulaConfig;
|
|
329
|
+
}
|
|
256
330
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
331
|
+
async getObjectFieldsFormulaConfig(config: any, datasource: string) {
|
|
332
|
+
const objectFieldsFormulaConfig = [];
|
|
333
|
+
for await (const field of _.values(config.fields)) {
|
|
334
|
+
// console.log('key', key);
|
|
335
|
+
// const field = config.fields[key]
|
|
336
|
+
if (field.type === "formula") {
|
|
337
|
+
// if(datasource !== "meteor" && datasource !== "default"){
|
|
338
|
+
// throw new Error(`The type of the field '${field.name}' on the object '${config.name}' can't be 'formula', because it is not in the default datasource.`);
|
|
339
|
+
// }
|
|
340
|
+
try {
|
|
341
|
+
// 这里一定要加try catch,否则某个字段报错后,后续其他字段及其他对象就再也没有正常加载了
|
|
342
|
+
const fieldFormulaConfig = await this.getObjectFieldFormulaConfig(
|
|
343
|
+
clone(field),
|
|
344
|
+
config,
|
|
345
|
+
);
|
|
346
|
+
objectFieldsFormulaConfig.push(fieldFormulaConfig);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error(error);
|
|
274
349
|
}
|
|
275
|
-
|
|
350
|
+
}
|
|
276
351
|
}
|
|
352
|
+
return objectFieldsFormulaConfig;
|
|
353
|
+
}
|
|
277
354
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
checkInfiniteLoop(data.key)
|
|
289
|
-
if(_.find(refs, function(ref){return ref === data.value})){
|
|
290
|
-
throw new Error(`Infinite Loop: ${JSON.stringify(data)}`);
|
|
355
|
+
checkRefMapData(maps, data) {
|
|
356
|
+
let refs = [];
|
|
357
|
+
function checkInfiniteLoop(value) {
|
|
358
|
+
_.each(maps, function (item) {
|
|
359
|
+
if (item.value === value) {
|
|
360
|
+
refs.push(item.key);
|
|
361
|
+
checkInfiniteLoop(item.key);
|
|
291
362
|
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
checkInfiniteLoop(data.key);
|
|
366
|
+
if (
|
|
367
|
+
_.find(refs, function (ref) {
|
|
368
|
+
return ref === data.value;
|
|
369
|
+
})
|
|
370
|
+
) {
|
|
371
|
+
throw new Error(`Infinite Loop: ${JSON.stringify(data)}`);
|
|
292
372
|
}
|
|
373
|
+
}
|
|
293
374
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
375
|
+
async getFormulaReferenceMaps(broker: any): Promise<any> {
|
|
376
|
+
const maps = [];
|
|
377
|
+
// let records = (await broker.call('metadata.filter', { key: this.cacherKey(`${refMapName}.*`) }, { meta: {} })) || {};
|
|
378
|
+
let records: any =
|
|
379
|
+
(await Register.filter(this.broker, this.cacherKey(`${refMapName}.*`))) ||
|
|
380
|
+
{};
|
|
381
|
+
for await (const item of records) {
|
|
382
|
+
const metadata = item?.metadata;
|
|
383
|
+
if (metadata) {
|
|
384
|
+
maps.push(item);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return { metadata: maps };
|
|
388
|
+
}
|
|
389
|
+
async addFormulaReferenceMaps(broker: any, key: string, value: string) {
|
|
390
|
+
let { metadata } = (await this.getFormulaReferenceMaps(broker)) || [];
|
|
391
|
+
let maps = [];
|
|
392
|
+
if (metadata) {
|
|
393
|
+
maps = metadata;
|
|
305
394
|
}
|
|
306
|
-
async addFormulaReferenceMaps(broker: any, key: string, value: string){
|
|
307
|
-
let { metadata } = (await this.getFormulaReferenceMaps(broker)) || [];
|
|
308
|
-
let maps = [];
|
|
309
|
-
if(metadata){
|
|
310
|
-
maps = metadata;
|
|
311
|
-
}
|
|
312
395
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
396
|
+
let data = {
|
|
397
|
+
key: key,
|
|
398
|
+
value: value,
|
|
399
|
+
};
|
|
317
400
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
401
|
+
// //checkData
|
|
402
|
+
this.checkRefMapData(maps, data);
|
|
403
|
+
// maps.push(data);
|
|
404
|
+
await Register.add(
|
|
405
|
+
this.broker,
|
|
406
|
+
{ key: this.cacherKey(`${refMapName}.${key}.${value}`), data: data },
|
|
407
|
+
{},
|
|
408
|
+
);
|
|
409
|
+
// await broker.call('metadata.add', {key: this.cacherKey(`${refMapName}.${key}.${value}`), data: data}, {meta: {}})
|
|
410
|
+
}
|
|
324
411
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
412
|
+
async addFormulaMetadata(config: any, datasource: string) {
|
|
413
|
+
const objectFieldsFormulaConfig = await this.getObjectFieldsFormulaConfig(
|
|
414
|
+
config,
|
|
415
|
+
datasource,
|
|
416
|
+
);
|
|
417
|
+
const objectFormulas = await this.filter({
|
|
418
|
+
params: { objectApiName: config.name },
|
|
419
|
+
});
|
|
420
|
+
const formulaFieldsName = _.map(objectFormulas, "field_name");
|
|
421
|
+
const objectFields = _.map(config.fields, "name");
|
|
422
|
+
const diff = _.difference(formulaFieldsName, objectFields);
|
|
423
|
+
for await (const fieldFormula of objectFieldsFormulaConfig) {
|
|
424
|
+
for await (const quote of fieldFormula.quotes) {
|
|
425
|
+
await this.addFormulaReferenceMaps(
|
|
426
|
+
this.broker,
|
|
427
|
+
`${fieldFormula._id}`,
|
|
428
|
+
`${quote.object_name}.${quote.field_name}`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
await Register.add(
|
|
432
|
+
this.broker,
|
|
433
|
+
{ key: this.cacherKey(fieldFormula._id), data: fieldFormula },
|
|
434
|
+
{},
|
|
435
|
+
);
|
|
436
|
+
// await this.broker.call('metadata.add', {key: this.cacherKey(fieldFormula._id), data: fieldFormula}, {meta: {}})
|
|
344
437
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
438
|
+
for (const deletedFieldName of diff) {
|
|
439
|
+
if (deletedFieldName) {
|
|
440
|
+
await Register.delete(
|
|
441
|
+
this.broker,
|
|
442
|
+
this.cacherKey(`${config.name}.${deletedFieldName}`),
|
|
443
|
+
);
|
|
444
|
+
}
|
|
349
445
|
}
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
350
448
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
if(recalcObjectConfig){
|
|
359
|
-
recalcObjects[recalcObjectApiName] = recalcObjectConfig
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
for (const objectName in recalcObjects) {
|
|
365
|
-
if (Object.prototype.hasOwnProperty.call(recalcObjects, objectName)) {
|
|
366
|
-
const recalcObjectConfig = recalcObjects[objectName];
|
|
367
|
-
await this.addFormulaMetadata(recalcObjectConfig, recalcObjectConfig.datasource);
|
|
368
|
-
await Register.delete(this.broker, this.cacherKey(getDynamicCalcMapCacherKey(objectConfig.name, objectName)))
|
|
369
|
-
// await this.broker.call('metadata.delete', {key: this.cacherKey(getDynamicCalcMapCacherKey(objectConfig.name, objectName))}, {meta: {}})
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
449
|
+
async getObjectDynamicCalcFormulaMap(objectApiName) {
|
|
450
|
+
return await Register.filter(
|
|
451
|
+
this.broker,
|
|
452
|
+
this.cacherKey(getDynamicCalcMapCacherKey(objectApiName, "*")),
|
|
453
|
+
);
|
|
454
|
+
// return await this.broker.call('metadata.filter', {key: this.cacherKey(getDynamicCalcMapCacherKey(objectApiName, '*'))}, {meta: {}});
|
|
455
|
+
}
|
|
373
456
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
457
|
+
async recalcObjectsFormulaMap(objectConfig) {
|
|
458
|
+
const recalcObjects = {};
|
|
459
|
+
const dynamicCalcFormulaMap = await this.getObjectDynamicCalcFormulaMap(
|
|
460
|
+
objectConfig.name,
|
|
461
|
+
);
|
|
462
|
+
for await (const item of dynamicCalcFormulaMap) {
|
|
463
|
+
const recalcObjectApiName = item?.metadata?.objectApiName;
|
|
464
|
+
if (recalcObjectApiName) {
|
|
465
|
+
const recalcObjectConfig =
|
|
466
|
+
await this.getObjectConfig(recalcObjectApiName);
|
|
467
|
+
if (recalcObjectConfig) {
|
|
468
|
+
recalcObjects[recalcObjectApiName] = recalcObjectConfig;
|
|
380
469
|
}
|
|
381
|
-
|
|
470
|
+
}
|
|
382
471
|
}
|
|
383
472
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
return configs;
|
|
473
|
+
for (const objectName in recalcObjects) {
|
|
474
|
+
if (Object.prototype.hasOwnProperty.call(recalcObjects, objectName)) {
|
|
475
|
+
const recalcObjectConfig = recalcObjects[objectName];
|
|
476
|
+
await this.addFormulaMetadata(
|
|
477
|
+
recalcObjectConfig,
|
|
478
|
+
recalcObjectConfig.datasource,
|
|
479
|
+
);
|
|
480
|
+
await Register.delete(
|
|
481
|
+
this.broker,
|
|
482
|
+
this.cacherKey(
|
|
483
|
+
getDynamicCalcMapCacherKey(objectConfig.name, objectName),
|
|
484
|
+
),
|
|
485
|
+
);
|
|
486
|
+
// await this.broker.call('metadata.delete', {key: this.cacherKey(getDynamicCalcMapCacherKey(objectConfig.name, objectName))}, {meta: {}})
|
|
487
|
+
}
|
|
400
488
|
}
|
|
489
|
+
}
|
|
401
490
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
491
|
+
async add(objectConfig) {
|
|
492
|
+
try {
|
|
493
|
+
await this.addFormulaMetadata(objectConfig, objectConfig.datasource);
|
|
494
|
+
return true;
|
|
495
|
+
} catch (error) {
|
|
496
|
+
this.broker.logger.error(error);
|
|
408
497
|
}
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
409
500
|
|
|
410
|
-
|
|
411
|
-
|
|
501
|
+
async filter(ctx) {
|
|
502
|
+
let { objectApiName, fieldApiName } = ctx.params;
|
|
503
|
+
if (!objectApiName) {
|
|
504
|
+
objectApiName = "*";
|
|
505
|
+
}
|
|
506
|
+
if (!fieldApiName) {
|
|
507
|
+
fieldApiName = "*";
|
|
508
|
+
}
|
|
509
|
+
const key = this.cacherKey(`${objectApiName}.${fieldApiName}`);
|
|
510
|
+
const configs = [];
|
|
511
|
+
// const res = await this.broker.call('metadata.filter', {key: key}, {meta: {}})
|
|
512
|
+
const res = await Register.filter(this.broker, key);
|
|
513
|
+
_.forEach(res, (item) => {
|
|
514
|
+
configs.push(item?.metadata);
|
|
515
|
+
});
|
|
516
|
+
return configs;
|
|
517
|
+
}
|
|
412
518
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
519
|
+
async get(ctx) {
|
|
520
|
+
let { fieldApiFullName } = ctx.params;
|
|
521
|
+
const key = this.cacherKey(fieldApiFullName);
|
|
522
|
+
// const res = await this.broker.call('metadata.get', {key: key}, {meta: {}});
|
|
523
|
+
const res = await Register.get(this.broker, key);
|
|
524
|
+
return res?.metadata;
|
|
525
|
+
}
|
|
419
526
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
key: fieldFormula._id,
|
|
423
|
-
value: `${quote.object_name}.${quote.field_name}`
|
|
424
|
-
}
|
|
425
|
-
this.checkRefMapData(maps, data);
|
|
426
|
-
}
|
|
527
|
+
async verifyObjectFieldFormulaConfig(ctx) {
|
|
528
|
+
let { fieldConfig, objectConfig } = ctx.params;
|
|
427
529
|
|
|
428
|
-
|
|
530
|
+
const fieldFormula = await this.getObjectFieldFormulaConfig(
|
|
531
|
+
fieldConfig,
|
|
532
|
+
objectConfig,
|
|
533
|
+
);
|
|
534
|
+
let { metadata } = (await this.getFormulaReferenceMaps(ctx.broker)) || [];
|
|
535
|
+
let maps = [];
|
|
536
|
+
if (metadata) {
|
|
537
|
+
maps = metadata;
|
|
429
538
|
}
|
|
430
539
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
540
|
+
for await (const quote of fieldFormula.quotes) {
|
|
541
|
+
let data = {
|
|
542
|
+
key: fieldFormula._id,
|
|
543
|
+
value: `${quote.object_name}.${quote.field_name}`,
|
|
544
|
+
};
|
|
545
|
+
this.checkRefMapData(maps, data);
|
|
434
546
|
}
|
|
547
|
+
|
|
548
|
+
return await this.getObjectFieldFormulaConfig(fieldConfig, objectConfig);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async getFormulaVarsAndQuotes(ctx) {
|
|
552
|
+
let { formula, objectConfig } = ctx.params;
|
|
553
|
+
return await this.computeFormulaVarsAndQuotes(formula, objectConfig);
|
|
554
|
+
}
|
|
435
555
|
}
|