Skip to main content

aster/blueprint/
task_granularity.rs

1//! 任务粒度自动控制机制
2//!
3//! 功能:
4//! 1. 评估任务复杂度
5//! 2. 检查任务是否需要拆分(过粗)
6//! 3. 检查任务是否需要合并(过细)
7//! 4. 自动调整任务树粒度
8//!
9//! 目标:
10//! - 避免任务过细(219 任务 vs 8 模块)
11//! - 避免任务过粗(无法并行执行)
12//! - 保持任务粒度适中(便于 TDD 循环)
13//!
14
15use super::types::{SystemModule, TaskNode, TaskTree};
16
17// ============================================================================
18// 配置接口
19// ============================================================================
20
21/// 粒度控制配置
22#[derive(Debug, Clone)]
23pub struct GranularityConfig {
24    /// 最小复杂度(低于此值需要合并)
25    pub min_task_complexity: f64,
26    /// 最大复杂度(高于此值需要拆分)
27    pub max_task_complexity: f64,
28    /// 理想执行时间(分钟)
29    pub ideal_task_duration: u32,
30    /// 最小执行时间(分钟)
31    pub min_task_duration: u32,
32    /// 最大执行时间(分钟)
33    pub max_task_duration: u32,
34    /// 最大树深度
35    pub max_depth: u32,
36    /// 最小树深度
37    pub min_depth: u32,
38    /// 单节点最大子任务数
39    pub max_children_per_node: u32,
40    /// 单节点最小子任务数
41    pub min_children_per_node: u32,
42    /// 每个任务预计的代码行数
43    pub estimated_lines_per_task: u32,
44    /// 每个任务最大代码行数
45    pub max_lines_per_task: u32,
46    /// 每个任务最小代码行数
47    pub min_lines_per_task: u32,
48}
49
50impl Default for GranularityConfig {
51    fn default() -> Self {
52        Self {
53            min_task_complexity: 15.0,
54            max_task_complexity: 75.0,
55            ideal_task_duration: 30,
56            min_task_duration: 10,
57            max_task_duration: 120,
58            max_depth: 5,
59            min_depth: 2,
60            max_children_per_node: 10,
61            min_children_per_node: 2,
62            estimated_lines_per_task: 100,
63            max_lines_per_task: 300,
64            min_lines_per_task: 20,
65        }
66    }
67}
68
69// ============================================================================
70// 复杂度评分
71// ============================================================================
72
73/// 复杂度因子
74#[derive(Debug, Clone, Default)]
75pub struct ComplexityFactors {
76    /// 代码量因子(0-1)
77    pub code_size: f64,
78    /// 依赖复杂度(0-1)
79    pub dependencies: f64,
80    /// 接口复杂度(0-1)
81    pub interfaces: f64,
82    /// 测试覆盖度(0-1)
83    pub test_coverage: f64,
84    /// 描述长度因子(0-1)
85    pub description_length: f64,
86    /// 子任务数量因子(0-1)
87    pub children_count: f64,
88}
89
90/// 复杂度权重
91#[derive(Debug, Clone)]
92pub struct ComplexityWeights {
93    pub code_size: f64,
94    pub dependencies: f64,
95    pub interfaces: f64,
96    pub test_coverage: f64,
97    pub description_length: f64,
98    pub children_count: f64,
99}
100
101impl Default for ComplexityWeights {
102    fn default() -> Self {
103        Self {
104            code_size: 0.3,
105            dependencies: 0.2,
106            interfaces: 0.15,
107            test_coverage: 0.15,
108            description_length: 0.1,
109            children_count: 0.1,
110        }
111    }
112}
113
114/// 诊断信息
115#[derive(Debug, Clone, Default)]
116pub struct ComplexityDiagnostic {
117    /// 估算的代码行数
118    pub estimated_lines: u32,
119    /// 估算的执行时间(分钟)
120    pub estimated_duration: u32,
121    /// 有依赖
122    pub has_dependencies: bool,
123    /// 有接口
124    pub has_interfaces: bool,
125    /// 有测试
126    pub has_tests: bool,
127    /// 树深度
128    pub depth: u32,
129    /// 子任务数
130    pub children_count: usize,
131}
132
133/// 复杂度评分
134#[derive(Debug, Clone)]
135pub struct ComplexityScore {
136    /// 总分(0-100)
137    pub total: f64,
138    /// 细分因子
139    pub factors: ComplexityFactors,
140    /// 权重配置
141    pub weights: ComplexityWeights,
142    /// 诊断信息
143    pub diagnostic: ComplexityDiagnostic,
144}
145
146// ============================================================================
147// 拆分/合并建议
148// ============================================================================
149
150/// 拆分策略
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152pub enum SplitStrategy {
153    /// 按功能拆分
154    ByFunction,
155    /// 按层次拆分
156    ByLayer,
157    /// 按依赖拆分
158    ByDependency,
159    /// 按接口拆分
160    ByInterface,
161}
162
163/// 拆分建议项
164#[derive(Debug, Clone)]
165pub struct SuggestedSplit {
166    pub name: String,
167    pub description: String,
168    pub strategy: SplitStrategy,
169}
170
171/// 拆分建议
172#[derive(Debug, Clone)]
173pub struct SplitSuggestion {
174    pub task_id: String,
175    pub task_name: String,
176    pub reason: String,
177    pub complexity: f64,
178    pub suggested_splits: Vec<SuggestedSplit>,
179}
180
181/// 合并策略
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum MergeStrategy {
184    /// 相关功能合并
185    RelatedFunctions,
186    /// 简单批量合并
187    SimpleBatch,
188    /// 同文件合并
189    SameFile,
190}
191
192/// 合并建议
193#[derive(Debug, Clone)]
194pub struct MergeSuggestion {
195    pub task_ids: Vec<String>,
196    pub task_names: Vec<String>,
197    pub reason: String,
198    pub avg_complexity: f64,
199    pub suggested_name: String,
200    pub suggested_description: String,
201    pub strategy: MergeStrategy,
202}
203
204/// 问题严重程度
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum IssueSeverity {
207    High,
208    Medium,
209    Low,
210}
211
212/// 问题类型
213#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214pub enum IssueType {
215    TooDeep,
216    TooShallow,
217    TooManyChildren,
218    TooFewChildren,
219    Unbalanced,
220}
221
222/// 结构问题
223#[derive(Debug, Clone)]
224pub struct StructureIssue {
225    pub issue_type: IssueType,
226    pub task_id: Option<String>,
227    pub task_name: Option<String>,
228    pub description: String,
229    pub severity: IssueSeverity,
230}
231
232/// 调整统计信息
233#[derive(Debug, Clone, Default)]
234pub struct AdjustmentStats {
235    pub total_tasks: u32,
236    pub too_simple: u32,
237    pub too_complex: u32,
238    pub just_right: u32,
239    pub avg_complexity: f64,
240    pub avg_depth: f64,
241    pub max_depth: u32,
242    pub avg_children: f64,
243    pub max_children: u32,
244}
245
246/// 调整结果
247#[derive(Debug, Clone, Default)]
248pub struct AdjustmentResult {
249    /// 是否需要调整
250    pub needs_adjustment: bool,
251    /// 拆分建议
252    pub split_suggestions: Vec<SplitSuggestion>,
253    /// 合并建议
254    pub merge_suggestions: Vec<MergeSuggestion>,
255    /// 统计信息
256    pub stats: AdjustmentStats,
257    /// 诊断问题
258    pub issues: Vec<StructureIssue>,
259}
260
261// ============================================================================
262// 任务粒度控制器
263// ============================================================================
264
265/// 任务粒度控制器
266pub struct TaskGranularityController {
267    config: GranularityConfig,
268}
269
270impl TaskGranularityController {
271    /// 创建新的控制器
272    pub fn new(config: GranularityConfig) -> Self {
273        Self { config }
274    }
275
276    /// 更新配置
277    pub fn update_config(&mut self, config: GranularityConfig) {
278        self.config = config;
279    }
280
281    /// 获取当前配置
282    pub fn config(&self) -> &GranularityConfig {
283        &self.config
284    }
285
286    // --------------------------------------------------------------------------
287    // 复杂度评估
288    // --------------------------------------------------------------------------
289
290    /// 评估任务复杂度
291    pub fn assess_complexity(
292        &self,
293        task: &TaskNode,
294        module: Option<&SystemModule>,
295    ) -> ComplexityScore {
296        let factors = ComplexityFactors {
297            code_size: self.assess_code_size_factor(task, module),
298            dependencies: self.assess_dependencies_factor(task, module),
299            interfaces: self.assess_interfaces_factor(module),
300            test_coverage: self.assess_test_coverage_factor(task),
301            description_length: self.assess_description_length_factor(task),
302            children_count: self.assess_children_count_factor(task),
303        };
304
305        let weights = ComplexityWeights::default();
306        let total = factors.code_size * weights.code_size
307            + factors.dependencies * weights.dependencies
308            + factors.interfaces * weights.interfaces
309            + factors.test_coverage * weights.test_coverage
310            + factors.description_length * weights.description_length
311            + factors.children_count * weights.children_count;
312
313        let estimated_lines = self.estimate_code_lines(task, module);
314        let estimated_duration = self.estimate_duration(estimated_lines, &factors);
315
316        ComplexityScore {
317            total: (total * 100.0 * 100.0).round() / 100.0,
318            factors,
319            weights,
320            diagnostic: ComplexityDiagnostic {
321                estimated_lines,
322                estimated_duration,
323                has_dependencies: !task.dependencies.is_empty(),
324                has_interfaces: module.is_some_and(|m| !m.interfaces.is_empty()),
325                has_tests: !task.acceptance_tests.is_empty() || task.test_spec.is_some(),
326                depth: task.depth,
327                children_count: task.children.len(),
328            },
329        }
330    }
331
332    /// 代码量因子(0-1)
333    fn assess_code_size_factor(&self, task: &TaskNode, module: Option<&SystemModule>) -> f64 {
334        let estimated_lines = self.estimate_code_lines(task, module) as f64;
335        let normalized = estimated_lines / self.config.estimated_lines_per_task as f64;
336        (1.0 / (1.0 + (-2.0 * (normalized - 1.0)).exp())).min(1.0)
337    }
338
339    /// 依赖复杂度因子(0-1)
340    fn assess_dependencies_factor(&self, task: &TaskNode, module: Option<&SystemModule>) -> f64 {
341        let task_deps = task.dependencies.len();
342        let module_deps = module.map_or(0, |m| m.dependencies.len());
343        let total_deps = task_deps + module_deps;
344        (total_deps as f64 / 10.0).min(1.0)
345    }
346
347    /// 接口复杂度因子(0-1)
348    fn assess_interfaces_factor(&self, module: Option<&SystemModule>) -> f64 {
349        module.map_or(0.0, |m| (m.interfaces.len() as f64 / 6.0).min(1.0))
350    }
351
352    /// 测试覆盖度因子(0-1)
353    fn assess_test_coverage_factor(&self, task: &TaskNode) -> f64 {
354        let test_factor = (task.acceptance_tests.len() as f64 / 6.0).min(1.0);
355        let has_test_spec = if task.test_spec.is_some() { 0.2 } else { 0.0 };
356        (test_factor + has_test_spec).min(1.0)
357    }
358
359    /// 描述长度因子(0-1)
360    fn assess_description_length_factor(&self, task: &TaskNode) -> f64 {
361        (task.description.len() as f64 / 300.0).min(1.0)
362    }
363
364    /// 子任务数量因子(0-1)
365    fn assess_children_count_factor(&self, task: &TaskNode) -> f64 {
366        if task.children.is_empty() {
367            0.3
368        } else {
369            0.3 + (task.children.len() as f64 / 10.0 * 0.7).min(0.7)
370        }
371    }
372
373    /// 估算代码行数
374    fn estimate_code_lines(&self, task: &TaskNode, module: Option<&SystemModule>) -> u32 {
375        let mut base_lines = self.config.estimated_lines_per_task as f64;
376
377        // 根据任务类型调整
378        if task.name.contains("设计") {
379            base_lines *= 0.3;
380        } else if task.name.contains("测试") {
381            base_lines *= 0.6;
382        } else if task.name.contains("实现") || task.name.contains("功能") {
383            base_lines *= 1.2;
384        } else if task.name.contains("接口") {
385            base_lines *= 0.8;
386        }
387
388        // 根据模块类型调整
389        if let Some(m) = module {
390            match m.module_type {
391                super::types::ModuleType::Frontend => base_lines *= 1.3,
392                super::types::ModuleType::Backend => base_lines *= 1.1,
393                super::types::ModuleType::Database => base_lines *= 0.7,
394                _ => {}
395            }
396        }
397
398        // 根据依赖数量调整
399        let dep_multiplier = 1.0 + (task.dependencies.len() as f64 * 0.1);
400        base_lines *= dep_multiplier;
401
402        // 根据描述长度调整
403        let desc_multiplier = (1.0 + task.description.len() as f64 / 1000.0).min(1.5);
404        base_lines *= desc_multiplier;
405
406        base_lines.round() as u32
407    }
408
409    /// 估算执行时间(分钟)
410    fn estimate_duration(&self, estimated_lines: u32, factors: &ComplexityFactors) -> u32 {
411        let mut duration = estimated_lines as f64 / 10.0;
412        duration *= 1.0 + (factors.dependencies * 0.5);
413        duration *= 1.0 + (factors.interfaces * 0.3);
414        duration *= 1.0 + (factors.test_coverage * 0.4);
415        duration.round() as u32
416    }
417
418    // --------------------------------------------------------------------------
419    // 拆分/合并判断
420    // --------------------------------------------------------------------------
421
422    /// 检查任务是否需要拆分
423    pub fn should_split(&self, task: &TaskNode, module: Option<&SystemModule>) -> SplitCheck {
424        let score = self.assess_complexity(task, module);
425
426        // 情况 1:复杂度过高
427        if score.total > self.config.max_task_complexity {
428            return SplitCheck {
429                should_split: true,
430                reason: format!(
431                    "任务复杂度过高({:.1} > {})",
432                    score.total, self.config.max_task_complexity
433                ),
434                complexity: score.total,
435            };
436        }
437
438        // 情况 2:估算时间过长
439        if score.diagnostic.estimated_duration > self.config.max_task_duration {
440            return SplitCheck {
441                should_split: true,
442                reason: format!(
443                    "估算执行时间过长({} 分钟 > {} 分钟)",
444                    score.diagnostic.estimated_duration, self.config.max_task_duration
445                ),
446                complexity: score.total,
447            };
448        }
449
450        // 情况 3:子任务过多
451        if task.children.len() as u32 > self.config.max_children_per_node {
452            return SplitCheck {
453                should_split: true,
454                reason: format!(
455                    "子任务数量过多({} > {})",
456                    task.children.len(),
457                    self.config.max_children_per_node
458                ),
459                complexity: score.total,
460            };
461        }
462
463        // 情况 4:深度不够但任务复杂
464        if task.depth < self.config.min_depth && score.total > 50.0 && task.children.is_empty() {
465            return SplitCheck {
466                should_split: true,
467                reason: format!(
468                    "任务深度不够且复杂度较高(depth={}, complexity={:.1})",
469                    task.depth, score.total
470                ),
471                complexity: score.total,
472            };
473        }
474
475        SplitCheck {
476            should_split: false,
477            reason: "任务粒度合适".to_string(),
478            complexity: score.total,
479        }
480    }
481}
482
483/// 拆分检查结果
484#[derive(Debug, Clone)]
485pub struct SplitCheck {
486    pub should_split: bool,
487    pub reason: String,
488    pub complexity: f64,
489}
490
491impl TaskGranularityController {
492    /// 检查任务列表是否需要合并
493    pub fn should_merge(&self, tasks: &[TaskNode], modules: Option<&[SystemModule]>) -> MergeCheck {
494        if tasks.len() < 2 {
495            return MergeCheck {
496                should_merge: false,
497                reason: "任务数量不足 2 个".to_string(),
498                task_ids: Vec::new(),
499            };
500        }
501
502        // 检查是否是兄弟任务
503        let parent_ids: std::collections::HashSet<_> =
504            tasks.iter().filter_map(|t| t.parent_id.clone()).collect();
505        if parent_ids.len() > 1 {
506            return MergeCheck {
507                should_merge: false,
508                reason: "任务不是兄弟节点".to_string(),
509                task_ids: Vec::new(),
510            };
511        }
512
513        // 计算平均复杂度
514        let complexities: Vec<_> = tasks
515            .iter()
516            .map(|t| {
517                let module = modules.and_then(|ms| {
518                    ms.iter()
519                        .find(|m| Some(&m.id) == t.blueprint_module_id.as_ref())
520                });
521                self.assess_complexity(t, module)
522            })
523            .collect();
524
525        let avg_complexity =
526            complexities.iter().map(|s| s.total).sum::<f64>() / complexities.len() as f64;
527
528        // 情况 1:所有任务复杂度都很低
529        if avg_complexity < self.config.min_task_complexity {
530            let too_simple: Vec<_> = tasks
531                .iter()
532                .zip(complexities.iter())
533                .filter(|(_, s)| s.total < self.config.min_task_complexity)
534                .map(|(t, _)| t.id.clone())
535                .collect();
536
537            if too_simple.len() >= 2 {
538                return MergeCheck {
539                    should_merge: true,
540                    reason: format!(
541                        "多个任务复杂度过低(平均 {:.1} < {})",
542                        avg_complexity, self.config.min_task_complexity
543                    ),
544                    task_ids: too_simple,
545                };
546            }
547        }
548
549        // 情况 2:任务数量过多且平均复杂度低
550        if tasks.len() as u32 > self.config.max_children_per_node && avg_complexity < 30.0 {
551            return MergeCheck {
552                should_merge: true,
553                reason: format!(
554                    "任务数量过多({} > {})且复杂度较低",
555                    tasks.len(),
556                    self.config.max_children_per_node
557                ),
558                task_ids: tasks.iter().map(|t| t.id.clone()).collect(),
559            };
560        }
561
562        MergeCheck {
563            should_merge: false,
564            reason: "任务粒度合适".to_string(),
565            task_ids: Vec::new(),
566        }
567    }
568}
569
570/// 合并检查结果
571#[derive(Debug, Clone)]
572pub struct MergeCheck {
573    pub should_merge: bool,
574    pub reason: String,
575    pub task_ids: Vec<String>,
576}
577
578impl TaskGranularityController {
579    // --------------------------------------------------------------------------
580    // 自动调整
581    // --------------------------------------------------------------------------
582
583    /// 自动调整任务树粒度
584    pub fn auto_adjust(
585        &self,
586        tree: &TaskTree,
587        modules: Option<&[SystemModule]>,
588    ) -> AdjustmentResult {
589        let mut result = AdjustmentResult::default();
590
591        // 收集所有任务
592        let mut all_tasks = Vec::new();
593        self.collect_all_tasks(&tree.root, &mut all_tasks);
594
595        let mut total_complexity = 0.0;
596        let mut total_depth = 0u32;
597        let mut total_children = 0usize;
598
599        for task in &all_tasks {
600            let module = modules.and_then(|ms| {
601                ms.iter()
602                    .find(|m| Some(&m.id) == task.blueprint_module_id.as_ref())
603            });
604            let complexity = self.assess_complexity(task, module);
605
606            total_complexity += complexity.total;
607            total_depth += task.depth;
608            total_children += task.children.len();
609
610            // 统计复杂度分布
611            if complexity.total < self.config.min_task_complexity {
612                result.stats.too_simple += 1;
613            } else if complexity.total > self.config.max_task_complexity {
614                result.stats.too_complex += 1;
615            } else {
616                result.stats.just_right += 1;
617            }
618
619            // 更新最大值
620            if task.depth > result.stats.max_depth {
621                result.stats.max_depth = task.depth;
622            }
623            if task.children.len() as u32 > result.stats.max_children {
624                result.stats.max_children = task.children.len() as u32;
625            }
626
627            // 检查是否需要拆分
628            let split_check = self.should_split(task, module);
629            if split_check.should_split {
630                result
631                    .split_suggestions
632                    .push(self.generate_split_suggestion(task, module, &split_check));
633            }
634        }
635
636        // 计算统计信息
637        let task_count = all_tasks.len() as f64;
638        result.stats.total_tasks = all_tasks.len() as u32;
639        result.stats.avg_complexity = total_complexity / task_count;
640        result.stats.avg_depth = total_depth as f64 / task_count;
641        result.stats.avg_children = total_children as f64 / task_count;
642
643        // 检测结构问题
644        self.detect_structure_issues(&result.stats, &mut result.issues);
645
646        // 判断是否需要调整
647        result.needs_adjustment = !result.split_suggestions.is_empty()
648            || !result.merge_suggestions.is_empty()
649            || result
650                .issues
651                .iter()
652                .any(|i| i.severity == IssueSeverity::High);
653
654        result
655    }
656
657    /// 收集所有任务
658    fn collect_all_tasks<'a>(&self, node: &'a TaskNode, result: &mut Vec<&'a TaskNode>) {
659        result.push(node);
660        for child in &node.children {
661            self.collect_all_tasks(child, result);
662        }
663    }
664
665    /// 生成拆分建议
666    fn generate_split_suggestion(
667        &self,
668        task: &TaskNode,
669        module: Option<&SystemModule>,
670        split_check: &SplitCheck,
671    ) -> SplitSuggestion {
672        let mut suggested_splits = Vec::new();
673
674        // 策略 1:按功能点拆分
675        if task.description.contains("和") || task.description.contains("及") {
676            suggested_splits.push(SuggestedSplit {
677                name: format!("{} - 功能A", task.name),
678                description: "拆分为独立的功能点".to_string(),
679                strategy: SplitStrategy::ByFunction,
680            });
681            suggested_splits.push(SuggestedSplit {
682                name: format!("{} - 功能B", task.name),
683                description: "拆分为独立的功能点".to_string(),
684                strategy: SplitStrategy::ByFunction,
685            });
686        }
687
688        // 策略 2:按层次拆分
689        if let Some(m) = module {
690            match m.module_type {
691                super::types::ModuleType::Frontend => {
692                    suggested_splits.push(SuggestedSplit {
693                        name: format!("{} - UI组件", task.name),
694                        description: "实现用户界面组件".to_string(),
695                        strategy: SplitStrategy::ByLayer,
696                    });
697                    suggested_splits.push(SuggestedSplit {
698                        name: format!("{} - 业务逻辑", task.name),
699                        description: "实现业务逻辑处理".to_string(),
700                        strategy: SplitStrategy::ByLayer,
701                    });
702                }
703                super::types::ModuleType::Backend => {
704                    suggested_splits.push(SuggestedSplit {
705                        name: format!("{} - API接口", task.name),
706                        description: "实现 API 接口定义".to_string(),
707                        strategy: SplitStrategy::ByLayer,
708                    });
709                    suggested_splits.push(SuggestedSplit {
710                        name: format!("{} - 业务逻辑", task.name),
711                        description: "实现核心业务逻辑".to_string(),
712                        strategy: SplitStrategy::ByLayer,
713                    });
714                }
715                _ => {}
716            }
717        }
718
719        // 如果没有特定的拆分策略,提供通用拆分
720        if suggested_splits.is_empty() {
721            suggested_splits.push(SuggestedSplit {
722                name: format!("{} - 第一部分", task.name),
723                description: "拆分任务的第一部分".to_string(),
724                strategy: SplitStrategy::ByFunction,
725            });
726            suggested_splits.push(SuggestedSplit {
727                name: format!("{} - 第二部分", task.name),
728                description: "拆分任务的第二部分".to_string(),
729                strategy: SplitStrategy::ByFunction,
730            });
731        }
732
733        SplitSuggestion {
734            task_id: task.id.clone(),
735            task_name: task.name.clone(),
736            reason: split_check.reason.clone(),
737            complexity: split_check.complexity,
738            suggested_splits: suggested_splits.into_iter().take(5).collect(),
739        }
740    }
741
742    /// 检测树结构问题
743    fn detect_structure_issues(&self, stats: &AdjustmentStats, issues: &mut Vec<StructureIssue>) {
744        // 检查树深度
745        if stats.max_depth > self.config.max_depth {
746            issues.push(StructureIssue {
747                issue_type: IssueType::TooDeep,
748                task_id: None,
749                task_name: None,
750                description: format!(
751                    "任务树过深({} > {}),建议减少层级",
752                    stats.max_depth, self.config.max_depth
753                ),
754                severity: IssueSeverity::High,
755            });
756        } else if stats.max_depth < self.config.min_depth {
757            issues.push(StructureIssue {
758                issue_type: IssueType::TooShallow,
759                task_id: None,
760                task_name: None,
761                description: format!(
762                    "任务树过浅({} < {}),建议增加细化",
763                    stats.max_depth, self.config.min_depth
764                ),
765                severity: IssueSeverity::Medium,
766            });
767        }
768
769        // 检查子任务数量
770        if stats.max_children > self.config.max_children_per_node {
771            issues.push(StructureIssue {
772                issue_type: IssueType::TooManyChildren,
773                task_id: None,
774                task_name: None,
775                description: format!(
776                    "某些节点子任务过多(最多 {} > {})",
777                    stats.max_children, self.config.max_children_per_node
778                ),
779                severity: IssueSeverity::High,
780            });
781        }
782
783        // 检查粒度问题
784        if stats.too_simple > stats.total_tasks * 30 / 100 {
785            issues.push(StructureIssue {
786                issue_type: IssueType::TooShallow,
787                task_id: None,
788                task_name: None,
789                description: format!(
790                    "{} 个任务({}%)复杂度过低,建议合并",
791                    stats.too_simple,
792                    stats.too_simple * 100 / stats.total_tasks
793                ),
794                severity: IssueSeverity::High,
795            });
796        }
797
798        if stats.too_complex > stats.total_tasks * 20 / 100 {
799            issues.push(StructureIssue {
800                issue_type: IssueType::TooDeep,
801                task_id: None,
802                task_name: None,
803                description: format!(
804                    "{} 个任务({}%)复杂度过高,建议拆分",
805                    stats.too_complex,
806                    stats.too_complex * 100 / stats.total_tasks
807                ),
808                severity: IssueSeverity::High,
809            });
810        }
811    }
812}
813
814impl Default for TaskGranularityController {
815    fn default() -> Self {
816        Self::new(GranularityConfig::default())
817    }
818}
819
820// ============================================================================
821// 工厂函数
822// ============================================================================
823
824/// 创建任务粒度控制器
825pub fn create_task_granularity_controller(
826    config: Option<GranularityConfig>,
827) -> TaskGranularityController {
828    TaskGranularityController::new(config.unwrap_or_default())
829}
830
831#[cfg(test)]
832mod tests {
833    use super::*;
834
835    #[test]
836    fn test_config_default() {
837        let config = GranularityConfig::default();
838        assert_eq!(config.min_task_complexity, 15.0);
839        assert_eq!(config.max_task_complexity, 75.0);
840        assert_eq!(config.ideal_task_duration, 30);
841    }
842
843    #[test]
844    fn test_complexity_weights_default() {
845        let weights = ComplexityWeights::default();
846        let total = weights.code_size
847            + weights.dependencies
848            + weights.interfaces
849            + weights.test_coverage
850            + weights.description_length
851            + weights.children_count;
852        assert!((total - 1.0).abs() < 0.001);
853    }
854
855    #[test]
856    fn test_assess_complexity() {
857        let controller = TaskGranularityController::default();
858        let task = TaskNode::new(
859            "测试任务".to_string(),
860            "这是一个测试任务描述".to_string(),
861            1,
862        );
863
864        let score = controller.assess_complexity(&task, None);
865
866        assert!(score.total >= 0.0);
867        assert!(score.total <= 100.0);
868        assert_eq!(score.diagnostic.depth, 1);
869    }
870
871    #[test]
872    fn test_should_split_simple_task() {
873        let controller = TaskGranularityController::default();
874        let task = TaskNode::new("简单任务".to_string(), "描述".to_string(), 2);
875
876        let check = controller.should_split(&task, None);
877
878        assert!(!check.should_split);
879    }
880
881    #[test]
882    fn test_should_merge_few_tasks() {
883        let controller = TaskGranularityController::default();
884        let task = TaskNode::new("任务".to_string(), "描述".to_string(), 1);
885
886        let check = controller.should_merge(&[task], None);
887
888        assert!(!check.should_merge);
889        assert_eq!(check.reason, "任务数量不足 2 个");
890    }
891}