intention-coding 0.6.4 → 0.6.6

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/dist/index.cjs CHANGED
@@ -11116,6 +11116,1836 @@ ${requirement_description}
11116
11116
  function getTechSpecOutputDir(projectPath) {
11117
11117
  return `${projectPath}/${TECH_SPEC_OUTPUT_DIR}`;
11118
11118
  }
11119
+ const FILE_TYPE_PATTERNS = {
11120
+ controller: [
11121
+ /Controller\.(ts|js|java)$/i,
11122
+ /controller\.(ts|js|java)$/i,
11123
+ /@Controller/,
11124
+ /@RestController/
11125
+ ],
11126
+ service: [
11127
+ /Service\.(ts|js|java)$/i,
11128
+ /service\.(ts|js|java)$/i,
11129
+ /@Service/,
11130
+ /ServiceImpl\.(ts|js|java)$/i
11131
+ ],
11132
+ entity: [
11133
+ /Entity\.(ts|js|java)$/i,
11134
+ /entity\.(ts|js|java)$/i,
11135
+ /Model\.(ts|js|java)$/i,
11136
+ /model\.(ts|js|java)$/i,
11137
+ /@Entity/,
11138
+ /@Table/
11139
+ ],
11140
+ dto: [
11141
+ /DTO\.(ts|js|java)$/i,
11142
+ /dto\.(ts|js|java)$/i,
11143
+ /Vo\.(ts|js|java)$/i,
11144
+ /vo\.(ts|js|java)$/i,
11145
+ /Bo\.(ts|js|java)$/i,
11146
+ /bo\.(ts|js|java)$/i,
11147
+ /Request\.(ts|js|java)$/i,
11148
+ /Response\.(ts|js|java)$/i,
11149
+ /Query\.(ts|js|java)$/i,
11150
+ /Form\.(ts|js|java)$/i
11151
+ ],
11152
+ config: [
11153
+ /Config\.(ts|js|java)$/i,
11154
+ /config\.(ts|js|java)$/i,
11155
+ /Configuration\.(ts|js|java)$/i,
11156
+ /@Configuration/
11157
+ ]
11158
+ };
11159
+ const IGNORE_PATTERNS = [
11160
+ "node_modules",
11161
+ ".git",
11162
+ ".svn",
11163
+ "target",
11164
+ "build",
11165
+ "dist",
11166
+ ".idea",
11167
+ ".vscode",
11168
+ "logs",
11169
+ "temp",
11170
+ "tmp",
11171
+ ".DS_Store",
11172
+ "Thumbs.db",
11173
+ "*.log",
11174
+ "*.tmp",
11175
+ "*.bak"
11176
+ ];
11177
+ function shouldIgnore(filePath) {
11178
+ const fileName = external_path_namespaceObject.basename(filePath);
11179
+ const dirName = external_path_namespaceObject.dirname(filePath);
11180
+ return IGNORE_PATTERNS.some((pattern)=>{
11181
+ if (pattern.includes("*")) {
11182
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
11183
+ return regex.test(fileName);
11184
+ }
11185
+ return fileName === pattern || dirName.includes(pattern) || filePath.includes(pattern);
11186
+ });
11187
+ }
11188
+ async function identifyFileType(filePath) {
11189
+ const fileName = external_path_namespaceObject.basename(filePath);
11190
+ for (const [type, patterns] of Object.entries(FILE_TYPE_PATTERNS))for (const pattern of patterns)if (pattern instanceof RegExp) {
11191
+ if (pattern.test(fileName)) return type;
11192
+ }
11193
+ try {
11194
+ const stats = await promises_namespaceObject.stat(filePath);
11195
+ if (stats.size < 51200) {
11196
+ const content = await promises_namespaceObject.readFile(filePath, "utf-8");
11197
+ for (const [type, patterns] of Object.entries(FILE_TYPE_PATTERNS))for (const pattern of patterns)if (pattern instanceof RegExp && pattern.test(content)) return type;
11198
+ }
11199
+ } catch (error) {}
11200
+ return "other";
11201
+ }
11202
+ async function scanDirectory(dirPath, projectPath, currentModule) {
11203
+ const files = [];
11204
+ try {
11205
+ const entries = await promises_namespaceObject.readdir(dirPath, {
11206
+ withFileTypes: true
11207
+ });
11208
+ for (const entry of entries){
11209
+ const fullPath = external_path_namespaceObject.join(dirPath, entry.name);
11210
+ const relativePath = external_path_namespaceObject.relative(projectPath, fullPath);
11211
+ if (!shouldIgnore(fullPath)) {
11212
+ if (entry.isDirectory()) {
11213
+ const subFiles = await scanDirectory(fullPath, projectPath, currentModule);
11214
+ files.push(...subFiles);
11215
+ } else if (entry.isFile()) {
11216
+ const ext = external_path_namespaceObject.extname(entry.name).toLowerCase();
11217
+ if ([
11218
+ ".ts",
11219
+ ".js",
11220
+ ".java",
11221
+ ".kt",
11222
+ ".py",
11223
+ ".go",
11224
+ ".cs"
11225
+ ].includes(ext)) {
11226
+ const fileType = await identifyFileType(fullPath);
11227
+ files.push({
11228
+ path: fullPath,
11229
+ name: entry.name,
11230
+ type: fileType,
11231
+ relativePath,
11232
+ module: currentModule
11233
+ });
11234
+ }
11235
+ }
11236
+ }
11237
+ }
11238
+ } catch (error) {
11239
+ logger.warn(`\u{626B}\u{63CF}\u{76EE}\u{5F55}\u{5931}\u{8D25}: ${dirPath}`, {
11240
+ error
11241
+ });
11242
+ }
11243
+ return files;
11244
+ }
11245
+ function identifyModules(files, projectPath) {
11246
+ const moduleMap = new Map();
11247
+ for (const file of files){
11248
+ const pathParts = file.relativePath.split(external_path_namespaceObject.sep);
11249
+ let moduleName = "default";
11250
+ if (pathParts.includes("src") && pathParts.includes("main") && pathParts.includes("java")) {
11251
+ const javaIndex = pathParts.indexOf("java");
11252
+ if (javaIndex + 3 < pathParts.length) moduleName = pathParts[javaIndex + 3];
11253
+ } else if (pathParts.includes("modules") || pathParts.includes("packages")) {
11254
+ const moduleIndex = pathParts.findIndex((p)=>"modules" === p || "packages" === p);
11255
+ if (moduleIndex + 1 < pathParts.length) moduleName = pathParts[moduleIndex + 1];
11256
+ } else if (pathParts.length > 1) moduleName = pathParts[0];
11257
+ if (!moduleMap.has(moduleName)) moduleMap.set(moduleName, []);
11258
+ moduleMap.get(moduleName).push({
11259
+ ...file,
11260
+ module: moduleName
11261
+ });
11262
+ }
11263
+ const modules = [];
11264
+ for (const [moduleName, moduleFiles] of moduleMap.entries()){
11265
+ if (0 === moduleFiles.length) continue;
11266
+ const modulePaths = moduleFiles.map((f)=>external_path_namespaceObject.dirname(f.path));
11267
+ const commonPath = findCommonPath(modulePaths);
11268
+ modules.push({
11269
+ name: moduleName,
11270
+ path: commonPath,
11271
+ relativePath: external_path_namespaceObject.relative(projectPath, commonPath),
11272
+ files: moduleFiles,
11273
+ subModules: []
11274
+ });
11275
+ }
11276
+ return modules;
11277
+ }
11278
+ function findCommonPath(paths) {
11279
+ if (0 === paths.length) return "";
11280
+ if (1 === paths.length) return external_path_namespaceObject.dirname(paths[0]);
11281
+ const parts = paths[0].split(external_path_namespaceObject.sep);
11282
+ let commonLength = parts.length;
11283
+ for(let i = 1; i < paths.length; i++){
11284
+ const currentParts = paths[i].split(external_path_namespaceObject.sep);
11285
+ commonLength = Math.min(commonLength, currentParts.length);
11286
+ for(let j = 0; j < commonLength; j++)if (parts[j] !== currentParts[j]) {
11287
+ commonLength = j;
11288
+ break;
11289
+ }
11290
+ }
11291
+ return parts.slice(0, commonLength).join(external_path_namespaceObject.sep);
11292
+ }
11293
+ async function analyzeProject(projectPath) {
11294
+ logger.info(`\u{5F00}\u{59CB}\u{5206}\u{6790}\u{9879}\u{76EE}: ${projectPath}`);
11295
+ try {
11296
+ const stats = await promises_namespaceObject.stat(projectPath);
11297
+ if (!stats.isDirectory()) throw new Error(`\u{9879}\u{76EE}\u{8DEF}\u{5F84}\u{4E0D}\u{662F}\u{76EE}\u{5F55}: ${projectPath}`);
11298
+ const allFiles = await scanDirectory(projectPath, projectPath);
11299
+ const modules = identifyModules(allFiles, projectPath);
11300
+ const statistics = {
11301
+ totalFiles: allFiles.length,
11302
+ controllerFiles: allFiles.filter((f)=>"controller" === f.type).length,
11303
+ serviceFiles: allFiles.filter((f)=>"service" === f.type).length,
11304
+ entityFiles: allFiles.filter((f)=>"entity" === f.type).length,
11305
+ dtoFiles: allFiles.filter((f)=>"dto" === f.type).length,
11306
+ moduleCount: modules.length
11307
+ };
11308
+ const projectName = external_path_namespaceObject.basename(projectPath);
11309
+ const result = {
11310
+ projectName,
11311
+ projectPath,
11312
+ modules,
11313
+ allFiles,
11314
+ statistics
11315
+ };
11316
+ logger.info(`\u{9879}\u{76EE}\u{5206}\u{6790}\u{5B8C}\u{6210}`, {
11317
+ projectName,
11318
+ moduleCount: modules.length,
11319
+ totalFiles: allFiles.length
11320
+ });
11321
+ return result;
11322
+ } catch (error) {
11323
+ logger.error(`\u{9879}\u{76EE}\u{5206}\u{6790}\u{5931}\u{8D25}: ${projectPath}`, {
11324
+ error: error.message
11325
+ });
11326
+ throw error;
11327
+ }
11328
+ }
11329
+ function getModuleNames(analysis) {
11330
+ return analysis.modules.map((m)=>m.name);
11331
+ }
11332
+ function extractComments(content) {
11333
+ const comments = new Map();
11334
+ const lines = content.split("\n");
11335
+ let currentComment = "";
11336
+ let commentStartLine = -1;
11337
+ for(let i = 0; i < lines.length; i++){
11338
+ const line = lines[i].trim();
11339
+ if (line.startsWith("/**")) {
11340
+ currentComment = "";
11341
+ commentStartLine = i;
11342
+ } else if (line.startsWith("*") && commentStartLine >= 0) {
11343
+ const commentText = line.replace(/^\*\s?/, "");
11344
+ if (commentText && !commentText.startsWith("@")) currentComment += (currentComment ? " " : "") + commentText;
11345
+ } else if (line.startsWith("*/") && commentStartLine >= 0) {
11346
+ if (currentComment) comments.set(i + 1, currentComment);
11347
+ currentComment = "";
11348
+ commentStartLine = -1;
11349
+ } else if (line.startsWith("//")) {
11350
+ const commentText = line.replace(/^\/\/\s?/, "");
11351
+ if (commentText) comments.set(i + 1, commentText);
11352
+ }
11353
+ }
11354
+ return comments;
11355
+ }
11356
+ function extractAnnotations(content, lineNumber) {
11357
+ const lines = content.split("\n");
11358
+ const annotations = [];
11359
+ for(let i = Math.max(0, lineNumber - 10); i < lineNumber; i++){
11360
+ const line = lines[i]?.trim();
11361
+ if (line && line.startsWith("@")) annotations.push(line);
11362
+ }
11363
+ return annotations;
11364
+ }
11365
+ async function analyzeJavaController(file) {
11366
+ try {
11367
+ const content = await promises_namespaceObject.readFile(file.path, "utf-8");
11368
+ const comments = extractComments(content);
11369
+ const lines = content.split("\n");
11370
+ let className = "";
11371
+ let classDescription = "";
11372
+ let classLineNumber = -1;
11373
+ for(let i = 0; i < lines.length; i++){
11374
+ const line = lines[i].trim();
11375
+ if (line.includes("class ") && (line.includes("Controller") || line.includes("@RestController") || line.includes("@Controller"))) {
11376
+ const match = line.match(/class\s+(\w+)/);
11377
+ if (match) {
11378
+ className = match[1];
11379
+ classLineNumber = i;
11380
+ classDescription = comments.get(i) || className;
11381
+ break;
11382
+ }
11383
+ }
11384
+ }
11385
+ if (!className) return null;
11386
+ const methods = [];
11387
+ for(let i = 0; i < lines.length; i++){
11388
+ const line = lines[i].trim();
11389
+ const methodMatch = line.match(/public\s+(\w+(?:<[^>]+>)?)\s+(\w+)\s*\([^)]*\)/);
11390
+ if (methodMatch) {
11391
+ const returnType = methodMatch[1];
11392
+ const methodName = methodMatch[2];
11393
+ const methodDescription = comments.get(i) || methodName;
11394
+ const annotations = extractAnnotations(content, i);
11395
+ let httpMethod = "";
11396
+ let path = "";
11397
+ for (const annotation of annotations){
11398
+ if (annotation.includes("@GetMapping") || annotation.includes("@RequestMapping") && annotation.includes("GET")) httpMethod = "GET";
11399
+ else if (annotation.includes("@PostMapping") || annotation.includes("@RequestMapping") && annotation.includes("POST")) httpMethod = "POST";
11400
+ else if (annotation.includes("@PutMapping")) httpMethod = "PUT";
11401
+ else if (annotation.includes("@DeleteMapping")) httpMethod = "DELETE";
11402
+ const pathMatch = annotation.match(/["\']([^"']+)["\']/);
11403
+ if (pathMatch) path = pathMatch[1];
11404
+ }
11405
+ const parameters = [];
11406
+ const paramMatch = line.match(/\(([^)]+)\)/);
11407
+ if (paramMatch) {
11408
+ const paramStr = paramMatch[1];
11409
+ const params = paramStr.split(",").map((p)=>p.trim()).filter((p)=>p);
11410
+ params.forEach((param, index)=>{
11411
+ const parts = param.split(/\s+/);
11412
+ if (parts.length >= 2) {
11413
+ const type = parts[parts.length - 2];
11414
+ const name = parts[parts.length - 1];
11415
+ parameters.push({
11416
+ id: String(index + 1),
11417
+ propertyType: type,
11418
+ propertyName: name,
11419
+ required: "\u662F",
11420
+ propertyDesc: `${name}\u{53C2}\u{6570}`
11421
+ });
11422
+ }
11423
+ });
11424
+ }
11425
+ methods.push({
11426
+ name: methodName,
11427
+ description: methodDescription,
11428
+ parameters,
11429
+ returnType,
11430
+ returnDescription: `${methodName}\u{8FD4}\u{56DE}\u{7ED3}\u{679C}`,
11431
+ httpMethod,
11432
+ path
11433
+ });
11434
+ }
11435
+ }
11436
+ return {
11437
+ name: className,
11438
+ description: classDescription,
11439
+ type: "controller",
11440
+ methods,
11441
+ fields: [],
11442
+ annotations: extractAnnotations(content, classLineNumber)
11443
+ };
11444
+ } catch (error) {
11445
+ logger.warn(`\u{5206}\u{6790}Controller\u{6587}\u{4EF6}\u{5931}\u{8D25}: ${file.path}`, {
11446
+ error
11447
+ });
11448
+ return null;
11449
+ }
11450
+ }
11451
+ async function analyzeJavaService(file) {
11452
+ try {
11453
+ const content = await promises_namespaceObject.readFile(file.path, "utf-8");
11454
+ const comments = extractComments(content);
11455
+ const lines = content.split("\n");
11456
+ let className = "";
11457
+ let classDescription = "";
11458
+ for(let i = 0; i < lines.length; i++){
11459
+ const line = lines[i].trim();
11460
+ if (line.includes("class ") && (line.includes("Service") || line.includes("ServiceImpl"))) {
11461
+ const match = line.match(/class\s+(\w+)/);
11462
+ if (match) {
11463
+ className = match[1];
11464
+ classDescription = comments.get(i) || className;
11465
+ break;
11466
+ }
11467
+ }
11468
+ }
11469
+ if (!className) return null;
11470
+ const methods = [];
11471
+ for(let i = 0; i < lines.length; i++){
11472
+ const line = lines[i].trim();
11473
+ const methodMatch = line.match(/public\s+(\w+(?:<[^>]+>)?)\s+(\w+)\s*\([^)]*\)/);
11474
+ if (methodMatch) {
11475
+ const returnType = methodMatch[1];
11476
+ const methodName = methodMatch[2];
11477
+ const methodDescription = comments.get(i) || methodName;
11478
+ methods.push({
11479
+ name: methodName,
11480
+ description: methodDescription,
11481
+ parameters: [],
11482
+ returnType,
11483
+ returnDescription: `${methodName}\u{8FD4}\u{56DE}\u{7ED3}\u{679C}`
11484
+ });
11485
+ }
11486
+ }
11487
+ return {
11488
+ name: className,
11489
+ description: classDescription,
11490
+ type: "service",
11491
+ methods,
11492
+ fields: [],
11493
+ annotations: []
11494
+ };
11495
+ } catch (error) {
11496
+ logger.warn(`\u{5206}\u{6790}Service\u{6587}\u{4EF6}\u{5931}\u{8D25}: ${file.path}`, {
11497
+ error
11498
+ });
11499
+ return null;
11500
+ }
11501
+ }
11502
+ async function analyzeJavaEntity(file) {
11503
+ try {
11504
+ const content = await promises_namespaceObject.readFile(file.path, "utf-8");
11505
+ const comments = extractComments(content);
11506
+ const lines = content.split("\n");
11507
+ let className = "";
11508
+ let classDescription = "";
11509
+ for(let i = 0; i < lines.length; i++){
11510
+ const line = lines[i].trim();
11511
+ if (line.includes("@Table")) {
11512
+ const tableMatch = line.match(/name\s*=\s*["\']([^"']+)["\']/);
11513
+ if (tableMatch) tableMatch[1];
11514
+ }
11515
+ if (line.includes("class ") && (line.includes("Entity") || content.includes("@Entity"))) {
11516
+ const match = line.match(/class\s+(\w+)/);
11517
+ if (match) {
11518
+ className = match[1];
11519
+ classDescription = comments.get(i) || className;
11520
+ break;
11521
+ }
11522
+ }
11523
+ }
11524
+ if (!className) return null;
11525
+ const fields = [];
11526
+ for(let i = 0; i < lines.length; i++){
11527
+ const line = lines[i].trim();
11528
+ const fieldMatch = line.match(/private\s+(\w+(?:<[^>]+>)?)\s+(\w+);?/);
11529
+ if (fieldMatch) {
11530
+ const fieldType = fieldMatch[1];
11531
+ const fieldName = fieldMatch[2];
11532
+ const fieldDescription = comments.get(i) || fieldName;
11533
+ const annotations = extractAnnotations(content, i);
11534
+ fields.push({
11535
+ name: fieldName,
11536
+ type: fieldType,
11537
+ description: fieldDescription,
11538
+ annotations
11539
+ });
11540
+ }
11541
+ }
11542
+ return {
11543
+ name: className,
11544
+ description: classDescription,
11545
+ type: "entity",
11546
+ methods: [],
11547
+ fields,
11548
+ annotations: []
11549
+ };
11550
+ } catch (error) {
11551
+ logger.warn(`\u{5206}\u{6790}Entity\u{6587}\u{4EF6}\u{5931}\u{8D25}: ${file.path}`, {
11552
+ error
11553
+ });
11554
+ return null;
11555
+ }
11556
+ }
11557
+ async function analyzeTypeScriptFile(file) {
11558
+ try {
11559
+ const content = await promises_namespaceObject.readFile(file.path, "utf-8");
11560
+ const comments = extractComments(content);
11561
+ const lines = content.split("\n");
11562
+ let className = "";
11563
+ let classDescription = "";
11564
+ for(let i = 0; i < lines.length; i++){
11565
+ const line = lines[i].trim();
11566
+ const classMatch = line.match(/(?:export\s+)?(?:class|interface)\s+(\w+)/);
11567
+ if (classMatch) {
11568
+ className = classMatch[1];
11569
+ classDescription = comments.get(i) || className;
11570
+ break;
11571
+ }
11572
+ }
11573
+ if (!className) return null;
11574
+ return {
11575
+ name: className,
11576
+ description: classDescription,
11577
+ type: file.type,
11578
+ methods: [],
11579
+ fields: [],
11580
+ annotations: []
11581
+ };
11582
+ } catch (error) {
11583
+ logger.warn(`\u{5206}\u{6790}TypeScript\u{6587}\u{4EF6}\u{5931}\u{8D25}: ${file.path}`, {
11584
+ error
11585
+ });
11586
+ return null;
11587
+ }
11588
+ }
11589
+ async function analyzeCodeFile(file) {
11590
+ const supportedTypes = [
11591
+ "controller",
11592
+ "service",
11593
+ "entity",
11594
+ "dto"
11595
+ ];
11596
+ if (!supportedTypes.includes(file.type)) return null;
11597
+ const ext = external_path_namespaceObject.extname(file.path).toLowerCase();
11598
+ switch(ext){
11599
+ case ".java":
11600
+ if ("controller" === file.type) return analyzeJavaController(file);
11601
+ if ("service" === file.type) return analyzeJavaService(file);
11602
+ if ("entity" === file.type) return analyzeJavaEntity(file);
11603
+ break;
11604
+ case ".ts":
11605
+ case ".js":
11606
+ return analyzeTypeScriptFile(file);
11607
+ default:
11608
+ break;
11609
+ }
11610
+ return null;
11611
+ }
11612
+ function classInfoToServiceInterface(classInfo, index) {
11613
+ const interfaceDetailList = classInfo.methods.map((method, methodIndex)=>({
11614
+ id: String(methodIndex + 1),
11615
+ functionDescription: method.description,
11616
+ serviceName: classInfo.name,
11617
+ methodName: method.name
11618
+ }));
11619
+ return {
11620
+ id: String(index + 1),
11621
+ serviceType: "controller" === classInfo.type ? "REST" : "Service",
11622
+ serviceEnglishName: classInfo.name,
11623
+ serviceChineseName: classInfo.description,
11624
+ serviceDescription: classInfo.description,
11625
+ interfaceDetailList
11626
+ };
11627
+ }
11628
+ function classInfoToDesignDetail(classInfo) {
11629
+ const interfaceDesignDetailList = classInfo.methods.map((method)=>({
11630
+ functionName: method.name,
11631
+ interfaceDesc: method.description,
11632
+ className: classInfo.name,
11633
+ methodName: method.name,
11634
+ parameterList: method.parameters,
11635
+ returnResultList: [
11636
+ {
11637
+ id: "1",
11638
+ propertyType: method.returnType,
11639
+ propertyName: "result",
11640
+ propertyDesc: method.returnDescription
11641
+ }
11642
+ ],
11643
+ implementLogic: `${method.description}\u{7684}\u{5177}\u{4F53}\u{5B9E}\u{73B0}\u{903B}\u{8F91}`
11644
+ }));
11645
+ return {
11646
+ serviceName: classInfo.name,
11647
+ serviceDesc: classInfo.description,
11648
+ interfaceDesignDetailList
11649
+ };
11650
+ }
11651
+ function classInfoToTableInfo(classInfo, index) {
11652
+ const tableDetailList = classInfo.fields.map((field, fieldIndex)=>({
11653
+ id: String(fieldIndex + 1),
11654
+ fieldName: field.name,
11655
+ fieldType: field.type,
11656
+ fieldDescription: field.description,
11657
+ fieldLength: getFieldLength(field.type)
11658
+ }));
11659
+ return {
11660
+ id: String(index + 1),
11661
+ tableName: camelToSnakeCase(classInfo.name),
11662
+ entityName: classInfo.name,
11663
+ tableDescription: classInfo.description,
11664
+ tableDetailList
11665
+ };
11666
+ }
11667
+ function getFieldLength(fieldType) {
11668
+ const type = fieldType.toLowerCase();
11669
+ if (type.includes("string") || type.includes("varchar")) return "255";
11670
+ if (type.includes("text")) return "65535";
11671
+ if (type.includes("int") || type.includes("integer")) return "11";
11672
+ if (type.includes("long")) return "20";
11673
+ if (type.includes("decimal") || type.includes("double")) return "10,2";
11674
+ else if (type.includes("date") || type.includes("time")) return "-";
11675
+ else return "255";
11676
+ }
11677
+ function camelToSnakeCase(str) {
11678
+ return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
11679
+ }
11680
+ async function analyzeProjectCode(analysis, options = {}) {
11681
+ logger.info(`\u{5F00}\u{59CB}\u{5206}\u{6790}\u{9879}\u{76EE}\u{4EE3}\u{7801}: ${analysis.projectName}`);
11682
+ let filesToAnalyze = analysis.allFiles;
11683
+ if (options.includeModules && options.includeModules.length > 0) filesToAnalyze = filesToAnalyze.filter((f)=>f.module && options.includeModules.includes(f.module));
11684
+ if (options.excludeModules && options.excludeModules.length > 0) filesToAnalyze = filesToAnalyze.filter((f)=>!f.module || !options.excludeModules.includes(f.module));
11685
+ if (options.includeFileTypes && options.includeFileTypes.length > 0) filesToAnalyze = filesToAnalyze.filter((f)=>options.includeFileTypes.includes(f.type));
11686
+ logger.info(`\u{9700}\u{8981}\u{5206}\u{6790}\u{7684}\u{6587}\u{4EF6}\u{6570}\u{91CF}: ${filesToAnalyze.length}`);
11687
+ const classInfoList = [];
11688
+ for (const file of filesToAnalyze)try {
11689
+ const classInfo = await analyzeCodeFile(file);
11690
+ if (classInfo) classInfoList.push(classInfo);
11691
+ } catch (error) {
11692
+ logger.warn(`\u{5206}\u{6790}\u{6587}\u{4EF6}\u{5931}\u{8D25}: ${file.path}`, {
11693
+ error
11694
+ });
11695
+ }
11696
+ logger.info(`\u{6210}\u{529F}\u{5206}\u{6790}\u{7684}\u{7C7B}\u{6570}\u{91CF}: ${classInfoList.length}`);
11697
+ const serviceInterfaceList = [];
11698
+ const designDetailList = [];
11699
+ const tableInfoList = [];
11700
+ let serviceIndex = 0;
11701
+ let tableIndex = 0;
11702
+ for (const classInfo of classInfoList)if ("controller" === classInfo.type || "service" === classInfo.type) {
11703
+ serviceInterfaceList.push(classInfoToServiceInterface(classInfo, serviceIndex++));
11704
+ designDetailList.push(classInfoToDesignDetail(classInfo));
11705
+ } else if ("entity" === classInfo.type) tableInfoList.push(classInfoToTableInfo(classInfo, tableIndex++));
11706
+ logger.info(`\u{4EE3}\u{7801}\u{5206}\u{6790}\u{5B8C}\u{6210}`, {
11707
+ serviceCount: serviceInterfaceList.length,
11708
+ designCount: designDetailList.length,
11709
+ tableCount: tableInfoList.length
11710
+ });
11711
+ return {
11712
+ serviceInterfaceList,
11713
+ designDetailList,
11714
+ tableInfoList
11715
+ };
11716
+ }
11717
+ class TechSpecStorage {
11718
+ dataDir;
11719
+ projectsFile;
11720
+ chaptersFile;
11721
+ modulesFile;
11722
+ filesFile;
11723
+ constructor(){
11724
+ try {
11725
+ const storageDir = getStorageDir();
11726
+ this.dataDir = external_path_namespaceObject.join(storageDir, "tech-spec");
11727
+ this.projectsFile = external_path_namespaceObject.join(this.dataDir, "projects.json");
11728
+ this.chaptersFile = external_path_namespaceObject.join(this.dataDir, "chapters.json");
11729
+ this.modulesFile = external_path_namespaceObject.join(this.dataDir, "modules.json");
11730
+ this.filesFile = external_path_namespaceObject.join(this.dataDir, "files.json");
11731
+ this.ensureDataDir();
11732
+ } catch (error) {
11733
+ console.warn("TechSpecStorage\u521D\u59CB\u5316\u5931\u8D25\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u8DEF\u5F84:", error);
11734
+ const defaultDir = external_path_namespaceObject.join(process.cwd(), ".aico");
11735
+ this.dataDir = external_path_namespaceObject.join(defaultDir, "tech-spec");
11736
+ this.projectsFile = external_path_namespaceObject.join(this.dataDir, "projects.json");
11737
+ this.chaptersFile = external_path_namespaceObject.join(this.dataDir, "chapters.json");
11738
+ this.modulesFile = external_path_namespaceObject.join(this.dataDir, "modules.json");
11739
+ this.filesFile = external_path_namespaceObject.join(this.dataDir, "files.json");
11740
+ this.ensureDataDir();
11741
+ }
11742
+ }
11743
+ ensureDataDir() {
11744
+ if (!external_fs_namespaceObject.existsSync(this.dataDir)) external_fs_namespaceObject.mkdirSync(this.dataDir, {
11745
+ recursive: true
11746
+ });
11747
+ if (!external_fs_namespaceObject.existsSync(this.projectsFile)) external_fs_namespaceObject.writeFileSync(this.projectsFile, "[]", "utf8");
11748
+ if (!external_fs_namespaceObject.existsSync(this.chaptersFile)) external_fs_namespaceObject.writeFileSync(this.chaptersFile, "[]", "utf8");
11749
+ if (!external_fs_namespaceObject.existsSync(this.modulesFile)) external_fs_namespaceObject.writeFileSync(this.modulesFile, "[]", "utf8");
11750
+ if (!external_fs_namespaceObject.existsSync(this.filesFile)) external_fs_namespaceObject.writeFileSync(this.filesFile, "[]", "utf8");
11751
+ }
11752
+ getNextId(items) {
11753
+ return items.length > 0 ? Math.max(...items.map((i)=>i.id)) + 1 : 1;
11754
+ }
11755
+ readProjects() {
11756
+ try {
11757
+ const data = external_fs_namespaceObject.readFileSync(this.projectsFile, "utf8");
11758
+ return JSON.parse(data).map((p)=>({
11759
+ ...p,
11760
+ createdAt: new Date(p.createdAt),
11761
+ updatedAt: new Date(p.updatedAt),
11762
+ completedAt: p.completedAt ? new Date(p.completedAt) : void 0
11763
+ }));
11764
+ } catch (error) {
11765
+ logger.error("Error reading tech spec projects file", error);
11766
+ return [];
11767
+ }
11768
+ }
11769
+ writeProjects(projects) {
11770
+ external_fs_namespaceObject.writeFileSync(this.projectsFile, JSON.stringify(projects, null, 2), "utf8");
11771
+ }
11772
+ async createTechSpecProject(data) {
11773
+ const projects = this.readProjects();
11774
+ const newProject = {
11775
+ id: this.getNextId(projects),
11776
+ uuid: data.uuid,
11777
+ projectName: data.projectName,
11778
+ moduleName: data.moduleName,
11779
+ projectPath: data.projectPath,
11780
+ status: "analyzing",
11781
+ totalModules: data.totalModules,
11782
+ processedModules: 0,
11783
+ totalFiles: data.totalFiles,
11784
+ processedFiles: 0,
11785
+ createdAt: new Date(),
11786
+ updatedAt: new Date()
11787
+ };
11788
+ projects.push(newProject);
11789
+ this.writeProjects(projects);
11790
+ return newProject;
11791
+ }
11792
+ async getTechSpecProject(uuid) {
11793
+ const projects = this.readProjects();
11794
+ return projects.find((p)=>p.uuid === uuid) || null;
11795
+ }
11796
+ async getAllTechSpecProjects() {
11797
+ return this.readProjects();
11798
+ }
11799
+ async updateTechSpecProject(uuid, data) {
11800
+ const projects = this.readProjects();
11801
+ const projectIndex = projects.findIndex((p)=>p.uuid === uuid);
11802
+ if (projectIndex >= 0) {
11803
+ projects[projectIndex] = {
11804
+ ...projects[projectIndex],
11805
+ ...data,
11806
+ updatedAt: new Date()
11807
+ };
11808
+ this.writeProjects(projects);
11809
+ }
11810
+ }
11811
+ readChapters() {
11812
+ try {
11813
+ const data = external_fs_namespaceObject.readFileSync(this.chaptersFile, "utf8");
11814
+ return JSON.parse(data).map((c)=>({
11815
+ ...c,
11816
+ createdAt: new Date(c.createdAt),
11817
+ updatedAt: new Date(c.updatedAt)
11818
+ }));
11819
+ } catch (error) {
11820
+ logger.error("Error reading tech spec chapters file", error);
11821
+ return [];
11822
+ }
11823
+ }
11824
+ writeChapters(chapters) {
11825
+ external_fs_namespaceObject.writeFileSync(this.chaptersFile, JSON.stringify(chapters, null, 2), "utf8");
11826
+ }
11827
+ async createTechSpecChapter(data) {
11828
+ const chapters = this.readChapters();
11829
+ const newChapter = {
11830
+ id: this.getNextId(chapters),
11831
+ uuid: data.uuid,
11832
+ projectUuid: data.projectUuid,
11833
+ chapterType: data.chapterType,
11834
+ chapterTitle: data.chapterTitle,
11835
+ chapterOrder: data.chapterOrder,
11836
+ content: data.content,
11837
+ moduleSource: data.moduleSource,
11838
+ fileCount: data.fileCount,
11839
+ status: "completed",
11840
+ createdAt: new Date(),
11841
+ updatedAt: new Date()
11842
+ };
11843
+ chapters.push(newChapter);
11844
+ this.writeChapters(chapters);
11845
+ return newChapter;
11846
+ }
11847
+ async getTechSpecChapters(projectUuid) {
11848
+ const chapters = this.readChapters();
11849
+ return chapters.filter((c)=>c.projectUuid === projectUuid).sort((a, b)=>a.chapterOrder - b.chapterOrder);
11850
+ }
11851
+ async getTechSpecChaptersByType(projectUuid, chapterType) {
11852
+ const chapters = this.readChapters();
11853
+ return chapters.filter((c)=>c.projectUuid === projectUuid && c.chapterType === chapterType).sort((a, b)=>a.chapterOrder - b.chapterOrder);
11854
+ }
11855
+ async updateTechSpecChapter(uuid, data) {
11856
+ const chapters = this.readChapters();
11857
+ const chapterIndex = chapters.findIndex((c)=>c.uuid === uuid);
11858
+ if (chapterIndex >= 0) {
11859
+ chapters[chapterIndex] = {
11860
+ ...chapters[chapterIndex],
11861
+ ...data,
11862
+ updatedAt: new Date()
11863
+ };
11864
+ this.writeChapters(chapters);
11865
+ }
11866
+ }
11867
+ readModules() {
11868
+ try {
11869
+ const data = external_fs_namespaceObject.readFileSync(this.modulesFile, "utf8");
11870
+ return JSON.parse(data).map((m)=>({
11871
+ ...m,
11872
+ createdAt: new Date(m.createdAt),
11873
+ updatedAt: new Date(m.updatedAt),
11874
+ startedAt: m.startedAt ? new Date(m.startedAt) : void 0,
11875
+ completedAt: m.completedAt ? new Date(m.completedAt) : void 0
11876
+ }));
11877
+ } catch (error) {
11878
+ logger.error("Error reading tech spec modules file", error);
11879
+ return [];
11880
+ }
11881
+ }
11882
+ writeModules(modules) {
11883
+ external_fs_namespaceObject.writeFileSync(this.modulesFile, JSON.stringify(modules, null, 2), "utf8");
11884
+ }
11885
+ async createModuleProcessStatus(data) {
11886
+ const modules = this.readModules();
11887
+ const newModule = {
11888
+ id: this.getNextId(modules),
11889
+ uuid: data.uuid,
11890
+ projectUuid: data.projectUuid,
11891
+ moduleName: data.moduleName,
11892
+ moduleType: data.moduleType,
11893
+ totalFiles: data.totalFiles,
11894
+ processedFiles: 0,
11895
+ status: "pending",
11896
+ createdAt: new Date(),
11897
+ updatedAt: new Date()
11898
+ };
11899
+ modules.push(newModule);
11900
+ this.writeModules(modules);
11901
+ return newModule;
11902
+ }
11903
+ async getModuleProcessStatuses(projectUuid) {
11904
+ const modules = this.readModules();
11905
+ return modules.filter((m)=>m.projectUuid === projectUuid);
11906
+ }
11907
+ async updateModuleProcessStatus(uuid, data) {
11908
+ const modules = this.readModules();
11909
+ const moduleIndex = modules.findIndex((m)=>m.uuid === uuid);
11910
+ if (moduleIndex >= 0) {
11911
+ modules[moduleIndex] = {
11912
+ ...modules[moduleIndex],
11913
+ ...data,
11914
+ updatedAt: new Date()
11915
+ };
11916
+ this.writeModules(modules);
11917
+ }
11918
+ }
11919
+ readFiles() {
11920
+ try {
11921
+ const data = external_fs_namespaceObject.readFileSync(this.filesFile, "utf8");
11922
+ return JSON.parse(data).map((f)=>({
11923
+ ...f,
11924
+ createdAt: new Date(f.createdAt),
11925
+ updatedAt: new Date(f.updatedAt)
11926
+ }));
11927
+ } catch (error) {
11928
+ logger.error("Error reading tech spec files file", error);
11929
+ return [];
11930
+ }
11931
+ }
11932
+ writeFiles(files) {
11933
+ external_fs_namespaceObject.writeFileSync(this.filesFile, JSON.stringify(files, null, 2), "utf8");
11934
+ }
11935
+ async createFileProcessRecord(data) {
11936
+ const files = this.readFiles();
11937
+ const newFile = {
11938
+ id: this.getNextId(files),
11939
+ uuid: data.uuid,
11940
+ projectUuid: data.projectUuid,
11941
+ moduleUuid: data.moduleUuid,
11942
+ filePath: data.filePath,
11943
+ fileName: data.fileName,
11944
+ fileType: data.fileType,
11945
+ extractedData: data.extractedData,
11946
+ status: "completed",
11947
+ processingTime: data.processingTime,
11948
+ createdAt: new Date(),
11949
+ updatedAt: new Date()
11950
+ };
11951
+ files.push(newFile);
11952
+ this.writeFiles(files);
11953
+ return newFile;
11954
+ }
11955
+ async getFileProcessRecords(projectUuid) {
11956
+ const files = this.readFiles();
11957
+ return files.filter((f)=>f.projectUuid === projectUuid);
11958
+ }
11959
+ async getFileProcessRecordsByModule(moduleUuid) {
11960
+ const files = this.readFiles();
11961
+ return files.filter((f)=>f.moduleUuid === moduleUuid);
11962
+ }
11963
+ async mergeChaptersByType(projectUuid, chapterType) {
11964
+ const chapters = await this.getTechSpecChaptersByType(projectUuid, chapterType);
11965
+ if (0 === chapters.length) return null;
11966
+ switch(chapterType){
11967
+ case "service_interface_list":
11968
+ return this.mergeServiceInterfaceChapters(chapters);
11969
+ case "interface_design":
11970
+ return this.mergeInterfaceDesignChapters(chapters);
11971
+ case "database_design":
11972
+ return this.mergeDatabaseDesignChapters(chapters);
11973
+ case "tech_solution":
11974
+ return this.mergeTechSolutionChapters(chapters);
11975
+ case "business_exception":
11976
+ return this.mergeBusinessExceptionChapters(chapters);
11977
+ case "appendix":
11978
+ return this.mergeAppendixChapters(chapters);
11979
+ default:
11980
+ return {
11981
+ content: chapters.map((c)=>JSON.parse(c.content)).join("\n\n"),
11982
+ totalModules: chapters.length,
11983
+ totalFiles: chapters.reduce((sum, c)=>sum + c.fileCount, 0)
11984
+ };
11985
+ }
11986
+ }
11987
+ mergeServiceInterfaceChapters(chapters) {
11988
+ const allInterfaces = [];
11989
+ let idCounter = 1;
11990
+ for (const chapter of chapters)try {
11991
+ const chapterData = JSON.parse(chapter.content);
11992
+ if (Array.isArray(chapterData)) {
11993
+ const interfaces = chapterData.map((item)=>({
11994
+ ...item,
11995
+ id: String(idCounter++),
11996
+ moduleSource: chapter.moduleSource
11997
+ }));
11998
+ allInterfaces.push(...interfaces);
11999
+ }
12000
+ } catch (error) {
12001
+ logger.warn(`\u{89E3}\u{6790}\u{7AE0}\u{8282}\u{5185}\u{5BB9}\u{5931}\u{8D25}: ${chapter.uuid}`, error);
12002
+ }
12003
+ return allInterfaces;
12004
+ }
12005
+ mergeInterfaceDesignChapters(chapters) {
12006
+ const allDesigns = [];
12007
+ for (const chapter of chapters)try {
12008
+ const chapterData = JSON.parse(chapter.content);
12009
+ if (Array.isArray(chapterData)) {
12010
+ const designs = chapterData.map((item)=>({
12011
+ ...item,
12012
+ moduleSource: chapter.moduleSource
12013
+ }));
12014
+ allDesigns.push(...designs);
12015
+ }
12016
+ } catch (error) {
12017
+ logger.warn(`\u{89E3}\u{6790}\u{7AE0}\u{8282}\u{5185}\u{5BB9}\u{5931}\u{8D25}: ${chapter.uuid}`, error);
12018
+ }
12019
+ return allDesigns;
12020
+ }
12021
+ mergeDatabaseDesignChapters(chapters) {
12022
+ const allTables = [];
12023
+ let idCounter = 1;
12024
+ for (const chapter of chapters)try {
12025
+ const chapterData = JSON.parse(chapter.content);
12026
+ if (Array.isArray(chapterData)) {
12027
+ const tables = chapterData.map((item)=>({
12028
+ ...item,
12029
+ id: String(idCounter++),
12030
+ moduleSource: chapter.moduleSource
12031
+ }));
12032
+ allTables.push(...tables);
12033
+ }
12034
+ } catch (error) {
12035
+ logger.warn(`\u{89E3}\u{6790}\u{7AE0}\u{8282}\u{5185}\u{5BB9}\u{5931}\u{8D25}: ${chapter.uuid}`, error);
12036
+ }
12037
+ return allTables;
12038
+ }
12039
+ mergeTechSolutionChapters(chapters) {
12040
+ const contents = [];
12041
+ for (const chapter of chapters)try {
12042
+ const chapterData = JSON.parse(chapter.content);
12043
+ if (chapterData.content) contents.push(`## ${chapter.moduleSource || "\u6A21\u5757"}\n${chapterData.content}`);
12044
+ } catch (error) {
12045
+ logger.warn(`\u{89E3}\u{6790}\u{7AE0}\u{8282}\u{5185}\u{5BB9}\u{5931}\u{8D25}: ${chapter.uuid}`, error);
12046
+ }
12047
+ return {
12048
+ content: contents.join("\n\n")
12049
+ };
12050
+ }
12051
+ mergeBusinessExceptionChapters(chapters) {
12052
+ const contents = [];
12053
+ for (const chapter of chapters)try {
12054
+ const chapterData = JSON.parse(chapter.content);
12055
+ if (chapterData.content) contents.push(`## ${chapter.moduleSource || "\u6A21\u5757"}\n${chapterData.content}`);
12056
+ } catch (error) {
12057
+ logger.warn(`\u{89E3}\u{6790}\u{7AE0}\u{8282}\u{5185}\u{5BB9}\u{5931}\u{8D25}: ${chapter.uuid}`, error);
12058
+ }
12059
+ return {
12060
+ content: contents.join("\n\n")
12061
+ };
12062
+ }
12063
+ mergeAppendixChapters(chapters) {
12064
+ const allExceptionCodes = [];
12065
+ const allCodeValues = [];
12066
+ let idCounter = 1;
12067
+ for (const chapter of chapters)try {
12068
+ const chapterData = JSON.parse(chapter.content);
12069
+ if (chapterData.exceptionCodeList && Array.isArray(chapterData.exceptionCodeList)) {
12070
+ const codes = chapterData.exceptionCodeList.map((item)=>({
12071
+ ...item,
12072
+ id: String(idCounter++),
12073
+ moduleSource: chapter.moduleSource
12074
+ }));
12075
+ allExceptionCodes.push(...codes);
12076
+ }
12077
+ if (chapterData.codeValueList && Array.isArray(chapterData.codeValueList)) {
12078
+ const values = chapterData.codeValueList.map((item)=>({
12079
+ ...item,
12080
+ id: String(idCounter++),
12081
+ moduleSource: chapter.moduleSource
12082
+ }));
12083
+ allCodeValues.push(...values);
12084
+ }
12085
+ } catch (error) {
12086
+ logger.warn(`\u{89E3}\u{6790}\u{7AE0}\u{8282}\u{5185}\u{5BB9}\u{5931}\u{8D25}: ${chapter.uuid}`, error);
12087
+ }
12088
+ return {
12089
+ exceptionCodeList: allExceptionCodes,
12090
+ codeValueList: allCodeValues
12091
+ };
12092
+ }
12093
+ async cleanupProject(projectUuid) {
12094
+ const chapters = this.readChapters();
12095
+ const filteredChapters = chapters.filter((c)=>c.projectUuid !== projectUuid);
12096
+ this.writeChapters(filteredChapters);
12097
+ const modules = this.readModules();
12098
+ const filteredModules = modules.filter((m)=>m.projectUuid !== projectUuid);
12099
+ this.writeModules(filteredModules);
12100
+ const files = this.readFiles();
12101
+ const filteredFiles = files.filter((f)=>f.projectUuid !== projectUuid);
12102
+ this.writeFiles(filteredFiles);
12103
+ logger.info(`\u{5DF2}\u{6E05}\u{7406}\u{9879}\u{76EE} ${projectUuid} \u{7684}\u{6240}\u{6709}\u{76F8}\u{5173}\u{6570}\u{636E}`);
12104
+ }
12105
+ async getProjectStatistics(projectUuid) {
12106
+ const chapters = await this.getTechSpecChapters(projectUuid);
12107
+ const modules = await this.getModuleProcessStatuses(projectUuid);
12108
+ const files = await this.getFileProcessRecords(projectUuid);
12109
+ const chaptersByType = {
12110
+ project_overview: 0,
12111
+ service_interface_list: 0,
12112
+ interface_design: 0,
12113
+ database_design: 0,
12114
+ tech_solution: 0,
12115
+ business_exception: 0,
12116
+ appendix: 0
12117
+ };
12118
+ chapters.forEach((chapter)=>{
12119
+ chaptersByType[chapter.chapterType]++;
12120
+ });
12121
+ return {
12122
+ totalChapters: chapters.length,
12123
+ completedChapters: chapters.filter((c)=>"completed" === c.status).length,
12124
+ totalModules: modules.length,
12125
+ completedModules: modules.filter((m)=>"completed" === m.status).length,
12126
+ totalFiles: files.length,
12127
+ completedFiles: files.filter((f)=>"completed" === f.status).length,
12128
+ chaptersByType
12129
+ };
12130
+ }
12131
+ }
12132
+ const techSpecStorage = new TechSpecStorage();
12133
+ const techSpecDbOperations = {
12134
+ createTechSpecProject: techSpecStorage.createTechSpecProject.bind(techSpecStorage),
12135
+ getTechSpecProject: techSpecStorage.getTechSpecProject.bind(techSpecStorage),
12136
+ getAllTechSpecProjects: techSpecStorage.getAllTechSpecProjects.bind(techSpecStorage),
12137
+ updateTechSpecProject: techSpecStorage.updateTechSpecProject.bind(techSpecStorage),
12138
+ createTechSpecChapter: techSpecStorage.createTechSpecChapter.bind(techSpecStorage),
12139
+ getTechSpecChapters: techSpecStorage.getTechSpecChapters.bind(techSpecStorage),
12140
+ getTechSpecChaptersByType: techSpecStorage.getTechSpecChaptersByType.bind(techSpecStorage),
12141
+ updateTechSpecChapter: techSpecStorage.updateTechSpecChapter.bind(techSpecStorage),
12142
+ createModuleProcessStatus: techSpecStorage.createModuleProcessStatus.bind(techSpecStorage),
12143
+ getModuleProcessStatuses: techSpecStorage.getModuleProcessStatuses.bind(techSpecStorage),
12144
+ updateModuleProcessStatus: techSpecStorage.updateModuleProcessStatus.bind(techSpecStorage),
12145
+ createFileProcessRecord: techSpecStorage.createFileProcessRecord.bind(techSpecStorage),
12146
+ getFileProcessRecords: techSpecStorage.getFileProcessRecords.bind(techSpecStorage),
12147
+ getFileProcessRecordsByModule: techSpecStorage.getFileProcessRecordsByModule.bind(techSpecStorage),
12148
+ mergeChaptersByType: techSpecStorage.mergeChaptersByType.bind(techSpecStorage),
12149
+ cleanupProject: techSpecStorage.cleanupProject.bind(techSpecStorage),
12150
+ getProjectStatistics: techSpecStorage.getProjectStatistics.bind(techSpecStorage)
12151
+ };
12152
+ const DEFAULT_CONFIG = {
12153
+ maxFilesPerBatch: 50,
12154
+ maxModulesPerChapter: 10,
12155
+ enableParallelProcessing: true,
12156
+ skipEmptyModules: true
12157
+ };
12158
+ class ChapterProcessor {
12159
+ config;
12160
+ constructor(config = {}){
12161
+ this.config = {
12162
+ ...DEFAULT_CONFIG,
12163
+ ...config
12164
+ };
12165
+ }
12166
+ async processFullProject(projectAnalysis, options = {}) {
12167
+ const startTime = Date.now();
12168
+ const projectUuid = (0, external_crypto_namespaceObject.randomUUID)();
12169
+ try {
12170
+ logger.info(`\u{5F00}\u{59CB}\u{5206}\u{7AE0}\u{8282}\u{5904}\u{7406}\u{9879}\u{76EE}: ${projectAnalysis.projectName}`, {
12171
+ projectUuid
12172
+ });
12173
+ const techSpecProject = await this.createProjectRecord(projectUuid, projectAnalysis, options);
12174
+ const filteredModules = this.filterModules(projectAnalysis.modules, options);
12175
+ if (0 === filteredModules.length) throw new Error("\u6CA1\u6709\u627E\u5230\u7B26\u5408\u6761\u4EF6\u7684\u6A21\u5757");
12176
+ const batches = this.createModuleBatches(filteredModules);
12177
+ logger.info(`\u{9879}\u{76EE}\u{5206}\u{4E3A} ${batches.length} \u{4E2A}\u{6279}\u{6B21}\u{5904}\u{7406}`, {
12178
+ projectUuid,
12179
+ totalModules: filteredModules.length,
12180
+ totalBatches: batches.length
12181
+ });
12182
+ let totalProcessedFiles = 0;
12183
+ let totalServiceInterfaces = 0;
12184
+ let totalDesignDetails = 0;
12185
+ let totalTableInfo = 0;
12186
+ for(let i = 0; i < batches.length; i++){
12187
+ const batch = batches[i];
12188
+ logger.info(`\u{5904}\u{7406}\u{6279}\u{6B21} ${i + 1}/${batches.length}`, {
12189
+ projectUuid,
12190
+ batchId: batch.batchId,
12191
+ moduleCount: batch.modules.length,
12192
+ fileCount: batch.totalFiles
12193
+ });
12194
+ try {
12195
+ const batchResult = await this.processBatch(projectUuid, batch);
12196
+ totalProcessedFiles += batchResult.processedFiles;
12197
+ totalServiceInterfaces += batchResult.serviceInterfaceCount;
12198
+ totalDesignDetails += batchResult.designDetailCount;
12199
+ totalTableInfo += batchResult.tableInfoCount;
12200
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12201
+ processedModules: (i + 1) * this.config.maxModulesPerChapter,
12202
+ processedFiles: totalProcessedFiles,
12203
+ status: "generating"
12204
+ });
12205
+ } catch (error) {
12206
+ logger.error(`\u{6279}\u{6B21}\u{5904}\u{7406}\u{5931}\u{8D25}: ${batch.batchId}`, {
12207
+ error: error.message
12208
+ });
12209
+ }
12210
+ }
12211
+ await this.generateProjectOverviewChapter(projectUuid, techSpecProject, projectAnalysis);
12212
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12213
+ status: "completed",
12214
+ completedAt: new Date(),
12215
+ processedModules: filteredModules.length,
12216
+ processedFiles: totalProcessedFiles
12217
+ });
12218
+ const processingTime = Date.now() - startTime;
12219
+ logger.info(`\u{9879}\u{76EE}\u{5904}\u{7406}\u{5B8C}\u{6210}`, {
12220
+ projectUuid,
12221
+ processingTime,
12222
+ totalModules: filteredModules.length,
12223
+ totalFiles: totalProcessedFiles
12224
+ });
12225
+ return {
12226
+ success: true,
12227
+ projectUuid,
12228
+ totalChapters: batches.length + 1,
12229
+ processedChapters: batches.length + 1,
12230
+ totalModules: filteredModules.length,
12231
+ processedModules: filteredModules.length,
12232
+ totalFiles: projectAnalysis.allFiles.length,
12233
+ processedFiles: totalProcessedFiles,
12234
+ statistics: {
12235
+ serviceInterfaceCount: totalServiceInterfaces,
12236
+ designDetailCount: totalDesignDetails,
12237
+ tableInfoCount: totalTableInfo,
12238
+ processingTime
12239
+ }
12240
+ };
12241
+ } catch (error) {
12242
+ logger.error(`\u{9879}\u{76EE}\u{5904}\u{7406}\u{5931}\u{8D25}: ${projectAnalysis.projectName}`, {
12243
+ error: error.message,
12244
+ projectUuid
12245
+ });
12246
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12247
+ status: "failed"
12248
+ });
12249
+ return {
12250
+ success: false,
12251
+ projectUuid,
12252
+ totalChapters: 0,
12253
+ processedChapters: 0,
12254
+ totalModules: 0,
12255
+ processedModules: 0,
12256
+ totalFiles: 0,
12257
+ processedFiles: 0,
12258
+ errorMessage: error.message,
12259
+ statistics: {
12260
+ serviceInterfaceCount: 0,
12261
+ designDetailCount: 0,
12262
+ tableInfoCount: 0,
12263
+ processingTime: Date.now() - startTime
12264
+ }
12265
+ };
12266
+ }
12267
+ }
12268
+ async createProjectRecord(projectUuid, projectAnalysis, options) {
12269
+ const projectName = options.projectName || projectAnalysis.projectName;
12270
+ const moduleName = options.moduleName || projectName;
12271
+ return await techSpecDbOperations.createTechSpecProject({
12272
+ uuid: projectUuid,
12273
+ projectName,
12274
+ moduleName,
12275
+ projectPath: projectAnalysis.projectPath,
12276
+ totalModules: projectAnalysis.modules.length,
12277
+ totalFiles: projectAnalysis.allFiles.length
12278
+ });
12279
+ }
12280
+ filterModules(modules, options) {
12281
+ let filteredModules = [
12282
+ ...modules
12283
+ ];
12284
+ if (options.includeModules && options.includeModules.length > 0) filteredModules = filteredModules.filter((m)=>options.includeModules.includes(m.name));
12285
+ if (options.excludeModules && options.excludeModules.length > 0) filteredModules = filteredModules.filter((m)=>!options.excludeModules.includes(m.name));
12286
+ if (this.config.skipEmptyModules) filteredModules = filteredModules.filter((m)=>m.files.length > 0);
12287
+ return filteredModules;
12288
+ }
12289
+ createModuleBatches(modules) {
12290
+ const batches = [];
12291
+ let currentBatch = [];
12292
+ let currentFileCount = 0;
12293
+ for (const module of modules){
12294
+ if (currentBatch.length >= this.config.maxModulesPerChapter || currentFileCount + module.files.length > this.config.maxFilesPerBatch) {
12295
+ if (currentBatch.length > 0) batches.push({
12296
+ batchId: (0, external_crypto_namespaceObject.randomUUID)(),
12297
+ modules: [
12298
+ ...currentBatch
12299
+ ],
12300
+ totalFiles: currentFileCount,
12301
+ chapterType: this.determineChapterType(currentBatch)
12302
+ });
12303
+ currentBatch = [];
12304
+ currentFileCount = 0;
12305
+ }
12306
+ currentBatch.push(module);
12307
+ currentFileCount += module.files.length;
12308
+ }
12309
+ if (currentBatch.length > 0) batches.push({
12310
+ batchId: (0, external_crypto_namespaceObject.randomUUID)(),
12311
+ modules: currentBatch,
12312
+ totalFiles: currentFileCount,
12313
+ chapterType: this.determineChapterType(currentBatch)
12314
+ });
12315
+ return batches;
12316
+ }
12317
+ determineChapterType(modules) {
12318
+ const fileTypes = modules.flatMap((m)=>m.files.map((f)=>f.type));
12319
+ if (fileTypes.includes("controller") || fileTypes.includes("service")) return "service_interface_list";
12320
+ if (fileTypes.includes("entity")) return "database_design";
12321
+ return "interface_design";
12322
+ }
12323
+ async processBatch(projectUuid, batch) {
12324
+ logger.info(`\u{5F00}\u{59CB}\u{5904}\u{7406}\u{6279}\u{6B21}: ${batch.batchId}`, {
12325
+ moduleCount: batch.modules.length,
12326
+ fileCount: batch.totalFiles
12327
+ });
12328
+ const moduleStatuses = [];
12329
+ for (const module of batch.modules){
12330
+ const moduleUuid = (0, external_crypto_namespaceObject.randomUUID)();
12331
+ const moduleStatus = await techSpecDbOperations.createModuleProcessStatus({
12332
+ uuid: moduleUuid,
12333
+ projectUuid,
12334
+ moduleName: module.name,
12335
+ moduleType: this.detectModuleType(module),
12336
+ totalFiles: module.files.length
12337
+ });
12338
+ moduleStatuses.push(moduleStatus);
12339
+ }
12340
+ const batchAnalysis = {
12341
+ projectName: `batch-${batch.batchId}`,
12342
+ projectPath: "",
12343
+ modules: batch.modules,
12344
+ allFiles: batch.modules.flatMap((m)=>m.files),
12345
+ statistics: {
12346
+ totalFiles: batch.totalFiles,
12347
+ controllerFiles: 0,
12348
+ serviceFiles: 0,
12349
+ entityFiles: 0,
12350
+ dtoFiles: 0,
12351
+ moduleCount: batch.modules.length
12352
+ }
12353
+ };
12354
+ const codeAnalysis = await analyzeProjectCode(batchAnalysis, {
12355
+ includeFileTypes: [
12356
+ "controller",
12357
+ "service",
12358
+ "entity",
12359
+ "dto"
12360
+ ]
12361
+ });
12362
+ await this.storeChapterContent(projectUuid, batch, codeAnalysis);
12363
+ for (const moduleStatus of moduleStatuses)await techSpecDbOperations.updateModuleProcessStatus(moduleStatus.uuid, {
12364
+ status: "completed",
12365
+ processedFiles: moduleStatus.totalFiles,
12366
+ completedAt: new Date()
12367
+ });
12368
+ return {
12369
+ processedFiles: batch.totalFiles,
12370
+ serviceInterfaceCount: codeAnalysis.serviceInterfaceList.length,
12371
+ designDetailCount: codeAnalysis.designDetailList.length,
12372
+ tableInfoCount: codeAnalysis.tableInfoList.length
12373
+ };
12374
+ }
12375
+ detectModuleType(module) {
12376
+ const javaFiles = module.files.filter((f)=>f.path.endsWith(".java"));
12377
+ const tsFiles = module.files.filter((f)=>f.path.endsWith(".ts") || f.path.endsWith(".js"));
12378
+ if (javaFiles.length > tsFiles.length) return "java";
12379
+ if (tsFiles.length > 0) return "typescript";
12380
+ return "other";
12381
+ }
12382
+ async storeChapterContent(projectUuid, batch, codeAnalysis) {
12383
+ const moduleNames = batch.modules.map((m)=>m.name).join(", ");
12384
+ if (codeAnalysis.serviceInterfaceList.length > 0) await techSpecDbOperations.createTechSpecChapter({
12385
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12386
+ projectUuid,
12387
+ chapterType: "service_interface_list",
12388
+ chapterTitle: `\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{6E05}\u{5355} - ${moduleNames}`,
12389
+ chapterOrder: this.getChapterOrder("service_interface_list"),
12390
+ content: JSON.stringify(codeAnalysis.serviceInterfaceList),
12391
+ moduleSource: moduleNames,
12392
+ fileCount: batch.totalFiles
12393
+ });
12394
+ if (codeAnalysis.designDetailList.length > 0) await techSpecDbOperations.createTechSpecChapter({
12395
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12396
+ projectUuid,
12397
+ chapterType: "interface_design",
12398
+ chapterTitle: `\u{63A5}\u{53E3}\u{8BBE}\u{8BA1}\u{8BE6}\u{60C5} - ${moduleNames}`,
12399
+ chapterOrder: this.getChapterOrder("interface_design"),
12400
+ content: JSON.stringify(codeAnalysis.designDetailList),
12401
+ moduleSource: moduleNames,
12402
+ fileCount: batch.totalFiles
12403
+ });
12404
+ if (codeAnalysis.tableInfoList.length > 0) await techSpecDbOperations.createTechSpecChapter({
12405
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12406
+ projectUuid,
12407
+ chapterType: "database_design",
12408
+ chapterTitle: `\u{6570}\u{636E}\u{5E93}\u{8BBE}\u{8BA1} - ${moduleNames}`,
12409
+ chapterOrder: this.getChapterOrder("database_design"),
12410
+ content: JSON.stringify(codeAnalysis.tableInfoList),
12411
+ moduleSource: moduleNames,
12412
+ fileCount: batch.totalFiles
12413
+ });
12414
+ const techSolutionContent = this.generateTechSolutionContent(batch, codeAnalysis);
12415
+ if (techSolutionContent) await techSpecDbOperations.createTechSpecChapter({
12416
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12417
+ projectUuid,
12418
+ chapterType: "tech_solution",
12419
+ chapterTitle: `\u{6280}\u{672F}\u{65B9}\u{6848} - ${moduleNames}`,
12420
+ chapterOrder: this.getChapterOrder("tech_solution"),
12421
+ content: JSON.stringify(techSolutionContent),
12422
+ moduleSource: moduleNames,
12423
+ fileCount: batch.totalFiles
12424
+ });
12425
+ const businessExceptionContent = this.generateBusinessExceptionContent(batch, codeAnalysis);
12426
+ if (businessExceptionContent) await techSpecDbOperations.createTechSpecChapter({
12427
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12428
+ projectUuid,
12429
+ chapterType: "business_exception",
12430
+ chapterTitle: `\u{4E1A}\u{52A1}\u{5F02}\u{5E38}\u{5904}\u{7406} - ${moduleNames}`,
12431
+ chapterOrder: this.getChapterOrder("business_exception"),
12432
+ content: JSON.stringify(businessExceptionContent),
12433
+ moduleSource: moduleNames,
12434
+ fileCount: batch.totalFiles
12435
+ });
12436
+ }
12437
+ getChapterOrder(chapterType) {
12438
+ const orderMap = {
12439
+ project_overview: 1,
12440
+ service_interface_list: 2,
12441
+ interface_design: 3,
12442
+ database_design: 4,
12443
+ tech_solution: 5,
12444
+ business_exception: 6,
12445
+ appendix: 7
12446
+ };
12447
+ return orderMap[chapterType] || 99;
12448
+ }
12449
+ generateTechSolutionContent(batch, codeAnalysis) {
12450
+ const moduleNames = batch.modules.map((m)=>m.name).join(", ");
12451
+ return {
12452
+ content: `
12453
+ ## ${moduleNames} \u{6A21}\u{5757}\u{6280}\u{672F}\u{65B9}\u{6848}
12454
+
12455
+ ### \u{6D41}\u{91CF}\u{4F30}\u{7B97}
12456
+ \u{6839}\u{636E} ${moduleNames} \u{6A21}\u{5757}\u{7684} ${codeAnalysis.serviceInterfaceList.length} \u{4E2A}\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{FF0C}\u{9884}\u{4F30}\u{65E5}\u{5747}\u{8BF7}\u{6C42}\u{91CF}\u{3002}
12457
+
12458
+ ### \u{6280}\u{672F}\u{67B6}\u{6784}
12459
+ \u{6A21}\u{5757}\u{5305}\u{542B} ${codeAnalysis.tableInfoList.length} \u{4E2A}\u{6570}\u{636E}\u{8868}\u{FF0C}${codeAnalysis.designDetailList.length} \u{4E2A}\u{63A5}\u{53E3}\u{8BBE}\u{8BA1}\u{3002}
12460
+
12461
+ ### \u{4E2D}\u{95F4}\u{4EF6}\u{9009}\u{578B}
12462
+ \u{57FA}\u{4E8E}\u{6A21}\u{5757}\u{7279}\u{70B9}\u{FF0C}\u{5EFA}\u{8BAE}\u{4F7F}\u{7528}\u{76F8}\u{5E94}\u{7684}\u{4E2D}\u{95F4}\u{4EF6}\u{8FDB}\u{884C}\u{652F}\u{6491}\u{3002}
12463
+ `.trim()
12464
+ };
12465
+ }
12466
+ generateBusinessExceptionContent(batch, codeAnalysis) {
12467
+ const moduleNames = batch.modules.map((m)=>m.name).join(", ");
12468
+ return {
12469
+ content: `
12470
+ ## ${moduleNames} \u{6A21}\u{5757}\u{4E1A}\u{52A1}\u{5F02}\u{5E38}\u{5904}\u{7406}
12471
+
12472
+ ### \u{5F02}\u{5E38}\u{5206}\u{7C7B}
12473
+ \u{6A21}\u{5757}\u{4E2D}\u{7684}\u{4E1A}\u{52A1}\u{5F02}\u{5E38}\u{6309}\u{7167}\u{7EDF}\u{4E00}\u{7684}\u{5206}\u{7C7B}\u{6807}\u{51C6}\u{8FDB}\u{884C}\u{5904}\u{7406}\u{3002}
12474
+
12475
+ ### \u{5F02}\u{5E38}\u{5904}\u{7406}\u{7B56}\u{7565}
12476
+ \u{9488}\u{5BF9} ${codeAnalysis.serviceInterfaceList.length} \u{4E2A}\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{7684}\u{5F02}\u{5E38}\u{5904}\u{7406}\u{7B56}\u{7565}\u{3002}
12477
+
12478
+ ### \u{5F02}\u{5E38}\u{7801}\u{5B9A}\u{4E49}
12479
+ \u{6A21}\u{5757}\u{76F8}\u{5173}\u{7684}\u{5F02}\u{5E38}\u{7801}\u{5B9A}\u{4E49}\u{548C}\u{5904}\u{7406}\u{89C4}\u{8303}\u{3002}
12480
+ `.trim()
12481
+ };
12482
+ }
12483
+ async generateProjectOverviewChapter(projectUuid, project, projectAnalysis) {
12484
+ const statistics = await techSpecDbOperations.getProjectStatistics(projectUuid);
12485
+ const overviewContent = {
12486
+ projectName: project.projectName,
12487
+ moduleName: project.moduleName,
12488
+ projectPath: project.projectPath,
12489
+ totalModules: statistics.totalModules,
12490
+ totalFiles: statistics.totalFiles,
12491
+ summary: `
12492
+ \u{9879}\u{76EE} ${project.projectName} \u{5305}\u{542B} ${statistics.totalModules} \u{4E2A}\u{4E1A}\u{52A1}\u{6A21}\u{5757}\u{FF0C}
12493
+ \u{5171}\u{8BA1} ${statistics.totalFiles} \u{4E2A}\u{4EE3}\u{7801}\u{6587}\u{4EF6}\u{3002}
12494
+
12495
+ \u{901A}\u{8FC7}\u{81EA}\u{52A8}\u{5316}\u{5206}\u{6790}\u{FF0C}\u{751F}\u{6210}\u{4E86} ${statistics.totalChapters} \u{4E2A}\u{6280}\u{672F}\u{89C4}\u{683C}\u{7AE0}\u{8282}\u{FF0C}
12496
+ \u{5305}\u{62EC}\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{6E05}\u{5355}\u{3001}\u{63A5}\u{53E3}\u{8BBE}\u{8BA1}\u{8BE6}\u{60C5}\u{3001}\u{6570}\u{636E}\u{5E93}\u{8BBE}\u{8BA1}\u{7B49}\u{5B8C}\u{6574}\u{5185}\u{5BB9}\u{3002}
12497
+ `.trim(),
12498
+ moduleList: projectAnalysis.modules.map((m)=>({
12499
+ name: m.name,
12500
+ path: m.relativePath,
12501
+ fileCount: m.files.length
12502
+ }))
12503
+ };
12504
+ await techSpecDbOperations.createTechSpecChapter({
12505
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12506
+ projectUuid,
12507
+ chapterType: "project_overview",
12508
+ chapterTitle: "\u9879\u76EE\u6982\u8FF0",
12509
+ chapterOrder: this.getChapterOrder("project_overview"),
12510
+ content: JSON.stringify(overviewContent),
12511
+ moduleSource: "all",
12512
+ fileCount: projectAnalysis.allFiles.length
12513
+ });
12514
+ }
12515
+ async processMultiProjects(config) {
12516
+ const startTime = Date.now();
12517
+ const projectUuid = (0, external_crypto_namespaceObject.randomUUID)();
12518
+ logger.info(`\u{5F00}\u{59CB}\u{5904}\u{7406}\u{591A}\u{9879}\u{76EE}\u{5408}\u{5E76}: ${config.mergedProjectName}`, {
12519
+ projectUuid,
12520
+ projectCount: config.projectPaths.length,
12521
+ projectPaths: config.projectPaths
12522
+ });
12523
+ const projectResults = [];
12524
+ let totalServiceInterfaces = 0;
12525
+ let totalDesignDetails = 0;
12526
+ let totalTables = 0;
12527
+ let totalFiles = 0;
12528
+ let processedProjects = 0;
12529
+ try {
12530
+ await techSpecDbOperations.createTechSpecProject({
12531
+ uuid: projectUuid,
12532
+ projectName: config.mergedProjectName,
12533
+ moduleName: config.mergedModuleName || config.mergedProjectName,
12534
+ projectPath: config.projectPaths.join(";"),
12535
+ totalModules: config.projectPaths.length,
12536
+ totalFiles: 0
12537
+ });
12538
+ for(let i = 0; i < config.projectPaths.length; i++){
12539
+ const projectPath = config.projectPaths[i];
12540
+ const projectAlias = config.projectAliases?.[projectPath];
12541
+ logger.info(`\u{5206}\u{6790}\u{5FAE}\u{670D}\u{52A1}\u{9879}\u{76EE} ${i + 1}/${config.projectPaths.length}: ${projectPath}`);
12542
+ try {
12543
+ const projectAnalysis = await analyzeProject(projectPath);
12544
+ const projectName = projectAlias || projectAnalysis.projectName;
12545
+ const codeAnalysis = await analyzeProjectCode(projectAnalysis, {
12546
+ includeFileTypes: [
12547
+ "controller",
12548
+ "service",
12549
+ "entity",
12550
+ "dto"
12551
+ ]
12552
+ });
12553
+ const prefixedData = this.addProjectPrefix(codeAnalysis, projectName, false !== config.addProjectPrefix);
12554
+ await this.storeMultiProjectChapterContent(projectUuid, projectName, prefixedData, projectAnalysis.allFiles.length);
12555
+ totalServiceInterfaces += prefixedData.serviceInterfaceList.length;
12556
+ totalDesignDetails += prefixedData.designDetailList.length;
12557
+ totalTables += prefixedData.tableInfoList.length;
12558
+ totalFiles += projectAnalysis.allFiles.length;
12559
+ processedProjects++;
12560
+ projectResults.push({
12561
+ projectPath,
12562
+ projectName,
12563
+ success: true,
12564
+ statistics: {
12565
+ serviceInterfaceCount: prefixedData.serviceInterfaceList.length,
12566
+ designDetailCount: prefixedData.designDetailList.length,
12567
+ tableInfoCount: prefixedData.tableInfoList.length
12568
+ }
12569
+ });
12570
+ logger.info(`\u{5FAE}\u{670D}\u{52A1}\u{9879}\u{76EE}\u{5206}\u{6790}\u{5B8C}\u{6210}: ${projectName}`, {
12571
+ serviceInterfaces: prefixedData.serviceInterfaceList.length,
12572
+ designDetails: prefixedData.designDetailList.length,
12573
+ tables: prefixedData.tableInfoList.length
12574
+ });
12575
+ } catch (error) {
12576
+ logger.error(`\u{5FAE}\u{670D}\u{52A1}\u{9879}\u{76EE}\u{5206}\u{6790}\u{5931}\u{8D25}: ${projectPath}`, {
12577
+ error: error.message
12578
+ });
12579
+ projectResults.push({
12580
+ projectPath,
12581
+ projectName: projectAlias || projectPath.split("/").pop() || projectPath,
12582
+ success: false,
12583
+ errorMessage: error.message,
12584
+ statistics: {
12585
+ serviceInterfaceCount: 0,
12586
+ designDetailCount: 0,
12587
+ tableInfoCount: 0
12588
+ }
12589
+ });
12590
+ }
12591
+ }
12592
+ await this.generateMultiProjectOverviewChapter(projectUuid, config, projectResults, {
12593
+ totalServiceInterfaces,
12594
+ totalDesignDetails,
12595
+ totalTables,
12596
+ totalFiles
12597
+ });
12598
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12599
+ status: "completed",
12600
+ completedAt: new Date(),
12601
+ totalFiles,
12602
+ processedFiles: totalFiles,
12603
+ processedModules: processedProjects
12604
+ });
12605
+ const processingTime = Date.now() - startTime;
12606
+ logger.info(`\u{591A}\u{9879}\u{76EE}\u{5408}\u{5E76}\u{5904}\u{7406}\u{5B8C}\u{6210}`, {
12607
+ projectUuid,
12608
+ processingTime,
12609
+ totalProjects: config.projectPaths.length,
12610
+ processedProjects,
12611
+ totalServiceInterfaces,
12612
+ totalDesignDetails,
12613
+ totalTables
12614
+ });
12615
+ return {
12616
+ success: true,
12617
+ projectUuid,
12618
+ mergedProjectName: config.mergedProjectName,
12619
+ totalProjects: config.projectPaths.length,
12620
+ processedProjects,
12621
+ projectResults,
12622
+ mergedStatistics: {
12623
+ totalServiceInterfaces,
12624
+ totalDesignDetails,
12625
+ totalTables,
12626
+ totalFiles,
12627
+ processingTime
12628
+ }
12629
+ };
12630
+ } catch (error) {
12631
+ logger.error(`\u{591A}\u{9879}\u{76EE}\u{5408}\u{5E76}\u{5904}\u{7406}\u{5931}\u{8D25}`, {
12632
+ error: error.message,
12633
+ projectUuid
12634
+ });
12635
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12636
+ status: "failed"
12637
+ });
12638
+ return {
12639
+ success: false,
12640
+ projectUuid,
12641
+ mergedProjectName: config.mergedProjectName,
12642
+ totalProjects: config.projectPaths.length,
12643
+ processedProjects,
12644
+ projectResults,
12645
+ mergedStatistics: {
12646
+ totalServiceInterfaces,
12647
+ totalDesignDetails,
12648
+ totalTables,
12649
+ totalFiles,
12650
+ processingTime: Date.now() - startTime
12651
+ },
12652
+ errorMessage: error.message
12653
+ };
12654
+ }
12655
+ }
12656
+ addProjectPrefix(codeAnalysis, projectName, addPrefix) {
12657
+ if (!addPrefix) return codeAnalysis;
12658
+ const prefix = `[${projectName}]`;
12659
+ return {
12660
+ serviceInterfaceList: codeAnalysis.serviceInterfaceList.map((item)=>({
12661
+ ...item,
12662
+ serviceChineseName: `${prefix} ${item.serviceChineseName || item.serviceEnglishName}`,
12663
+ serviceDescription: `${prefix} ${item.serviceDescription || ""}`,
12664
+ _sourceProject: projectName
12665
+ })),
12666
+ designDetailList: codeAnalysis.designDetailList.map((item)=>({
12667
+ ...item,
12668
+ serviceName: `${prefix} ${item.serviceName}`,
12669
+ serviceDesc: `${prefix} ${item.serviceDesc || ""}`,
12670
+ _sourceProject: projectName
12671
+ })),
12672
+ tableInfoList: codeAnalysis.tableInfoList.map((item)=>({
12673
+ ...item,
12674
+ tableDescription: `${prefix} ${item.tableDescription || item.tableName}`,
12675
+ _sourceProject: projectName
12676
+ }))
12677
+ };
12678
+ }
12679
+ async storeMultiProjectChapterContent(projectUuid, projectName, codeAnalysis, fileCount) {
12680
+ if (codeAnalysis.serviceInterfaceList.length > 0) await techSpecDbOperations.createTechSpecChapter({
12681
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12682
+ projectUuid,
12683
+ chapterType: "service_interface_list",
12684
+ chapterTitle: `\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{6E05}\u{5355} - ${projectName}`,
12685
+ chapterOrder: this.getChapterOrder("service_interface_list"),
12686
+ content: JSON.stringify(codeAnalysis.serviceInterfaceList),
12687
+ moduleSource: projectName,
12688
+ fileCount
12689
+ });
12690
+ if (codeAnalysis.designDetailList.length > 0) await techSpecDbOperations.createTechSpecChapter({
12691
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12692
+ projectUuid,
12693
+ chapterType: "interface_design",
12694
+ chapterTitle: `\u{63A5}\u{53E3}\u{8BBE}\u{8BA1}\u{8BE6}\u{60C5} - ${projectName}`,
12695
+ chapterOrder: this.getChapterOrder("interface_design"),
12696
+ content: JSON.stringify(codeAnalysis.designDetailList),
12697
+ moduleSource: projectName,
12698
+ fileCount
12699
+ });
12700
+ if (codeAnalysis.tableInfoList.length > 0) await techSpecDbOperations.createTechSpecChapter({
12701
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12702
+ projectUuid,
12703
+ chapterType: "database_design",
12704
+ chapterTitle: `\u{6570}\u{636E}\u{5E93}\u{8BBE}\u{8BA1} - ${projectName}`,
12705
+ chapterOrder: this.getChapterOrder("database_design"),
12706
+ content: JSON.stringify(codeAnalysis.tableInfoList),
12707
+ moduleSource: projectName,
12708
+ fileCount
12709
+ });
12710
+ const techSolutionContent = {
12711
+ content: `
12712
+ ## ${projectName} \u{5FAE}\u{670D}\u{52A1}\u{6280}\u{672F}\u{65B9}\u{6848}
12713
+
12714
+ ### \u{670D}\u{52A1}\u{6982}\u{8FF0}
12715
+ ${projectName} \u{5FAE}\u{670D}\u{52A1}\u{5305}\u{542B} ${codeAnalysis.serviceInterfaceList.length} \u{4E2A}\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{FF0C}${codeAnalysis.tableInfoList.length} \u{4E2A}\u{6570}\u{636E}\u{8868}\u{3002}
12716
+
12717
+ ### \u{6280}\u{672F}\u{67B6}\u{6784}
12718
+ \u{57FA}\u{4E8E}\u{5FAE}\u{670D}\u{52A1}\u{67B6}\u{6784}\u{8BBE}\u{8BA1}\u{FF0C}\u{63D0}\u{4F9B}\u{72EC}\u{7ACB}\u{7684}\u{4E1A}\u{52A1}\u{529F}\u{80FD}\u{6A21}\u{5757}\u{3002}
12719
+
12720
+ ### \u{63A5}\u{53E3}\u{8BBE}\u{8BA1}
12721
+ \u{5171}\u{8BBE}\u{8BA1} ${codeAnalysis.designDetailList.length} \u{4E2A}\u{63A5}\u{53E3}\u{FF0C}\u{652F}\u{6301}\u{670D}\u{52A1}\u{95F4}\u{8C03}\u{7528}\u{548C}\u{5916}\u{90E8}\u{8BBF}\u{95EE}\u{3002}
12722
+ `.trim()
12723
+ };
12724
+ await techSpecDbOperations.createTechSpecChapter({
12725
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12726
+ projectUuid,
12727
+ chapterType: "tech_solution",
12728
+ chapterTitle: `\u{6280}\u{672F}\u{65B9}\u{6848} - ${projectName}`,
12729
+ chapterOrder: this.getChapterOrder("tech_solution"),
12730
+ content: JSON.stringify(techSolutionContent),
12731
+ moduleSource: projectName,
12732
+ fileCount
12733
+ });
12734
+ }
12735
+ async generateMultiProjectOverviewChapter(projectUuid, config, projectResults, statistics) {
12736
+ const successfulProjects = projectResults.filter((p)=>p.success);
12737
+ const failedProjects = projectResults.filter((p)=>!p.success);
12738
+ const overviewContent = {
12739
+ projectName: config.mergedProjectName,
12740
+ moduleName: config.mergedModuleName || config.mergedProjectName,
12741
+ isMultiProject: true,
12742
+ totalProjects: config.projectPaths.length,
12743
+ successfulProjects: successfulProjects.length,
12744
+ failedProjects: failedProjects.length,
12745
+ summary: `
12746
+ \u{672C}\u{6280}\u{672F}\u{89C4}\u{683C}\u{8BF4}\u{660E}\u{4E66}\u{6DB5}\u{76D6} ${config.mergedProjectName} \u{9879}\u{76EE}\u{7FA4}\u{FF0C}\u{5305}\u{542B} ${successfulProjects.length} \u{4E2A}\u{5FAE}\u{670D}\u{52A1}\u{9879}\u{76EE}\u{3002}
12747
+
12748
+ ### \u{9879}\u{76EE}\u{6982}\u{89C8}
12749
+ - \u{5FAE}\u{670D}\u{52A1}\u{6570}\u{91CF}\u{FF1A}${successfulProjects.length} \u{4E2A}
12750
+ - \u{670D}\u{52A1}\u{63A5}\u{53E3}\u{603B}\u{6570}\u{FF1A}${statistics.totalServiceInterfaces} \u{4E2A}
12751
+ - \u{63A5}\u{53E3}\u{8BBE}\u{8BA1}\u{603B}\u{6570}\u{FF1A}${statistics.totalDesignDetails} \u{4E2A}
12752
+ - \u{6570}\u{636E}\u{8868}\u{603B}\u{6570}\u{FF1A}${statistics.totalTables} \u{4E2A}
12753
+ - \u{4EE3}\u{7801}\u{6587}\u{4EF6}\u{603B}\u{6570}\u{FF1A}${statistics.totalFiles} \u{4E2A}
12754
+
12755
+ ### \u{5FAE}\u{670D}\u{52A1}\u{5217}\u{8868}
12756
+ ${successfulProjects.map((p, i)=>`${i + 1}. **${p.projectName}**
12757
+ - \u{670D}\u{52A1}\u{63A5}\u{53E3}\u{FF1A}${p.statistics.serviceInterfaceCount} \u{4E2A}
12758
+ - \u{63A5}\u{53E3}\u{8BBE}\u{8BA1}\u{FF1A}${p.statistics.designDetailCount} \u{4E2A}
12759
+ - \u{6570}\u{636E}\u{8868}\u{FF1A}${p.statistics.tableInfoCount} \u{4E2A}`).join("\n")}
12760
+
12761
+ ${failedProjects.length > 0 ? `
12762
+ ### \u{5206}\u{6790}\u{5931}\u{8D25}\u{7684}\u{9879}\u{76EE}
12763
+ ${failedProjects.map((p)=>`- ${p.projectName}: ${p.errorMessage}`).join("\n")}
12764
+ ` : ""}
12765
+ `.trim(),
12766
+ projectList: projectResults.map((p)=>({
12767
+ projectPath: p.projectPath,
12768
+ projectName: p.projectName,
12769
+ success: p.success,
12770
+ errorMessage: p.errorMessage,
12771
+ statistics: p.statistics
12772
+ })),
12773
+ mergedStatistics: statistics
12774
+ };
12775
+ await techSpecDbOperations.createTechSpecChapter({
12776
+ uuid: (0, external_crypto_namespaceObject.randomUUID)(),
12777
+ projectUuid,
12778
+ chapterType: "project_overview",
12779
+ chapterTitle: "\u9879\u76EE\u6982\u8FF0",
12780
+ chapterOrder: this.getChapterOrder("project_overview"),
12781
+ content: JSON.stringify(overviewContent),
12782
+ moduleSource: "all",
12783
+ fileCount: statistics.totalFiles
12784
+ });
12785
+ }
12786
+ }
12787
+ const DEFAULT_MERGE_CONFIG = {
12788
+ includeEmptyChapters: false,
12789
+ generateDefaultContent: true,
12790
+ maxItemsPerChapter: 1000,
12791
+ enableChapterSummary: true
12792
+ };
12793
+ class DocumentMerger {
12794
+ config;
12795
+ constructor(config = {}){
12796
+ this.config = {
12797
+ ...DEFAULT_MERGE_CONFIG,
12798
+ ...config
12799
+ };
12800
+ }
12801
+ async mergeAndRenderDocument(projectUuid, outputDir) {
12802
+ const startTime = Date.now();
12803
+ try {
12804
+ const project = await techSpecDbOperations.getTechSpecProject(projectUuid);
12805
+ if (!project) throw new Error(`\u{9879}\u{76EE}\u{4E0D}\u{5B58}\u{5728}: ${projectUuid}`);
12806
+ const statistics = await techSpecDbOperations.getProjectStatistics(projectUuid);
12807
+ const techSpecData = await this.mergeAllChapters(projectUuid, project);
12808
+ let finalOutputDir = outputDir;
12809
+ if (!finalOutputDir) {
12810
+ const projectPath = project.projectPath;
12811
+ const firstProjectPath = projectPath.includes(";") ? projectPath.split(";")[0].trim() : projectPath;
12812
+ finalOutputDir = getTechSpecOutputDir(firstProjectPath);
12813
+ }
12814
+ const renderResult = await renderTechSpecDoc(techSpecData, finalOutputDir);
12815
+ if (!renderResult.success) throw new Error(renderResult.error || "\u6587\u6863\u6E32\u67D3\u5931\u8D25");
12816
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12817
+ status: "completed",
12818
+ completedAt: new Date()
12819
+ });
12820
+ return {
12821
+ success: true,
12822
+ outputPath: renderResult.outputPath,
12823
+ techSpecData,
12824
+ statistics: {
12825
+ totalChapters: statistics.totalChapters,
12826
+ mergedChapters: statistics.completedChapters,
12827
+ totalServiceInterfaces: techSpecData.serviceInterfaceList.length,
12828
+ totalDesignDetails: techSpecData.designDetailList.length,
12829
+ totalTables: techSpecData.tableInfoList.length,
12830
+ processingTime: Date.now() - startTime
12831
+ }
12832
+ };
12833
+ } catch (error) {
12834
+ await techSpecDbOperations.updateTechSpecProject(projectUuid, {
12835
+ status: "failed"
12836
+ });
12837
+ return {
12838
+ success: false,
12839
+ statistics: {
12840
+ totalChapters: 0,
12841
+ mergedChapters: 0,
12842
+ totalServiceInterfaces: 0,
12843
+ totalDesignDetails: 0,
12844
+ totalTables: 0,
12845
+ processingTime: Date.now() - startTime
12846
+ },
12847
+ errorMessage: error.message
12848
+ };
12849
+ }
12850
+ }
12851
+ async mergeAllChapters(projectUuid, project) {
12852
+ const now = new Date();
12853
+ const isMultiProject = project.projectPath?.includes(";");
12854
+ const techSpecData = {
12855
+ projectName: project.projectName,
12856
+ moduleName: project.moduleName,
12857
+ year: String(now.getFullYear()),
12858
+ month: String(now.getMonth() + 1).padStart(2, "0"),
12859
+ referDoc: isMultiProject ? "\u591A\u5FAE\u670D\u52A1\u9879\u76EE\u4EE3\u7801\u81EA\u52A8\u5206\u6790\u751F\u6210" : "\u9879\u76EE\u4EE3\u7801\u81EA\u52A8\u5206\u6790\u751F\u6210",
12860
+ serviceInterfaceList: [],
12861
+ designDetailList: [],
12862
+ tableInfoList: []
12863
+ };
12864
+ const serviceInterfaces = await techSpecDbOperations.mergeChaptersByType(projectUuid, "service_interface_list");
12865
+ if (serviceInterfaces && Array.isArray(serviceInterfaces)) techSpecData.serviceInterfaceList = this.limitAndReindex(serviceInterfaces, this.config.maxItemsPerChapter);
12866
+ const designDetails = await techSpecDbOperations.mergeChaptersByType(projectUuid, "interface_design");
12867
+ if (designDetails && Array.isArray(designDetails)) techSpecData.designDetailList = designDetails.slice(0, this.config.maxItemsPerChapter);
12868
+ const tableInfos = await techSpecDbOperations.mergeChaptersByType(projectUuid, "database_design");
12869
+ if (tableInfos && Array.isArray(tableInfos)) techSpecData.tableInfoList = this.limitAndReindex(tableInfos, this.config.maxItemsPerChapter);
12870
+ const techSolution = await techSpecDbOperations.mergeChaptersByType(projectUuid, "tech_solution");
12871
+ if (techSolution && techSolution.content) techSpecData.techSolution = {
12872
+ content: techSolution.content
12873
+ };
12874
+ else if (this.config.generateDefaultContent) techSpecData.techSolution = this.generateDefaultTechSolution(techSpecData);
12875
+ const businessException = await techSpecDbOperations.mergeChaptersByType(projectUuid, "business_exception");
12876
+ if (businessException && businessException.content) techSpecData.businessException = {
12877
+ content: businessException.content
12878
+ };
12879
+ else if (this.config.generateDefaultContent) techSpecData.businessException = this.generateDefaultBusinessException();
12880
+ const appendix = await techSpecDbOperations.mergeChaptersByType(projectUuid, "appendix");
12881
+ if (appendix) techSpecData.appendix = {
12882
+ exceptionCodeList: appendix.exceptionCodeList || [],
12883
+ codeValueList: appendix.codeValueList || []
12884
+ };
12885
+ else if (this.config.generateDefaultContent) techSpecData.appendix = this.generateDefaultAppendix();
12886
+ return techSpecData;
12887
+ }
12888
+ limitAndReindex(items, maxItems) {
12889
+ const limitedItems = items.slice(0, maxItems);
12890
+ return limitedItems.map((item, index)=>({
12891
+ ...item,
12892
+ id: String(index + 1)
12893
+ }));
12894
+ }
12895
+ generateDefaultTechSolution(techSpecData) {
12896
+ const serviceCount = techSpecData.serviceInterfaceList.length;
12897
+ const tableCount = techSpecData.tableInfoList.length;
12898
+ return {
12899
+ content: `\u{6839}\u{636E}\u{9879}\u{76EE}\u{89C4}\u{6A21}\u{5206}\u{6790}\u{FF0C}\u{7CFB}\u{7EDF}\u{5305}\u{542B} ${serviceCount} \u{4E2A}\u{670D}\u{52A1}\u{63A5}\u{53E3}\u{FF0C}${tableCount} \u{4E2A}\u{6570}\u{636E}\u{8868}\u{3002}\u{5EFA}\u{8BAE}\u{91C7}\u{7528}\u{5FAE}\u{670D}\u{52A1}\u{67B6}\u{6784}\u{3002}`
12900
+ };
12901
+ }
12902
+ generateDefaultBusinessException() {
12903
+ return {
12904
+ content: `\u{9879}\u{76EE}\u{91C7}\u{7528}\u{7EDF}\u{4E00}\u{5F02}\u{5E38}\u{5904}\u{7406}\u{673A}\u{5236}\u{FF0C}\u{5305}\u{62EC}\u{7CFB}\u{7EDF}\u{5F02}\u{5E38}\u{548C}\u{4E1A}\u{52A1}\u{5F02}\u{5E38}\u{4E24}\u{5927}\u{7C7B}\u{3002}`
12905
+ };
12906
+ }
12907
+ generateDefaultAppendix() {
12908
+ return {
12909
+ exceptionCodeList: [
12910
+ {
12911
+ id: "1",
12912
+ exceptionCode: "SYS0001",
12913
+ exceptionDescription: "\u7CFB\u7EDF\u5185\u90E8\u9519\u8BEF"
12914
+ }
12915
+ ],
12916
+ codeValueList: [
12917
+ {
12918
+ id: "1",
12919
+ propertyName: "\u7528\u6237\u72B6\u6001",
12920
+ codeValue: "1-\u6B63\u5E38\uFF0C0-\u7981\u7528"
12921
+ }
12922
+ ]
12923
+ };
12924
+ }
12925
+ async getProjectProgress(projectUuid) {
12926
+ const project = await techSpecDbOperations.getTechSpecProject(projectUuid);
12927
+ const statistics = await techSpecDbOperations.getProjectStatistics(projectUuid);
12928
+ if (!project) throw new Error(`\u{9879}\u{76EE}\u{4E0D}\u{5B58}\u{5728}: ${projectUuid}`);
12929
+ const totalSteps = 7;
12930
+ const completedSteps = statistics.completedChapters;
12931
+ const percentage = Math.round(completedSteps / totalSteps * 100);
12932
+ let currentStep = "\u51C6\u5907\u4E2D";
12933
+ if ("analyzing" === project.status) currentStep = "\u5206\u6790\u9879\u76EE\u7ED3\u6784";
12934
+ else if ("generating" === project.status) currentStep = `\u{751F}\u{6210}\u{7AE0}\u{8282}\u{5185}\u{5BB9} (${completedSteps}/${totalSteps})`;
12935
+ else if ("completed" === project.status) currentStep = "\u5DF2\u5B8C\u6210";
12936
+ else if ("failed" === project.status) currentStep = "\u5904\u7406\u5931\u8D25";
12937
+ return {
12938
+ project,
12939
+ statistics,
12940
+ progress: {
12941
+ totalSteps,
12942
+ completedSteps,
12943
+ currentStep,
12944
+ percentage
12945
+ }
12946
+ };
12947
+ }
12948
+ }
11119
12949
  const TableFieldDetailSchema = objectType({
11120
12950
  id: stringType().describe("\u5E8F\u53F7"),
11121
12951
  fieldName: stringType().describe("\u5B57\u6BB5\u540D"),
@@ -11614,65 +13444,392 @@ ${requirement_description}
11614
13444
  ]
11615
13445
  })
11616
13446
  };
11617
- const generateFinalDocumentTool = {
11618
- name: "generate_final_document",
11619
- description: "\u6574\u5408\u6240\u6709\u6536\u96C6\u7684\u4FE1\u606F\uFF0C\u751F\u6210\u5B8C\u6574\u7684\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66",
13447
+ const generateFinalDocumentTool = {
13448
+ name: "generate_final_document",
13449
+ description: "\u6574\u5408\u6240\u6709\u6536\u96C6\u7684\u4FE1\u606F\uFF0C\u751F\u6210\u5B8C\u6574\u7684\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66",
13450
+ inputSchema: {
13451
+ projectPath: stringType().min(1).describe(PROJECT_PATH_DESCRIPTION),
13452
+ techSpecData: objectType({
13453
+ projectName: stringType().describe("\u9879\u76EE\u540D\u79F0"),
13454
+ moduleName: stringType().describe("\u6A21\u5757\u540D\u79F0"),
13455
+ year: stringType().optional().describe("\u5E74\u4EFD\uFF0C\u9ED8\u8BA4\u5F53\u524D\u5E74\u4EFD"),
13456
+ month: stringType().optional().describe("\u6708\u4EFD\uFF0C\u9ED8\u8BA4\u5F53\u524D\u6708\u4EFD"),
13457
+ referDoc: stringType().optional().describe("\u53C2\u8003\u6587\u6863"),
13458
+ serviceInterfaceList: arrayType(ServiceInterfaceSchema).default([]).describe("\u670D\u52A1\u63A5\u53E3\u6E05\u5355"),
13459
+ designDetailList: arrayType(DesignDetailSchema).default([]).describe("\u670D\u52A1\u63A5\u53E3\u8BBE\u8BA1\u8BE6\u60C5"),
13460
+ tableInfoList: arrayType(TableInfoSchema).default([]).describe("\u6570\u636E\u5E93\u8868\u4FE1\u606F\u5217\u8868"),
13461
+ techSolution: TechSolutionSchema.optional().describe("\u6280\u672F\u65B9\u6848"),
13462
+ businessException: BusinessExceptionSchema.optional().describe("\u4E1A\u52A1\u5F02\u5E38\u5904\u7406"),
13463
+ appendix: AppendixSchema.optional().describe("\u9644\u5F55"),
13464
+ dtoDefinitions: recordType(stringType(), arrayType(objectType({
13465
+ name: stringType().optional().describe("\u5B57\u6BB5\u540D"),
13466
+ type: stringType().optional().describe("\u5B57\u6BB5\u7C7B\u578B"),
13467
+ required: stringType().optional().describe("\u662F\u5426\u5FC5\u586B"),
13468
+ description: stringType().optional().describe("\u5B57\u6BB5\u63CF\u8FF0")
13469
+ }))).optional().describe("DTO\u5B9A\u4E49\u6620\u5C04\uFF0C\u7528\u4E8E\u5C55\u5F00\u63A5\u53E3\u53C2\u6570\u4E2D\u7684DTO\u4E3A\u5177\u4F53\u5B57\u6BB5")
13470
+ }).describe("\u5B8C\u6574\u7684\u6280\u672F\u89C4\u683C\u6570\u636E")
13471
+ },
13472
+ handler: async (args)=>{
13473
+ try {
13474
+ const { projectPath } = args;
13475
+ let { techSpecData } = args;
13476
+ if (!projectPath || !techSpecData) return {
13477
+ content: [
13478
+ {
13479
+ type: "text",
13480
+ text: JSON.stringify({
13481
+ success: false,
13482
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570"
13483
+ })
13484
+ }
13485
+ ],
13486
+ isError: true
13487
+ };
13488
+ if ("string" == typeof techSpecData) techSpecData = JSON.parse(techSpecData);
13489
+ const now = new Date();
13490
+ const normalizedData = normalizeInputData(techSpecData);
13491
+ const data = {
13492
+ projectName: normalizedData.projectName || "\u672A\u547D\u540D\u9879\u76EE",
13493
+ moduleName: normalizedData.moduleName || "\u672A\u547D\u540D\u6A21\u5757",
13494
+ year: normalizedData.year || String(now.getFullYear()),
13495
+ month: normalizedData.month || String(now.getMonth() + 1).padStart(2, "0"),
13496
+ referDoc: normalizedData.referDoc || "\u65E0",
13497
+ serviceInterfaceList: normalizedData.serviceInterfaceList || [],
13498
+ designDetailList: normalizedData.designDetailList || [],
13499
+ tableInfoList: normalizedData.tableInfoList || [],
13500
+ techSolution: normalizedData.techSolution || void 0,
13501
+ businessException: normalizedData.businessException || void 0,
13502
+ appendix: normalizedData.appendix || void 0
13503
+ };
13504
+ const outputDir = getTechSpecOutputDir(projectPath);
13505
+ const result = await renderTechSpecDoc(data, outputDir);
13506
+ if (result.success) return {
13507
+ content: [
13508
+ {
13509
+ type: "text",
13510
+ text: JSON.stringify({
13511
+ success: true,
13512
+ message: "\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66\u751F\u6210\u6210\u529F",
13513
+ data: {
13514
+ outputPath: result.outputPath,
13515
+ projectName: data.projectName,
13516
+ moduleName: data.moduleName,
13517
+ statistics: {
13518
+ tableCount: data.tableInfoList.length,
13519
+ serviceCount: data.serviceInterfaceList.length,
13520
+ designCount: data.designDetailList.length,
13521
+ hasTechSolution: !!data.techSolution,
13522
+ hasBusinessException: !!data.businessException,
13523
+ hasAppendix: !!data.appendix
13524
+ }
13525
+ }
13526
+ })
13527
+ }
13528
+ ]
13529
+ };
13530
+ return {
13531
+ content: [
13532
+ {
13533
+ type: "text",
13534
+ text: JSON.stringify({
13535
+ success: false,
13536
+ message: result.error || "\u6587\u6863\u751F\u6210\u5931\u8D25"
13537
+ })
13538
+ }
13539
+ ],
13540
+ isError: true
13541
+ };
13542
+ } catch (error) {
13543
+ return {
13544
+ content: [
13545
+ {
13546
+ type: "text",
13547
+ text: JSON.stringify({
13548
+ success: false,
13549
+ message: error.message || "\u751F\u6210\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF"
13550
+ })
13551
+ }
13552
+ ],
13553
+ isError: true
13554
+ };
13555
+ }
13556
+ }
13557
+ };
13558
+ const analyzeLargeProjectTool = {
13559
+ name: "analyze_large_project",
13560
+ description: "\u5206\u6790\u5927\u578B\u9879\u76EE\u5E76\u5206\u7AE0\u8282\u5B58\u50A8\u5230\u6570\u636E\u5E93\uFF0C\u652F\u6301\u5904\u7406\u5305\u542B\u6570\u767E\u4E2A\u6587\u4EF6\u7684\u5927\u578B\u9879\u76EE\u3002\u9002\u7528\u4E8E\u4F01\u4E1A\u7EA7\u9879\u76EE\u7684\u5B8C\u6574\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66\u751F\u6210\u3002",
13561
+ inputSchema: {
13562
+ projectPath: stringType().min(1).describe(PROJECT_PATH_DESCRIPTION),
13563
+ projectName: stringType().optional().describe("\u9879\u76EE\u540D\u79F0\uFF0C\u5982\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528\u76EE\u5F55\u540D"),
13564
+ moduleName: stringType().optional().describe("\u6A21\u5757\u540D\u79F0\uFF0C\u5982\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528\u9879\u76EE\u540D"),
13565
+ includeModules: arrayType(stringType()).optional().describe("\u8981\u5305\u542B\u7684\u6A21\u5757\u5217\u8868\uFF0C\u5982\u4E0D\u63D0\u4F9B\u5219\u5305\u542B\u6240\u6709\u6A21\u5757"),
13566
+ excludeModules: arrayType(stringType()).optional().describe("\u8981\u6392\u9664\u7684\u6A21\u5757\u5217\u8868"),
13567
+ maxFilesPerBatch: numberType().optional().default(50).describe("\u6BCF\u6279\u5904\u7406\u7684\u6700\u5927\u6587\u4EF6\u6570"),
13568
+ maxModulesPerChapter: numberType().optional().default(10).describe("\u6BCF\u7AE0\u8282\u7684\u6700\u5927\u6A21\u5757\u6570")
13569
+ },
13570
+ handler: async (args)=>{
13571
+ try {
13572
+ const { projectPath, projectName, moduleName, includeModules, excludeModules, maxFilesPerBatch = 50, maxModulesPerChapter = 10 } = args;
13573
+ if (!projectPath) return {
13574
+ content: [
13575
+ {
13576
+ type: "text",
13577
+ text: JSON.stringify({
13578
+ success: false,
13579
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1AprojectPath"
13580
+ })
13581
+ }
13582
+ ],
13583
+ isError: true
13584
+ };
13585
+ logger.info(`\u{5F00}\u{59CB}\u{5206}\u{6790}\u{5927}\u{578B}\u{9879}\u{76EE}: ${projectPath}`);
13586
+ const projectAnalysis = await analyzeProject(projectPath);
13587
+ const chapterProcessor = new ChapterProcessor({
13588
+ maxFilesPerBatch,
13589
+ maxModulesPerChapter,
13590
+ enableParallelProcessing: true,
13591
+ skipEmptyModules: true
13592
+ });
13593
+ const result = await chapterProcessor.processFullProject(projectAnalysis, {
13594
+ projectName,
13595
+ moduleName,
13596
+ includeModules,
13597
+ excludeModules
13598
+ });
13599
+ if (result.success) return {
13600
+ content: [
13601
+ {
13602
+ type: "text",
13603
+ text: JSON.stringify({
13604
+ success: true,
13605
+ message: "\u5927\u578B\u9879\u76EE\u5206\u6790\u5B8C\u6210\uFF0C\u7AE0\u8282\u5DF2\u5B58\u50A8\u5230\u6570\u636E\u5E93",
13606
+ data: {
13607
+ projectUuid: result.projectUuid,
13608
+ projectAnalysis: {
13609
+ projectName: projectAnalysis.projectName,
13610
+ moduleCount: projectAnalysis.modules.length,
13611
+ totalFiles: projectAnalysis.statistics.totalFiles,
13612
+ modules: getModuleNames(projectAnalysis)
13613
+ },
13614
+ processingResult: {
13615
+ totalChapters: result.totalChapters,
13616
+ processedChapters: result.processedChapters,
13617
+ totalModules: result.totalModules,
13618
+ processedModules: result.processedModules,
13619
+ totalFiles: result.totalFiles,
13620
+ processedFiles: result.processedFiles
13621
+ },
13622
+ statistics: result.statistics,
13623
+ nextStep: `\u{8C03}\u{7528} render_final_document \u{5DE5}\u{5177}\u{FF0C}\u{4F7F}\u{7528} projectUuid: ${result.projectUuid} \u{751F}\u{6210}\u{6700}\u{7EC8}\u{6587}\u{6863}`
13624
+ }
13625
+ })
13626
+ }
13627
+ ]
13628
+ };
13629
+ return {
13630
+ content: [
13631
+ {
13632
+ type: "text",
13633
+ text: JSON.stringify({
13634
+ success: false,
13635
+ message: result.errorMessage || "\u5927\u578B\u9879\u76EE\u5206\u6790\u5931\u8D25",
13636
+ data: {
13637
+ projectUuid: result.projectUuid,
13638
+ statistics: result.statistics
13639
+ }
13640
+ })
13641
+ }
13642
+ ],
13643
+ isError: true
13644
+ };
13645
+ } catch (error) {
13646
+ logger.error("\u5927\u578B\u9879\u76EE\u5206\u6790\u5931\u8D25", {
13647
+ error: error.message
13648
+ });
13649
+ return {
13650
+ content: [
13651
+ {
13652
+ type: "text",
13653
+ text: JSON.stringify({
13654
+ success: false,
13655
+ message: error.message || "\u5206\u6790\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF"
13656
+ })
13657
+ }
13658
+ ],
13659
+ isError: true
13660
+ };
13661
+ }
13662
+ }
13663
+ };
13664
+ const analyzeMultiProjectsTool = {
13665
+ name: "analyze_multi_projects",
13666
+ description: "\u5206\u6790\u591A\u4E2A\u5FAE\u670D\u52A1\u9879\u76EE\u5E76\u5408\u5E76\u751F\u6210\u4E00\u4E2A\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66\u3002\u9002\u7528\u4E8E\u62C6\u5206\u4E3A\u591A\u4E2A\u5FAE\u670D\u52A1\u7684\u5927\u578B\u9879\u76EE\uFF0C\u5C06\u6240\u6709\u5FAE\u670D\u52A1\u7684\u529F\u80FD\u7EDF\u4E00\u6574\u5408\u5230\u4E00\u4EFD\u6587\u6863\u4E2D\u3002",
13667
+ inputSchema: {
13668
+ projectPaths: arrayType(stringType().min(1)).min(1).describe("\u5FAE\u670D\u52A1\u9879\u76EE\u8DEF\u5F84\u5217\u8868\uFF0C\u6BCF\u4E2A\u8DEF\u5F84\u6307\u5411\u4E00\u4E2A\u72EC\u7ACB\u7684\u5FAE\u670D\u52A1\u9879\u76EE"),
13669
+ mergedProjectName: stringType().min(1).describe("\u5408\u5E76\u540E\u7684\u9879\u76EE\u540D\u79F0\uFF0C\u5C06\u663E\u793A\u5728\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66\u7684\u6807\u9898\u4E2D"),
13670
+ mergedModuleName: stringType().optional().describe("\u5408\u5E76\u540E\u7684\u6A21\u5757\u540D\u79F0\uFF0C\u5982\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528\u9879\u76EE\u540D\u79F0"),
13671
+ outputDir: stringType().optional().describe("\u8F93\u51FA\u76EE\u5F55\uFF0C\u5982\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528\u7B2C\u4E00\u4E2A\u9879\u76EE\u7684\u9ED8\u8BA4\u76EE\u5F55"),
13672
+ addProjectPrefix: booleanType().optional().default(true).describe("\u662F\u5426\u4E3A\u6BCF\u4E2A\u5FAE\u670D\u52A1\u7684\u5185\u5BB9\u6DFB\u52A0\u9879\u76EE\u524D\u7F00\u6807\u8BC6\uFF0C\u4FBF\u4E8E\u533A\u5206\u6765\u6E90"),
13673
+ projectAliases: recordType(stringType(), stringType()).optional().describe("\u9879\u76EE\u522B\u540D\u6620\u5C04\uFF0Ckey\u4E3A\u9879\u76EE\u8DEF\u5F84\uFF0Cvalue\u4E3A\u663E\u793A\u540D\u79F0\u3002\u7528\u4E8E\u81EA\u5B9A\u4E49\u5FAE\u670D\u52A1\u7684\u663E\u793A\u540D\u79F0")
13674
+ },
13675
+ handler: async (args)=>{
13676
+ try {
13677
+ const { projectPaths, mergedProjectName, mergedModuleName, outputDir, addProjectPrefix = true, projectAliases } = args;
13678
+ if (!projectPaths || 0 === projectPaths.length) return {
13679
+ content: [
13680
+ {
13681
+ type: "text",
13682
+ text: JSON.stringify({
13683
+ success: false,
13684
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1AprojectPaths\uFF08\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u9879\u76EE\u8DEF\u5F84\uFF09"
13685
+ })
13686
+ }
13687
+ ],
13688
+ isError: true
13689
+ };
13690
+ if (!mergedProjectName) return {
13691
+ content: [
13692
+ {
13693
+ type: "text",
13694
+ text: JSON.stringify({
13695
+ success: false,
13696
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1AmergedProjectName"
13697
+ })
13698
+ }
13699
+ ],
13700
+ isError: true
13701
+ };
13702
+ logger.info(`\u{5F00}\u{59CB}\u{5206}\u{6790}\u{591A}\u{4E2A}\u{5FAE}\u{670D}\u{52A1}\u{9879}\u{76EE}`, {
13703
+ projectCount: projectPaths.length,
13704
+ mergedProjectName,
13705
+ projectPaths
13706
+ });
13707
+ const chapterProcessor = new ChapterProcessor({
13708
+ maxFilesPerBatch: 50,
13709
+ maxModulesPerChapter: 10,
13710
+ enableParallelProcessing: true,
13711
+ skipEmptyModules: true
13712
+ });
13713
+ const result = await chapterProcessor.processMultiProjects({
13714
+ projectPaths,
13715
+ mergedProjectName,
13716
+ mergedModuleName,
13717
+ outputDir,
13718
+ addProjectPrefix,
13719
+ projectAliases
13720
+ });
13721
+ if (result.success) return {
13722
+ content: [
13723
+ {
13724
+ type: "text",
13725
+ text: JSON.stringify({
13726
+ success: true,
13727
+ message: "\u591A\u5FAE\u670D\u52A1\u9879\u76EE\u5206\u6790\u5B8C\u6210\uFF0C\u7AE0\u8282\u5DF2\u5B58\u50A8\u5230\u6570\u636E\u5E93",
13728
+ data: {
13729
+ projectUuid: result.projectUuid,
13730
+ mergedProjectName: result.mergedProjectName,
13731
+ summary: {
13732
+ totalProjects: result.totalProjects,
13733
+ processedProjects: result.processedProjects,
13734
+ successfulProjects: result.projectResults.filter((p)=>p.success).length,
13735
+ failedProjects: result.projectResults.filter((p)=>!p.success).length
13736
+ },
13737
+ projectResults: result.projectResults.map((p)=>({
13738
+ projectPath: p.projectPath,
13739
+ projectName: p.projectName,
13740
+ success: p.success,
13741
+ errorMessage: p.errorMessage,
13742
+ statistics: p.statistics
13743
+ })),
13744
+ mergedStatistics: result.mergedStatistics,
13745
+ nextStep: `\u{8C03}\u{7528} render_final_document \u{5DE5}\u{5177}\u{FF0C}\u{4F7F}\u{7528} projectUuid: ${result.projectUuid} \u{751F}\u{6210}\u{6700}\u{7EC8}\u{7684}\u{5408}\u{5E76}\u{6587}\u{6863}`
13746
+ }
13747
+ })
13748
+ }
13749
+ ]
13750
+ };
13751
+ return {
13752
+ content: [
13753
+ {
13754
+ type: "text",
13755
+ text: JSON.stringify({
13756
+ success: false,
13757
+ message: result.errorMessage || "\u591A\u9879\u76EE\u5206\u6790\u5931\u8D25",
13758
+ data: {
13759
+ projectUuid: result.projectUuid,
13760
+ projectResults: result.projectResults,
13761
+ mergedStatistics: result.mergedStatistics
13762
+ }
13763
+ })
13764
+ }
13765
+ ],
13766
+ isError: true
13767
+ };
13768
+ } catch (error) {
13769
+ logger.error("\u591A\u9879\u76EE\u5206\u6790\u5931\u8D25", {
13770
+ error: error.message
13771
+ });
13772
+ return {
13773
+ content: [
13774
+ {
13775
+ type: "text",
13776
+ text: JSON.stringify({
13777
+ success: false,
13778
+ message: error.message || "\u5206\u6790\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF"
13779
+ })
13780
+ }
13781
+ ],
13782
+ isError: true
13783
+ };
13784
+ }
13785
+ }
13786
+ };
13787
+ const renderFinalDocumentTool = {
13788
+ name: "render_final_document",
13789
+ description: "\u5C06\u6570\u636E\u5E93\u4E2D\u5B58\u50A8\u7684\u7AE0\u8282\u5408\u5E76\u5E76\u6E32\u67D3\u4E3A\u6700\u7EC8\u7684\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66Word\u6587\u6863\u3002\u7528\u4E8E\u5927\u578B\u9879\u76EE\u7684\u6700\u7EC8\u6587\u6863\u751F\u6210\u3002",
11620
13790
  inputSchema: {
11621
- projectPath: stringType().min(1).describe(PROJECT_PATH_DESCRIPTION),
11622
- techSpecData: objectType({
11623
- projectName: stringType().describe("\u9879\u76EE\u540D\u79F0"),
11624
- moduleName: stringType().describe("\u6A21\u5757\u540D\u79F0"),
11625
- year: stringType().optional().describe("\u5E74\u4EFD\uFF0C\u9ED8\u8BA4\u5F53\u524D\u5E74\u4EFD"),
11626
- month: stringType().optional().describe("\u6708\u4EFD\uFF0C\u9ED8\u8BA4\u5F53\u524D\u6708\u4EFD"),
11627
- referDoc: stringType().optional().describe("\u53C2\u8003\u6587\u6863"),
11628
- serviceInterfaceList: arrayType(ServiceInterfaceSchema).default([]).describe("\u670D\u52A1\u63A5\u53E3\u6E05\u5355"),
11629
- designDetailList: arrayType(DesignDetailSchema).default([]).describe("\u670D\u52A1\u63A5\u53E3\u8BBE\u8BA1\u8BE6\u60C5"),
11630
- tableInfoList: arrayType(TableInfoSchema).default([]).describe("\u6570\u636E\u5E93\u8868\u4FE1\u606F\u5217\u8868"),
11631
- techSolution: TechSolutionSchema.optional().describe("\u6280\u672F\u65B9\u6848"),
11632
- businessException: BusinessExceptionSchema.optional().describe("\u4E1A\u52A1\u5F02\u5E38\u5904\u7406"),
11633
- appendix: AppendixSchema.optional().describe("\u9644\u5F55"),
11634
- dtoDefinitions: recordType(stringType(), arrayType(objectType({
11635
- name: stringType().optional().describe("\u5B57\u6BB5\u540D"),
11636
- type: stringType().optional().describe("\u5B57\u6BB5\u7C7B\u578B"),
11637
- required: stringType().optional().describe("\u662F\u5426\u5FC5\u586B"),
11638
- description: stringType().optional().describe("\u5B57\u6BB5\u63CF\u8FF0")
11639
- }))).optional().describe("DTO\u5B9A\u4E49\u6620\u5C04\uFF0C\u7528\u4E8E\u5C55\u5F00\u63A5\u53E3\u53C2\u6570\u4E2D\u7684DTO\u4E3A\u5177\u4F53\u5B57\u6BB5")
11640
- }).describe("\u5B8C\u6574\u7684\u6280\u672F\u89C4\u683C\u6570\u636E")
13791
+ projectUuid: stringType().min(1).describe("\u9879\u76EEUUID\uFF0C\u7531 analyze_large_project \u5DE5\u5177\u8FD4\u56DE"),
13792
+ outputDir: stringType().optional().describe("\u8F93\u51FA\u76EE\u5F55\uFF0C\u5982\u4E0D\u63D0\u4F9B\u5219\u4F7F\u7528\u9879\u76EE\u9ED8\u8BA4\u76EE\u5F55"),
13793
+ includeEmptyChapters: booleanType().optional().default(false).describe("\u662F\u5426\u5305\u542B\u7A7A\u7AE0\u8282"),
13794
+ generateDefaultContent: booleanType().optional().default(true).describe("\u662F\u5426\u4E3A\u7A7A\u7AE0\u8282\u751F\u6210\u9ED8\u8BA4\u5185\u5BB9"),
13795
+ maxItemsPerChapter: numberType().optional().default(1000).describe("\u6BCF\u7AE0\u8282\u6700\u5927\u6761\u76EE\u6570")
11641
13796
  },
11642
13797
  handler: async (args)=>{
11643
13798
  try {
11644
- const { projectPath } = args;
11645
- let { techSpecData } = args;
11646
- if (!projectPath || !techSpecData) return {
13799
+ const { projectUuid, outputDir, includeEmptyChapters = false, generateDefaultContent = true, maxItemsPerChapter = 1000 } = args;
13800
+ if (!projectUuid) return {
11647
13801
  content: [
11648
13802
  {
11649
13803
  type: "text",
11650
13804
  text: JSON.stringify({
11651
13805
  success: false,
11652
- message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570"
13806
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1AprojectUuid"
11653
13807
  })
11654
13808
  }
11655
13809
  ],
11656
13810
  isError: true
11657
13811
  };
11658
- if ("string" == typeof techSpecData) techSpecData = JSON.parse(techSpecData);
11659
- const now = new Date();
11660
- const normalizedData = normalizeInputData(techSpecData);
11661
- const data = {
11662
- projectName: normalizedData.projectName || "\u672A\u547D\u540D\u9879\u76EE",
11663
- moduleName: normalizedData.moduleName || "\u672A\u547D\u540D\u6A21\u5757",
11664
- year: normalizedData.year || String(now.getFullYear()),
11665
- month: normalizedData.month || String(now.getMonth() + 1).padStart(2, "0"),
11666
- referDoc: normalizedData.referDoc || "\u65E0",
11667
- serviceInterfaceList: normalizedData.serviceInterfaceList || [],
11668
- designDetailList: normalizedData.designDetailList || [],
11669
- tableInfoList: normalizedData.tableInfoList || [],
11670
- techSolution: normalizedData.techSolution || void 0,
11671
- businessException: normalizedData.businessException || void 0,
11672
- appendix: normalizedData.appendix || void 0
13812
+ logger.info(`\u{5F00}\u{59CB}\u{6E32}\u{67D3}\u{6700}\u{7EC8}\u{6587}\u{6863}: ${projectUuid}`);
13813
+ const project = await techSpecDbOperations.getTechSpecProject(projectUuid);
13814
+ if (!project) return {
13815
+ content: [
13816
+ {
13817
+ type: "text",
13818
+ text: JSON.stringify({
13819
+ success: false,
13820
+ message: `\u{9879}\u{76EE}\u{4E0D}\u{5B58}\u{5728}: ${projectUuid}`
13821
+ })
13822
+ }
13823
+ ],
13824
+ isError: true
11673
13825
  };
11674
- const outputDir = getTechSpecOutputDir(projectPath);
11675
- const result = await renderTechSpecDoc(data, outputDir);
13826
+ const documentMerger = new DocumentMerger({
13827
+ includeEmptyChapters,
13828
+ generateDefaultContent,
13829
+ maxItemsPerChapter,
13830
+ enableChapterSummary: true
13831
+ });
13832
+ const result = await documentMerger.mergeAndRenderDocument(projectUuid, outputDir);
11676
13833
  if (result.success) return {
11677
13834
  content: [
11678
13835
  {
@@ -11681,16 +13838,19 @@ ${requirement_description}
11681
13838
  success: true,
11682
13839
  message: "\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66\u751F\u6210\u6210\u529F",
11683
13840
  data: {
13841
+ projectUuid,
11684
13842
  outputPath: result.outputPath,
11685
- projectName: data.projectName,
11686
- moduleName: data.moduleName,
11687
- statistics: {
11688
- tableCount: data.tableInfoList.length,
11689
- serviceCount: data.serviceInterfaceList.length,
11690
- designCount: data.designDetailList.length,
11691
- hasTechSolution: !!data.techSolution,
11692
- hasBusinessException: !!data.businessException,
11693
- hasAppendix: !!data.appendix
13843
+ projectInfo: {
13844
+ projectName: project.projectName,
13845
+ moduleName: project.moduleName,
13846
+ projectPath: project.projectPath
13847
+ },
13848
+ statistics: result.statistics,
13849
+ techSpecSummary: {
13850
+ serviceInterfaceCount: result.statistics.totalServiceInterfaces,
13851
+ designDetailCount: result.statistics.totalDesignDetails,
13852
+ tableCount: result.statistics.totalTables,
13853
+ chapterCount: result.statistics.totalChapters
11694
13854
  }
11695
13855
  }
11696
13856
  })
@@ -11703,20 +13863,231 @@ ${requirement_description}
11703
13863
  type: "text",
11704
13864
  text: JSON.stringify({
11705
13865
  success: false,
11706
- message: result.error || "\u6587\u6863\u751F\u6210\u5931\u8D25"
13866
+ message: result.errorMessage || "\u6587\u6863\u6E32\u67D3\u5931\u8D25",
13867
+ data: {
13868
+ projectUuid,
13869
+ statistics: result.statistics
13870
+ }
11707
13871
  })
11708
13872
  }
11709
13873
  ],
11710
13874
  isError: true
11711
13875
  };
11712
13876
  } catch (error) {
13877
+ logger.error("\u6587\u6863\u6E32\u67D3\u5931\u8D25", {
13878
+ error: error.message
13879
+ });
11713
13880
  return {
11714
13881
  content: [
11715
13882
  {
11716
13883
  type: "text",
11717
13884
  text: JSON.stringify({
11718
13885
  success: false,
11719
- message: error.message || "\u751F\u6210\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF"
13886
+ message: error.message || "\u6E32\u67D3\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF"
13887
+ })
13888
+ }
13889
+ ],
13890
+ isError: true
13891
+ };
13892
+ }
13893
+ }
13894
+ };
13895
+ const getProjectProgressTool = {
13896
+ name: "get_project_progress",
13897
+ description: "\u83B7\u53D6\u5927\u578B\u9879\u76EE\u5904\u7406\u8FDB\u5EA6\u548C\u72B6\u6001\u4FE1\u606F\u3002\u7528\u4E8E\u76D1\u63A7\u9879\u76EE\u5206\u6790\u548C\u6587\u6863\u751F\u6210\u7684\u8FDB\u5EA6\u3002",
13898
+ inputSchema: {
13899
+ projectUuid: stringType().min(1).describe("\u9879\u76EEUUID")
13900
+ },
13901
+ handler: async (args)=>{
13902
+ try {
13903
+ const { projectUuid } = args;
13904
+ if (!projectUuid) return {
13905
+ content: [
13906
+ {
13907
+ type: "text",
13908
+ text: JSON.stringify({
13909
+ success: false,
13910
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1AprojectUuid"
13911
+ })
13912
+ }
13913
+ ],
13914
+ isError: true
13915
+ };
13916
+ const documentMerger = new DocumentMerger();
13917
+ const progressInfo = await documentMerger.getProjectProgress(projectUuid);
13918
+ return {
13919
+ content: [
13920
+ {
13921
+ type: "text",
13922
+ text: JSON.stringify({
13923
+ success: true,
13924
+ message: "\u9879\u76EE\u8FDB\u5EA6\u83B7\u53D6\u6210\u529F",
13925
+ data: {
13926
+ projectUuid,
13927
+ project: {
13928
+ projectName: progressInfo.project.projectName,
13929
+ moduleName: progressInfo.project.moduleName,
13930
+ status: progressInfo.project.status,
13931
+ createdAt: progressInfo.project.createdAt,
13932
+ updatedAt: progressInfo.project.updatedAt,
13933
+ completedAt: progressInfo.project.completedAt
13934
+ },
13935
+ progress: progressInfo.progress,
13936
+ statistics: progressInfo.statistics,
13937
+ isCompleted: "completed" === progressInfo.project.status,
13938
+ canRender: progressInfo.statistics.completedChapters > 0
13939
+ }
13940
+ })
13941
+ }
13942
+ ]
13943
+ };
13944
+ } catch (error) {
13945
+ return {
13946
+ content: [
13947
+ {
13948
+ type: "text",
13949
+ text: JSON.stringify({
13950
+ success: false,
13951
+ message: error.message || "\u83B7\u53D6\u8FDB\u5EA6\u5931\u8D25"
13952
+ })
13953
+ }
13954
+ ],
13955
+ isError: true
13956
+ };
13957
+ }
13958
+ }
13959
+ };
13960
+ const listTechSpecProjectsTool = {
13961
+ name: "list_tech_spec_projects",
13962
+ description: "\u5217\u51FA\u6240\u6709\u6280\u672F\u89C4\u683C\u8BF4\u660E\u4E66\u9879\u76EE\u53CA\u5176\u72B6\u6001\u3002\u7528\u4E8E\u67E5\u770B\u5386\u53F2\u9879\u76EE\u548C\u7BA1\u7406\u9879\u76EE\u3002",
13963
+ inputSchema: {
13964
+ status: stringType().optional().describe("\u8FC7\u6EE4\u72B6\u6001\uFF1Aanalyzing, generating, completed, failed"),
13965
+ limit: numberType().optional().default(20).describe("\u8FD4\u56DE\u6570\u91CF\u9650\u5236")
13966
+ },
13967
+ handler: async (args)=>{
13968
+ try {
13969
+ const { status, limit = 20 } = args;
13970
+ let projects = await techSpecDbOperations.getAllTechSpecProjects();
13971
+ if (status) projects = projects.filter((p)=>p.status === status);
13972
+ projects.sort((a, b)=>b.createdAt.getTime() - a.createdAt.getTime());
13973
+ projects = projects.slice(0, limit);
13974
+ const projectsWithStats = await Promise.all(projects.map(async (project)=>{
13975
+ try {
13976
+ const statistics = await techSpecDbOperations.getProjectStatistics(project.uuid);
13977
+ return {
13978
+ ...project,
13979
+ statistics
13980
+ };
13981
+ } catch (error) {
13982
+ return {
13983
+ ...project,
13984
+ statistics: null
13985
+ };
13986
+ }
13987
+ }));
13988
+ return {
13989
+ content: [
13990
+ {
13991
+ type: "text",
13992
+ text: JSON.stringify({
13993
+ success: true,
13994
+ message: `\u{627E}\u{5230} ${projectsWithStats.length} \u{4E2A}\u{9879}\u{76EE}`,
13995
+ data: {
13996
+ projects: projectsWithStats.map((p)=>({
13997
+ uuid: p.uuid,
13998
+ projectName: p.projectName,
13999
+ moduleName: p.moduleName,
14000
+ status: p.status,
14001
+ totalModules: p.totalModules,
14002
+ processedModules: p.processedModules,
14003
+ totalFiles: p.totalFiles,
14004
+ processedFiles: p.processedFiles,
14005
+ createdAt: p.createdAt,
14006
+ updatedAt: p.updatedAt,
14007
+ completedAt: p.completedAt,
14008
+ statistics: p.statistics
14009
+ })),
14010
+ totalCount: projectsWithStats.length
14011
+ }
14012
+ })
14013
+ }
14014
+ ]
14015
+ };
14016
+ } catch (error) {
14017
+ return {
14018
+ content: [
14019
+ {
14020
+ type: "text",
14021
+ text: JSON.stringify({
14022
+ success: false,
14023
+ message: error.message || "\u83B7\u53D6\u9879\u76EE\u5217\u8868\u5931\u8D25"
14024
+ })
14025
+ }
14026
+ ],
14027
+ isError: true
14028
+ };
14029
+ }
14030
+ }
14031
+ };
14032
+ const analyzeProjectStructureTool = {
14033
+ name: "analyze_project_structure",
14034
+ description: "\u4EC5\u5206\u6790\u9879\u76EE\u7ED3\u6784\uFF0C\u4E0D\u751F\u6210\u6587\u6863\u3002\u7528\u4E8E\u4E86\u89E3\u9879\u76EE\u7684\u6A21\u5757\u548C\u6587\u4EF6\u7EC4\u7EC7\u60C5\u51B5\u3002",
14035
+ inputSchema: {
14036
+ projectPath: stringType().min(1).describe(PROJECT_PATH_DESCRIPTION)
14037
+ },
14038
+ handler: async (args)=>{
14039
+ try {
14040
+ const { projectPath } = args;
14041
+ if (!projectPath) return {
14042
+ content: [
14043
+ {
14044
+ type: "text",
14045
+ text: JSON.stringify({
14046
+ success: false,
14047
+ message: "\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570\uFF1AprojectPath"
14048
+ })
14049
+ }
14050
+ ],
14051
+ isError: true
14052
+ };
14053
+ const analysis = await analyzeProject(projectPath);
14054
+ return {
14055
+ content: [
14056
+ {
14057
+ type: "text",
14058
+ text: JSON.stringify({
14059
+ success: true,
14060
+ message: "\u9879\u76EE\u7ED3\u6784\u5206\u6790\u5B8C\u6210",
14061
+ data: {
14062
+ projectName: analysis.projectName,
14063
+ projectPath: analysis.projectPath,
14064
+ statistics: analysis.statistics,
14065
+ modules: analysis.modules.map((m)=>({
14066
+ name: m.name,
14067
+ relativePath: m.relativePath,
14068
+ fileCount: m.files.length,
14069
+ fileTypes: {
14070
+ controller: m.files.filter((f)=>"controller" === f.type).length,
14071
+ service: m.files.filter((f)=>"service" === f.type).length,
14072
+ entity: m.files.filter((f)=>"entity" === f.type).length,
14073
+ dto: m.files.filter((f)=>"dto" === f.type).length,
14074
+ other: m.files.filter((f)=>"other" === f.type).length
14075
+ }
14076
+ })),
14077
+ availableModules: getModuleNames(analysis)
14078
+ }
14079
+ })
14080
+ }
14081
+ ]
14082
+ };
14083
+ } catch (error) {
14084
+ return {
14085
+ content: [
14086
+ {
14087
+ type: "text",
14088
+ text: JSON.stringify({
14089
+ success: false,
14090
+ message: error.message || "\u9879\u76EE\u7ED3\u6784\u5206\u6790\u5931\u8D25"
11720
14091
  })
11721
14092
  }
11722
14093
  ],
@@ -11726,6 +14097,12 @@ ${requirement_description}
11726
14097
  }
11727
14098
  };
11728
14099
  const techSpecGeneratorTools = [
14100
+ analyzeLargeProjectTool,
14101
+ analyzeMultiProjectsTool,
14102
+ renderFinalDocumentTool,
14103
+ getProjectProgressTool,
14104
+ listTechSpecProjectsTool,
14105
+ analyzeProjectStructureTool,
11729
14106
  analyzeProjectContextTool,
11730
14107
  collectServiceInterfacesTool,
11731
14108
  generateInterfaceDesignsTool,