befly 3.5.0 → 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.
- package/bin/index.ts +17 -130
- package/checks/table.ts +5 -7
- package/commands/index.ts +2 -153
- package/commands/sync.ts +60 -28
- package/commands/syncApi.ts +22 -52
- package/commands/syncDb/index.ts +31 -31
- package/commands/syncDb.ts +5 -5
- package/commands/syncDev.ts +37 -35
- package/commands/syncMenu.ts +25 -76
- package/lib/database.ts +0 -9
- package/lib/logger.ts +7 -5
- package/lifecycle/checker.ts +8 -25
- package/lifecycle/lifecycle.ts +5 -34
- package/lifecycle/loader.ts +10 -100
- package/main.ts +0 -1
- package/package.json +2 -4
- package/commands/build.ts +0 -62
package/commands/syncApi.ts
CHANGED
|
@@ -32,6 +32,13 @@ interface ApiInfo {
|
|
|
32
32
|
addonTitle: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
export interface SyncApiStats {
|
|
36
|
+
totalApis: number;
|
|
37
|
+
created: number;
|
|
38
|
+
updated: number;
|
|
39
|
+
deleted: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
/**
|
|
36
43
|
* 递归扫描目录下的所有 .ts 文件
|
|
37
44
|
*/
|
|
@@ -111,36 +118,29 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
|
|
|
111
118
|
const apis: ApiInfo[] = [];
|
|
112
119
|
|
|
113
120
|
// 1. 扫描 Core 框架 API
|
|
114
|
-
Logger.info('=== 扫描 Core 框架 API (core/apis) ===');
|
|
115
121
|
const coreApisDir = join(dirname(projectRoot), 'core', 'apis');
|
|
116
122
|
try {
|
|
117
123
|
const coreApiFiles = scanTsFiles(coreApisDir);
|
|
118
|
-
Logger.info(` 找到 ${coreApiFiles.length} 个核心 API 文件`);
|
|
119
124
|
|
|
120
125
|
for (const filePath of coreApiFiles) {
|
|
121
126
|
const apiInfo = await extractApiInfo(filePath, coreApisDir, 'core', '', '核心接口');
|
|
122
127
|
if (apiInfo) {
|
|
123
128
|
apis.push(apiInfo);
|
|
124
|
-
Logger.info(` └ ${apiInfo.path} - ${apiInfo.name}`);
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
// 2. 扫描项目 API
|
|
129
|
-
Logger.info('\n=== 扫描项目 API (apis) ===');
|
|
130
133
|
const projectApisDir = join(projectRoot, 'apis');
|
|
131
134
|
const projectApiFiles = scanTsFiles(projectApisDir);
|
|
132
|
-
Logger.info(` 找到 ${projectApiFiles.length} 个项目 API 文件`);
|
|
133
135
|
|
|
134
136
|
for (const filePath of projectApiFiles) {
|
|
135
137
|
const apiInfo = await extractApiInfo(filePath, projectApisDir, 'app', '', '项目接口');
|
|
136
138
|
if (apiInfo) {
|
|
137
139
|
apis.push(apiInfo);
|
|
138
|
-
Logger.info(` └ ${apiInfo.path} - ${apiInfo.name}`);
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
// 3. 扫描组件 API (node_modules/@befly-addon/*)
|
|
143
|
-
Logger.info('\n=== 扫描组件 API (node_modules/@befly-addon/*) ===');
|
|
144
144
|
const addonNames = scanAddons();
|
|
145
145
|
|
|
146
146
|
for (const addonName of addonNames) {
|
|
@@ -148,7 +148,6 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
|
|
|
148
148
|
|
|
149
149
|
// 检查 apis 子目录是否存在
|
|
150
150
|
if (!addonDirExists(addonName, 'apis')) {
|
|
151
|
-
Logger.info(` [${addonName}] 无 apis 目录,跳过`);
|
|
152
151
|
continue;
|
|
153
152
|
}
|
|
154
153
|
|
|
@@ -162,17 +161,15 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
|
|
|
162
161
|
const config = await configFile.json();
|
|
163
162
|
addonTitle = config.title || addonName;
|
|
164
163
|
} catch (error) {
|
|
165
|
-
|
|
164
|
+
// 忽略配置读取错误
|
|
166
165
|
}
|
|
167
166
|
|
|
168
167
|
const addonApiFiles = scanTsFiles(addonApisDir);
|
|
169
|
-
Logger.info(` [${addonName}] 找到 ${addonApiFiles.length} 个 API 文件`);
|
|
170
168
|
|
|
171
169
|
for (const filePath of addonApiFiles) {
|
|
172
170
|
const apiInfo = await extractApiInfo(filePath, addonApisDir, 'addon', addonName, addonTitle);
|
|
173
171
|
if (apiInfo) {
|
|
174
172
|
apis.push(apiInfo);
|
|
175
|
-
Logger.info(` └ ${apiInfo.path} - ${apiInfo.name}`);
|
|
176
173
|
}
|
|
177
174
|
}
|
|
178
175
|
}
|
|
@@ -180,6 +177,7 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
|
|
|
180
177
|
return apis;
|
|
181
178
|
} catch (error: any) {
|
|
182
179
|
Logger.error(`接口扫描失败:`, error);
|
|
180
|
+
return apis;
|
|
183
181
|
}
|
|
184
182
|
}
|
|
185
183
|
|
|
@@ -191,6 +189,7 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<{ created: number
|
|
|
191
189
|
|
|
192
190
|
for (const api of apis) {
|
|
193
191
|
try {
|
|
192
|
+
// 根据 path 查询是否存在
|
|
194
193
|
const existing = await helper.getOne({
|
|
195
194
|
table: 'core_api',
|
|
196
195
|
where: { path: api.path }
|
|
@@ -209,7 +208,6 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<{ created: number
|
|
|
209
208
|
}
|
|
210
209
|
});
|
|
211
210
|
stats.updated++;
|
|
212
|
-
Logger.info(` └ 更新接口: ${api.name} (ID: ${existing.id}, Path: ${api.path})`);
|
|
213
211
|
} else {
|
|
214
212
|
const id = await helper.insData({
|
|
215
213
|
table: 'core_api',
|
|
@@ -223,7 +221,6 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<{ created: number
|
|
|
223
221
|
}
|
|
224
222
|
});
|
|
225
223
|
stats.created++;
|
|
226
|
-
Logger.info(` └ 新增接口: ${api.name} (ID: ${id}, Path: ${api.path})`);
|
|
227
224
|
}
|
|
228
225
|
} catch (error: any) {
|
|
229
226
|
Logger.error(`同步接口 "${api.name}" 失败:`, error);
|
|
@@ -237,8 +234,6 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<{ created: number
|
|
|
237
234
|
* 删除配置中不存在的记录
|
|
238
235
|
*/
|
|
239
236
|
async function deleteObsoleteRecords(helper: any, apiPaths: Set<string>): Promise<number> {
|
|
240
|
-
Logger.info(`\n=== 删除配置中不存在的记录 ===`);
|
|
241
|
-
|
|
242
237
|
const allRecords = await helper.getAll({
|
|
243
238
|
table: 'core_api',
|
|
244
239
|
fields: ['id', 'path', 'name'],
|
|
@@ -253,34 +248,22 @@ async function deleteObsoleteRecords(helper: any, apiPaths: Set<string>): Promis
|
|
|
253
248
|
where: { id: record.id }
|
|
254
249
|
});
|
|
255
250
|
deletedCount++;
|
|
256
|
-
Logger.info(` └ 删除记录: ${record.name} (ID: ${record.id}, path: ${record.path})`);
|
|
257
251
|
}
|
|
258
252
|
}
|
|
259
253
|
|
|
260
|
-
if (deletedCount === 0) {
|
|
261
|
-
Logger.info(' ✅ 无需删除的记录');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
254
|
return deletedCount;
|
|
265
255
|
}
|
|
266
256
|
|
|
267
257
|
/**
|
|
268
258
|
* SyncApi 命令主函数
|
|
269
259
|
*/
|
|
270
|
-
export async function syncApiCommand(options: SyncApiOptions = {}) {
|
|
260
|
+
export async function syncApiCommand(options: SyncApiOptions = {}): Promise<SyncApiStats> {
|
|
271
261
|
try {
|
|
272
262
|
if (options.plan) {
|
|
273
263
|
Logger.info('[计划] 同步 API 接口到数据库(plan 模式不执行)');
|
|
274
|
-
|
|
275
|
-
Logger.info('[计划] 2. 提取每个 API 的配置信息');
|
|
276
|
-
Logger.info('[计划] 3. 根据 path 检查接口是否存在');
|
|
277
|
-
Logger.info('[计划] 4. 存在则更新,不存在则新增');
|
|
278
|
-
Logger.info('[计划] 5. 删除文件中不存在的接口记录');
|
|
279
|
-
return;
|
|
264
|
+
return { totalApis: 0, created: 0, updated: 0, deleted: 0 };
|
|
280
265
|
}
|
|
281
266
|
|
|
282
|
-
Logger.info('开始同步 API 接口到数据库...\n');
|
|
283
|
-
|
|
284
267
|
const projectRoot = process.cwd();
|
|
285
268
|
|
|
286
269
|
// 连接数据库(SQL + Redis)
|
|
@@ -289,7 +272,6 @@ export async function syncApiCommand(options: SyncApiOptions = {}) {
|
|
|
289
272
|
const helper = Database.getDbHelper();
|
|
290
273
|
|
|
291
274
|
// 1. 检查表是否存在
|
|
292
|
-
Logger.info('=== 检查数据表 ===');
|
|
293
275
|
const exists = await helper.tableExists('core_api');
|
|
294
276
|
|
|
295
277
|
if (!exists) {
|
|
@@ -297,30 +279,17 @@ export async function syncApiCommand(options: SyncApiOptions = {}) {
|
|
|
297
279
|
process.exit(1);
|
|
298
280
|
}
|
|
299
281
|
|
|
300
|
-
Logger.info(`✅ 表 core_api 存在\n`);
|
|
301
|
-
|
|
302
282
|
// 2. 扫描所有 API 文件
|
|
303
|
-
Logger.info('=== 步骤 2: 扫描 API 文件 ===');
|
|
304
283
|
const apis = await scanAllApis(projectRoot);
|
|
305
284
|
const apiPaths = new Set(apis.map((api) => api.path));
|
|
306
|
-
Logger.info(`\n✅ 共扫描到 ${apis.length} 个 API 接口\n`);
|
|
307
285
|
|
|
308
286
|
// 3. 同步 API 数据
|
|
309
|
-
Logger.info('=== 步骤 3: 同步 API 数据(新增/更新) ===');
|
|
310
287
|
const stats = await syncApis(helper, apis);
|
|
311
288
|
|
|
312
289
|
// 4. 删除文件中不存在的接口
|
|
313
290
|
const deletedCount = await deleteObsoleteRecords(helper, apiPaths);
|
|
314
291
|
|
|
315
|
-
// 5.
|
|
316
|
-
Logger.info(`\n=== 接口同步完成 ===`);
|
|
317
|
-
Logger.info(`新增接口: ${stats.created} 个`);
|
|
318
|
-
Logger.info(`更新接口: ${stats.updated} 个`);
|
|
319
|
-
Logger.info(`删除接口: ${deletedCount} 个`);
|
|
320
|
-
Logger.info(`当前总接口数: ${apis.length} 个`);
|
|
321
|
-
|
|
322
|
-
// 6. 缓存接口数据到 Redis
|
|
323
|
-
Logger.info('\n=== 步骤 4: 缓存接口数据到 Redis ===');
|
|
292
|
+
// 5. 缓存接口数据到 Redis
|
|
324
293
|
try {
|
|
325
294
|
const apiList = await helper.getAll({
|
|
326
295
|
table: 'core_api',
|
|
@@ -328,16 +297,17 @@ export async function syncApiCommand(options: SyncApiOptions = {}) {
|
|
|
328
297
|
orderBy: ['addonName#ASC', 'path#ASC']
|
|
329
298
|
});
|
|
330
299
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (result === null) {
|
|
334
|
-
Logger.warn('⚠️ 接口缓存失败');
|
|
335
|
-
} else {
|
|
336
|
-
Logger.info(`✅ 已缓存 ${apiList.length} 个接口到 Redis (Key: apis:all)`);
|
|
337
|
-
}
|
|
300
|
+
await RedisHelper.setObject('apis:all', apiList);
|
|
338
301
|
} catch (error: any) {
|
|
339
|
-
|
|
302
|
+
// 忽略缓存错误
|
|
340
303
|
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
totalApis: apis.length,
|
|
307
|
+
created: stats.created,
|
|
308
|
+
updated: stats.updated,
|
|
309
|
+
deleted: deletedCount
|
|
310
|
+
};
|
|
341
311
|
} catch (error: any) {
|
|
342
312
|
Logger.error('API 同步失败:', error);
|
|
343
313
|
process.exit(1);
|
package/commands/syncDb/index.ts
CHANGED
|
@@ -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<
|
|
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
|
-
|
|
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);
|
package/commands/syncDb.ts
CHANGED
|
@@ -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
|
-
|
|
27
|
+
// 执行同步并返回统计
|
|
28
|
+
const stats = await SyncDb();
|
|
29
|
+
return stats;
|
|
30
30
|
} catch (error: any) {
|
|
31
31
|
Logger.error('数据库同步失败:', error);
|
|
32
32
|
process.exit(1);
|
package/commands/syncDev.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.info(`查询到 ${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.info(`查询到 ${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
|
-
|
|
158
|
+
isNew = true;
|
|
167
159
|
}
|
|
168
160
|
|
|
169
161
|
// 缓存角色权限数据到 Redis
|
|
170
|
-
|
|
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 (
|
|
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) {
|
|
@@ -212,20 +201,33 @@ export async function syncDevCommand(options: SyncDevOptions = {}) {
|
|
|
212
201
|
// 先删除旧数据
|
|
213
202
|
await redis.del(redisKey);
|
|
214
203
|
|
|
215
|
-
// 批量添加到 Set
|
|
216
|
-
const result = await redis.sadd(redisKey, roleApiPaths);
|
|
204
|
+
// 批量添加到 Set(使用扩展运算符展开数组)
|
|
205
|
+
const result = await redis.sadd(redisKey, ...roleApiPaths);
|
|
217
206
|
|
|
218
207
|
if (result > 0) {
|
|
219
|
-
|
|
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
|
-
|
|
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);
|