oak-backend-base 4.1.25 → 4.1.27
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/AppLoader.d.ts +7 -6
- package/lib/AppLoader.js +53 -13
- package/lib/ClusterAppLoader.d.ts +3 -2
- package/lib/ClusterAppLoader.js +8 -2
- package/lib/DbStore.d.ts +8 -14
- package/lib/DbStore.js +117 -112
- package/lib/utils/dbPriority.d.ts +16 -0
- package/lib/utils/dbPriority.js +48 -0
- package/package.json +4 -4
package/lib/AppLoader.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
2
|
-
import { AppLoader as GeneralAppLoader, Trigger, EntityDict, Watcher, OpRecord, FreeTimer, OperationResult } from "oak-domain/lib/types";
|
|
3
|
-
import { DbStore } from "./DbStore";
|
|
2
|
+
import { AppLoader as GeneralAppLoader, Trigger, EntityDict, Watcher, OpRecord, FreeTimer, OperationResult, BaseTimer } from "oak-domain/lib/types";
|
|
4
3
|
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
5
4
|
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
|
6
5
|
import { Namespace } from 'socket.io';
|
|
@@ -8,13 +7,14 @@ import DataSubscriber from './cluster/DataSubscriber';
|
|
|
8
7
|
import Synchronizer from './Synchronizer';
|
|
9
8
|
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
|
|
10
9
|
import { InternalErrorHandler } from './types';
|
|
10
|
+
import { AppDbStore } from './DbStore';
|
|
11
11
|
export declare class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends GeneralAppLoader<ED, Cxt> {
|
|
12
|
-
protected dbStore:
|
|
12
|
+
protected dbStore: AppDbStore<ED, Cxt>;
|
|
13
13
|
private aspectDict;
|
|
14
14
|
private externalDependencies;
|
|
15
15
|
protected dataSubscriber?: DataSubscriber<ED, Cxt>;
|
|
16
16
|
protected synchronizer?: Synchronizer<ED, Cxt>;
|
|
17
|
-
protected contextBuilder: (store:
|
|
17
|
+
protected contextBuilder: (store: AppDbStore<ED, Cxt>) => Cxt;
|
|
18
18
|
private nsSocket?;
|
|
19
19
|
private watcherTimerId?;
|
|
20
20
|
private scheduledJobs;
|
|
@@ -47,7 +47,7 @@ export declare class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt exten
|
|
|
47
47
|
message?: string;
|
|
48
48
|
}>;
|
|
49
49
|
initialize(ifExists?: 'drop' | 'omit' | 'dropIfNotStatic'): Promise<void>;
|
|
50
|
-
getStore():
|
|
50
|
+
getStore(): AppDbStore<ED, Cxt>;
|
|
51
51
|
getEndpoints(prefix: string): [string, "post" | "get" | "put" | "delete", string, (params: Record<string, string>, headers: IncomingHttpHeaders, req: IncomingMessage, body?: any) => Promise<{
|
|
52
52
|
headers?: Record<string, string | string[]>;
|
|
53
53
|
data: any;
|
|
@@ -59,7 +59,8 @@ export declare class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt exten
|
|
|
59
59
|
protected getCheckpointTs(): number;
|
|
60
60
|
protected checkpoint(): Promise<number>;
|
|
61
61
|
startWatchers(): void;
|
|
62
|
-
protected
|
|
62
|
+
protected execBaseTimer(timer: BaseTimer<ED, Cxt>, context: Cxt): Promise<OperationResult<ED>> | undefined;
|
|
63
|
+
protected execFreeTimer(timer: FreeTimer<ED, Cxt>, contextBuilder: () => Promise<Cxt>): Promise<OperationResult<ED>> | undefined;
|
|
63
64
|
startTimers(): void;
|
|
64
65
|
execStartRoutines(): Promise<void>;
|
|
65
66
|
execStopRoutines(): Promise<void>;
|
package/lib/AppLoader.js
CHANGED
|
@@ -9,7 +9,6 @@ const IntrinsicLogics_1 = require("oak-domain/lib/store/IntrinsicLogics");
|
|
|
9
9
|
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
|
10
10
|
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
|
11
11
|
const types_1 = require("oak-domain/lib/types");
|
|
12
|
-
const DbStore_1 = require("./DbStore");
|
|
13
12
|
const index_1 = tslib_1.__importStar(require("oak-common-aspect/lib/index"));
|
|
14
13
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
15
14
|
const dependencyBuilder_1 = require("oak-domain/lib/compiler/dependencyBuilder");
|
|
@@ -18,6 +17,8 @@ const env_1 = require("./cluster/env");
|
|
|
18
17
|
const Synchronizer_1 = tslib_1.__importDefault(require("./Synchronizer"));
|
|
19
18
|
const i18n_1 = tslib_1.__importDefault(require("oak-domain/lib/data/i18n"));
|
|
20
19
|
const requirePrj_1 = tslib_1.__importDefault(require("./utils/requirePrj"));
|
|
20
|
+
const dbPriority_1 = require("./utils/dbPriority");
|
|
21
|
+
const DbStore_1 = require("./DbStore");
|
|
21
22
|
class AppLoader extends types_1.AppLoader {
|
|
22
23
|
dbStore;
|
|
23
24
|
aspectDict;
|
|
@@ -56,13 +57,12 @@ class AppLoader extends types_1.AppLoader {
|
|
|
56
57
|
* 发布内部错误事件给注册的处理器
|
|
57
58
|
*/
|
|
58
59
|
async publishInternalError(type, message, err) {
|
|
59
|
-
const errorToPublish = (0, lodash_1.cloneDeep)(err);
|
|
60
60
|
await Promise.all(this.internalErrorHandlers.map((handler) => {
|
|
61
61
|
return new Promise(async (resolve) => {
|
|
62
62
|
const ctx = await this.makeContext();
|
|
63
63
|
try {
|
|
64
64
|
console.log(`调用internalErrorHandler【${handler.name}】处理内部错误事件`);
|
|
65
|
-
await handler.handle(ctx, type, message,
|
|
65
|
+
await handler.handle(ctx, type, message, err);
|
|
66
66
|
await ctx.commit();
|
|
67
67
|
}
|
|
68
68
|
catch (e) {
|
|
@@ -95,8 +95,7 @@ class AppLoader extends types_1.AppLoader {
|
|
|
95
95
|
* 后台启动的configuration,统一放在这里读取
|
|
96
96
|
*/
|
|
97
97
|
getConfiguration() {
|
|
98
|
-
const
|
|
99
|
-
const dbConfig = require(dbConfigFile);
|
|
98
|
+
const dbConfig = (0, dbPriority_1.getDbConfig)(this.path);
|
|
100
99
|
const syncConfigFile = (0, path_1.join)(this.path, 'lib', 'configuration', 'sync.js');
|
|
101
100
|
const syncConfigs = (0, fs_1.existsSync)(syncConfigFile) && require(syncConfigFile).default;
|
|
102
101
|
return {
|
|
@@ -112,7 +111,7 @@ class AppLoader extends types_1.AppLoader {
|
|
|
112
111
|
this.externalDependencies = depGraph.ascOrder;
|
|
113
112
|
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = this.requireSth('lib/configuration/relation');
|
|
114
113
|
this.aspectDict = Object.assign({}, index_1.default, this.requireSth('lib/aspects/index'));
|
|
115
|
-
this.dbStore =
|
|
114
|
+
this.dbStore = (0, DbStore_1.createDbStore)(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
|
116
115
|
if (nsSubscribe) {
|
|
117
116
|
this.dataSubscriber = new DataSubscriber_1.default(nsSubscribe, nsServer);
|
|
118
117
|
}
|
|
@@ -290,7 +289,7 @@ class AppLoader extends types_1.AppLoader {
|
|
|
290
289
|
}
|
|
291
290
|
}
|
|
292
291
|
}
|
|
293
|
-
await this.dbStore.disconnect();
|
|
292
|
+
// await this.dbStore.disconnect(); // 不需要马上断开连接,在initialize后可能还会有操作,unmount时会断开
|
|
294
293
|
}
|
|
295
294
|
getStore() {
|
|
296
295
|
return this.dbStore;
|
|
@@ -370,8 +369,22 @@ class AppLoader extends types_1.AppLoader {
|
|
|
370
369
|
});
|
|
371
370
|
}
|
|
372
371
|
async execWatcher(watcher) {
|
|
373
|
-
const context = await this.makeContext();
|
|
374
372
|
let result;
|
|
373
|
+
if (watcher.hasOwnProperty('type') && watcher.type === 'free') {
|
|
374
|
+
const selectContext = await this.makeContext();
|
|
375
|
+
const { entity, projection, fn, filter, singleton, forUpdate } = watcher;
|
|
376
|
+
const filter2 = typeof filter === 'function' ? await filter() : (0, lodash_1.cloneDeep)(filter);
|
|
377
|
+
const projection2 = typeof projection === 'function' ? await projection() : (0, lodash_1.cloneDeep)(projection);
|
|
378
|
+
const rows = await this.selectInWatcher(entity, {
|
|
379
|
+
data: projection2,
|
|
380
|
+
filter: filter2,
|
|
381
|
+
}, selectContext, forUpdate, singleton);
|
|
382
|
+
if (rows.length > 0) {
|
|
383
|
+
result = await fn(() => this.makeContext(), rows);
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
const context = await this.makeContext();
|
|
375
388
|
try {
|
|
376
389
|
if (watcher.hasOwnProperty('actionData')) {
|
|
377
390
|
const { entity, action, filter, actionData, singleton } = watcher;
|
|
@@ -423,7 +436,13 @@ class AppLoader extends types_1.AppLoader {
|
|
|
423
436
|
const { watchers: adWatchers } = (0, IntrinsicLogics_1.makeIntrinsicLogics)(this.dbStore.getSchema(), ActionDefDict);
|
|
424
437
|
const totalWatchers = (watchers || []).concat(adWatchers);
|
|
425
438
|
let count = 0;
|
|
439
|
+
const skipOnceSet = new Set();
|
|
426
440
|
const execOne = async (watcher, start) => {
|
|
441
|
+
if (skipOnceSet.has(watcher.name)) {
|
|
442
|
+
skipOnceSet.delete(watcher.name);
|
|
443
|
+
console.log(`跳过本次执行watcher【${watcher.name}】`);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
427
446
|
try {
|
|
428
447
|
const result = await this.execWatcher(watcher);
|
|
429
448
|
if (result) {
|
|
@@ -455,12 +474,22 @@ class AppLoader extends types_1.AppLoader {
|
|
|
455
474
|
}
|
|
456
475
|
this.watcherTimerId = setTimeout(() => doWatchers(), 120000);
|
|
457
476
|
};
|
|
477
|
+
// 首次执行时,跳过所有lazy的watcher
|
|
478
|
+
for (const w of totalWatchers) {
|
|
479
|
+
if (w.lazy) {
|
|
480
|
+
skipOnceSet.add(w.name);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
458
483
|
doWatchers();
|
|
459
484
|
}
|
|
460
|
-
|
|
485
|
+
execBaseTimer(timer, context) {
|
|
461
486
|
const { timer: timerFn } = timer;
|
|
462
487
|
return timerFn(context);
|
|
463
488
|
}
|
|
489
|
+
execFreeTimer(timer, contextBuilder) {
|
|
490
|
+
const { timer: timerFn } = timer;
|
|
491
|
+
return timerFn(contextBuilder);
|
|
492
|
+
}
|
|
464
493
|
startTimers() {
|
|
465
494
|
const timers = this.requireSth('lib/timers/index');
|
|
466
495
|
if (timers) {
|
|
@@ -480,11 +509,22 @@ class AppLoader extends types_1.AppLoader {
|
|
|
480
509
|
}
|
|
481
510
|
}
|
|
482
511
|
else {
|
|
512
|
+
if (timer.hasOwnProperty('type') && timer.type === 'free') {
|
|
513
|
+
try {
|
|
514
|
+
const result = await this.execFreeTimer(timer, () => this.makeContext());
|
|
515
|
+
console.log(`定时器【${name}】执行成功,耗时${Date.now() - start}毫秒,结果是`, result);
|
|
516
|
+
}
|
|
517
|
+
catch (err) {
|
|
518
|
+
console.error(`定时器【${name}】执行失败,耗时${Date.now() - start}毫秒,错误是`, err);
|
|
519
|
+
this.publishInternalError(`timer`, `定时器【${name}】执行失败`, err);
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
483
523
|
const context = await this.makeContext();
|
|
484
524
|
try {
|
|
485
|
-
const result = await this.
|
|
525
|
+
const result = await this.execBaseTimer(timer, context);
|
|
486
526
|
if (result) {
|
|
487
|
-
console.log(`定时器【${name}】执行成功,耗时${Date.now() - start}
|
|
527
|
+
console.log(`定时器【${name}】执行成功,耗时${Date.now() - start}毫秒,结果是`, result);
|
|
488
528
|
}
|
|
489
529
|
await context.commit();
|
|
490
530
|
}
|
|
@@ -536,7 +576,7 @@ class AppLoader extends types_1.AppLoader {
|
|
|
536
576
|
socket: this.nsSocket,
|
|
537
577
|
contextBuilder: () => this.makeContext(),
|
|
538
578
|
});
|
|
539
|
-
console.log(`例程【${name}】执行成功,耗时${Date.now() - start}
|
|
579
|
+
console.log(`例程【${name}】执行成功,耗时${Date.now() - start}毫秒,结果是`, result);
|
|
540
580
|
await context.commit();
|
|
541
581
|
}
|
|
542
582
|
catch (err) {
|
|
@@ -571,7 +611,7 @@ class AppLoader extends types_1.AppLoader {
|
|
|
571
611
|
socket: this.nsSocket,
|
|
572
612
|
contextBuilder: () => this.makeContext(),
|
|
573
613
|
});
|
|
574
|
-
console.log(`例程【${name}】执行成功,耗时${Date.now() - start}
|
|
614
|
+
console.log(`例程【${name}】执行成功,耗时${Date.now() - start}毫秒,结果是`, result);
|
|
575
615
|
await context.commit();
|
|
576
616
|
}
|
|
577
617
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
2
|
-
import { EntityDict, OperationResult, Trigger, Watcher, FreeTimer } from 'oak-domain/lib/types';
|
|
2
|
+
import { EntityDict, OperationResult, Trigger, Watcher, FreeTimer, BaseTimer } from 'oak-domain/lib/types';
|
|
3
3
|
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
4
4
|
import { AppLoader } from './AppLoader';
|
|
5
5
|
import { Namespace } from 'socket.io';
|
|
@@ -15,6 +15,7 @@ export declare class ClusterAppLoader<ED extends EntityDict & BaseEntityDict, Cx
|
|
|
15
15
|
protected operateInWatcher<T extends keyof ED>(entity: T, operation: ED[T]['Update'], context: Cxt, singleton?: true): Promise<OperationResult<ED>>;
|
|
16
16
|
protected selectInWatcher<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt, forUpdate?: true, singleton?: true): Promise<Partial<ED[T]['Schema']>[]>;
|
|
17
17
|
protected execWatcher(watcher: Watcher<ED, keyof ED, Cxt>): Promise<OperationResult<ED> | undefined>;
|
|
18
|
-
protected
|
|
18
|
+
protected execBaseTimer(timer: BaseTimer<ED, Cxt>, context: Cxt): Promise<OperationResult<ED>> | undefined;
|
|
19
|
+
protected execFreeTimer(timer: FreeTimer<ED, Cxt>, contextBuilder: () => Promise<Cxt>): Promise<OperationResult<ED>> | undefined;
|
|
19
20
|
protected checkpoint(): Promise<number>;
|
|
20
21
|
}
|
package/lib/ClusterAppLoader.js
CHANGED
|
@@ -168,11 +168,17 @@ class ClusterAppLoader extends AppLoader_1.AppLoader {
|
|
|
168
168
|
}
|
|
169
169
|
return super.execWatcher(watcher);
|
|
170
170
|
}
|
|
171
|
-
|
|
171
|
+
execBaseTimer(timer, context) {
|
|
172
172
|
if (timer.singleton && (0, env_1.getClusterInfo)().instanceId !== 0) {
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
175
|
-
return super.
|
|
175
|
+
return super.execBaseTimer(timer, context);
|
|
176
|
+
}
|
|
177
|
+
execFreeTimer(timer, contextBuilder) {
|
|
178
|
+
if (timer.singleton && (0, env_1.getClusterInfo)().instanceId !== 0) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
return super.execFreeTimer(timer, contextBuilder);
|
|
176
182
|
}
|
|
177
183
|
async checkpoint() {
|
|
178
184
|
const { instanceCount, instanceId } = (0, env_1.getClusterInfo)();
|
package/lib/DbStore.d.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EntityDict, StorageSchema, Trigger, Checker,
|
|
1
|
+
import { DbConfiguration } from 'oak-db/src/types/configuration';
|
|
2
|
+
import { EntityDict, StorageSchema, Trigger, Checker, SelectFreeEntities, UpdateFreeDict, AuthDeduceRelationMap, VolatileTrigger, OperateOption } from 'oak-domain/lib/types';
|
|
3
3
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
-
import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
|
|
5
4
|
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
private relationAuth;
|
|
10
|
-
constructor(storageSchema: StorageSchema<ED>, contextBuilder: () => Cxt, mysqlConfiguration: MySQLConfiguration, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: SelectFreeEntities<ED>, updateFreeDict?: UpdateFreeDict<ED>, onVolatileTrigger?: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>);
|
|
11
|
-
checkRelationAsync<T extends keyof ED, Cxt extends AsyncContext<ED>>(entity: T, operation: Omit<ED[T]["Operation"] | ED[T]["Selection"], "id">, context: Cxt): Promise<void>;
|
|
12
|
-
protected cascadeUpdateAsync<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: AsyncContext<ED>, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
13
|
-
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
14
|
-
select<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: MySqlSelectOption): Promise<Partial<ED[T]["Schema"]>[]>;
|
|
15
|
-
count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: SelectOption): Promise<number>;
|
|
5
|
+
import { CascadeStore } from 'oak-domain/lib/store/CascadeStore';
|
|
6
|
+
import { DbStore } from 'oak-db/lib/types/dbStore';
|
|
7
|
+
export type TriggerStore<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> = {
|
|
16
8
|
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
|
|
17
9
|
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
|
|
18
10
|
setOnVolatileTrigger(onVolatileTrigger: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>): void;
|
|
19
11
|
execVolatileTrigger<T extends keyof ED>(entity: T, name: string, ids: string[], context: Cxt, option: OperateOption): Promise<void>;
|
|
20
12
|
checkpoint(ts: number): Promise<number>;
|
|
21
13
|
independentCheckPoint(name: string, ts: number, instanceCount?: number, instanceId?: number): Promise<number>;
|
|
22
|
-
}
|
|
14
|
+
};
|
|
15
|
+
export type AppDbStore<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> = DbStore<ED, Cxt> & CascadeStore<ED> & TriggerStore<ED, Cxt>;
|
|
16
|
+
export declare const createDbStore: <ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>>(storageSchema: StorageSchema<ED>, contextBuilder: () => Cxt, dbConfiguration: DbConfiguration, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: SelectFreeEntities<ED>, updateFreeDict?: UpdateFreeDict<ED>, onVolatileTrigger?: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>) => AppDbStore<ED, Cxt>;
|
package/lib/DbStore.js
CHANGED
|
@@ -1,128 +1,133 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const oak_db_1 = require("oak-db");
|
|
3
|
+
exports.createDbStore = void 0;
|
|
5
4
|
const TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor");
|
|
6
5
|
const RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (autoCommit) {
|
|
33
|
-
await context.begin();
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
await this.relationAuth.checkRelationAsync(entity, operation, context);
|
|
37
|
-
result = await super.operate(entity, operation, context, option);
|
|
38
|
-
}
|
|
39
|
-
catch (err) {
|
|
40
|
-
await context.rollback();
|
|
41
|
-
throw err;
|
|
42
|
-
}
|
|
43
|
-
if (autoCommit) {
|
|
44
|
-
await context.commit();
|
|
45
|
-
}
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
async select(entity, selection, context, option) {
|
|
49
|
-
const autoCommit = !context.getCurrentTxnId();
|
|
50
|
-
if (autoCommit) {
|
|
51
|
-
await context.begin();
|
|
52
|
-
}
|
|
53
|
-
let result;
|
|
54
|
-
// select的trigger应加在根select之前,cascade的select不加处理
|
|
55
|
-
Object.assign(selection, {
|
|
56
|
-
action: 'select',
|
|
57
|
-
});
|
|
58
|
-
if (!option.blockTrigger) {
|
|
59
|
-
await this.executor.preOperation(entity, selection, context, option);
|
|
6
|
+
const dbPriority_1 = require("./utils/dbPriority");
|
|
7
|
+
const createDbStore = (storageSchema, contextBuilder, dbConfiguration, authDeduceRelationMap, selectFreeEntities = [], updateFreeDict = {}, onVolatileTrigger) => {
|
|
8
|
+
const BaseStoreClass = (0, dbPriority_1.getDbStoreClass)();
|
|
9
|
+
// 动态创建继承类
|
|
10
|
+
class DynamicDbStore extends BaseStoreClass {
|
|
11
|
+
executor;
|
|
12
|
+
relationAuth;
|
|
13
|
+
constructor() {
|
|
14
|
+
super(storageSchema, dbConfiguration);
|
|
15
|
+
this.executor = new TriggerExecutor_1.TriggerExecutor(contextBuilder, undefined, onVolatileTrigger);
|
|
16
|
+
this.relationAuth = new RelationAuth_1.RelationAuth(storageSchema, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
|
17
|
+
}
|
|
18
|
+
checkRelationAsync(entity, operation, context) {
|
|
19
|
+
return this.relationAuth.checkRelationAsync(entity, operation, context);
|
|
20
|
+
}
|
|
21
|
+
async cascadeUpdateAsync(entity, operation, context, option) {
|
|
22
|
+
// 如果是在modi处理过程中,所有的trigger也可以延时到apply时再处理(这时候因为modi中的数据并不实际存在,处理会有问题)
|
|
23
|
+
if (!option.blockTrigger) {
|
|
24
|
+
await this.executor.preOperation(entity, operation, context, option);
|
|
25
|
+
}
|
|
26
|
+
const result = await super.cascadeUpdateAsync(entity, operation, context, option);
|
|
27
|
+
if (!option.blockTrigger) {
|
|
28
|
+
await this.executor.postOperation(entity, operation, context, option);
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
60
31
|
}
|
|
61
|
-
|
|
62
|
-
|
|
32
|
+
async operate(entity, operation, context, option) {
|
|
33
|
+
const autoCommit = !context.getCurrentTxnId();
|
|
34
|
+
let result;
|
|
35
|
+
if (autoCommit) {
|
|
36
|
+
await context.begin();
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await this.relationAuth.checkRelationAsync(entity, operation, context);
|
|
40
|
+
result = await super.operate(entity, operation, context, option);
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
await context.rollback();
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
if (autoCommit) {
|
|
47
|
+
await context.commit();
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
63
50
|
}
|
|
64
|
-
|
|
65
|
-
|
|
51
|
+
async select(entity, selection, context, option) {
|
|
52
|
+
const autoCommit = !context.getCurrentTxnId();
|
|
53
|
+
if (autoCommit) {
|
|
54
|
+
await context.begin();
|
|
55
|
+
}
|
|
56
|
+
let result;
|
|
57
|
+
// select的trigger应加在根select之前,cascade的select不加处理
|
|
58
|
+
Object.assign(selection, {
|
|
59
|
+
action: 'select',
|
|
60
|
+
});
|
|
66
61
|
if (!option.blockTrigger) {
|
|
67
|
-
await this.executor.
|
|
62
|
+
await this.executor.preOperation(entity, selection, context, option);
|
|
63
|
+
}
|
|
64
|
+
if (!option.dontCollect) {
|
|
65
|
+
await this.relationAuth.checkRelationAsync(entity, selection, context);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
result = await super.select(entity, selection, context, option);
|
|
69
|
+
if (!option.blockTrigger) {
|
|
70
|
+
await this.executor.postOperation(entity, selection, context, option, result);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
await context.rollback();
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
if (autoCommit) {
|
|
78
|
+
await context.commit();
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
async count(entity, selection, context, option) {
|
|
83
|
+
const autoCommit = !context.getCurrentTxnId();
|
|
84
|
+
let result;
|
|
85
|
+
if (autoCommit) {
|
|
86
|
+
await context.begin();
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
// count不用检查权限,因为检查权限中本身要用到count
|
|
90
|
+
// const selection2 = Object.assign({
|
|
91
|
+
// action: 'select',
|
|
92
|
+
// }, selection) as ED[T]['Operation'];
|
|
93
|
+
// await this.relationAuth.checkRelationAsync(entity, selection2, context);
|
|
94
|
+
// if (!option.blockTrigger) {
|
|
95
|
+
// await this.executor.preOperation(entity, selection2, context, option);
|
|
96
|
+
// }
|
|
97
|
+
result = await super.count(entity, selection, context, option);
|
|
98
|
+
/* count应该不存在后trigger吧
|
|
99
|
+
if (!option.blockTrigger) {
|
|
100
|
+
await this.executor.postOperation(entity, selection2, context, option);
|
|
101
|
+
} */
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
await context.rollback();
|
|
105
|
+
throw err;
|
|
68
106
|
}
|
|
107
|
+
if (autoCommit) {
|
|
108
|
+
await context.commit();
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
69
111
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
throw err;
|
|
112
|
+
registerTrigger(trigger) {
|
|
113
|
+
this.executor.registerTrigger(trigger);
|
|
73
114
|
}
|
|
74
|
-
|
|
75
|
-
|
|
115
|
+
registerChecker(checker) {
|
|
116
|
+
this.executor.registerChecker(checker, this.getSchema());
|
|
76
117
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
async count(entity, selection, context, option) {
|
|
80
|
-
const autoCommit = !context.getCurrentTxnId();
|
|
81
|
-
let result;
|
|
82
|
-
if (autoCommit) {
|
|
83
|
-
await context.begin();
|
|
118
|
+
setOnVolatileTrigger(onVolatileTrigger) {
|
|
119
|
+
this.executor.setOnVolatileTrigger(onVolatileTrigger);
|
|
84
120
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// const selection2 = Object.assign({
|
|
88
|
-
// action: 'select',
|
|
89
|
-
// }, selection) as ED[T]['Operation'];
|
|
90
|
-
// await this.relationAuth.checkRelationAsync(entity, selection2, context);
|
|
91
|
-
// if (!option.blockTrigger) {
|
|
92
|
-
// await this.executor.preOperation(entity, selection2, context, option);
|
|
93
|
-
// }
|
|
94
|
-
result = await super.count(entity, selection, context, option);
|
|
95
|
-
/* count应该不存在后trigger吧
|
|
96
|
-
if (!option.blockTrigger) {
|
|
97
|
-
await this.executor.postOperation(entity, selection2, context, option);
|
|
98
|
-
} */
|
|
121
|
+
async execVolatileTrigger(entity, name, ids, context, option) {
|
|
122
|
+
return this.executor.execVolatileTrigger(entity, name, ids, context, option);
|
|
99
123
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
throw err;
|
|
124
|
+
checkpoint(ts) {
|
|
125
|
+
return this.executor.checkpoint(ts);
|
|
103
126
|
}
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
independentCheckPoint(name, ts, instanceCount, instanceId) {
|
|
128
|
+
return this.executor.independentCheckPoint(name, ts, instanceCount, instanceId);
|
|
106
129
|
}
|
|
107
|
-
return result;
|
|
108
|
-
}
|
|
109
|
-
registerTrigger(trigger) {
|
|
110
|
-
this.executor.registerTrigger(trigger);
|
|
111
|
-
}
|
|
112
|
-
registerChecker(checker) {
|
|
113
|
-
this.executor.registerChecker(checker, this.getSchema());
|
|
114
|
-
}
|
|
115
|
-
setOnVolatileTrigger(onVolatileTrigger) {
|
|
116
|
-
this.executor.setOnVolatileTrigger(onVolatileTrigger);
|
|
117
|
-
}
|
|
118
|
-
async execVolatileTrigger(entity, name, ids, context, option) {
|
|
119
|
-
return this.executor.execVolatileTrigger(entity, name, ids, context, option);
|
|
120
|
-
}
|
|
121
|
-
checkpoint(ts) {
|
|
122
|
-
return this.executor.checkpoint(ts);
|
|
123
|
-
}
|
|
124
|
-
independentCheckPoint(name, ts, instanceCount, instanceId) {
|
|
125
|
-
return this.executor.independentCheckPoint(name, ts, instanceCount, instanceId);
|
|
126
130
|
}
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
return new DynamicDbStore();
|
|
132
|
+
};
|
|
133
|
+
exports.createDbStore = createDbStore;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MysqlStore, PostgreSQLStore } from "oak-db";
|
|
2
|
+
import { DbConfiguration } from "oak-db/src/types/configuration";
|
|
3
|
+
import { AsyncRowStore } from "oak-domain/lib/store/AsyncRowStore";
|
|
4
|
+
import { EntityDict, StorageSchema } from 'oak-domain/lib/types';
|
|
5
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
6
|
+
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
7
|
+
import { CascadeStore } from "oak-domain/lib/store/CascadeStore";
|
|
8
|
+
/**
|
|
9
|
+
* 数据库优先级列表,按顺序尝试获取配置文件
|
|
10
|
+
*/
|
|
11
|
+
export declare const dbList: {
|
|
12
|
+
mysql: typeof MysqlStore;
|
|
13
|
+
postgres: typeof PostgreSQLStore;
|
|
14
|
+
};
|
|
15
|
+
export declare const getDbConfig: (path: string) => DbConfiguration;
|
|
16
|
+
export declare const getDbStoreClass: <ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>>() => new (schema: StorageSchema<ED>, config: DbConfiguration) => AsyncRowStore<ED, Cxt> & CascadeStore<ED>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDbStoreClass = exports.getDbConfig = exports.dbList = void 0;
|
|
4
|
+
const oak_db_1 = require("oak-db");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
/**
|
|
8
|
+
* 数据库优先级列表,按顺序尝试获取配置文件
|
|
9
|
+
*/
|
|
10
|
+
exports.dbList = {
|
|
11
|
+
mysql: oak_db_1.MysqlStore,
|
|
12
|
+
postgres: oak_db_1.PostgreSQLStore
|
|
13
|
+
};
|
|
14
|
+
let usedDbType = null;
|
|
15
|
+
const getDbConfig = (path) => {
|
|
16
|
+
for (const db of Object.keys(exports.dbList)) {
|
|
17
|
+
try {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
19
|
+
let dbConfigFile = (0, path_1.join)(path, 'configuration', `${db}.${process.env.NODE_ENV}.json`);
|
|
20
|
+
if ((0, fs_1.existsSync)(dbConfigFile) === false) {
|
|
21
|
+
dbConfigFile = (0, path_1.join)(path, 'configuration', `${db}.json`);
|
|
22
|
+
}
|
|
23
|
+
if ((0, fs_1.existsSync)(dbConfigFile) === false) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const config = require(dbConfigFile);
|
|
27
|
+
console.log(`使用${db}作为数据库`);
|
|
28
|
+
usedDbType = db;
|
|
29
|
+
return Object.assign({}, config);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
// do nothing
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`没有找到数据库配置文件,请在configuration目录下添加任一配置文件:${Object.keys(exports.dbList).map(ele => `${ele}.json`).join('、')}`);
|
|
36
|
+
};
|
|
37
|
+
exports.getDbConfig = getDbConfig;
|
|
38
|
+
const getDbStoreClass = () => {
|
|
39
|
+
const dbType = usedDbType || (() => {
|
|
40
|
+
throw new Error('无法确定数据库类型');
|
|
41
|
+
})();
|
|
42
|
+
const DbStoreClass = exports.dbList[dbType.toLowerCase()];
|
|
43
|
+
if (!DbStoreClass) {
|
|
44
|
+
throw new Error(`不支持的数据库类型:${dbType},请确认是否存在以下配置文件:${Object.keys(exports.dbList).map(ele => `${ele}.json`).join('、')}`);
|
|
45
|
+
}
|
|
46
|
+
return DbStoreClass;
|
|
47
|
+
};
|
|
48
|
+
exports.getDbStoreClass = getDbStoreClass;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oak-backend-base",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.27",
|
|
4
4
|
"description": "oak-backend-base",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"author": {
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"mysql2": "^2.3.3",
|
|
23
23
|
"node-schedule": "^2.1.0",
|
|
24
24
|
"oak-common-aspect": "^3.0.5",
|
|
25
|
-
"oak-db": "^3.3.
|
|
26
|
-
"oak-domain": "^5.1.
|
|
27
|
-
"oak-frontend-base": "^5.3.
|
|
25
|
+
"oak-db": "^3.3.13",
|
|
26
|
+
"oak-domain": "^5.1.34",
|
|
27
|
+
"oak-frontend-base": "^5.3.45",
|
|
28
28
|
"socket.io": "^4.8.1",
|
|
29
29
|
"socket.io-client": "^4.7.2",
|
|
30
30
|
"uuid": "^8.3.2"
|