befly 3.8.29 → 3.8.31

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 (70) hide show
  1. package/README.md +91 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkApp.ts +31 -1
  4. package/checks/checkTable.ts +3 -2
  5. package/hooks/cors.ts +3 -3
  6. package/hooks/parser.ts +8 -6
  7. package/hooks/permission.ts +12 -5
  8. package/hooks/validator.ts +1 -1
  9. package/lib/cacheHelper.ts +73 -65
  10. package/lib/cipher.ts +2 -1
  11. package/lib/connect.ts +23 -52
  12. package/lib/dbHelper.ts +14 -11
  13. package/lib/jwt.ts +58 -437
  14. package/lib/logger.ts +76 -197
  15. package/lib/redisHelper.ts +163 -1
  16. package/lib/sqlBuilder.ts +2 -1
  17. package/lib/validator.ts +150 -384
  18. package/loader/loadApis.ts +4 -7
  19. package/loader/loadHooks.ts +6 -5
  20. package/loader/loadPlugins.ts +11 -13
  21. package/main.ts +26 -53
  22. package/package.json +10 -8
  23. package/paths.ts +0 -6
  24. package/plugins/cipher.ts +1 -1
  25. package/plugins/config.ts +3 -4
  26. package/plugins/db.ts +6 -7
  27. package/plugins/jwt.ts +7 -6
  28. package/plugins/logger.ts +6 -6
  29. package/plugins/redis.ts +9 -13
  30. package/router/api.ts +2 -2
  31. package/router/static.ts +4 -8
  32. package/sync/syncAll.ts +8 -13
  33. package/sync/syncApi.ts +14 -10
  34. package/sync/syncDb/apply.ts +1 -2
  35. package/sync/syncDb.ts +12 -15
  36. package/sync/syncDev.ts +19 -56
  37. package/sync/syncMenu.ts +182 -137
  38. package/tests/cacheHelper.test.ts +327 -0
  39. package/tests/dbHelper-columns.test.ts +5 -20
  40. package/tests/dbHelper-execute.test.ts +14 -68
  41. package/tests/fields-redis-cache.test.ts +5 -3
  42. package/tests/integration.test.ts +17 -32
  43. package/tests/jwt.test.ts +36 -94
  44. package/tests/logger.test.ts +32 -34
  45. package/tests/redisHelper.test.ts +271 -2
  46. package/tests/redisKeys.test.ts +76 -0
  47. package/tests/sync-connection.test.ts +0 -6
  48. package/tests/syncDb-constants.test.ts +12 -12
  49. package/tests/util.test.ts +5 -1
  50. package/tests/validator.test.ts +611 -85
  51. package/types/befly.d.ts +9 -15
  52. package/types/cache.d.ts +73 -0
  53. package/types/common.d.ts +10 -128
  54. package/types/database.d.ts +221 -5
  55. package/types/index.ts +6 -5
  56. package/types/plugin.d.ts +1 -4
  57. package/types/redis.d.ts +37 -2
  58. package/types/table.d.ts +175 -0
  59. package/config.ts +0 -70
  60. package/hooks/_rateLimit.ts +0 -64
  61. package/lib/regexAliases.ts +0 -59
  62. package/lib/xml.ts +0 -383
  63. package/tests/validator-advanced.test.ts +0 -653
  64. package/tests/xml.test.ts +0 -101
  65. package/types/addon.d.ts +0 -50
  66. package/types/crypto.d.ts +0 -23
  67. package/types/jwt.d.ts +0 -99
  68. package/types/logger.d.ts +0 -43
  69. package/types/tool.d.ts +0 -67
  70. package/types/validator.d.ts +0 -43
package/lib/validator.ts CHANGED
@@ -1,461 +1,227 @@
1
1
  /**
2
2
  * 数据验证器 - Befly 项目专用
3
- * 内置 RegexAliases,支持对象格式的字段定义
3
+ * 纯静态类设计,简洁易用
4
4
  */
5
5
 
6
- import type { TableDefinition, FieldDefinition } from '../types/common.js';
7
- import type { ValidationResult, ValidationError } from '../types/validator';
8
- import { RegexAliases } from './regexAliases.js';
6
+ import { RegexAliases, getCompiledRegex } from 'befly-shared/regex';
7
+ import type { TableDefinition, FieldDefinition, ValidateResult, SingleResult } from 'befly-shared/types';
9
8
 
10
9
  /**
11
- * 验证器类(Befly 项目专用)
12
- * 内置 RegexAliases,直接使用 util.ts 中的 parseRule
10
+ * 验证器类
11
+ *
12
+ * @example
13
+ * const result = Validator.validate(data, rules, ['email', 'name']);
14
+ * if (result.failed) {
15
+ * console.log(result.firstError);
16
+ * console.log(result.errors);
17
+ * console.log(result.errorFields);
18
+ * }
19
+ *
20
+ * const single = Validator.single(value, fieldDef);
21
+ * if (!single.error) {
22
+ * console.log(single.value);
23
+ * }
13
24
  */
14
25
  export class Validator {
15
- /** 正则别名映射(内置) */
16
- private readonly regexAliases: Record<string, string> = RegexAliases;
17
-
18
26
  /**
19
- * 验证数据
20
- * @param data - 要验证的数据对象
21
- * @param rules - 验证规则对象
22
- * @param required - 必传字段数组
23
- * @returns 验证结果 { code: 0|1, fields: {} }
27
+ * 验证数据对象
24
28
  */
25
- validate(data: Record<string, any>, rules: TableDefinition, required: string[] = []): ValidationResult {
26
- const result: ValidationResult = {
27
- code: 0,
28
- fields: {}
29
- };
29
+ static validate(data: Record<string, any>, rules: TableDefinition, required: string[] = []): ValidateResult {
30
+ const fieldErrors: Record<string, string> = {};
30
31
 
31
32
  // 参数检查
32
- if (!this.checkParams(data, rules, required, result)) {
33
- return result;
34
- }
35
-
36
- // 检查必传字段
37
- this.checkRequiredFields(data, rules, required, result);
38
-
39
- // 验证所有在规则中定义的字段
40
- this.validateFields(data, rules, required, result);
41
-
42
- return result;
43
- }
44
-
45
- /**
46
- * 检查参数有效性
47
- */
48
- private checkParams(data: any, rules: any, required: any, result: ValidationResult): boolean {
49
33
  if (!data || typeof data !== 'object' || Array.isArray(data)) {
50
- result.code = 1;
51
- result.fields.error = '数据必须是对象格式';
52
- return false;
34
+ return this.buildResult({ _error: '数据必须是对象格式' });
53
35
  }
54
-
55
36
  if (!rules || typeof rules !== 'object') {
56
- result.code = 1;
57
- result.fields.error = '验证规则必须是对象格式';
58
- return false;
37
+ return this.buildResult({ _error: '验证规则必须是对象格式' });
59
38
  }
60
39
 
61
- if (!Array.isArray(required)) {
62
- result.code = 1;
63
- result.fields.error = '必传字段必须是数组格式';
64
- return false;
40
+ // 检查必填字段
41
+ for (const field of required) {
42
+ const value = data[field];
43
+ if (value === undefined || value === null || value === '') {
44
+ const label = rules[field]?.name || field;
45
+ fieldErrors[field] = `${label}为必填项`;
46
+ }
65
47
  }
66
48
 
67
- return true;
68
- }
49
+ // 验证有值的字段
50
+ for (const [field, rule] of Object.entries(rules)) {
51
+ if (fieldErrors[field]) continue;
52
+ if (!(field in data) && !required.includes(field)) continue;
69
53
 
70
- /**
71
- * 检查必传字段
72
- */
73
- private checkRequiredFields(data: Record<string, any>, rules: TableDefinition, required: string[], result: ValidationResult): void {
74
- for (const fieldName of required) {
75
- const value = data[fieldName];
76
- if (!(fieldName in data) || value === undefined || value === null || value === '') {
77
- result.code = 1;
78
- const fieldDef = rules[fieldName];
79
- const fieldLabel = fieldDef?.name || fieldName;
80
- result.fields[fieldName] = `${fieldLabel}(${fieldName})为必填项`;
81
- }
54
+ const error = this.checkField(data[field], rule, field);
55
+ if (error) fieldErrors[field] = error;
82
56
  }
57
+
58
+ return this.buildResult(fieldErrors);
83
59
  }
84
60
 
85
61
  /**
86
- * 验证所有字段
62
+ * 验证单个值(带类型转换)
87
63
  */
88
- private validateFields(data: Record<string, any>, rules: TableDefinition, required: string[], result: ValidationResult): void {
89
- for (const [fieldName, rule] of Object.entries(rules)) {
90
- // 如果字段不存在且不是必传字段,跳过验证
91
- if (!(fieldName in data) && !required.includes(fieldName)) {
92
- continue;
93
- }
94
-
95
- // 如果必传验证已经失败,跳过后续验证
96
- if (result.fields[fieldName]) {
97
- continue;
98
- }
64
+ static single(value: any, fieldDef: FieldDefinition): SingleResult {
65
+ const { type, default: defaultValue } = fieldDef;
99
66
 
100
- const value = data[fieldName];
101
- const error = this.validateFieldValue(value, rules[fieldName], fieldName);
102
-
103
- if (error) {
104
- result.code = 1;
105
- result.fields[fieldName] = error;
106
- }
67
+ // 处理空值
68
+ if (value === undefined || value === null || value === '') {
69
+ return { value: this.defaultFor(type, defaultValue), error: null };
107
70
  }
108
- }
109
71
 
110
- /**
111
- * 解析 regex 别名
112
- */
113
- private resolveRegexAlias(regex: string | null): string | null {
114
- if (!regex || typeof regex !== 'string') {
115
- return regex;
72
+ // 类型转换
73
+ const converted = this.convert(value, type);
74
+ if (converted.error) {
75
+ return { value: null, error: converted.error };
116
76
  }
117
77
 
118
- if (regex.startsWith('@')) {
119
- const aliasName = regex.substring(1);
120
- const resolvedRegex = this.regexAliases[aliasName];
121
- if (resolvedRegex) {
122
- return resolvedRegex;
123
- }
124
- return regex;
78
+ // 规则验证
79
+ const error = this.checkRule(converted.value, fieldDef);
80
+ if (error) {
81
+ return { value: null, error: error };
125
82
  }
126
83
 
127
- return regex;
84
+ return { value: converted.value, error: null };
128
85
  }
129
86
 
130
- /**
131
- * 验证单个字段的值
132
- */
133
- private validateFieldValue(value: any, fieldDef: FieldDefinition, fieldName: string): ValidationError {
134
- let { name, type, min, max, regexp } = fieldDef;
87
+ // ========== 私有方法 ==========
135
88
 
136
- regexp = this.resolveRegexAlias(regexp);
89
+ /** 构建结果对象 */
90
+ private static buildResult(fieldErrors: Record<string, string>): ValidateResult {
91
+ const errors = Object.values(fieldErrors);
92
+ const errorFields = Object.keys(fieldErrors);
93
+ const failed = errors.length > 0;
137
94
 
138
- switch (type.toLowerCase()) {
139
- case 'number':
140
- return this.validateNumber(value, name, min, max, regexp, fieldName);
141
- case 'string':
142
- case 'text':
143
- return this.validateString(value, name, min, max, regexp, fieldName);
144
- case 'array_string':
145
- case 'array_text':
146
- return this.validateArray(value, name, min, max, regexp, fieldName);
147
- default:
148
- return `字段 ${fieldName} 的类型 ${type} 不支持`;
149
- }
95
+ return {
96
+ code: failed ? 1 : 0,
97
+ failed: failed,
98
+ firstError: failed ? errors[0] : null,
99
+ errors: errors,
100
+ errorFields: errorFields,
101
+ fieldErrors: fieldErrors
102
+ };
150
103
  }
151
104
 
152
- /**
153
- * 验证数字类型
154
- */
155
- private validateNumber(value: any, name: string, min: number | null, max: number | null, spec: string | null, fieldName: string): ValidationError {
156
- try {
157
- // 允许数字类型的字符串
158
- let numValue = value;
159
- if (typeof value === 'string') {
160
- numValue = Number(value);
161
- if (Number.isNaN(numValue) || !isFinite(numValue)) {
162
- return `${name}(${fieldName})必须是数字`;
163
- }
164
- } else if (typeof numValue !== 'number' || Number.isNaN(numValue) || !isFinite(numValue)) {
165
- return `${name}(${fieldName})必须是数字`;
166
- }
167
-
168
- if (min !== null && numValue < min) {
169
- return `${name}(${fieldName})不能小于${min}`;
170
- }
171
-
172
- if (max !== null && max > 0 && numValue > max) {
173
- return `${name}(${fieldName})不能大于${max}`;
174
- }
175
-
176
- if (spec && spec.trim() !== '') {
177
- try {
178
- const regExp = new RegExp(spec);
179
- if (!regExp.test(String(numValue))) {
180
- return `${name}(${fieldName})格式不正确`;
181
- }
182
- } catch (error: any) {
183
- return `${name}(${fieldName})的正则表达式格式错误`;
184
- }
185
- }
105
+ /** 验证单个字段 */
106
+ private static checkField(value: any, fieldDef: FieldDefinition, fieldName: string): string | null {
107
+ const label = fieldDef.name || fieldName;
186
108
 
187
- return null;
188
- } catch (error: any) {
189
- return `${name}(${fieldName})验证出错: ${error.message}`;
109
+ const converted = this.convert(value, fieldDef.type);
110
+ if (converted.error) {
111
+ return `${label}${converted.error}`;
190
112
  }
191
- }
192
113
 
193
- /**
194
- * 验证字符串类型
195
- */
196
- private validateString(value: any, name: string, min: number | null, max: number | null, spec: string | null, fieldName: string): ValidationError {
197
- try {
198
- if (typeof value !== 'string') {
199
- return `${name}(${fieldName})必须是字符串`;
200
- }
201
-
202
- if (min !== null && value.length < min) {
203
- return `${name}(${fieldName})长度不能少于${min}个字符`;
204
- }
205
-
206
- if (max !== null && max > 0 && value.length > max) {
207
- return `${name}(${fieldName})长度不能超过${max}个字符`;
208
- }
209
-
210
- if (spec && spec.trim() !== '') {
211
- try {
212
- const regExp = new RegExp(spec);
213
- if (!regExp.test(value)) {
214
- return `${name}(${fieldName})格式不正确`;
215
- }
216
- } catch (error: any) {
217
- return `${name}(${fieldName})的正则表达式格式错误`;
218
- }
219
- }
220
-
221
- return null;
222
- } catch (error: any) {
223
- return `${name}(${fieldName})验证出错: ${error.message}`;
224
- }
114
+ const error = this.checkRule(converted.value, fieldDef);
115
+ return error ? `${label}${error}` : null;
225
116
  }
226
117
 
227
- /**
228
- * 验证数组类型
229
- */
230
- private validateArray(value: any, name: string, min: number | null, max: number | null, spec: string | null, fieldName: string): ValidationError {
231
- try {
232
- if (!Array.isArray(value)) {
233
- return `${name}(${fieldName})必须是数组`;
234
- }
235
-
236
- if (min !== null && value.length < min) {
237
- return `${name}(${fieldName})至少需要${min}个元素`;
238
- }
239
-
240
- if (max !== null && max > 0 && value.length > max) {
241
- return `${name}(${fieldName})最多只能有${max}个元素`;
242
- }
243
-
244
- if (spec && spec.trim() !== '') {
245
- try {
246
- const regExp = new RegExp(spec);
247
- for (const item of value) {
248
- if (!regExp.test(String(item))) {
249
- return `${name}(${fieldName})中的元素"${item}"格式不正确`;
250
- }
251
- }
252
- } catch (error: any) {
253
- return `${name}(${fieldName})的正则表达式格式错误`;
118
+ /** 类型转换 */
119
+ private static convert(value: any, type: string): { value: any; error: string | null } {
120
+ switch (type.toLowerCase()) {
121
+ case 'number':
122
+ if (typeof value === 'number') {
123
+ return Number.isNaN(value) || !isFinite(value) ? { value: null, error: '必须是有效数字' } : { value: value, error: null };
254
124
  }
255
- }
256
-
257
- return null;
258
- } catch (error: any) {
259
- return `${name}(${fieldName})验证出错: ${error.message}`;
260
- }
261
- }
125
+ if (typeof value === 'string') {
126
+ const num = Number(value);
127
+ return Number.isNaN(num) || !isFinite(num) ? { value: null, error: '必须是数字' } : { value: num, error: null };
128
+ }
129
+ return { value: null, error: '必须是数字' };
262
130
 
263
- /**
264
- * 验证单个值(支持对象格式字段定义)
265
- */
266
- validateSingleValue(value: any, fieldDef: FieldDefinition): { valid: boolean; value: any; errors: string[] } {
267
- let { name, type, min, max, regexp, default: defaultValue } = fieldDef;
131
+ case 'string':
132
+ case 'text':
133
+ return typeof value === 'string' ? { value: value, error: null } : { value: null, error: '必须是字符串' };
268
134
 
269
- regexp = this.resolveRegexAlias(regexp);
135
+ case 'array_string':
136
+ case 'array_text':
137
+ return Array.isArray(value) ? { value: value, error: null } : { value: null, error: '必须是数组' };
270
138
 
271
- // 处理 undefined/null 值,使用默认值
272
- if (value === undefined || value === null) {
273
- if (defaultValue !== null) {
274
- if ((type === 'array_string' || type === 'array_text') && typeof defaultValue === 'string') {
275
- if (defaultValue === '[]') {
276
- return { valid: true, value: [], errors: [] };
277
- }
278
- try {
279
- const parsedArray = JSON.parse(defaultValue);
280
- if (Array.isArray(parsedArray)) {
281
- return { valid: true, value: parsedArray, errors: [] };
282
- }
283
- } catch {
284
- return { valid: true, value: [], errors: [] };
285
- }
286
- }
287
- // 数字类型默认值转换
288
- if (type === 'number' && typeof defaultValue === 'string') {
289
- const numValue = Number(defaultValue);
290
- if (!isNaN(numValue)) {
291
- return { valid: true, value: numValue, errors: [] };
292
- }
293
- }
294
- return { valid: true, value: defaultValue, errors: [] };
295
- }
296
- if (type === 'number') {
297
- return { valid: true, value: 0, errors: [] };
298
- } else if (type === 'array_string' || type === 'array_text') {
299
- return { valid: true, value: [], errors: [] };
300
- } else if (type === 'string' || type === 'text') {
301
- return { valid: true, value: '', errors: [] };
302
- }
139
+ default:
140
+ return { value: value, error: null };
303
141
  }
142
+ }
304
143
 
305
- const errors: string[] = [];
144
+ /** 规则验证 */
145
+ private static checkRule(value: any, fieldDef: FieldDefinition): string | null {
146
+ const { type, min, max, regexp } = fieldDef;
147
+ const regex = this.resolveRegex(regexp);
306
148
 
307
- // 类型转换
308
- let convertedValue = value;
309
- if (type === 'number' && typeof value === 'string') {
310
- convertedValue = Number(value);
311
- if (isNaN(convertedValue)) {
312
- errors.push(`${name || '值'}必须是有效的数字`);
313
- return { valid: false, value: null, errors };
314
- }
315
- }
316
-
317
- // 类型验证
318
149
  switch (type.toLowerCase()) {
319
150
  case 'number':
320
- if (typeof convertedValue !== 'number' || Number.isNaN(convertedValue)) {
321
- errors.push(`${name || '值'}必须是数字`);
322
- }
323
- if (min !== null && convertedValue < min) {
324
- errors.push(`${name || '值'}不能小于${min}`);
325
- }
326
- if (max !== null && max > 0 && convertedValue > max) {
327
- errors.push(`${name || '值'}不能大于${max}`);
328
- }
329
- if (regexp && regexp.trim() !== '') {
330
- try {
331
- const regExp = new RegExp(regexp);
332
- if (!regExp.test(String(convertedValue))) {
333
- errors.push(`${name || '值'}格式不正确`);
334
- }
335
- } catch (e: any) {
336
- errors.push(`${name || '值'}的正则表达式格式错误: ${e.message}`);
337
- }
338
- }
151
+ if (min !== null && value < min) return `不能小于${min}`;
152
+ if (max !== null && max > 0 && value > max) return `不能大于${max}`;
153
+ if (regex && !this.testRegex(regex, String(value))) return '格式不正确';
339
154
  break;
340
155
 
341
156
  case 'string':
342
157
  case 'text':
343
- if (typeof convertedValue !== 'string') {
344
- errors.push(`${name || '值'}必须是字符串`);
345
- }
346
- if (min !== null && convertedValue.length < min) {
347
- errors.push(`${name || '值'}长度不能少于${min}个字符`);
348
- }
349
- if (max !== null && max > 0 && convertedValue.length > max) {
350
- errors.push(`${name || '值'}长度不能超过${max}个字符`);
351
- }
352
- if (regexp && regexp.trim() !== '') {
353
- try {
354
- const regExp = new RegExp(regexp);
355
- if (!regExp.test(convertedValue)) {
356
- errors.push(`${name || '值'}格式不正确`);
357
- }
358
- } catch {
359
- errors.push(`${name || '值'}的正则表达式格式错误`);
360
- }
361
- }
158
+ if (min !== null && value.length < min) return `长度不能少于${min}个字符`;
159
+ if (max !== null && max > 0 && value.length > max) return `长度不能超过${max}个字符`;
160
+ if (regex && !this.testRegex(regex, value)) return '格式不正确';
362
161
  break;
363
162
 
364
163
  case 'array_string':
365
164
  case 'array_text':
366
- if (!Array.isArray(convertedValue)) {
367
- errors.push(`${name || '值'}必须是数组`);
368
- }
369
- if (min !== null && convertedValue.length < min) {
370
- errors.push(`${name || ''}元素数量不能少于${min}个`);
371
- }
372
- if (max !== null && max > 0 && convertedValue.length > max) {
373
- errors.push(`${name || '值'}元素数量不能超过${max}个`);
374
- }
375
- if (regexp && regexp.trim() !== '') {
376
- try {
377
- const regExp = new RegExp(regexp);
378
- for (const item of convertedValue) {
379
- if (!regExp.test(String(item))) {
380
- errors.push(`${name || '值'}的元素格式不正确`);
381
- break;
382
- }
383
- }
384
- } catch {
385
- errors.push(`${name || '值'}的正则表达式格式错误`);
165
+ if (min !== null && value.length < min) return `至少需要${min}个元素`;
166
+ if (max !== null && max > 0 && value.length > max) return `最多只能有${max}个元素`;
167
+ if (regex) {
168
+ for (const item of value) {
169
+ if (!this.testRegex(regex, String(item))) return '元素格式不正确';
386
170
  }
387
171
  }
388
172
  break;
389
173
  }
390
-
391
- return {
392
- valid: errors.length === 0,
393
- value: errors.length === 0 ? convertedValue : null,
394
- errors
395
- };
174
+ return null;
396
175
  }
397
176
 
398
- /**
399
- * 静态方法:快速验证
400
- */
401
- static validate(data: Record<string, any>, rules: TableDefinition, required?: string[]): ValidationResult;
402
- static validate(value: any, fieldDef: FieldDefinition): { valid: boolean; value: any; errors: string[] };
403
- static validate(dataOrValue: any, rulesOrFieldDef: any, required?: string[]): any {
404
- const validator = new Validator();
405
-
406
- if (rulesOrFieldDef && 'type' in rulesOrFieldDef) {
407
- return validator.validateSingleValue(dataOrValue, rulesOrFieldDef);
177
+ /** 解析正则别名 */
178
+ private static resolveRegex(regexp: string | null): string | null {
179
+ if (!regexp) return null;
180
+ if (regexp.startsWith('@')) {
181
+ return RegexAliases[regexp.substring(1)] || regexp;
408
182
  }
409
-
410
- return validator.validate(dataOrValue, rulesOrFieldDef, required || []);
183
+ return regexp;
411
184
  }
412
185
 
413
- /**
414
- * 检查验证是否通过
415
- */
416
- static isPassed(result: ValidationResult | { valid: boolean; value: any; errors: string[] }): boolean {
417
- if ('valid' in result) {
418
- return result.valid === true;
186
+ /** 测试正则 */
187
+ private static testRegex(pattern: string, value: string): boolean {
188
+ try {
189
+ return getCompiledRegex(pattern).test(value);
190
+ } catch {
191
+ return false;
419
192
  }
420
- return result.code === 0;
421
193
  }
422
194
 
423
- /**
424
- * 检查验证是否失败
425
- */
426
- static isFailed(result: ValidationResult): boolean {
427
- return result.code === 1;
428
- }
429
-
430
- /**
431
- * 获取第一个错误信息
432
- */
433
- static getFirstError(result: ValidationResult | { valid: boolean; value: any; errors: string[] }): string | null {
434
- if ('valid' in result) {
435
- return result.errors.length > 0 ? result.errors[0] : null;
195
+ /** 获取默认值 */
196
+ private static defaultFor(type: string, defaultValue: any): any {
197
+ if (defaultValue !== null && defaultValue !== undefined) {
198
+ // 数组默认值
199
+ if ((type === 'array_string' || type === 'array_text') && typeof defaultValue === 'string') {
200
+ if (defaultValue === '[]') return [];
201
+ try {
202
+ const parsed = JSON.parse(defaultValue);
203
+ return Array.isArray(parsed) ? parsed : [];
204
+ } catch {
205
+ return [];
206
+ }
207
+ }
208
+ // 数字默认值
209
+ if (type === 'number' && typeof defaultValue === 'string') {
210
+ const num = Number(defaultValue);
211
+ return isNaN(num) ? 0 : num;
212
+ }
213
+ return defaultValue;
436
214
  }
437
- if (result.code === 0) return null;
438
- if (!result.fields) return null;
439
- const errors = Object.values(result.fields);
440
- return errors.length > 0 ? errors[0] : null;
441
- }
442
215
 
443
- /**
444
- * 获取所有错误信息
445
- */
446
- static getAllErrors(result: ValidationResult | { valid: boolean; value: any; errors: string[] }): string[] {
447
- if ('valid' in result) {
448
- return result.errors;
216
+ // 类型默认值
217
+ switch (type.toLowerCase()) {
218
+ case 'number':
219
+ return 0;
220
+ case 'array_string':
221
+ case 'array_text':
222
+ return [];
223
+ default:
224
+ return '';
449
225
  }
450
- if (result.code === 0) return [];
451
- if (!result.fields) return [];
452
- return Object.values(result.fields);
453
- }
454
-
455
- /**
456
- * 获取错误字段列表
457
- */
458
- static getErrorFields(result: ValidationResult): string[] {
459
- return result.code === 0 ? [] : Object.keys(result.fields);
460
226
  }
461
227
  }
@@ -9,7 +9,8 @@ import { existsSync } from 'node:fs';
9
9
  // 外部依赖
10
10
  import { relative, basename, join } from 'pathe';
11
11
  import { isPlainObject } from 'es-toolkit/compat';
12
- import { calcPerfTime, scanFiles, scanAddons, getAddonDir, addonDirExists } from 'befly-util';
12
+ import { scanFiles } from 'befly-shared/scanFiles';
13
+ import { scanAddons, getAddonDir, addonDirExists } from 'befly-shared/addonHelper';
13
14
 
14
15
  // 相对导入
15
16
  import { Logger } from '../lib/logger.js';
@@ -62,8 +63,6 @@ const DEFAULT_API_FIELDS = {
62
63
  */
63
64
  export async function loadApis(apis: Map<string, ApiRoute>): Promise<void> {
64
65
  try {
65
- const loadStartTime = Bun.nanoseconds();
66
-
67
66
  // 1. 扫描项目 API
68
67
  const projectApiFiles = await scanFiles(projectApiDir);
69
68
  const projectApiList = projectApiFiles.map((file) => ({
@@ -122,14 +121,12 @@ export async function loadApis(apis: Map<string, ApiRoute>): Promise<void> {
122
121
  api.route = `${api.method.toUpperCase()}/api/${apiFile.routePrefix ? apiFile.routePrefix + '/' : ''}${apiFile.relativePath}`;
123
122
  apis.set(api.route, api);
124
123
  } catch (error: any) {
125
- Logger.error(`[${apiFile.typeName}] 接口 ${apiFile.relativePath} 加载失败`, error);
124
+ Logger.error({ err: error, api: apiFile.relativePath, type: apiFile.typeName }, '接口加载失败');
126
125
  process.exit(1);
127
126
  }
128
127
  }
129
-
130
- const totalLoadTime = calcPerfTime(loadStartTime);
131
128
  } catch (error: any) {
132
- Logger.error('加载 API 时发生错误', error);
129
+ Logger.error({ err: error }, '加载 API 时发生错误');
133
130
  process.exit(1);
134
131
  }
135
132
  }