befly 3.16.9 → 3.16.11

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 (178) hide show
  1. package/README.md +0 -129
  2. package/befly.js +12769 -0
  3. package/befly.min.js +47 -0
  4. package/package.json +18 -29
  5. package/dist/befly.config.d.ts +0 -7
  6. package/dist/befly.config.js +0 -128
  7. package/dist/befly.js +0 -17276
  8. package/dist/befly.min.js +0 -23
  9. package/dist/checks/checkApi.d.ts +0 -1
  10. package/dist/checks/checkApi.js +0 -124
  11. package/dist/checks/checkConfig.d.ts +0 -9
  12. package/dist/checks/checkConfig.js +0 -255
  13. package/dist/checks/checkHook.d.ts +0 -1
  14. package/dist/checks/checkHook.js +0 -132
  15. package/dist/checks/checkMenu.d.ts +0 -3
  16. package/dist/checks/checkMenu.js +0 -106
  17. package/dist/checks/checkPlugin.d.ts +0 -1
  18. package/dist/checks/checkPlugin.js +0 -132
  19. package/dist/checks/checkTable.d.ts +0 -7
  20. package/dist/checks/checkTable.js +0 -431
  21. package/dist/configs/presetRegexp.d.ts +0 -145
  22. package/dist/configs/presetRegexp.js +0 -218
  23. package/dist/hooks/auth.d.ts +0 -3
  24. package/dist/hooks/auth.js +0 -24
  25. package/dist/hooks/cors.d.ts +0 -7
  26. package/dist/hooks/cors.js +0 -36
  27. package/dist/hooks/parser.d.ts +0 -10
  28. package/dist/hooks/parser.js +0 -76
  29. package/dist/hooks/permission.d.ts +0 -10
  30. package/dist/hooks/permission.js +0 -64
  31. package/dist/hooks/validator.d.ts +0 -7
  32. package/dist/hooks/validator.js +0 -52
  33. package/dist/index.d.ts +0 -28
  34. package/dist/index.js +0 -316
  35. package/dist/lib/asyncContext.d.ts +0 -21
  36. package/dist/lib/asyncContext.js +0 -27
  37. package/dist/lib/cacheHelper.d.ts +0 -128
  38. package/dist/lib/cacheHelper.js +0 -477
  39. package/dist/lib/cacheKeys.d.ts +0 -27
  40. package/dist/lib/cacheKeys.js +0 -37
  41. package/dist/lib/cipher.d.ts +0 -153
  42. package/dist/lib/cipher.js +0 -237
  43. package/dist/lib/connect.d.ts +0 -95
  44. package/dist/lib/connect.js +0 -313
  45. package/dist/lib/dbHelper.d.ts +0 -229
  46. package/dist/lib/dbHelper.js +0 -1069
  47. package/dist/lib/dbUtils.d.ts +0 -91
  48. package/dist/lib/dbUtils.js +0 -544
  49. package/dist/lib/jwt.d.ts +0 -13
  50. package/dist/lib/jwt.js +0 -77
  51. package/dist/lib/logger.d.ts +0 -46
  52. package/dist/lib/logger.js +0 -731
  53. package/dist/lib/redisHelper.d.ts +0 -193
  54. package/dist/lib/redisHelper.js +0 -598
  55. package/dist/lib/sqlBuilder.d.ts +0 -160
  56. package/dist/lib/sqlBuilder.js +0 -837
  57. package/dist/lib/sqlCheck.d.ts +0 -23
  58. package/dist/lib/sqlCheck.js +0 -119
  59. package/dist/lib/validator.d.ts +0 -45
  60. package/dist/lib/validator.js +0 -424
  61. package/dist/loader/loadApis.d.ts +0 -12
  62. package/dist/loader/loadApis.js +0 -71
  63. package/dist/loader/loadHooks.d.ts +0 -7
  64. package/dist/loader/loadHooks.js +0 -50
  65. package/dist/loader/loadPlugins.d.ts +0 -8
  66. package/dist/loader/loadPlugins.js +0 -69
  67. package/dist/paths.d.ts +0 -93
  68. package/dist/paths.js +0 -100
  69. package/dist/plugins/cache.d.ts +0 -10
  70. package/dist/plugins/cache.js +0 -24
  71. package/dist/plugins/cipher.d.ts +0 -7
  72. package/dist/plugins/cipher.js +0 -14
  73. package/dist/plugins/config.d.ts +0 -3
  74. package/dist/plugins/config.js +0 -9
  75. package/dist/plugins/db.d.ts +0 -10
  76. package/dist/plugins/db.js +0 -48
  77. package/dist/plugins/jwt.d.ts +0 -6
  78. package/dist/plugins/jwt.js +0 -13
  79. package/dist/plugins/logger.d.ts +0 -10
  80. package/dist/plugins/logger.js +0 -21
  81. package/dist/plugins/redis.d.ts +0 -10
  82. package/dist/plugins/redis.js +0 -40
  83. package/dist/plugins/tool.d.ts +0 -75
  84. package/dist/plugins/tool.js +0 -105
  85. package/dist/router/api.d.ts +0 -14
  86. package/dist/router/api.js +0 -109
  87. package/dist/router/static.d.ts +0 -9
  88. package/dist/router/static.js +0 -56
  89. package/dist/scripts/ensureDist.d.ts +0 -1
  90. package/dist/scripts/ensureDist.js +0 -296
  91. package/dist/sync/syncApi.d.ts +0 -3
  92. package/dist/sync/syncApi.js +0 -140
  93. package/dist/sync/syncCache.d.ts +0 -2
  94. package/dist/sync/syncCache.js +0 -14
  95. package/dist/sync/syncDev.d.ts +0 -6
  96. package/dist/sync/syncDev.js +0 -166
  97. package/dist/sync/syncMenu.d.ts +0 -14
  98. package/dist/sync/syncMenu.js +0 -308
  99. package/dist/sync/syncTable.d.ts +0 -126
  100. package/dist/sync/syncTable.js +0 -1129
  101. package/dist/types/api.d.ts +0 -175
  102. package/dist/types/api.js +0 -4
  103. package/dist/types/befly.d.ts +0 -231
  104. package/dist/types/befly.js +0 -4
  105. package/dist/types/cache.d.ts +0 -96
  106. package/dist/types/cache.js +0 -4
  107. package/dist/types/cipher.d.ts +0 -27
  108. package/dist/types/cipher.js +0 -7
  109. package/dist/types/common.d.ts +0 -127
  110. package/dist/types/common.js +0 -5
  111. package/dist/types/context.d.ts +0 -39
  112. package/dist/types/context.js +0 -4
  113. package/dist/types/coreError.d.ts +0 -31
  114. package/dist/types/coreError.js +0 -38
  115. package/dist/types/crypto.d.ts +0 -20
  116. package/dist/types/crypto.js +0 -4
  117. package/dist/types/database.d.ts +0 -182
  118. package/dist/types/database.js +0 -4
  119. package/dist/types/hook.d.ts +0 -30
  120. package/dist/types/hook.js +0 -19
  121. package/dist/types/jwt.d.ts +0 -76
  122. package/dist/types/jwt.js +0 -4
  123. package/dist/types/logger.d.ts +0 -95
  124. package/dist/types/logger.js +0 -6
  125. package/dist/types/plugin.d.ts +0 -27
  126. package/dist/types/plugin.js +0 -17
  127. package/dist/types/redis.d.ts +0 -80
  128. package/dist/types/redis.js +0 -4
  129. package/dist/types/roleApisCache.d.ts +0 -21
  130. package/dist/types/roleApisCache.js +0 -8
  131. package/dist/types/sync.d.ts +0 -93
  132. package/dist/types/sync.js +0 -4
  133. package/dist/types/table.d.ts +0 -34
  134. package/dist/types/table.js +0 -4
  135. package/dist/types/validate.d.ts +0 -113
  136. package/dist/types/validate.js +0 -4
  137. package/dist/utils/calcPerfTime.d.ts +0 -4
  138. package/dist/utils/calcPerfTime.js +0 -13
  139. package/dist/utils/cors.d.ts +0 -8
  140. package/dist/utils/cors.js +0 -17
  141. package/dist/utils/dbFieldRules.d.ts +0 -31
  142. package/dist/utils/dbFieldRules.js +0 -94
  143. package/dist/utils/fieldClear.d.ts +0 -11
  144. package/dist/utils/fieldClear.js +0 -57
  145. package/dist/utils/formatYmdHms.d.ts +0 -1
  146. package/dist/utils/formatYmdHms.js +0 -20
  147. package/dist/utils/getClientIp.d.ts +0 -6
  148. package/dist/utils/getClientIp.js +0 -39
  149. package/dist/utils/importDefault.d.ts +0 -1
  150. package/dist/utils/importDefault.js +0 -53
  151. package/dist/utils/isDirentDirectory.d.ts +0 -3
  152. package/dist/utils/isDirentDirectory.js +0 -18
  153. package/dist/utils/loadMenuConfigs.d.ts +0 -11
  154. package/dist/utils/loadMenuConfigs.js +0 -130
  155. package/dist/utils/loggerUtils.d.ts +0 -18
  156. package/dist/utils/loggerUtils.js +0 -171
  157. package/dist/utils/mergeAndConcat.d.ts +0 -7
  158. package/dist/utils/mergeAndConcat.js +0 -77
  159. package/dist/utils/normalizeFieldDefinition.d.ts +0 -18
  160. package/dist/utils/normalizeFieldDefinition.js +0 -27
  161. package/dist/utils/processInfo.d.ts +0 -26
  162. package/dist/utils/processInfo.js +0 -41
  163. package/dist/utils/response.d.ts +0 -20
  164. package/dist/utils/response.js +0 -96
  165. package/dist/utils/scanAddons.d.ts +0 -15
  166. package/dist/utils/scanAddons.js +0 -35
  167. package/dist/utils/scanCoreBuiltins.d.ts +0 -3
  168. package/dist/utils/scanCoreBuiltins.js +0 -72
  169. package/dist/utils/scanFiles.d.ts +0 -32
  170. package/dist/utils/scanFiles.js +0 -124
  171. package/dist/utils/scanSources.d.ts +0 -10
  172. package/dist/utils/scanSources.js +0 -46
  173. package/dist/utils/sortModules.d.ts +0 -28
  174. package/dist/utils/sortModules.js +0 -105
  175. package/dist/utils/sqlUtil.d.ts +0 -33
  176. package/dist/utils/sqlUtil.js +0 -146
  177. package/dist/utils/util.d.ts +0 -172
  178. package/dist/utils/util.js +0 -517
@@ -1,132 +0,0 @@
1
- import { Logger } from "../lib/logger";
2
- import { CoreError } from "../types/coreError";
3
- import { isPlainObject, omit } from "../utils/util";
4
- const exportKeys = ["name", "enable", "deps", "handler"];
5
- export async function checkPlugin(plugins) {
6
- let hasError = false;
7
- // 说明:plugins 实际是 scanFiles/scanSources 的结果对象(包含元信息字段)。
8
- // 这里不再对白名单枚举 metaKeys(因为它们是系统生成的),只校验“用户 default export 导出的字段”。
9
- const coreBuiltinNameRegexp = /^[a-z]+(?:_[a-z]+)*$/;
10
- for (const plugin of plugins) {
11
- try {
12
- if (!isPlainObject(plugin)) {
13
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件导出必须是对象(export default { deps, handler })" }));
14
- hasError = true;
15
- continue;
16
- }
17
- const record = plugin;
18
- // moduleName 必须存在(用于依赖排序与运行时挂载)。
19
- const moduleName = record["moduleName"];
20
- if (typeof moduleName !== "string" || moduleName.trim() === "") {
21
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件的 moduleName 必须是非空字符串(由系统生成,用于 deps 与运行时挂载)" }));
22
- hasError = true;
23
- continue;
24
- }
25
- const customKeys = record["customKeys"];
26
- if (!Array.isArray(customKeys)) {
27
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件扫描结果缺少 customKeys(无法判断用户导出的字段是否合法)" }));
28
- hasError = true;
29
- continue;
30
- }
31
- if (customKeys.some((k) => typeof k !== "string")) {
32
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件的 customKeys 必须是 string[](由系统生成)" }));
33
- hasError = true;
34
- continue;
35
- }
36
- // 严格字段校验:仅检查用户 default export 的字段集合,出现任何未支持字段都应视为错误。
37
- const unknownCustomKeys = customKeys.filter((k) => !exportKeys.includes(k));
38
- if (unknownCustomKeys.length > 0) {
39
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: `插件导出存在不支持的属性:${unknownCustomKeys.join(", ")};仅允许:${exportKeys.join(", ")};当前 customKeys:${customKeys.join(", ")}` }));
40
- hasError = true;
41
- continue;
42
- }
43
- const hasCustomEnable = customKeys.includes("enable");
44
- const hasCustomDeps = customKeys.includes("deps");
45
- // enable 必须显式声明且只能为 boolean(true/false),不允许 0/1 等其他类型。
46
- // - 允许缺省:由系统在此处补全默认值 true
47
- // - 若用户显式导出 enable:必须是 boolean
48
- if (hasCustomEnable) {
49
- const enable = record["enable"];
50
- if (typeof enable !== "boolean") {
51
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件的 enable 属性必须是 boolean(true/false),不允许 0/1 等其他类型" }));
52
- hasError = true;
53
- continue;
54
- }
55
- }
56
- else {
57
- record["enable"] = true;
58
- }
59
- // core 内置插件:必须来自静态注册(filePath 以 core:plugin: 开头),且 name 必须显式指定并与 moduleName 一致。
60
- if (record["source"] === "core") {
61
- const name = typeof record["name"] === "string" ? record["name"] : "";
62
- if (name === "") {
63
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "core 内置插件必须显式设置 name(string),用于确定插件名称" }));
64
- hasError = true;
65
- continue;
66
- }
67
- // name 必须满足:小写字母 + 下划线(不允许空格、驼峰、数字等)。
68
- if (!coreBuiltinNameRegexp.test(name)) {
69
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "core 内置插件的 name 必须满足小写字母+下划线格式(例如 logger / redis_cache),不允许空格、驼峰或其他字符" }));
70
- hasError = true;
71
- continue;
72
- }
73
- if (!coreBuiltinNameRegexp.test(String(record["moduleName"]))) {
74
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "core 内置插件的 moduleName 必须满足小写字母+下划线格式(由系统生成,且必须与 name 一致)" }));
75
- hasError = true;
76
- continue;
77
- }
78
- if (name !== String(record["moduleName"])) {
79
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "core 内置插件的 name 必须与 moduleName 完全一致" }));
80
- hasError = true;
81
- continue;
82
- }
83
- const filePath = record["filePath"];
84
- if (typeof filePath !== "string" || !filePath.startsWith(`core:plugin:${name}`)) {
85
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "core 内置插件必须来自静态注册(filePath 必须以 core:plugin:<name> 开头),不允许通过扫描目录加载" }));
86
- hasError = true;
87
- continue;
88
- }
89
- }
90
- // deps:允许缺省(补全为 []),但如果用户显式导出 deps,则必须是 string[]。
91
- if (hasCustomDeps) {
92
- const deps = record["deps"];
93
- if (!Array.isArray(deps)) {
94
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件的 deps 属性必须是字符串数组" }));
95
- hasError = true;
96
- continue;
97
- }
98
- if (deps.some((depItem) => typeof depItem !== "string")) {
99
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件的 deps 属性必须是字符串数组" }));
100
- hasError = true;
101
- continue;
102
- }
103
- }
104
- else {
105
- const deps = record["deps"];
106
- if (!Array.isArray(deps)) {
107
- record["deps"] = [];
108
- }
109
- }
110
- if (typeof record["handler"] !== "function") {
111
- Logger.warn(Object.assign({}, omit(plugin, ["handler"]), { msg: "插件的 handler 属性必须是函数" }));
112
- hasError = true;
113
- continue;
114
- }
115
- }
116
- catch (error) {
117
- Logger.error({ err: error, item: plugin, msg: "插件解析失败" });
118
- hasError = true;
119
- }
120
- }
121
- if (hasError) {
122
- throw new CoreError({
123
- kind: "policy",
124
- message: "插件结构检查失败",
125
- logged: true,
126
- meta: {
127
- subsystem: "checks",
128
- operation: "checkPlugin"
129
- }
130
- });
131
- }
132
- }
@@ -1,7 +0,0 @@
1
- import type { BeflyOptions } from "../types/befly";
2
- import type { ScanFileResult } from "../utils/scanFiles";
3
- /**
4
- * 检查表定义文件
5
- * @throws 当检查失败时抛出异常
6
- */
7
- export declare function checkTable(tables: ScanFileResult[], config: BeflyOptions): Promise<void>;
@@ -1,431 +0,0 @@
1
- import { Logger } from "../lib/logger";
2
- import { CoreError } from "../types/coreError";
3
- import { isDecimalDbType as isDecimalDbTypeRule, isEnumDbType as isEnumDbTypeRule, isFloatDbType as isFloatDbTypeRule, isIntDbType as isIntDbTypeRule, isJsonDbType as isJsonDbTypeRule, isNumericDbType as isNumericDbTypeRule, isStringDbType as isStringDbTypeRule, isTextDbType as isTextDbTypeRule, normalizeTypeAndInput, parseEnumRuleValues } from "../utils/dbFieldRules";
4
- import { formatValuePreview, isAliasInput, isEnumInput, isJsonValue, isRegexInput, snakeCase } from "../utils/util";
5
- /**
6
- * 保留字段列表
7
- */
8
- const RESERVED_FIELDS = ["id", "created_at", "updated_at", "deleted_at", "state"];
9
- const RESERVED_FIELD_SET = new Set(RESERVED_FIELDS);
10
- /**
11
- * 允许的数据库字段类型
12
- */
13
- const FIELD_TYPES = ["tinyint", "smallint", "mediumint", "int", "bigint", "decimal", "float", "double", "char", "varchar", "enum", "tinytext", "text", "mediumtext", "longtext", "datetime", "timestamp", "json"];
14
- const FIELD_TYPE_SET = new Set(FIELD_TYPES);
15
- /**
16
- * 允许的输入类型
17
- */
18
- const INPUT_TYPES = ["number", "integer", "string", "char", "array", "array_number", "array_integer", "json", "json_number", "json_integer"];
19
- const INPUT_TYPE_SET = new Set(INPUT_TYPES);
20
- /**
21
- * 允许的字段属性列表
22
- */
23
- const ALLOWED_FIELD_PROPERTIES = ["name", "type", "input", "min", "max", "default", "detail", "precision", "scale", "index", "unique", "nullable", "unsigned"];
24
- const ALLOWED_FIELD_PROPERTY_SET = new Set(ALLOWED_FIELD_PROPERTIES);
25
- /**
26
- * 小驼峰命名正则
27
- * 可选:以下划线开头(用于特殊文件,如通用字段定义)
28
- * 必须以小写字母开头,后续可包含小写/数字,或多个 [大写+小写/数字] 片段
29
- * 示例:userTable、testCustomers、common
30
- */
31
- const LOWER_CAMEL_CASE_REGEX = /^_?[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
32
- /**
33
- * 字段名称正则
34
- * 必须为中文、数字、字母、下划线、短横线、空格
35
- */
36
- const FIELD_NAME_REGEX = /^[\u4e00-\u9fa5a-zA-Z0-9 _-]+$/;
37
- // MySQL VARCHAR 最大长度(utf8mb4 下受行大小限制;本项目采用保守上限,用于表定义校验)
38
- const MAX_VARCHAR_LENGTH = 16383;
39
- // 项目索引策略(更简单、更保守):
40
- // - index=true: 只允许 max<=500
41
- // - unique=true: 只允许 max<=180
42
- const MAX_INDEX_STRING_LENGTH_FOR_INDEX = 500;
43
- const MAX_INDEX_STRING_LENGTH_FOR_UNIQUE = 180;
44
- /**
45
- * 检查表定义文件
46
- * @throws 当检查失败时抛出异常
47
- */
48
- export async function checkTable(tables, config) {
49
- // 收集所有表文件
50
- let hasError = false;
51
- // 合并进行验证逻辑
52
- for (const item of tables) {
53
- if (item.type !== "table") {
54
- continue;
55
- }
56
- const sourceName = typeof item.sourceName === "string" ? item.sourceName : "";
57
- const tablePrefix = sourceName ? `${sourceName}表 ` : "表 ";
58
- try {
59
- const fileName = item.fileName;
60
- // 前置约束(供 syncTable 直接假设成立):
61
- // - addon 表必须有 addonName
62
- // - 表定义必须是对象(非 null/数组)
63
- if (item.source === "addon" && String(item.addonName).trim() === "") {
64
- Logger.warn(`${tablePrefix}${fileName} addon 表缺少 addonName`);
65
- hasError = true;
66
- continue;
67
- }
68
- const table = item.content;
69
- if (typeof table !== "object" || table === null || Array.isArray(table)) {
70
- Logger.warn(`${tablePrefix}${fileName} 表定义无效:必须为对象`);
71
- hasError = true;
72
- continue;
73
- }
74
- // 1) 文件名小驼峰校验
75
- if (!LOWER_CAMEL_CASE_REGEX.test(fileName)) {
76
- Logger.warn(`${tablePrefix}${fileName} 文件名必须使用小驼峰命名(例如 testCustomers.json)`);
77
- hasError = true;
78
- continue;
79
- }
80
- // 检查 table 中的每个验证规则
81
- for (const [colKey, fieldDef] of Object.entries(table)) {
82
- if (typeof fieldDef !== "object" || fieldDef === null || Array.isArray(fieldDef)) {
83
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 规则必须为对象`);
84
- hasError = true;
85
- continue;
86
- }
87
- // 检查是否使用了保留字段
88
- const dbFieldName = snakeCase(colKey);
89
- if (RESERVED_FIELD_SET.has(colKey) || RESERVED_FIELD_SET.has(dbFieldName)) {
90
- Logger.warn(`${tablePrefix}${fileName} 文件包含保留字段 ${colKey}(映射列名: ${dbFieldName}),` + `不能在表定义中使用以下字段: ${RESERVED_FIELDS.join(", ")}`);
91
- hasError = true;
92
- }
93
- // 直接使用字段对象
94
- const field = fieldDef;
95
- // 检查是否存在非法属性
96
- const fieldKeys = Object.keys(field);
97
- const illegalProps = fieldKeys.filter((key) => !ALLOWED_FIELD_PROPERTY_SET.has(key));
98
- if (illegalProps.length > 0) {
99
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 包含非法属性: ${illegalProps.join(", ")},` + `允许的属性为: ${ALLOWED_FIELD_PROPERTIES.join(", ")}`);
100
- hasError = true;
101
- }
102
- // 检查必填字段:name, type
103
- if (!field.name || typeof field.name !== "string") {
104
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 缺少必填字段 name 或类型错误`);
105
- hasError = true;
106
- continue;
107
- }
108
- if (!field.type || typeof field.type !== "string") {
109
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 缺少必填字段 type 或类型错误`);
110
- hasError = true;
111
- continue;
112
- }
113
- if (field.input !== undefined && typeof field.input !== "string") {
114
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input 类型错误,必须为字符串`);
115
- hasError = true;
116
- }
117
- // 检查可选字段的类型
118
- if (field.min !== undefined && !(field.min === null || typeof field.min === "number")) {
119
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 min 类型错误,必须为 null 或数字`);
120
- hasError = true;
121
- }
122
- if (field.max !== undefined && !(field.max === null || typeof field.max === "number")) {
123
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 max 类型错误,必须为 null 或数字`);
124
- hasError = true;
125
- }
126
- if (field.detail !== undefined && typeof field.detail !== "string") {
127
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 detail 类型错误,必须为字符串`);
128
- hasError = true;
129
- }
130
- if (field.precision !== undefined && !(field.precision === null || typeof field.precision === "number")) {
131
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 precision 类型错误,必须为 null 或数字`);
132
- hasError = true;
133
- }
134
- if (field.scale !== undefined && !(field.scale === null || typeof field.scale === "number")) {
135
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 scale 类型错误,必须为 null 或数字`);
136
- hasError = true;
137
- }
138
- if (field.index !== undefined && typeof field.index !== "boolean") {
139
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 index 类型错误,必须为布尔值`);
140
- hasError = true;
141
- }
142
- if (field.unique !== undefined && typeof field.unique !== "boolean") {
143
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 unique 类型错误,必须为布尔值`);
144
- hasError = true;
145
- }
146
- if (field.nullable !== undefined && typeof field.nullable !== "boolean") {
147
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 nullable 类型错误,必须为布尔值`);
148
- hasError = true;
149
- }
150
- if (field.unsigned !== undefined && typeof field.unsigned !== "boolean") {
151
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 unsigned 类型错误,必须为布尔值`);
152
- hasError = true;
153
- }
154
- const normalizedTypeInput = normalizeTypeAndInput(field.type, field.input || "");
155
- const effectiveType = normalizedTypeInput.type;
156
- const effectiveInput = normalizedTypeInput.input;
157
- // 约束:default 若存在,必须可 JSON 序列化(避免 syncTable 运行期再做防御判断)
158
- if (field.default !== undefined && field.default !== null && !isJsonValue(field.default)) {
159
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 default 类型错误,必须为可 JSON 序列化的值或 null` + `(typeof=${typeof field.default},value=${formatValuePreview(field.default)})`);
160
- hasError = true;
161
- }
162
- // 字段名称必须为中文、数字、字母、下划线、短横线、空格
163
- if (config.strict) {
164
- if (!FIELD_NAME_REGEX.test(field.name)) {
165
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段名称 "${field.name}" 格式错误,` + `必须为中文、数字、字母、下划线、短横线、空格`);
166
- hasError = true;
167
- }
168
- }
169
- // 字段类型必须为 FIELD_TYPES 之一
170
- if (!FIELD_TYPE_SET.has(effectiveType)) {
171
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段类型 "${effectiveType}" 格式错误,` + `必须为${FIELD_TYPES.join("、")}之一`);
172
- hasError = true;
173
- }
174
- const normalizedInput = effectiveInput.trim();
175
- if (normalizedInput === "") {
176
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input 无法推导`);
177
- hasError = true;
178
- }
179
- else if (!INPUT_TYPE_SET.has(normalizedInput) && !isRegexInput(normalizedInput) && !isEnumInput(normalizedInput) && !isAliasInput(normalizedInput)) {
180
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input "${normalizedInput}" 不合法,` + `必须为${INPUT_TYPES.join("、")}之一,或正则/枚举/正则别名`);
181
- hasError = true;
182
- }
183
- const isDecimalDbType = isDecimalDbTypeRule(effectiveType);
184
- const isFloatDbType = isFloatDbTypeRule(effectiveType);
185
- const isJsonDbType = isJsonDbTypeRule(effectiveType);
186
- const isEnumDbType = isEnumDbTypeRule(effectiveType);
187
- const isNumericDbType = isNumericDbTypeRule(effectiveType);
188
- const isStringDbTypeNormalized = isStringDbTypeRule(effectiveType);
189
- const isTextDbTypeNormalized = isTextDbTypeRule(effectiveType);
190
- const isIntDbTypeNormalized = isIntDbTypeRule(effectiveType);
191
- if (isEnumDbType) {
192
- if (normalizedInput.trim() === "") {
193
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,必须设置 input 枚举规则(例如 a|b|c)`);
194
- hasError = true;
195
- }
196
- else if (!isEnumInput(normalizedInput)) {
197
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,input 必须为枚举规则(例如 a|b|c),当前为 ${formatValuePreview(normalizedInput)}`);
198
- hasError = true;
199
- }
200
- }
201
- if (isRegexInput(normalizedInput) || isEnumInput(normalizedInput) || isAliasInput(normalizedInput)) {
202
- if (!isStringDbTypeNormalized) {
203
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input 使用正则/枚举/正则别名,仅允许字符串类字段(char/varchar/enum/text)`);
204
- hasError = true;
205
- }
206
- }
207
- if (normalizedInput === "number" || normalizedInput === "integer") {
208
- if (normalizedInput === "integer" && !isIntDbTypeNormalized) {
209
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input=${normalizedInput} 仅允许整数类字段(tinyint/smallint/mediumint/int/bigint)`);
210
- hasError = true;
211
- }
212
- if (normalizedInput === "number" && !isNumericDbType) {
213
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input=${normalizedInput} 仅允许数值类字段(tinyint/smallint/mediumint/int/bigint/decimal)`);
214
- hasError = true;
215
- }
216
- }
217
- if (normalizedInput === "array" || normalizedInput === "array_number" || normalizedInput === "array_integer") {
218
- if (!isStringDbTypeNormalized) {
219
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input=${normalizedInput} 仅允许字符串类字段(char/varchar/text)`);
220
- hasError = true;
221
- }
222
- }
223
- if (normalizedInput === "json" || normalizedInput === "json_number" || normalizedInput === "json_integer") {
224
- if (!(isJsonDbType || isTextDbTypeNormalized)) {
225
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段 input=${normalizedInput} 仅允许 json 或 text 类字段`);
226
- hasError = true;
227
- }
228
- }
229
- // unsigned 仅对 number 类型有效(且仅 MySQL 语义上生效)
230
- if (!isNumericDbType && field.unsigned !== undefined) {
231
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 字段类型为 ${effectiveType},不允许设置 unsigned(仅数值类型有效)`);
232
- hasError = true;
233
- }
234
- // 约束:unique 与 index 不能同时为 true(否则会重复索引),必须阻断启动。
235
- if (field.unique === true && field.index === true) {
236
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 同时设置了 unique=true 和 index=true,` + `unique 和 index 不能同时设置,请删除其一(否则会创建重复索引)`);
237
- hasError = true;
238
- }
239
- // 约束:当最小值与最大值均为数字时,要求最小值 <= 最大值
240
- if (field.min !== undefined && field.max !== undefined && field.min !== null && field.max !== null) {
241
- if (field.min > field.max) {
242
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 最小值 "${field.min}" 不能大于最大值 "${field.max}"`);
243
- hasError = true;
244
- }
245
- }
246
- // 类型联动校验 + 默认值规则
247
- if (isTextDbTypeNormalized || isJsonDbType) {
248
- // text/json:min/max 必须为 null,默认值必须为 null,且不支持索引/唯一约束
249
- if (field.min !== undefined && field.min !== null) {
250
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 的 ${effectiveType} 类型最小值应为 null,当前为 "${field.min}"`);
251
- hasError = true;
252
- }
253
- if (field.max !== undefined && field.max !== null) {
254
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 的 ${effectiveType} 类型最大长度应为 null,当前为 "${field.max}"`);
255
- hasError = true;
256
- }
257
- if (field.default !== undefined && field.default !== null) {
258
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,默认值必须为 null,当前为 "${field.default}"`);
259
- hasError = true;
260
- }
261
- if (field.index === true) {
262
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,不支持创建索引(index=true 无效)`);
263
- hasError = true;
264
- }
265
- if (field.unique === true) {
266
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,不支持唯一约束(unique=true 无效)`);
267
- hasError = true;
268
- }
269
- }
270
- else if (effectiveType === "datetime" || effectiveType === "timestamp") {
271
- // datetime/timestamp:对应 MySQL DATETIME/TIMESTAMP(到秒)
272
- // - min/max 必须为 null
273
- // - default 必须为 null(避免把 DDL 表达式当作运行期默认值)
274
- // - 不允许 unsigned
275
- if (field.min !== undefined && field.min !== null) {
276
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,min 必须为 null,当前为 "${field.min}"`);
277
- hasError = true;
278
- }
279
- if (field.max !== undefined && field.max !== null) {
280
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,max 必须为 null,当前为 "${field.max}"`);
281
- hasError = true;
282
- }
283
- if (field.default !== undefined && field.default !== null) {
284
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,默认值必须为 null(如需当前时间,请在业务写入时赋值)。当前为 ${formatValuePreview(field.default)}`);
285
- hasError = true;
286
- }
287
- if (field.unsigned !== undefined) {
288
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,不允许设置 unsigned`);
289
- hasError = true;
290
- }
291
- }
292
- else if (effectiveType === "enum") {
293
- // enum:对应 MySQL ENUM('a','b',...)。
294
- // 约束:必须提供 input 枚举规则(a|b|c),由上方 isEnumDbType 分支保证。
295
- // - min/max/precision/scale 必须为 null
296
- // - default 若提供,必须为枚举值之一
297
- // - 若 nullable!=true,则必须提供 default(避免 NOT NULL 无默认值导致写入失败)
298
- // - 不允许 unsigned
299
- if (field.min !== undefined && field.min !== null) {
300
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,min 必须为 null,当前为 "${field.min}"`);
301
- hasError = true;
302
- }
303
- if (field.max !== undefined && field.max !== null) {
304
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,max 必须为 null,当前为 "${field.max}"`);
305
- hasError = true;
306
- }
307
- if (field.precision !== undefined && field.precision !== null) {
308
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,不允许设置 precision`);
309
- hasError = true;
310
- }
311
- if (field.scale !== undefined && field.scale !== null) {
312
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,不允许设置 scale`);
313
- hasError = true;
314
- }
315
- if (field.unsigned !== undefined) {
316
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,不允许设置 unsigned`);
317
- hasError = true;
318
- }
319
- const enumValues = parseEnumRuleValues(normalizedInput);
320
- if (field.default === undefined || field.default === null) {
321
- if (field.nullable !== true) {
322
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,nullable!=true 时必须设置 default(且 default 必须为枚举值之一)`);
323
- hasError = true;
324
- }
325
- }
326
- else if (typeof field.default !== "string") {
327
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,默认值必须为字符串或 null` + `(typeof=${typeof field.default},value=${formatValuePreview(field.default)})`);
328
- hasError = true;
329
- }
330
- else if (!enumValues.includes(field.default)) {
331
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 enum 类型,默认值不在枚举范围内(default=${formatValuePreview(field.default)},enums=${formatValuePreview(normalizedInput)})`);
332
- hasError = true;
333
- }
334
- }
335
- else if (isDecimalDbType) {
336
- // decimal:对应 MySQL DECIMAL(precision, scale)
337
- // - precision/scale 必须为数字
338
- // - precision: 1..65
339
- // - scale: 0..30 且 <= precision
340
- const precision = field.precision;
341
- const scale = field.scale;
342
- if (typeof precision !== "number") {
343
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 decimal 类型,必须设置 precision(总位数)`);
344
- hasError = true;
345
- }
346
- else if (precision < 1 || precision > 65) {
347
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 decimal 类型,precision 必须在 1..65 范围内,当前为 ${precision}`);
348
- hasError = true;
349
- }
350
- if (typeof scale !== "number") {
351
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 decimal 类型,必须设置 scale(小数位数)`);
352
- hasError = true;
353
- }
354
- else if (scale < 0 || scale > 30) {
355
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 decimal 类型,scale 必须在 0..30 范围内,当前为 ${scale}`);
356
- hasError = true;
357
- }
358
- if (typeof precision === "number" && typeof scale === "number" && scale > precision) {
359
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 decimal 类型,scale 不能大于 precision(precision=${precision}, scale=${scale})`);
360
- hasError = true;
361
- }
362
- if (field.default !== undefined && field.default !== null && typeof field.default !== "number") {
363
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 decimal 类型,默认值必须为数字或 null` + `(typeof=${typeof field.default},value=${formatValuePreview(field.default)})`);
364
- hasError = true;
365
- }
366
- }
367
- else if (effectiveType === "char" || effectiveType === "varchar") {
368
- // 约束:char/varchar 必须声明 max。
369
- // 说明:当 input 为 array/array_number/array_integer 时,max 表示单个元素长度。
370
- if (field.max === undefined || field.max === null || typeof field.max !== "number") {
371
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,` + `必须设置 max 且类型为数字,当前为 "${field.max}"`);
372
- hasError = true;
373
- }
374
- else if (field.max > MAX_VARCHAR_LENGTH) {
375
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 最大长度 ${field.max} 越界,` + `${effectiveType} 类型长度必须在 1..${MAX_VARCHAR_LENGTH} 范围内`);
376
- hasError = true;
377
- }
378
- else {
379
- if (field.index === true && field.max > MAX_INDEX_STRING_LENGTH_FOR_INDEX) {
380
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 设置了 index=true,` + `但 max=${field.max} 超出允许范围(要求 <= ${MAX_INDEX_STRING_LENGTH_FOR_INDEX})`);
381
- hasError = true;
382
- }
383
- if (field.unique === true && field.max > MAX_INDEX_STRING_LENGTH_FOR_UNIQUE) {
384
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 设置了 unique=true,` + `但 max=${field.max} 超出允许范围(要求 <= ${MAX_INDEX_STRING_LENGTH_FOR_UNIQUE})`);
385
- hasError = true;
386
- }
387
- }
388
- // input=char 时,max 必须为 1
389
- if (normalizedInput === "char" && typeof field.max === "number" && field.max > 1) {
390
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} input=char 时 max 必须为 1,当前为 ${field.max}`);
391
- hasError = true;
392
- }
393
- // default 规则:默认值必须为 string 或 null
394
- if (field.default !== undefined && field.default !== null && typeof field.default !== "string") {
395
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,默认值必须为字符串或 null` + `(typeof=${typeof field.default},value=${formatValuePreview(field.default)})`);
396
- hasError = true;
397
- }
398
- }
399
- else if (isIntDbTypeNormalized) {
400
- // 整数类型:default 如果存在,必须为 null 或 number
401
- if (field.default !== undefined && field.default !== null && typeof field.default !== "number") {
402
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,默认值必须为数字或 null` + `(typeof=${typeof field.default},value=${formatValuePreview(field.default)})`);
403
- hasError = true;
404
- }
405
- }
406
- else if (isFloatDbType) {
407
- // float/double:default 如果存在,必须为 null 或 number
408
- if (field.default !== undefined && field.default !== null && typeof field.default !== "number") {
409
- Logger.warn(`${tablePrefix}${fileName} 文件 ${colKey} 为 ${effectiveType} 类型,默认值必须为数字或 null` + `(typeof=${typeof field.default},value=${formatValuePreview(field.default)})`);
410
- hasError = true;
411
- }
412
- }
413
- }
414
- }
415
- catch (error) {
416
- Logger.error({ msg: `${tablePrefix}${item.fileName} 解析失败`, err: error });
417
- hasError = true;
418
- }
419
- }
420
- if (hasError) {
421
- throw new CoreError({
422
- kind: "policy",
423
- message: "表结构检查失败",
424
- logged: true,
425
- meta: {
426
- subsystem: "checks",
427
- operation: "checkTable"
428
- }
429
- });
430
- }
431
- }