befly 2.3.2 → 3.0.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.
Files changed (93) hide show
  1. package/apis/health/info.ts +64 -0
  2. package/apis/tool/tokenCheck.ts +51 -0
  3. package/bin/befly.ts +202 -0
  4. package/checks/conflict.ts +408 -0
  5. package/checks/table.ts +284 -0
  6. package/config/env.ts +218 -0
  7. package/config/reserved.ts +96 -0
  8. package/main.ts +101 -0
  9. package/package.json +45 -16
  10. package/plugins/{db.js → db.ts} +25 -12
  11. package/plugins/logger.ts +28 -0
  12. package/plugins/redis.ts +51 -0
  13. package/plugins/tool.ts +34 -0
  14. package/scripts/syncDb/apply.ts +171 -0
  15. package/scripts/syncDb/constants.ts +70 -0
  16. package/scripts/syncDb/ddl.ts +182 -0
  17. package/scripts/syncDb/helpers.ts +172 -0
  18. package/scripts/syncDb/index.ts +215 -0
  19. package/scripts/syncDb/schema.ts +199 -0
  20. package/scripts/syncDb/sqlite.ts +50 -0
  21. package/scripts/syncDb/state.ts +104 -0
  22. package/scripts/syncDb/table.ts +204 -0
  23. package/scripts/syncDb/tableCreate.ts +142 -0
  24. package/scripts/syncDb/tests/constants.test.ts +104 -0
  25. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  26. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  27. package/scripts/syncDb/types.ts +92 -0
  28. package/scripts/syncDb/version.ts +73 -0
  29. package/scripts/syncDb.ts +9 -0
  30. package/scripts/syncDev.ts +112 -0
  31. package/system.ts +149 -0
  32. package/tables/_common.json +21 -0
  33. package/tables/admin.json +10 -0
  34. package/tsconfig.json +58 -0
  35. package/types/api.d.ts +246 -0
  36. package/types/befly.d.ts +234 -0
  37. package/types/common.d.ts +215 -0
  38. package/types/context.ts +167 -0
  39. package/types/crypto.d.ts +23 -0
  40. package/types/database.d.ts +278 -0
  41. package/types/index.d.ts +16 -0
  42. package/types/index.ts +459 -0
  43. package/types/jwt.d.ts +99 -0
  44. package/types/logger.d.ts +43 -0
  45. package/types/plugin.d.ts +109 -0
  46. package/types/redis.d.ts +44 -0
  47. package/types/tool.d.ts +67 -0
  48. package/types/validator.d.ts +45 -0
  49. package/utils/addonHelper.ts +60 -0
  50. package/utils/api.ts +23 -0
  51. package/utils/{colors.js → colors.ts} +79 -21
  52. package/utils/crypto.ts +308 -0
  53. package/utils/datetime.ts +51 -0
  54. package/utils/dbHelper.ts +142 -0
  55. package/utils/errorHandler.ts +68 -0
  56. package/utils/index.ts +46 -0
  57. package/utils/jwt.ts +493 -0
  58. package/utils/logger.ts +284 -0
  59. package/utils/objectHelper.ts +68 -0
  60. package/utils/pluginHelper.ts +62 -0
  61. package/utils/redisHelper.ts +338 -0
  62. package/utils/response.ts +38 -0
  63. package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
  64. package/utils/sqlHelper.ts +447 -0
  65. package/utils/tableHelper.ts +167 -0
  66. package/utils/tool.ts +230 -0
  67. package/utils/typeHelper.ts +101 -0
  68. package/utils/validate.ts +451 -0
  69. package/utils/{xml.js → xml.ts} +100 -74
  70. package/.npmrc +0 -3
  71. package/.prettierignore +0 -2
  72. package/.prettierrc +0 -11
  73. package/apis/health/info.js +0 -49
  74. package/apis/tool/tokenCheck.js +0 -29
  75. package/checks/table.js +0 -221
  76. package/config/env.js +0 -62
  77. package/main.js +0 -579
  78. package/plugins/logger.js +0 -14
  79. package/plugins/redis.js +0 -32
  80. package/plugins/tool.js +0 -8
  81. package/scripts/syncDb.js +0 -603
  82. package/system.js +0 -118
  83. package/tables/common.json +0 -16
  84. package/tables/tool.json +0 -6
  85. package/utils/api.js +0 -27
  86. package/utils/crypto.js +0 -260
  87. package/utils/index.js +0 -387
  88. package/utils/jwt.js +0 -387
  89. package/utils/logger.js +0 -143
  90. package/utils/redisHelper.js +0 -74
  91. package/utils/sqlManager.js +0 -471
  92. package/utils/tool.js +0 -31
  93. package/utils/validate.js +0 -228
@@ -1,39 +1,66 @@
1
1
  /**
2
- * XML 解析器
2
+ * XML 解析器 - TypeScript 版本
3
3
  * 将 XML 字符串解析为 JSON 对象
4
4
  */
5
- class Xml {
5
+
6
+ /**
7
+ * XML 解析选项
8
+ */
9
+ interface XmlParseOptions {
10
+ /** 是否忽略属性 */
11
+ ignoreAttributes?: boolean;
12
+ /** 属性前缀 */
13
+ attributePrefix?: string;
14
+ /** 文本内容的键名 */
15
+ textKey?: string;
16
+ /** 是否去除首尾空格 */
17
+ trimValues?: boolean;
18
+ /** 是否解析布尔值 */
19
+ parseBooleans?: boolean;
20
+ /** 是否解析数字 */
21
+ parseNumbers?: boolean;
22
+ /** 是否使用自定义解析器 */
23
+ customParser?: boolean;
24
+ }
25
+
26
+ /**
27
+ * 解析结果接口(内部使用)
28
+ */
29
+ interface ParseResult {
30
+ value: Record<string, any>;
31
+ end: number;
32
+ }
33
+
34
+ /**
35
+ * XML 解析器类
36
+ */
37
+ export class Xml {
38
+ private options: Required<XmlParseOptions>;
39
+
6
40
  /**
7
41
  * 构造函数
8
- * @param {Object} options - 解析选项
42
+ * @param options - 解析选项
9
43
  */
10
- constructor(options = {}) {
44
+ constructor(options: XmlParseOptions = {}) {
11
45
  // 默认配置
12
46
  this.options = {
13
- // 基本配置
14
- ignoreAttributes: false, // 是否忽略属性
15
- attributePrefix: '@', // 属性前缀
16
- textKey: '#text', // 文本内容的键名
17
-
18
- // 格式化配置
19
- trimValues: true, // 是否去除首尾空格
20
- parseBooleans: true, // 是否解析布尔值
21
- parseNumbers: true, // 是否解析数字
22
-
23
- // 高级配置
24
- customParser: true, // 是否使用自定义解析器
25
-
26
- // 合并用户选项
47
+ ignoreAttributes: false,
48
+ attributePrefix: '@',
49
+ textKey: '#text',
50
+ trimValues: true,
51
+ parseBooleans: true,
52
+ parseNumbers: true,
53
+ customParser: true,
27
54
  ...options
28
55
  };
29
56
  }
30
57
 
31
58
  /**
32
59
  * 解析 XML 字符串为 JSON 对象
33
- * @param {string} xmlData - XML 字符串
34
- * @returns {Object} 解析后的 JSON 对象
60
+ * @param xmlData - XML 字符串
61
+ * @returns 解析后的 JSON 对象
35
62
  */
36
- parse(xmlData) {
63
+ parse(xmlData: string): Record<string, any> {
37
64
  if (typeof xmlData !== 'string') {
38
65
  throw new Error('无效的 XML 数据');
39
66
  }
@@ -54,20 +81,22 @@ class Xml {
54
81
 
55
82
  // 自定义解析
56
83
  if (this.options.customParser) {
57
- return this.#customParse(xmlData);
84
+ return this.customParse(xmlData);
58
85
  }
86
+
87
+ return {};
59
88
  }
60
89
 
61
90
  /**
62
91
  * 解析属性字符串
63
- * @param {string} attributesStr - 属性字符串
64
- * @param {Object} element - 元素对象
92
+ * @param attributesStr - 属性字符串
93
+ * @param element - 元素对象
65
94
  */
66
- #parseAttributes(attributesStr, element) {
95
+ private parseAttributes(attributesStr: string, element: Record<string, any>): void {
67
96
  // 移除标签名,只保留属性部分
68
97
  const attrPart = attributesStr.replace(/^\w+\s*/, '');
69
98
  const attrRegex = /(\w+)=["']([^"']*)["']/g;
70
- let match;
99
+ let match: RegExpExecArray | null;
71
100
 
72
101
  while ((match = attrRegex.exec(attrPart)) !== null) {
73
102
  const attrName = this.options.attributePrefix + match[1];
@@ -78,18 +107,18 @@ class Xml {
78
107
  element[attrName] = attrValue;
79
108
  } else {
80
109
  // 其他属性进行类型解析
81
- element[attrName] = this.#parseValue(attrValue);
110
+ element[attrName] = this.parseValue(attrValue);
82
111
  }
83
112
  }
84
113
  }
85
114
 
86
115
  /**
87
116
  * 自定义解析方法
88
- * @param {string} xmlData - XML 数据
89
- * @returns {Object} 解析结果
117
+ * @param xmlData - XML 数据
118
+ * @returns 解析结果
90
119
  */
91
- #customParse(xmlData) {
92
- const result = this.#parseXmlElement(xmlData, 0).value;
120
+ private customParse(xmlData: string): Record<string, any> {
121
+ const result = this.parseXmlElement(xmlData, 0).value;
93
122
 
94
123
  // 如果结果只有一个根元素,返回该元素的内容
95
124
  const keys = Object.keys(result);
@@ -101,11 +130,11 @@ class Xml {
101
130
 
102
131
  /**
103
132
  * 解析 XML 元素
104
- * @param {string} xml - XML 字符串
105
- * @param {number} start - 开始位置
106
- * @returns {Object} 包含解析结果和结束位置的对象
133
+ * @param xml - XML 字符串
134
+ * @param start - 开始位置
135
+ * @returns 包含解析结果和结束位置的对象
107
136
  */
108
- #parseXmlElement(xml, start) {
137
+ private parseXmlElement(xml: string, start: number): ParseResult {
109
138
  const tagStart = xml.indexOf('<', start);
110
139
  if (tagStart === -1) {
111
140
  return { value: {}, end: xml.length };
@@ -117,13 +146,13 @@ class Xml {
117
146
  }
118
147
 
119
148
  const fullTag = xml.slice(tagStart + 1, tagEnd);
120
- const element = {};
149
+ const element: Record<string, any> = {};
121
150
 
122
151
  // 自闭合标签
123
152
  if (fullTag.endsWith('/')) {
124
153
  const tagName = fullTag.slice(0, -1).split(/\s+/)[0];
125
154
  if (!this.options.ignoreAttributes && fullTag.includes(' ')) {
126
- this.#parseAttributes(fullTag.slice(0, -1), element);
155
+ this.parseAttributes(fullTag.slice(0, -1), element);
127
156
  }
128
157
  return { value: { [tagName]: Object.keys(element).length > 0 ? element : '' }, end: tagEnd + 1 };
129
158
  }
@@ -134,7 +163,7 @@ class Xml {
134
163
  if (commentEnd === -1) {
135
164
  throw new Error('格式错误:未找到注释结束符');
136
165
  }
137
- return this.#parseXmlElement(xml, commentEnd + 3);
166
+ return this.parseXmlElement(xml, commentEnd + 3);
138
167
  }
139
168
 
140
169
  // CDATA
@@ -143,15 +172,14 @@ class Xml {
143
172
  if (cdataEnd === -1) {
144
173
  throw new Error('格式错误:未找到 CDATA 结束符');
145
174
  }
146
- const cdataContent = xml.slice(tagEnd + 1, cdataEnd);
147
- return this.#parseXmlElement(xml, cdataEnd + 3);
175
+ return this.parseXmlElement(xml, cdataEnd + 3);
148
176
  }
149
177
 
150
178
  const tagName = fullTag.split(/\s+/)[0];
151
179
 
152
180
  // 解析属性
153
181
  if (!this.options.ignoreAttributes && fullTag.includes(' ')) {
154
- this.#parseAttributes(fullTag, element);
182
+ this.parseAttributes(fullTag, element);
155
183
  }
156
184
 
157
185
  // 查找结束标签
@@ -171,7 +199,7 @@ class Xml {
171
199
 
172
200
  // 如果没有子标签,直接解析文本
173
201
  if (!content.includes('<')) {
174
- const textValue = this.#parseValue(content);
202
+ const textValue = this.parseValue(content);
175
203
  if (Object.keys(element).length === 0) {
176
204
  return { value: { [tagName]: textValue }, end: endTagStart + endTag.length };
177
205
  } else {
@@ -181,7 +209,7 @@ class Xml {
181
209
  } else {
182
210
  // 解析子元素和混合内容
183
211
  let pos = 0;
184
- const texts = [];
212
+ const texts: string[] = [];
185
213
 
186
214
  while (pos < content.length) {
187
215
  const nextTag = content.indexOf('<', pos);
@@ -200,12 +228,12 @@ class Xml {
200
228
  }
201
229
 
202
230
  // 解析子元素
203
- const childResult = this.#parseXmlElement(content, nextTag);
231
+ const childResult = this.parseXmlElement(content, nextTag);
204
232
  const childObj = childResult.value;
205
233
 
206
234
  // 合并子元素到当前元素
207
235
  for (const [key, value] of Object.entries(childObj)) {
208
- this.#addElement(element, key, value);
236
+ this.addElement(element, key, value);
209
237
  }
210
238
 
211
239
  pos = childResult.end;
@@ -215,9 +243,9 @@ class Xml {
215
243
  if (texts.length > 0) {
216
244
  const combinedText = texts.join(' ');
217
245
  if (Object.keys(element).length === 0) {
218
- return { value: { [tagName]: this.#parseValue(combinedText) }, end: endTagStart + endTag.length };
246
+ return { value: { [tagName]: this.parseValue(combinedText) }, end: endTagStart + endTag.length };
219
247
  } else {
220
- element[this.options.textKey] = this.#parseValue(combinedText);
248
+ element[this.options.textKey] = this.parseValue(combinedText);
221
249
  }
222
250
  }
223
251
 
@@ -227,11 +255,11 @@ class Xml {
227
255
 
228
256
  /**
229
257
  * 添加元素到对象
230
- * @param {Object} parent - 父对象
231
- * @param {string} name - 元素名
232
- * @param {any} value - 元素值
258
+ * @param parent - 父对象
259
+ * @param name - 元素名
260
+ * @param value - 元素值
233
261
  */
234
- #addElement(parent, name, value) {
262
+ private addElement(parent: Record<string, any>, name: string, value: any): void {
235
263
  if (parent[name] === undefined) {
236
264
  parent[name] = value;
237
265
  } else if (Array.isArray(parent[name])) {
@@ -243,10 +271,10 @@ class Xml {
243
271
 
244
272
  /**
245
273
  * 解析值的类型
246
- * @param {string} value - 原始值
247
- * @returns {any} 解析后的值
274
+ * @param value - 原始值
275
+ * @returns 解析后的值
248
276
  */
249
- #parseValue(value) {
277
+ private parseValue(value: string): string | number | boolean {
250
278
  if (!value || typeof value !== 'string') {
251
279
  return value;
252
280
  }
@@ -288,16 +316,16 @@ class Xml {
288
316
  }
289
317
 
290
318
  // 处理 XML 实体
291
- return this.#decodeEntities(value);
319
+ return this.decodeEntities(value);
292
320
  }
293
321
 
294
322
  /**
295
323
  * 解码 XML 实体
296
- * @param {string} value - 包含实体的字符串
297
- * @returns {string} 解码后的字符串
324
+ * @param value - 包含实体的字符串
325
+ * @returns 解码后的字符串
298
326
  */
299
- #decodeEntities(value) {
300
- const entities = {
327
+ private decodeEntities(value: string): string {
328
+ const entities: Record<string, string> = {
301
329
  '&amp;': '&',
302
330
  '&lt;': '<',
303
331
  '&gt;': '>',
@@ -312,39 +340,39 @@ class Xml {
312
340
 
313
341
  /**
314
342
  * 静态方法:快速解析 XML
315
- * @param {string} xmlData - XML 数据
316
- * @param {Object} options - 解析选项
317
- * @returns {Object} 解析后的 JSON 对象
343
+ * @param xmlData - XML 数据
344
+ * @param options - 解析选项
345
+ * @returns 解析后的 JSON 对象
318
346
  */
319
- static parse(xmlData, options = {}) {
347
+ static parse(xmlData: string, options: XmlParseOptions = {}): Record<string, any> {
320
348
  const parser = new Xml(options);
321
349
  return parser.parse(xmlData);
322
350
  }
323
351
 
324
352
  /**
325
353
  * 静态方法:解析 XML 并保留属性
326
- * @param {string} xmlData - XML 数据
327
- * @returns {Object} 解析后的 JSON 对象
354
+ * @param xmlData - XML 数据
355
+ * @returns 解析后的 JSON 对象
328
356
  */
329
- static parseWithAttributes(xmlData) {
357
+ static parseWithAttributes(xmlData: string): Record<string, any> {
330
358
  return Xml.parse(xmlData, { ignoreAttributes: false });
331
359
  }
332
360
 
333
361
  /**
334
362
  * 静态方法:解析 XML 并忽略属性
335
- * @param {string} xmlData - XML 数据
336
- * @returns {Object} 解析后的 JSON 对象
363
+ * @param xmlData - XML 数据
364
+ * @returns 解析后的 JSON 对象
337
365
  */
338
- static parseIgnoreAttributes(xmlData) {
366
+ static parseIgnoreAttributes(xmlData: string): Record<string, any> {
339
367
  return Xml.parse(xmlData, { ignoreAttributes: true });
340
368
  }
341
369
 
342
370
  /**
343
371
  * 静态方法:验证 XML 格式
344
- * @param {string} xmlData - XML 数据
345
- * @returns {boolean} 是否有效
372
+ * @param xmlData - XML 数据
373
+ * @returns 是否有效
346
374
  */
347
- static validate(xmlData) {
375
+ static validate(xmlData: string): boolean {
348
376
  try {
349
377
  Xml.parse(xmlData);
350
378
  return true;
@@ -353,5 +381,3 @@ class Xml {
353
381
  }
354
382
  }
355
383
  }
356
-
357
- export { Xml };
package/.npmrc DELETED
@@ -1,3 +0,0 @@
1
- # registry=https://registry.npmmirror.com
2
- registry=https://registry.npmjs.org
3
- link-workspace-packages=true
package/.prettierignore DELETED
@@ -1,2 +0,0 @@
1
- LICENSE
2
- LICENSE.md
package/.prettierrc DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "trailingComma": "none",
3
- "tabWidth": 4,
4
- "semi": true,
5
- "singleQuote": true,
6
- "printWidth": 1024,
7
- "bracketSpacing": true,
8
- "useTabs": false,
9
- "arrowParens": "always",
10
- "endOfLine": "lf"
11
- }
@@ -1,49 +0,0 @@
1
- import { Env } from '../../config/env.js';
2
- import { Api } from '../../utils/api.js';
3
- import { RYes, RNo } from '../../utils/index.js';
4
-
5
- export default Api.POST(
6
- //
7
- '健康检查',
8
- false,
9
- {},
10
- [],
11
- async (befly, ctx) => {
12
- try {
13
- const info = {
14
- status: 'ok',
15
- timestamp: new Date().toISOString(),
16
- uptime: process.uptime(),
17
- memory: process.memoryUsage(),
18
- runtime: 'Bun',
19
- version: Bun.version,
20
- platform: process.platform,
21
- arch: process.arch
22
- };
23
- // 检查 Redis 连接状态
24
- if (Env.REDIS_ENABLE === 1) {
25
- if (befly.redis) {
26
- try {
27
- await befly.redis.ping();
28
- info.redis = '已连接';
29
- } catch (error) {
30
- info.redis = '未连接';
31
- info.redisError = error.message;
32
- }
33
- } else {
34
- info.redis = '未开启';
35
- }
36
- } else {
37
- info.redis = '禁用';
38
- }
39
- return RYes('健康检查成功', info);
40
- } catch (error) {
41
- befly.logger.error({
42
- msg: '健康检查失败',
43
- error: error.message,
44
- stack: error.stack
45
- });
46
- return RNo('健康检查失败');
47
- }
48
- }
49
- );
@@ -1,29 +0,0 @@
1
- import { Env } from '../../config/env.js';
2
- import { Api } from '../../utils/api.js';
3
- import { RYes, RNo } from '../../utils/index.js';
4
- import { Jwt } from '../../utils/jwt.js';
5
-
6
- export default Api.POST(
7
- //
8
- '令牌检测',
9
- false,
10
- {},
11
- [],
12
- async (befly, ctx) => {
13
- try {
14
- const token = ctx.headers?.authorization?.split(' ')[1] || '';
15
- if (!token) {
16
- return RNo('令牌不能为空');
17
- }
18
- const jwtData = await Jwt.verify(token);
19
- return RYes('令牌有效');
20
- } catch (error) {
21
- befly.logger.error({
22
- msg: '令牌检测失败',
23
- error: error.message,
24
- stack: error.stack
25
- });
26
- return RNo('令牌检测失败');
27
- }
28
- }
29
- );
package/checks/table.js DELETED
@@ -1,221 +0,0 @@
1
- import path from 'node:path';
2
- import { Logger } from '../utils/logger.js';
3
- import { parseFieldRule, validateFieldName, validateFieldType, validateMinMax, validateDefaultValue, validateIndex, validateRegex } from '../utils/index.js';
4
- import { __dirtables, getProjectDir } from '../system.js';
5
-
6
- // 所有校验函数均复用 utils/index.js 导出的实现
7
-
8
- export const checkTable = async () => {
9
- try {
10
- const tablesGlob = new Bun.Glob('*.json');
11
-
12
- // 统计信息
13
- let totalFiles = 0;
14
- let totalRules = 0;
15
- let validFiles = 0;
16
- let invalidFiles = 0;
17
-
18
- // 收集所有表文件
19
- const allTableFiles = [];
20
- const coreTableNames = new Set(); // 存储内核表文件名
21
-
22
- // 收集内核表字段定义文件
23
- for await (const file of tablesGlob.scan({
24
- cwd: __dirtables,
25
- absolute: true,
26
- onlyFiles: true
27
- })) {
28
- const fileName = path.basename(file, '.json');
29
- coreTableNames.add(fileName);
30
- allTableFiles.push({ file, type: 'core' });
31
- }
32
-
33
- // 收集项目表字段定义文件,并检查是否与内核表同名
34
- for await (const file of tablesGlob.scan({
35
- cwd: getProjectDir('tables'),
36
- absolute: true,
37
- onlyFiles: true
38
- })) {
39
- const fileName = path.basename(file, '.json');
40
-
41
- // 检查项目表是否与内核表同名
42
- if (coreTableNames.has(fileName)) {
43
- Logger.error(`项目表 ${fileName}.json 与内核表同名,项目表不能与内核表定义文件同名`);
44
- invalidFiles++;
45
- totalFiles++;
46
- continue;
47
- }
48
-
49
- allTableFiles.push({ file, type: 'project' });
50
- }
51
-
52
- // 保留字段列表
53
- const reservedFields = ['id', 'created_at', 'updated_at', 'deleted_at', 'state'];
54
-
55
- // 合并进行验证逻辑
56
- for (const { file, type } of allTableFiles) {
57
- totalFiles++;
58
- const fileName = path.basename(file);
59
- const fileBaseName = path.basename(file, '.json');
60
- const fileType = type === 'core' ? '内核' : '项目';
61
-
62
- try {
63
- // 1) 文件名小驼峰校验:必须以小写字母开头,后续可包含小写/数字,或多个 [大写+小写/数字] 片段
64
- // 示例:userTable、testCustomers、common
65
- const lowerCamelCaseRegex = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
66
- if (!lowerCamelCaseRegex.test(fileBaseName)) {
67
- Logger.error(`${fileType}表 ${fileName} 文件名必须使用小驼峰命名(例如 testCustomers.json)`);
68
- // 命名不合规,直接标记为无效文件
69
- throw new Error('文件命名不符合小驼峰规范');
70
- }
71
-
72
- // 读取并解析 JSON 文件
73
- const table = await Bun.file(file).json();
74
- let fileValid = true;
75
- let fileRules = 0;
76
-
77
- // 检查 table 中的每个验证规则
78
- for (const [fieldName, rule] of Object.entries(table)) {
79
- fileRules++;
80
- totalRules++;
81
-
82
- // 检查是否使用了保留字段
83
- if (reservedFields.includes(fieldName)) {
84
- Logger.error(`${fileType}表 ${fileName} 文件包含保留字段 ${fieldName},不能在表定义中使用以下字段: ${reservedFields.join(', ')}`);
85
- fileValid = false;
86
- continue;
87
- }
88
-
89
- // 验证规则格式
90
- try {
91
- const ruleParts = parseFieldRule(rule);
92
-
93
- if (ruleParts.length !== 7) {
94
- Logger.warn(`${fileType}表 ${fileName} 文件 ${fieldName} 验证规则错误,应包含 7 个部分,但包含 ${ruleParts.length} 个部分`);
95
- fileValid = false;
96
- continue;
97
- }
98
-
99
- const [name, type, minStr, maxStr, defaultValue, isIndexStr, regexConstraint] = ruleParts;
100
-
101
- // 使用新的验证函数进行严格验证
102
- // 第1个值:名称必须为中文、数字、字母
103
- if (!validateFieldName(name)) {
104
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 名称 "${name}" 格式错误,必须为中文、数字、字母`);
105
- fileValid = false;
106
- continue;
107
- }
108
-
109
- // 第2个值:字段类型必须为string,number,text,array之一
110
- if (!validateFieldType(type)) {
111
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 类型 "${type}" 格式错误,必须为string、number、text、array之一`);
112
- fileValid = false;
113
- continue;
114
- }
115
-
116
- // 第3个值:最小值必须为null或数字
117
- if (!validateMinMax(minStr)) {
118
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最小值 "${minStr}" 格式错误,必须为null或数字`);
119
- fileValid = false;
120
- continue;
121
- }
122
-
123
- // 第4个值与类型联动校验
124
- if (type === 'text') {
125
- // text 类型:不允许设置最小/最大长度与默认值(均需为 null)
126
- if (minStr !== 'null') {
127
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 的 text 类型最小值必须为 null,当前为 "${minStr}"`);
128
- fileValid = false;
129
- continue;
130
- }
131
- if (maxStr !== 'null') {
132
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 的 text 类型最大长度必须为 null,当前为 "${maxStr}"`);
133
- fileValid = false;
134
- continue;
135
- }
136
- } else if (type === 'string') {
137
- // string:最大长度必为具体数字,且 1..65535
138
- if (maxStr === 'null' || !validateMinMax(maxStr)) {
139
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大长度 "${maxStr}" 格式错误,string 类型必须为具体数字`);
140
- fileValid = false;
141
- continue;
142
- }
143
- const maxVal = parseInt(maxStr, 10);
144
- if (!(maxVal > 0 && maxVal <= 65535)) {
145
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大长度 ${maxStr} 越界,string 类型长度必须在 1..65535 范围内`);
146
- fileValid = false;
147
- continue;
148
- }
149
- } else if (type === 'array') {
150
- // array:最大长度必为数字(用于JSON字符串长度限制)
151
- if (maxStr === 'null' || !validateMinMax(maxStr)) {
152
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大长度 "${maxStr}" 格式错误,array 类型必须为具体数字`);
153
- fileValid = false;
154
- continue;
155
- }
156
- } else {
157
- // number 等其他:允许 null 或数字
158
- if (!validateMinMax(maxStr)) {
159
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大值 "${maxStr}" 格式错误,必须为null或数字`);
160
- fileValid = false;
161
- continue;
162
- }
163
- }
164
-
165
- // 第5个值:默认值校验
166
- if (type === 'text') {
167
- // text 不允许默认值
168
- if (defaultValue !== 'null') {
169
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 为 text 类型,默认值必须为 null,当前为 "${defaultValue}"`);
170
- fileValid = false;
171
- continue;
172
- }
173
- } else {
174
- if (!validateDefaultValue(defaultValue)) {
175
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 默认值 "${defaultValue}" 格式错误,必须为null、字符串或数字`);
176
- fileValid = false;
177
- continue;
178
- }
179
- }
180
-
181
- // 第6个值:是否创建索引必须为0或1
182
- if (!validateIndex(isIndexStr)) {
183
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 索引标识 "${isIndexStr}" 格式错误,必须为0或1`);
184
- fileValid = false;
185
- continue;
186
- }
187
-
188
- // 第7个值:必须为null或正则表达式
189
- if (!validateRegex(regexConstraint)) {
190
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 正则约束 "${regexConstraint}" 格式错误,必须为null或有效的正则表达式`);
191
- fileValid = false;
192
- continue;
193
- }
194
- } catch (error) {
195
- Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 验证规则解析失败: ${error.message}`);
196
- fileValid = false;
197
- continue;
198
- }
199
- }
200
-
201
- if (fileValid) {
202
- validFiles++;
203
- } else {
204
- invalidFiles++;
205
- }
206
- } catch (error) {
207
- Logger.error(`${fileType}表 ${fileName} 解析失败: ${error.message}`);
208
- invalidFiles++;
209
- }
210
- }
211
-
212
- if (invalidFiles > 0) {
213
- return false;
214
- } else {
215
- return true;
216
- }
217
- } catch (error) {
218
- Logger.error(`Tables 检查过程中出错:`, error);
219
- return false;
220
- }
221
- };