midway-fatcms 0.0.5 → 0.0.7

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 (132) hide show
  1. package/.qoder/skills/midway-fatcms-crud/SKILL.md +375 -0
  2. package/.qoder/skills/midway-fatcms-crud/examples.md +990 -0
  3. package/.qoder/skills/midway-fatcms-crud/reference.md +568 -0
  4. package/README.md +377 -134
  5. package/dist/controller/manage/CrudStandardDesignApi.d.ts +0 -2
  6. package/dist/controller/manage/CrudStandardDesignApi.js +11 -85
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +2 -0
  9. package/dist/libs/crud-pro/CrudPro.d.ts +9 -1
  10. package/dist/libs/crud-pro/CrudPro.js +15 -0
  11. package/dist/libs/crud-pro/README.md +809 -0
  12. package/dist/libs/crud-pro/README_FUNC.md +193 -0
  13. package/dist/libs/crud-pro/exceptions.d.ts +2 -0
  14. package/dist/libs/crud-pro/exceptions.js +2 -0
  15. package/dist/libs/crud-pro/interfaces.d.ts +34 -1
  16. package/dist/libs/crud-pro/models/ExecuteContext.d.ts +3 -3
  17. package/dist/libs/crud-pro/models/ExecuteContext.js +2 -0
  18. package/dist/libs/crud-pro/models/RequestModel.d.ts +6 -2
  19. package/dist/libs/crud-pro/models/RequestModel.js +20 -53
  20. package/dist/libs/crud-pro/models/ResModel.d.ts +6 -4
  21. package/dist/libs/crud-pro/models/ServiceHub.d.ts +1 -0
  22. package/dist/libs/crud-pro/models/keys.d.ts +6 -1
  23. package/dist/libs/crud-pro/models/keys.js +5 -0
  24. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.d.ts +52 -0
  25. package/dist/libs/crud-pro/services/CrudProDataTypeConvertService.js +158 -0
  26. package/dist/libs/crud-pro/services/CrudProExecuteSqlService.js +20 -1
  27. package/dist/libs/crud-pro/services/CrudProFieldValidateService.d.ts +7 -0
  28. package/dist/libs/crud-pro/services/CrudProFieldValidateService.js +32 -0
  29. package/dist/libs/crud-pro/services/CrudProGenSqlService.d.ts +13 -0
  30. package/dist/libs/crud-pro/services/CrudProGenSqlService.js +44 -7
  31. package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.d.ts +43 -0
  32. package/dist/libs/crud-pro/services/CrudProOriginToExecuteSql.js +132 -1
  33. package/dist/libs/crud-pro/services/CrudProTableMetaService.d.ts +15 -1
  34. package/dist/libs/crud-pro/services/CrudProTableMetaService.js +107 -0
  35. package/dist/libs/crud-pro/services/CurdProServiceHub.d.ts +5 -1
  36. package/dist/libs/crud-pro/services/CurdProServiceHub.js +11 -0
  37. package/dist/libs/crud-pro/utils/DateTimeUtils.d.ts +1 -0
  38. package/dist/libs/crud-pro/utils/DateTimeUtils.js +3 -0
  39. package/dist/libs/crud-pro/utils/MixinUtils.d.ts +32 -0
  40. package/dist/libs/crud-pro/utils/MixinUtils.js +85 -1
  41. package/dist/libs/crud-pro/utils/OrderByUtils.d.ts +70 -0
  42. package/dist/libs/crud-pro/utils/OrderByUtils.js +158 -0
  43. package/dist/libs/crud-pro/utils/ValidateUtils.js +1 -1
  44. package/dist/libs/crud-sharding/ROUTING_LOGIC.md +944 -0
  45. package/dist/libs/crud-sharding/ShardingConfig.d.ts +218 -0
  46. package/dist/libs/crud-sharding/ShardingConfig.js +32 -0
  47. package/dist/libs/crud-sharding/ShardingCountCache.d.ts +69 -0
  48. package/dist/libs/crud-sharding/ShardingCountCache.js +160 -0
  49. package/dist/libs/crud-sharding/ShardingCrudPro.d.ts +363 -0
  50. package/dist/libs/crud-sharding/ShardingCrudPro.js +675 -0
  51. package/dist/libs/crud-sharding/ShardingMerger.d.ts +130 -0
  52. package/dist/libs/crud-sharding/ShardingMerger.js +282 -0
  53. package/dist/libs/crud-sharding/ShardingRouter.d.ts +69 -0
  54. package/dist/libs/crud-sharding/ShardingRouter.js +377 -0
  55. package/dist/libs/crud-sharding/ShardingTableCreator.d.ts +146 -0
  56. package/dist/libs/crud-sharding/ShardingTableCreator.js +805 -0
  57. package/dist/libs/crud-sharding/ShardingUtils.d.ts +38 -0
  58. package/dist/libs/crud-sharding/ShardingUtils.js +77 -0
  59. package/dist/libs/crud-sharding/index.d.ts +45 -0
  60. package/dist/libs/crud-sharding/index.js +55 -0
  61. package/dist/models/StandardColumns.d.ts +71 -0
  62. package/dist/models/StandardColumns.js +28 -0
  63. package/dist/service/SysAppService.js +2 -2
  64. package/dist/service/SysConfigService.js +1 -1
  65. package/dist/service/SysDictDataService.js +2 -2
  66. package/dist/service/SysMenuService.js +1 -1
  67. package/dist/service/UserAccountService.d.ts +1 -1
  68. package/dist/service/crudstd/CrudStdService.d.ts +0 -1
  69. package/dist/service/crudstd/CrudStdService.js +0 -27
  70. package/dist/service/curd/CrudProQuick.d.ts +134 -4
  71. package/dist/service/curd/CrudProQuick.js +155 -3
  72. package/dist/service/curd/CurdMixService.d.ts +2 -1
  73. package/dist/service/curd/CurdMixService.js +5 -1
  74. package/dist/service/curd/CurdProService.d.ts +44 -2
  75. package/dist/service/curd/CurdProService.js +53 -1
  76. package/dist/service/curd/README.md +1100 -0
  77. package/dist/service/curd/fixSoftDelete.d.ts +14 -0
  78. package/dist/service/curd/fixSoftDelete.js +29 -11
  79. package/dist/service/flow/FlowConfigService.js +1 -1
  80. package/dist/service/flow/FlowInstanceCrudService.js +1 -1
  81. package/package.json +4 -1
  82. package/src/controller/gateway/AsyncTaskController.ts +1 -1
  83. package/src/controller/manage/CrudStandardDesignApi.ts +16 -100
  84. package/src/index.ts +3 -0
  85. package/src/libs/crud-pro/CrudPro.ts +19 -1
  86. package/src/libs/crud-pro/README.md +809 -0
  87. package/src/libs/crud-pro/README_FUNC.md +193 -0
  88. package/src/libs/crud-pro/exceptions.ts +2 -0
  89. package/src/libs/crud-pro/interfaces.ts +38 -1
  90. package/src/libs/crud-pro/models/ExecuteContext.ts +6 -3
  91. package/src/libs/crud-pro/models/RequestModel.ts +23 -65
  92. package/src/libs/crud-pro/models/ResModel.ts +10 -4
  93. package/src/libs/crud-pro/models/ServiceHub.ts +2 -0
  94. package/src/libs/crud-pro/models/keys.ts +5 -0
  95. package/src/libs/crud-pro/services/CrudProDataTypeConvertService.ts +171 -0
  96. package/src/libs/crud-pro/services/CrudProExecuteSqlService.ts +24 -1
  97. package/src/libs/crud-pro/services/CrudProFieldValidateService.ts +53 -1
  98. package/src/libs/crud-pro/services/CrudProGenSqlService.ts +51 -7
  99. package/src/libs/crud-pro/services/CrudProOriginToExecuteSql.ts +159 -2
  100. package/src/libs/crud-pro/services/CrudProTableMetaService.ts +139 -1
  101. package/src/libs/crud-pro/services/CurdProServiceHub.ts +16 -1
  102. package/src/libs/crud-pro/utils/DateTimeUtils.ts +3 -0
  103. package/src/libs/crud-pro/utils/MixinUtils.ts +97 -1
  104. package/src/libs/crud-pro/utils/OrderByUtils.ts +169 -0
  105. package/src/libs/crud-pro/utils/ValidateUtils.ts +1 -1
  106. package/src/libs/crud-sharding/ROUTING_LOGIC.md +944 -0
  107. package/src/libs/crud-sharding/ShardingConfig.ts +240 -0
  108. package/src/libs/crud-sharding/ShardingCountCache.ts +200 -0
  109. package/src/libs/crud-sharding/ShardingCrudPro.ts +835 -0
  110. package/src/libs/crud-sharding/ShardingMerger.ts +384 -0
  111. package/src/libs/crud-sharding/ShardingRouter.ts +512 -0
  112. package/src/libs/crud-sharding/ShardingTableCreator.ts +1007 -0
  113. package/src/libs/crud-sharding/ShardingUtils.ts +84 -0
  114. package/src/libs/crud-sharding/index.ts +64 -0
  115. package/src/models/StandardColumns.ts +76 -0
  116. package/src/service/FileCenterService.ts +1 -1
  117. package/src/service/SysAppService.ts +2 -2
  118. package/src/service/SysConfigService.ts +1 -1
  119. package/src/service/SysDictDataService.ts +2 -2
  120. package/src/service/SysMenuService.ts +2 -2
  121. package/src/service/WorkbenchService.ts +1 -1
  122. package/src/service/anyapi/AnyApiService.ts +1 -1
  123. package/src/service/asyncTask/AsyncTaskRunnerService.ts +1 -1
  124. package/src/service/crudstd/CrudStdService.ts +0 -32
  125. package/src/service/curd/CrudProQuick.ts +164 -5
  126. package/src/service/curd/CurdMixService.ts +7 -2
  127. package/src/service/curd/CurdProService.ts +62 -3
  128. package/src/service/curd/README.md +1100 -0
  129. package/src/service/curd/fixCfgModel.ts +1 -2
  130. package/src/service/curd/fixSoftDelete.ts +38 -16
  131. package/src/service/flow/FlowConfigService.ts +1 -1
  132. package/src/service/flow/FlowInstanceCrudService.ts +1 -1
@@ -7,6 +7,7 @@ const keys_1 = require("../models/keys");
7
7
  class CrudProTableMetaCache {
8
8
  constructor() {
9
9
  this.cacheMap = {};
10
+ this.tableInfoCacheMap = {};
10
11
  }
11
12
  getCacheKey(query) {
12
13
  return `${query.sqlDatabase}:::${query.sqlSchema}:::${query.sqlDbType}:::${query.sqlTable}`;
@@ -23,6 +24,19 @@ class CrudProTableMetaCache {
23
24
  const cacheKey = this.getCacheKey(query);
24
25
  this.cacheMap[cacheKey] = metaObj;
25
26
  }
27
+ getTableInfos(cacheKey) {
28
+ const cached = this.tableInfoCacheMap[cacheKey];
29
+ if (cached && cached.expiredTime > Date.now()) {
30
+ return cached.tableInfos;
31
+ }
32
+ return null;
33
+ }
34
+ setTableInfos(cacheKey, tableInfos, cacheTime) {
35
+ this.tableInfoCacheMap[cacheKey] = {
36
+ tableInfos,
37
+ expiredTime: Date.now() + cacheTime,
38
+ };
39
+ }
26
40
  }
27
41
  const metaCache = new CrudProTableMetaCache();
28
42
  class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
@@ -34,6 +48,99 @@ class CrudProTableMetaService extends CrudProServiceBase_1.CrudProServiceBase {
34
48
  }
35
49
  return obj;
36
50
  }
51
+ // ============ 获取表和视图信息(带类型) ============
52
+ /**
53
+ * 获取数据库所有表和视图信息(包含类型)
54
+ *
55
+ * @param query 数据库查询参数
56
+ * @param options 缓存控制选项
57
+ * - skipCache: 跳过缓存,直接查询数据库
58
+ * - refreshCache: 查询后更新缓存
59
+ */
60
+ async getAllTableInfos(query, options = {}) {
61
+ const { skipCache = false, refreshCache = false } = options;
62
+ // 1. 尝试从缓存获取(使用独立的缓存key)
63
+ const cacheKey = this.getTableInfoCacheKey(query);
64
+ if (!skipCache) {
65
+ const cached = metaCache.getTableInfos(cacheKey);
66
+ if (cached) {
67
+ return cached;
68
+ }
69
+ }
70
+ // 2. 查询数据库
71
+ const result = await this.loadAllTableInfos(query);
72
+ // 3. 更新缓存
73
+ if (!skipCache || refreshCache) {
74
+ const { tableMetaCacheTime } = this.getContextCfg();
75
+ metaCache.setTableInfos(cacheKey, result, tableMetaCacheTime || 3600000);
76
+ }
77
+ return result;
78
+ }
79
+ getTableInfoCacheKey(query) {
80
+ return `tableInfo:::${query.sqlDatabase}:::${query.sqlSchema || 'public'}:::${query.sqlDbType}`;
81
+ }
82
+ async loadAllTableInfos(query) {
83
+ const { sqlDbType } = query;
84
+ if (sqlDbType === keys_1.SqlDbType.mysql) {
85
+ return this.loadMySQLAllTableInfos(query);
86
+ }
87
+ else if (sqlDbType === keys_1.SqlDbType.postgres) {
88
+ return this.loadPostgreSQLAllTableInfos(query);
89
+ }
90
+ else if (sqlDbType === keys_1.SqlDbType.sqlserver) {
91
+ return this.loadSQLServerAllTableInfos(query);
92
+ }
93
+ throw new Error('暂不支持的数据库类型:' + sqlDbType);
94
+ }
95
+ async loadMySQLAllTableInfos(query) {
96
+ const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType };
97
+ // 获取物理表
98
+ const tableSql = `SHOW TABLES FROM ${query.sqlDatabase}`;
99
+ const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
100
+ const tableNames = (tableRes.rows || []).map((row) => Object.values(row)[0]);
101
+ // 获取视图
102
+ const viewSql = `SHOW FULL TABLES FROM ${query.sqlDatabase} WHERE Table_type = 'VIEW'`;
103
+ const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
104
+ const viewNames = (viewRes.rows || []).map((row) => Object.values(row)[0]);
105
+ const tables = [
106
+ ...tableNames.map(name => ({ name, tableType: 'table' })),
107
+ ...viewNames.map(name => ({ name, tableType: 'view' })),
108
+ ];
109
+ return { tables, tableNames, viewNames };
110
+ }
111
+ async loadPostgreSQLAllTableInfos(query) {
112
+ const schema = query.sqlSchema || 'public';
113
+ const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType };
114
+ // 获取物理表
115
+ const tableSql = `SELECT tablename FROM pg_tables WHERE schemaname = '${schema}' ORDER BY tablename`;
116
+ const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
117
+ const tableNames = (tableRes.rows || []).map((row) => row.tablename);
118
+ // 获取视图
119
+ const viewSql = `SELECT viewname FROM pg_views WHERE schemaname = '${schema}' ORDER BY viewname`;
120
+ const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
121
+ const viewNames = (viewRes.rows || []).map((row) => row.viewname);
122
+ const tables = [
123
+ ...tableNames.map(name => ({ name, tableType: 'table' })),
124
+ ...viewNames.map(name => ({ name, tableType: 'view' })),
125
+ ];
126
+ return { tables, tableNames, viewNames };
127
+ }
128
+ async loadSQLServerAllTableInfos(query) {
129
+ const dbUtil = { sqlDatabase: query.sqlDatabase, sqlDbType: query.sqlDbType };
130
+ // 获取物理表
131
+ const tableSql = `SELECT name FROM sys.tables`;
132
+ const tableRes = await this.executeUnsafeQuery(dbUtil, tableSql);
133
+ const tableNames = (tableRes.rows || []).map((row) => row.name);
134
+ // 获取视图
135
+ const viewSql = `SELECT name FROM sys.views`;
136
+ const viewRes = await this.executeUnsafeQuery(dbUtil, viewSql);
137
+ const viewNames = (viewRes.rows || []).map((row) => row.name);
138
+ const tables = [
139
+ ...tableNames.map(name => ({ name, tableType: 'table' })),
140
+ ...viewNames.map(name => ({ name, tableType: 'view' })),
141
+ ];
142
+ return { tables, tableNames, viewNames };
143
+ }
37
144
  async loadTableMeta(query) {
38
145
  const { tableMetaCacheTime } = this.getContextCfg();
39
146
  const obj = {
@@ -1,4 +1,4 @@
1
- import { IFuncCfgModel, IRequestCfgModel, ISqlCfgModel, ITableMeta, ITableMetaQuery } from '../interfaces';
1
+ import { IFuncCfgModel, IRequestCfgModel, ISqlCfgModel, ITableListResult, ITableMeta, ITableMetaQuery, ITableNamesOptions, ITableNamesQuery } from '../interfaces';
2
2
  import { RequestModel } from '../models/RequestModel';
3
3
  import { ExecuteContext } from '../models/ExecuteContext';
4
4
  import { SqlCfgModel } from '../models/SqlCfgModel';
@@ -16,11 +16,13 @@ declare class CurdProServiceHub implements ICurdProServiceHub {
16
16
  private readonly executeFuncService;
17
17
  private readonly tableMetaService;
18
18
  private readonly dataFilterService;
19
+ private readonly dataTypeConvertService;
19
20
  constructor(executeContext: ExecuteContext);
20
21
  getExecuteContext(): ExecuteContext;
21
22
  validateByAllow(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
22
23
  validateByReject(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
23
24
  validateByCfg(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
25
+ validateDataType(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
24
26
  validateByAuthCfg(cfgModel: RequestCfgModel, reqModel: RequestModel): Promise<any>;
25
27
  updateByCfg(cfgModel: RequestCfgModel, reqModel: RequestModel): void;
26
28
  getCachedCfgByMethod(method: string, isEnableCache: boolean): Promise<IRequestCfgModel>;
@@ -29,6 +31,8 @@ declare class CurdProServiceHub implements ICurdProServiceHub {
29
31
  convertOriginToExecuteSql(sqlCfgModel: SqlCfgModel): Promise<void>;
30
32
  executeFuncCfg(funCfg: IFuncCfgModel, funcContext: FuncContext): any;
31
33
  getTableMeta(query: ITableMetaQuery): Promise<ITableMeta>;
34
+ getAllTableInfos(query: ITableNamesQuery, options?: ITableNamesOptions): Promise<ITableListResult>;
32
35
  filterDataByTableMeta(data: Record<string, any>, sqlCfgModel: ISqlCfgModel | SqlCfgModel): Promise<Record<string, any>>;
36
+ convertDataTypeByTableMeta(reqModel: RequestModel, cfgModel: RequestCfgModel): Promise<void>;
33
37
  }
34
38
  export { CurdProServiceHub };
@@ -10,6 +10,7 @@ const CrudProOriginToExecuteSql_1 = require("./CrudProOriginToExecuteSql");
10
10
  const CrudProExecuteFuncService_1 = require("./CrudProExecuteFuncService");
11
11
  const CrudProTableMetaService_1 = require("./CrudProTableMetaService");
12
12
  const CrudProDataFilterService_1 = require("./CrudProDataFilterService");
13
+ const CrudProDataTypeConvertService_1 = require("./CrudProDataTypeConvertService");
13
14
  class CurdProServiceHub {
14
15
  constructor(executeContext) {
15
16
  this.executeContext = executeContext;
@@ -22,6 +23,7 @@ class CurdProServiceHub {
22
23
  this.executeFuncService = new CrudProExecuteFuncService_1.CrudProExecuteFuncService(this);
23
24
  this.tableMetaService = new CrudProTableMetaService_1.CrudProTableMetaService(this);
24
25
  this.dataFilterService = new CrudProDataFilterService_1.CrudProDataFilterService(this);
26
+ this.dataTypeConvertService = new CrudProDataTypeConvertService_1.CrudProDataTypeConvertService(this);
25
27
  }
26
28
  getExecuteContext() {
27
29
  return this.executeContext;
@@ -35,6 +37,9 @@ class CurdProServiceHub {
35
37
  validateByCfg(cfgModel, reqModel) {
36
38
  return this.fieldValidateService.validateByCfg(cfgModel, reqModel);
37
39
  }
40
+ validateDataType(cfgModel, reqModel) {
41
+ return this.fieldValidateService.validateDataType(cfgModel, reqModel);
42
+ }
38
43
  async validateByAuthCfg(cfgModel, reqModel) {
39
44
  const contextFunc = this.executeContext.contextFunc;
40
45
  if (contextFunc && contextFunc.validateByAuthCfg) {
@@ -62,8 +67,14 @@ class CurdProServiceHub {
62
67
  async getTableMeta(query) {
63
68
  return await this.tableMetaService.getTableMeta(query);
64
69
  }
70
+ async getAllTableInfos(query, options) {
71
+ return await this.tableMetaService.getAllTableInfos(query, options);
72
+ }
65
73
  async filterDataByTableMeta(data, sqlCfgModel) {
66
74
  return await this.dataFilterService.filterDataByTableMeta(data, sqlCfgModel);
67
75
  }
76
+ async convertDataTypeByTableMeta(reqModel, cfgModel) {
77
+ return await this.dataTypeConvertService.convertDataTypeByTableMeta(reqModel, cfgModel);
78
+ }
68
79
  }
69
80
  exports.CurdProServiceHub = CurdProServiceHub;
@@ -1,5 +1,6 @@
1
1
  declare const DateTimeUtils: {
2
2
  getCurrentTimeStampMs(): number;
3
+ getCurrentTimeString(): string;
3
4
  getCurrentTimeStampSecond(): number;
4
5
  getCurrentDateFormat(format: string): string;
5
6
  getYesterdayDateFormat(format: string): string;
@@ -7,6 +7,9 @@ const DateTimeUtils = {
7
7
  getCurrentTimeStampMs() {
8
8
  return Date.now();
9
9
  },
10
+ getCurrentTimeString() {
11
+ return moment().format('YYYY-MM-DD HH:mm:ss');
12
+ },
10
13
  // 以秒为单位的时间戳
11
14
  getCurrentTimeStampSecond() {
12
15
  return Math.floor(Date.now() / 1000);
@@ -1,3 +1,20 @@
1
+ /**
2
+ * 生成雪花ID(64位整数,JS 中以 string 返回)
3
+ * 结构:1位符号 + 41位时间戳(ms) + 10位机器ID + 12位序列号
4
+ * - 时间戳:相对于 2024-01-01 00:00:00 的毫秒偏移,可用约 69 年
5
+ * - 机器ID:0-1023,优先取参数 > 环境变量 SNOWFLAKE_MACHINE_ID > process.pid 低10位
6
+ * - 序列号:0-4095,同一毫秒内自增
7
+ *
8
+ * @param machineId 机器ID(0-1023),不传则依次从环境变量 SNOWFLAKE_MACHINE_ID、process.pid 获取
9
+ * @returns 雪花ID字符串
10
+ */
11
+ declare function generateSnowflakeId(machineId?: number): string;
12
+ /**
13
+ * 生成 UUID v4 字符串(纯随机,无外部依赖)
14
+ * 格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,其中 y 为 8/9/a/b 之一
15
+ * @returns 符合 RFC 4122 v4 规范的 UUID 字符串
16
+ */
17
+ declare function uuid(): string;
1
18
  declare const MixinUtils: {
2
19
  isNil(obj: any): boolean;
3
20
  isNotNil(obj: any): boolean;
@@ -36,5 +53,20 @@ declare const MixinUtils: {
36
53
  selectNotEmpty(a: any, b: any): any;
37
54
  parseValueByType(value: any, targetType: string): any;
38
55
  sleepMs(ms: number): Promise<any>;
56
+ /**
57
+ * 生成 UUID v4 字符串(纯随机,无外部依赖)
58
+ * 格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,其中 y 为 8/9/a/b 之一
59
+ * @returns 符合 RFC 4122 v4 规范的 UUID 字符串
60
+ */
61
+ uuid: typeof uuid;
62
+ /**
63
+ * 生成雪花ID(64位整数,以字符串返回)
64
+ * 结构:1位符号 + 41位时间戳(ms) + 10位机器ID + 12位序列号
65
+ * 同一毫秒内可生成 4096 个不重复ID,单机每秒可生成约 400 万个
66
+ *
67
+ * @param machineId 机器ID(0-1023),不传则依次从环境变量 SNOWFLAKE_MACHINE_ID、process.pid 获取
68
+ * @returns 雪花ID字符串
69
+ */
70
+ generateSnowflakeId: typeof generateSnowflakeId;
39
71
  };
40
72
  export { MixinUtils };
@@ -4,6 +4,75 @@ exports.MixinUtils = void 0;
4
4
  const exceptions_1 = require("../exceptions");
5
5
  // 至少需要2个字符
6
6
  const FIELD_NAME_REG_EXP = /^[a-zA-Z][0-9a-zA-Z_]+$/;
7
+ // ===================== 雪花ID =====================
8
+ let snowflakeSeq = 0;
9
+ let snowflakeLastTs = -1;
10
+ /**
11
+ * 生成雪花ID(64位整数,JS 中以 string 返回)
12
+ * 结构:1位符号 + 41位时间戳(ms) + 10位机器ID + 12位序列号
13
+ * - 时间戳:相对于 2024-01-01 00:00:00 的毫秒偏移,可用约 69 年
14
+ * - 机器ID:0-1023,优先取参数 > 环境变量 SNOWFLAKE_MACHINE_ID > process.pid 低10位
15
+ * - 序列号:0-4095,同一毫秒内自增
16
+ *
17
+ * @param machineId 机器ID(0-1023),不传则依次从环境变量 SNOWFLAKE_MACHINE_ID、process.pid 获取
18
+ * @returns 雪花ID字符串
19
+ */
20
+ function generateSnowflakeId(machineId) {
21
+ const EPOCH = 1704067200000; // 2024-01-01 00:00:00 UTC
22
+ let mid = machineId;
23
+ if (mid === undefined || mid === null) {
24
+ // 优先从环境变量获取,适配多实例部署场景
25
+ const envMid = typeof process !== 'undefined' && process.env && process.env.SNOWFLAKE_MACHINE_ID
26
+ ? parseInt(process.env.SNOWFLAKE_MACHINE_ID, 10)
27
+ : NaN;
28
+ mid = !isNaN(envMid) ? envMid : (typeof process !== 'undefined' && process.pid ? process.pid & 0x3ff : 0);
29
+ }
30
+ mid = mid & 0x3ff; // 确保在 0-1023 范围
31
+ let now = Date.now();
32
+ let ts = now - EPOCH;
33
+ if (ts === snowflakeLastTs) {
34
+ snowflakeSeq = (snowflakeSeq + 1) & 0xfff; // 0-4095 循环
35
+ if (snowflakeSeq === 0) {
36
+ // 本毫秒序列号耗尽,等待下一毫秒
37
+ while (now <= Date.now()) {
38
+ now = Date.now();
39
+ }
40
+ ts = now - EPOCH;
41
+ }
42
+ }
43
+ else {
44
+ snowflakeSeq = 0;
45
+ }
46
+ snowflakeLastTs = ts;
47
+ // 雪花ID超过JS安全整数范围(2^53),不能用Number位运算,采用字符串拼接
48
+ const tsPart = ts * 4194304; // ts << 22,等价于 ts * 2^22
49
+ const midPart = mid * 4096; // mid << 12,等价于 mid * 2^12
50
+ return '' + (tsPart + midPart + snowflakeSeq);
51
+ }
52
+ /**
53
+ * 生成 UUID v4 字符串(纯随机,无外部依赖)
54
+ * 格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,其中 y 为 8/9/a/b 之一
55
+ * @returns 符合 RFC 4122 v4 规范的 UUID 字符串
56
+ */
57
+ function uuid() {
58
+ // crypto.getRandomValues 在 Node.js >= 11 和现代浏览器中可用
59
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
60
+ const bytes = new Uint8Array(16);
61
+ crypto.getRandomValues(bytes);
62
+ // 版本位:第6字节高4位设为 0100 (v4)
63
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
64
+ // 变体位:第8字节高2位设为 10
65
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
66
+ const hex = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
67
+ return hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-' + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20);
68
+ }
69
+ // 降级方案:Math.random
70
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
71
+ const r = Math.random() * 16 | 0;
72
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
73
+ return v.toString(16);
74
+ });
75
+ }
7
76
  const MixinUtils = {
8
77
  isNil(obj) {
9
78
  return typeof obj === 'undefined' || obj === null;
@@ -254,6 +323,21 @@ const MixinUtils = {
254
323
  },
255
324
  sleepMs(ms) {
256
325
  return new Promise(resolve => setTimeout(resolve, ms));
257
- }
326
+ },
327
+ /**
328
+ * 生成 UUID v4 字符串(纯随机,无外部依赖)
329
+ * 格式:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,其中 y 为 8/9/a/b 之一
330
+ * @returns 符合 RFC 4122 v4 规范的 UUID 字符串
331
+ */
332
+ uuid: uuid,
333
+ /**
334
+ * 生成雪花ID(64位整数,以字符串返回)
335
+ * 结构:1位符号 + 41位时间戳(ms) + 10位机器ID + 12位序列号
336
+ * 同一毫秒内可生成 4096 个不重复ID,单机每秒可生成约 400 万个
337
+ *
338
+ * @param machineId 机器ID(0-1023),不传则依次从环境变量 SNOWFLAKE_MACHINE_ID、process.pid 获取
339
+ * @returns 雪花ID字符串
340
+ */
341
+ generateSnowflakeId: generateSnowflakeId,
258
342
  };
259
343
  exports.MixinUtils = MixinUtils;
@@ -0,0 +1,70 @@
1
+ import { IOrderByItem } from '../interfaces';
2
+ /**
3
+ * OrderBy 解析工具类
4
+ *
5
+ * 提供统一的 orderBy 参数解析功能,支持多种格式:
6
+ * 1. 字符串格式(逗号分隔多个字段):
7
+ * - 标准 SQL 格式:'created_at DESC, amount ASC'
8
+ * - 简写 +/- 格式:'created_at-, amount+'('-' 表示 DESC,'+' 或省略表示 ASC)
9
+ * - 默认升序:'order_id'(无后缀时默认为 ASC)
10
+ *
11
+ * 2. 数组格式:
12
+ * - 纯对象数组:[{ fieldName: 'created_at', orderType: 'desc' }]
13
+ * - 混合数组(字符串+对象):['order_id+', { fieldName: 'amount', orderType: 'asc' }]
14
+ *
15
+ * SQL 注入防护:
16
+ * - 所有字段名必须通过 MixinUtils.isValidFieldName() 校验
17
+ * - 只允许 ASC/DESC 作为排序方向
18
+ */
19
+ export declare class OrderByUtils {
20
+ /**
21
+ * 解析 orderBy 参数为 IOrderByItem 数组
22
+ *
23
+ * @param orderByStr 排序参数,可以是字符串或数组
24
+ * @returns IOrderByItem[] 解析后的排序项数组
25
+ */
26
+ static parseOrderBys(orderByStr: any): IOrderByItem[];
27
+ /**
28
+ * 获取第一个排序项
29
+ *
30
+ * @param orderByStr 排序参数
31
+ * @returns IOrderByItem | null 第一个排序项,无则返回 null
32
+ */
33
+ static getFirstOrderBy(orderByStr: any): IOrderByItem | null;
34
+ /**
35
+ * 判断是否为 ASC 升序排序
36
+ *
37
+ * @param orderByStr 排序参数
38
+ * @returns true 表示 ASC,false 表示 DESC 或无排序
39
+ */
40
+ static isFirstOrderByAsc(orderByStr: any): boolean;
41
+ /**
42
+ * 判断是否为 DESC 降序排序
43
+ *
44
+ * @param orderByStr 排序参数
45
+ * @returns true 表示 DESC,false 表示 ASC 或无排序
46
+ */
47
+ static isFirstOrderByDesc(orderByStr: any): boolean;
48
+ /**
49
+ * 判断排序字段是否匹配指定的时间字段
50
+ *
51
+ * 用于分表查询时校验排序字段是否为分表字段
52
+ *
53
+ * @param orderByStr 排序参数
54
+ * @param timeColumn 时间字段名
55
+ * @returns true 表示匹配,false 表示不匹配或无排序
56
+ */
57
+ static isOrderByTimeColumn(orderByStr: any, timeColumn: string): boolean;
58
+ /**
59
+ * 解析单个排序项(数组元素)
60
+ */
61
+ private static parseOrderByItem;
62
+ /**
63
+ * 解析字符串格式的排序项(支持标准 SQL 和简写格式)
64
+ */
65
+ private static parseOrderByString;
66
+ /**
67
+ * SQL 注入防护:校验字段名格式
68
+ */
69
+ private static validateFieldName;
70
+ }
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OrderByUtils = void 0;
4
+ const MixinUtils_1 = require("./MixinUtils");
5
+ const exceptions_1 = require("../exceptions");
6
+ /**
7
+ * OrderBy 解析工具类
8
+ *
9
+ * 提供统一的 orderBy 参数解析功能,支持多种格式:
10
+ * 1. 字符串格式(逗号分隔多个字段):
11
+ * - 标准 SQL 格式:'created_at DESC, amount ASC'
12
+ * - 简写 +/- 格式:'created_at-, amount+'('-' 表示 DESC,'+' 或省略表示 ASC)
13
+ * - 默认升序:'order_id'(无后缀时默认为 ASC)
14
+ *
15
+ * 2. 数组格式:
16
+ * - 纯对象数组:[{ fieldName: 'created_at', orderType: 'desc' }]
17
+ * - 混合数组(字符串+对象):['order_id+', { fieldName: 'amount', orderType: 'asc' }]
18
+ *
19
+ * SQL 注入防护:
20
+ * - 所有字段名必须通过 MixinUtils.isValidFieldName() 校验
21
+ * - 只允许 ASC/DESC 作为排序方向
22
+ */
23
+ class OrderByUtils {
24
+ /**
25
+ * 解析 orderBy 参数为 IOrderByItem 数组
26
+ *
27
+ * @param orderByStr 排序参数,可以是字符串或数组
28
+ * @returns IOrderByItem[] 解析后的排序项数组
29
+ */
30
+ static parseOrderBys(orderByStr) {
31
+ if (MixinUtils_1.MixinUtils.isEmpty(orderByStr)) {
32
+ return [];
33
+ }
34
+ // 数组格式:支持字符串和对象混合
35
+ if (Array.isArray(orderByStr)) {
36
+ return orderByStr
37
+ .map(item => this.parseOrderByItem(item))
38
+ .filter((o) => !!o);
39
+ }
40
+ // 字符串格式:逗号分隔多个字段
41
+ return orderByStr
42
+ .split(',')
43
+ .map(s => s.trim())
44
+ .filter(s => !!s)
45
+ .map(item => this.parseOrderByString(item))
46
+ .filter((o) => !!o);
47
+ }
48
+ /**
49
+ * 获取第一个排序项
50
+ *
51
+ * @param orderByStr 排序参数
52
+ * @returns IOrderByItem | null 第一个排序项,无则返回 null
53
+ */
54
+ static getFirstOrderBy(orderByStr) {
55
+ const orderBys = this.parseOrderBys(orderByStr);
56
+ return orderBys.length > 0 ? orderBys[0] : null;
57
+ }
58
+ /**
59
+ * 判断是否为 ASC 升序排序
60
+ *
61
+ * @param orderByStr 排序参数
62
+ * @returns true 表示 ASC,false 表示 DESC 或无排序
63
+ */
64
+ static isFirstOrderByAsc(orderByStr) {
65
+ const first = this.getFirstOrderBy(orderByStr);
66
+ if (!first) {
67
+ return false;
68
+ }
69
+ return first.orderType.toUpperCase() === 'ASC';
70
+ }
71
+ /**
72
+ * 判断是否为 DESC 降序排序
73
+ *
74
+ * @param orderByStr 排序参数
75
+ * @returns true 表示 DESC,false 表示 ASC 或无排序
76
+ */
77
+ static isFirstOrderByDesc(orderByStr) {
78
+ const first = this.getFirstOrderBy(orderByStr);
79
+ if (!first) {
80
+ return false;
81
+ }
82
+ return first.orderType.toUpperCase() === 'DESC';
83
+ }
84
+ /**
85
+ * 判断排序字段是否匹配指定的时间字段
86
+ *
87
+ * 用于分表查询时校验排序字段是否为分表字段
88
+ *
89
+ * @param orderByStr 排序参数
90
+ * @param timeColumn 时间字段名
91
+ * @returns true 表示匹配,false 表示不匹配或无排序
92
+ */
93
+ static isOrderByTimeColumn(orderByStr, timeColumn) {
94
+ const first = this.getFirstOrderBy(orderByStr);
95
+ if (!first) {
96
+ return false;
97
+ }
98
+ return first.fieldName === timeColumn;
99
+ }
100
+ /**
101
+ * 解析单个排序项(数组元素)
102
+ */
103
+ static parseOrderByItem(item) {
104
+ // 字符串格式:解析标准 SQL 格式或简写格式
105
+ if (typeof item === 'string') {
106
+ return this.parseOrderByString(item);
107
+ }
108
+ // 对象格式:提取 fieldName 和 orderType
109
+ const { fieldName, orderType = 'asc' } = item || {};
110
+ if (!fieldName) {
111
+ return null;
112
+ }
113
+ this.validateFieldName(fieldName, fieldName);
114
+ return { fieldName, orderType };
115
+ }
116
+ /**
117
+ * 解析字符串格式的排序项(支持标准 SQL 和简写格式)
118
+ */
119
+ static parseOrderByString(orderByStr) {
120
+ let orderType = 'asc';
121
+ let fieldName = orderByStr;
122
+ // 检查是否为空格分隔的标准 SQL 格式(如 'created_at DESC')
123
+ const spaceIndex = orderByStr.lastIndexOf(' ');
124
+ if (spaceIndex > 0) {
125
+ const beforeSpace = orderByStr.substring(0, spaceIndex).trim();
126
+ const afterSpace = orderByStr.substring(spaceIndex + 1).trim().toUpperCase();
127
+ if (afterSpace === 'ASC' || afterSpace === 'DESC') {
128
+ fieldName = beforeSpace;
129
+ orderType = afterSpace.toLowerCase();
130
+ }
131
+ else {
132
+ throw new exceptions_1.CommonException(exceptions_1.Exceptions.REQUEST_MODEL_PARSE_ORDER_BY_FAILED, orderByStr);
133
+ }
134
+ }
135
+ else if (orderByStr.endsWith('+')) {
136
+ // 简写格式:+ 表示升序
137
+ fieldName = orderByStr.slice(0, -1);
138
+ orderType = 'asc';
139
+ }
140
+ else if (orderByStr.endsWith('-')) {
141
+ // 简写格式:- 表示降序
142
+ fieldName = orderByStr.slice(0, -1);
143
+ orderType = 'desc';
144
+ }
145
+ fieldName = fieldName.trim();
146
+ this.validateFieldName(fieldName, orderByStr);
147
+ return { fieldName, orderType };
148
+ }
149
+ /**
150
+ * SQL 注入防护:校验字段名格式
151
+ */
152
+ static validateFieldName(fieldName, originalValue) {
153
+ if (MixinUtils_1.MixinUtils.isEmpty(fieldName) || !MixinUtils_1.MixinUtils.isValidFieldName(fieldName)) {
154
+ throw new exceptions_1.CommonException(exceptions_1.Exceptions.REQUEST_MODEL_PARSE_ORDER_BY_FAILED, originalValue);
155
+ }
156
+ }
157
+ }
158
+ exports.OrderByUtils = OrderByUtils;
@@ -50,7 +50,7 @@ const validatorUtils = {
50
50
  return false;
51
51
  },
52
52
  isMomentValid(itemCfg, itemValue) {
53
- const cfgPart = itemCfg.substring(keys_1.KeysOfValidators.ENUM.length).trim();
53
+ const cfgPart = itemCfg.substring(keys_1.KeysOfValidators.MOMENT.length).trim();
54
54
  return moment(itemValue, cfgPart, true).isValid();
55
55
  },
56
56
  isEnumValid(itemCfg, itemValue) {