@gingkoo/base-server 0.0.1-alpha.0 → 0.0.1-alpha.10

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.
Files changed (122) hide show
  1. package/README.md +4 -0
  2. package/app.js +41 -40
  3. package/backend/common/entity.js +55 -0
  4. package/backend/common/enum.js +247 -0
  5. package/backend/common/fss/index.js +18 -0
  6. package/backend/common/fss/routers/attachment.js +45 -0
  7. package/backend/common/fss/routers/download.js +43 -0
  8. package/backend/common/fss/routers/import_export.js +113 -0
  9. package/backend/common/fss/routers/resources.js +149 -0
  10. package/backend/common/fss/routers/upload.js +81 -0
  11. package/backend/common/fss/services/import_export.js +159 -0
  12. package/backend/common/fss/services/index.js +92 -0
  13. package/backend/common/fss/utils.js +39 -0
  14. package/backend/common/ginfo/config.js +5 -0
  15. package/backend/common/ginfo/index.js +62 -0
  16. package/backend/common/index_template.html +28 -0
  17. package/backend/common/logger/index.js +96 -0
  18. package/backend/common/mapping.js +128 -0
  19. package/backend/common/middleware/auth.js +109 -0
  20. package/backend/common/middleware/cors.js +13 -0
  21. package/backend/common/middleware/response.js +25 -0
  22. package/backend/common/page_engine.js +487 -0
  23. package/backend/common/schedule.js +45 -0
  24. package/backend/common/services/dataCombine.js +67 -0
  25. package/backend/common/services/dept.js +37 -0
  26. package/backend/common/services/dict.js +488 -0
  27. package/backend/common/services/email.js +49 -0
  28. package/backend/common/services/generalConfig.js +137 -0
  29. package/backend/common/services/login.js +18 -0
  30. package/backend/common/services/notice.js +260 -0
  31. package/backend/common/services/permission.js +500 -0
  32. package/backend/common/services/roles.js +57 -0
  33. package/backend/common/services/send-notice.js +86 -0
  34. package/backend/common/services/task.js +259 -0
  35. package/backend/common/services/user.js +673 -0
  36. package/backend/common/socket.js +18 -0
  37. package/backend/common/sse/index.js +81 -0
  38. package/backend/common/sse/router.js +30 -0
  39. package/backend/common/task.js +75 -0
  40. package/backend/common/wechat/index.js +9 -0
  41. package/backend/common/wechat/routers/auth.js +238 -0
  42. package/{modules/user/frontend → backend/common/wechat/routers}/index.html +14 -7
  43. package/backend/common/wechat/services/auth.js +209 -0
  44. package/backend/common/wechat/services/notice.js +171 -0
  45. package/backend/config/index.js +61 -0
  46. package/backend/config/path.js +3 -0
  47. package/backend/router.js +100 -0
  48. package/backend/routers/app.js +222 -0
  49. package/backend/routers/automate.js +276 -0
  50. package/backend/routers/dict.js +370 -0
  51. package/backend/routers/email.js +85 -0
  52. package/backend/routers/generalConfig.js +276 -0
  53. package/backend/routers/idm.js +245 -0
  54. package/backend/routers/module.js +357 -0
  55. package/backend/routers/notice.js +138 -0
  56. package/backend/routers/pages.js +46 -0
  57. package/backend/routers/permission.js +985 -0
  58. package/backend/routers/setting.js +184 -0
  59. package/backend/routers/team/index.js +22 -0
  60. package/backend/routers/team/routers/mapping.js +29 -0
  61. package/backend/routers/team/routers/member.js +72 -0
  62. package/backend/routers/team/routers/membermanage.js +289 -0
  63. package/backend/routers/team/routers/pages.js +47 -0
  64. package/backend/routers/team/routers/roles.js +92 -0
  65. package/backend/routers/team/routers/teaminfo.js +27 -0
  66. package/backend/routers/team/routers/usergroup.js +213 -0
  67. package/backend/routers/team/services/mapping.js +101 -0
  68. package/backend/routers/team/services/member.js +206 -0
  69. package/backend/routers/team/services/roles.js +71 -0
  70. package/backend/routers/team/services/teaminfo.js +20 -0
  71. package/backend/routers/team/services/usergroup.js +128 -0
  72. package/backend/routers/user.js +436 -0
  73. package/backend/services/automate.js +60 -0
  74. package/backend/services/config.js +14 -0
  75. package/backend/services/module.js +298 -0
  76. package/backend/services/permission.js +192 -0
  77. package/backend/services/services.js +115 -0
  78. package/backend/services/setting.js +190 -0
  79. package/backend/services/token.js +42 -0
  80. package/backend/space.js +52 -0
  81. package/backend/space_mapping.js +22 -0
  82. package/backend/utils/avatar.js +48 -0
  83. package/backend/utils/color_gen_helper.js +20 -0
  84. package/backend/utils/date.js +66 -0
  85. package/backend/utils/excel.js +446 -0
  86. package/backend/utils/fs/doc.md +64 -0
  87. package/backend/utils/fs/index.js +127 -0
  88. package/backend/utils/jwt.js +54 -0
  89. package/backend/utils/modules/sequence.js +93 -0
  90. package/backend/utils/object.js +31 -0
  91. package/backend/utils/page-query-helper/index.js +61 -0
  92. package/backend/utils/path.js +123 -0
  93. package/backend/utils/run.js +25 -0
  94. package/backend/utils/tokenize.js +82 -0
  95. package/backend/utils/typeof.js +5 -0
  96. package/backend/utils/util.js +153 -0
  97. package/backend/views/api/index.js +32 -0
  98. package/backend/views/api/index.xml +49 -0
  99. package/backend/views/dict/index.js +80 -0
  100. package/backend/views/dict/index.xml +52 -0
  101. package/backend/views/index.js +32 -0
  102. package/backend/views/members_manage/index.js +68 -0
  103. package/backend/views/members_manage/index.xml +68 -0
  104. package/backend/views/roles/index.js +17 -0
  105. package/backend/views/roles/index.xml +47 -0
  106. package/backend/views/usergroup/index.js +68 -0
  107. package/backend/views/usergroup/index.xml +65 -0
  108. package/dist/base-assets/css/index-ffdb55a5.css +3 -0
  109. package/dist/base-assets/css/index-ffdb55a5.css.gz +0 -0
  110. package/dist/base-assets/js/index-b3998a47.js +762 -0
  111. package/dist/base-assets/js/index-b3998a47.js.gz +0 -0
  112. package/dist/base-assets/js/react-cropper.es-d5f06996.js +10 -0
  113. package/dist/base-assets/js/react-cropper.es-d5f06996.js.gz +0 -0
  114. package/dist/base-assets/png/u9-2348c304.png +0 -0
  115. package/dist/base-assets/woff2/materialicons-83be7b2f.woff2 +0 -0
  116. package/dist/index.html +162 -0
  117. package/package.json +62 -5
  118. package/common/router.js +0 -42
  119. package/modules/user/backend/index.js +0 -19
  120. package/modules/user/backend/routers/user.js +0 -11
  121. package/modules/user/frontend/index.js +0 -1
  122. package/serve.js +0 -9
@@ -0,0 +1,487 @@
1
+ const { readFileSync } = require('fs');
2
+ const _ = require('lodash');
3
+ const xmlreader = require('xmlreader');
4
+ const { logger } = require('./logger');
5
+ const { deepMerge } = require('../utils/object');
6
+ const tokenize = require('../utils/tokenize');
7
+
8
+ /**
9
+ * 将字符串转 bool
10
+ * @param {string} v 值
11
+ * @returns boolean
12
+ */
13
+ function isbool(v) {
14
+ return v === 'true' ? true : false;
15
+ }
16
+
17
+ /**
18
+ * 出来字符串中的特殊字符 如 %26 -> &
19
+ * @param {string} str
20
+ */
21
+ function handleSpecialChars(str) {
22
+ if (!str || typeof str !== 'string') return str;
23
+ return str.replace(/%26/g, '&');
24
+ }
25
+
26
+ /**
27
+ * 拆分字符串
28
+ * @param {string} str 源字符串
29
+ * @param {string?} delimiter 分隔符 默认不传是逗号
30
+ * @returns string[]
31
+ */
32
+ function splitStr(str, delimiter = ',') {
33
+ if (!str || typeof str !== 'string') return [];
34
+ return str.split(delimiter).map((v) => v.trim());
35
+ }
36
+
37
+ function handleFields(fields) {
38
+ if (!Array.isArray(fields)) return [];
39
+ return fields.map((item) => {
40
+ let {
41
+ id,
42
+ component,
43
+ clearable,
44
+ required,
45
+ multiple,
46
+ searchable,
47
+ showValue,
48
+ block,
49
+ isFilter,
50
+ ...rest
51
+ } = item.attributes();
52
+
53
+ let field = {
54
+ type: typeMap[component] || component,
55
+ name: id,
56
+ ...rest,
57
+ };
58
+ if (clearable) {
59
+ field.clearable = isbool(clearable);
60
+ }
61
+ if (required) {
62
+ field.required = isbool(required);
63
+ }
64
+ if (multiple) {
65
+ field.multiple = isbool(multiple);
66
+ }
67
+ if (searchable) {
68
+ field.searchable = isbool(searchable);
69
+ }
70
+ if (block) {
71
+ field.block = isbool(block);
72
+ }
73
+ if (showValue) {
74
+ field.showValue = isbool(showValue);
75
+ }
76
+
77
+ // 是否支持搜索
78
+ if (isFilter) {
79
+ field.isFilter = isbool(isFilter) === false ? false : true;
80
+ }
81
+
82
+ if (item.element && Array.isArray(item.element.array)) {
83
+ field.children = handleFields(item.element.array);
84
+ }
85
+ return field;
86
+ });
87
+ }
88
+
89
+ // xml type 和 pandora 渲染器对应关系
90
+ let typeMap = {
91
+ text: 'input-text',
92
+ dropdown: 'select',
93
+ date: 'input-date',
94
+ number: 'input-text',
95
+ image: 'input-image',
96
+ file: 'input-file',
97
+ tag: 'input-tag',
98
+ textarea: 'textarea',
99
+ };
100
+
101
+ // schema 缓存
102
+ // TODO. 页面需要存 xml 最好也来一份 因为解析xml也废时间
103
+ const caches = {
104
+ pages: {},
105
+ _getKey(keyData) {
106
+ return (keyData = typeof keyData === 'string' ? keyData : JSON.stringify(keyData));
107
+ },
108
+ add(keyData, pageContent) {
109
+ let cacheId = this._getKey(keyData);
110
+ this.pages[cacheId] = pageContent;
111
+ },
112
+ get(keyData) {
113
+ let cacheId = this._getKey(keyData);
114
+ return this.pages[cacheId];
115
+ },
116
+ };
117
+
118
+ class BaseXML {
119
+ constructor(xml, data) {
120
+ this.data = data;
121
+ this.xmlPath = xml;
122
+ this.schema = null;
123
+ }
124
+
125
+ loadXml(xmlPath) {
126
+ let xmlContent = readFileSync(xmlPath, 'utf8');
127
+ return xmlContent;
128
+ }
129
+
130
+ getXmlData() {
131
+ const xmlContent = this.loadXml(this.xmlPath);
132
+
133
+ let schema = {};
134
+ xmlreader.read(xmlContent, (err, res) => {
135
+ if (err) return logger.error(err);
136
+ let Fields = res.PageView.Fields;
137
+ let Operations = res.PageView.Operations;
138
+ let Table = res.PageView.Table;
139
+ let Form = res.PageView.Form;
140
+
141
+ // 公共的表单信息
142
+ let fields = handleFields(Array.isArray(Fields?.element?.array) ? Fields.element.array : []);
143
+
144
+ // 公共的按钮信息
145
+ let btns = (Array.isArray(Operations?.element?.array) ? Operations.element.array : []).reduce(
146
+ (mo, item) => {
147
+ let { id, ...rest } = item.attributes();
148
+ if (Reflect.has(rest, 'api')) {
149
+ rest.api = tokenize(handleSpecialChars(rest.api), this.data);
150
+ }
151
+ if (Reflect.has(rest, 'api2')) {
152
+ rest.api2 = tokenize(handleSpecialChars(rest.api2), this.data);
153
+ }
154
+
155
+ mo[id] = rest;
156
+ return mo;
157
+ },
158
+ {},
159
+ );
160
+
161
+ let body = [];
162
+
163
+ if (Table) {
164
+ let tableSchame = this.parseTable(Table, fields, btns);
165
+ body.push(tableSchame);
166
+ }
167
+
168
+ if (Form) {
169
+ let formSchame = this.parseForm(Form, fields, btns);
170
+ body.push(formSchame);
171
+ }
172
+ // PROJECT_ID;
173
+ let { title, blockApi, initApi } = res.PageView.attributes();
174
+
175
+ if (title) {
176
+ schema.title = title;
177
+ }
178
+ if (blockApi) {
179
+ schema.blockApi = splitStr(handleSpecialChars(blockApi)).map((it) =>
180
+ tokenize(it, this.data),
181
+ );
182
+ }
183
+ if (initApi) {
184
+ schema.initApi = splitStr(handleSpecialChars(initApi)).map((it) => tokenize(it, this.data));
185
+ }
186
+
187
+ schema.type = 'page';
188
+ schema.body = body;
189
+ });
190
+ this.schema = schema;
191
+ }
192
+
193
+ getXmlFieldsData() {
194
+ const xmlContent = this.loadXml(this.xmlPath);
195
+
196
+ let fields = {};
197
+ xmlreader.read(xmlContent, (err, res) => {
198
+ if (err) return logger.error(err);
199
+ let Fields = res.PageView.Fields;
200
+
201
+ // 公共的表单信息
202
+ (Array.isArray(Fields?.element?.array) ? Fields.element.array : []).forEach((item) => {
203
+ let { id, label, component, source } = item.attributes();
204
+ if (id === 'STAGE_NAME') {
205
+ (Array.isArray(item?.element?.array) ? item?.element?.array : []).forEach((v) => {
206
+ let { id, label, component, source } = v.attributes();
207
+ fields[id] = {
208
+ label,
209
+ component,
210
+ source,
211
+ };
212
+ });
213
+ }
214
+ fields[id] = {
215
+ label,
216
+ component,
217
+ source,
218
+ };
219
+ });
220
+ });
221
+
222
+ return fields;
223
+ }
224
+
225
+ // 解析 表单
226
+ parseForm() {}
227
+
228
+ // 解析 table
229
+ parseTable(Table, fields, btns) {
230
+ let DefaultQuery = Table.DefaultQuery;
231
+ let TableHead = Table.TableHead;
232
+ let TableBody = Table.TableBody;
233
+ let TableFoot = Table.TableFoot;
234
+
235
+ let {
236
+ rowKey, // 行唯一key
237
+ autoFillHeight, // 表格是否一屏显示
238
+ api, // 表格查询接口
239
+ getAllApi, // 表格查询接口 (不分页)
240
+ addApi, // 表格新增接口
241
+ delApi, // 表格删除接口
242
+ editApi, // 表格编辑接口
243
+ saveOrderApi, // 表格排序接口
244
+ columnApi, // 左上角radio列表接口
245
+ addModalSize = '', // 新增modal 弹框大小 xs、sm、md、lg,xl, xxl 有五个档 其中xxl 是 全屏, 默认不填 是 sm
246
+ editModalSize = '', // 编辑modal 弹框大小
247
+ editSingleSave, // 编辑弹框是否单个保存
248
+ placeholder = '', // 单元格占位符
249
+ } = Table.attributes();
250
+ let schemaTable = {
251
+ fields,
252
+ rowKey,
253
+ placeholder,
254
+ autoFillHeight: isbool(autoFillHeight),
255
+ api: tokenize(handleSpecialChars(api), this.data),
256
+ getAllApi: tokenize(handleSpecialChars(getAllApi), this.data),
257
+ addApi: tokenize(handleSpecialChars(addApi), this.data),
258
+ delApi: tokenize(handleSpecialChars(delApi), this.data),
259
+ editApi: tokenize(handleSpecialChars(editApi), this.data),
260
+ saveOrderApi: tokenize(handleSpecialChars(saveOrderApi), this.data),
261
+ columnApi: tokenize(handleSpecialChars(columnApi), this.data),
262
+ addModalSize,
263
+ editModalSize,
264
+ editSingleSave: isbool(editSingleSave),
265
+ };
266
+
267
+ // 默认查询参数
268
+ if (Array.isArray(DefaultQuery?.element?.array) && DefaultQuery.element.array.length) {
269
+ schemaTable.defaultParams = {};
270
+ DefaultQuery.element.array.forEach((item) => {
271
+ let { id, value } = item.attributes();
272
+ schemaTable.defaultParams[id] = value;
273
+ });
274
+ }
275
+
276
+ // 过滤器
277
+ if (TableHead) {
278
+ let filterOptions = { leftAction: {}, rightAction: {} };
279
+ let { isReset, isMode, isContrl, isFilterPlus, isGroup, draggable, defaultGroupKey } =
280
+ TableHead.attributes();
281
+ filterOptions.isFilterPlus = isbool(isFilterPlus);
282
+ filterOptions.draggable = isbool(draggable);
283
+ filterOptions.rightAction.isReset = isbool(isReset);
284
+ filterOptions.rightAction.isMode = isbool(isMode);
285
+ filterOptions.rightAction.isContrl = isbool(isContrl);
286
+ filterOptions.rightAction.isGroup = isbool(isGroup);
287
+ if (defaultGroupKey) {
288
+ filterOptions.rightAction.defaultGroupKey = defaultGroupKey;
289
+ }
290
+
291
+ if (TableHead.filter) {
292
+ let { list } = TableHead.filter.attributes();
293
+ filterOptions.rightAction.filter = splitStr(list);
294
+ }
295
+ if (TableHead.filterPlus && isbool(isFilterPlus)) {
296
+ let { get, add, edit, save } = TableHead.filterPlus.attributes();
297
+ filterOptions.filterPlusApi = {
298
+ get: tokenize(handleSpecialChars(get), this.data),
299
+ add: tokenize(handleSpecialChars(add), this.data),
300
+ edit: tokenize(handleSpecialChars(edit), this.data),
301
+ save: tokenize(handleSpecialChars(save), this.data),
302
+ };
303
+ }
304
+ if (TableHead.mode) {
305
+ filterOptions.rightAction.modeOptions = {};
306
+ let {
307
+ default: defaultMode,
308
+ minTableWidth,
309
+ minDetailWidth,
310
+ detailWidth,
311
+ } = TableHead.mode.attributes();
312
+
313
+ if (defaultMode) {
314
+ filterOptions.rightAction.modeOptions.default = defaultMode;
315
+ }
316
+ if (minTableWidth) {
317
+ filterOptions.rightAction.modeOptions.minTableWidth = +minTableWidth;
318
+ }
319
+ if (minDetailWidth) {
320
+ filterOptions.rightAction.modeOptions.minDetailWidth = +minDetailWidth;
321
+ }
322
+ if (detailWidth) {
323
+ filterOptions.rightAction.modeOptions.detailWidth = +detailWidth;
324
+ }
325
+ }
326
+ if (TableHead.contrl) {
327
+ filterOptions.rightAction.contrlApi = {};
328
+ let { get, save } = TableHead.contrl.attributes();
329
+
330
+ if (get) {
331
+ filterOptions.rightAction.contrlApi.get = tokenize(handleSpecialChars(get), this.data);
332
+ }
333
+ if (save) {
334
+ filterOptions.rightAction.contrlApi.save = tokenize(handleSpecialChars(save), this.data);
335
+ }
336
+ }
337
+ if (TableHead.action) {
338
+ let { components } = TableHead.action.attributes();
339
+ filterOptions.leftAction.action = splitStr(components).reduce((mo, btn) => {
340
+ if (btn in btns) {
341
+ mo.push(btns[btn]);
342
+ }
343
+ return mo;
344
+ }, []);
345
+ }
346
+ if (TableHead.actionMore) {
347
+ let { components } = TableHead.actionMore.attributes();
348
+ filterOptions.leftAction.actionMore = splitStr(components).reduce((mo, btn) => {
349
+ if (btn in btns) {
350
+ mo.push(btns[btn]);
351
+ }
352
+ return mo;
353
+ }, []);
354
+ }
355
+
356
+ schemaTable.filterOptions = filterOptions;
357
+ }
358
+
359
+ // 表格列信息
360
+ let columns = (Array.isArray(TableBody?.column?.array) ? TableBody.column.array : []).map(
361
+ (item) => {
362
+ let { id, label, components, width, toggled, fixed, sortable, filterable, showValue } =
363
+ item.attributes();
364
+ let column = {
365
+ name: id,
366
+ };
367
+ if (label) {
368
+ column.label = label;
369
+ }
370
+ if (width) {
371
+ column.width = +width;
372
+ }
373
+ if (toggled) {
374
+ column.toggled = isbool(toggled);
375
+ }
376
+ if (fixed) {
377
+ column.fixed = fixed;
378
+ }
379
+ if (sortable) {
380
+ column.sortable = isbool(sortable);
381
+ }
382
+ if (filterable) {
383
+ column.filterable = isbool(filterable);
384
+ }
385
+ if (showValue) {
386
+ column.showValue = isbool(showValue);
387
+ }
388
+
389
+ if (id === 'opr' && components) {
390
+ schemaTable.oprBtns = splitStr(components).reduce((mo, btn) => {
391
+ if (btn in btns) {
392
+ mo.push(btns[btn]);
393
+ }
394
+ return mo;
395
+ }, []);
396
+ }
397
+ return column;
398
+ },
399
+ );
400
+
401
+ // 表格底部分页部分
402
+ if (TableFoot) {
403
+ let { show, showPageInput, statistics, placeLeft, placeRight, switchPage } =
404
+ TableFoot.attributes();
405
+ if (show === 'false') {
406
+ schemaTable.pagination = null;
407
+ } else {
408
+ schemaTable.pagination = {
409
+ place: [],
410
+ showPageInput: isbool(showPageInput),
411
+ statistics,
412
+ switchPage: splitStr(switchPage),
413
+ };
414
+ if (placeLeft) {
415
+ schemaTable.pagination.place[0] = splitStr(placeLeft);
416
+ }
417
+ if (placeRight) {
418
+ schemaTable.pagination.place[1] = splitStr(placeRight);
419
+ }
420
+ }
421
+ }
422
+
423
+ schemaTable.type = 'table';
424
+ schemaTable.columns = columns;
425
+
426
+ return schemaTable;
427
+ }
428
+ }
429
+
430
+ class PageEngine extends BaseXML {
431
+ constructor({ json, xml, html }, data) {
432
+ super(xml, data);
433
+ this.jsonPath = json;
434
+ this.htmlPath = html;
435
+ }
436
+
437
+ /**
438
+ * 生成html
439
+ * @param {object} schemaData schema 数据,可以传进来也可以不传
440
+ */
441
+ renderHtml(schemaData = null) {
442
+ let schema = schemaData || this.generateSchema();
443
+
444
+ let tmplContent = readFileSync(this.htmlPath, 'utf8');
445
+
446
+ // TODO. 下面这种约定死的占位 可以优化
447
+ let html = tmplContent.replace('{title}', schema?.title || '');
448
+ let htmlContent = html.replace("'{{schema}}'", JSON.stringify(schema));
449
+
450
+ return htmlContent;
451
+ }
452
+
453
+ // 生成 schema
454
+ generateSchema() {
455
+ let cacheContent = null;
456
+ if ((cacheContent = caches.get(this.data))) return cacheContent;
457
+
458
+ this.getXmlData();
459
+
460
+ cacheContent = require(this.jsonPath);
461
+ if (this.schema) {
462
+ cacheContent = deepMerge(this.schema, cacheContent);
463
+ }
464
+
465
+ caches.add(this.data, cacheContent);
466
+ return cacheContent;
467
+ }
468
+
469
+ /**
470
+ * 获取页面配置 json 不传则返回全部
471
+ * @param {string?} field json指定字段, 不传返回 整个page,传了 就从 tableJson 第一层拿
472
+ */
473
+ renderJson(field) {
474
+ let schema = this.generateSchema();
475
+ if (!field) return schema;
476
+
477
+ let tableJson = schema?.body?.[0] || {};
478
+ return tableJson?.[field] || null;
479
+ }
480
+
481
+ getFields() {
482
+ let fields = this.getXmlFieldsData();
483
+ return fields;
484
+ }
485
+ }
486
+
487
+ module.exports = PageEngine;
@@ -0,0 +1,45 @@
1
+ // 定时任务
2
+ const schedule = require('node-schedule');
3
+ const { schedulelog } = require('./logger');
4
+
5
+ class Schedule {
6
+ tasks = {};
7
+ /**
8
+ * Registers a new task with a given name, cron expression, and callback function.
9
+ *
10
+ * @param {string} name - The name of the task.
11
+ * @param {string} cron - The cron expression for the task's schedule.
12
+ * @param {function} cb - The callback function to be executed when the task runs.
13
+ *
14
+ * @returns {void} - No return value.
15
+ *
16
+ * @example
17
+ * const schedule = require('./schedule');
18
+ * const task = new schedule.Schedule();
19
+ * task.registerTask('myTask', '0 0 * * *', () => {
20
+ * console.log('This task runs every minute');
21
+ * });
22
+ */
23
+ registerTask(name, cron, cb) {
24
+ if (typeof name !== 'string') return console.log('请输入定时器名字');
25
+ if (typeof cron !== 'string') return console.log('请输入定时器规则');
26
+ if (typeof cb !== 'function') return console.log('请输入定时器逻辑');
27
+
28
+ this.tasks[name] = {
29
+ cron,
30
+ cb,
31
+ };
32
+ }
33
+ run() {
34
+ for (const name in this.tasks) {
35
+ schedule.scheduleJob(this.tasks[name].cron, () => {
36
+ schedulelog.debug(`执行job: ${name}`);
37
+ this.tasks[name].cb(() => {
38
+ schedulelog.debug(`${name} 执行完成`);
39
+ });
40
+ });
41
+ }
42
+ }
43
+ }
44
+
45
+ module.exports = new Schedule();
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+ const { v4: uuidv4 } = require('uuid');
3
+ const { sqlExecutor, queryBaseSqlBuilder, insertSqlBuilder } = require('@gingkoo/node-tools');
4
+ const { logger } = require('../logger/index');
5
+ const entity = require('../entity');
6
+
7
+ const dataCombineService = {
8
+ fetchFields: [
9
+ 'DATA_ID', // 数据 id
10
+ 'MODULE_ID', // 模块 id
11
+ ],
12
+
13
+ createFields: [
14
+ 'DATA_LINK_ID', //关联 id, 逗号分割
15
+ 'MODULE_ID', //模块
16
+ 'DATA_DESC', //描述
17
+ 'EXPIRED_TIME', //过期时间
18
+ ],
19
+
20
+ async fetch(_where) {
21
+ let where = {};
22
+ this.fetchFields.forEach((field) => {
23
+ if (Reflect.has(_where, field)) {
24
+ where[field] = _where[field];
25
+ }
26
+ });
27
+
28
+ let filter = {
29
+ ORG_ID: global.orgid,
30
+ STATUS: '00',
31
+ ...where,
32
+ };
33
+ let { sql, params } = await queryBaseSqlBuilder(entity.DATA_COMBINE, filter);
34
+ let { error, results } = await sqlExecutor(sql + ' order by DATA_CRT_TIME desc', params);
35
+ if (error) {
36
+ logger.error('查询数据失败', error);
37
+ }
38
+
39
+ return [error, results];
40
+ },
41
+
42
+ async create(_props) {
43
+ let props = {};
44
+ this.createFields.forEach((field) => {
45
+ if (Reflect.has(_props, field)) {
46
+ props[field] = _props[field];
47
+ }
48
+ });
49
+
50
+ let DATA_ID = uuidv4();
51
+ props = {
52
+ ORG_ID: global.orgid,
53
+ STATUS: '00',
54
+ DATA_ID,
55
+ ...props,
56
+ };
57
+
58
+ let { sql, params } = insertSqlBuilder(entity.DATA_COMBINE, props);
59
+ let { error } = await sqlExecutor(sql, params);
60
+ if (error) {
61
+ logger.error('配置数据记录失败', error);
62
+ }
63
+ return [error, DATA_ID];
64
+ },
65
+ };
66
+
67
+ module.exports = dataCombineService;
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+ const { sqlExecutor, insertSqlBuilder } = require('@gingkoo/node-tools');
3
+ const { logger } = require('../logger/index');
4
+ const entity = require('../entity');
5
+
6
+ const deptService = {
7
+ // 获取部门列表
8
+ async getDepts() {
9
+ let { error, results } = await sqlExecutor(
10
+ `select DEPT_NO, DEPT_NAME FROM ${entity.IDM_DEPTS} where ORG_ID=?`,
11
+ [global.orgid],
12
+ );
13
+
14
+ if (error || !results.length) {
15
+ error && logger.error(error);
16
+ return null;
17
+ }
18
+
19
+ return results;
20
+ },
21
+
22
+ // 添加部门
23
+ async createDept(params) {
24
+ if (!params.DEPT_NO) return null;
25
+ !params.ORG_ID && (params['ORG_ID'] = global.orgid);
26
+ let sqlResult = insertSqlBuilder(entity.IDM_DEPTS, params);
27
+ let dbResult = await sqlExecutor(sqlResult.sql, sqlResult.params);
28
+ if (dbResult.error) {
29
+ logger.error(dbResult.error);
30
+ return null;
31
+ }
32
+
33
+ return true;
34
+ },
35
+ };
36
+
37
+ module.exports = deptService;