oak-domain 5.1.13 → 5.1.14
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/store/CascadeStore.d.ts +1 -0
- package/lib/store/CascadeStore.js +168 -123
- package/lib/store/RelationAuth.js +37 -1
- package/lib/types/Entity.d.ts +0 -1
- package/lib/types/Expression.d.ts +1 -1
- package/lib/types/Expression.js +1 -4
- package/lib/utils/SimpleConnector.js +23 -22
- package/package.json +1 -1
|
@@ -69,6 +69,7 @@ export declare abstract class CascadeStore<ED extends EntityDict & BaseEntityDic
|
|
|
69
69
|
protected preProcessDataCreated<T extends keyof ED>(entity: T, data: ED[T]['Create']['data']): void;
|
|
70
70
|
protected preProcessDataUpdated(action: string, data: Record<string, any>, async?: true): void;
|
|
71
71
|
judgeRelation(entity: keyof ED, attr: string): string | 1 | 2 | string[] | 0 | -1;
|
|
72
|
+
private tryMergeModi;
|
|
72
73
|
/**
|
|
73
74
|
* 和具体的update过程无关的例程放在这里,包括对later动作的处理、对oper的记录以及对record的收集等
|
|
74
75
|
* @param entity
|
|
@@ -1051,6 +1051,118 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1051
1051
|
judgeRelation(entity, attr) {
|
|
1052
1052
|
return (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
|
|
1053
1053
|
}
|
|
1054
|
+
async tryMergeModi(entity, operation, context, ids, option) {
|
|
1055
|
+
const { action, data, filter, id: operId } = operation;
|
|
1056
|
+
const [upsertModi] = await this.selectAbjointRowAsync('modi', {
|
|
1057
|
+
data: {
|
|
1058
|
+
id: 1,
|
|
1059
|
+
data: 1,
|
|
1060
|
+
action: 1,
|
|
1061
|
+
},
|
|
1062
|
+
filter: {
|
|
1063
|
+
targetEntity: entity,
|
|
1064
|
+
iState: 'active',
|
|
1065
|
+
filter: ids.length > 0 ? {
|
|
1066
|
+
id: {
|
|
1067
|
+
$in: ids,
|
|
1068
|
+
},
|
|
1069
|
+
} : {
|
|
1070
|
+
id: ids[0],
|
|
1071
|
+
},
|
|
1072
|
+
},
|
|
1073
|
+
sorter: [
|
|
1074
|
+
{
|
|
1075
|
+
$attr: {
|
|
1076
|
+
$$createAt$$: 1,
|
|
1077
|
+
},
|
|
1078
|
+
$direction: 'desc',
|
|
1079
|
+
}
|
|
1080
|
+
],
|
|
1081
|
+
indexFrom: 0,
|
|
1082
|
+
count: 1,
|
|
1083
|
+
}, context, option);
|
|
1084
|
+
if (upsertModi) {
|
|
1085
|
+
const { data: data2, id: id2, action: action2 } = upsertModi;
|
|
1086
|
+
if (action === 'remove') {
|
|
1087
|
+
// 之前的都不做数
|
|
1088
|
+
if (action2 === 'create') {
|
|
1089
|
+
// 对冲掉
|
|
1090
|
+
return {
|
|
1091
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1092
|
+
action: 'remove',
|
|
1093
|
+
data: {},
|
|
1094
|
+
filter: {
|
|
1095
|
+
id: upsertModi.id,
|
|
1096
|
+
},
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
// 直接把这个改成删除
|
|
1101
|
+
(0, assert_1.default)(action2 !== 'remove');
|
|
1102
|
+
return {
|
|
1103
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1104
|
+
action: 'update',
|
|
1105
|
+
data: {
|
|
1106
|
+
action,
|
|
1107
|
+
data,
|
|
1108
|
+
},
|
|
1109
|
+
filter: {
|
|
1110
|
+
id: upsertModi.id,
|
|
1111
|
+
},
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
// 是update,直接把原来的data覆盖掉
|
|
1117
|
+
if (action !== action2 && process.env.NODE_ENV === 'development') {
|
|
1118
|
+
// 这种情况感觉是不会发生的
|
|
1119
|
+
console.warn('发生了同一行数据的modi的action不一致,请注意查看');
|
|
1120
|
+
}
|
|
1121
|
+
return {
|
|
1122
|
+
id: 'dummy',
|
|
1123
|
+
action: 'update',
|
|
1124
|
+
data: {
|
|
1125
|
+
data: Object.assign({}, data2, data),
|
|
1126
|
+
},
|
|
1127
|
+
filter: {
|
|
1128
|
+
id: id2,
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
// 说明没有已有的modi,必须要创建新的,此时应当在option中有相应的parentEntity/Id
|
|
1134
|
+
const { modiParentEntity, modiParentId } = option;
|
|
1135
|
+
(0, assert_1.default)(modiParentEntity && modiParentId);
|
|
1136
|
+
return {
|
|
1137
|
+
id: 'dummy',
|
|
1138
|
+
action: 'create',
|
|
1139
|
+
data: {
|
|
1140
|
+
id: operId,
|
|
1141
|
+
targetEntity: entity,
|
|
1142
|
+
entity: modiParentEntity,
|
|
1143
|
+
entityId: modiParentId,
|
|
1144
|
+
action,
|
|
1145
|
+
data,
|
|
1146
|
+
iState: 'active',
|
|
1147
|
+
filter: ids.length > 0 ? {
|
|
1148
|
+
id: {
|
|
1149
|
+
$in: ids,
|
|
1150
|
+
},
|
|
1151
|
+
} : {
|
|
1152
|
+
id: ids[0],
|
|
1153
|
+
},
|
|
1154
|
+
modiEntity$modi: {
|
|
1155
|
+
id: 'dummy',
|
|
1156
|
+
action: 'create',
|
|
1157
|
+
data: await Promise.all(ids.map(async (id) => ({
|
|
1158
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1159
|
+
entity: entity,
|
|
1160
|
+
entityId: id,
|
|
1161
|
+
}))),
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1054
1166
|
/**
|
|
1055
1167
|
* 和具体的update过程无关的例程放在这里,包括对later动作的处理、对oper的记录以及对record的收集等
|
|
1056
1168
|
* @param entity
|
|
@@ -1282,100 +1394,60 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1282
1394
|
if (data) {
|
|
1283
1395
|
this.preProcessDataUpdated(action, data, true);
|
|
1284
1396
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
// 变成对modi的插入
|
|
1288
|
-
// 优化,这里如果是对同一个targetEntity反复update,则变成对最后一条create/update的modi进行update,以避免发布文章这样的需求时产生过多的modi
|
|
1289
|
-
let modiUpsert;
|
|
1290
|
-
if (action === 'update') {
|
|
1291
|
-
// 如果action本身是update,且没有实际update属性,这里可以忽略
|
|
1292
|
-
const updateAttrCount = Object.keys(data).length;
|
|
1293
|
-
if (updateAttrCount === 0) {
|
|
1294
|
-
return {};
|
|
1295
|
-
}
|
|
1296
|
-
// 尝试和当前targetEntity的最后一条create/update进行合并,优化modi的条数
|
|
1297
|
-
const upsertModis = await this.selectAbjointRowAsync('modi', {
|
|
1298
|
-
data: {
|
|
1299
|
-
id: 1,
|
|
1300
|
-
data: 1,
|
|
1301
|
-
action: 1,
|
|
1302
|
-
},
|
|
1303
|
-
filter: {
|
|
1304
|
-
targetEntity: entity,
|
|
1305
|
-
entity: option.modiParentEntity,
|
|
1306
|
-
entityId: option.modiParentId,
|
|
1307
|
-
iState: 'active',
|
|
1308
|
-
filter: ids.length > 0 ? {
|
|
1309
|
-
id: {
|
|
1310
|
-
$in: ids,
|
|
1311
|
-
},
|
|
1312
|
-
} : filter,
|
|
1313
|
-
},
|
|
1314
|
-
sorter: [
|
|
1315
|
-
{
|
|
1316
|
-
$attr: {
|
|
1317
|
-
$$createAt$$: 1,
|
|
1318
|
-
},
|
|
1319
|
-
$direction: 'desc',
|
|
1320
|
-
}
|
|
1321
|
-
],
|
|
1322
|
-
indexFrom: 0,
|
|
1323
|
-
count: 1,
|
|
1324
|
-
}, context, option);
|
|
1325
|
-
if (upsertModis.length > 0) {
|
|
1326
|
-
const { data: originData, id: originId, action } = upsertModis[0];
|
|
1327
|
-
if (['create', 'update'].includes(action)) {
|
|
1328
|
-
modiUpsert = {
|
|
1329
|
-
id: 'dummy',
|
|
1330
|
-
action: 'update',
|
|
1331
|
-
data: {
|
|
1332
|
-
data: Object.assign({}, originData, data),
|
|
1333
|
-
},
|
|
1334
|
-
filter: {
|
|
1335
|
-
id: originId,
|
|
1336
|
-
}
|
|
1337
|
-
};
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
if (!modiUpsert) {
|
|
1342
|
-
modiUpsert = {
|
|
1343
|
-
id: 'dummy',
|
|
1344
|
-
action: 'create',
|
|
1345
|
-
data: {
|
|
1346
|
-
id: operId,
|
|
1347
|
-
targetEntity: entity,
|
|
1348
|
-
entity: option.modiParentEntity,
|
|
1349
|
-
entityId: option.modiParentId,
|
|
1350
|
-
action,
|
|
1351
|
-
data,
|
|
1352
|
-
iState: 'active',
|
|
1353
|
-
filter,
|
|
1354
|
-
},
|
|
1355
|
-
};
|
|
1356
|
-
if (ids.length > 0) {
|
|
1357
|
-
modiUpsert.data.modiEntity$modi = {
|
|
1358
|
-
id: 'dummy',
|
|
1359
|
-
action: 'create',
|
|
1360
|
-
data: await Promise.all(ids.map(async (id) => ({
|
|
1361
|
-
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1362
|
-
entity: entity,
|
|
1363
|
-
entityId: id,
|
|
1364
|
-
}))),
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1397
|
+
const createModi = async () => {
|
|
1398
|
+
const modiOperation = await this.tryMergeModi(entity, operation, context, ids, option);
|
|
1368
1399
|
const closeRootMode = context.openRootMode();
|
|
1369
|
-
await this.cascadeUpdateAsync('modi',
|
|
1400
|
+
await this.cascadeUpdateAsync('modi', modiOperation, context, option);
|
|
1370
1401
|
closeRootMode();
|
|
1371
1402
|
return {
|
|
1372
1403
|
modi: {
|
|
1373
1404
|
['create']: 1,
|
|
1374
1405
|
},
|
|
1375
1406
|
};
|
|
1407
|
+
};
|
|
1408
|
+
if (option.modiParentEntity && !['modi', 'modiEntity'].includes(entity)) {
|
|
1409
|
+
// 延时更新,变成对modi的操作
|
|
1410
|
+
if (action === 'update' && Object.keys(data).length === 0) {
|
|
1411
|
+
return {};
|
|
1412
|
+
}
|
|
1413
|
+
return createModi();
|
|
1376
1414
|
}
|
|
1377
1415
|
else {
|
|
1378
|
-
const
|
|
1416
|
+
const saveRecordAndCreateOper = async () => {
|
|
1417
|
+
if (action === 'remove') {
|
|
1418
|
+
if (!option.dontCollect) {
|
|
1419
|
+
context.saveOpRecord(entity, {
|
|
1420
|
+
id: operId,
|
|
1421
|
+
action,
|
|
1422
|
+
data: data,
|
|
1423
|
+
filter: {
|
|
1424
|
+
id: {
|
|
1425
|
+
$in: ids,
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
else {
|
|
1432
|
+
const updateAttrCount = Object.keys(data).length;
|
|
1433
|
+
if (updateAttrCount > 0) {
|
|
1434
|
+
if (!option.dontCollect) {
|
|
1435
|
+
context.saveOpRecord(entity, {
|
|
1436
|
+
id: operId,
|
|
1437
|
+
action,
|
|
1438
|
+
data: data,
|
|
1439
|
+
filter: {
|
|
1440
|
+
id: {
|
|
1441
|
+
$in: ids,
|
|
1442
|
+
}
|
|
1443
|
+
},
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
else {
|
|
1448
|
+
return {};
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1379
1451
|
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi', 'log'].includes(entity) && ids.length > 0) {
|
|
1380
1452
|
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1381
1453
|
(0, assert_1.default)(operId);
|
|
@@ -1412,48 +1484,21 @@ class CascadeStore extends RowStore_1.RowStore {
|
|
|
1412
1484
|
});
|
|
1413
1485
|
closeRootMode();
|
|
1414
1486
|
}
|
|
1487
|
+
return {
|
|
1488
|
+
[entity]: {
|
|
1489
|
+
[action]: ids.length,
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1415
1492
|
};
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
id: operId,
|
|
1420
|
-
action,
|
|
1421
|
-
data: data,
|
|
1422
|
-
filter: {
|
|
1423
|
-
id: {
|
|
1424
|
-
$in: ids,
|
|
1425
|
-
}
|
|
1426
|
-
},
|
|
1427
|
-
});
|
|
1428
|
-
}
|
|
1493
|
+
const count = await this.updateAbjointRowAsync(entity, operation, context, option);
|
|
1494
|
+
if (count === ids.length) {
|
|
1495
|
+
return await saveRecordAndCreateOper();
|
|
1429
1496
|
}
|
|
1430
1497
|
else {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
context.saveOpRecord(entity, {
|
|
1435
|
-
id: operId,
|
|
1436
|
-
action,
|
|
1437
|
-
data: data,
|
|
1438
|
-
filter: {
|
|
1439
|
-
id: {
|
|
1440
|
-
$in: ids,
|
|
1441
|
-
}
|
|
1442
|
-
},
|
|
1443
|
-
});
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
else {
|
|
1447
|
-
return {};
|
|
1448
|
-
}
|
|
1498
|
+
// 如果没有更新到行,说明这些数据还在modi当中
|
|
1499
|
+
(0, assert_1.default)(count === 0, 'update成功的行数只能为id所在行数或者0');
|
|
1500
|
+
return await createModi();
|
|
1449
1501
|
}
|
|
1450
|
-
await this.updateAbjointRowAsync(entity, operation, context, option);
|
|
1451
|
-
await createOper();
|
|
1452
|
-
return {
|
|
1453
|
-
[entity]: {
|
|
1454
|
-
[action]: ids.length,
|
|
1455
|
-
}
|
|
1456
|
-
};
|
|
1457
1502
|
}
|
|
1458
1503
|
}
|
|
1459
1504
|
}
|
|
@@ -352,12 +352,47 @@ class RelationAuth {
|
|
|
352
352
|
const dealWithData = (rows) => {
|
|
353
353
|
// 这里如果entity指向不同的实体,一般出现这样的查询,则其权限应当不由这条deduce路径处理
|
|
354
354
|
// 同上,如果找到的行数大于1行,说明deduce路径上的对象不确定,也暂不处理 by Xc 20230725
|
|
355
|
-
if (rows.length > 1
|
|
355
|
+
if (rows.length > 1) {
|
|
356
356
|
if (process.env.NODE_ENV === 'development') {
|
|
357
357
|
console.warn(`进行deduce推导时找到了${rows.length}行${entity}数据`);
|
|
358
358
|
}
|
|
359
359
|
return entityFilters;
|
|
360
360
|
}
|
|
361
|
+
else if (rows.length === 0) {
|
|
362
|
+
// 说明没有找到行,这时候有一种可能是modi。这时候只能假设是指定id更新了,其它情况很难处理。by Xc
|
|
363
|
+
if (filter.id) {
|
|
364
|
+
// 用modi对应的entity/entityId来判定
|
|
365
|
+
const modies = context.select('modi', {
|
|
366
|
+
data: {
|
|
367
|
+
id: 1,
|
|
368
|
+
data: 1,
|
|
369
|
+
entity: 1,
|
|
370
|
+
entityId: 1,
|
|
371
|
+
},
|
|
372
|
+
filter: {
|
|
373
|
+
filter,
|
|
374
|
+
}
|
|
375
|
+
}, {});
|
|
376
|
+
const getModiEntity = (modies) => {
|
|
377
|
+
if (modies[0]) {
|
|
378
|
+
const { entity, entityId } = modies[0];
|
|
379
|
+
entityFilters.push({
|
|
380
|
+
entity: entity,
|
|
381
|
+
filter: {
|
|
382
|
+
id: entityId,
|
|
383
|
+
},
|
|
384
|
+
actions: ['update'],
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return entityFilters;
|
|
388
|
+
};
|
|
389
|
+
if (modies instanceof Promise) {
|
|
390
|
+
return modies.then((modies2) => getModiEntity(modies2));
|
|
391
|
+
}
|
|
392
|
+
return getModiEntity(modies);
|
|
393
|
+
}
|
|
394
|
+
return entityFilters;
|
|
395
|
+
}
|
|
361
396
|
const { entity: deducedEntity, entityId: deducedEntityId } = rows[0];
|
|
362
397
|
if (!deducedEntity || !deducedEntityId) {
|
|
363
398
|
// 这种情况会出现在前台缓存里
|
|
@@ -801,6 +836,7 @@ class RelationAuth {
|
|
|
801
836
|
*/
|
|
802
837
|
findActionAuthsOnNode(node, context) {
|
|
803
838
|
const { entity, filter, action, userRelations } = node;
|
|
839
|
+
(0, assert_1.default)(filter);
|
|
804
840
|
const deducedEntityFilters2 = this.getDeducedEntityFilters(entity, filter, [action], context);
|
|
805
841
|
/**
|
|
806
842
|
* 搜索判定是否允许自建对象,自建的条件是 path = '',destEntity === entity
|
package/lib/types/Entity.d.ts
CHANGED
|
@@ -25,7 +25,6 @@ type FilterPart<A extends string, F extends Object | undefined> = {
|
|
|
25
25
|
export type SelectOption = {
|
|
26
26
|
dontCollect?: boolean;
|
|
27
27
|
blockTrigger?: true;
|
|
28
|
-
obscure?: boolean;
|
|
29
28
|
forUpdate?: true | 'skip locked' | 'nowait';
|
|
30
29
|
includedDeleted?: true;
|
|
31
30
|
ignoreAttrMiss?: true;
|
|
@@ -150,7 +150,7 @@ export declare function isStringExpression<A>(expression: any): expression is St
|
|
|
150
150
|
export declare function isAggrExpression<A>(expression: any): expression is AggrExpression<A>;
|
|
151
151
|
export declare function isExpression<A>(expression: any): expression is Expression<A>;
|
|
152
152
|
export declare function opMultipleParams(op: string): boolean;
|
|
153
|
-
export declare function execOp(op: string, params: any
|
|
153
|
+
export declare function execOp(op: string, params: any): ExpressionConstant;
|
|
154
154
|
/**
|
|
155
155
|
* 检查一个表达式,并分析其涉及到的属性
|
|
156
156
|
* @param expression
|
package/lib/types/Expression.js
CHANGED
|
@@ -136,10 +136,7 @@ function opMultipleParams(op) {
|
|
|
136
136
|
'$round', '$floor', '$ceil', '$$max', '$$min', '$$sum', '$$avg', '$$count'].includes(op);
|
|
137
137
|
}
|
|
138
138
|
exports.opMultipleParams = opMultipleParams;
|
|
139
|
-
function execOp(op, params
|
|
140
|
-
if (obscure && (params === undefined || params.includes(undefined))) {
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
139
|
+
function execOp(op, params) {
|
|
143
140
|
switch (op) {
|
|
144
141
|
case '$gt': {
|
|
145
142
|
return params[0] > params[1];
|
|
@@ -246,28 +246,29 @@ class SimpleConnector {
|
|
|
246
246
|
return {};
|
|
247
247
|
}
|
|
248
248
|
async fetchWithTimeout(url, options, timeout = 5000) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
249
|
+
return global.fetch(url, options);
|
|
250
|
+
// if (typeof AbortController === 'undefined' || timeout === 0) {
|
|
251
|
+
// return global.fetch(url, options);
|
|
252
|
+
// }
|
|
253
|
+
// const controller = new AbortController();
|
|
254
|
+
// const signal = controller.signal;
|
|
255
|
+
// // 设置超时
|
|
256
|
+
// const timeoutId = setTimeout(() => {
|
|
257
|
+
// controller.abort();
|
|
258
|
+
// }, timeout);
|
|
259
|
+
// // 发起 fetch 请求并传递 signal
|
|
260
|
+
// return global.fetch(url, Object.assign({}, options, { signal }))
|
|
261
|
+
// .then(response => {
|
|
262
|
+
// clearTimeout(timeoutId); // 如果请求成功,清除超时
|
|
263
|
+
// return response;
|
|
264
|
+
// })
|
|
265
|
+
// .catch(error => {
|
|
266
|
+
// clearTimeout(timeoutId); // 如果请求失败,清除超时
|
|
267
|
+
// if (error.name === 'AbortError') {
|
|
268
|
+
// throw new OakRequestTimeoutException();
|
|
269
|
+
// }
|
|
270
|
+
// throw error; // 其他错误
|
|
271
|
+
// });
|
|
271
272
|
}
|
|
272
273
|
}
|
|
273
274
|
exports.default = SimpleConnector;
|