This commit is contained in:
sonto.lau 2026-03-07 00:47:54 +08:00
parent d2b1a0b657
commit d48ec86026
1 changed files with 501 additions and 1 deletions

View File

@ -226,14 +226,514 @@ def module_redescribe(
with console.status( with console.status(
f"[cyan]LLM 正在重新分解模块 '{target_module}' 需求...[/cyan]" f"[cyan]LLM 正在重新分解模块 '{target_module}' 需求...[/cyan]"
): ):
# ✅ 修复:移除不存在的 module_hint 参数
# 分解后统一通过 req.module = target_module 强制设置模块归属
new_reqs = analyzer.decompose( new_reqs = analyzer.decompose(
raw_requirement = new_desc_text, raw_requirement = new_desc_text,
project_id = project.id, project_id = project.id,
raw_req_id = raw_req_id, raw_req_id = raw_req_id,
knowledge = knowledge_text, knowledge = knowledge_text,
module_hint = target_module,
) )
# 强制将所有新需求归属到当前模块
for req in new_reqs:
req.module = target_module
console.print(f"[green]✓ 重新分解完成,共 {len(new_reqs)} 条新功能需求[/green]")
print_req_diff(old_reqs=module_reqs, new_reqs=new_reqs)
if not Confirm.ask(
f"确认用以上 {len(new_reqs)} 条新需求替换模块 '{target_module}' 的旧需求?",
default=True,
):
console.print("[yellow]已取消重新描述。[/yellow]")
return
# ── 软删除旧需求 ──────────────────────────────────
deleted_ids = []
for req in module_reqs:
req.status = "deleted"
db.update_functional_requirement(req)
deleted_ids.append(req.id)
console.print(
f"[dim]✓ 已软删除旧需求 {len(deleted_ids)}ID: {deleted_ids}[/dim]"
)
# ── 持久化新需求 ──────────────────────────────────
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]✓ 新功能需求已持久化,共 {len(new_reqs)} 条[/green]")
# ── 记录变更历史 ──────────────────────────────────
change_summary = (
f"模块 '{target_module}' 需求重新描述:\n"
f" 旧需求 {len(module_reqs)} 条(已软删除):\n"
+ "\n".join(f" - [{r.id}] {r.title}" for r in module_reqs)
+ f"\n 新需求 {len(new_reqs)} 条:\n"
+ "\n".join(f" + [{r.id}] {r.title}" for r in new_reqs)
)
log_change(
project_id = project.id,
change_type = CHG_REDESCRIBE,
summary = change_summary,
module = target_module,
)
# ── 重新生成签名与代码 ────────────────────────────
console.print(
f"\n[cyan]正在为模块 '{target_module}' 重新生成签名与代码...[/cyan]"
)
generator = CodeGenerator(llm)
signatures = generate_signatures(analyzer, new_reqs, knowledge_text)
mod_sig_file = f"module_{target_module}_redescribe_signatures.json"
write_function_signatures_json(
output_dir=output_dir, signatures=signatures,
project_name=project.name, project_description=project.description or "",
file_name=mod_sig_file,
)
print_signatures_preview(signatures)
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=mod_sig_file,
)
merge_signatures_to_main(project, signatures, output_dir)
all_active = active_reqs(db.list_functional_requirements(project.id))
write_readme(project, all_active, output_dir)
console.print(Panel.fit(
f"[bold green]✅ 模块 '{target_module}' 需求重新描述完成![/bold green]\n"
f"新需求 {len(new_reqs)} 条,生成代码 {len(code_files)} 个文件",
border_style="green",
))
# ══════════════════════════════════════════════════════
# B3逐条修改模块内需求
# ══════════════════════════════════════════════════════
def module_edit_each(
project: Project,
module_reqs: list,
output_dir: str,
knowledge_text: str = "",
) -> None:
"""对模块内需求逐条展示并允许修改,每条确认后立即重新生成签名与代码,记录变更历史。"""
llm = LLMClient()
analyzer = RequirementAnalyzer(llm)
generator = CodeGenerator(llm)
for req in module_reqs:
console.print(Rule(f"[cyan]需求 ID={req.id} · {req.title}[/cyan]"))
console.print(
f" 描述: {req.description}\n"
f" 优先级: {req.priority}\n"
f" 模块: {req.module}"
)
if not Confirm.ask("是否修改此需求?", default=False):
continue
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]")
continue
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]")
continue
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" [cyan]正在重新生成 {req.function_name} 的签名与代码...[/cyan]")
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,
)
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]✓ 需求 ID={req.id} 变更完成[/green]")
console.print("[green]✓ 模块内需求逐条修改完成[/green]")
# ══════════════════════════════════════════════════════
# B4批量重新生成模块代码
# ══════════════════════════════════════════════════════
def module_regen_code(
project: Project,
module_reqs: list,
output_dir: str,
knowledge_text: str = "",
) -> None:
"""保持需求内容不变,对整个模块批量重新生成签名与代码,记录变更历史。"""
module_name = module_reqs[0].module if module_reqs else "未知模块"
console.print(Panel(
f"模块:[bold magenta]{module_name}[/bold magenta]\n"
f"需求数量:{len(module_reqs)}\n"
+ "\n".join(
f" · [{r.id}] {r.title} ({r.function_name})"
for r in module_reqs
),
title="⚡ 批量重新生成预览",
border_style="cyan",
))
if not Confirm.ask(
f"确认对模块 '{module_name}'{len(module_reqs)} 条需求批量重新生成代码?",
default=True,
):
console.print("[yellow]已取消。[/yellow]")
return
llm = LLMClient()
analyzer = RequirementAnalyzer(llm)
generator = CodeGenerator(llm)
for req in module_reqs:
req.status = "pending"
db.update_functional_requirement(req)
console.print(f"\n[cyan]正在为模块 '{module_name}' 生成函数签名...[/cyan]")
signatures = generate_signatures(analyzer, module_reqs, knowledge_text)
mod_sig_file = f"module_{module_name}_signatures.json"
write_function_signatures_json(
output_dir=output_dir, signatures=signatures,
project_name=project.name, project_description=project.description or "",
file_name=mod_sig_file,
)
print_signatures_preview(signatures)
console.print(f"\n[cyan]正在为模块 '{module_name}' 生成代码文件...[/cyan]")
code_files = generate_code(
generator=generator, project=project, func_reqs=module_reqs,
output_dir=output_dir, knowledge_text=knowledge_text, signatures=signatures,
)
for cf in code_files:
db.upsert_code_file(cf)
for req in module_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=mod_sig_file,
)
merge_signatures_to_main(project, signatures, output_dir)
change_summary = (
f"模块 '{module_name}' 批量重新生成代码,共 {len(module_reqs)} 条需求:\n"
+ "\n".join(f" · [{r.id}] {r.title}" for r in module_reqs)
)
log_change(
project_id = project.id,
change_type = CHG_REGEN,
summary = change_summary,
module = module_name,
)
console.print(
f"[bold green]✅ 模块 '{module_name}' 批量重新生成完成!"
f"{len(code_files)} 个文件[/bold green]"
)# handlers/module_handler.py - 模块级变更处理
# B1 重命名 / B2 重新描述 / B3 逐条修改 / B4 批量重新生成
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
import config
from constants import CHG_EDIT, CHG_REGEN, CHG_RENAME, CHG_REDESCRIBE
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 FunctionalRequirement, Project
from ui.display import (
print_functional_requirements, print_module_list,
print_req_diff, print_signatures_preview,
)
from utils.output_writer import write_function_signatures_json
console = Console()
db = DBManager()
# ══════════════════════════════════════════════════════
# 模块变更入口(子菜单)
# ══════════════════════════════════════════════════════
def change_module_requirements(
project: Project,
func_reqs: list,
output_dir: str,
knowledge_text: str = "",
) -> None:
"""
选择目标模块进入模块级变更子菜单
B1 · 重命名模块
B2 · 重新描述模块需求
B3 · 逐条修改模块内需求
B4 · 批量重新生成模块代码
"""
console.print(Rule("[bold magenta]变更指定模块需求[/bold magenta]"))
modules = print_module_list(func_reqs)
if not modules:
console.print("[yellow]当前项目暂无模块信息。[/yellow]")
return
mod_idx_str = Prompt.ask("请输入模块序号(输入 0 返回)", default="0")
if mod_idx_str == "0":
return
try:
mod_idx = int(mod_idx_str) - 1
if mod_idx < 0 or mod_idx >= len(modules):
raise ValueError
target_module = modules[mod_idx]
except ValueError:
console.print("[red]序号无效[/red]")
return
module_reqs = [
r for r in func_reqs
if (r.module or config.DEFAULT_MODULE) == target_module
and r.status != "deleted"
]
console.print(
f"\n[bold magenta]模块:{target_module}[/bold magenta]"
f"{len(module_reqs)} 条需求"
)
print_functional_requirements(module_reqs)
sub_menu = [
("B1", "✏️ 重命名模块", "修改模块名,批量更新该模块所有需求"),
("B2", "📝 重新描述模块需求", "重新输入模块需求描述LLM 重新分解并生成代码"),
("B3", "🔧 逐条修改模块内需求", "对模块内每条需求单独修改并重新生成"),
("B4", "⚡ 批量重新生成模块代码", "保持需求内容不变,整体重新生成签名+代码"),
]
while True:
console.print(Rule())
table = Table(show_header=False, box=None, padding=(0, 2))
table.add_column("选项", style="bold yellow", width=5)
table.add_column("功能", style="bold", width=26)
table.add_column("说明", style="dim", width=42)
for key, name, desc in sub_menu:
table.add_row(f"[{key}]", name, desc)
table.add_row("[back]", "↩ 返回", "")
console.print(table)
sub_action = Prompt.ask(
"请选择操作",
choices=["B1", "B2", "B3", "B4", "back"],
default="back",
)
if sub_action == "B1":
module_rename(
project=project, module_reqs=module_reqs,
old_module=target_module,
)
break
elif sub_action == "B2":
module_redescribe(
project=project, module_reqs=module_reqs,
target_module=target_module, output_dir=output_dir,
knowledge_text=knowledge_text,
)
break
elif sub_action == "B3":
module_edit_each(
project=project, module_reqs=module_reqs,
output_dir=output_dir, knowledge_text=knowledge_text,
)
module_reqs = [
db.get_functional_requirement(r.id)
for r in module_reqs
if db.get_functional_requirement(r.id)
]
elif sub_action == "B4":
module_regen_code(
project=project, module_reqs=module_reqs,
output_dir=output_dir, knowledge_text=knowledge_text,
)
else:
break
# ══════════════════════════════════════════════════════
# B1重命名模块
# ══════════════════════════════════════════════════════
def module_rename(
project: Project,
module_reqs: list,
old_module: str,
) -> None:
"""将指定模块重命名,批量更新该模块下所有需求的 module 字段,记录变更历史。"""
new_module = Prompt.ask(f"请输入新模块名(当前:{old_module}")
if not new_module.strip():
console.print("[red]模块名不能为空[/red]")
return
if new_module == old_module:
console.print("[dim]模块名未变更,跳过。[/dim]")
return
change_summary = (
f"模块重命名:'{old_module}''{new_module}'\n"
f" 影响需求数:{len(module_reqs)}\n"
+ "\n".join(f" · [{r.id}] {r.title}" for r in module_reqs)
)
console.print(Panel(change_summary, title="📝 模块重命名预览", border_style="yellow"))
if not Confirm.ask("确认执行模块重命名?", default=True):
console.print("[yellow]已取消。[/yellow]")
return
for req in module_reqs:
req.module = new_module
db.update_functional_requirement(req)
log_change(
project_id = project.id,
change_type = CHG_RENAME,
summary = change_summary,
module = old_module,
)
console.print(
f"[green]✓ 模块已重命名:'{old_module}''{new_module}'"
f"共更新 {len(module_reqs)} 条需求[/green]"
)
# ══════════════════════════════════════════════════════
# B2重新描述模块需求
# ══════════════════════════════════════════════════════
def module_redescribe(
project: Project,
module_reqs: list,
target_module: str,
output_dir: str,
knowledge_text: str = "",
) -> None:
"""
用户重新输入该模块的需求描述文本
LLM 重新分解为功能需求展示新旧对比后确认
软删除旧需求持久化新需求重新生成签名+代码记录变更历史
"""
console.print(Rule(
f"[bold magenta]重新描述模块 '{target_module}' 需求[/bold magenta]"
))
console.print("[bold]当前模块需求:[/bold]")
print_functional_requirements(module_reqs)
console.print(
f"\n[bold]请重新输入模块 '[magenta]{target_module}[/magenta]'"
" 的需求描述[/bold](输入空行结束):"
)
lines = []
while True:
line = input()
if line == "":
break
lines.append(line)
new_desc_text = "\n".join(lines).strip()
if not new_desc_text:
console.print("[yellow]输入为空,已取消。[/yellow]")
return
llm = LLMClient()
analyzer = RequirementAnalyzer(llm)
raw_req_id = module_reqs[0].raw_req_id if module_reqs else None
with console.status(
f"[cyan]LLM 正在重新分解模块 '{target_module}' 需求...[/cyan]"
):
# ✅ 修复:移除不存在的 module_hint 参数
# 分解后统一通过 req.module = target_module 强制设置模块归属
new_reqs = analyzer.decompose(
raw_requirement = new_desc_text,
project_id = project.id,
raw_req_id = raw_req_id,
knowledge = knowledge_text,
)
# 强制将所有新需求归属到当前模块
for req in new_reqs: for req in new_reqs:
req.module = target_module req.module = target_module