AIDeveloper-PC/requirements_generator/handlers/change_handler.py

321 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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",
))