This commit is contained in:
sonto.lau 2026-03-07 00:10:39 +08:00
parent 0da31ea6c7
commit 4855388b7c
1 changed files with 606 additions and 275 deletions

View File

@ -12,7 +12,6 @@ from rich.table import Table
from rich.panel import Panel from rich.panel import Panel
from rich.prompt import Prompt, Confirm from rich.prompt import Prompt, Confirm
from rich.rule import Rule from rich.rule import Rule
from rich.text import Text
import config import config
from database.db_manager import DBManager from database.db_manager import DBManager
@ -51,7 +50,7 @@ def print_main_menu():
console.print(Rule("[bold cyan]主菜单[/bold cyan]")) console.print(Rule("[bold cyan]主菜单[/bold cyan]"))
menu_items = [ menu_items = [
("1", "📁 新建项目", "输入需求 → 分解 → 生成代码"), ("1", "📁 新建项目", "输入需求 → 分解 → 生成代码"),
("2", "🔄 变更项目需求", "变更已有需求 / 新增需求"), ("2", "🔄 变更项目需求", "变更已有需求 / 变更模块需求 / 新增需求"),
("3", "📋 查看所有项目", "列表展示全部项目"), ("3", "📋 查看所有项目", "列表展示全部项目"),
("4", "🔍 查看项目详情", "需求列表 / 模块分组 / 变更历史"), ("4", "🔍 查看项目详情", "需求列表 / 模块分组 / 变更历史"),
("5", "🗑 删除指定项目", "删除项目及其所有数据"), ("5", "🗑 删除指定项目", "删除项目及其所有数据"),
@ -59,8 +58,8 @@ def print_main_menu():
] ]
table = Table(show_header=False, box=None, padding=(0, 2)) table = Table(show_header=False, box=None, padding=(0, 2))
table.add_column("选项", style="bold yellow", width=4) table.add_column("选项", style="bold yellow", width=4)
table.add_column("功能", style="bold", width=20) table.add_column("功能", style="bold", width=22)
table.add_column("说明", style="dim", width=35) table.add_column("说明", style="dim", width=38)
for key, name, desc in menu_items: for key, name, desc in menu_items:
table.add_row(f"[{key}]", name, desc) table.add_row(f"[{key}]", name, desc)
console.print(table) console.print(table)
@ -68,7 +67,6 @@ def print_main_menu():
def print_projects_table(projects: List[Project]): def print_projects_table(projects: List[Project]):
"""以表格形式展示所有项目"""
if not projects: if not projects:
console.print("[yellow] 暂无项目记录。[/yellow]") console.print("[yellow] 暂无项目记录。[/yellow]")
return return
@ -79,18 +77,18 @@ def print_projects_table(projects: List[Project]):
table.add_column("描述", width=35) table.add_column("描述", width=35)
table.add_column("输出目录", style="dim", width=30) table.add_column("输出目录", style="dim", width=30)
for p in projects: for p in projects:
desc = p.description or ""
table.add_row( table.add_row(
str(p.id), str(p.id),
p.name, p.name,
p.language or "-", p.language or "-",
(p.description or "")[:40] + ("..." if len(p.description or "") > 40 else ""), desc[:40] + ("..." if len(desc) > 40 else ""),
p.output_dir or "-", p.output_dir or "-",
) )
console.print(table) console.print(table)
def print_functional_requirements(reqs: List[FunctionalRequirement]): def print_functional_requirements(reqs: List[FunctionalRequirement]):
"""以表格形式展示功能需求列表"""
if not reqs: if not reqs:
console.print("[yellow] 暂无功能需求。[/yellow]") console.print("[yellow] 暂无功能需求。[/yellow]")
return return
@ -103,12 +101,12 @@ def print_functional_requirements(reqs: List[FunctionalRequirement]):
table.add_column("优先级", width=8) table.add_column("优先级", width=8)
table.add_column("状态", width=10) table.add_column("状态", width=10)
table.add_column("描述", width=35) table.add_column("描述", width=35)
priority_color = {"high": "red", "medium": "yellow", "low": "green"} priority_color = {"high": "red", "medium": "yellow", "low": "green"}
status_color = {"pending": "yellow", "generated": "green", "failed": "red"} status_color = {"pending": "yellow", "generated": "green", "failed": "red"}
for req in reqs: for req in reqs:
pc = priority_color.get(req.priority, "white") pc = priority_color.get(req.priority, "white")
sc = status_color.get(req.status, "white") sc = status_color.get(req.status, "white")
desc = req.description
table.add_row( table.add_row(
str(req.index_no), str(req.index_no),
str(req.id) if req.id else "-", str(req.id) if req.id else "-",
@ -117,13 +115,12 @@ def print_functional_requirements(reqs: List[FunctionalRequirement]):
f"[code]{req.function_name}[/code]", f"[code]{req.function_name}[/code]",
f"[{pc}]{req.priority}[/{pc}]", f"[{pc}]{req.priority}[/{pc}]",
f"[{sc}]{req.status}[/{sc}]", f"[{sc}]{req.status}[/{sc}]",
req.description[:40] + "..." if len(req.description) > 40 else req.description, desc[:40] + "..." if len(desc) > 40 else desc,
) )
console.print(table) console.print(table)
def print_module_summary(reqs: List[FunctionalRequirement]): def print_module_summary(reqs: List[FunctionalRequirement]):
"""打印模块分组摘要"""
module_map: Dict[str, List[str]] = {} module_map: Dict[str, List[str]] = {}
for req in reqs: for req in reqs:
m = req.module or config.DEFAULT_MODULE m = req.module or config.DEFAULT_MODULE
@ -137,8 +134,28 @@ def print_module_summary(reqs: List[FunctionalRequirement]):
console.print(table) console.print(table)
def print_module_list(reqs: List[FunctionalRequirement]) -> List[str]:
"""打印带序号的模块列表,返回有序模块名列表。"""
module_map: Dict[str, List[FunctionalRequirement]] = {}
for req in reqs:
m = req.module or config.DEFAULT_MODULE
module_map.setdefault(m, []).append(req)
table = Table(title="📦 模块列表", show_lines=True)
table.add_column("序号", style="cyan", width=6)
table.add_column("模块名", style="magenta bold", width=22)
table.add_column("需求数量", style="yellow", width=8)
table.add_column("包含函数", width=45)
modules = sorted(module_map.keys())
for i, module in enumerate(modules, 1):
funcs = [r.function_name for r in module_map[module]]
table.add_row(str(i), module, str(len(funcs)), ", ".join(funcs))
console.print(table)
return modules
def print_signatures_preview(signatures: List[dict]): def print_signatures_preview(signatures: List[dict]):
"""以表格形式预览函数签名列表"""
table = Table(title="📄 函数签名预览", show_lines=True) table = Table(title="📄 函数签名预览", show_lines=True)
table.add_column("需求编号", style="cyan", width=8) table.add_column("需求编号", style="cyan", width=8)
table.add_column("模块", style="magenta", width=15) table.add_column("模块", style="magenta", width=15)
@ -162,7 +179,6 @@ def print_signatures_preview(signatures: List[dict]):
def print_change_history(histories: List[ChangeHistory]): def print_change_history(histories: List[ChangeHistory]):
"""展示变更历史"""
if not histories: if not histories:
console.print("[dim] 暂无变更历史。[/dim]") console.print("[dim] 暂无变更历史。[/dim]")
return return
@ -180,7 +196,6 @@ def print_change_history(histories: List[ChangeHistory]):
def select_project(prompt_text: str = "请选择项目 ID") -> Optional[Project]: def select_project(prompt_text: str = "请选择项目 ID") -> Optional[Project]:
"""列出所有项目并让用户选择,返回 Project 对象;取消返回 None"""
projects = db.list_projects() projects = db.list_projects()
if not projects: if not projects:
console.print("[yellow]当前暂无项目,请先新建项目。[/yellow]") console.print("[yellow]当前暂无项目,请先新建项目。[/yellow]")
@ -245,12 +260,7 @@ def menu_create_project():
console.print(f"[green]✓ 项目已创建: {project_name} (ID={project.id})[/green]") console.print(f"[green]✓ 项目已创建: {project_name} (ID={project.id})[/green]")
# ── 2. 知识库(可选)──────────────────────────── # ── 2. 知识库(可选)────────────────────────────
knowledge_text = "" knowledge_text = _load_knowledge_optional()
if Confirm.ask("是否加载知识库文件?", default=False):
kb_path = Prompt.ask("知识库文件路径(多个用逗号分隔)")
paths = [p.strip() for p in kb_path.split(",") if p.strip()]
knowledge_text = merge_knowledge_files(paths)
console.print(f"[dim]已加载 {len(paths)} 个知识库文件[/dim]")
# ── 3. 原始需求输入 ────────────────────────────── # ── 3. 原始需求输入 ──────────────────────────────
console.print("\n[bold]请选择需求来源:[/bold]") console.print("\n[bold]请选择需求来源:[/bold]")
@ -300,7 +310,6 @@ def menu_create_project():
console.print(f"[green]✓ 分解完成,共 {len(func_reqs)} 条功能需求[/green]") console.print(f"[green]✓ 分解完成,共 {len(func_reqs)} 条功能需求[/green]")
print_functional_requirements(func_reqs) print_functional_requirements(func_reqs)
# 交互式编辑分解结果
while True: while True:
action = Prompt.ask( action = Prompt.ask(
"操作", "操作",
@ -316,7 +325,6 @@ def menu_create_project():
elif action == "add": elif action == "add":
func_reqs = _interactive_add_req(func_reqs, project, raw_req_id) func_reqs = _interactive_add_req(func_reqs, project, raw_req_id)
# 持久化功能需求
for req in func_reqs: for req in func_reqs:
req.id = db.create_functional_requirement(req) req.id = db.create_functional_requirement(req)
console.print(f"[green]✓ 功能需求已持久化,共 {len(func_reqs)} 条[/green]") console.print(f"[green]✓ 功能需求已持久化,共 {len(func_reqs)} 条[/green]")
@ -334,16 +342,12 @@ def menu_create_project():
if Confirm.ask("是否手动调整模块归属?", default=False): if Confirm.ask("是否手动调整模块归属?", default=False):
func_reqs = _interactive_adjust_modules(func_reqs) func_reqs = _interactive_adjust_modules(func_reqs)
# ── 6. 确保输出目录 ────────────────────────────── # ── 6. 输出目录 ──────────────────────────────────
output_dir = ensure_project_dir(project.name) output_dir = ensure_project_dir(project.name)
# ── 7. 生成函数签名 ────────────────────────────── # ── 7. 生成函数签名 ──────────────────────────────
console.print("\n[bold]Step · 生成函数签名[/bold]", style="blue") console.print("\n[bold]Step · 生成函数签名[/bold]", style="blue")
signatures = _generate_signatures( signatures = _generate_signatures(analyzer, func_reqs, knowledge_text)
analyzer = analyzer,
func_reqs = func_reqs,
knowledge_text = knowledge_text,
)
json_path = write_function_signatures_json( json_path = write_function_signatures_json(
output_dir=output_dir, output_dir=output_dir,
signatures=signatures, signatures=signatures,
@ -414,21 +418,35 @@ def menu_change_requirements():
return return
output_dir = ensure_project_dir(project.name) output_dir = ensure_project_dir(project.name)
# 知识库(可选复用)
knowledge_text = _load_knowledge_optional() knowledge_text = _load_knowledge_optional()
change_menu = [
("A", "🔧 变更指定功能需求", "按需求 ID 修改单条需求"),
("B", "📦 变更指定模块需求", "按模块批量操作模块内所有需求"),
("C", " 新增需求", "输入新需求文本LLM 分解后生成代码"),
]
while True: while True:
console.print() console.print()
print_functional_requirements(func_reqs) print_functional_requirements(func_reqs)
console.print(Rule()) 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( action = Prompt.ask(
"请选择操作", "请选择操作",
choices=["A-变更已有需求", "B-新增需求", "back"], choices=["A", "B", "C", "back"],
default="back", default="back",
) )
if action == "A-变更已有需求": if action == "A":
_change_existing_requirement( _change_existing_requirement(
project=project, project=project,
func_reqs=func_reqs, func_reqs=func_reqs,
@ -437,7 +455,16 @@ def menu_change_requirements():
) )
func_reqs = db.list_functional_requirements(project.id) func_reqs = db.list_functional_requirements(project.id)
elif action == "B-新增需求": elif action == "B":
_change_module_requirements(
project=project,
func_reqs=func_reqs,
output_dir=output_dir,
knowledge_text=knowledge_text,
)
func_reqs = db.list_functional_requirements(project.id)
elif action == "C":
_add_new_requirements( _add_new_requirements(
project=project, project=project,
func_reqs=func_reqs, func_reqs=func_reqs,
@ -474,7 +501,6 @@ def menu_project_detail():
if not project: if not project:
return return
# 项目基本信息
console.print(Panel( console.print(Panel(
f"[bold]项目名称:[/bold]{project.name}\n" f"[bold]项目名称:[/bold]{project.name}\n"
f"[bold]语言: [/bold]{project.language}\n" f"[bold]语言: [/bold]{project.language}\n"
@ -484,21 +510,17 @@ def menu_project_detail():
border_style="cyan", border_style="cyan",
)) ))
# 功能需求列表
func_reqs = db.list_functional_requirements(project.id) func_reqs = db.list_functional_requirements(project.id)
print_functional_requirements(func_reqs) print_functional_requirements(func_reqs)
# 模块分组
if func_reqs: if func_reqs:
print_module_summary(func_reqs) print_module_summary(func_reqs)
# 变更历史
llm = LLMClient() llm = LLMClient()
analyzer = RequirementAnalyzer(llm) analyzer = RequirementAnalyzer(llm)
histories = analyzer.get_change_history(project.id) histories = analyzer.get_change_history(project.id)
print_change_history(histories) print_change_history(histories)
# 签名 JSON 预览
if project.output_dir: if project.output_dir:
sig_path = os.path.join(project.output_dir, "function_signatures.json") sig_path = os.path.join(project.output_dir, "function_signatures.json")
if os.path.exists(sig_path): if os.path.exists(sig_path):
@ -539,7 +561,6 @@ def menu_delete_project():
_pause() _pause()
return return
# 二次确认
confirm_name = Prompt.ask("请再次输入项目名称以确认") confirm_name = Prompt.ask("请再次输入项目名称以确认")
if confirm_name != project.name: if confirm_name != project.name:
console.print("[red]项目名称不匹配,删除已取消。[/red]") console.print("[red]项目名称不匹配,删除已取消。[/red]")
@ -552,7 +573,7 @@ def menu_delete_project():
# ══════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════
# 变更已有需求(内部) # 变更操作 A变更指定功能需求
# ══════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════
def _change_existing_requirement( def _change_existing_requirement(
@ -561,6 +582,7 @@ def _change_existing_requirement(
output_dir: str, output_dir: str,
knowledge_text: str = "", knowledge_text: str = "",
) -> None: ) -> None:
"""按需求 ID 修改单条功能需求,确认后重新生成签名与代码。"""
req_id_str = Prompt.ask("请输入要变更的功能需求 ID") req_id_str = Prompt.ask("请输入要变更的功能需求 ID")
try: try:
req_id = int(req_id_str) req_id = int(req_id_str)
@ -613,12 +635,10 @@ def _change_existing_requirement(
db.update_functional_requirement(req) db.update_functional_requirement(req)
console.print(f"[green]✓ 需求 ID={req.id} 已更新[/green]") console.print(f"[green]✓ 需求 ID={req.id} 已更新[/green]")
# 重新生成签名与代码
console.print("\n[cyan]正在重新生成签名与代码...[/cyan]") console.print("\n[cyan]正在重新生成签名与代码...[/cyan]")
change_sig_file = f"change_{req.id}_signatures.json" change_sig_file = f"change_{req.id}_signatures.json"
signatures = _generate_signatures(analyzer, [req], knowledge_text) signatures = _generate_signatures(analyzer, [req], knowledge_text)
json_path = write_function_signatures_json( write_function_signatures_json(
output_dir=output_dir, output_dir=output_dir,
signatures=signatures, signatures=signatures,
project_name=project.name, project_name=project.name,
@ -648,13 +668,341 @@ def _change_existing_requirement(
output_dir=output_dir, output_dir=output_dir,
file_name=change_sig_file, file_name=change_sig_file,
) )
# 合并到主签名 JSON
_merge_signatures_to_main(project, signatures, output_dir) _merge_signatures_to_main(project, signatures, output_dir)
console.print(f"[green]✓ 变更完成,代码已重新生成[/green]") console.print(f"[green]✓ 变更完成,代码已重新生成[/green]")
# ══════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════
# 新增需求(内部) # 变更操作 B变更指定模块需求
# ══════════════════════════════════════════════════════
def _change_module_requirements(
project: Project,
func_reqs: List[FunctionalRequirement],
output_dir: str,
knowledge_text: str = "",
) -> None:
"""
选择目标模块进入模块级变更子菜单
B1 · 重命名模块
B2 · 逐条修改模块内需求
B3 · 批量重新生成模块代码
"""
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
]
console.print(
f"\n[bold magenta]模块:{target_module}[/bold magenta]"
f"{len(module_reqs)} 条需求"
)
print_functional_requirements(module_reqs)
# ── 模块级操作子菜单 ─────────────────────────────
sub_menu = [
("B1", "✏️ 重命名模块", "修改模块名,批量更新该模块所有需求"),
("B2", "🔧 逐条修改模块内需求", "对模块内每条需求单独修改并重新生成"),
("B3", "⚡ 批量重新生成模块代码", "保持需求内容不变,整体重新生成签名+代码"),
]
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=24)
table.add_column("说明", style="dim", width=38)
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", "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_edit_each(
project=project,
module_reqs=module_reqs,
output_dir=output_dir,
knowledge_text=knowledge_text,
)
# 刷新 module_reqs
module_reqs = [
db.get_functional_requirement(r.id)
for r in module_reqs
]
elif sub_action == "B3":
_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[FunctionalRequirement],
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
llm = LLMClient()
analyzer = RequirementAnalyzer(llm)
analyzer.log_change(project.id, change_summary)
for req in module_reqs:
req.module = new_module
db.update_functional_requirement(req)
console.print(
f"[green]✓ 模块已重命名:'{old_module}''{new_module}'"
f"共更新 {len(module_reqs)} 条需求[/green]"
)
# ── B2逐条修改模块内需求 ────────────────────────────
def _module_edit_each(
project: Project,
module_reqs: List[FunctionalRequirement],
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
analyzer.log_change(project.id, change_summary)
req.description = new_description
req.priority = new_priority
req.module = new_module
req.status = "pending"
db.update_functional_requirement(req)
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]")
# ── B3批量重新生成模块代码 ──────────────────────────
def _module_regen_code(
project: Project,
module_reqs: List[FunctionalRequirement],
output_dir: str,
knowledge_text: str = "",
) -> None:
"""
保持需求内容不变对整个模块的所有需求
批量重新生成函数签名与代码文件并更新主签名 JSON
"""
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)} 条需求"
)
analyzer.log_change(project.id, change_summary)
console.print(
f"[bold green]✅ 模块 '{module_name}' 批量重新生成完成!"
f"{len(code_files)} 个文件[/bold green]"
)
# ══════════════════════════════════════════════════════
# 变更操作 C新增需求
# ══════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════
def _add_new_requirements( def _add_new_requirements(
@ -709,7 +1057,6 @@ def _add_new_requirements(
console.print("[yellow]已取消新增。[/yellow]") console.print("[yellow]已取消新增。[/yellow]")
return return
# 模块分类
with console.status("[cyan]LLM 正在进行模块分类...[/cyan]"): with console.status("[cyan]LLM 正在进行模块分类...[/cyan]"):
module_map = analyzer.classify_modules(new_reqs, knowledge_text) module_map = analyzer.classify_modules(new_reqs, knowledge_text)
name_to_module = {item["function_name"]: item["module"] for item in module_map} name_to_module = {item["function_name"]: item["module"] for item in module_map}
@ -718,15 +1065,11 @@ def _add_new_requirements(
req.module = name_to_module[req.function_name] req.module = name_to_module[req.function_name]
print_module_summary(new_reqs) print_module_summary(new_reqs)
# 持久化
for req in new_reqs: for req in new_reqs:
req.id = db.create_functional_requirement(req) req.id = db.create_functional_requirement(req)
console.print(f"[green]✓ 新增功能需求已持久化[/green]") console.print(f"[green]✓ 新增功能需求已持久化[/green]")
# 记录变更历史
analyzer.log_change(project.id, change_summary) analyzer.log_change(project.id, change_summary)
# 生成签名
signatures = _generate_signatures(analyzer, new_reqs, knowledge_text) signatures = _generate_signatures(analyzer, new_reqs, knowledge_text)
write_function_signatures_json( write_function_signatures_json(
output_dir=output_dir, output_dir=output_dir,
@ -737,7 +1080,6 @@ def _add_new_requirements(
) )
print_signatures_preview(signatures) print_signatures_preview(signatures)
# 生成代码
generator = CodeGenerator(llm) generator = CodeGenerator(llm)
code_files = _generate_code( code_files = _generate_code(
generator=generator, generator=generator,
@ -753,11 +1095,9 @@ def _add_new_requirements(
req.status = "generated" req.status = "generated"
db.update_functional_requirement(req) db.update_functional_requirement(req)
# 更新 README
all_reqs = db.list_functional_requirements(project.id) all_reqs = db.list_functional_requirements(project.id)
_write_readme(project, all_reqs, output_dir) _write_readme(project, all_reqs, output_dir)
# 回填 URL & 合并主签名
_patch_and_save_signatures( _patch_and_save_signatures(
project=project, project=project,
signatures=signatures, signatures=signatures,
@ -777,7 +1117,6 @@ def _add_new_requirements(
# ══════════════════════════════════════════════════════ # ══════════════════════════════════════════════════════
def _load_knowledge_optional() -> str: def _load_knowledge_optional() -> str:
"""可选加载知识库文件,返回合并后的文本"""
if Confirm.ask("是否加载知识库文件?", default=False): if Confirm.ask("是否加载知识库文件?", default=False):
kb_path = Prompt.ask("知识库文件路径(多个用逗号分隔)") kb_path = Prompt.ask("知识库文件路径(多个用逗号分隔)")
paths = [p.strip() for p in kb_path.split(",") if p.strip()] paths = [p.strip() for p in kb_path.split(",") if p.strip()]
@ -792,9 +1131,6 @@ def _generate_signatures(
func_reqs: List[FunctionalRequirement], func_reqs: List[FunctionalRequirement],
knowledge_text: str = "", knowledge_text: str = "",
) -> List[dict]: ) -> List[dict]:
"""批量生成函数签名,带进度输出"""
total = len(func_reqs)
def on_progress(i, t, req, sig, err): def on_progress(i, t, req, sig, err):
status = "[red]✗ 降级[/red]" if err else "[green]✓[/green]" status = "[red]✗ 降级[/red]" if err else "[green]✓[/green]"
console.print( console.print(
@ -818,7 +1154,6 @@ def _generate_code(
knowledge_text: str = "", knowledge_text: str = "",
signatures: List[dict] = None, signatures: List[dict] = None,
) -> list: ) -> list:
"""批量生成代码文件,带进度输出"""
def on_progress(i, t, req, code_file, err): def on_progress(i, t, req, code_file, err):
if err: if err:
console.print( console.print(
@ -890,7 +1225,6 @@ def _merge_signatures_to_main(
output_dir: str, output_dir: str,
main_file: str = "function_signatures.json", main_file: str = "function_signatures.json",
) -> None: ) -> None:
"""将新签名合并追加到主签名 JSON以 name 去重,新覆盖旧)"""
main_path = os.path.join(output_dir, main_file) main_path = os.path.join(output_dir, main_file)
if os.path.exists(main_path): if os.path.exists(main_path):
with open(main_path, "r", encoding="utf-8") as f: with open(main_path, "r", encoding="utf-8") as f:
@ -903,12 +1237,10 @@ def _merge_signatures_to_main(
"description": project.description or "", "description": project.description or "",
"functions": [], "functions": [],
} }
sig_map = {s["name"]: s for s in existing_sigs} sig_map = {s["name"]: s for s in existing_sigs}
for sig in new_sigs: for sig in new_sigs:
sig_map[sig["name"]] = sig sig_map[sig["name"]] = sig
main_doc["functions"] = list(sig_map.values()) main_doc["functions"] = list(sig_map.values())
with open(main_path, "w", encoding="utf-8") as f: with open(main_path, "w", encoding="utf-8") as f:
json.dump(main_doc, f, ensure_ascii=False, indent=2) json.dump(main_doc, f, ensure_ascii=False, indent=2)
console.print( console.print(
@ -1003,7 +1335,6 @@ def _interactive_adjust_modules(
def _pause(): def _pause():
"""等待用户按 Enter 键继续"""
console.print() console.print()
input(" 按 Enter 键返回主菜单...") input(" 按 Enter 键返回主菜单...")