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

@@ -260,6 +260,7 @@ def cmd_add_project(
program: str = "",
start: str = "",
end: str = "",
approved_budget: str = "",
expected_json: str = "",
excerpt: str = "",
status: str = "",
@@ -278,7 +279,7 @@ def cmd_add_project(
print(f"ERROR:ENTITLEMENT_DENIED {reason}")
return 1
fields: dict[str, Any] = {"project_no": project_no}
for key, value in (("project_name", name), ("pi_name", pi), ("department", department), ("program", program), ("start_date", start), ("end_date", end), ("expected_json", expected_json), ("annual_report_excerpt", excerpt), ("status", status)):
for key, value in (("project_name", name), ("pi_name", pi), ("department", department), ("program", program), ("start_date", start), ("end_date", end), ("approved_budget", approved_budget), ("expected_json", expected_json), ("annual_report_excerpt", excerpt), ("status", status)):
if value:
fields[key] = value
payload = upsert_project(fields)
@@ -411,6 +412,86 @@ def cmd_import(projects_json: str = "", achievements_json: str = "", expenses_js
return 1
def _cmd_import_one(label: str, raw_json: str, import_fn) -> int:
"""单一台账批量导入(import-projects / import-achievements / import-expenses 共用)。"""
log = _get_task_logger()
with job_context(skill=SKILL_SLUG):
log.info("task_start task_type=import_%s stage=ledger", label)
emit(f"开始批量导入{label}", skill=SKILL_SLUG, stage="ledger")
try:
ok, reason = check_entitlement(SKILL_SLUG)
if not ok:
log.warning("entitlement_failed task_type=import_%s stage=auth", label)
finish(status="failed", message=reason, skill=SKILL_SLUG, error_code="ENTITLEMENT_DENIED", stage="auth")
print(f"ERROR:ENTITLEMENT_DENIED {reason}")
return 1
payload = import_fn(_parse_json_arg(raw_json, f"--{label}-json"))
_print_json(payload)
finish(status="success", message=f"导入完成:新增 {payload['created']},更新 {payload['updated']}", skill=SKILL_SLUG, stage="ledger")
return 0
except ValueError as exc:
log.warning("task_failed task_type=import_%s error_code=PROJECT_INVALID detail=%s", label, exc)
finish(status="failed", message=str(exc), skill=SKILL_SLUG, error_code="PROJECT_INVALID", stage="ledger")
print(f"ERROR:PROJECT_INVALID {exc}")
return 1
except JobStopped:
raise
except Exception:
log.exception("task_failed task_type=import_%s stage=ledger", label)
finish(status="failed", message="批量导入异常,详见统一日志", skill=SKILL_SLUG, stage="ledger")
return 1
def cmd_import_projects(projects_json: str) -> int:
from service.ledger_service import import_projects
return _cmd_import_one("projects", projects_json, import_projects)
def cmd_import_achievements(achievements_json: str) -> int:
from service.ledger_service import import_achievements
return _cmd_import_one("achievements", achievements_json, import_achievements)
def cmd_import_expenses(expenses_json: str) -> int:
from service.ledger_service import import_expenses
return _cmd_import_one("expenses", expenses_json, import_expenses)
def _cmd_list_one(label: str, fetch) -> int:
log = _get_task_logger()
with job_context(skill=SKILL_SLUG):
log.info("task_start task_type=list_%s", label)
try:
rows = fetch()
_print_json({"ok": True, "count": len(rows), "rows": rows})
return 0
except Exception:
log.exception("task_failed task_type=list_%s", label)
print("ERROR:QUERY_FAILED 台账查询异常,详见统一日志")
return 1
def cmd_list_projects(status: str = "") -> int:
from service.ledger_service import list_projects
return _cmd_list_one("projects", lambda: list_projects(status=status))
def cmd_list_achievements(project_no: str) -> int:
from service.ledger_service import list_achievements
return _cmd_list_one("achievements", lambda: list_achievements(project_no))
def cmd_list_expenses(project_no: str) -> int:
from service.ledger_service import list_expenses
return _cmd_list_one("expenses", lambda: list_expenses(project_no))
def cmd_list(kind: str = "projects", project_no: str = "", status: str = "") -> int:
log = _get_task_logger()
from service.ledger_service import list_achievements, list_expenses, list_projects