- 会话支持 name/project 字段,上传要件定义后自动命名;前端侧边栏会话历史 + localStorage 恢复,顶部只显示会话名 - 新增 ProjectsStore(SQLite)与 /api/projects CRUD;绑定项目后 _rebuild_source 合并模板/规则/代码库/设计文档,上传区仅要件定义 - StructuredSource.design_docs 与 ImpactReport.design_references;影响调查新增既有设计文档确定性交叉引用(无 LLM) - 同步更新 docs/design.md §12.7、README、_AI_USAGE_LOG.md;全量测试 558 通过,覆盖率 99.10%
102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
from genesis.data_models import StructuredSource
|
||
from genesis.impact.code_parser import CodeParser
|
||
from genesis.impact.existing_system_explorer import ExistingSystemExplorer
|
||
from genesis.parsers.excel_parser import ExcelParser
|
||
from genesis.parsers.rule_doc_parser import RuleDocParser
|
||
from genesis.parsers.word_template_parser import WordTemplateParser
|
||
|
||
XLSX_EXTS = (".xlsx", ".xls")
|
||
DOCX_EXT = ".docx"
|
||
|
||
|
||
def _validate_path(path: str | Path, allowed_exts: tuple[str, ...]) -> Path:
|
||
"""校验文件扩展名合法且文件存在(T7 DRY:消除三处重复校验)。
|
||
|
||
Raises:
|
||
ValueError: 扩展名不在 allowed_exts(含无扩展名)
|
||
FileNotFoundError: 文件不存在
|
||
"""
|
||
p = Path(path)
|
||
if p.suffix.lower() not in allowed_exts:
|
||
raise ValueError(f"不支持的文件类型: {p.suffix or '(无扩展名)'}")
|
||
if not p.exists():
|
||
raise FileNotFoundError(str(path))
|
||
return p
|
||
|
||
|
||
class SourceParser:
|
||
"""全量输入门面:Excel 要件定义 + Word 模板 + Word 规则 → StructuredSource。
|
||
|
||
角色由调用方按 api-design file_type 语义显式传入(requirements/template/
|
||
write_instruction/rules),不做基于文件名的隐式猜测(spec §3.4)。
|
||
"""
|
||
|
||
def __init__(self) -> None:
|
||
self._excel = ExcelParser()
|
||
|
||
def parse(
|
||
self,
|
||
requirement_paths: list[str | Path] | None = None,
|
||
template_path: str | Path | None = None,
|
||
write_instruction_paths: list[str | Path] | None = None,
|
||
rule_paths: list[str | Path] | None = None,
|
||
existing_system_path: str | Path | None = None,
|
||
existing_system_language: str | None = None,
|
||
design_doc_paths: list[str | Path] | None = None,
|
||
) -> StructuredSource:
|
||
"""扩展名校验先于存在性校验(不存在的文件若扩展名未知将抛出 ValueError 而非 FileNotFoundError)。
|
||
|
||
existing_system_path:既有系统源码目录(追加/改修场景)。提供时解析为
|
||
ExistingSystemInfo(门控通过 → 进入影响调查);未提供/解析失败 → 保持 None。
|
||
existing_system_language:既有系统源码开发语言(如 "java")。默认 None 表示
|
||
由 CodeParser 按扩展名自动探测;显式指定时按该语言解析(多语言支持扩展点)。
|
||
"""
|
||
|
||
requirement_paths = requirement_paths or []
|
||
write_instruction_paths = write_instruction_paths or []
|
||
rule_paths = rule_paths or []
|
||
|
||
tables = []
|
||
comments = []
|
||
for p in requirement_paths:
|
||
path = _validate_path(p, XLSX_EXTS)
|
||
result = self._excel.parse(path)
|
||
tables.extend(result.tables)
|
||
comments.extend(result.comments)
|
||
|
||
template = None
|
||
if template_path is not None:
|
||
tpath = _validate_path(template_path, (DOCX_EXT,))
|
||
template = WordTemplateParser().parse(tpath)
|
||
|
||
rule_docs = []
|
||
for p in [*write_instruction_paths, *rule_paths]:
|
||
path = _validate_path(p, (DOCX_EXT,))
|
||
# 做成说明书与记入规则均为 Type A 写入规则 → write(api-design §2.2)
|
||
rule_docs.append(RuleDocParser().parse(path, category="write"))
|
||
|
||
existing_system = None
|
||
if existing_system_path is not None:
|
||
code = CodeParser().parse(existing_system_path, language=existing_system_language)
|
||
existing_system = ExistingSystemExplorer().explore(code)
|
||
|
||
design_docs = []
|
||
for p in (design_doc_paths or []):
|
||
dpath = _validate_path(p, (DOCX_EXT,))
|
||
# 设计文档为 Type A 参考材料(category="design",区别于写入规则)
|
||
design_docs.append(RuleDocParser().parse(dpath, category="design"))
|
||
|
||
return StructuredSource(
|
||
tables=tables,
|
||
template=template,
|
||
rule_docs=rule_docs,
|
||
image_analyses=[],
|
||
existing_system=existing_system,
|
||
comments=comments,
|
||
design_docs=design_docs,
|
||
)
|