befly 3.19.8 → 3.20.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.
- package/checks/config.js +3 -3
- package/configs/beflyConfig.json +1 -2
- package/configs/constConfig.js +0 -3
- package/lib/dbHelper/builders.js +5 -5
- package/lib/dbHelper/dataOps.js +14 -17
- package/lib/dbHelper/index.js +1 -2
- package/package.json +2 -2
- package/plugins/mysql.js +0 -1
- package/scripts/syncDb/context.js +9 -2
- package/scripts/syncDb/diff.js +22 -3
- package/scripts/syncDb/index.js +5 -1
- package/scripts/syncDb/report.js +5 -0
package/checks/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
|
|
3
3
|
import { Logger } from "../lib/logger.js";
|
|
4
|
-
import {
|
|
4
|
+
import { RUN_MODE_VALUES } from "../configs/constConfig.js";
|
|
5
5
|
import { formatZodIssues } from "../utils/formatZodIssues.js";
|
|
6
6
|
import { isNoTrimStringAllowEmpty, isValidTimeZone } from "../utils/is.js";
|
|
7
7
|
|
|
@@ -9,6 +9,7 @@ z.config(z.locales.zhCN());
|
|
|
9
9
|
|
|
10
10
|
const boolIntSchema = z.union([z.literal(0), z.literal(1), z.literal(true), z.literal(false)]);
|
|
11
11
|
const noTrimString = z.string().refine(isNoTrimStringAllowEmpty, "不允许首尾空格");
|
|
12
|
+
const beflyModeSchema = z.union([z.literal("manual"), z.literal("auto")]);
|
|
12
13
|
|
|
13
14
|
const configSchema = z
|
|
14
15
|
.object({
|
|
@@ -36,14 +37,13 @@ const configSchema = z
|
|
|
36
37
|
|
|
37
38
|
mysql: z
|
|
38
39
|
.object({
|
|
39
|
-
idMode: z.enum(DB_ID_MODE_VALUES),
|
|
40
40
|
hostname: noTrimString,
|
|
41
41
|
port: z.int().min(1).max(65535),
|
|
42
42
|
username: noTrimString,
|
|
43
43
|
password: noTrimString,
|
|
44
44
|
database: noTrimString,
|
|
45
45
|
max: z.number().min(1),
|
|
46
|
-
beflyMode:
|
|
46
|
+
beflyMode: beflyModeSchema.optional()
|
|
47
47
|
})
|
|
48
48
|
.strict(),
|
|
49
49
|
|
package/configs/beflyConfig.json
CHANGED
|
@@ -20,14 +20,13 @@
|
|
|
20
20
|
},
|
|
21
21
|
|
|
22
22
|
"mysql": {
|
|
23
|
-
"idMode": "timeId",
|
|
24
23
|
"hostname": "127.0.0.1",
|
|
25
24
|
"port": 3306,
|
|
26
25
|
"username": "root",
|
|
27
26
|
"password": "root",
|
|
28
27
|
"database": "befly_dev",
|
|
29
28
|
"max": 10,
|
|
30
|
-
"beflyMode":
|
|
29
|
+
"beflyMode": "auto"
|
|
31
30
|
},
|
|
32
31
|
|
|
33
32
|
"redis": {
|
package/configs/constConfig.js
CHANGED
|
@@ -30,9 +30,6 @@ export const ENUM_KIND_TYPES = ["enum"];
|
|
|
30
30
|
// 运行模式可选值
|
|
31
31
|
export const RUN_MODE_VALUES = ["development", "production"];
|
|
32
32
|
|
|
33
|
-
// 数据库 ID 生成模式可选值
|
|
34
|
-
export const DB_ID_MODE_VALUES = ["timeId", "autoId"];
|
|
35
|
-
|
|
36
33
|
// ==========================
|
|
37
34
|
// 字段验证规则配置
|
|
38
35
|
// ==========================
|
package/lib/dbHelper/builders.js
CHANGED
|
@@ -354,8 +354,8 @@ export function processJoinOn(on) {
|
|
|
354
354
|
return result;
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
export function addDefaultStateFilter(where = {}, table, hasJoins = false, beflyMode =
|
|
358
|
-
if (beflyMode ===
|
|
357
|
+
export function addDefaultStateFilter(where = {}, table, hasJoins = false, beflyMode = "auto") {
|
|
358
|
+
if (beflyMode === "manual") {
|
|
359
359
|
return where;
|
|
360
360
|
}
|
|
361
361
|
|
|
@@ -488,12 +488,12 @@ export function buildInsertRow(options) {
|
|
|
488
488
|
result[key] = value;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
if (options.
|
|
491
|
+
if (options.beflyMode === "auto") {
|
|
492
492
|
validateTimeIdValue(options.id);
|
|
493
493
|
result["id"] = options.id;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
if (options.beflyMode !==
|
|
496
|
+
if (options.beflyMode !== "manual") {
|
|
497
497
|
result["created_at"] = options.now;
|
|
498
498
|
result["updated_at"] = options.now;
|
|
499
499
|
result["state"] = 1;
|
|
@@ -509,7 +509,7 @@ export function buildUpdateRow(options) {
|
|
|
509
509
|
for (const [key, value] of Object.entries(userData)) {
|
|
510
510
|
result[key] = value;
|
|
511
511
|
}
|
|
512
|
-
if (options.beflyMode !==
|
|
512
|
+
if (options.beflyMode !== "manual") {
|
|
513
513
|
result["updated_at"] = options.now;
|
|
514
514
|
}
|
|
515
515
|
return result;
|
package/lib/dbHelper/dataOps.js
CHANGED
|
@@ -192,9 +192,8 @@ export const dataOpsMethods = {
|
|
|
192
192
|
const now = Date.now();
|
|
193
193
|
|
|
194
194
|
let processed;
|
|
195
|
-
if (this.
|
|
195
|
+
if (this.beflyMode === "manual") {
|
|
196
196
|
processed = buildInsertRow({
|
|
197
|
-
idMode: "autoId",
|
|
198
197
|
data: data,
|
|
199
198
|
now: now,
|
|
200
199
|
beflyMode: this.beflyMode
|
|
@@ -213,7 +212,6 @@ export const dataOpsMethods = {
|
|
|
213
212
|
});
|
|
214
213
|
}
|
|
215
214
|
processed = buildInsertRow({
|
|
216
|
-
idMode: "timeId",
|
|
217
215
|
data: data,
|
|
218
216
|
id: id,
|
|
219
217
|
now: now,
|
|
@@ -231,9 +229,9 @@ export const dataOpsMethods = {
|
|
|
231
229
|
const processedIdNum = isNumber(processedId) ? processedId : 0;
|
|
232
230
|
const lastInsertRowidNum = toNumberFromSql(executeRes.data?.lastInsertRowid);
|
|
233
231
|
|
|
234
|
-
const insertedId = this.
|
|
235
|
-
if (this.
|
|
236
|
-
throw new Error(`插入失败:
|
|
232
|
+
const insertedId = this.beflyMode === "manual" ? lastInsertRowidNum || 0 : processedIdNum || lastInsertRowidNum || 0;
|
|
233
|
+
if (this.beflyMode === "manual" && insertedId <= 0) {
|
|
234
|
+
throw new Error(`插入失败:beflyMode=manual 时无法获取 lastInsertRowid (table: ${table})`, {
|
|
237
235
|
cause: null,
|
|
238
236
|
code: "runtime"
|
|
239
237
|
});
|
|
@@ -261,9 +259,9 @@ export const dataOpsMethods = {
|
|
|
261
259
|
let ids = [];
|
|
262
260
|
|
|
263
261
|
let processedList;
|
|
264
|
-
if (this.
|
|
262
|
+
if (this.beflyMode === "manual") {
|
|
265
263
|
processedList = dataList.map((data) => {
|
|
266
|
-
return buildInsertRow({
|
|
264
|
+
return buildInsertRow({ data: data, now: now, beflyMode: this.beflyMode });
|
|
267
265
|
});
|
|
268
266
|
} else {
|
|
269
267
|
const nextIds = [];
|
|
@@ -274,7 +272,7 @@ export const dataOpsMethods = {
|
|
|
274
272
|
processedList = dataList.map((data, index) => {
|
|
275
273
|
const id = nextIds[index];
|
|
276
274
|
validateGeneratedBatchId(id, snakeTable, index);
|
|
277
|
-
return buildInsertRow({
|
|
275
|
+
return buildInsertRow({ data: data, id: id, now: now, beflyMode: this.beflyMode });
|
|
278
276
|
});
|
|
279
277
|
}
|
|
280
278
|
|
|
@@ -285,10 +283,10 @@ export const dataOpsMethods = {
|
|
|
285
283
|
try {
|
|
286
284
|
const executeRes = await this.execute(sql, params);
|
|
287
285
|
|
|
288
|
-
if (this.
|
|
286
|
+
if (this.beflyMode === "manual") {
|
|
289
287
|
const firstId = toNumberFromSql(executeRes.data?.lastInsertRowid);
|
|
290
288
|
if (firstId <= 0) {
|
|
291
|
-
throw new Error(`批量插入失败:
|
|
289
|
+
throw new Error(`批量插入失败:beflyMode=manual 时无法获取 lastInsertRowid (table: ${table})`, {
|
|
292
290
|
cause: null,
|
|
293
291
|
code: "runtime"
|
|
294
292
|
});
|
|
@@ -393,10 +391,10 @@ export const dataOpsMethods = {
|
|
|
393
391
|
rows: processedList,
|
|
394
392
|
fields: fields,
|
|
395
393
|
quoteIdent: quoteIdentMySql,
|
|
396
|
-
updatedAtField: this.beflyMode ===
|
|
397
|
-
updatedAtValue: this.beflyMode ===
|
|
394
|
+
updatedAtField: this.beflyMode === "auto" ? "updated_at" : "",
|
|
395
|
+
updatedAtValue: this.beflyMode === "auto" ? now : null,
|
|
398
396
|
stateField: "state",
|
|
399
|
-
stateGtZero: this.beflyMode ===
|
|
397
|
+
stateGtZero: this.beflyMode === "auto"
|
|
400
398
|
});
|
|
401
399
|
|
|
402
400
|
const executeRes = await this.execute(query.sql, query.params);
|
|
@@ -431,8 +429,8 @@ export const dataOpsMethods = {
|
|
|
431
429
|
validateTableWhereOptions(options, "delData", true);
|
|
432
430
|
const { table, where } = options;
|
|
433
431
|
|
|
434
|
-
// beflyMode=
|
|
435
|
-
if (this.beflyMode ===
|
|
432
|
+
// beflyMode=manual:关闭默认 state 软删语义,delData 直接走物理删除
|
|
433
|
+
if (this.beflyMode === "manual") {
|
|
436
434
|
return await this.delForce({
|
|
437
435
|
table: table,
|
|
438
436
|
where: where
|
|
@@ -552,7 +550,6 @@ export const dataOpsMethods = {
|
|
|
552
550
|
dbName: this.dbName,
|
|
553
551
|
sql: tx,
|
|
554
552
|
isTransaction: true,
|
|
555
|
-
idMode: this.idMode,
|
|
556
553
|
beflyMode: this.beflyMode
|
|
557
554
|
});
|
|
558
555
|
const result = await callback(trans);
|
package/lib/dbHelper/index.js
CHANGED
|
@@ -16,8 +16,7 @@ function DbHelper(options) {
|
|
|
16
16
|
|
|
17
17
|
this.sql = options.sql || null;
|
|
18
18
|
this.isTransaction = options.isTransaction === true;
|
|
19
|
-
this.
|
|
20
|
-
this.beflyMode = options.beflyMode === 0 ? 0 : 1;
|
|
19
|
+
this.beflyMode = options.beflyMode === "manual" ? "manual" : "auto";
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
Object.assign(DbHelper.prototype, builderMethods, executeMethods, dataOpsMethods);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"gitHead": "
|
|
3
|
+
"version": "3.20.0",
|
|
4
|
+
"gitHead": "218c4ffe07983e46bbb85f9e0a626a6b3b38c179",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
|
|
7
7
|
"keywords": [
|
package/plugins/mysql.js
CHANGED
|
@@ -9,6 +9,7 @@ import { isNonEmptyString, isPlainObject } from "../../utils/is.js";
|
|
|
9
9
|
import { camelCase } from "../../utils/util.js";
|
|
10
10
|
import { buildSyncDbDiff, groupSyncDbColumns } from "./diff.js";
|
|
11
11
|
import { querySyncDbColumns } from "./query.js";
|
|
12
|
+
import { printSyncDbProcessLog } from "./report.js";
|
|
12
13
|
|
|
13
14
|
async function loadSyncDbTableDefs(tablesDir) {
|
|
14
15
|
const tableMap = new Map();
|
|
@@ -52,6 +53,7 @@ async function loadSyncDbTableDefs(tablesDir) {
|
|
|
52
53
|
export async function prepareSyncDbBaseContext(mysqlConfig) {
|
|
53
54
|
const tablesDir = resolve(join(process.cwd(), "tables"));
|
|
54
55
|
await mkdir(tablesDir, { recursive: true });
|
|
56
|
+
printSyncDbProcessLog(`tables目录=${tablesDir}`);
|
|
55
57
|
|
|
56
58
|
if (!isPlainObject(mysqlConfig)) {
|
|
57
59
|
throw new Error("syncDb 缺少 mysql 配置", {
|
|
@@ -62,16 +64,19 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
|
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
printSyncDbProcessLog(`beflyMode=${mysqlConfig.beflyMode === "manual" ? "manual" : "auto"}`);
|
|
68
|
+
printSyncDbProcessLog(`连接 MySQL,database=${mysqlConfig.database}`);
|
|
65
69
|
await Connect.connectMysql(mysqlConfig);
|
|
66
70
|
const mysql = new DbHelper({
|
|
67
71
|
redis: null,
|
|
68
72
|
dbName: mysqlConfig.database,
|
|
69
73
|
sql: Connect.getMysql(),
|
|
70
|
-
idMode: mysqlConfig.idMode,
|
|
71
74
|
beflyMode: mysqlConfig.beflyMode
|
|
72
75
|
});
|
|
73
76
|
|
|
77
|
+
printSyncDbProcessLog("开始读取数据库字段信息");
|
|
74
78
|
const dbColumns = await querySyncDbColumns(mysql);
|
|
79
|
+
printSyncDbProcessLog(`数据库字段读取完成,columnCount=${dbColumns.length}`);
|
|
75
80
|
const skippedBeflyTables = new Set();
|
|
76
81
|
for (const row of dbColumns) {
|
|
77
82
|
const tableName = String(row?.tableName || "");
|
|
@@ -89,7 +94,9 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
|
|
|
89
94
|
|
|
90
95
|
const groupedDbColumns = groupSyncDbColumns(dbColumns);
|
|
91
96
|
const existingTableMap = await loadSyncDbTableDefs(tablesDir);
|
|
92
|
-
const diff = buildSyncDbDiff(groupedDbColumns, existingTableMap);
|
|
97
|
+
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)}`);
|
|
93
100
|
|
|
94
101
|
return {
|
|
95
102
|
diff: diff,
|
package/scripts/syncDb/diff.js
CHANGED
|
@@ -2,8 +2,20 @@ import { join } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import { isNonEmptyString, isPlainObject } from "../../utils/is.js";
|
|
4
4
|
import { camelCase } from "../../utils/util.js";
|
|
5
|
+
import { printSyncDbProcessLog } from "./report.js";
|
|
5
6
|
import { toSyncDbFieldDef } from "./transform.js";
|
|
6
7
|
|
|
8
|
+
const SYNC_DB_MANAGED_FIELD_NAMES = new Set(["id", "created_at", "updated_at", "deleted_at", "state"]);
|
|
9
|
+
|
|
10
|
+
function shouldSkipSyncDbColumn(columnMeta, beflyMode) {
|
|
11
|
+
if (beflyMode !== "manual") {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const columnName = String(columnMeta?.columnName || "").toLowerCase();
|
|
16
|
+
return SYNC_DB_MANAGED_FIELD_NAMES.has(columnName);
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
export function groupSyncDbColumns(rows) {
|
|
8
20
|
const grouped = new Map();
|
|
9
21
|
|
|
@@ -36,26 +48,31 @@ export function groupSyncDbColumns(rows) {
|
|
|
36
48
|
return grouped;
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
export function buildSyncDbDiff(groupedDbColumns, existingTableMap) {
|
|
51
|
+
export function buildSyncDbDiff(groupedDbColumns, existingTableMap, beflyMode = "auto") {
|
|
40
52
|
const missingTables = [];
|
|
41
53
|
const missingFieldsByTable = {};
|
|
42
54
|
|
|
43
55
|
for (const [tableName, columns] of groupedDbColumns.entries()) {
|
|
44
56
|
const tableFileName = camelCase(tableName);
|
|
45
57
|
const existing = existingTableMap.get(String(tableFileName).toLowerCase());
|
|
58
|
+
const filteredColumns = columns.filter((columnMeta) => !shouldSkipSyncDbColumn(columnMeta, beflyMode));
|
|
46
59
|
|
|
47
60
|
if (!existing) {
|
|
61
|
+
if (filteredColumns.length === 0) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
missingTables.push({
|
|
49
66
|
tableName: tableName,
|
|
50
67
|
tableFileName: tableFileName,
|
|
51
|
-
columns:
|
|
68
|
+
columns: filteredColumns
|
|
52
69
|
});
|
|
53
70
|
continue;
|
|
54
71
|
}
|
|
55
72
|
|
|
56
73
|
const existingFields = isPlainObject(existing.tableDef) ? existing.tableDef : {};
|
|
57
74
|
const missingFields = [];
|
|
58
|
-
for (const columnMeta of
|
|
75
|
+
for (const columnMeta of filteredColumns) {
|
|
59
76
|
const fieldInfo = toSyncDbFieldDef(columnMeta);
|
|
60
77
|
if (Object.hasOwn(existingFields, fieldInfo.fieldName)) {
|
|
61
78
|
continue;
|
|
@@ -103,6 +120,7 @@ export async function applySyncDbDiff(diff, tablesDir, existingTableMap) {
|
|
|
103
120
|
const filePath = join(tablesDir, `${missingTable.tableFileName}.json`);
|
|
104
121
|
await Bun.write(filePath, `${JSON.stringify(tableDef, null, 4)}\n`);
|
|
105
122
|
createdTableFileCount = createdTableFileCount + 1;
|
|
123
|
+
printSyncDbProcessLog(`已生成表映射 ${filePath}`);
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
for (const [tableFileName, tableInfo] of Object.entries(diff.missingFieldsByTable)) {
|
|
@@ -116,6 +134,7 @@ export async function applySyncDbDiff(diff, tablesDir, existingTableMap) {
|
|
|
116
134
|
}
|
|
117
135
|
|
|
118
136
|
await Bun.write(targetFilePath, `${JSON.stringify(currentTableDef, null, 4)}\n`);
|
|
137
|
+
printSyncDbProcessLog(`已补充字段映射 ${targetFilePath},fieldCount=${tableInfo.fields.length}`);
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
return {
|
package/scripts/syncDb/index.js
CHANGED
|
@@ -5,10 +5,11 @@ import { Connect } from "../../lib/connect.js";
|
|
|
5
5
|
import { Logger } from "../../lib/logger.js";
|
|
6
6
|
import { applySyncDbDiff, countSyncDbMissingFields } from "./diff.js";
|
|
7
7
|
import { prepareSyncDbBaseContext } from "./context.js";
|
|
8
|
-
import { printSyncDbDiffSummary, writeSyncDbCheckReport } from "./report.js";
|
|
8
|
+
import { printSyncDbDiffSummary, printSyncDbProcessLog, writeSyncDbCheckReport } from "./report.js";
|
|
9
9
|
|
|
10
10
|
export async function syncDbCheck(mysqlConfig) {
|
|
11
11
|
try {
|
|
12
|
+
printSyncDbProcessLog("开始执行 check");
|
|
12
13
|
const reportPath = resolve(join(process.cwd(), "db.check.md"));
|
|
13
14
|
await mkdir(dirname(reportPath), { recursive: true });
|
|
14
15
|
await Bun.write(reportPath, "");
|
|
@@ -17,6 +18,7 @@ export async function syncDbCheck(mysqlConfig) {
|
|
|
17
18
|
const missingFieldCount = countSyncDbMissingFields(diff);
|
|
18
19
|
const writtenReportPath = await writeSyncDbCheckReport(reportPath, diff, groupedDbColumns, existingTableMap);
|
|
19
20
|
printSyncDbDiffSummary("check", diff);
|
|
21
|
+
printSyncDbProcessLog(`check完成,missingTableCount=${diff.missingTables.length},missingFieldCount=${missingFieldCount}`);
|
|
20
22
|
|
|
21
23
|
return {
|
|
22
24
|
mode: "check",
|
|
@@ -42,10 +44,12 @@ export async function syncDbCheck(mysqlConfig) {
|
|
|
42
44
|
|
|
43
45
|
export async function syncDbApply(mysqlConfig) {
|
|
44
46
|
try {
|
|
47
|
+
printSyncDbProcessLog("开始执行 apply");
|
|
45
48
|
const { diff, tablesDir, existingTableMap } = await prepareSyncDbBaseContext(mysqlConfig);
|
|
46
49
|
printSyncDbDiffSummary("check", diff);
|
|
47
50
|
const applyResult = await applySyncDbDiff(diff, tablesDir, existingTableMap);
|
|
48
51
|
printSyncDbDiffSummary("apply", diff);
|
|
52
|
+
printSyncDbProcessLog(`apply完成,createdTableFileCount=${applyResult.createdTableFileCount},appendedFieldCount=${applyResult.appendedFieldCount}`);
|
|
49
53
|
|
|
50
54
|
return {
|
|
51
55
|
mode: "apply",
|
package/scripts/syncDb/report.js
CHANGED
|
@@ -7,6 +7,10 @@ import { camelCase } from "../../utils/util.js";
|
|
|
7
7
|
import { countSyncDbMissingFields } from "./diff.js";
|
|
8
8
|
import { toSyncDbFieldDef } from "./transform.js";
|
|
9
9
|
|
|
10
|
+
export function printSyncDbProcessLog(message) {
|
|
11
|
+
process.stdout.write(`[syncDb] ${message}\n`);
|
|
12
|
+
}
|
|
13
|
+
|
|
10
14
|
export function printSyncDbDiffSummary(mode, diff) {
|
|
11
15
|
const tableNames = diff.missingTables.map((item) => item.tableName);
|
|
12
16
|
const missingFieldGroups = Object.values(diff.missingFieldsByTable);
|
|
@@ -182,6 +186,7 @@ export async function writeSyncDbCheckReport(reportPath, diff, groupedDbColumns,
|
|
|
182
186
|
await mkdir(dirname(reportPath), { recursive: true });
|
|
183
187
|
const markdown = buildSyncDbCheckMarkdown(diff, groupedDbColumns, existingTableMap);
|
|
184
188
|
await Bun.write(reportPath, markdown);
|
|
189
|
+
printSyncDbProcessLog(`差异报告已写入 ${reportPath}`);
|
|
185
190
|
Logger.info("差异报告写入完成", {
|
|
186
191
|
reportPath: reportPath
|
|
187
192
|
});
|