oak-backend-base 3.0.0 → 3.1.0
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 +31 -25
- package/lib/AppLoader.js +343 -251
- package/lib/DataSubscriber.d.ts +18 -0
- package/lib/DataSubscriber.js +148 -0
- package/lib/DbStore.d.ts +18 -16
- package/lib/DbStore.js +116 -112
- package/lib/index.d.ts +1 -1
- package/lib/index.js +5 -5
- package/lib/polyfill.d.ts +3 -3
- package/lib/polyfill.js +12 -12
- package/package.json +41 -37
package/lib/AppLoader.d.ts
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
3
|
+
import { AppLoader as GeneralAppLoader, EntityDict, OpRecord } from "oak-domain/lib/types";
|
|
4
|
+
import { DbStore } from "./DbStore";
|
|
5
|
+
import { BackendRuntimeContext } from 'oak-frontend-base';
|
|
6
|
+
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
|
7
|
+
import { Namespace } from 'socket.io';
|
|
8
|
+
export declare class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends GeneralAppLoader<ED, Cxt> {
|
|
9
|
+
private dbStore;
|
|
10
|
+
private aspectDict;
|
|
11
|
+
private externalDependencies;
|
|
12
|
+
private dataSubscriber?;
|
|
13
|
+
private contextBuilder;
|
|
14
|
+
private requireSth;
|
|
15
|
+
constructor(path: string, contextBuilder: (scene?: string) => (store: DbStore<ED, Cxt>, header?: IncomingHttpHeaders) => Promise<Cxt>, ns?: Namespace);
|
|
16
|
+
initTriggers(): void;
|
|
17
|
+
startWatchers(): void;
|
|
18
|
+
mount(initialize?: true): Promise<void>;
|
|
19
|
+
unmount(): Promise<void>;
|
|
20
|
+
execAspect(name: string, header?: IncomingHttpHeaders, contextString?: string, params?: any): Promise<{
|
|
21
|
+
opRecords: OpRecord<ED>[];
|
|
22
|
+
result: any;
|
|
23
|
+
message?: string;
|
|
24
|
+
}>;
|
|
25
|
+
initialize(dropIfExists?: boolean): Promise<void>;
|
|
26
|
+
getStore(): DbStore<ED, Cxt>;
|
|
27
|
+
getEndpoints(prefix: string): [string, "get" | "post" | "put" | "delete", string, (params: Record<string, string>, headers: IncomingHttpHeaders, req: IncomingMessage, body?: any) => Promise<any>][];
|
|
28
|
+
startTimers(): void;
|
|
29
|
+
execStartRoutines(): Promise<void>;
|
|
30
|
+
execRoutine(routine: (context: Cxt) => Promise<void>): Promise<void>;
|
|
31
|
+
}
|
package/lib/AppLoader.js
CHANGED
|
@@ -1,251 +1,343 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AppLoader = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
const node_schedule_1 = require("node-schedule");
|
|
8
|
-
const env_1 = require("oak-domain/lib/compiler/env");
|
|
9
|
-
const actionDef_1 = require("oak-domain/lib/store/actionDef");
|
|
10
|
-
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
|
11
|
-
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
|
12
|
-
const types_1 = require("oak-domain/lib/types");
|
|
13
|
-
const DbStore_1 = require("./DbStore");
|
|
14
|
-
const index_1 = tslib_1.__importStar(require("oak-common-aspect/lib/index"));
|
|
15
|
-
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
Object.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AppLoader = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const node_schedule_1 = require("node-schedule");
|
|
8
|
+
const env_1 = require("oak-domain/lib/compiler/env");
|
|
9
|
+
const actionDef_1 = require("oak-domain/lib/store/actionDef");
|
|
10
|
+
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
|
11
|
+
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
|
12
|
+
const types_1 = require("oak-domain/lib/types");
|
|
13
|
+
const DbStore_1 = require("./DbStore");
|
|
14
|
+
const index_1 = tslib_1.__importStar(require("oak-common-aspect/lib/index"));
|
|
15
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
16
|
+
const DataSubscriber_1 = tslib_1.__importDefault(require("./DataSubscriber"));
|
|
17
|
+
class AppLoader extends types_1.AppLoader {
|
|
18
|
+
dbStore;
|
|
19
|
+
aspectDict;
|
|
20
|
+
externalDependencies;
|
|
21
|
+
dataSubscriber;
|
|
22
|
+
contextBuilder;
|
|
23
|
+
requireSth(filePath) {
|
|
24
|
+
const depFilePath = (0, path_1.join)(this.path, filePath);
|
|
25
|
+
let sth;
|
|
26
|
+
if ((0, fs_1.existsSync)(`${depFilePath}.js`)) {
|
|
27
|
+
sth = require((0, path_1.join)(this.path, filePath)).default;
|
|
28
|
+
}
|
|
29
|
+
const sthExternal = this.externalDependencies.map(ele => {
|
|
30
|
+
const depFilePath = (0, path_1.join)(this.path, 'node_modules', ele, filePath);
|
|
31
|
+
if ((0, fs_1.existsSync)(`${depFilePath}.js`)) {
|
|
32
|
+
return require(depFilePath).default;
|
|
33
|
+
}
|
|
34
|
+
}).filter(ele => !!ele);
|
|
35
|
+
if (!sth) {
|
|
36
|
+
if (sthExternal.length > 0 && sthExternal[0] instanceof Array) {
|
|
37
|
+
sth = [];
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
sth = {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (sth instanceof Array) {
|
|
44
|
+
sthExternal.forEach((sth2, idx) => {
|
|
45
|
+
(0, assert_1.default)(sth2 instanceof Array, `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是数组,与项目对应路径的输出不一致`);
|
|
46
|
+
sth.push(...sth2);
|
|
47
|
+
});
|
|
48
|
+
return sth;
|
|
49
|
+
}
|
|
50
|
+
(0, assert_1.default)(typeof sth === 'object');
|
|
51
|
+
const sthOut = {};
|
|
52
|
+
sthExternal.forEach((sth2, idx) => {
|
|
53
|
+
(0, assert_1.default)(typeof sth2 === 'object' && !(sth2 instanceof Array), `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是非数组对象,与项目对应路径的输出不一致`);
|
|
54
|
+
const inter = (0, lodash_1.intersection)(Object.keys(sthOut), Object.keys(sth2));
|
|
55
|
+
if (inter.length > 0) {
|
|
56
|
+
console.warn(`${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${inter.join(',')}】与其它对应路径输出的key值有冲突,请仔细检查避免错误`);
|
|
57
|
+
inter.forEach((ele) => {
|
|
58
|
+
if (sth2[ele] instanceof Array && sthOut[ele]) {
|
|
59
|
+
(0, assert_1.default)(sthOut[ele] instanceof Array, `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`);
|
|
60
|
+
console.warn(`${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】与其它对应路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`);
|
|
61
|
+
sth2[ele].push(...sthOut[ele]);
|
|
62
|
+
}
|
|
63
|
+
else if (!(sth2[ele] instanceof Array) && sthOut[ele]) {
|
|
64
|
+
(0, assert_1.default)(!(sthOut[ele] instanceof Array), `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`);
|
|
65
|
+
console.warn(`${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】将对与其它对应路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
Object.assign(sthOut, sth2);
|
|
70
|
+
});
|
|
71
|
+
const inter = (0, lodash_1.intersection)(Object.keys(sthOut), Object.keys(sth));
|
|
72
|
+
if (inter.length > 0) {
|
|
73
|
+
inter.forEach((ele) => {
|
|
74
|
+
if (sth[ele] instanceof Array && sthOut[ele]) {
|
|
75
|
+
(0, assert_1.default)(sthOut[ele] instanceof Array, `项目${filePath}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`);
|
|
76
|
+
console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】与其它引用包该路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`);
|
|
77
|
+
sth[ele].push(...sthOut[ele]);
|
|
78
|
+
}
|
|
79
|
+
else if (!(sth[ele] instanceof Array) && sthOut[ele]) {
|
|
80
|
+
(0, assert_1.default)(!(sthOut[ele] instanceof Array), `项目${filePath}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`);
|
|
81
|
+
console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】将对其它引用包该路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
Object.assign(sthOut, sth);
|
|
86
|
+
return sthOut;
|
|
87
|
+
}
|
|
88
|
+
constructor(path, contextBuilder, ns) {
|
|
89
|
+
super(path);
|
|
90
|
+
const dbConfig = require((0, path_1.join)(path, '/configuration/mysql.json'));
|
|
91
|
+
const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
|
|
92
|
+
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = require(`${path}/lib/config/relation`);
|
|
93
|
+
this.externalDependencies = require((0, env_1.OAK_EXTERNAL_LIBS_FILEPATH)((0, path_1.join)(path, 'lib')));
|
|
94
|
+
this.aspectDict = Object.assign({}, index_1.default, this.requireSth('lib/aspects/index'));
|
|
95
|
+
this.dbStore = new DbStore_1.DbStore(storageSchema, contextBuilder, dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
|
96
|
+
if (ns) {
|
|
97
|
+
this.dataSubscriber = new DataSubscriber_1.default(ns, (scene) => this.contextBuilder(scene)(this.dbStore));
|
|
98
|
+
this.contextBuilder = (scene) => async (store) => {
|
|
99
|
+
const context = await contextBuilder(scene)(store);
|
|
100
|
+
// 注入在提交前向dataSubscribe
|
|
101
|
+
const originCommit = context.commit;
|
|
102
|
+
context.commit = async () => {
|
|
103
|
+
this.dataSubscriber.onDataCommited(context);
|
|
104
|
+
await originCommit.call(context);
|
|
105
|
+
};
|
|
106
|
+
return context;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.contextBuilder = contextBuilder;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
initTriggers() {
|
|
114
|
+
const triggers = this.requireSth('lib/triggers/index');
|
|
115
|
+
const checkers = this.requireSth('lib/checkers/index');
|
|
116
|
+
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
|
117
|
+
const { triggers: adTriggers, checkers: adCheckers } = (0, actionDef_1.makeIntrinsicCTWs)(this.dbStore.getSchema(), ActionDefDict);
|
|
118
|
+
triggers.forEach((trigger) => this.dbStore.registerTrigger(trigger));
|
|
119
|
+
adTriggers.forEach((trigger) => this.dbStore.registerTrigger(trigger));
|
|
120
|
+
checkers.forEach((checker) => this.dbStore.registerChecker(checker));
|
|
121
|
+
adCheckers.forEach((checker) => this.dbStore.registerChecker(checker));
|
|
122
|
+
}
|
|
123
|
+
startWatchers() {
|
|
124
|
+
const watchers = this.requireSth('lib/watchers/index');
|
|
125
|
+
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
|
126
|
+
const { watchers: adWatchers } = (0, actionDef_1.makeIntrinsicCTWs)(this.dbStore.getSchema(), ActionDefDict);
|
|
127
|
+
const totalWatchers = watchers.concat(adWatchers);
|
|
128
|
+
let count = 0;
|
|
129
|
+
const doWatchers = async () => {
|
|
130
|
+
count++;
|
|
131
|
+
const start = Date.now();
|
|
132
|
+
const context = await this.contextBuilder()(this.dbStore);
|
|
133
|
+
for (const w of totalWatchers) {
|
|
134
|
+
await context.begin();
|
|
135
|
+
try {
|
|
136
|
+
if (w.hasOwnProperty('actionData')) {
|
|
137
|
+
const { entity, action, filter, actionData } = w;
|
|
138
|
+
const filter2 = typeof filter === 'function' ? await filter() : filter;
|
|
139
|
+
const data = typeof actionData === 'function' ? await actionData() : actionData; // 这里有个奇怪的编译错误,不理解 by Xc
|
|
140
|
+
const result = await this.dbStore.operate(entity, {
|
|
141
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
142
|
+
action,
|
|
143
|
+
data,
|
|
144
|
+
filter: filter2
|
|
145
|
+
}, context, {
|
|
146
|
+
dontCollect: true,
|
|
147
|
+
});
|
|
148
|
+
console.log(`执行了watcher【${w.name}】,结果是:`, result);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const { entity, projection, fn, filter } = w;
|
|
152
|
+
const filter2 = typeof filter === 'function' ? await filter() : filter;
|
|
153
|
+
const projection2 = typeof projection === 'function' ? await projection() : projection;
|
|
154
|
+
const rows = await this.dbStore.select(entity, {
|
|
155
|
+
data: projection2,
|
|
156
|
+
filter: filter2,
|
|
157
|
+
}, context, {
|
|
158
|
+
dontCollect: true,
|
|
159
|
+
blockTrigger: true,
|
|
160
|
+
});
|
|
161
|
+
if (rows.length > 0) {
|
|
162
|
+
const result = await fn(context, rows);
|
|
163
|
+
console.log(`执行了watcher【${w.name}】,结果是:`, result);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
await context.commit();
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
await context.rollback();
|
|
170
|
+
console.error(`执行了watcher【${w.name}】,发生错误:`, err);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const duration = Date.now() - start;
|
|
174
|
+
console.log(`第${count}次执行watchers,共执行${watchers.length}个,耗时${duration}毫秒`);
|
|
175
|
+
const now = Date.now();
|
|
176
|
+
try {
|
|
177
|
+
await this.dbStore.checkpoint(process.env.NODE_ENV === 'development' ? now - 30 * 1000 : now - 120 * 1000);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.error(`执行了checkpoint,发生错误:`, err);
|
|
181
|
+
}
|
|
182
|
+
setTimeout(() => doWatchers(), 120000);
|
|
183
|
+
};
|
|
184
|
+
doWatchers();
|
|
185
|
+
}
|
|
186
|
+
async mount(initialize) {
|
|
187
|
+
const { path } = this;
|
|
188
|
+
if (!initialize) {
|
|
189
|
+
this.initTriggers();
|
|
190
|
+
}
|
|
191
|
+
const { importations, exportations } = require(`${path}/lib/ports/index`);
|
|
192
|
+
(0, index_1.registerPorts)(importations || [], exportations || []);
|
|
193
|
+
this.dbStore.connect();
|
|
194
|
+
}
|
|
195
|
+
async unmount() {
|
|
196
|
+
(0, index_1.clearPorts)();
|
|
197
|
+
this.dbStore.disconnect();
|
|
198
|
+
}
|
|
199
|
+
async execAspect(name, header, contextString, params) {
|
|
200
|
+
const context = await this.contextBuilder(contextString)(this.dbStore, header);
|
|
201
|
+
const fn = this.aspectDict[name];
|
|
202
|
+
if (!fn) {
|
|
203
|
+
throw new Error(`不存在的接口名称: ${name}`);
|
|
204
|
+
}
|
|
205
|
+
await context.begin();
|
|
206
|
+
try {
|
|
207
|
+
const result = await fn(params, context);
|
|
208
|
+
await context.commit();
|
|
209
|
+
await context.refineOpRecords();
|
|
210
|
+
return {
|
|
211
|
+
opRecords: context.opRecords,
|
|
212
|
+
message: context.getMessage(),
|
|
213
|
+
result,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
await context.rollback();
|
|
218
|
+
throw err;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async initialize(dropIfExists) {
|
|
222
|
+
await this.dbStore.initialize(dropIfExists);
|
|
223
|
+
const data = this.requireSth('lib/data/index');
|
|
224
|
+
const context = await this.contextBuilder()(this.dbStore);
|
|
225
|
+
for (const entity in data) {
|
|
226
|
+
let rows = data[entity];
|
|
227
|
+
if (entity === 'area') {
|
|
228
|
+
// 对area暂时处理一下
|
|
229
|
+
rows = require('./data/area.json');
|
|
230
|
+
}
|
|
231
|
+
if (rows.length > 0) {
|
|
232
|
+
await context.begin();
|
|
233
|
+
try {
|
|
234
|
+
await this.dbStore.operate(entity, {
|
|
235
|
+
data: rows,
|
|
236
|
+
action: 'create',
|
|
237
|
+
}, context, {
|
|
238
|
+
dontCollect: true,
|
|
239
|
+
dontCreateOper: true,
|
|
240
|
+
});
|
|
241
|
+
await context.commit();
|
|
242
|
+
console.log(`data in ${entity} initialized, ${rows.length} rows inserted`);
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
await context.rollback();
|
|
246
|
+
console.error(`data on ${entity} initilization failed!`);
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
this.dbStore.disconnect();
|
|
252
|
+
}
|
|
253
|
+
getStore() {
|
|
254
|
+
return this.dbStore;
|
|
255
|
+
}
|
|
256
|
+
getEndpoints(prefix) {
|
|
257
|
+
const endpoints = this.requireSth('lib/endpoints/index');
|
|
258
|
+
const endPointRouters = [];
|
|
259
|
+
const endPointMap = {};
|
|
260
|
+
const transformEndpointItem = (key, item) => {
|
|
261
|
+
const { name, method, fn, params: itemParams } = item;
|
|
262
|
+
const k = `${key}-${name}-${method}`;
|
|
263
|
+
if (endPointMap[k]) {
|
|
264
|
+
throw new Error(`endpoint中,url为「${key}」、名为${name}的方法「${method}」存在重复定义`);
|
|
265
|
+
}
|
|
266
|
+
endPointMap[k] = true;
|
|
267
|
+
let url = `${prefix}/${key}`;
|
|
268
|
+
if (itemParams) {
|
|
269
|
+
for (const p of itemParams) {
|
|
270
|
+
url += `/:${p}`;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
endPointRouters.push([name, method, url, async (params, headers, req, body) => {
|
|
274
|
+
const context = await this.contextBuilder()(this.dbStore);
|
|
275
|
+
await context.begin();
|
|
276
|
+
try {
|
|
277
|
+
const result = await fn(context, params, headers, req, body);
|
|
278
|
+
await context.commit();
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
await context.rollback();
|
|
283
|
+
console.error(`endpoint「${key}」方法「${method}」出错`, err);
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
}]);
|
|
287
|
+
};
|
|
288
|
+
for (const router in endpoints) {
|
|
289
|
+
const item = endpoints[router];
|
|
290
|
+
if (item instanceof Array) {
|
|
291
|
+
item.forEach(ele => transformEndpointItem(router, ele));
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
transformEndpointItem(router, item);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return endPointRouters;
|
|
298
|
+
}
|
|
299
|
+
startTimers() {
|
|
300
|
+
const timers = this.requireSth('lib/timers/index');
|
|
301
|
+
for (const timer of timers) {
|
|
302
|
+
const { cron, fn, name } = timer;
|
|
303
|
+
(0, node_schedule_1.scheduleJob)(name, cron, async (date) => {
|
|
304
|
+
const start = Date.now();
|
|
305
|
+
const context = await this.contextBuilder()(this.dbStore);
|
|
306
|
+
await context.begin();
|
|
307
|
+
console.log(`定时器【${name}】开始执行,时间是【${date.toLocaleTimeString()}】`);
|
|
308
|
+
try {
|
|
309
|
+
const result = await fn(context);
|
|
310
|
+
console.log(`定时器【${name}】执行完成,耗时${Date.now() - start}毫秒,结果是【${result}】`);
|
|
311
|
+
await context.commit();
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
console.warn(`定时器【${name}】执行失败,耗时${Date.now() - start}毫秒,错误是`, err);
|
|
315
|
+
await context.rollback();
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async execStartRoutines() {
|
|
321
|
+
const routines = this.requireSth('lib/routines/start');
|
|
322
|
+
for (const routine of routines) {
|
|
323
|
+
const { name, fn } = routine;
|
|
324
|
+
const context = await this.contextBuilder()(this.dbStore);
|
|
325
|
+
const start = Date.now();
|
|
326
|
+
await context.begin();
|
|
327
|
+
try {
|
|
328
|
+
const result = await fn(context);
|
|
329
|
+
console.log(`例程【${name}】执行完成,耗时${Date.now() - start}毫秒,结果是【${result}】`);
|
|
330
|
+
await context.commit();
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
console.warn(`例程【${name}】执行失败,耗时${Date.now() - start}毫秒,错误是`, err);
|
|
334
|
+
await context.rollback();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async execRoutine(routine) {
|
|
339
|
+
const context = await this.contextBuilder()(this.dbStore);
|
|
340
|
+
await routine(context);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
exports.AppLoader = AppLoader;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { EntityDict } from 'oak-domain/lib/types';
|
|
2
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
3
|
+
import { BackendRuntimeContext } from 'oak-frontend-base';
|
|
4
|
+
import { Namespace } from 'socket.io';
|
|
5
|
+
export default class DataSubscriber<ED extends EntityDict & BaseEntityDict, Context extends BackendRuntimeContext<ED>> {
|
|
6
|
+
private ns;
|
|
7
|
+
private contextBuilder;
|
|
8
|
+
private filterMap;
|
|
9
|
+
private idEntityMap;
|
|
10
|
+
constructor(ns: Namespace, contextBuilder: (scene?: string) => Promise<Context>);
|
|
11
|
+
private formCreateRoomRoutine;
|
|
12
|
+
/**
|
|
13
|
+
* 来自外部的socket连接,监听数据变化
|
|
14
|
+
*/
|
|
15
|
+
private startup;
|
|
16
|
+
private sendRecord;
|
|
17
|
+
onDataCommited(context: Context): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
|
4
|
+
const oak_domain_1 = require("oak-domain");
|
|
5
|
+
class DataSubscriber {
|
|
6
|
+
ns;
|
|
7
|
+
contextBuilder;
|
|
8
|
+
filterMap;
|
|
9
|
+
idEntityMap;
|
|
10
|
+
constructor(ns, contextBuilder) {
|
|
11
|
+
this.ns = ns;
|
|
12
|
+
this.contextBuilder = contextBuilder;
|
|
13
|
+
this.startup();
|
|
14
|
+
this.filterMap = {};
|
|
15
|
+
this.idEntityMap = {};
|
|
16
|
+
}
|
|
17
|
+
formCreateRoomRoutine(def) {
|
|
18
|
+
const { id, entity, filter } = def;
|
|
19
|
+
return (room) => {
|
|
20
|
+
if (room === id) {
|
|
21
|
+
console.log('add filter', room);
|
|
22
|
+
// 本房间不存在,说明这个filter是新出现的
|
|
23
|
+
if (this.filterMap[entity]) {
|
|
24
|
+
// id的唯一性由前台保证,重复则无视
|
|
25
|
+
Object.assign(this.filterMap[entity], {
|
|
26
|
+
[id]: filter,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
Object.assign(this.filterMap, {
|
|
31
|
+
[entity]: {
|
|
32
|
+
[id]: filter,
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
this.idEntityMap[id] = entity;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 来自外部的socket连接,监听数据变化
|
|
42
|
+
*/
|
|
43
|
+
startup() {
|
|
44
|
+
this.ns.on('connection', async (socket) => {
|
|
45
|
+
try {
|
|
46
|
+
const { 'oak-cxt': cxtStr } = socket.handshake.headers;
|
|
47
|
+
const context = await this.contextBuilder(cxtStr);
|
|
48
|
+
socket.userId = context.getCurrentUserId();
|
|
49
|
+
socket.context = context;
|
|
50
|
+
socket.idMap = {};
|
|
51
|
+
socket.on('sub', async (data) => {
|
|
52
|
+
try {
|
|
53
|
+
await Promise.all(data.map(async (ele) => {
|
|
54
|
+
const { id, entity, filter } = ele;
|
|
55
|
+
// 尝试select此filter,如果失败说明权限越界
|
|
56
|
+
await context.select(entity, {
|
|
57
|
+
data: {
|
|
58
|
+
id: 1,
|
|
59
|
+
},
|
|
60
|
+
filter,
|
|
61
|
+
}, {});
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
socket.emit('error', err.toString());
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
data.forEach((ele) => {
|
|
69
|
+
const createRoomRoutine = this.formCreateRoomRoutine(ele);
|
|
70
|
+
this.ns.adapter.on('create-room', createRoomRoutine);
|
|
71
|
+
socket.join(ele.id);
|
|
72
|
+
this.ns.adapter.off('create-room', createRoomRoutine);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
socket.on('unsub', (ids) => {
|
|
76
|
+
ids.forEach((id) => {
|
|
77
|
+
socket.leave(id);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
socket.emit('error', err.toString());
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this.ns.adapter.on('delete-room', (room) => {
|
|
86
|
+
const entity = this.idEntityMap[room];
|
|
87
|
+
if (entity) {
|
|
88
|
+
console.log('remove filter', room);
|
|
89
|
+
(0, lodash_1.unset)(this.filterMap[entity], room);
|
|
90
|
+
(0, lodash_1.unset)(this.idEntityMap, room);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
sendRecord(entity, filter, record, sid, isCreate) {
|
|
95
|
+
if (this.filterMap[entity]) {
|
|
96
|
+
Object.keys(this.filterMap[entity]).forEach(async (room) => {
|
|
97
|
+
const context = await this.contextBuilder();
|
|
98
|
+
const filter2 = this.filterMap[entity][room];
|
|
99
|
+
let needSend = false;
|
|
100
|
+
if (isCreate) {
|
|
101
|
+
// 如果是插入数据肯定是单行,使用相容性检测
|
|
102
|
+
const contained = await (0, oak_domain_1.checkFilterContains)(entity, context, filter2, filter, true);
|
|
103
|
+
needSend = contained;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const repeled = await (0, oak_domain_1.checkFilterRepel)(entity, context, filter, filter2, true);
|
|
107
|
+
needSend = !repeled;
|
|
108
|
+
}
|
|
109
|
+
if (needSend) {
|
|
110
|
+
if (sid) {
|
|
111
|
+
this.ns.to(room).except(sid).emit('data', [record], [room]);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.ns.to(room).emit('data', [record], [room]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
onDataCommited(context) {
|
|
121
|
+
const sid = context.getSubscriberId();
|
|
122
|
+
const { opRecords } = context;
|
|
123
|
+
opRecords.forEach((record) => {
|
|
124
|
+
const { a } = record;
|
|
125
|
+
switch (a) {
|
|
126
|
+
case 'c': {
|
|
127
|
+
const { e, d } = record;
|
|
128
|
+
this.sendRecord(e, d, record, sid, true);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 'u': {
|
|
132
|
+
const { e, d, f } = record;
|
|
133
|
+
this.sendRecord(e, f, record, sid);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'r': {
|
|
137
|
+
const { e, f } = record;
|
|
138
|
+
this.sendRecord(e, f, record, sid);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
default: {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.default = DataSubscriber;
|
package/lib/DbStore.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db';
|
|
2
|
-
import { EntityDict, StorageSchema, Trigger, Checker,
|
|
3
|
-
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
-
import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db';
|
|
2
|
+
import { EntityDict, StorageSchema, Trigger, Checker, SelectOption, SelectFreeEntities, UpdateFreeDict, AuthDeduceRelationMap } from 'oak-domain/lib/types';
|
|
3
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
+
import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
|
|
5
|
+
import { BackendRuntimeContext } from 'oak-frontend-base';
|
|
6
|
+
import { AsyncContext, AsyncRowStore } from 'oak-domain/lib/store/AsyncRowStore';
|
|
7
|
+
export declare class DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends MysqlStore<ED, Cxt> implements AsyncRowStore<ED, Cxt> {
|
|
8
|
+
private executor;
|
|
9
|
+
private relationAuth;
|
|
10
|
+
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (scene?: string) => (store: DbStore<ED, Cxt>) => Promise<Cxt>, mysqlConfiguration: MySQLConfiguration, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: SelectFreeEntities<ED>, updateFreeDict?: UpdateFreeDict<ED>);
|
|
11
|
+
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>>;
|
|
12
|
+
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
13
|
+
select<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: MySqlSelectOption): Promise<Partial<ED[T]["Schema"]>[]>;
|
|
14
|
+
count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: SelectOption): Promise<number>;
|
|
15
|
+
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
|
|
16
|
+
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
|
|
17
|
+
checkpoint(ts: number): Promise<number>;
|
|
18
|
+
}
|
package/lib/DbStore.js
CHANGED
|
@@ -1,112 +1,116 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DbStore = void 0;
|
|
4
|
-
const oak_db_1 = require("oak-db");
|
|
5
|
-
const TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor");
|
|
6
|
-
const RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
|
|
7
|
-
class DbStore extends oak_db_1.MysqlStore {
|
|
8
|
-
executor;
|
|
9
|
-
relationAuth;
|
|
10
|
-
constructor(storageSchema, contextBuilder, mysqlConfiguration,
|
|
11
|
-
super(storageSchema, mysqlConfiguration);
|
|
12
|
-
this.executor = new TriggerExecutor_1.TriggerExecutor((scene) => contextBuilder(scene)(this));
|
|
13
|
-
this.relationAuth = new RelationAuth_1.RelationAuth(storageSchema,
|
|
14
|
-
}
|
|
15
|
-
async cascadeUpdateAsync(entity, operation, context, option) {
|
|
16
|
-
// 如果是在modi处理过程中,所有的trigger也可以延时到apply时再处理(这时候因为modi中的数据并不实际存在,处理会有问题)
|
|
17
|
-
if (!option.blockTrigger
|
|
18
|
-
await this.executor.preOperation(entity, operation, context, option);
|
|
19
|
-
}
|
|
20
|
-
const result = await super.cascadeUpdateAsync(entity, operation, context, option);
|
|
21
|
-
if (!option.blockTrigger
|
|
22
|
-
await this.executor.postOperation(entity, operation, context, option);
|
|
23
|
-
}
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
async operate(entity, operation, context, option) {
|
|
27
|
-
const autoCommit = !context.getCurrentTxnId();
|
|
28
|
-
let result;
|
|
29
|
-
if (autoCommit) {
|
|
30
|
-
await context.begin();
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
await this.relationAuth.checkRelationAsync(entity, operation, context);
|
|
34
|
-
result = await super.operate(entity, operation, context, option);
|
|
35
|
-
}
|
|
36
|
-
catch (err) {
|
|
37
|
-
await context.rollback();
|
|
38
|
-
throw err;
|
|
39
|
-
}
|
|
40
|
-
if (autoCommit) {
|
|
41
|
-
await context.commit();
|
|
42
|
-
}
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
async select(entity, selection, context, option) {
|
|
46
|
-
const autoCommit = !context.getCurrentTxnId();
|
|
47
|
-
if (autoCommit) {
|
|
48
|
-
await context.begin();
|
|
49
|
-
}
|
|
50
|
-
let result;
|
|
51
|
-
// select的trigger应加在根select之前,cascade的select不加处理
|
|
52
|
-
Object.assign(selection, {
|
|
53
|
-
action: 'select',
|
|
54
|
-
});
|
|
55
|
-
if (!option.blockTrigger) {
|
|
56
|
-
await this.executor.preOperation(entity, selection, context, option);
|
|
57
|
-
}
|
|
58
|
-
if (!option.dontCollect) {
|
|
59
|
-
await this.relationAuth.checkRelationAsync(entity, selection, context);
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
result = await super.select(entity, selection, context, option);
|
|
63
|
-
if (!option.blockTrigger) {
|
|
64
|
-
await this.executor.postOperation(entity, selection, context, option, result);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
await context.rollback();
|
|
69
|
-
throw err;
|
|
70
|
-
}
|
|
71
|
-
if (autoCommit) {
|
|
72
|
-
await context.commit();
|
|
73
|
-
}
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
async count(entity, selection, context, option) {
|
|
77
|
-
const autoCommit = !context.getCurrentTxnId();
|
|
78
|
-
let result;
|
|
79
|
-
if (autoCommit) {
|
|
80
|
-
await context.begin();
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DbStore = void 0;
|
|
4
|
+
const oak_db_1 = require("oak-db");
|
|
5
|
+
const TriggerExecutor_1 = require("oak-domain/lib/store/TriggerExecutor");
|
|
6
|
+
const RelationAuth_1 = require("oak-domain/lib/store/RelationAuth");
|
|
7
|
+
class DbStore extends oak_db_1.MysqlStore {
|
|
8
|
+
executor;
|
|
9
|
+
relationAuth;
|
|
10
|
+
constructor(storageSchema, contextBuilder, mysqlConfiguration, authDeduceRelationMap, selectFreeEntities = [], updateFreeDict = {}) {
|
|
11
|
+
super(storageSchema, mysqlConfiguration);
|
|
12
|
+
this.executor = new TriggerExecutor_1.TriggerExecutor((scene) => contextBuilder(scene)(this));
|
|
13
|
+
this.relationAuth = new RelationAuth_1.RelationAuth(storageSchema, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
|
14
|
+
}
|
|
15
|
+
async cascadeUpdateAsync(entity, operation, context, option) {
|
|
16
|
+
// 如果是在modi处理过程中,所有的trigger也可以延时到apply时再处理(这时候因为modi中的数据并不实际存在,处理会有问题)
|
|
17
|
+
if (!option.blockTrigger) {
|
|
18
|
+
await this.executor.preOperation(entity, operation, context, option);
|
|
19
|
+
}
|
|
20
|
+
const result = await super.cascadeUpdateAsync(entity, operation, context, option);
|
|
21
|
+
if (!option.blockTrigger) {
|
|
22
|
+
await this.executor.postOperation(entity, operation, context, option);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
async operate(entity, operation, context, option) {
|
|
27
|
+
const autoCommit = !context.getCurrentTxnId();
|
|
28
|
+
let result;
|
|
29
|
+
if (autoCommit) {
|
|
30
|
+
await context.begin();
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
await this.relationAuth.checkRelationAsync(entity, operation, context);
|
|
34
|
+
result = await super.operate(entity, operation, context, option);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
await context.rollback();
|
|
38
|
+
throw err;
|
|
39
|
+
}
|
|
40
|
+
if (autoCommit) {
|
|
41
|
+
await context.commit();
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
async select(entity, selection, context, option) {
|
|
46
|
+
const autoCommit = !context.getCurrentTxnId();
|
|
47
|
+
if (autoCommit) {
|
|
48
|
+
await context.begin();
|
|
49
|
+
}
|
|
50
|
+
let result;
|
|
51
|
+
// select的trigger应加在根select之前,cascade的select不加处理
|
|
52
|
+
Object.assign(selection, {
|
|
53
|
+
action: 'select',
|
|
54
|
+
});
|
|
55
|
+
if (!option.blockTrigger) {
|
|
56
|
+
await this.executor.preOperation(entity, selection, context, option);
|
|
57
|
+
}
|
|
58
|
+
if (!option.dontCollect) {
|
|
59
|
+
await this.relationAuth.checkRelationAsync(entity, selection, context);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
result = await super.select(entity, selection, context, option);
|
|
63
|
+
if (!option.blockTrigger) {
|
|
64
|
+
await this.executor.postOperation(entity, selection, context, option, result);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
await context.rollback();
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
if (autoCommit) {
|
|
72
|
+
await context.commit();
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
async count(entity, selection, context, option) {
|
|
77
|
+
const autoCommit = !context.getCurrentTxnId();
|
|
78
|
+
let result;
|
|
79
|
+
if (autoCommit) {
|
|
80
|
+
await context.begin();
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
// count不用检查权限,因为检查权限中本身要用到count
|
|
84
|
+
// const selection2 = Object.assign({
|
|
85
|
+
// action: 'select',
|
|
86
|
+
// }, selection) as ED[T]['Operation'];
|
|
87
|
+
// await this.relationAuth.checkRelationAsync(entity, selection2, context);
|
|
88
|
+
// if (!option.blockTrigger) {
|
|
89
|
+
// await this.executor.preOperation(entity, selection2, context, option);
|
|
90
|
+
// }
|
|
91
|
+
result = await super.count(entity, selection, context, option);
|
|
92
|
+
/* count应该不存在后trigger吧
|
|
93
|
+
if (!option.blockTrigger) {
|
|
94
|
+
await this.executor.postOperation(entity, selection2, context, option);
|
|
95
|
+
} */
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
await context.rollback();
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
if (autoCommit) {
|
|
102
|
+
await context.commit();
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
registerTrigger(trigger) {
|
|
107
|
+
this.executor.registerTrigger(trigger);
|
|
108
|
+
}
|
|
109
|
+
registerChecker(checker) {
|
|
110
|
+
this.executor.registerChecker(checker);
|
|
111
|
+
}
|
|
112
|
+
checkpoint(ts) {
|
|
113
|
+
return this.executor.checkpoint(ts);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.DbStore = DbStore;
|
package/lib/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { AppLoader } from './AppLoader';
|
|
1
|
+
export { AppLoader } from './AppLoader';
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AppLoader = void 0;
|
|
4
|
-
var AppLoader_1 = require("./AppLoader");
|
|
5
|
-
Object.defineProperty(exports, "AppLoader", { enumerable: true, get: function () { return AppLoader_1.AppLoader; } });
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AppLoader = void 0;
|
|
4
|
+
var AppLoader_1 = require("./AppLoader");
|
|
5
|
+
Object.defineProperty(exports, "AppLoader", { enumerable: true, get: function () { return AppLoader_1.AppLoader; } });
|
package/lib/polyfill.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
shuffle?: boolean;
|
|
3
|
-
};
|
|
1
|
+
export type GenerateIdOption = {
|
|
2
|
+
shuffle?: boolean;
|
|
3
|
+
};
|
package/lib/polyfill.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const uuid_1 = require("uuid");
|
|
4
|
-
async function generateNewId(option) {
|
|
5
|
-
if (option?.shuffle && process.env.NODE_ENV === 'development') {
|
|
6
|
-
return (0, uuid_1.v4)();
|
|
7
|
-
}
|
|
8
|
-
return (0, uuid_1.v1)();
|
|
9
|
-
}
|
|
10
|
-
Object.assign(global, {
|
|
11
|
-
generateNewId,
|
|
12
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const uuid_1 = require("uuid");
|
|
4
|
+
async function generateNewId(option) {
|
|
5
|
+
if (option?.shuffle && process.env.NODE_ENV === 'development') {
|
|
6
|
+
return (0, uuid_1.v4)();
|
|
7
|
+
}
|
|
8
|
+
return (0, uuid_1.v1)();
|
|
9
|
+
}
|
|
10
|
+
Object.assign(global, {
|
|
11
|
+
generateNewId,
|
|
12
|
+
});
|
package/package.json
CHANGED
|
@@ -1,37 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "oak-backend-base",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "oak-backend-base",
|
|
5
|
+
"main": "lib/index",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "XuChang"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"lib/**/*"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"copy-files": "copyfiles -u 1 src/**/*.json lib/",
|
|
14
|
+
"test": "ts-node test/test.ts",
|
|
15
|
+
"test2": "ts-node test/testDbStore.ts",
|
|
16
|
+
"build": "tsc && npm run copy-files"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"lodash": "^4.17.21",
|
|
20
|
+
"mysql": "^2.18.1",
|
|
21
|
+
"mysql2": "^2.3.3",
|
|
22
|
+
"node-schedule": "^2.1.0",
|
|
23
|
+
"oak-common-aspect": "^2,2,2",
|
|
24
|
+
"oak-frontend-base": "^3.0.5",
|
|
25
|
+
"oak-db": "^3.0.2",
|
|
26
|
+
"oak-domain": "^3.0.4",
|
|
27
|
+
"socket.io": "^4.7.2",
|
|
28
|
+
"uuid": "^8.3.2"
|
|
29
|
+
},
|
|
30
|
+
"license": "ISC",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/lodash": "^4.14.198",
|
|
33
|
+
"@types/node": "^20.6.0",
|
|
34
|
+
"@types/node-schedule": "^2.1.0",
|
|
35
|
+
"@types/uuid": "^8.3.4",
|
|
36
|
+
"copyfiles": "^2.4.1",
|
|
37
|
+
"ts-node": "^10.9.1",
|
|
38
|
+
"tslib": "^2.4.0",
|
|
39
|
+
"typescript": "^5.2.2"
|
|
40
|
+
}
|
|
41
|
+
}
|