oak-domain 2.6.6 → 2.6.8
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/base-app-domain/Modi/Schema.d.ts +0 -11
- package/lib/base-app-domain/Modi/Storage.js +5 -0
- package/lib/base-app-domain/ModiEntity/Storage.js +3 -0
- package/lib/base-app-domain/Oper/Schema.d.ts +6 -0
- package/lib/base-app-domain/Oper/Storage.js +9 -0
- package/lib/base-app-domain/OperEntity/Schema.d.ts +4 -31
- package/lib/base-app-domain/OperEntity/Storage.js +4 -1
- package/lib/base-app-domain/User/Schema.d.ts +11 -11
- package/lib/base-app-domain/UserEntityGrant/Schema.d.ts +11 -11
- package/lib/base-app-domain/UserEntityGrant/Storage.js +3 -0
- package/lib/checkers/index.js +1 -0
- package/lib/compiler/env.d.ts +1 -0
- package/lib/compiler/env.js +2 -1
- package/lib/compiler/schemalBuilder.js +33 -16
- package/lib/entities/Oper.d.ts +1 -0
- package/lib/entities/Oper.js +1 -0
- package/lib/store/AsyncRowStore.d.ts +5 -1
- package/lib/store/CascadeStore.d.ts +7 -1
- package/lib/store/CascadeStore.js +387 -131
- package/lib/store/SyncRowStore.d.ts +1 -1
- package/lib/store/TriggerExecutor.js +1 -1
- package/lib/store/checker.d.ts +2 -1
- package/lib/store/checker.js +158 -5
- package/lib/store/modi.js +2 -2
- package/lib/store/relation.js +1 -0
- package/lib/timers/oper.d.ts +18 -0
- package/lib/timers/oper.js +60 -0
- package/lib/timers/vaccum.d.ts +20 -0
- package/lib/timers/vaccum.js +176 -0
- package/lib/types/Connector.d.ts +2 -2
- package/lib/types/Entity.d.ts +2 -1
- package/lib/types/Exception.d.ts +5 -2
- package/lib/types/Exception.js +15 -1
- package/lib/types/RowStore.d.ts +4 -0
- package/lib/utils/SimpleConnector.d.ts +2 -2
- package/lib/utils/SimpleConnector.js +24 -14
- package/lib/utils/validator.js +1 -1
- package/package.json +1 -1
- package/src/entities/Oper.ts +2 -0
- package/lib/store/selection.d.ts +0 -19
- package/lib/store/selection.js +0 -265
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EntityDict, RowStore, OperateOption, OperationResult, SelectOption, TxnOption, Context, AggregationResult } from "../types";
|
|
2
2
|
export declare abstract class SyncContext<ED extends EntityDict> implements Context {
|
|
3
|
-
|
|
3
|
+
rowStore: SyncRowStore<ED, this>;
|
|
4
4
|
private uuid?;
|
|
5
5
|
constructor(rowStore: SyncRowStore<ED, SyncContext<ED>>);
|
|
6
6
|
abstract getCurrentUserId(allowUnloggedIn?: boolean): string | undefined;
|
|
@@ -33,7 +33,7 @@ var TriggerExecutor = /** @class */ (function () {
|
|
|
33
33
|
TriggerExecutor.prototype.registerChecker = function (checker) {
|
|
34
34
|
var entity = checker.entity, action = checker.action, type = checker.type, conditionalFilter = checker.conditionalFilter;
|
|
35
35
|
var triggerName = "".concat(String(entity)).concat(action, "\u6743\u9650\u68C0\u67E5-").concat(this.counter++);
|
|
36
|
-
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker
|
|
36
|
+
var _a = (0, checker_1.translateCheckerInAsyncContext)(checker), fn = _a.fn, when = _a.when;
|
|
37
37
|
var priority = type === 'data' ? Trigger_1.DATA_CHECKER_DEFAULT_PRIORITY : Trigger_1.CHECKER_DEFAULT_PRIORITY; // checker的默认优先级最低(前面的trigger可能会赋上一些相应的值)
|
|
38
38
|
var trigger = {
|
|
39
39
|
checkerType: type,
|
package/lib/store/checker.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { SyncContext } from './SyncRowStore';
|
|
|
8
8
|
* @param silent 如果silent,则row和relation类型的checker只会把限制条件加到查询上,而不报错(除掉create动作)
|
|
9
9
|
* @returns
|
|
10
10
|
*/
|
|
11
|
-
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt
|
|
11
|
+
export declare function translateCheckerInAsyncContext<ED extends EntityDict & BaseEntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>>(checker: Checker<ED, T, Cxt>): {
|
|
12
12
|
fn: Trigger<ED, T, Cxt>['fn'];
|
|
13
13
|
when: 'before' | 'after';
|
|
14
14
|
};
|
|
@@ -30,3 +30,4 @@ export declare function createAuthCheckers<ED extends EntityDict & BaseEntityDic
|
|
|
30
30
|
* 如果有的对象允许删除,需要使用trigger来处理其相关联的外键对象,这些trigger写作before,则会在checker之前执行,仍然可以删除成功
|
|
31
31
|
*/
|
|
32
32
|
export declare function createRemoveCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>, authDict?: AuthDefDict<ED>): Checker<ED, keyof ED, Cxt>[];
|
|
33
|
+
export declare function createCreateCheckers<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED> | SyncContext<ED>>(schema: StorageSchema<ED>): Checker<ED, keyof ED, Cxt>[];
|
package/lib/store/checker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createRemoveCheckers = exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
|
3
|
+
exports.createCreateCheckers = exports.createRemoveCheckers = exports.createAuthCheckers = exports.translateCheckerInSyncContext = exports.translateCheckerInAsyncContext = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
6
|
var filter_1 = require("../store/filter");
|
|
@@ -11,13 +11,14 @@ var string_1 = require("../utils/string");
|
|
|
11
11
|
var lodash_1 = require("../utils/lodash");
|
|
12
12
|
var relation_1 = require("./relation");
|
|
13
13
|
var uuid_1 = require("../utils/uuid");
|
|
14
|
+
var action_1 = require("../actions/action");
|
|
14
15
|
/**
|
|
15
16
|
*
|
|
16
17
|
* @param checker 要翻译的checker
|
|
17
18
|
* @param silent 如果silent,则row和relation类型的checker只会把限制条件加到查询上,而不报错(除掉create动作)
|
|
18
19
|
* @returns
|
|
19
20
|
*/
|
|
20
|
-
function translateCheckerInAsyncContext(checker
|
|
21
|
+
function translateCheckerInAsyncContext(checker) {
|
|
21
22
|
var _this = this;
|
|
22
23
|
var entity = checker.entity, type = checker.type;
|
|
23
24
|
var when = 'before'; // 现在create的relation改成提前的expression检查了,原先是先插入再后检查,性能不行,而且select也需要实现前检查
|
|
@@ -65,7 +66,7 @@ function translateCheckerInAsyncContext(checker, silent) {
|
|
|
65
66
|
_c.label = 3;
|
|
66
67
|
case 3:
|
|
67
68
|
filter2 = _b;
|
|
68
|
-
if (!
|
|
69
|
+
if (!['select', 'count', 'stat'].includes(action)) return [3 /*break*/, 4];
|
|
69
70
|
operation.filter = (0, filter_1.addFilterSegment)(operationFilter || {}, filter2);
|
|
70
71
|
return [2 /*return*/, 0];
|
|
71
72
|
case 4: return [4 /*yield*/, (0, filter_1.checkFilterContains)(entity, context, filter2, operationFilter || {}, true)];
|
|
@@ -135,7 +136,7 @@ function translateCheckerInAsyncContext(checker, silent) {
|
|
|
135
136
|
console.warn("".concat(entity, "\u5BF9\u8C61\u7684create\u7C7B\u578B\u7684checker\u4E2D\uFF0C\u5B58\u5728\u65E0\u6CD5\u8F6C\u6362\u4E3A\u8868\u8FBE\u5F0F\u5F62\u5F0F\u7684\u60C5\u51B5\uFF0C\u8BF7\u5C3D\u91CF\u4F7F\u7528authDef\u683C\u5F0F\u5B9A\u4E49\u8FD9\u7C7Bchecker"));
|
|
136
137
|
return [2 /*return*/, 0];
|
|
137
138
|
}
|
|
138
|
-
if (
|
|
139
|
+
if (['select', 'count', 'stat'].includes(action)) {
|
|
139
140
|
operation.filter = (0, filter_1.addFilterSegment)(filter || {}, result);
|
|
140
141
|
return [2 /*return*/, 0];
|
|
141
142
|
}
|
|
@@ -409,7 +410,7 @@ function translateCascadeRelationFilterMaker(schema, lch, entity2, pathPrefix) {
|
|
|
409
410
|
counters.push.apply(counters, tslib_1.__spreadArray([], tslib_1.__read(ca2), false));
|
|
410
411
|
}
|
|
411
412
|
if (filter.$or) {
|
|
412
|
-
var countersOr = filter.$
|
|
413
|
+
var countersOr = filter.$or.map(function (ele) { return translateCreateFilterMaker(entity, ele, userId); });
|
|
413
414
|
// or也只要有一个满足就行(不能否定)
|
|
414
415
|
var co2 = countersOr.filter(function (ele) { return !(ele instanceof Exception_1.OakUserUnpermittedException); });
|
|
415
416
|
counters.push.apply(counters, tslib_1.__spreadArray([], tslib_1.__read(co2), false));
|
|
@@ -1216,3 +1217,155 @@ function createRemoveCheckers(schema, authDict) {
|
|
|
1216
1217
|
return checkers;
|
|
1217
1218
|
}
|
|
1218
1219
|
exports.createRemoveCheckers = createRemoveCheckers;
|
|
1220
|
+
function checkAttributeLegal(schema, entity, data) {
|
|
1221
|
+
var _a;
|
|
1222
|
+
var attributes = schema[entity].attributes;
|
|
1223
|
+
for (var attr in data) {
|
|
1224
|
+
if (attributes[attr]) {
|
|
1225
|
+
var _b = attributes[attr], type = _b.type, params = _b.params, defaultValue = _b.default, enumeration = _b.enumeration, notNull = _b.notNull;
|
|
1226
|
+
if (data[attr] === null || data[attr] === undefined) {
|
|
1227
|
+
if (notNull && defaultValue === undefined) {
|
|
1228
|
+
throw new Exception_1.OakAttrNotNullException(entity, [attr]);
|
|
1229
|
+
}
|
|
1230
|
+
if (defaultValue !== undefined) {
|
|
1231
|
+
Object.assign(data, (_a = {},
|
|
1232
|
+
_a[attr] = defaultValue,
|
|
1233
|
+
_a));
|
|
1234
|
+
}
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
switch (type) {
|
|
1238
|
+
case 'char':
|
|
1239
|
+
case 'varchar': {
|
|
1240
|
+
if (typeof data[attr] !== 'string') {
|
|
1241
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a string');
|
|
1242
|
+
}
|
|
1243
|
+
var length_1 = params.length;
|
|
1244
|
+
if (length_1 && data[attr].length > length_1) {
|
|
1245
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too long');
|
|
1246
|
+
}
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
case 'int':
|
|
1250
|
+
case 'smallint':
|
|
1251
|
+
case 'tinyint':
|
|
1252
|
+
case 'bigint':
|
|
1253
|
+
case 'decimal':
|
|
1254
|
+
case 'money': {
|
|
1255
|
+
if (typeof data[attr] !== 'number') {
|
|
1256
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not a number');
|
|
1257
|
+
}
|
|
1258
|
+
var _c = params || {}, min = _c.min, max = _c.max;
|
|
1259
|
+
if (typeof min === 'number' && data[attr] < min) {
|
|
1260
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too small');
|
|
1261
|
+
}
|
|
1262
|
+
if (typeof max === 'number' && data[attr] > max) {
|
|
1263
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'too big');
|
|
1264
|
+
}
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
case 'enum': {
|
|
1268
|
+
(0, assert_1.default)(enumeration);
|
|
1269
|
+
if (!enumeration.includes(data[attr])) {
|
|
1270
|
+
throw new Exception_1.OakInputIllegalException(entity, [attr], 'not in enumberation');
|
|
1271
|
+
}
|
|
1272
|
+
break;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
// 这里似乎还有一种update中带cascade remove的case,等遇到再说(貌似cascadeUpdate没有处理完整这种情况) by Xc
|
|
1278
|
+
if (typeof data[attr] === 'object' && data[attr].action === 'remove') {
|
|
1279
|
+
console.warn('cascade remove可能是未处理的边界,请注意');
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
function createCreateCheckers(schema) {
|
|
1285
|
+
var checkers = [];
|
|
1286
|
+
var _loop_9 = function (entity) {
|
|
1287
|
+
var _a = schema[entity], attributes = _a.attributes, actions = _a.actions;
|
|
1288
|
+
var notNullAttrs = Object.keys(attributes).filter(function (ele) { return attributes[ele].notNull; });
|
|
1289
|
+
var updateActions = (0, lodash_1.difference)(actions, action_1.excludeUpdateActions);
|
|
1290
|
+
checkers.push({
|
|
1291
|
+
entity: entity,
|
|
1292
|
+
type: 'data',
|
|
1293
|
+
action: 'create',
|
|
1294
|
+
checker: function (data) {
|
|
1295
|
+
var checkData = function (data2) {
|
|
1296
|
+
var e_9, _a, e_10, _b;
|
|
1297
|
+
var illegalNullAttrs = (0, lodash_1.difference)(notNullAttrs, Object.keys(data2));
|
|
1298
|
+
if (illegalNullAttrs.length > 0) {
|
|
1299
|
+
try {
|
|
1300
|
+
// 要处理多对一的cascade create
|
|
1301
|
+
for (var illegalNullAttrs_1 = (e_9 = void 0, tslib_1.__values(illegalNullAttrs)), illegalNullAttrs_1_1 = illegalNullAttrs_1.next(); !illegalNullAttrs_1_1.done; illegalNullAttrs_1_1 = illegalNullAttrs_1.next()) {
|
|
1302
|
+
var attr = illegalNullAttrs_1_1.value;
|
|
1303
|
+
if (attr === 'entityId') {
|
|
1304
|
+
if (illegalNullAttrs.includes('entity')) {
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
else if (attr === 'entity' && attributes[attr].type === 'ref') {
|
|
1309
|
+
var hasCascadeCreate = false;
|
|
1310
|
+
try {
|
|
1311
|
+
for (var _c = (e_10 = void 0, tslib_1.__values(attributes[attr].ref)), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
1312
|
+
var ref = _d.value;
|
|
1313
|
+
if (data2[ref] && data2[ref].action === 'create') {
|
|
1314
|
+
hasCascadeCreate = true;
|
|
1315
|
+
break;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
catch (e_10_1) { e_10 = { error: e_10_1 }; }
|
|
1320
|
+
finally {
|
|
1321
|
+
try {
|
|
1322
|
+
if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
|
|
1323
|
+
}
|
|
1324
|
+
finally { if (e_10) throw e_10.error; }
|
|
1325
|
+
}
|
|
1326
|
+
if (hasCascadeCreate) {
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
else if (attributes[attr].type === 'ref') {
|
|
1331
|
+
var ref = attributes[attr].ref;
|
|
1332
|
+
if (data2[ref] && data2[ref].action === 'create') {
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
// 到这里说明确实是有not null的属性没有赋值
|
|
1337
|
+
throw new Exception_1.OakAttrNotNullException(entity, illegalNullAttrs);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
catch (e_9_1) { e_9 = { error: e_9_1 }; }
|
|
1341
|
+
finally {
|
|
1342
|
+
try {
|
|
1343
|
+
if (illegalNullAttrs_1_1 && !illegalNullAttrs_1_1.done && (_a = illegalNullAttrs_1.return)) _a.call(illegalNullAttrs_1);
|
|
1344
|
+
}
|
|
1345
|
+
finally { if (e_9) throw e_9.error; }
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
checkAttributeLegal(schema, entity, data2);
|
|
1349
|
+
};
|
|
1350
|
+
if (data instanceof Array) {
|
|
1351
|
+
data.forEach(function (ele) { return checkData(ele); });
|
|
1352
|
+
}
|
|
1353
|
+
else {
|
|
1354
|
+
checkData(data);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}, {
|
|
1358
|
+
entity: entity,
|
|
1359
|
+
type: 'data',
|
|
1360
|
+
action: updateActions,
|
|
1361
|
+
checker: function (data) {
|
|
1362
|
+
checkAttributeLegal(schema, entity, data);
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
};
|
|
1366
|
+
for (var entity in schema) {
|
|
1367
|
+
_loop_9(entity);
|
|
1368
|
+
}
|
|
1369
|
+
return checkers;
|
|
1370
|
+
}
|
|
1371
|
+
exports.createCreateCheckers = createCreateCheckers;
|
package/lib/store/modi.js
CHANGED
|
@@ -151,8 +151,8 @@ function createModiRelatedTriggers(schema) {
|
|
|
151
151
|
var _this = this;
|
|
152
152
|
var triggers = [];
|
|
153
153
|
var _loop_2 = function (entity) {
|
|
154
|
-
var
|
|
155
|
-
if (
|
|
154
|
+
var toModi = schema[entity].toModi;
|
|
155
|
+
if (toModi) {
|
|
156
156
|
// 当关联modi的对象被删除时,对应的modi也删除。这里似乎只需要删除掉活跃对象?因为oper不能删除,所以oper和modi是必须要支持对deleted对象的容错?
|
|
157
157
|
// 这里没有想清楚,by Xc 20230209
|
|
158
158
|
triggers.push({
|
package/lib/store/relation.js
CHANGED
|
@@ -55,6 +55,7 @@ function judgeRelation(schema, entity, attr) {
|
|
|
55
55
|
else if (attributes.hasOwnProperty('entity')
|
|
56
56
|
&& attributes.hasOwnProperty('entityId')
|
|
57
57
|
&& schema.hasOwnProperty(attr)) {
|
|
58
|
+
(0, assert_1.default)(attributes.entity.ref.includes(attr), '不应当出现的case');
|
|
58
59
|
// 反向指针的外键
|
|
59
60
|
return 2;
|
|
60
61
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EntityDict } from '../types/Entity';
|
|
2
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
3
|
+
import { AsyncContext } from '../store/AsyncRowStore';
|
|
4
|
+
export declare type VaccumOperOption<ED extends EntityDict & BaseEntityDict> = {
|
|
5
|
+
aliveLine: number;
|
|
6
|
+
excludeOpers?: {
|
|
7
|
+
[T in keyof ED]?: ED[T]['Action'][];
|
|
8
|
+
};
|
|
9
|
+
backupDir?: string;
|
|
10
|
+
zip?: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 将一定日期之前的oper对象清空
|
|
14
|
+
* @param option
|
|
15
|
+
* @param context
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export declare function vaccumOper<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(option: VaccumOperOption<ED>, context: Cxt): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.vaccumOper = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var vaccum_1 = require("./vaccum");
|
|
6
|
+
var filter_1 = require("../store/filter");
|
|
7
|
+
/**
|
|
8
|
+
* 将一定日期之前的oper对象清空
|
|
9
|
+
* @param option
|
|
10
|
+
* @param context
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
function vaccumOper(option, context) {
|
|
14
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
15
|
+
var aliveLine, excludeOpers, rest, operFilter, notFilters, key;
|
|
16
|
+
return tslib_1.__generator(this, function (_a) {
|
|
17
|
+
aliveLine = option.aliveLine, excludeOpers = option.excludeOpers, rest = tslib_1.__rest(option, ["aliveLine", "excludeOpers"]);
|
|
18
|
+
operFilter = {};
|
|
19
|
+
if (excludeOpers) {
|
|
20
|
+
notFilters = [];
|
|
21
|
+
for (key in excludeOpers) {
|
|
22
|
+
if (excludeOpers[key].length > 0) {
|
|
23
|
+
notFilters.push({
|
|
24
|
+
targetEntity: key,
|
|
25
|
+
action: {
|
|
26
|
+
$in: excludeOpers[key],
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
notFilters.push({
|
|
32
|
+
targetEntity: key,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (notFilters.length > 0) {
|
|
37
|
+
operFilter.$not = {
|
|
38
|
+
$or: notFilters,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return [2 /*return*/, (0, vaccum_1.vaccumEntities)(tslib_1.__assign({ entities: [{
|
|
43
|
+
entity: 'operEntity',
|
|
44
|
+
aliveLine: aliveLine + 10000,
|
|
45
|
+
filter: {
|
|
46
|
+
oper: (0, filter_1.combineFilters)([operFilter, {
|
|
47
|
+
$$createAt$$: {
|
|
48
|
+
$lt: aliveLine,
|
|
49
|
+
}
|
|
50
|
+
}]),
|
|
51
|
+
},
|
|
52
|
+
}, {
|
|
53
|
+
entity: 'oper',
|
|
54
|
+
aliveLine: aliveLine,
|
|
55
|
+
filter: operFilter,
|
|
56
|
+
}] }, rest), context)];
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
exports.vaccumOper = vaccumOper;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { EntityDict } from '../types/Entity';
|
|
2
|
+
import { EntityDict as BaseEntityDict } from '../base-app-domain';
|
|
3
|
+
import { AsyncContext } from '../store/AsyncRowStore';
|
|
4
|
+
declare type VaccumOptionEntity<ED extends EntityDict & BaseEntityDict, T extends keyof ED> = {
|
|
5
|
+
entity: T;
|
|
6
|
+
filter?: ED[T]['Selection']['filter'];
|
|
7
|
+
aliveLine: number;
|
|
8
|
+
};
|
|
9
|
+
declare type VaccumOption<ED extends EntityDict & BaseEntityDict> = {
|
|
10
|
+
entities: Array<VaccumOptionEntity<ED, keyof ED>>;
|
|
11
|
+
backupDir?: string;
|
|
12
|
+
zip?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* 删除数据库中的部分数据,减少体积
|
|
16
|
+
* 一般只删除日志类数据
|
|
17
|
+
* @param option
|
|
18
|
+
*/
|
|
19
|
+
export declare function vaccumEntities<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>>(option: VaccumOption<ED>, context: Cxt): Promise<void>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.vaccumEntities = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
|
|
6
|
+
var fs_1 = require("fs");
|
|
7
|
+
var filter_1 = require("../store/filter");
|
|
8
|
+
var stream_1 = require("stream");
|
|
9
|
+
var uuid_1 = require("../utils/uuid");
|
|
10
|
+
/**
|
|
11
|
+
* 删除数据库中的部分数据,减少体积
|
|
12
|
+
* 一般只删除日志类数据
|
|
13
|
+
* @param option
|
|
14
|
+
*/
|
|
15
|
+
function vaccumEntities(option, context) {
|
|
16
|
+
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
|
17
|
+
var entities, backupDir, _loop_1, entities_1, entities_1_1, ele, e_1_1;
|
|
18
|
+
var e_1, _a;
|
|
19
|
+
var _this = this;
|
|
20
|
+
return tslib_1.__generator(this, function (_b) {
|
|
21
|
+
switch (_b.label) {
|
|
22
|
+
case 0:
|
|
23
|
+
entities = option.entities, backupDir = option.backupDir;
|
|
24
|
+
_loop_1 = function (ele) {
|
|
25
|
+
var entity, filter, aliveLine, filter2, zip, now, backFile, fd_1, attributes_1, projection_1, attr, count_1, appendData_1, createGzip, gzip_1, source_1, destination_1, _c, _d, _e;
|
|
26
|
+
var _f, _g;
|
|
27
|
+
return tslib_1.__generator(this, function (_h) {
|
|
28
|
+
switch (_h.label) {
|
|
29
|
+
case 0:
|
|
30
|
+
entity = ele.entity, filter = ele.filter, aliveLine = ele.aliveLine;
|
|
31
|
+
filter2 = {
|
|
32
|
+
$$createAt$$: {
|
|
33
|
+
$lt: aliveLine,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
if (filter) {
|
|
37
|
+
filter2 = (0, filter_1.combineFilters)([filter2, filter]);
|
|
38
|
+
}
|
|
39
|
+
if (!(backupDir && process.env.OAK_PLATFORM === 'server')) return [3 /*break*/, 4];
|
|
40
|
+
zip = option.zip;
|
|
41
|
+
now = (0, dayjs_1.default)();
|
|
42
|
+
backFile = "".concat(backupDir, "/").concat(entity, "-").concat(now.format('YYYY-MM-DD HH:mm:ss'), ".csv");
|
|
43
|
+
if ((0, fs_1.existsSync)(backFile)) {
|
|
44
|
+
(0, fs_1.rmSync)(backFile);
|
|
45
|
+
}
|
|
46
|
+
fd_1 = (0, fs_1.openSync)(backFile, 'a');
|
|
47
|
+
attributes_1 = ['id', '$$createAt$$', '$$updateAt$$', '$$deleteAt$$'];
|
|
48
|
+
projection_1 = {
|
|
49
|
+
id: 1,
|
|
50
|
+
$$createAt$$: 1,
|
|
51
|
+
$$updateAt$$: 1,
|
|
52
|
+
$$deleteAt$$: 1,
|
|
53
|
+
};
|
|
54
|
+
for (attr in context.getSchema()[entity].attributes) {
|
|
55
|
+
Object.assign(projection_1, (_f = {},
|
|
56
|
+
_f[attr] = 1,
|
|
57
|
+
_f));
|
|
58
|
+
attributes_1.push(attr);
|
|
59
|
+
}
|
|
60
|
+
(0, fs_1.appendFileSync)(fd_1, attributes_1.join(','));
|
|
61
|
+
(0, fs_1.appendFileSync)(fd_1, '\n');
|
|
62
|
+
count_1 = 0;
|
|
63
|
+
appendData_1 = function (minCreateAt) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
|
64
|
+
var filter3, rows, csvTxt, maxCreateAt;
|
|
65
|
+
return tslib_1.__generator(this, function (_a) {
|
|
66
|
+
switch (_a.label) {
|
|
67
|
+
case 0:
|
|
68
|
+
filter3 = (0, filter_1.combineFilters)([filter2, {
|
|
69
|
+
$$createAt$$: {
|
|
70
|
+
$gt: minCreateAt,
|
|
71
|
+
},
|
|
72
|
+
}]);
|
|
73
|
+
return [4 /*yield*/, context.select(entity, {
|
|
74
|
+
data: projection_1,
|
|
75
|
+
filter: filter3,
|
|
76
|
+
sorter: [{
|
|
77
|
+
$attr: {
|
|
78
|
+
$$createAt$$: 1,
|
|
79
|
+
},
|
|
80
|
+
$direction: 'asc'
|
|
81
|
+
}],
|
|
82
|
+
indexFrom: 0,
|
|
83
|
+
count: 1000,
|
|
84
|
+
}, { includedDeleted: true })];
|
|
85
|
+
case 1:
|
|
86
|
+
rows = _a.sent();
|
|
87
|
+
csvTxt = rows.map(function (row) { return attributes_1.map(function (attr) { return JSON.stringify(row[attr]); }).join(','); }).join('\n');
|
|
88
|
+
(0, fs_1.appendFileSync)(fd_1, csvTxt);
|
|
89
|
+
(0, fs_1.appendFileSync)(fd_1, '\n');
|
|
90
|
+
count_1 += rows.length;
|
|
91
|
+
if (rows.length === 1000) {
|
|
92
|
+
maxCreateAt = rows[999].$$createAt$$;
|
|
93
|
+
return [2 /*return*/, appendData_1(maxCreateAt)];
|
|
94
|
+
}
|
|
95
|
+
return [2 /*return*/];
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}); };
|
|
99
|
+
return [4 /*yield*/, appendData_1(0)];
|
|
100
|
+
case 1:
|
|
101
|
+
_h.sent();
|
|
102
|
+
(0, fs_1.closeSync)(fd_1);
|
|
103
|
+
console.log("\u5907\u4EFD".concat(entity, "\u5BF9\u8C61\u5B8C\u6BD5\uFF0C\u5171\u5907\u4EFD\u4E86").concat(count_1, "\u884C\u6570\u636E"));
|
|
104
|
+
if (!(count_1 === 0)) return [3 /*break*/, 2];
|
|
105
|
+
(0, fs_1.rmSync)(backFile);
|
|
106
|
+
return [3 /*break*/, 4];
|
|
107
|
+
case 2:
|
|
108
|
+
if (!zip) return [3 /*break*/, 4];
|
|
109
|
+
createGzip = require('zlib').createGzip;
|
|
110
|
+
gzip_1 = createGzip();
|
|
111
|
+
source_1 = (0, fs_1.createReadStream)(backFile);
|
|
112
|
+
destination_1 = (0, fs_1.createWriteStream)("".concat(backFile, ".zip"));
|
|
113
|
+
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
|
114
|
+
(0, stream_1.pipeline)(source_1, gzip_1, destination_1, function (err) {
|
|
115
|
+
if (err) {
|
|
116
|
+
reject(err);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
resolve(undefined);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
})];
|
|
123
|
+
case 3:
|
|
124
|
+
_h.sent();
|
|
125
|
+
_h.label = 4;
|
|
126
|
+
case 4:
|
|
127
|
+
_d = (_c = context).operate;
|
|
128
|
+
_e = [entity];
|
|
129
|
+
_g = {};
|
|
130
|
+
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
|
|
131
|
+
case 5:
|
|
132
|
+
// 将对应的数据删除
|
|
133
|
+
return [4 /*yield*/, _d.apply(_c, _e.concat([(_g.id = _h.sent(),
|
|
134
|
+
_g.action = 'remove',
|
|
135
|
+
_g.data = {},
|
|
136
|
+
_g.filter = filter2,
|
|
137
|
+
_g), { deletePhysically: true }]))];
|
|
138
|
+
case 6:
|
|
139
|
+
// 将对应的数据删除
|
|
140
|
+
_h.sent();
|
|
141
|
+
return [2 /*return*/];
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
_b.label = 1;
|
|
146
|
+
case 1:
|
|
147
|
+
_b.trys.push([1, 6, 7, 8]);
|
|
148
|
+
entities_1 = tslib_1.__values(entities), entities_1_1 = entities_1.next();
|
|
149
|
+
_b.label = 2;
|
|
150
|
+
case 2:
|
|
151
|
+
if (!!entities_1_1.done) return [3 /*break*/, 5];
|
|
152
|
+
ele = entities_1_1.value;
|
|
153
|
+
return [5 /*yield**/, _loop_1(ele)];
|
|
154
|
+
case 3:
|
|
155
|
+
_b.sent();
|
|
156
|
+
_b.label = 4;
|
|
157
|
+
case 4:
|
|
158
|
+
entities_1_1 = entities_1.next();
|
|
159
|
+
return [3 /*break*/, 2];
|
|
160
|
+
case 5: return [3 /*break*/, 8];
|
|
161
|
+
case 6:
|
|
162
|
+
e_1_1 = _b.sent();
|
|
163
|
+
e_1 = { error: e_1_1 };
|
|
164
|
+
return [3 /*break*/, 8];
|
|
165
|
+
case 7:
|
|
166
|
+
try {
|
|
167
|
+
if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
|
|
168
|
+
}
|
|
169
|
+
finally { if (e_1) throw e_1.error; }
|
|
170
|
+
return [7 /*endfinally*/];
|
|
171
|
+
case 8: return [2 /*return*/];
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
exports.vaccumEntities = vaccumEntities;
|
package/lib/types/Connector.d.ts
CHANGED
|
@@ -16,10 +16,10 @@ export declare abstract class Connector<ED extends EntityDict, BackCxt extends A
|
|
|
16
16
|
params: any;
|
|
17
17
|
context: BackCxt;
|
|
18
18
|
}>;
|
|
19
|
-
abstract serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): {
|
|
19
|
+
abstract serializeResult(result: any, context: BackCxt, headers: IncomingHttpHeaders, body: any): Promise<{
|
|
20
20
|
body: any;
|
|
21
21
|
headers?: Record<string, any>;
|
|
22
|
-
}
|
|
22
|
+
}>;
|
|
23
23
|
abstract serializeException(exception: OakException<ED>, headers: IncomingHttpHeaders, body: any): {
|
|
24
24
|
body: any;
|
|
25
25
|
headers?: Record<string, any>;
|
package/lib/types/Entity.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export declare type OperateOption = {
|
|
|
37
37
|
allowExists?: boolean;
|
|
38
38
|
modiParentId?: string;
|
|
39
39
|
modiParentEntity?: string;
|
|
40
|
+
deletePhysically?: boolean;
|
|
40
41
|
dummy?: 1;
|
|
41
42
|
};
|
|
42
43
|
export declare type FormUpdateData<SH extends GeneralEntityShape> = Partial<{
|
|
@@ -64,7 +65,7 @@ export interface EntityShape {
|
|
|
64
65
|
$$updateAt$$: number | Date;
|
|
65
66
|
$$deleteAt$$?: number | Date | null;
|
|
66
67
|
}
|
|
67
|
-
interface GeneralEntityShape extends EntityShape {
|
|
68
|
+
export interface GeneralEntityShape extends EntityShape {
|
|
68
69
|
[K: string]: any;
|
|
69
70
|
}
|
|
70
71
|
export declare type MakeAction<A extends string> = A;
|
package/lib/types/Exception.d.ts
CHANGED
|
@@ -55,12 +55,15 @@ export declare class OakRowInconsistencyException<ED extends EntityDict> extends
|
|
|
55
55
|
export declare class OakInputIllegalException<ED extends EntityDict> extends OakUserException<ED> {
|
|
56
56
|
private attributes;
|
|
57
57
|
private entity;
|
|
58
|
-
constructor(entity:
|
|
59
|
-
getEntity():
|
|
58
|
+
constructor(entity: keyof ED, attributes: string[], message?: string);
|
|
59
|
+
getEntity(): keyof ED;
|
|
60
60
|
getAttributes(): string[];
|
|
61
61
|
addAttributesPrefix(prefix: string): void;
|
|
62
62
|
toString(): string;
|
|
63
63
|
}
|
|
64
|
+
export declare class OakAttrNotNullException<ED extends EntityDict> extends OakInputIllegalException<ED> {
|
|
65
|
+
constructor(entity: keyof ED, attributes: string[], message?: string);
|
|
66
|
+
}
|
|
64
67
|
/**
|
|
65
68
|
* 用户权限不够时抛的异常
|
|
66
69
|
*/
|
package/lib/types/Exception.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.makeException = exports.OakPreConditionUnsetException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = void 0;
|
|
3
|
+
exports.makeException = exports.OakPreConditionUnsetException = exports.OakDeadlock = exports.OakCongruentRowExists = exports.OakRowLockedException = exports.OakUnloggedInException = exports.OakUserUnpermittedException = exports.OakAttrNotNullException = exports.OakInputIllegalException = exports.OakRowInconsistencyException = exports.OakUserException = exports.OakExternalException = exports.OakRowUnexistedException = exports.OakOperExistedException = exports.OakImportDataParseException = exports.OakUniqueViolationException = exports.OakDataException = exports.OakException = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
6
|
var OakException = /** @class */ (function (_super) {
|
|
@@ -179,6 +179,15 @@ var OakInputIllegalException = /** @class */ (function (_super) {
|
|
|
179
179
|
}(OakUserException));
|
|
180
180
|
exports.OakInputIllegalException = OakInputIllegalException;
|
|
181
181
|
;
|
|
182
|
+
// 属性为空
|
|
183
|
+
var OakAttrNotNullException = /** @class */ (function (_super) {
|
|
184
|
+
tslib_1.__extends(OakAttrNotNullException, _super);
|
|
185
|
+
function OakAttrNotNullException(entity, attributes, message) {
|
|
186
|
+
return _super.call(this, entity, attributes, message || '属性不允许为空') || this;
|
|
187
|
+
}
|
|
188
|
+
return OakAttrNotNullException;
|
|
189
|
+
}(OakInputIllegalException));
|
|
190
|
+
exports.OakAttrNotNullException = OakAttrNotNullException;
|
|
182
191
|
/**
|
|
183
192
|
* 用户权限不够时抛的异常
|
|
184
193
|
*/
|
|
@@ -344,6 +353,11 @@ function makeException(data) {
|
|
|
344
353
|
e.setOpRecords(data.opRecords);
|
|
345
354
|
return e;
|
|
346
355
|
}
|
|
356
|
+
case 'OakAttrNotNullException': {
|
|
357
|
+
var e = new OakAttrNotNullException(data.entity, data.attributes, data.message);
|
|
358
|
+
e.setOpRecords(data.opRecords);
|
|
359
|
+
return e;
|
|
360
|
+
}
|
|
347
361
|
default:
|
|
348
362
|
return;
|
|
349
363
|
}
|
package/lib/types/RowStore.d.ts
CHANGED
|
@@ -3,9 +3,13 @@ import { StorageSchema } from './Storage';
|
|
|
3
3
|
export declare type TxnOption = {
|
|
4
4
|
isolationLevel: 'repeatable read' | 'serializable';
|
|
5
5
|
};
|
|
6
|
+
export declare type SelectionRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, selection: ED[keyof ED]['Selection']) => void;
|
|
7
|
+
export declare type OperationRewriter<ED extends EntityDict> = (schema: StorageSchema<ED>, entity: keyof ED, operate: ED[keyof ED]['Operation']) => void;
|
|
6
8
|
export declare abstract class RowStore<ED extends EntityDict> {
|
|
7
9
|
protected storageSchema: StorageSchema<ED>;
|
|
8
10
|
constructor(storageSchema: StorageSchema<ED>);
|
|
11
|
+
abstract registerOperationRewriter(rewriter: OperationRewriter<ED>): void;
|
|
12
|
+
abstract registerSelectionRewriter(rewriter: SelectionRewriter<ED>): void;
|
|
9
13
|
getSchema(): StorageSchema<ED>;
|
|
10
14
|
mergeOperationResult(result: OperationResult<ED>, toBeMerged: OperationResult<ED>): void;
|
|
11
15
|
mergeMultipleResults(toBeMerged: OperationResult<ED>[]): OperationResult<ED>;
|