befly 3.21.1 → 3.21.2

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.21.1",
4
- "gitHead": "9dd9b9f2547fe46fe861a95c70e160cae1d74b26",
3
+ "version": "3.21.2",
4
+ "gitHead": "d134ac529ad22517624353dfcfcc1b598f009a49",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
7
7
  "keywords": [
package/plugins/mysql.js CHANGED
@@ -19,7 +19,8 @@ export default {
19
19
  redis: befly.redis,
20
20
  dbName: befly.config?.mysql?.database,
21
21
  sql: Connect.getMysql(),
22
- beflyMode: befly.config?.mysql?.beflyMode
22
+ beflyMode: befly.config?.mysql?.beflyMode,
23
+ tables: befly.tables
23
24
  });
24
25
 
25
26
  return dbManager;
@@ -8,52 +8,30 @@ import { importDefault } from "../../utils/importDefault.js";
8
8
  import { isNonEmptyString, isPlainObject } from "../../utils/is.js";
9
9
  import { camelCase } from "../../utils/util.js";
10
10
  import { buildSyncDbDiff, groupSyncDbColumns } from "./diff.js";
11
- import { querySyncDbColumns } from "./query.js";
12
11
  import { printSyncDbProcessLog } from "./report.js";
13
12
 
14
- async function loadSyncDbTableDefs(tablesDir) {
15
- const tableMap = new Map();
16
-
17
- const glob = new Bun.Glob("*.json");
18
- const files = await glob.scan({
19
- cwd: tablesDir,
20
- onlyFiles: true,
21
- absolute: true,
22
- followSymlinks: true
23
- });
24
-
25
- for await (const filePath of files) {
26
- const fileName = basename(filePath, ".json");
27
- const tableKey = String(camelCase(fileName)).toLowerCase();
28
- if (fileName.startsWith("_")) {
29
- continue;
30
- }
31
-
32
- const loaded = await importDefault(filePath, {});
33
- if (!isPlainObject(loaded)) {
34
- Logger.warn("tables 定义文件不是对象,已按空对象处理", {
35
- filePath: filePath
36
- });
37
- tableMap.set(tableKey, {
38
- filePath: filePath,
39
- tableDef: {}
40
- });
41
- continue;
42
- }
43
-
44
- tableMap.set(tableKey, {
45
- filePath: filePath,
46
- tableDef: loaded
47
- });
48
- }
49
-
50
- return tableMap;
51
- }
13
+ const SYNC_DB_COLUMNS_SQL = `
14
+ SELECT
15
+ c.TABLE_NAME AS tableName,
16
+ t.TABLE_COMMENT AS tableComment,
17
+ c.COLUMN_NAME AS columnName,
18
+ c.DATA_TYPE AS dataType,
19
+ c.COLUMN_TYPE AS columnType,
20
+ c.CHARACTER_MAXIMUM_LENGTH AS charLength,
21
+ c.COLUMN_COMMENT AS columnComment,
22
+ c.ORDINAL_POSITION AS ordinalPosition
23
+ FROM information_schema.COLUMNS AS c
24
+ LEFT JOIN information_schema.TABLES AS t
25
+ ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
26
+ AND t.TABLE_NAME = c.TABLE_NAME
27
+ WHERE c.TABLE_SCHEMA = DATABASE()
28
+ ORDER BY c.TABLE_NAME ASC, c.ORDINAL_POSITION ASC
29
+ `;
52
30
 
53
31
  export async function prepareSyncDbBaseContext(mysqlConfig) {
54
32
  const tablesDir = resolve(join(process.cwd(), "tables"));
55
33
  await mkdir(tablesDir, { recursive: true });
56
- printSyncDbProcessLog(`tables目录=${tablesDir}`);
34
+ printSyncDbProcessLog(`表定义目录=${tablesDir}`);
57
35
 
58
36
  if (!isPlainObject(mysqlConfig)) {
59
37
  throw new Error("syncDb 缺少 mysql 配置", {
@@ -64,8 +42,8 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
64
42
  });
65
43
  }
66
44
 
67
- printSyncDbProcessLog(`beflyMode=${mysqlConfig.beflyMode === "manual" ? "manual" : "auto"}`);
68
- printSyncDbProcessLog(`连接 MySQL,database=${mysqlConfig.database}`);
45
+ printSyncDbProcessLog(`同步模式=${mysqlConfig.beflyMode === "manual" ? "手动" : "自动"}`);
46
+ printSyncDbProcessLog(`连接 MySQL,数据库名=${mysqlConfig.database}`);
69
47
  await Connect.connectMysql(mysqlConfig);
70
48
  const mysql = new DbHelper({
71
49
  redis: null,
@@ -75,8 +53,10 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
75
53
  });
76
54
 
77
55
  printSyncDbProcessLog("开始读取数据库字段信息");
78
- const dbColumns = await querySyncDbColumns(mysql);
79
- printSyncDbProcessLog(`数据库字段读取完成,columnCount=${dbColumns.length}`);
56
+ const queryRes = await mysql.execute(SYNC_DB_COLUMNS_SQL);
57
+ const dbColumns = Array.isArray(queryRes.data) ? queryRes.data : [];
58
+ printSyncDbProcessLog(`数据库字段读取完成,字段数量=${dbColumns.length}`);
59
+
80
60
  const skippedBeflyTables = new Set();
81
61
  for (const row of dbColumns) {
82
62
  const tableName = String(row?.tableName || "");
@@ -93,10 +73,45 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
93
73
  }
94
74
 
95
75
  const groupedDbColumns = groupSyncDbColumns(dbColumns);
96
- const existingTableMap = await loadSyncDbTableDefs(tablesDir);
76
+ const existingTableMap = new Map();
77
+ const glob = new Bun.Glob("*.json");
78
+ const files = await glob.scan({
79
+ cwd: tablesDir,
80
+ onlyFiles: true,
81
+ absolute: true,
82
+ followSymlinks: true
83
+ });
84
+
85
+ for await (const filePath of files) {
86
+ const tableFileName = basename(filePath, ".json");
87
+ if (tableFileName.startsWith("_")) {
88
+ continue;
89
+ }
90
+
91
+ const tableKey = String(camelCase(tableFileName)).toLowerCase();
92
+ const loaded = await importDefault(filePath, {});
93
+ if (!isPlainObject(loaded)) {
94
+ Logger.warn("tables 定义文件不是对象,已按空对象处理", {
95
+ filePath: filePath
96
+ });
97
+ existingTableMap.set(tableKey, {
98
+ tableFileName: tableFileName,
99
+ filePath: filePath,
100
+ tableDef: {}
101
+ });
102
+ continue;
103
+ }
104
+
105
+ existingTableMap.set(tableKey, {
106
+ tableFileName: tableFileName,
107
+ filePath: filePath,
108
+ tableDef: loaded
109
+ });
110
+ }
111
+
97
112
  const diff = buildSyncDbDiff(groupedDbColumns, existingTableMap, mysqlConfig.beflyMode);
98
- printSyncDbProcessLog(`tables定义文件数量=${existingTableMap.size}`);
99
- printSyncDbProcessLog(`差异扫描完成,missingTableCount=${diff.missingTables.length}missingFieldCount=${Object.values(diff.missingFieldsByTable).reduce((sum, item) => sum + item.fields.length, 0)}`);
113
+ printSyncDbProcessLog(`表定义文件数量=${existingTableMap.size}`);
114
+ printSyncDbProcessLog(`差异扫描完成,缺少表数量=${diff.missingTables.length},缺少字段数量=${diff.missingFieldCount},多余表数量=${diff.extraTables.length},多余字段数量=${diff.extraFieldCount}`);
100
115
 
101
116
  return {
102
117
  diff: diff,
@@ -1,7 +1,7 @@
1
1
  import { join } from "node:path";
2
2
 
3
3
  import { isNonEmptyString, isPlainObject } from "../../utils/is.js";
4
- import { camelCase } from "../../utils/util.js";
4
+ import { camelCase, snakeCase } from "../../utils/util.js";
5
5
  import { printSyncDbProcessLog } from "./report.js";
6
6
  import { toSyncDbFieldDef } from "./transform.js";
7
7
 
@@ -16,6 +16,25 @@ function shouldSkipSyncDbColumn(columnMeta, beflyMode) {
16
16
  return SYNC_DB_MANAGED_FIELD_NAMES.has(columnName);
17
17
  }
18
18
 
19
+ function shouldSkipSyncDbFieldName(fieldName, beflyMode) {
20
+ if (beflyMode !== "manual") {
21
+ return false;
22
+ }
23
+
24
+ return SYNC_DB_MANAGED_FIELD_NAMES.has(snakeCase(String(fieldName || "")));
25
+ }
26
+
27
+ function sortSyncDbObjectKeys(source) {
28
+ const entries = Object.entries(source).sort(([left], [right]) => left.localeCompare(right));
29
+ const output = {};
30
+
31
+ for (const [key, value] of entries) {
32
+ output[key] = value;
33
+ }
34
+
35
+ return output;
36
+ }
37
+
19
38
  export function groupSyncDbColumns(rows) {
20
39
  const grouped = new Map();
21
40
 
@@ -50,11 +69,18 @@ export function groupSyncDbColumns(rows) {
50
69
 
51
70
  export function buildSyncDbDiff(groupedDbColumns, existingTableMap, beflyMode = "auto") {
52
71
  const missingTables = [];
72
+ const extraTables = [];
53
73
  const missingFieldsByTable = {};
74
+ const extraFieldsByTable = {};
75
+ const groupedDbTableKeySet = new Set();
76
+ let missingFieldCount = 0;
77
+ let extraFieldCount = 0;
54
78
 
55
79
  for (const [tableName, columns] of groupedDbColumns.entries()) {
56
80
  const tableFileName = camelCase(tableName);
57
- const existing = existingTableMap.get(String(tableFileName).toLowerCase());
81
+ const tableKey = String(tableFileName).toLowerCase();
82
+ groupedDbTableKeySet.add(tableKey);
83
+ const existing = existingTableMap.get(tableKey);
58
84
  const filteredColumns = columns.filter((columnMeta) => !shouldSkipSyncDbColumn(columnMeta, beflyMode));
59
85
 
60
86
  if (!existing) {
@@ -72,8 +98,10 @@ export function buildSyncDbDiff(groupedDbColumns, existingTableMap, beflyMode =
72
98
 
73
99
  const existingFields = isPlainObject(existing.tableDef) ? existing.tableDef : {};
74
100
  const missingFields = [];
101
+ const dbFieldNameSet = new Set();
75
102
  for (const columnMeta of filteredColumns) {
76
103
  const fieldInfo = toSyncDbFieldDef(columnMeta);
104
+ dbFieldNameSet.add(fieldInfo.fieldName);
77
105
  if (Object.hasOwn(existingFields, fieldInfo.fieldName)) {
78
106
  continue;
79
107
  }
@@ -91,18 +119,61 @@ export function buildSyncDbDiff(groupedDbColumns, existingTableMap, beflyMode =
91
119
  }
92
120
 
93
121
  if (missingFields.length > 0) {
122
+ missingFieldCount = missingFieldCount + missingFields.length;
94
123
  missingFieldsByTable[tableFileName] = {
95
124
  tableName: tableName,
96
- tableFileName: tableFileName,
125
+ tableFileName: existing.tableFileName || tableFileName,
97
126
  filePath: existing.filePath,
98
127
  fields: missingFields
99
128
  };
100
129
  }
130
+
131
+ const extraFields = [];
132
+ for (const fieldName of Object.keys(existingFields)) {
133
+ if (shouldSkipSyncDbFieldName(fieldName, beflyMode)) {
134
+ continue;
135
+ }
136
+ if (dbFieldNameSet.has(fieldName)) {
137
+ continue;
138
+ }
139
+
140
+ extraFields.push({
141
+ tableName: tableName,
142
+ tableFileName: tableFileName,
143
+ fieldName: fieldName,
144
+ fieldDef: existingFields[fieldName]
145
+ });
146
+ }
147
+
148
+ if (extraFields.length > 0) {
149
+ extraFieldCount = extraFieldCount + extraFields.length;
150
+ extraFieldsByTable[tableFileName] = {
151
+ tableName: tableName,
152
+ tableFileName: existing.tableFileName || tableFileName,
153
+ filePath: existing.filePath,
154
+ fields: extraFields
155
+ };
156
+ }
157
+ }
158
+
159
+ for (const [tableKey, existing] of existingTableMap.entries()) {
160
+ if (groupedDbTableKeySet.has(tableKey)) {
161
+ continue;
162
+ }
163
+
164
+ extraTables.push({
165
+ tableFileName: existing.tableFileName || camelCase(tableKey),
166
+ filePath: existing.filePath
167
+ });
101
168
  }
102
169
 
103
170
  return {
104
171
  missingTables: missingTables,
105
- missingFieldsByTable: missingFieldsByTable
172
+ extraTables: extraTables,
173
+ missingFieldsByTable: missingFieldsByTable,
174
+ extraFieldsByTable: extraFieldsByTable,
175
+ missingFieldCount: missingFieldCount,
176
+ extraFieldCount: extraFieldCount
106
177
  };
107
178
  }
108
179
 
@@ -118,7 +189,7 @@ export async function applySyncDbDiff(diff, tablesDir, existingTableMap) {
118
189
  }
119
190
 
120
191
  const filePath = join(tablesDir, `${missingTable.tableFileName}.json`);
121
- await Bun.write(filePath, `${JSON.stringify(tableDef, null, 4)}\n`);
192
+ await Bun.write(filePath, `${JSON.stringify(sortSyncDbObjectKeys(tableDef), null, 4)}\n`);
122
193
  createdTableFileCount = createdTableFileCount + 1;
123
194
  printSyncDbProcessLog(`已生成表映射 ${filePath}`);
124
195
  }
@@ -133,8 +204,8 @@ export async function applySyncDbDiff(diff, tablesDir, existingTableMap) {
133
204
  appendedFieldCount = appendedFieldCount + 1;
134
205
  }
135
206
 
136
- await Bun.write(targetFilePath, `${JSON.stringify(currentTableDef, null, 4)}\n`);
137
- printSyncDbProcessLog(`已补充字段映射 ${targetFilePath},fieldCount=${tableInfo.fields.length}`);
207
+ await Bun.write(targetFilePath, `${JSON.stringify(sortSyncDbObjectKeys(currentTableDef), null, 4)}\n`);
208
+ printSyncDbProcessLog(`已补充字段映射 ${targetFilePath},补充字段数量=${tableInfo.fields.length}`);
138
209
  }
139
210
 
140
211
  return {
@@ -142,11 +213,3 @@ export async function applySyncDbDiff(diff, tablesDir, existingTableMap) {
142
213
  appendedFieldCount: appendedFieldCount
143
214
  };
144
215
  }
145
-
146
- export function countSyncDbMissingFields(diff) {
147
- let missingFieldCount = 0;
148
- for (const item of Object.values(diff.missingFieldsByTable)) {
149
- missingFieldCount = missingFieldCount + item.fields.length;
150
- }
151
- return missingFieldCount;
152
- }
@@ -1,55 +1,23 @@
1
- import { mkdir } from "node:fs/promises";
2
- import { dirname, join, resolve } from "node:path";
1
+ import { join, resolve } from "node:path";
3
2
 
4
3
  import { Connect } from "../../lib/connect.js";
5
4
  import { Logger } from "../../lib/logger.js";
6
- import { applySyncDbDiff, countSyncDbMissingFields } from "./diff.js";
5
+ import { applySyncDbDiff } from "./diff.js";
7
6
  import { prepareSyncDbBaseContext } from "./context.js";
8
- import { printSyncDbDiffSummary, printSyncDbProcessLog, writeSyncDbCheckReport } from "./report.js";
9
-
10
- export async function syncDbCheck(mysqlConfig) {
11
- try {
12
- printSyncDbProcessLog("开始执行 check");
13
- const reportPath = resolve(join(process.cwd(), "db.check.md"));
14
- await mkdir(dirname(reportPath), { recursive: true });
15
- await Bun.write(reportPath, "");
16
-
17
- const { diff, groupedDbColumns, existingTableMap } = await prepareSyncDbBaseContext(mysqlConfig);
18
- const missingFieldCount = countSyncDbMissingFields(diff);
19
- const writtenReportPath = await writeSyncDbCheckReport(reportPath, diff, groupedDbColumns, existingTableMap);
20
- printSyncDbDiffSummary("check", diff);
21
- printSyncDbProcessLog(`check完成,missingTableCount=${diff.missingTables.length},missingFieldCount=${missingFieldCount}`);
22
-
23
- return {
24
- mode: "check",
25
- createdTableFileCount: 0,
26
- appendedFieldCount: 0,
27
- missingTableCount: diff.missingTables.length,
28
- missingFieldCount: missingFieldCount,
29
- hasDiff: diff.missingTables.length > 0 || missingFieldCount > 0,
30
- reportPath: writtenReportPath
31
- };
32
- } catch (error) {
33
- throw new Error("执行表定义同步失败", {
34
- cause: error,
35
- code: "runtime",
36
- subsystem: "scripts",
37
- operation: "syncDb"
38
- });
39
- } finally {
40
- await Connect.disconnect();
41
- await Logger.flush();
42
- }
43
- }
7
+ import { printSyncDbDiffSummary, printSyncDbProcessLog, writeSyncDbReport } from "./report.js";
44
8
 
45
9
  export async function syncDbApply(mysqlConfig) {
10
+ const reportPath = resolve(join(process.cwd(), "db.sync.md"));
46
11
  try {
47
- printSyncDbProcessLog("开始执行 apply");
48
- const { diff, tablesDir, existingTableMap } = await prepareSyncDbBaseContext(mysqlConfig);
49
- printSyncDbDiffSummary("check", diff);
12
+ printSyncDbProcessLog("开始执行应用");
13
+ const { diff, groupedDbColumns, tablesDir, existingTableMap } = await prepareSyncDbBaseContext(mysqlConfig);
14
+ const extraFieldCount = diff.extraFieldCount;
15
+ const writtenReportPath = await writeSyncDbReport(reportPath, diff, groupedDbColumns, existingTableMap);
16
+ printSyncDbDiffSummary(diff);
50
17
  const applyResult = await applySyncDbDiff(diff, tablesDir, existingTableMap);
51
- printSyncDbDiffSummary("apply", diff);
52
- printSyncDbProcessLog(`apply完成,createdTableFileCount=${applyResult.createdTableFileCount},appendedFieldCount=${applyResult.appendedFieldCount}`);
18
+ const hasDiff = diff.missingTables.length > 0 || applyResult.appendedFieldCount > 0 || diff.extraTables.length > 0 || extraFieldCount > 0;
19
+
20
+ printSyncDbProcessLog(`应用完成,缺少表数量=${diff.missingTables.length},缺少字段数量=${applyResult.appendedFieldCount},多余表数量=${diff.extraTables.length},多余字段数量=${extraFieldCount},新建表定义文件数量=${applyResult.createdTableFileCount},补充字段数量=${applyResult.appendedFieldCount}`);
53
21
 
54
22
  return {
55
23
  mode: "apply",
@@ -57,8 +25,10 @@ export async function syncDbApply(mysqlConfig) {
57
25
  appendedFieldCount: applyResult.appendedFieldCount,
58
26
  missingTableCount: diff.missingTables.length,
59
27
  missingFieldCount: applyResult.appendedFieldCount,
60
- hasDiff: diff.missingTables.length > 0 || applyResult.appendedFieldCount > 0,
61
- reportPath: ""
28
+ extraTableCount: diff.extraTables.length,
29
+ extraFieldCount: extraFieldCount,
30
+ hasDiff: hasDiff,
31
+ reportPath: writtenReportPath
62
32
  };
63
33
  } catch (error) {
64
34
  throw new Error("执行表定义同步失败", {