befly 3.5.1 → 3.5.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.
@@ -41,6 +41,21 @@ const globalCount: Record<string, number> = {
41
41
  indexDrop: 0
42
42
  };
43
43
 
44
+ // 导出统计接口
45
+ export interface SyncDbStats {
46
+ processedTables: number;
47
+ createdTables: number;
48
+ modifiedTables: number;
49
+ addFields: number;
50
+ nameChanges: number;
51
+ typeChanges: number;
52
+ minChanges: number;
53
+ maxChanges: number;
54
+ defaultChanges: number;
55
+ indexCreate: number;
56
+ indexDrop: number;
57
+ }
58
+
44
59
  /**
45
60
  * 主同步函数
46
61
  *
@@ -49,15 +64,13 @@ const globalCount: Record<string, number> = {
49
64
  * 2. 建立数据库连接并检查版本
50
65
  * 3. 扫描表定义文件(核心表、项目表、addon表)
51
66
  * 4. 对比并应用表结构变更
52
- * 5. 输出统计信息
67
+ * 5. 返回统计信息
53
68
  */
54
- export const SyncDb = async (): Promise<void> => {
69
+ export const SyncDb = async (): Promise<SyncDbStats> => {
55
70
  const perfTracker = new PerformanceTracker();
56
71
  const progressLogger = new ProgressLogger();
57
72
 
58
73
  try {
59
- Logger.info('开始数据库表结构同步...');
60
-
61
74
  // 重置全局统计,避免多次调用累加
62
75
  for (const k of Object.keys(globalCount)) {
63
76
  if (typeof globalCount[k] === 'number') globalCount[k] = 0;
@@ -68,13 +81,11 @@ export const SyncDb = async (): Promise<void> => {
68
81
  if (!(await checkTable())) {
69
82
  throw new Error('表定义验证失败');
70
83
  }
71
- Logger.info(`✓ 表定义验证完成,耗时: ${perfTracker.getPhaseTime('表定义验证')}`);
72
84
 
73
85
  // 阶段2:建立数据库连接并检查版本
74
86
  perfTracker.markPhase('数据库连接');
75
87
  sql = await Database.connectSql({ max: 1 });
76
88
  await ensureDbVersion(sql);
77
- Logger.info(`✓ 数据库连接建立,耗时: ${perfTracker.getPhaseTime('数据库连接')}`);
78
89
 
79
90
  // 阶段3:扫描表定义文件
80
91
  perfTracker.markPhase('扫描表文件');
@@ -113,7 +124,6 @@ export const SyncDb = async (): Promise<void> => {
113
124
  }
114
125
  }
115
126
  perfTracker.finishPhase('扫描表文件');
116
- Logger.info(`✓ 扫描完成,发现 ${totalTables} 个表定义文件,耗时: ${perfTracker.getPhaseTime('扫描表文件')}`);
117
127
 
118
128
  // 阶段4:处理表文件
119
129
  perfTracker.markPhase('同步处理');
@@ -132,7 +142,6 @@ export const SyncDb = async (): Promise<void> => {
132
142
 
133
143
  // 跳过以下划线开头的文件(这些是公共字段规则,不是表定义)
134
144
  if (fileName.startsWith('_')) {
135
- Logger.info(`跳过非表定义文件: ${fileName}.json`);
136
145
  continue;
137
146
  }
138
147
 
@@ -155,7 +164,6 @@ export const SyncDb = async (): Promise<void> => {
155
164
  }
156
165
 
157
166
  processedCount++;
158
- progressLogger.logTableProgress(processedCount, totalTables, tableName, dirType);
159
167
 
160
168
  const tableDefinition = await Bun.file(file).json();
161
169
  const existsTable = await tableExists(sql!, tableName);
@@ -171,29 +179,21 @@ export const SyncDb = async (): Promise<void> => {
171
179
  }
172
180
 
173
181
  perfTracker.finishPhase('同步处理');
174
- Logger.info(`✓ 表处理完成,耗时: ${perfTracker.getPhaseTime('同步处理')}`);
175
-
176
- // 阶段5:显示统计信息
177
- Logger.info('\n=== 同步统计信息 ===');
178
- Logger.info(`总耗时: ${perfTracker.getTotalTime()}`);
179
- Logger.info(`处理表总数: ${globalCount.processedTables}`);
180
- Logger.info(`创建表: ${globalCount.createdTables}`);
181
- Logger.info(`修改表: ${globalCount.modifiedTables}`);
182
- Logger.info(`字段新增: ${globalCount.addFields}`);
183
- Logger.info(`字段名称变更: ${globalCount.nameChanges}`);
184
- Logger.info(`字段类型变更: ${globalCount.typeChanges}`);
185
- Logger.info(`字段最小值变更: ${globalCount.minChanges}`);
186
- Logger.info(`字段最大值变更: ${globalCount.maxChanges}`);
187
- Logger.info(`字段默认值变更: ${globalCount.defaultChanges}`);
188
- Logger.info(`索引新增: ${globalCount.indexCreate}`);
189
- Logger.info(`索引删除: ${globalCount.indexDrop}`);
190
-
191
- if (globalCount.processedTables === 0) {
192
- Logger.warn('没有找到任何表定义文件');
193
- }
194
182
 
195
- // 输出性能统计
196
- perfTracker.logStats();
183
+ // 返回统计信息
184
+ return {
185
+ processedTables: globalCount.processedTables,
186
+ createdTables: globalCount.createdTables,
187
+ modifiedTables: globalCount.modifiedTables,
188
+ addFields: globalCount.addFields,
189
+ nameChanges: globalCount.nameChanges,
190
+ typeChanges: globalCount.typeChanges,
191
+ minChanges: globalCount.minChanges,
192
+ maxChanges: globalCount.maxChanges,
193
+ defaultChanges: globalCount.defaultChanges,
194
+ indexCreate: globalCount.indexCreate,
195
+ indexDrop: globalCount.indexDrop
196
+ };
197
197
  } catch (error: any) {
198
198
  Logger.error(`数据库同步失败`, error);
199
199
  process.exit(1);
@@ -6,14 +6,14 @@ import { Command } from 'commander';
6
6
  import { join } from 'pathe';
7
7
  import { existsSync } from 'node:fs';
8
8
  import { Logger } from '../lib/logger.js';
9
- import { SyncDb } from './syncDb/index.js';
9
+ import { SyncDb, type SyncDbStats } from './syncDb/index.js';
10
10
 
11
11
  interface SyncDbOptions {
12
12
  table?: string;
13
13
  dryRun: boolean;
14
14
  }
15
15
 
16
- export async function syncDbCommand(options: SyncDbOptions) {
16
+ export async function syncDbCommand(options: SyncDbOptions): Promise<SyncDbStats> {
17
17
  try {
18
18
  // 设置环境变量
19
19
  if (options.dryRun) {
@@ -24,9 +24,9 @@ export async function syncDbCommand(options: SyncDbOptions) {
24
24
  process.env.SYNC_TABLE = options.table;
25
25
  }
26
26
 
27
- // 执行同步
28
- await SyncDb();
29
- Logger.info('数据库表结构同步完成');
27
+ // 执行同步并返回统计
28
+ const stats = await SyncDb();
29
+ return stats;
30
30
  } catch (error: any) {
31
31
  Logger.error('数据库同步失败:', error);
32
32
  process.exit(1);
@@ -16,23 +16,26 @@ interface SyncDevOptions {
16
16
  plan?: boolean;
17
17
  }
18
18
 
19
+ export interface SyncDevStats {
20
+ adminCount: number;
21
+ roleCount: number;
22
+ cachedRoles: number;
23
+ }
24
+
19
25
  /**
20
26
  * SyncDev 命令主函数
21
27
  */
22
- export async function syncDevCommand(options: SyncDevOptions = {}) {
28
+ export async function syncDevCommand(options: SyncDevOptions = {}): Promise<SyncDevStats> {
23
29
  try {
24
30
  if (options.plan) {
25
31
  Logger.info('[计划] 同步完成后将初始化/更新开发管理员账号(plan 模式不执行)');
26
- return;
32
+ return { adminCount: 0, roleCount: 0, cachedRoles: 0 };
27
33
  }
28
34
 
29
35
  if (!Env.DEV_PASSWORD) {
30
- Logger.warn('跳过开发管理员初始化:缺少 DEV_PASSWORD 配置');
31
- return;
36
+ return { adminCount: 0, roleCount: 0, cachedRoles: 0 };
32
37
  }
33
38
 
34
- Logger.info('开始同步开发管理员账号...\n');
35
-
36
39
  // 连接数据库(SQL + Redis)
37
40
  await Database.connect();
38
41
 
@@ -41,22 +44,19 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
41
44
  // 检查 core_admin 表是否存在
42
45
  const existAdmin = await helper.tableExists('core_admin');
43
46
  if (!existAdmin) {
44
- Logger.warn('跳过开发管理员初始化:未检测到 core_admin 表');
45
- return;
47
+ return { adminCount: 0, roleCount: 0, cachedRoles: 0 };
46
48
  }
47
49
 
48
50
  // 检查 core_role 表是否存在
49
51
  const existRole = await helper.tableExists('core_role');
50
52
  if (!existRole) {
51
- Logger.warn('跳过开发管理员初始化:未检测到 core_role 表');
52
- return;
53
+ return { adminCount: 0, roleCount: 0, cachedRoles: 0 };
53
54
  }
54
55
 
55
56
  // 检查 core_menu 表是否存在
56
57
  const existMenu = await helper.tableExists('core_menu');
57
58
  if (!existMenu) {
58
- Logger.warn('跳过开发管理员初始化:未检测到 core_menu 表');
59
- return;
59
+ return { adminCount: 0, roleCount: 0, cachedRoles: 0 };
60
60
  }
61
61
 
62
62
  // 查询所有菜单 ID
@@ -66,12 +66,10 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
66
66
  });
67
67
 
68
68
  if (!allMenus || !Array.isArray(allMenus)) {
69
- Logger.warn('查询菜单失败或菜单表为空');
70
- return;
69
+ return { adminCount: 0, roleCount: 0, cachedRoles: 0 };
71
70
  }
72
71
 
73
72
  const menuIds = allMenus.length > 0 ? allMenus.map((m: any) => m.id).join(',') : '';
74
- Logger.debug(`查询到 ${allMenus.length} 个菜单,ID 列表: ${menuIds || '(空)'}`);
75
73
 
76
74
  // 查询所有接口 ID
77
75
  const existApi = await helper.tableExists('core_api');
@@ -84,12 +82,7 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
84
82
 
85
83
  if (allApis && Array.isArray(allApis) && allApis.length > 0) {
86
84
  apiIds = allApis.map((a: any) => a.id).join(',');
87
- Logger.debug(`查询到 ${allApis.length} 个接口,ID 列表: ${apiIds}`);
88
- } else {
89
- Logger.info('未查询到接口数据');
90
85
  }
91
- } else {
92
- Logger.info('接口表不存在,跳过接口权限配置');
93
86
  }
94
87
 
95
88
  // 查询或创建 dev 角色
@@ -110,7 +103,6 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
110
103
  apis: apiIds
111
104
  }
112
105
  });
113
- Logger.info('dev 角色菜单和接口权限已更新');
114
106
  } else {
115
107
  // 创建 dev 角色
116
108
  const roleId = await helper.insData({
@@ -149,6 +141,7 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
149
141
  where: { email: Env.DEV_EMAIL }
150
142
  });
151
143
 
144
+ let isNew = false;
152
145
  if (existing) {
153
146
  // 更新现有账号
154
147
  await helper.updData({
@@ -156,26 +149,23 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
156
149
  where: { email: Env.DEV_EMAIL },
157
150
  data: devData
158
151
  });
159
- Logger.info(`✅ 开发管理员已更新:email=${Env.DEV_EMAIL}, username=dev, roleCode=dev, roleType=admin`);
160
152
  } else {
161
153
  // 插入新账号
162
154
  await helper.insData({
163
155
  table: 'core_admin',
164
156
  data: devData
165
157
  });
166
- Logger.info(`✅ 开发管理员已初始化:email=${Env.DEV_EMAIL}, username=dev, roleCode=dev, roleType=admin`);
158
+ isNew = true;
167
159
  }
168
160
 
169
161
  // 缓存角色权限数据到 Redis
170
- Logger.info('\n=== 缓存角色权限到 Redis ===');
162
+ let cachedRolesCount = 0;
171
163
  try {
172
164
  // 检查必要的表是否存在
173
165
  const apiTableExists = await helper.tableExists('core_api');
174
166
  const roleTableExists = await helper.tableExists('core_role');
175
167
 
176
- if (!apiTableExists || !roleTableExists) {
177
- Logger.warn('⚠️ 接口或角色表不存在,跳过角色权限缓存');
178
- } else {
168
+ if (apiTableExists && roleTableExists) {
179
169
  // 查询所有角色
180
170
  const roles = await helper.getAll({
181
171
  table: 'core_role',
@@ -189,7 +179,6 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
189
179
  });
190
180
 
191
181
  const redis = Database.getRedis();
192
- let cachedRoles = 0;
193
182
 
194
183
  // 为每个角色缓存接口权限
195
184
  for (const role of roles) {
@@ -216,16 +205,29 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
216
205
  const result = await redis.sadd(redisKey, ...roleApiPaths);
217
206
 
218
207
  if (result > 0) {
219
- cachedRoles++;
220
- Logger.debug(` └ 角色 ${role.code}: ${result} 个接口`);
208
+ cachedRolesCount++;
221
209
  }
222
210
  }
223
-
224
- Logger.info(`✅ 已缓存 ${cachedRoles} 个角色的接口权限`);
225
211
  }
226
212
  } catch (error: any) {
227
- Logger.error('⚠️ 角色权限缓存异常:', error);
213
+ // 忽略缓存错误
228
214
  }
215
+
216
+ // 获取统计数据
217
+ const allAdmins = await helper.getAll({
218
+ table: 'core_admin',
219
+ fields: ['id']
220
+ });
221
+ const allRoles = await helper.getAll({
222
+ table: 'core_role',
223
+ fields: ['id']
224
+ });
225
+
226
+ return {
227
+ adminCount: allAdmins.length,
228
+ roleCount: allRoles.length,
229
+ cachedRoles: cachedRolesCount
230
+ };
229
231
  } catch (error: any) {
230
232
  Logger.error('开发管理员同步失败:', error);
231
233
  process.exit(1);
@@ -34,6 +34,15 @@ interface MenuConfig {
34
34
  children?: MenuConfig[];
35
35
  }
36
36
 
37
+ export interface SyncMenuStats {
38
+ totalMenus: number;
39
+ parentMenus: number;
40
+ childMenus: number;
41
+ created: number;
42
+ updated: number;
43
+ deleted: number;
44
+ }
45
+
37
46
  /**
38
47
  * 读取菜单配置文件
39
48
  * 如果文件不存在或不是数组格式,返回空数组
@@ -41,7 +50,6 @@ interface MenuConfig {
41
50
  async function readMenuConfig(filePath: string): Promise<MenuConfig[]> {
42
51
  try {
43
52
  if (!existsSync(filePath)) {
44
- Logger.warn(`菜单配置文件不存在: ${filePath},使用空数组`);
45
53
  return [];
46
54
  }
47
55
 
@@ -50,13 +58,11 @@ async function readMenuConfig(filePath: string): Promise<MenuConfig[]> {
50
58
 
51
59
  // 验证是否为数组
52
60
  if (!Array.isArray(content)) {
53
- Logger.warn(`菜单配置文件格式错误(非数组): ${filePath},使用空数组`);
54
61
  return [];
55
62
  }
56
63
 
57
64
  return content;
58
65
  } catch (error: any) {
59
- Logger.warn(`读取菜单配置失败: ${filePath},使用空数组`, error.message);
60
66
  return [];
61
67
  }
62
68
  }
@@ -176,7 +182,6 @@ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<{ created: n
176
182
  });
177
183
  parentId = existingParent.id;
178
184
  stats.updated++;
179
- Logger.debug(` └ 更新父级菜单: ${menu.name} (ID: ${parentId}, Path: ${menu.path})`);
180
185
  } else {
181
186
  parentId = await helper.insData({
182
187
  table: 'core_menu',
@@ -190,7 +195,6 @@ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<{ created: n
190
195
  }
191
196
  });
192
197
  stats.created++;
193
- Logger.debug(` └ 新增父级菜单: ${menu.name} (ID: ${parentId}, Path: ${menu.path})`);
194
198
  }
195
199
 
196
200
  // 2. 同步子级菜单(自动追加父级路径前缀)
@@ -217,9 +221,8 @@ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<{ created: n
217
221
  }
218
222
  });
219
223
  stats.updated++;
220
- Logger.debug(` └ 更新子级菜单: ${child.name} (ID: ${existingChild.id}, PID: ${parentId}, Path: ${childFullPath})`);
221
224
  } else {
222
- const childId = await helper.insData({
225
+ await helper.insData({
223
226
  table: 'core_menu',
224
227
  data: {
225
228
  pid: parentId,
@@ -231,7 +234,6 @@ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<{ created: n
231
234
  }
232
235
  });
233
236
  stats.created++;
234
- Logger.debug(` └ 新增子级菜单: ${child.name} (ID: ${childId}, PID: ${parentId}, Path: ${childFullPath})`);
235
237
  }
236
238
  }
237
239
  }
@@ -248,8 +250,6 @@ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<{ created: n
248
250
  * 删除配置中不存在的菜单(强制删除)
249
251
  */
250
252
  async function deleteObsoleteRecords(helper: any, configPaths: Set<string>): Promise<number> {
251
- Logger.debug(`\n=== 删除配置中不存在的记录 ===`);
252
-
253
253
  const allRecords = await helper.getAll({
254
254
  table: 'core_menu',
255
255
  fields: ['id', 'path', 'name'],
@@ -264,60 +264,31 @@ async function deleteObsoleteRecords(helper: any, configPaths: Set<string>): Pro
264
264
  where: { id: record.id }
265
265
  });
266
266
  deletedCount++;
267
- Logger.debug(` └ 删除记录: ${record.name} (ID: ${record.id}, path: ${record.path})`);
268
267
  }
269
268
  }
270
269
 
271
- if (deletedCount === 0) {
272
- Logger.debug(' ✅ 无需删除的记录');
273
- }
274
-
275
270
  return deletedCount;
276
271
  }
277
272
 
278
273
  /**
279
274
  * SyncMenu 命令主函数
280
275
  */
281
- export async function syncMenuCommand(options: SyncMenuOptions = {}) {
276
+ export async function syncMenuCommand(options: SyncMenuOptions = {}): Promise<SyncMenuStats> {
282
277
  try {
283
278
  if (options.plan) {
284
279
  Logger.info('[计划] 同步菜单配置到数据库(plan 模式不执行)');
285
- Logger.info('[计划] 1. 读取 core/config/menu.json 和项目根目录 menu.json');
286
- Logger.info('[计划] 2. 合并菜单配置(core 优先覆盖项目)');
287
- Logger.info('[计划] 3. 子级菜单自动追加父级路径前缀');
288
- Logger.info('[计划] 4. 根据 path 检查菜单是否存在');
289
- Logger.info('[计划] 5. 存在则更新,不存在则新增');
290
- Logger.info('[计划] 6. 强制删除配置中不存在的菜单');
291
- Logger.info('[计划] 7. 显示菜单结构预览');
292
- return;
280
+ return { totalMenus: 0, parentMenus: 0, childMenus: 0, created: 0, updated: 0, deleted: 0 };
293
281
  }
294
282
 
295
- Logger.info('开始同步菜单配置到数据库...\n');
296
-
297
283
  // 1. 读取两个配置文件
298
- Logger.debug('=== 步骤 1: 读取菜单配置文件 ===');
299
284
  const projectMenuPath = join(projectDir, 'menu.json');
300
285
  const coreMenuPath = join(coreDir, 'menu.json');
301
286
 
302
- Logger.info(` 项目路径: ${projectMenuPath}`);
303
- Logger.info(` core 路径: ${coreMenuPath}`);
304
-
305
287
  const projectMenus = await readMenuConfig(projectMenuPath);
306
288
  const coreMenus = await readMenuConfig(coreMenuPath);
307
289
 
308
- Logger.info(`✅ 项目配置: ${projectMenus.length} 个父级菜单`);
309
- Logger.info(`✅ core 配置: ${coreMenus.length} 个父级菜单`);
310
-
311
290
  // 2. 合并菜单配置
312
- Logger.info('\n=== 步骤 2: 合并菜单配置(core 优先覆盖项目) ===');
313
291
  const mergedMenus = mergeMenuConfigs(projectMenus, coreMenus);
314
- Logger.info(`✅ 合并后共有 ${mergedMenus.length} 个父级菜单`);
315
-
316
- // 打印合并后的菜单结构
317
- for (const menu of mergedMenus) {
318
- const childCount = menu.children?.length || 0;
319
- Logger.debug(` └ ${menu.name} (${menu.path}) - ${childCount} 个子菜单`);
320
- }
321
292
 
322
293
  // 连接数据库(SQL + Redis)
323
294
  await Database.connect();
@@ -325,7 +296,6 @@ export async function syncMenuCommand(options: SyncMenuOptions = {}) {
325
296
  const helper = Database.getDbHelper();
326
297
 
327
298
  // 3. 检查表是否存在
328
- Logger.debug('\n=== 步骤 3: 检查数据表 ===');
329
299
  const exists = await helper.tableExists('core_menu');
330
300
 
331
301
  if (!exists) {
@@ -333,47 +303,23 @@ export async function syncMenuCommand(options: SyncMenuOptions = {}) {
333
303
  process.exit(1);
334
304
  }
335
305
 
336
- Logger.debug(`✅ 表 core_menu 存在`);
337
-
338
306
  // 4. 收集配置文件中所有菜单的 path
339
- Logger.debug('\n=== 步骤 4: 收集配置菜单路径 ===');
340
307
  const configPaths = collectPaths(mergedMenus);
341
- Logger.debug(`✅ 配置文件中共有 ${configPaths.size} 个菜单路径`);
342
308
 
343
309
  // 5. 同步菜单
344
- Logger.debug('\n=== 步骤 5: 同步菜单数据(新增/更新) ===');
345
310
  const stats = await syncMenus(helper, mergedMenus);
346
311
 
347
312
  // 6. 删除文件中不存在的菜单(强制删除)
348
313
  const deletedCount = await deleteObsoleteRecords(helper, configPaths);
349
314
 
350
- // 7. 构建树形结构预览
351
- Logger.debug('\n=== 步骤 7: 菜单结构预览 ===');
315
+ // 7. 获取最终菜单数据
352
316
  const allMenus = await helper.getAll({
353
317
  table: 'core_menu',
354
318
  fields: ['id', 'pid', 'name', 'path', 'type'],
355
319
  orderBy: ['pid#ASC', 'sort#ASC', 'id#ASC']
356
320
  });
357
321
 
358
- const parentMenus = allMenus.filter((m: any) => m.pid === 0);
359
- for (const parent of parentMenus) {
360
- const children = allMenus.filter((m: any) => m.pid === parent.id);
361
- Logger.debug(` └ ${parent.name} (${parent.path})`);
362
- for (const child of children) {
363
- Logger.debug(` └ ${child.name} (${child.path})`);
364
- }
365
- }
366
-
367
- // 8. 输出统计信息
368
- Logger.info(`\n=== 菜单同步完成 ===`);
369
- Logger.info(`新增菜单: ${stats.created} 个`);
370
- Logger.info(`更新菜单: ${stats.updated} 个`);
371
- Logger.info(`删除菜单: ${deletedCount} 个`);
372
- Logger.info(`当前父级菜单: ${allMenus.filter((m: any) => m.pid === 0).length} 个`);
373
- Logger.info(`当前子级菜单: ${allMenus.filter((m: any) => m.pid !== 0).length} 个`);
374
-
375
- // 9. 缓存菜单数据到 Redis
376
- Logger.info('\n=== 步骤 8: 缓存菜单数据到 Redis ===');
322
+ // 8. 缓存菜单数据到 Redis
377
323
  try {
378
324
  const menus = await helper.getAll({
379
325
  table: 'core_menu',
@@ -381,16 +327,19 @@ export async function syncMenuCommand(options: SyncMenuOptions = {}) {
381
327
  orderBy: ['sort#ASC', 'id#ASC']
382
328
  });
383
329
 
384
- const result = await RedisHelper.setObject('menus:all', menus);
385
-
386
- if (result === null) {
387
- Logger.warn('⚠️ 菜单缓存失败');
388
- } else {
389
- Logger.info(`✅ 已缓存 ${menus.length} 个菜单到 Redis (Key: menus:all)`);
390
- }
330
+ await RedisHelper.setObject('menus:all', menus);
391
331
  } catch (error: any) {
392
- Logger.error('⚠️ 菜单缓存异常:', error);
332
+ // 忽略缓存错误
393
333
  }
334
+
335
+ return {
336
+ totalMenus: allMenus.length,
337
+ parentMenus: allMenus.filter((m: any) => m.pid === 0).length,
338
+ childMenus: allMenus.filter((m: any) => m.pid !== 0).length,
339
+ created: stats.created,
340
+ updated: stats.updated,
341
+ deleted: deletedCount
342
+ };
394
343
  } catch (error: any) {
395
344
  Logger.error('菜单同步失败:', error);
396
345
  process.exit(1);
package/lib/database.ts CHANGED
@@ -87,8 +87,6 @@ export class Database {
87
87
 
88
88
  const version = await Promise.race([healthCheckPromise, timeoutPromise]);
89
89
 
90
- Logger.info(`数据库连接成功,version: ${version}`);
91
-
92
90
  this.sqlClient = sql;
93
91
  return sql;
94
92
  } catch (error: any) {
@@ -110,7 +108,6 @@ export class Database {
110
108
  if (this.sqlClient) {
111
109
  try {
112
110
  await this.sqlClient.close();
113
- Logger.info('SQL 连接已关闭');
114
111
  } catch (error: any) {
115
112
  Logger.warn('关闭 SQL 连接时出错:', error.message);
116
113
  }
@@ -186,7 +183,6 @@ export class Database {
186
183
  });
187
184
 
188
185
  await redis.ping();
189
- Logger.info('Redis 连接成功');
190
186
 
191
187
  this.redisClient = redis;
192
188
  return redis;
@@ -203,7 +199,6 @@ export class Database {
203
199
  if (this.redisClient) {
204
200
  try {
205
201
  this.redisClient.close();
206
- Logger.info('Redis 连接已关闭');
207
202
  } catch (error: any) {
208
203
  Logger.warn('关闭 Redis 连接时出错:', error);
209
204
  }
@@ -233,16 +228,12 @@ export class Database {
233
228
  static async connect(options?: { sql?: SqlClientOptions; redis?: boolean }): Promise<void> {
234
229
  try {
235
230
  if (options?.sql !== false) {
236
- Logger.info('正在初始化 SQL 连接...');
237
231
  await this.connectSql(options?.sql);
238
232
  }
239
233
 
240
234
  if (options?.redis !== false) {
241
- Logger.info('正在初始化 Redis 连接...');
242
235
  await this.connectRedis();
243
236
  }
244
-
245
- Logger.info('数据库连接初始化完成');
246
237
  } catch (error: any) {
247
238
  Logger.error('数据库初始化失败', error);
248
239
  await this.disconnect();
package/lib/logger.ts CHANGED
@@ -217,10 +217,12 @@ export class Logger {
217
217
  * 用于命令开始时提示用户当前环境
218
218
  */
219
219
  static printEnv(): void {
220
- const env = process.env.NODE_ENV || 'development';
221
- const envColor = env === 'production' ? chalk.red : env === 'test' ? chalk.yellow : chalk.green;
222
- console.log(chalk.gray('────────────────────────────────────────'));
223
- console.log(chalk.bold('运行环境: ') + envColor.bold(env.toUpperCase()));
224
- console.log(chalk.gray('────────────────────────────────────────'));
220
+ console.log('========================================');
221
+ console.log('开始执行完整同步流程');
222
+ console.log(`当前环境: ${process.env.NODE_ENV || 'development'}`);
223
+ console.log(`项目名称: ${Env.APP_NAME}`);
224
+ console.log(`数据库地址: ${Env.DB_HOST}`);
225
+ console.log(`数据库名称: ${Env.DB_NAME}`);
226
+ console.log('========================================\n');
225
227
  }
226
228
  }