# handlers/change_handler.py - 需求变更菜单处理 # 菜单 2 入口 / 操作 A 单条变更 / 操作 C 新增需求 from rich.console import Console from rich.panel import Panel from rich.prompt import Confirm, Prompt from rich.rule import Rule from rich.table import Table from constants import CHG_ADD, CHG_EDIT from core_utils import ( active_reqs, generate_code, generate_signatures, log_change, merge_signatures_to_main, patch_and_save_signatures, write_readme, ) from core.code_generator import CodeGenerator from core.llm_client import LLMClient from core.requirement_analyzer import RequirementAnalyzer from database.db_manager import DBManager from database.models import Project from handlers.module_handler import change_module_requirements from ui.display import ( print_functional_requirements, print_module_summary, print_signatures_preview, ) from ui.prompts import ( load_knowledge_optional, pause, select_project, ) from utils.output_writer import ( ensure_project_dir, write_function_signatures_json, ) console = Console() db = DBManager() # ══════════════════════════════════════════════════════ # 菜单 2:变更项目需求(入口) # ══════════════════════════════════════════════════════ def menu_change_requirements() -> None: console.print(Rule("[bold blue]变更项目需求[/bold blue]")) project = select_project("请选择要变更需求的项目 ID") if not project: return func_reqs = db.list_functional_requirements(project.id) if not active_reqs(func_reqs): console.print("[yellow]当前项目暂无功能需求。[/yellow]") pause() return output_dir = ensure_project_dir(project.name) knowledge_text = load_knowledge_optional() change_menu = [ ("A", "🔧 变更指定功能需求", "按需求 ID 修改单条需求"), ("B", "📦 变更指定模块需求", "按模块批量操作模块内所有需求"), ("C", "➕ 新增需求", "输入新需求文本,LLM 分解后生成代码"), ] while True: console.print() print_functional_requirements(active_reqs(func_reqs)) console.print(Rule("[bold]变更操作[/bold]")) table = Table(show_header=False, box=None, padding=(0, 2)) table.add_column("选项", style="bold yellow", width=5) table.add_column("功能", style="bold", width=22) table.add_column("说明", style="dim", width=38) for key, name, desc in change_menu: table.add_row(f"[{key}]", name, desc) table.add_row("[back]", "↩ 返回主菜单", "") console.print(table) action = Prompt.ask( "请选择操作", choices=["A", "B", "C", "back"], default="back", ) if action == "A": change_existing_requirement( project=project, func_reqs=func_reqs, output_dir=output_dir, knowledge_text=knowledge_text, ) elif action == "B": change_module_requirements( project=project, func_reqs=func_reqs, output_dir=output_dir, knowledge_text=knowledge_text, ) elif action == "C": add_new_requirements( project=project, func_reqs=func_reqs, output_dir=output_dir, knowledge_text=knowledge_text, ) else: break # 每次操作后刷新需求列表 func_reqs = db.list_functional_requirements(project.id) pause() # ══════════════════════════════════════════════════════ # 操作 A:变更指定功能需求(单条) # ══════════════════════════════════════════════════════ def change_existing_requirement( project: Project, func_reqs: list, output_dir: str, knowledge_text: str = "", ) -> None: """按需求 ID 修改单条功能需求,确认后重新生成签名与代码,记录变更历史。""" req_id_str = Prompt.ask("请输入要变更的功能需求 ID") try: req_id = int(req_id_str) except ValueError: console.print("[red]ID 必须为整数[/red]") return req = db.get_functional_requirement(req_id) if not req or req.project_id != project.id: console.print("[red]需求 ID 不存在或不属于当前项目[/red]") return if req.status == "deleted": console.print("[red]该需求已被删除,无法变更[/red]") return console.print(f"\n当前需求: [bold]{req.title}[/bold](ID={req.id})") new_description = Prompt.ask(" 新描述", default=req.description) new_priority = Prompt.ask( " 新优先级", default=req.priority, choices=["high", "medium", "low"], ) new_module = Prompt.ask(" 新模块名", default=req.module) changed = ( new_description != req.description or new_priority != req.priority or new_module != req.module ) if not changed: console.print("[dim]未检测到任何变更,跳过。[/dim]") return change_summary = ( f"需求 '{req.title}'(ID={req.id})变更:\n" f" 描述: '{req.description}' → '{new_description}'\n" f" 优先级: '{req.priority}' → '{new_priority}'\n" f" 模块: '{req.module}' → '{new_module}'" ) console.print(Panel(change_summary, title="📝 变更预览", border_style="yellow")) if not Confirm.ask("确认执行以上变更?", default=True): console.print("[yellow]已取消变更。[/yellow]") return req.description = new_description req.priority = new_priority req.module = new_module req.status = "pending" db.update_functional_requirement(req) log_change( project_id = project.id, change_type = CHG_EDIT, summary = change_summary, module = new_module, req_id = req.id, ) console.print(f"[green]✓ 需求 ID={req.id} 已更新[/green]") console.print("\n[cyan]正在重新生成签名与代码...[/cyan]") llm = LLMClient() analyzer = RequirementAnalyzer(llm) generator = CodeGenerator(llm) change_sig_file = f"change_{req.id}_signatures.json" signatures = generate_signatures(analyzer, [req], knowledge_text) write_function_signatures_json( output_dir=output_dir, signatures=signatures, project_name=project.name, project_description=project.description or "", file_name=change_sig_file, ) print_signatures_preview(signatures) code_files = generate_code( generator=generator, project=project, func_reqs=[req], output_dir=output_dir, knowledge_text=knowledge_text, signatures=signatures, ) for cf in code_files: db.upsert_code_file(cf) req.status = "generated" db.update_functional_requirement(req) patch_and_save_signatures( project=project, signatures=signatures, code_files=code_files, output_dir=output_dir, file_name=change_sig_file, ) merge_signatures_to_main(project, signatures, output_dir) console.print(f"[green]✓ 变更完成,代码已重新生成[/green]") # ══════════════════════════════════════════════════════ # 操作 C:新增需求 # ══════════════════════════════════════════════════════ def add_new_requirements( project: Project, func_reqs: list, output_dir: str, knowledge_text: str = "", ) -> None: """输入新需求描述,LLM 分解后持久化并生成代码,记录变更历史。""" raw_reqs = db.get_raw_requirements_by_project(project.id) if not raw_reqs: console.print("[red]当前项目无原始需求记录,请先完成初始需求录入流程。[/red]") return raw_req_id = raw_reqs[0].id console.print("\n[bold]请输入新增需求描述[/bold](输入空行结束):") lines = [] while True: line = input() if line == "": break lines.append(line) new_req_text = "\n".join(lines).strip() if not new_req_text: console.print("[yellow]新增需求内容为空,已取消。[/yellow]") return llm = LLMClient() analyzer = RequirementAnalyzer(llm) with console.status("[cyan]LLM 正在分解新增需求...[/cyan]"): new_reqs = analyzer.add_new_requirements( new_requirement_text = new_req_text, project_id = project.id, raw_req_id = raw_req_id, existing_reqs = active_reqs(func_reqs), knowledge = knowledge_text, ) console.print(f"[green]✓ 分解完成,新增 {len(new_reqs)} 条功能需求[/green]") print_functional_requirements(new_reqs) change_summary = ( f"新增 {len(new_reqs)} 条功能需求:\n" + "\n".join( f" + [{r.index_no}] {r.title} ({r.function_name})" for r in new_reqs ) ) console.print(Panel(change_summary, title="📝 新增需求预览", border_style="green")) if not Confirm.ask("确认新增以上需求并生成代码?", default=True): console.print("[yellow]已取消新增。[/yellow]") return # ── 模块分类 ────────────────────────────────────── with console.status("[cyan]LLM 正在进行模块分类...[/cyan]"): module_map = analyzer.classify_modules(new_reqs, knowledge_text) name_to_module = {item["function_name"]: item["module"] for item in module_map} for req in new_reqs: if req.function_name in name_to_module: req.module = name_to_module[req.function_name] print_module_summary(new_reqs) # ── 持久化新需求 ────────────────────────────────── all_reqs = db.list_functional_requirements(project.id) max_idx = max((r.index_no for r in all_reqs), default=0) for i, req in enumerate(new_reqs): req.index_no = max_idx + i + 1 req.id = db.create_functional_requirement(req) console.print(f"[green]✓ 新增功能需求已持久化[/green]") # ── 记录变更历史 ────────────────────────────────── log_change( project_id = project.id, change_type = CHG_ADD, summary = change_summary, ) # ── 生成签名与代码 ──────────────────────────────── signatures = generate_signatures(analyzer, new_reqs, knowledge_text) write_function_signatures_json( output_dir=output_dir, signatures=signatures, project_name=project.name, project_description=project.description or "", file_name="function_signatures_new.json", ) print_signatures_preview(signatures) generator = CodeGenerator(llm) code_files = generate_code( generator=generator, project=project, func_reqs=new_reqs, output_dir=output_dir, knowledge_text=knowledge_text, signatures=signatures, ) for cf in code_files: db.upsert_code_file(cf) for req in new_reqs: req.status = "generated" db.update_functional_requirement(req) patch_and_save_signatures( project=project, signatures=signatures, code_files=code_files, output_dir=output_dir, file_name="function_signatures_new.json", ) merge_signatures_to_main(project, signatures, output_dir) # ── 更新 README ─────────────────────────────────── all_active = active_reqs(db.list_functional_requirements(project.id)) write_readme(project, all_active, output_dir) console.print(Panel.fit( f"[bold green]✅ 新增需求处理完成![/bold green]\n" f"共生成 {len(code_files)} 个代码文件", border_style="green", ))