fix: 预览按 draft-{id} 命名且重建全量同构 + C1 空台账改判数据待补 + 立项经费结余结论 + CLI 拆分 import-*/list-* + docx 占位黄底

This commit is contained in:
chenfuqiang
2026-09-27 21:47:56 +08:00
parent 7b24521b43
commit db11f22dc8
11 changed files with 508 additions and 107 deletions

View File

@@ -19,9 +19,15 @@ from service.task_service import (
cmd_draft_report,
cmd_health,
cmd_import,
cmd_import_achievements,
cmd_import_expenses,
cmd_import_projects,
cmd_init_db,
cmd_list,
cmd_list_achievements,
cmd_list_drafts,
cmd_list_expenses,
cmd_list_projects,
cmd_render_html,
cmd_run,
cmd_show_draft,
@@ -51,6 +57,16 @@ def _cli_float_or_none(raw: Optional[str]) -> Optional[float]:
return None
def _cli_int_or_none(raw: Optional[str]) -> Optional[int]:
raw = _cli_str_or_none(raw)
if raw is None:
return None
try:
return int(float(raw))
except ValueError:
return None
def _handle_run(args: argparse.Namespace) -> int:
tail = [str(x).strip() for x in (args.run_tail or []) if str(x).strip()]
if len(tail) > 2:
@@ -80,6 +96,7 @@ def _handle_add_project(args: argparse.Namespace) -> int:
program=_cli_str_or_none(args.program) or "",
start=_cli_str_or_none(args.start) or "",
end=_cli_str_or_none(args.end) or "",
approved_budget=_cli_str_or_none(args.approved_budget) or "",
expected_json=_cli_str_or_none(args.expected_json) or "",
excerpt=_cli_str_or_none(args.excerpt) or "",
status=_cli_str_or_none(args.status) or "",
@@ -103,18 +120,22 @@ def _handle_list(args: argparse.Namespace) -> int:
def _handle_render_html(args: argparse.Namespace) -> int:
report_id = getattr(args, "report_id", None)
return cmd_render_html(report_id=int(report_id) if report_id else None)
# 宿主行上下文可能给空串或未解析占位符(如 {{id}}):解析失败一律回退
# 缺省行为(重建最近 10 份),不让 args 变体把渲染打崩。
report_id = _cli_int_or_none(getattr(args, "report_id", None))
return cmd_render_html(report_id=report_id)
def _print_full_usage() -> None:
print("结题报告初稿助手(main.py)可用命令:")
print(" python main.py add-project --project-no X --name Y [--pi 张三] [--program 国自然青年] [--start D] [--end D]")
print(" [--expected-json '[{\"type\":\"论文\",\"target\":3,\"unit\":\"篇\"}]'] [--excerpt 摘句] [--status 待结题]")
print(" [--approved-budget 500000] [--expected-json '[{\"type\":\"论文\",\"target\":3,\"unit\":\"篇\"}]'] [--excerpt 摘句] [--status 待结题]")
print(" python main.py add-achievement --project-no X --type 论文 --title T [--venue 期刊] [--date D] [--representative 1]")
print(" python main.py add-expense --project-no X --category 差旅费 --amount 23000 [--date D] [--description 摘要]")
print(" python main.py import --projects-json F [--achievements-json F] [--expenses-json F]")
print(" python main.py list [projects|achievements|expenses] [--project-no X] [--status 待结题]")
print(" python main.py import-projects --projects-json <file|-> | import-achievements --achievements-json <file|->")
print(" | import-expenses --expenses-json <file|-> # 兼容别名:import --projects-json ...")
print(" python main.py list-projects [--status 待结题] | list-achievements --project-no X | list-expenses --project-no X")
print(" # 兼容别名:list [projects|achievements|expenses]")
print(" python main.py draft-report --project-no X # 主命令:生成 Word 初稿 + HTML 预览")
print(" python main.py draft-batch-pending # 批量生成全部待结题初稿")
print(" python main.py list-drafts [--limit N] | show-draft --id N | render-html [--report-id N]")
@@ -139,6 +160,7 @@ def build_parser() -> ZhArgumentParser:
sp.add_argument("--program", default=None, metavar="项目类型")
sp.add_argument("--start", default=None, metavar="执行期开始")
sp.add_argument("--end", default=None, metavar="执行期结束")
sp.add_argument("--approved-budget", default=None, metavar="立项经费(元)", dest="approved_budget")
sp.add_argument("--expected-json", default=None, metavar="预期指标JSON", dest="expected_json")
sp.add_argument("--excerpt", default=None, metavar="年度报告摘句")
sp.add_argument("--status", default=None, metavar="状态(待结题/初稿已生成/已提交)")
@@ -173,18 +195,42 @@ def build_parser() -> ZhArgumentParser:
)
)
sp = sub.add_parser("import", help="批量导入(projects/achievements/expenses JSON)")
sp = sub.add_parser("import", help="批量导入(兼容别名:projects/achievements/expenses JSON)")
sp.add_argument("--projects-json", default=None, metavar="项目JSON(file|-)", dest="projects_json")
sp.add_argument("--achievements-json", default=None, metavar="成果JSON(file|-)", dest="achievements_json")
sp.add_argument("--expenses-json", default=None, metavar="决算JSON(file|-)", dest="expenses_json")
sp.set_defaults(handler=_handle_import)
sp = sub.add_parser("list", help="查询台账(projects/achievements/expenses)")
sp = sub.add_parser("import-projects", help="批量导入结题项目")
sp.add_argument("--projects-json", required=True, metavar="JSON(file|-)", dest="projects_json")
sp.set_defaults(handler=lambda a: cmd_import_projects(str(a.projects_json)))
sp = sub.add_parser("import-achievements", help="批量导入成果清单")
sp.add_argument("--achievements-json", required=True, metavar="JSON(file|-)", dest="achievements_json")
sp.set_defaults(handler=lambda a: cmd_import_achievements(str(a.achievements_json)))
sp = sub.add_parser("import-expenses", help="批量导入决算明细")
sp.add_argument("--expenses-json", required=True, metavar="JSON(file|-)", dest="expenses_json")
sp.set_defaults(handler=lambda a: cmd_import_expenses(str(a.expenses_json)))
sp = sub.add_parser("list", help="查询台账(兼容别名:projects/achievements/expenses)")
sp.add_argument("--kind", default="projects", metavar="projects|achievements|expenses", dest="kind")
sp.add_argument("--project-no", default=None, metavar="项目编号", dest="project_no")
sp.add_argument("--status", default=None, metavar="状态筛选")
sp.set_defaults(handler=_handle_list)
sp = sub.add_parser("list-projects", help="查询结题项目")
sp.add_argument("--status", default=None, metavar="状态筛选")
sp.set_defaults(handler=lambda a: cmd_list_projects(status=_cli_str_or_none(a.status) or ""))
sp = sub.add_parser("list-achievements", help="查询成果清单")
sp.add_argument("--project-no", required=True, metavar="项目编号", dest="project_no")
sp.set_defaults(handler=lambda a: cmd_list_achievements(project_no=str(a.project_no)))
sp = sub.add_parser("list-expenses", help="查询决算明细")
sp.add_argument("--project-no", required=True, metavar="项目编号", dest="project_no")
sp.set_defaults(handler=lambda a: cmd_list_expenses(project_no=str(a.project_no)))
sp = sub.add_parser("draft-report", help="生成结题报告初稿(主命令)")
sp.add_argument("--project-no", required=True, metavar="项目编号", dest="project_no")
sp.set_defaults(handler=lambda a: cmd_draft_report(project_no=str(a.project_no)))