midway-fatcms 0.0.18 → 0.0.20

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.
@@ -1,20 +1,25 @@
1
1
  /**
2
- * 128 位雪花 ID 生成器(独立于 MixinUtils.generateSnowflakeId,原 63 位算法不变)
2
+ * 128 位雪花 ID 生成器(优化版)
3
3
  *
4
4
  * 位布局(128 bit,无符号,BigInt 拼接后 Base58 编码返回):
5
- * - 64 bit 时间戳:相对 2024-01-01 00:00:00 UTC 的毫秒偏移
6
- * - 16 bit 机器ID:0-65535,优先参数 > 环境变量 SNOWFLAKE_MACHINE_ID > process.pid 低 16 位
7
- * - 48 bit 序列号:同一毫秒内自增,约 2.8e14 个/ms,实际不会耗尽
5
+ * - 41 bit 时间戳:相对 2024-01-01 00:00:00 UTC 的毫秒偏移,可用约 69 年(至 ~2093 年)
6
+ * - 10 bit 机器ID:0-1023,与标准 64 位雪花 ID 一致,优先参数 > 环境变量 > process.pid
7
+ * - 77 bit 序列号:同一毫秒内自增,2^77 ≈ 1.5×10²³,无需考虑耗尽
8
8
  *
9
- * 输出:Bitcoin 字母表 Base58(无 0/O/I/l),当前约 21 字符,理论最大 22 字符,建议 VARCHAR(24)
9
+ * 输出:Bitcoin 字母表 Base58(无 0/O/I/l),左填充 '1' 至恒定 22 字符,建议 VARCHAR(24)
10
+ * 时间戳在高位,ID 随时间单调递增,适合做数据库主键。
11
+ * 2^128-1 < 58^22,故 22 字符足以覆盖全部 128 位取值范围。
10
12
  */
11
13
 
12
14
  const EPOCH = BigInt(1704067200000); // 2024-01-01 00:00:00 UTC
13
15
 
14
- const TS_SHIFT = BigInt(64);
15
- const MID_SHIFT = BigInt(48);
16
- const MID_MASK = BigInt(0xffff);
17
- const SEQ_MASK = BigInt('0xffffffffffff'); // 48 bit
16
+ const TS_BITS = BigInt(41);
17
+ const MID_BITS = BigInt(10);
18
+ const SEQ_BITS = BigInt(77);
19
+ const TS_MASK = (BigInt(1) << TS_BITS) - BigInt(1); // 41 bit
20
+ const MID_MASK = (BigInt(1) << MID_BITS) - BigInt(1); // 10 bit
21
+ const SEQ_MASK = (BigInt(1) << SEQ_BITS) - BigInt(1); // 77 bit
22
+ const PAD_LENGTH = 22;
18
23
 
19
24
  const SEQ_START = BigInt(1);
20
25
  const DRIFT_TOLERANCE = BigInt(5);
@@ -41,7 +46,7 @@ function encodeBase58(value: bigint): string {
41
46
  return result;
42
47
  }
43
48
 
44
- /** 解析机器 ID(16 bit) */
49
+ /** 解析机器 ID(10 bit,0-1023) */
45
50
  function resolveMachineId(machineId?: number): bigint {
46
51
  let mid = Number(machineId);
47
52
  if (machineId === undefined || machineId === null || isNaN(mid)) {
@@ -59,10 +64,16 @@ function resolveMachineId(machineId?: number): bigint {
59
64
  }
60
65
 
61
66
  /**
62
- * 生成 128 位雪花 ID(Base58 字符串)
67
+ * 生成 128 位雪花 ID(Base58 字符串,恒定 22 字符)
63
68
  *
64
- * @param machineId 机器ID(0-65535),不传则依次从环境变量 SNOWFLAKE_MACHINE_ID、process.pid 获取
65
- * @returns Base58 编码的 128 位雪花 ID
69
+ * 位布局:41 bit 时间戳 | 10 bit 机器ID | 77 bit 序列号
70
+ * 时间戳采用 41 bit(与标准 64 位雪花 ID 一致),毫秒精度,可用约 69 年。
71
+ * 机器ID 采用 10 bit(0-1023),与标准 64 位雪花 ID 一致,防止多机 ID 冲突。
72
+ * 时间戳在高位,ID 随时间单调递增,适合做数据库主键。
73
+ * Base58 输出左填充 '1'(零字符)至 22 字符,保证长度恒定且不破坏字典序排序。
74
+ *
75
+ * @param machineId 机器ID(0-1023),不传则依次从环境变量 SNOWFLAKE_MACHINE_ID、process.pid 获取
76
+ * @returns Base58 编码的 128 位雪花 ID(恒定 22 字符)
66
77
  */
67
78
  function generateSnowflakeIdExt(machineId?: number): string {
68
79
  const mid = resolveMachineId(machineId);
@@ -82,18 +93,14 @@ function generateSnowflakeIdExt(machineId?: number): string {
82
93
 
83
94
  if (ts === snowflakeExtLastTs) {
84
95
  snowflakeExtSeq = (snowflakeExtSeq + BigInt(1)) & SEQ_MASK;
85
- if (snowflakeExtSeq === BigInt(0)) {
86
- ts = snowflakeExtLastTs + BigInt(1);
87
- snowflakeExtSeq = SEQ_START;
88
- }
89
96
  } else {
90
97
  snowflakeExtSeq = SEQ_START;
91
98
  }
92
99
 
93
100
  snowflakeExtLastTs = ts;
94
101
 
95
- const id = (ts << TS_SHIFT) | (mid << MID_SHIFT) | snowflakeExtSeq;
96
- return encodeBase58(id);
102
+ const id = ((ts & TS_MASK) << (MID_BITS + SEQ_BITS)) | (mid << SEQ_BITS) | snowflakeExtSeq;
103
+ return encodeBase58(id).padStart(PAD_LENGTH, BASE58_ALPHABET[0]);
97
104
  }
98
105
 
99
106
  export { generateSnowflakeIdExt, EPOCH as SNOWFLAKE_EXT_EPOCH };
@@ -1,8 +1,7 @@
1
- import { KeysOfSimpleSQL } from "@/libs/crud-pro/models/keys";
2
- import { IRequestModel, IVisitor } from "@/libs/crud-pro/interfaces";
3
- import { IRequestCfgModel2, ICommonStandardColumns, ICommonStandardColumns2 } from "./models";
4
- import { DateTimeUtils } from "@/libs/crud-pro/utils/DateTimeUtils";
5
-
1
+ import { KeysOfSimpleSQL } from '@/libs/crud-pro/models/keys';
2
+ import { IRequestModel, IVisitor } from '@/libs/crud-pro/interfaces';
3
+ import { IRequestCfgModel2, ICommonStandardColumns, ICommonStandardColumns2 } from './models';
4
+ import { DateTimeUtils } from '@/libs/crud-pro/utils/DateTimeUtils';
6
5
 
7
6
  /**
8
7
  * 软删除处理函数
@@ -18,14 +17,7 @@ import { DateTimeUtils } from "@/libs/crud-pro/utils/DateTimeUtils";
18
17
  * @param params 请求参数(会被修改)
19
18
  * @param visitor 获取当前用户信息的函数(用于软删除时记录删除人)
20
19
  */
21
- function fixSoftDelete1(
22
- oldSqlSimpleName: KeysOfSimpleSQL,
23
- cfgModel: IRequestCfgModel2,
24
- params: IRequestModel,
25
- visitor?: IVisitor
26
- ): void {
27
-
28
-
20
+ function fixSoftDelete1(oldSqlSimpleName: KeysOfSimpleSQL, cfgModel: IRequestCfgModel2, params: IRequestModel, visitor?: IVisitor): void {
29
21
  // INSERT 操作:刚插入的数据,肯定是未删除的
30
22
  if (KeysOfSimpleSQL.SIMPLE_INSERT === oldSqlSimpleName) {
31
23
  if (!params.data) {
@@ -38,12 +30,10 @@ function fixSoftDelete1(
38
30
  // DELETE 操作:如果是软删除,修改为 UPDATE 语句
39
31
  // 标准软删除:deleted_at 字段为 0 代表未删除,有任意时间戳代表已删除
40
32
  if (KeysOfSimpleSQL.SIMPLE_DELETE === oldSqlSimpleName) {
41
-
42
33
  if (!params.condition || Object.keys(params.condition).length === 0) {
43
34
  throw new Error('执行删除操作,必须指定删除条件');
44
35
  }
45
36
 
46
-
47
37
  // 在原有 data 对象上添加软删除字段
48
38
  if (!params.data) {
49
39
  params.data = {};
@@ -57,10 +47,7 @@ function fixSoftDelete1(
57
47
  }
58
48
 
59
49
  // QUERY 操作:强制查询未删除的记录
60
- if (KeysOfSimpleSQL.SIMPLE_QUERY_ONE === oldSqlSimpleName
61
- || KeysOfSimpleSQL.SIMPLE_QUERY === oldSqlSimpleName
62
- || KeysOfSimpleSQL.SIMPLE_QUERY_PAGE === oldSqlSimpleName) {
63
-
50
+ if (KeysOfSimpleSQL.SIMPLE_QUERY_ONE === oldSqlSimpleName || KeysOfSimpleSQL.SIMPLE_QUERY === oldSqlSimpleName || KeysOfSimpleSQL.SIMPLE_QUERY_PAGE === oldSqlSimpleName) {
64
51
  if (!params.condition) {
65
52
  params.condition = {};
66
53
  }
@@ -68,15 +55,7 @@ function fixSoftDelete1(
68
55
  }
69
56
  }
70
57
 
71
-
72
- function fixSoftDelete2(
73
- oldSqlSimpleName: KeysOfSimpleSQL,
74
- cfgModel: IRequestCfgModel2,
75
- params: IRequestModel,
76
- visitor?: IVisitor
77
- ): void {
78
-
79
-
58
+ function fixSoftDelete2(oldSqlSimpleName: KeysOfSimpleSQL, cfgModel: IRequestCfgModel2, params: IRequestModel, visitor?: IVisitor): void {
80
59
  // INSERT 操作:刚插入的数据,肯定是未删除的
81
60
  if (KeysOfSimpleSQL.SIMPLE_INSERT === oldSqlSimpleName) {
82
61
  if (!params.data) {
@@ -88,12 +67,10 @@ function fixSoftDelete2(
88
67
 
89
68
  // DELETE 操作:如果是软删除,修改为 UPDATE 语句
90
69
  if (KeysOfSimpleSQL.SIMPLE_DELETE === oldSqlSimpleName) {
91
-
92
70
  if (!params.condition || Object.keys(params.condition).length === 0) {
93
71
  throw new Error('执行删除操作,必须指定删除条件');
94
72
  }
95
73
 
96
-
97
74
  // 在原有 data 对象上添加软删除字段
98
75
  if (!params.data) {
99
76
  params.data = {};
@@ -108,10 +85,7 @@ function fixSoftDelete2(
108
85
  }
109
86
 
110
87
  // QUERY 操作:强制查询未删除的记录
111
- if (KeysOfSimpleSQL.SIMPLE_QUERY_ONE === oldSqlSimpleName
112
- || KeysOfSimpleSQL.SIMPLE_QUERY === oldSqlSimpleName
113
- || KeysOfSimpleSQL.SIMPLE_QUERY_PAGE === oldSqlSimpleName) {
114
-
88
+ if (KeysOfSimpleSQL.SIMPLE_QUERY_ONE === oldSqlSimpleName || KeysOfSimpleSQL.SIMPLE_QUERY === oldSqlSimpleName || KeysOfSimpleSQL.SIMPLE_QUERY_PAGE === oldSqlSimpleName) {
115
89
  if (!params.condition) {
116
90
  params.condition = {};
117
91
  }
@@ -119,8 +93,6 @@ function fixSoftDelete2(
119
93
  }
120
94
  }
121
95
 
122
-
123
-
124
96
  /**
125
97
  * 软删除处理函数
126
98
  *
@@ -129,14 +101,7 @@ function fixSoftDelete2(
129
101
  * @param params 请求参数(会被修改)
130
102
  * @param visitor 获取当前用户信息的函数(用于软删除时记录删除人)
131
103
  */
132
- function fixSoftDelete(
133
- oldSqlSimpleName: KeysOfSimpleSQL,
134
- cfgModel: IRequestCfgModel2,
135
- params: IRequestModel,
136
- visitor?: IVisitor
137
- ): void {
138
-
139
-
104
+ function fixSoftDelete(oldSqlSimpleName: KeysOfSimpleSQL, cfgModel: IRequestCfgModel2, params: IRequestModel, visitor?: IVisitor): void {
140
105
  if (cfgModel.enableSoftDelete === true || cfgModel.enableSoftDelete === 'soft') {
141
106
  fixSoftDelete1(oldSqlSimpleName, cfgModel, params, visitor);
142
107
  return;
@@ -146,11 +111,6 @@ function fixSoftDelete(
146
111
  fixSoftDelete2(oldSqlSimpleName, cfgModel, params, visitor);
147
112
  return;
148
113
  }
149
-
150
114
  }
151
115
 
152
-
153
- export {
154
- fixSoftDelete,
155
- };
156
-
116
+ export { fixSoftDelete };
@@ -10,7 +10,7 @@ import { IRequestCfgModel2 } from '@/models/bizmodels';
10
10
  import { CurdMixByLinkToCustomService } from './CurdMixByLinkToCustomService';
11
11
  // import { SqlDbType } from '@/libs/crud-pro/models/keys';
12
12
  import { ExecuteContext, IExecuteContextCustom } from '@/libs/crud-pro/models/ExecuteContext';
13
- import { ShardingBase } from "@/libs/crud-sharding";
13
+ import { ShardingBase } from '@/libs/crud-sharding';
14
14
  import { IGetQuickCrudParam, IGetShardingCrudParam } from './CurdProService';
15
15
 
16
16
  export interface ILinkColumnRelationParam {
@@ -50,7 +50,6 @@ export class CurdMixService {
50
50
  return this.curdProService.executeCrudByCfg(reqJson, cfgModel);
51
51
  }
52
52
 
53
-
54
53
  // /**
55
54
  // * @deprecated 请使用 getQuickCrud 替代
56
55
  // * @param sqlDatabase
@@ -67,13 +66,11 @@ export class CurdMixService {
67
66
  return this.curdProService.getQuickCrud(param);
68
67
  }
69
68
 
70
-
71
69
  public getShardingCrud(param: IGetShardingCrudParam): ShardingBase {
72
70
  this.prepare();
73
71
  return this.curdProService.getShardingCrud(param);
74
72
  }
75
73
 
76
-
77
74
  async executeSQL(sqlCfgModel: ISqlCfgModel): Promise<ExecuteSQLResult> {
78
75
  this.prepare();
79
76
  return this.curdProService.executeSQL(sqlCfgModel);
@@ -20,7 +20,7 @@ import { CrudProQuick } from '@/libs/crud-pro-quick';
20
20
  import { ShardingBase, ShardingByTimeCrud, ShardingByHashCrud, ShardingByKeyCrud, ShardingByCustomCrud, ShardingType, IShardingConfig } from '@/libs/crud-sharding';
21
21
  import { fixCfgModel } from './fixCfgModel';
22
22
  import { GLOBAL_STATIC_CONFIG } from '@/libs/global-config/global-config';
23
- import { fixSoftDelete } from "@/libs/crud-pro-quick/fixSoftDelete";
23
+ import { fixSoftDelete } from '@/libs/crud-pro-quick/fixSoftDelete';
24
24
 
25
25
  /**
26
26
  * getQuickCrud 参数接口
@@ -321,7 +321,6 @@ export class CurdProService extends BaseService {
321
321
  return await curdPro.executeCrudByCfg(reqJson, cfgModel);
322
322
  }
323
323
 
324
-
325
324
  /**
326
325
  * 获取 CrudProQuick 工具类
327
326
  *
@@ -431,14 +430,14 @@ export class CurdProService extends BaseService {
431
430
  return curdPro.getAllTableInfos(query, options);
432
431
  }
433
432
 
434
-
435
433
  public async loadTableMetaCache(ctx?: any): Promise<void> {
436
434
  const { SystemDbName, SystemDbType } = GLOBAL_STATIC_CONFIG.getConfig();
437
- await this.getAllTableInfos({
438
- sqlDatabase: SystemDbName,
439
- sqlDbType: SystemDbType,
440
- }, { skipCache: true });
435
+ await this.getAllTableInfos(
436
+ {
437
+ sqlDatabase: SystemDbName,
438
+ sqlDbType: SystemDbType,
439
+ },
440
+ { skipCache: true }
441
+ );
441
442
  }
442
443
  }
443
-
444
-
@@ -1,145 +1,145 @@
1
- import * as _ from 'lodash';
2
- import { CTX_VISITOR_ACCOUNT_TYPE, CTX_VISITOR_AVATAR, CTX_VISITOR_ID, CTX_VISITOR_NICKNAME, IRequestCfgModel2 } from '@/models/bizmodels';
3
- import { KeysOfSimpleSQL } from '@/libs/crud-pro/models/keys';
4
-
5
- function isSimpleQuery(sqlSimpleName: KeysOfSimpleSQL): boolean {
6
- return sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY_COUNT || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY_PAGE || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY_ONE;
7
- }
8
-
9
- function fixCfgModel(cfgModel: IRequestCfgModel2) {
10
- if (!cfgModel.method) {
11
- cfgModel.method = 'anonymous';
12
- }
13
-
14
- if (!cfgModel.updateCfg) {
15
- cfgModel.updateCfg = {};
16
- }
17
-
18
- const sqlSimpleName = cfgModel.sqlSimpleName;
19
-
20
- const enableStandardUpdateCfg = cfgModel.enableStandardUpdateCfg; // 用于设置data字段
21
- const enableStandardUpdateCfgCondition = cfgModel.enableStandardUpdateCfgCondition; // 用于设置condition字段
22
-
23
- // 彻底关闭
24
- if (enableStandardUpdateCfg === false) {
25
- return;
26
- }
27
-
28
- const getDataCfgArray = () => {
29
- // 明确有配置
30
- if (Array.isArray(enableStandardUpdateCfg)) {
31
- if (enableStandardUpdateCfg.includes('null')) {
32
- return [];
33
- }
34
- return enableStandardUpdateCfg;
35
- }
36
- // update 、insert 添加 by
37
- return ['by', 'at']; // 创建/修改语句默认添加by
38
- };
39
-
40
- const getConditionCfgArray = () => {
41
- // 明确有配置
42
- if (Array.isArray(enableStandardUpdateCfgCondition)) {
43
- if (enableStandardUpdateCfgCondition.includes('null')) {
44
- return [];
45
- }
46
- return enableStandardUpdateCfgCondition;
47
- }
48
- return [];
49
- };
50
-
51
- const dataCfgArray = getDataCfgArray();
52
- const conditionCfgArray = getConditionCfgArray();
53
-
54
- const mapping: Record<string, any> = {
55
- 'condition.created_by': { contextAsString: CTX_VISITOR_ID },
56
- 'condition.created_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
57
-
58
- 'data.created_at': { functionName: 'getCurrentTimeString' },
59
- 'data.created_by': { contextAsString: CTX_VISITOR_ID },
60
- 'data.created_avatar': { contextAsString: CTX_VISITOR_AVATAR },
61
- 'data.created_nickname': { contextAsString: CTX_VISITOR_NICKNAME },
62
- 'data.created_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
63
-
64
- 'data.modified_at': { functionName: 'getCurrentTimeString' },
65
- 'data.modified_by': { contextAsString: CTX_VISITOR_ID },
66
- 'data.modified_avatar': { contextAsString: CTX_VISITOR_AVATAR },
67
- 'data.modified_nickname': { contextAsString: CTX_VISITOR_NICKNAME },
68
- 'data.modified_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
69
- };
70
-
71
- const buildUpdateCfgBy = (cfgArray: string[], prefix: string): any => {
72
- const obj: any = {};
73
- if (Array.isArray(cfgArray)) {
74
- for (let i = 0; i < cfgArray.length; i++) {
75
- const suffix = cfgArray[i];
76
- if (suffix) {
77
- const key = `${prefix}_${suffix}`;
78
- if (mapping[key]) {
79
- obj[key] = mapping[key];
80
- }
81
- }
82
- }
83
- }
84
- return obj;
85
- };
86
-
87
- // 查询语句
88
- if (isSimpleQuery(sqlSimpleName)) {
89
- const updateCfgObj = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 查询用户本人创建的
90
- _.merge(cfgModel.updateCfg, updateCfgObj);
91
- }
92
-
93
- // 删除语句
94
- if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_DELETE) {
95
- const updateCfgObj = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 删除用户本人创建的
96
- _.merge(cfgModel.updateCfg, updateCfgObj);
97
- }
98
-
99
- // 插入语句
100
- if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_INSERT) {
101
- const updateCfgObj1 = buildUpdateCfgBy(dataCfgArray, 'data.created');
102
- _.merge(cfgModel.updateCfg, updateCfgObj1);
103
-
104
- // 启用: 雪花ID
105
- if (dataCfgArray.includes('idBySnowflakeId')) {
106
- _.merge(cfgModel.updateCfg, {
107
- 'data.id': { functionName: 'generateSnowflakeId' },
108
- });
109
- }
110
-
111
- // 启用: 扩展的雪花ID: -128bit,base58(建议数据库varchar(24))
112
- if (dataCfgArray.includes('idBySnowflakeIdExt')) {
113
- _.merge(cfgModel.updateCfg, {
114
- 'data.id': { functionName: 'generateSnowflakeIdExt' },
115
- });
116
- }
117
-
118
- // 启用: UUID
119
- if (dataCfgArray.includes('idByUUID')) {
120
- _.merge(cfgModel.updateCfg, {
121
- 'data.id': { functionName: 'uuid' },
122
- });
123
- }
124
- }
125
-
126
- // 更新语句
127
- if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_UPDATE) {
128
- const updateCfgObj1 = buildUpdateCfgBy(dataCfgArray, 'data.modified');
129
- const updateCfgObj2 = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 更新用户本人创建的
130
- _.merge(cfgModel.updateCfg, updateCfgObj1);
131
- _.merge(cfgModel.updateCfg, updateCfgObj2);
132
- }
133
-
134
- // 插入或更新
135
- if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE) {
136
- const updateCfgObj1 = buildUpdateCfgBy(dataCfgArray, 'data.created');
137
- const updateCfgObj2 = buildUpdateCfgBy(dataCfgArray, 'data.modified');
138
- const updateCfgObj3 = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 更新用户本人创建的
139
- _.merge(cfgModel.updateCfg, updateCfgObj1);
140
- _.merge(cfgModel.updateCfg, updateCfgObj2);
141
- _.merge(cfgModel.updateCfg, updateCfgObj3);
142
- }
143
- }
144
-
145
- export { fixCfgModel };
1
+ import * as _ from 'lodash';
2
+ import { CTX_VISITOR_ACCOUNT_TYPE, CTX_VISITOR_AVATAR, CTX_VISITOR_ID, CTX_VISITOR_NICKNAME, IRequestCfgModel2 } from '@/models/bizmodels';
3
+ import { KeysOfSimpleSQL } from '@/libs/crud-pro/models/keys';
4
+
5
+ function isSimpleQuery(sqlSimpleName: KeysOfSimpleSQL): boolean {
6
+ return sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY_COUNT || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY_PAGE || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_QUERY_ONE;
7
+ }
8
+
9
+ function fixCfgModel(cfgModel: IRequestCfgModel2) {
10
+ if (!cfgModel.method) {
11
+ cfgModel.method = 'anonymous';
12
+ }
13
+
14
+ if (!cfgModel.updateCfg) {
15
+ cfgModel.updateCfg = {};
16
+ }
17
+
18
+ const sqlSimpleName = cfgModel.sqlSimpleName;
19
+
20
+ const enableStandardUpdateCfg = cfgModel.enableStandardUpdateCfg; // 用于设置data字段
21
+ const enableStandardUpdateCfgCondition = cfgModel.enableStandardUpdateCfgCondition; // 用于设置condition字段
22
+
23
+ // 彻底关闭
24
+ if (enableStandardUpdateCfg === false) {
25
+ return;
26
+ }
27
+
28
+ const getDataCfgArray = () => {
29
+ // 明确有配置
30
+ if (Array.isArray(enableStandardUpdateCfg)) {
31
+ if (enableStandardUpdateCfg.includes('null')) {
32
+ return [];
33
+ }
34
+ return enableStandardUpdateCfg;
35
+ }
36
+ // update 、insert 添加 by
37
+ return ['by', 'at']; // 创建/修改语句默认添加by
38
+ };
39
+
40
+ const getConditionCfgArray = () => {
41
+ // 明确有配置
42
+ if (Array.isArray(enableStandardUpdateCfgCondition)) {
43
+ if (enableStandardUpdateCfgCondition.includes('null')) {
44
+ return [];
45
+ }
46
+ return enableStandardUpdateCfgCondition;
47
+ }
48
+ return [];
49
+ };
50
+
51
+ const dataCfgArray = getDataCfgArray();
52
+ const conditionCfgArray = getConditionCfgArray();
53
+
54
+ const mapping: Record<string, any> = {
55
+ 'condition.created_by': { contextAsString: CTX_VISITOR_ID },
56
+ 'condition.created_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
57
+
58
+ 'data.created_at': { functionName: 'getCurrentTimeString' },
59
+ 'data.created_by': { contextAsString: CTX_VISITOR_ID },
60
+ 'data.created_avatar': { contextAsString: CTX_VISITOR_AVATAR },
61
+ 'data.created_nickname': { contextAsString: CTX_VISITOR_NICKNAME },
62
+ 'data.created_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
63
+
64
+ 'data.modified_at': { functionName: 'getCurrentTimeString' },
65
+ 'data.modified_by': { contextAsString: CTX_VISITOR_ID },
66
+ 'data.modified_avatar': { contextAsString: CTX_VISITOR_AVATAR },
67
+ 'data.modified_nickname': { contextAsString: CTX_VISITOR_NICKNAME },
68
+ 'data.modified_account_type': { contextAsString: CTX_VISITOR_ACCOUNT_TYPE },
69
+ };
70
+
71
+ const buildUpdateCfgBy = (cfgArray: string[], prefix: string): any => {
72
+ const obj: any = {};
73
+ if (Array.isArray(cfgArray)) {
74
+ for (let i = 0; i < cfgArray.length; i++) {
75
+ const suffix = cfgArray[i];
76
+ if (suffix) {
77
+ const key = `${prefix}_${suffix}`;
78
+ if (mapping[key]) {
79
+ obj[key] = mapping[key];
80
+ }
81
+ }
82
+ }
83
+ }
84
+ return obj;
85
+ };
86
+
87
+ // 查询语句
88
+ if (isSimpleQuery(sqlSimpleName)) {
89
+ const updateCfgObj = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 查询用户本人创建的
90
+ _.merge(cfgModel.updateCfg, updateCfgObj);
91
+ }
92
+
93
+ // 删除语句
94
+ if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_DELETE) {
95
+ const updateCfgObj = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 删除用户本人创建的
96
+ _.merge(cfgModel.updateCfg, updateCfgObj);
97
+ }
98
+
99
+ // 插入语句
100
+ if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_INSERT) {
101
+ const updateCfgObj1 = buildUpdateCfgBy(dataCfgArray, 'data.created');
102
+ _.merge(cfgModel.updateCfg, updateCfgObj1);
103
+
104
+ // 启用: 雪花ID
105
+ if (dataCfgArray.includes('idBySnowflakeId')) {
106
+ _.merge(cfgModel.updateCfg, {
107
+ 'data.id': { functionName: 'generateSnowflakeId' },
108
+ });
109
+ }
110
+
111
+ // 启用: 扩展的雪花ID: -128bit,base58(建议数据库varchar(24))
112
+ if (dataCfgArray.includes('idBySnowflakeIdExt')) {
113
+ _.merge(cfgModel.updateCfg, {
114
+ 'data.id': { functionName: 'generateSnowflakeIdExt' },
115
+ });
116
+ }
117
+
118
+ // 启用: UUID
119
+ if (dataCfgArray.includes('idByUUID')) {
120
+ _.merge(cfgModel.updateCfg, {
121
+ 'data.id': { functionName: 'uuid' },
122
+ });
123
+ }
124
+ }
125
+
126
+ // 更新语句
127
+ if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_UPDATE) {
128
+ const updateCfgObj1 = buildUpdateCfgBy(dataCfgArray, 'data.modified');
129
+ const updateCfgObj2 = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 更新用户本人创建的
130
+ _.merge(cfgModel.updateCfg, updateCfgObj1);
131
+ _.merge(cfgModel.updateCfg, updateCfgObj2);
132
+ }
133
+
134
+ // 插入或更新
135
+ if (sqlSimpleName === KeysOfSimpleSQL.SIMPLE_INSERT_OR_UPDATE || sqlSimpleName === KeysOfSimpleSQL.SIMPLE_INSERT_ON_DUPLICATE_UPDATE) {
136
+ const updateCfgObj1 = buildUpdateCfgBy(dataCfgArray, 'data.created');
137
+ const updateCfgObj2 = buildUpdateCfgBy(dataCfgArray, 'data.modified');
138
+ const updateCfgObj3 = buildUpdateCfgBy(conditionCfgArray, 'condition.created'); // 更新用户本人创建的
139
+ _.merge(cfgModel.updateCfg, updateCfgObj1);
140
+ _.merge(cfgModel.updateCfg, updateCfgObj2);
141
+ _.merge(cfgModel.updateCfg, updateCfgObj3);
142
+ }
143
+ }
144
+
145
+ export { fixCfgModel };
@@ -5,7 +5,7 @@ import { RouteTrie } from './RouteTrie';
5
5
  import { RouteHandler } from './RouteHandler';
6
6
  import { IProxyApiEntity, IUpstreamInfo } from '../../models/SystemEntities';
7
7
  import { isEntityOK, parseJsonObject } from '../../libs/utils/functions';
8
- import { BALANCE_STRATEGY } from './ProxyApiUtils';
8
+ import { BALANCE_STRATEGY, normalizePathPrefix } from './ProxyApiUtils';
9
9
  import { WeightedRandom } from './WeightedRandom';
10
10
  import { WeightedRoundRobin } from './WeightedRoundRobin';
11
11
  import { bizMemCacheStore } from '@/service/base/bizmemcache/BizMemCacheStore';
@@ -74,7 +74,8 @@ export class ProxyApiLoadService extends BaseService {
74
74
  for (let i = 0; i < entities.length; i++) {
75
75
  const entity = entities[i];
76
76
  if (entity.status === 1) {
77
- const path_prefix2 = '/' + entity.upstream_code + entity.path_prefix;
77
+ const pathPrefix = normalizePathPrefix(entity.path_prefix);
78
+ const path_prefix2 = '/' + entity.upstream_code + pathPrefix;
78
79
  routeTrie.addRoute(path_prefix2, new RouteHandler(entity));
79
80
  }
80
81
  }
@@ -15,6 +15,14 @@ export const BALANCE_STRATEGY = {
15
15
  * @param str
16
16
  * @param mod
17
17
  */
18
+ /** path_prefix 为空表示代理上游根路径;单独的 "/" 会在路由树中注册无效节点,统一归一化为空 */
19
+ export function normalizePathPrefix(path_prefix: string | null | undefined): string {
20
+ if (path_prefix === '/' || path_prefix == null) {
21
+ return '';
22
+ }
23
+ return path_prefix;
24
+ }
25
+
18
26
  export function stringHashMod(str: string, mod: number): number {
19
27
  if (!str) {
20
28
  throw new Error('stringHashMod str is required');