mr-sliy 1.0.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.
Files changed (50) hide show
  1. package/.env.example +145 -0
  2. package/database/schema.sql +187 -0
  3. package/package.json +74 -0
  4. package/scripts/download-tree-sitter.js +171 -0
  5. package/scripts/postinstall.js +134 -0
  6. package/src/agent/agent.js +563 -0
  7. package/src/agent.js +87 -0
  8. package/src/cli/index.js +1643 -0
  9. package/src/config/index.js +232 -0
  10. package/src/engine/dualModeEngine.js +486 -0
  11. package/src/index.js +165 -0
  12. package/src/middlewares/errorHandler.js +166 -0
  13. package/src/middlewares/index.js +23 -0
  14. package/src/routes/aiRoutes.js +117 -0
  15. package/src/routes/configRoutes.js +31 -0
  16. package/src/routes/index.js +75 -0
  17. package/src/routes/issueRoutes.js +195 -0
  18. package/src/routes/projectRoutes.js +46 -0
  19. package/src/routes/reportRoutes.js +40 -0
  20. package/src/routes/scanRoutes.js +245 -0
  21. package/src/routes/userRoutes.js +47 -0
  22. package/src/services/ast/parser.js +503 -0
  23. package/src/services/detection/detector.js +934 -0
  24. package/src/services/llm/providers.js +1107 -0
  25. package/src/services/rag/agent.js +375 -0
  26. package/src/services/vector/knowledgeBase.js +863 -0
  27. package/src/skills/Skill.js +38 -0
  28. package/src/skills/code-analysis/index.js +272 -0
  29. package/src/skills/code-detection/index.js +166 -0
  30. package/src/skills/code-detection/rules/console-log.js +45 -0
  31. package/src/skills/code-detection/rules/deep-nesting.js +76 -0
  32. package/src/skills/code-detection/rules/duplicate-code.js +57 -0
  33. package/src/skills/code-detection/rules/high-complexity.js +109 -0
  34. package/src/skills/code-detection/rules/index.js +59 -0
  35. package/src/skills/code-detection/rules/long-functions.js +54 -0
  36. package/src/skills/code-detection/rules/magic-numbers.js +48 -0
  37. package/src/skills/code-detection/rules/missing-comment.js +64 -0
  38. package/src/skills/code-detection/rules/null-check.js +71 -0
  39. package/src/skills/code-detection/rules/unnecessary-else.js +46 -0
  40. package/src/skills/code-detection/rules/unused-functions.js +57 -0
  41. package/src/skills/code-detection/rules/unused-imports.js +57 -0
  42. package/src/skills/code-detection/rules/unused-variables.js +54 -0
  43. package/src/skills/code-optimization/index.js +319 -0
  44. package/src/skills/index.js +152 -0
  45. package/src/utils/crypto.js +212 -0
  46. package/src/utils/database.js +125 -0
  47. package/src/utils/helpers.js +226 -0
  48. package/src/utils/logger.js +202 -0
  49. package/src/utils/mysql.js +198 -0
  50. package/src/utils/response.js +124 -0
@@ -0,0 +1,863 @@
1
+ /**
2
+ * 本地向量知识库模块
3
+ * 基于SQLite的离线RAG知识库
4
+ * 存储代码优化案例、最佳实践、编码规范等知识
5
+ */
6
+
7
+ const { getDatabase } = require('../../utils/database');
8
+ const { logger } = require('../../utils/logger');
9
+ const { generateUUID } = require('../../utils/helpers');
10
+
11
+ /**
12
+ * 简化的文本向量化(基于TF-IDF思想)
13
+ * 实际项目中可替换为Embedding模型(如sentence-transformers)
14
+ */
15
+ class SimpleEmbedding {
16
+ constructor() {
17
+ this.vocabulary = new Set();
18
+ }
19
+
20
+ /**
21
+ * 分词
22
+ */
23
+ tokenize(text) {
24
+ return text.toLowerCase()
25
+ .replace(/[^a-z0-9_\s]/g, ' ')
26
+ .split(/\s+/)
27
+ .filter(t => t.length > 2);
28
+ }
29
+
30
+ /**
31
+ * 计算文本向量
32
+ */
33
+ embed(text) {
34
+ const tokens = this.tokenize(text);
35
+ const vector = {};
36
+ const tf = {};
37
+
38
+ // 计算词频
39
+ tokens.forEach(token => {
40
+ tf[token] = (tf[token] || 0) + 1;
41
+ });
42
+
43
+ // 归一化
44
+ const totalTokens = tokens.length;
45
+ Object.keys(tf).forEach(token => {
46
+ vector[token] = tf[token] / totalTokens;
47
+ });
48
+
49
+ return vector;
50
+ }
51
+
52
+ /**
53
+ * 计算余弦相似度
54
+ */
55
+ cosineSimilarity(vec1, vec2) {
56
+ const keys1 = Object.keys(vec1);
57
+ const keys2 = Object.keys(vec2);
58
+ const allKeys = new Set([...keys1, ...keys2]);
59
+
60
+ let dotProduct = 0;
61
+ let norm1 = 0;
62
+ let norm2 = 0;
63
+
64
+ allKeys.forEach(key => {
65
+ const v1 = vec1[key] || 0;
66
+ const v2 = vec2[key] || 0;
67
+ dotProduct += v1 * v2;
68
+ norm1 += v1 * v1;
69
+ norm2 += v2 * v2;
70
+ });
71
+
72
+ if (norm1 === 0 || norm2 === 0) return 0;
73
+ return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
74
+ }
75
+ }
76
+
77
+ const embedder = new SimpleEmbedding();
78
+
79
+ /**
80
+ * 本地知识库管理器
81
+ */
82
+ class LocalKnowledgeBase {
83
+ constructor() {
84
+ this.initialized = false;
85
+ }
86
+
87
+ /**
88
+ * 初始化知识库表
89
+ */
90
+ init() {
91
+ if (this.initialized) return;
92
+
93
+ const db = getDatabase();
94
+
95
+ // 知识条目表
96
+ db.exec(`
97
+ CREATE TABLE IF NOT EXISTS kb_entries (
98
+ id TEXT PRIMARY KEY,
99
+ content TEXT NOT NULL,
100
+ content_type TEXT NOT NULL,
101
+ language TEXT,
102
+ tags TEXT,
103
+ source TEXT,
104
+ vector_json TEXT,
105
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
106
+ )
107
+ `);
108
+
109
+ // 知识库元数据表
110
+ db.exec(`
111
+ CREATE TABLE IF NOT EXISTS kb_metadata (
112
+ key TEXT PRIMARY KEY,
113
+ value TEXT,
114
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
115
+ )
116
+ `);
117
+
118
+ // 优化案例表
119
+ db.exec(`
120
+ CREATE TABLE IF NOT EXISTS kb_cases (
121
+ id TEXT PRIMARY KEY,
122
+ original_code TEXT NOT NULL,
123
+ optimized_code TEXT NOT NULL,
124
+ explanation TEXT,
125
+ language TEXT,
126
+ issue_type TEXT,
127
+ vector_json TEXT,
128
+ usage_count INTEGER DEFAULT 0,
129
+ rating REAL DEFAULT 0,
130
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
131
+ )
132
+ `);
133
+
134
+ // 创建索引
135
+ db.exec('CREATE INDEX IF NOT EXISTS idx_kb_entries_type ON kb_entries(content_type)');
136
+ db.exec('CREATE INDEX IF NOT EXISTS idx_kb_entries_lang ON kb_entries(language)');
137
+ db.exec('CREATE INDEX IF NOT EXISTS idx_kb_cases_type ON kb_cases(issue_type)');
138
+ db.exec('CREATE INDEX IF NOT EXISTS idx_kb_cases_lang ON kb_cases(language)');
139
+
140
+ this.initialized = true;
141
+ logger.debug('本地知识库初始化完成');
142
+ }
143
+
144
+ /**
145
+ * 添加知识条目
146
+ */
147
+ addEntry(content, options = {}) {
148
+ this.init();
149
+ const db = getDatabase();
150
+
151
+ const id = generateUUID();
152
+ const vector = embedder.embed(content);
153
+
154
+ const stmt = db.prepare(`
155
+ INSERT INTO kb_entries (id, content, content_type, language, tags, source, vector_json)
156
+ VALUES (?, ?, ?, ?, ?, ?, ?)
157
+ `);
158
+
159
+ stmt.run(
160
+ id,
161
+ content,
162
+ options.type || 'general',
163
+ options.language || null,
164
+ options.tags ? JSON.stringify(options.tags) : null,
165
+ options.source || null,
166
+ JSON.stringify(vector)
167
+ );
168
+
169
+ logger.debug(`添加知识条目: ${id}`);
170
+ return id;
171
+ }
172
+
173
+ /**
174
+ * 添加优化案例
175
+ */
176
+ addCase(originalCode, optimizedCode, explanation, options = {}) {
177
+ this.init();
178
+ const db = getDatabase();
179
+
180
+ const id = generateUUID();
181
+ const combinedText = `${originalCode} ${optimizedCode} ${explanation}`;
182
+ const vector = embedder.embed(combinedText);
183
+
184
+ const stmt = db.prepare(`
185
+ INSERT INTO kb_cases (id, original_code, optimized_code, explanation, language, issue_type, vector_json)
186
+ VALUES (?, ?, ?, ?, ?, ?, ?)
187
+ `);
188
+
189
+ stmt.run(
190
+ id,
191
+ originalCode,
192
+ optimizedCode,
193
+ explanation,
194
+ options.language || null,
195
+ options.issueType || null,
196
+ JSON.stringify(vector)
197
+ );
198
+
199
+ logger.debug(`添加优化案例: ${id}`);
200
+ return id;
201
+ }
202
+
203
+ /**
204
+ * 检索相似知识条目
205
+ */
206
+ searchEntries(query, options = {}) {
207
+ this.init();
208
+ const db = getDatabase();
209
+ const queryVector = embedder.embed(query);
210
+ const topK = options.topK || 5;
211
+ const type = options.type;
212
+ const language = options.language;
213
+
214
+ let sql = 'SELECT id, content, content_type, language, tags, source, vector_json FROM kb_entries';
215
+ const conditions = [];
216
+
217
+ if (type) conditions.push(`content_type = '${type}'`);
218
+ if (language) conditions.push(`language = '${language}'`);
219
+
220
+ if (conditions.length > 0) {
221
+ sql += ' WHERE ' + conditions.join(' AND ');
222
+ }
223
+
224
+ const stmt = db.prepare(sql);
225
+ const entries = stmt.all();
226
+
227
+ // 计算相似度并排序
228
+ const results = entries.map(entry => {
229
+ const entryVector = JSON.parse(entry.vector_json || '{}');
230
+ const similarity = embedder.cosineSimilarity(queryVector, entryVector);
231
+ return {
232
+ id: entry.id,
233
+ content: entry.content,
234
+ type: entry.content_type,
235
+ language: entry.language,
236
+ tags: entry.tags ? JSON.parse(entry.tags) : [],
237
+ source: entry.source,
238
+ similarity
239
+ };
240
+ });
241
+
242
+ results.sort((a, b) => b.similarity - a.similarity);
243
+ return results.slice(0, topK);
244
+ }
245
+
246
+ /**
247
+ * 检索相似优化案例
248
+ */
249
+ searchCases(query, options = {}) {
250
+ this.init();
251
+ const db = getDatabase();
252
+ const queryVector = embedder.embed(query);
253
+ const topK = options.topK || 3;
254
+ const language = options.language;
255
+ const issueType = options.issueType;
256
+
257
+ let sql = 'SELECT id, original_code, optimized_code, explanation, language, issue_type, vector_json, usage_count, rating FROM kb_cases';
258
+ const conditions = [];
259
+
260
+ if (language) conditions.push(`language = '${language}'`);
261
+ if (issueType) conditions.push(`issue_type = '${issueType}'`);
262
+
263
+ if (conditions.length > 0) {
264
+ sql += ' WHERE ' + conditions.join(' AND ');
265
+ }
266
+
267
+ const stmt = db.prepare(sql);
268
+ const cases = stmt.all();
269
+
270
+ const results = cases.map(c => {
271
+ const caseVector = JSON.parse(c.vector_json || '{}');
272
+ const similarity = embedder.cosineSimilarity(queryVector, caseVector);
273
+ return {
274
+ id: c.id,
275
+ originalCode: c.original_code,
276
+ optimizedCode: c.optimized_code,
277
+ explanation: c.explanation,
278
+ language: c.language,
279
+ issueType: c.issue_type,
280
+ usageCount: c.usage_count,
281
+ rating: c.rating,
282
+ similarity
283
+ };
284
+ });
285
+
286
+ results.sort((a, b) => b.similarity - a.similarity);
287
+ return results.slice(0, topK);
288
+ }
289
+
290
+ /**
291
+ * 根据代码片段检索相似案例(用于离线优化)
292
+ */
293
+ findSimilarCases(codeSnippet, options = {}) {
294
+ return this.searchCases(codeSnippet, options);
295
+ }
296
+
297
+ /**
298
+ * 获取知识库统计
299
+ */
300
+ getStats() {
301
+ this.init();
302
+ const db = getDatabase();
303
+
304
+ const entryCount = db.prepare('SELECT COUNT(*) as count FROM kb_entries').get().count;
305
+ const caseCount = db.prepare('SELECT COUNT(*) as count FROM kb_cases').get().count;
306
+
307
+ const typeStats = db.prepare(`
308
+ SELECT content_type, COUNT(*) as count FROM kb_entries GROUP BY content_type
309
+ `).all();
310
+
311
+ const languageStats = db.prepare(`
312
+ SELECT language, COUNT(*) as count FROM kb_entries WHERE language IS NOT NULL GROUP BY language
313
+ `).all();
314
+
315
+ return {
316
+ totalEntries: entryCount,
317
+ totalCases: caseCount,
318
+ typeStats,
319
+ languageStats
320
+ };
321
+ }
322
+
323
+ /**
324
+ * 导出知识库为JSON
325
+ */
326
+ exportToJSON(options = {}) {
327
+ this.init();
328
+ const db = getDatabase();
329
+
330
+ const entries = db.prepare('SELECT * FROM kb_entries').all();
331
+ const cases = db.prepare('SELECT * FROM kb_cases').all();
332
+
333
+ const exportData = {
334
+ version: '1.0',
335
+ exportedAt: new Date().toISOString(),
336
+ entries: entries.map(e => ({
337
+ id: e.id,
338
+ content: e.content,
339
+ content_type: e.content_type,
340
+ language: e.language,
341
+ tags: e.tags ? JSON.parse(e.tags) : [],
342
+ source: e.source,
343
+ created_at: e.created_at
344
+ })),
345
+ cases: cases.map(c => ({
346
+ id: c.id,
347
+ original_code: c.original_code,
348
+ optimized_code: c.optimized_code,
349
+ explanation: c.explanation,
350
+ language: c.language,
351
+ issue_type: c.issue_type,
352
+ usage_count: c.usage_count,
353
+ rating: c.rating,
354
+ created_at: c.created_at
355
+ })),
356
+ stats: {
357
+ entryCount: entries.length,
358
+ caseCount: cases.length
359
+ }
360
+ };
361
+
362
+ if (options.includeVectors) {
363
+ exportData.entries.forEach((e, i) => {
364
+ e.vector_json = entries[i].vector_json;
365
+ });
366
+ exportData.cases.forEach((c, i) => {
367
+ c.vector_json = cases[i].vector_json;
368
+ });
369
+ }
370
+
371
+ return exportData;
372
+ }
373
+
374
+ /**
375
+ * 从JSON导入知识库
376
+ */
377
+ importFromJSON(data, options = {}) {
378
+ this.init();
379
+ const db = getDatabase();
380
+ const { merge = true, skipExisting = true } = options;
381
+
382
+ if (!merge) {
383
+ db.prepare('DELETE FROM kb_entries').run();
384
+ db.prepare('DELETE FROM kb_cases').run();
385
+ }
386
+
387
+ let importedEntries = 0;
388
+ let importedCases = 0;
389
+ let skippedEntries = 0;
390
+ let skippedCases = 0;
391
+
392
+ const insertEntry = db.prepare(`
393
+ INSERT INTO kb_entries (id, content, content_type, language, tags, source, vector_json)
394
+ VALUES (?, ?, ?, ?, ?, ?, ?)
395
+ `);
396
+
397
+ const insertCase = db.prepare(`
398
+ INSERT INTO kb_cases (id, original_code, optimized_code, explanation, language, issue_type, vector_json, usage_count, rating)
399
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
400
+ `);
401
+
402
+ const checkEntryExists = db.prepare('SELECT id FROM kb_entries WHERE id = ?');
403
+ const checkCaseExists = db.prepare('SELECT id FROM kb_cases WHERE id = ?');
404
+
405
+ const tx = db.transaction(() => {
406
+ for (const entry of data.entries || []) {
407
+ if (skipExisting && checkEntryExists.get(entry.id)) {
408
+ skippedEntries++;
409
+ continue;
410
+ }
411
+
412
+ const vector = entry.vector_json || JSON.stringify(embedder.embed(entry.content));
413
+ insertEntry.run(
414
+ entry.id,
415
+ entry.content,
416
+ entry.content_type || 'general',
417
+ entry.language || null,
418
+ entry.tags ? JSON.stringify(entry.tags) : null,
419
+ entry.source || 'imported',
420
+ vector
421
+ );
422
+ importedEntries++;
423
+ }
424
+
425
+ for (const caseItem of data.cases || []) {
426
+ if (skipExisting && checkCaseExists.get(caseItem.id)) {
427
+ skippedCases++;
428
+ continue;
429
+ }
430
+
431
+ const combinedText = `${caseItem.original_code || ''} ${caseItem.optimized_code || ''} ${caseItem.explanation || ''}`;
432
+ const vector = caseItem.vector_json || JSON.stringify(embedder.embed(combinedText));
433
+ insertCase.run(
434
+ caseItem.id,
435
+ caseItem.original_code,
436
+ caseItem.optimized_code,
437
+ caseItem.explanation || null,
438
+ caseItem.language || null,
439
+ caseItem.issue_type || null,
440
+ vector,
441
+ caseItem.usage_count || 0,
442
+ caseItem.rating || 0
443
+ );
444
+ importedCases++;
445
+ }
446
+ });
447
+
448
+ tx();
449
+
450
+ return {
451
+ importedEntries,
452
+ importedCases,
453
+ skippedEntries,
454
+ skippedCases,
455
+ totalEntries: importedEntries + skippedEntries,
456
+ totalCases: importedCases + skippedCases
457
+ };
458
+ }
459
+
460
+ /**
461
+ * 导出知识库到文件
462
+ */
463
+ exportToFile(filePath, options = {}) {
464
+ const fs = require('fs');
465
+ const path = require('path');
466
+
467
+ const exportData = this.exportToJSON(options);
468
+ const dir = path.dirname(filePath);
469
+
470
+ if (!fs.existsSync(dir)) {
471
+ fs.mkdirSync(dir, { recursive: true });
472
+ }
473
+
474
+ fs.writeFileSync(filePath, JSON.stringify(exportData, null, 2), 'utf8');
475
+ return exportData.stats;
476
+ }
477
+
478
+ /**
479
+ * 从文件导入知识库
480
+ */
481
+ importFromFile(filePath, options = {}) {
482
+ const fs = require('fs');
483
+
484
+ if (!fs.existsSync(filePath)) {
485
+ throw new Error(`文件不存在: ${filePath}`);
486
+ }
487
+
488
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
489
+ return this.importFromJSON(data, options);
490
+ }
491
+
492
+ /**
493
+ * 同步到云端MySQL
494
+ */
495
+ async syncToCloud() {
496
+ let mysql;
497
+ try {
498
+ mysql = require('../../utils/mysql');
499
+ } catch (e) {
500
+ return { success: false, message: 'MySQL模块未找到' };
501
+ }
502
+
503
+ if (!mysql.isEnabled()) {
504
+ return { success: false, message: 'MySQL未启用' };
505
+ }
506
+
507
+ try {
508
+ await mysql.initDatabase();
509
+ const data = this.exportToJSON({ includeVectors: false });
510
+ let syncedEntries = 0;
511
+ let syncedCases = 0;
512
+
513
+ for (const entry of data.entries) {
514
+ const existing = await mysql.query(
515
+ 'SELECT id FROM knowledge_entries WHERE id = ?',
516
+ [entry.id]
517
+ );
518
+
519
+ if (existing.length > 0) {
520
+ await mysql.execute(
521
+ `UPDATE knowledge_entries SET title = ?, category = ?, content = ?, tags = ?, language = ?, source = ? WHERE id = ?`,
522
+ [
523
+ entry.content.substring(0, 100),
524
+ entry.content_type,
525
+ entry.content,
526
+ entry.tags ? JSON.stringify(entry.tags) : null,
527
+ entry.language,
528
+ entry.source,
529
+ entry.id
530
+ ]
531
+ );
532
+ } else {
533
+ await mysql.execute(
534
+ `INSERT INTO knowledge_entries (id, title, category, content, tags, language, source) VALUES (?, ?, ?, ?, ?, ?, ?)`,
535
+ [
536
+ entry.id,
537
+ entry.content.substring(0, 100),
538
+ entry.content_type,
539
+ entry.content,
540
+ entry.tags ? JSON.stringify(entry.tags) : null,
541
+ entry.language,
542
+ entry.source
543
+ ]
544
+ );
545
+ }
546
+ syncedEntries++;
547
+ }
548
+
549
+ for (const caseItem of data.cases) {
550
+ const existing = await mysql.query(
551
+ 'SELECT id FROM optimization_cases WHERE id = ?',
552
+ [caseItem.id]
553
+ );
554
+
555
+ if (existing.length > 0) {
556
+ await mysql.execute(
557
+ `UPDATE optimization_cases SET title = ?, category = ?, before_code = ?, after_code = ?, description = ?, language = ?, tags = ? WHERE id = ?`,
558
+ [
559
+ caseItem.explanation ? caseItem.explanation.substring(0, 100) : caseItem.id,
560
+ caseItem.issue_type || 'general',
561
+ caseItem.original_code,
562
+ caseItem.optimized_code,
563
+ caseItem.explanation,
564
+ caseItem.language,
565
+ caseItem.usage_count,
566
+ caseItem.id
567
+ ]
568
+ );
569
+ } else {
570
+ await mysql.execute(
571
+ `INSERT INTO optimization_cases (id, title, category, before_code, after_code, description, language, complexity, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
572
+ [
573
+ caseItem.id,
574
+ caseItem.explanation ? caseItem.explanation.substring(0, 100) : caseItem.id,
575
+ caseItem.issue_type || 'general',
576
+ caseItem.original_code,
577
+ caseItem.optimized_code,
578
+ caseItem.explanation,
579
+ caseItem.language,
580
+ 'medium',
581
+ null
582
+ ]
583
+ );
584
+ }
585
+ syncedCases++;
586
+ }
587
+
588
+ const os = require('os');
589
+ const crypto = require('crypto');
590
+ const machineId = crypto.createHash('md5')
591
+ .update(`${os.hostname()}-${os.userInfo().username}-${os.platform()}`)
592
+ .digest('hex')
593
+ .substring(0, 8);
594
+
595
+ await mysql.execute(
596
+ `INSERT INTO sync_metadata (table_name, last_sync_at, record_count, machine_id) VALUES (?, NOW(), ?, ?) ON DUPLICATE KEY UPDATE last_sync_at = NOW(), record_count = ?`,
597
+ ['kb_entries', syncedEntries, machineId, syncedEntries]
598
+ );
599
+
600
+ await mysql.execute(
601
+ `INSERT INTO sync_metadata (table_name, last_sync_at, record_count, machine_id) VALUES (?, NOW(), ?, ?) ON DUPLICATE KEY UPDATE last_sync_at = NOW(), record_count = ?`,
602
+ ['kb_cases', syncedCases, machineId, syncedCases]
603
+ );
604
+
605
+ return {
606
+ success: true,
607
+ syncedEntries,
608
+ syncedCases,
609
+ message: `同步成功: ${syncedEntries} 条知识, ${syncedCases} 个案例`
610
+ };
611
+ } catch (error) {
612
+ return { success: false, message: error.message };
613
+ }
614
+ }
615
+
616
+ /**
617
+ * 从云端MySQL同步到本地
618
+ */
619
+ async syncFromCloud() {
620
+ let mysql;
621
+ try {
622
+ mysql = require('../../utils/mysql');
623
+ } catch (e) {
624
+ return { success: false, message: 'MySQL模块未找到' };
625
+ }
626
+
627
+ if (!mysql.isEnabled()) {
628
+ return { success: false, message: 'MySQL未启用' };
629
+ }
630
+
631
+ try {
632
+ await mysql.initDatabase();
633
+
634
+ const entries = await mysql.query('SELECT * FROM knowledge_entries');
635
+ const cases = await mysql.query('SELECT * FROM optimization_cases');
636
+
637
+ const importData = {
638
+ version: '1.0',
639
+ exportedAt: new Date().toISOString(),
640
+ entries: entries.map(e => ({
641
+ id: e.id,
642
+ content: e.content,
643
+ content_type: e.category,
644
+ language: e.language,
645
+ tags: e.tags ? JSON.parse(e.tags) : [],
646
+ source: e.source || 'cloud'
647
+ })),
648
+ cases: cases.map(c => ({
649
+ id: c.id,
650
+ original_code: c.before_code,
651
+ optimized_code: c.after_code,
652
+ explanation: c.description,
653
+ language: c.language,
654
+ issue_type: c.category,
655
+ usage_count: 0,
656
+ rating: 0
657
+ }))
658
+ };
659
+
660
+ const result = this.importFromJSON(importData, { merge: true, skipExisting: false });
661
+
662
+ return {
663
+ success: true,
664
+ ...result,
665
+ message: `从云端同步完成: ${result.importedEntries} 条知识, ${result.importedCases} 个案例`
666
+ };
667
+ } catch (error) {
668
+ return { success: false, message: error.message };
669
+ }
670
+ }
671
+
672
+ /**
673
+ * 测试云端连接
674
+ */
675
+ async testCloudConnection() {
676
+ let mysql;
677
+ try {
678
+ mysql = require('../../utils/mysql');
679
+ } catch (e) {
680
+ return { success: false, message: 'MySQL模块未找到' };
681
+ }
682
+
683
+ return await mysql.testConnection();
684
+ }
685
+
686
+ /**
687
+ * 初始化默认知识库(编码规范、最佳实践、设计模式、优化案例)
688
+ */
689
+ seedDefaultKnowledge() {
690
+ this.init();
691
+
692
+ const defaultEntries = [
693
+ { content: '避免使用魔法数字,应将其提取为命名常量。例如:const MAX_RETRY_COUNT = 3;', type: 'best_practice', language: 'javascript', tags: ['magic_number', 'constants'] },
694
+ { content: '函数应该保持单一职责,长度不超过50行。如果函数过长,应拆分为多个小函数。', type: 'best_practice', language: 'general', tags: ['function', 'single_responsibility'] },
695
+ { content: '删除未使用的变量和导入,减少代码冗余和打包体积。', type: 'best_practice', language: 'general', tags: ['unused', 'cleanup'] },
696
+ { content: '使用const声明不会重新赋值的变量,使用let声明会重新赋值的变量,避免使用var。', type: 'best_practice', language: 'javascript', tags: ['variable', 'const', 'let'] },
697
+ { content: '异步操作应使用async/await而非回调函数,提高代码可读性。', type: 'best_practice', language: 'javascript', tags: ['async', 'await', 'promise'] },
698
+ { content: 'Python中应使用列表推导式替代简单的for循环,提高代码简洁性。', type: 'best_practice', language: 'python', tags: ['list_comprehension', 'pythonic'] },
699
+ { content: '错误处理应使用try/catch块,并提供有意义的错误信息。', type: 'best_practice', language: 'general', tags: ['error_handling', 'try_catch'] },
700
+ { content: '圈复杂度应保持在10以下,过高的复杂度会增加维护成本和Bug风险。', type: 'best_practice', language: 'general', tags: ['complexity', 'cyclomatic'] },
701
+ { content: '避免深层嵌套(超过4层),使用提前返回(early return)和卫语句来扁平化代码。', type: 'best_practice', language: 'general', tags: ['nesting', 'early_return', 'guard_clause'] },
702
+ { content: '在return语句后不需要使用else,可以直接返回以减少嵌套层级。', type: 'best_practice', language: 'general', tags: ['else', 'unnecessary', 'code_style'] },
703
+ { content: '使用对象解构和数组解构来简化代码,提高可读性。', type: 'best_practice', language: 'javascript', tags: ['destructuring', 'es6'] },
704
+ { content: '使用模板字符串代替字符串拼接,使代码更清晰易读。', type: 'best_practice', language: 'javascript', tags: ['template_string', 'es6', 'string'] },
705
+ { content: '避免使用全局变量,使用模块化和闭包来封装状态。', type: 'best_practice', language: 'general', tags: ['global', 'module', 'closure'] },
706
+ { content: '函数参数应保持在3个以内,过多参数可使用对象参数代替。', type: 'best_practice', language: 'general', tags: ['function', 'parameters', 'api_design'] },
707
+ { content: '使用有意义的变量名和函数名,代码应自文档化。', type: 'best_practice', language: 'general', tags: ['naming', 'readability'] },
708
+ { content: '避免重复代码(DRY原则),将重复逻辑提取为函数或模块。', type: 'best_practice', language: 'general', tags: ['dry', 'duplicate', 'refactoring'] },
709
+ { content: '优先使用纯函数,减少副作用,使代码更易于测试和推理。', type: 'best_practice', language: 'general', tags: ['pure_function', 'functional', 'side_effect'] },
710
+ { content: '使用默认参数值代替条件判断,简化函数逻辑。', type: 'best_practice', language: 'javascript', tags: ['default_parameter', 'es6', 'function'] },
711
+ { content: '使用扩展运算符(spread)来复制数组和对象,避免直接修改原数据。', type: 'best_practice', language: 'javascript', tags: ['spread', 'immutable', 'es6'] },
712
+ { content: '使用Map和Set替代普通对象,提供更好的性能和更丰富的API。', type: 'best_practice', language: 'javascript', tags: ['map', 'set', 'data_structure'] },
713
+ { content: 'JavaScript中应严格检查null和undefined,避免运行时错误。', type: 'best_practice', language: 'javascript', tags: ['null', 'undefined', 'safety'] },
714
+ { content: '使用可选链操作符(?.)和空值合并操作符(??)安全访问嵌套属性。', type: 'best_practice', language: 'javascript', tags: ['optional_chaining', 'nullish', 'es2020'] },
715
+ { content: '代码应包含适当的注释,解释为什么这样做而不是做了什么。', type: 'best_practice', language: 'general', tags: ['comment', 'documentation'] },
716
+ { content: '生产代码中应移除console.log等调试语句,使用正式的日志系统。', type: 'best_practice', language: 'javascript', tags: ['console', 'debug', 'logging'] },
717
+ { content: '单例模式确保一个类只有一个实例,并提供全局访问点。', type: 'design_pattern', language: 'general', tags: ['singleton', 'creational'] },
718
+ { content: '工厂模式通过工厂方法创建对象,而不直接使用new操作符。', type: 'design_pattern', language: 'general', tags: ['factory', 'creational'] },
719
+ { content: '观察者模式定义对象间一对多的依赖关系,当一个对象状态改变时所有依赖者都会被通知。', type: 'design_pattern', language: 'general', tags: ['observer', 'behavioral'] },
720
+ { content: '策略模式定义一系列算法,把它们封装起来并可以相互替换。', type: 'design_pattern', language: 'general', tags: ['strategy', 'behavioral'] },
721
+ { content: '装饰器模式动态地给一个对象添加额外的职责,比继承更灵活。', type: 'design_pattern', language: 'general', tags: ['decorator', 'structural'] },
722
+ { content: '适配器模式将一个类的接口转换成客户希望的另一个接口。', type: 'design_pattern', language: 'general', tags: ['adapter', 'structural'] },
723
+ { content: 'Promise.all用于并行执行多个异步操作,提高性能。', type: 'pattern', language: 'javascript', tags: ['promise', 'parallel', 'async'] },
724
+ { content: '使用防抖(debounce)和节流(throttle)优化频繁触发的事件处理。', type: 'pattern', language: 'javascript', tags: ['debounce', 'throttle', 'performance'] },
725
+ { content: '使用记忆化(memoization)缓存昂贵函数的计算结果。', type: 'pattern', language: 'general', tags: ['memoization', 'performance', 'cache'] },
726
+ { content: '惰性求值(Lazy evaluation)延迟计算直到真正需要结果时才执行。', type: 'pattern', language: 'general', tags: ['lazy', 'performance'] },
727
+ { content: '使用错误边界(Error Boundary)优雅地处理React组件中的错误。', type: 'pattern', language: 'javascript', tags: ['error_boundary', 'react'] },
728
+ { content: '中间件模式(Middleware)用于处理请求/响应管道中的横切关注点。', type: 'pattern', language: 'general', tags: ['middleware', 'express'] },
729
+ { content: '批量操作数据库查询,减少数据库访问次数以提高性能。', type: 'performance', language: 'general', tags: ['database', 'batch', 'performance'] },
730
+ { content: '使用索引优化数据库查询速度,避免全表扫描。', type: 'performance', language: 'general', tags: ['database', 'index', 'performance'] },
731
+ { content: '避免在循环中进行DOM操作,应批量修改后一次性更新。', type: 'performance', language: 'javascript', tags: ['dom', 'performance', 'reflow'] },
732
+ { content: '使用事件委托减少事件监听器数量,提高性能并简化代码。', type: 'performance', language: 'javascript', tags: ['event_delegation', 'performance'] },
733
+ { content: '合理使用缓存(内存缓存、Redis、HTTP缓存)减少重复计算和网络请求。', type: 'performance', language: 'general', tags: ['cache', 'performance'] },
734
+ { content: '代码审查应关注:命名清晰度、复杂度、错误处理、边界条件、安全性。', type: 'code_review', language: 'general', tags: ['review', 'quality'] },
735
+ { content: '测试应覆盖正常路径、边界条件和错误场景,确保代码的健壮性。', type: 'testing', language: 'general', tags: ['testing', 'quality'] },
736
+ { content: '使用版本控制(Git)管理代码,每次提交应有清晰的提交信息。', type: 'version_control', language: 'general', tags: ['git', 'best_practice'] },
737
+ { content: '安全编码原则:永远不要信任用户输入,始终进行验证和转义。', type: 'security', language: 'general', tags: ['security', 'input_validation'] },
738
+ { content: '防止SQL注入:使用参数化查询或ORM,永远不要拼接SQL字符串。', type: 'security', language: 'general', tags: ['security', 'sql_injection'] },
739
+ { content: '防止XSS攻击:对用户输入进行HTML转义,使用CSP策略。', type: 'security', language: 'javascript', tags: ['security', 'xss'] },
740
+ { content: 'Python中使用with语句管理资源,确保文件、连接等被正确关闭。', type: 'best_practice', language: 'python', tags: ['context_manager', 'with', 'resource'] },
741
+ { content: 'Python中使用生成器(generator)处理大数据集,节省内存。', type: 'best_practice', language: 'python', tags: ['generator', 'memory', 'performance'] },
742
+ { content: 'Java中使用try-with-resources自动关闭资源。', type: 'best_practice', language: 'java', tags: ['try_with_resources', 'resource', 'java'] },
743
+ { content: 'Go中使用defer语句确保资源释放和清理操作的执行。', type: 'best_practice', language: 'go', tags: ['defer', 'resource', 'go'] }
744
+ ];
745
+
746
+ defaultEntries.forEach(entry => {
747
+ this.addEntry(entry.content, {
748
+ type: entry.type,
749
+ language: entry.language,
750
+ tags: entry.tags,
751
+ source: 'default'
752
+ });
753
+ });
754
+
755
+ const defaultCases = [
756
+ {
757
+ original: 'for (let i = 0; i < arr.length; i++) { result.push(arr[i] * 2); }',
758
+ optimized: 'const result = arr.map(item => item * 2);',
759
+ explanation: '使用Array.map替代for循环,更简洁且表达力更强',
760
+ language: 'javascript',
761
+ issueType: 'loop_optimization'
762
+ },
763
+ {
764
+ original: 'if (user !== null && user !== undefined && user.name) { ... }',
765
+ optimized: 'if (user?.name) { ... }',
766
+ explanation: '使用可选链操作符简化嵌套属性的空值检查',
767
+ language: 'javascript',
768
+ issueType: 'null_check'
769
+ },
770
+ {
771
+ original: 'const name = user.name ? user.name : "default";',
772
+ optimized: 'const name = user.name ?? "default";',
773
+ explanation: '使用空值合并操作符替代三元运算符,更简洁',
774
+ language: 'javascript',
775
+ issueType: 'code_style'
776
+ },
777
+ {
778
+ original: 'function getFullName(user) { return user.firstName + " " + user.lastName; }',
779
+ optimized: 'const getFullName = ({ firstName, lastName }) => `${firstName} ${lastName}`;',
780
+ explanation: '使用解构和模板字符串简化函数,提高可读性',
781
+ language: 'javascript',
782
+ issueType: 'code_style'
783
+ },
784
+ {
785
+ original: 'let items = []; for (let i = 0; i < data.length; i++) { if (data[i].active) { items.push(data[i]); } }',
786
+ optimized: 'const items = data.filter(item => item.active);',
787
+ explanation: '使用Array.filter替代for循环+条件判断,更函数式',
788
+ language: 'javascript',
789
+ issueType: 'loop_optimization'
790
+ },
791
+ {
792
+ original: 'if (err) { callback(err); } else { callback(null, result); }',
793
+ optimized: 'callback(err, result);',
794
+ explanation: '直接传递参数,移除不必要的if/else',
795
+ language: 'javascript',
796
+ issueType: 'unnecessary_else'
797
+ },
798
+ {
799
+ original: 'function calculate(a, b, c, d, e) { ... }',
800
+ optimized: 'function calculate({ a, b, c, d, e }) { ... }',
801
+ explanation: '使用对象参数替代多个参数,提高可读性和扩展性',
802
+ language: 'javascript',
803
+ issueType: 'function_design'
804
+ },
805
+ {
806
+ original: 'const copy = Object.assign({}, obj);',
807
+ optimized: 'const copy = { ...obj };',
808
+ explanation: '使用扩展运算符替代Object.assign,更简洁',
809
+ language: 'javascript',
810
+ issueType: 'code_style'
811
+ },
812
+ {
813
+ original: 'squares = []\nfor x in range(10):\n squares.append(x**2)',
814
+ optimized: 'squares = [x**2 for x in range(10)]',
815
+ explanation: '使用列表推导式替代for循环+append,更Pythonic',
816
+ language: 'python',
817
+ issueType: 'loop_optimization'
818
+ },
819
+ {
820
+ original: 'if x > 0:\n result = "positive"\nelse:\n result = "negative"',
821
+ optimized: 'result = "positive" if x > 0 else "negative"',
822
+ explanation: '使用三元表达式简化简单的if/else赋值',
823
+ language: 'python',
824
+ issueType: 'code_style'
825
+ }
826
+ ];
827
+
828
+ defaultCases.forEach(c => {
829
+ this.addCase(c.original, c.optimized, c.explanation, {
830
+ language: c.language,
831
+ issueType: c.issueType
832
+ });
833
+ });
834
+
835
+ logger.debug(`默认知识库初始化完成 (${defaultEntries.length}条知识, ${defaultCases.length}个案例)`);
836
+ }
837
+
838
+ /**
839
+ * 更新案例使用次数和评分
840
+ */
841
+ updateCaseUsage(caseId, rating) {
842
+ this.init();
843
+ const db = getDatabase();
844
+
845
+ const stmt = db.prepare(`
846
+ UPDATE kb_cases
847
+ SET usage_count = usage_count + 1,
848
+ rating = (rating * usage_count + ?) / (usage_count + 1)
849
+ WHERE id = ?
850
+ `);
851
+
852
+ stmt.run(rating || 5, caseId);
853
+ }
854
+ }
855
+
856
+ // 单例实例
857
+ const knowledgeBase = new LocalKnowledgeBase();
858
+
859
+ module.exports = {
860
+ LocalKnowledgeBase,
861
+ knowledgeBase,
862
+ SimpleEmbedding
863
+ };