fix: 对照《财务处Skill002》验收整改并升版 v1.0.2
All checks were successful
技能自动化发布 / release (push) Successful in 9s

- 状态标签:C3 触发且 D 未判 → 待补录,优先级对齐需求 §6.2
- broadcast-monthly 逐项目增量 emit,任务中心可见进行中进度
- progress_reports 回填 project_id;新增 items_json/updated_at;旧库重建式安全迁移(含回归测试)
- 简报:TITLE 转义防注入、章节对齐 §10(独立「暂付款/结题提醒」)、页脚逐字统一
- 金标准测试 +17:±10%/20% 阈值恰界、C2 钳位、N2 反例、C3 逐字段、HTML 转义、async+cron 定位守护、迁移回归
- 文档 v1.1(开发文档 + REQUIREMENTS):ERROR:<CODE> <message>、reports_brief、--all-status、skill-detail 表外挂载、C1 level=warn
- 落盘验收报告 acceptance-reports/20260924-105401-acceptance.md(338 测试全绿)
- 版本 1.0.1 → 1.0.2(constants / SKILL.md / CHANGELOG 三处对齐)
This commit is contained in:
chenfuqiang
2026-09-24 17:19:30 +08:00
parent a91ead3862
commit f90f742c1e
12 changed files with 490 additions and 25 deletions

View File

@@ -51,8 +51,6 @@ _KEY_ALIASES = {
"remark": ("remark", "备注"),
}
_STATUS_RANK = {STATUS_INCOMPLETE: 0, STATUS_NO_FUND: 1, STATUS_NORMAL: 2, STATUS_OVERSHOOT: 3, STATUS_LAG: 4}
def _log():
try:
@@ -322,10 +320,14 @@ def compute_project(project: dict[str, Any], rules: dict[str, float], as_of: dat
)
)
# 状态修正:无法计算时归为未到账/待补录
if spend_rate is None and status_tag == STATUS_NORMAL:
has_no_fund = any(i["rule_id"] == "C1" for i in items)
status_tag = STATUS_NO_FUND if has_no_fund else STATUS_INCOMPLETE
# 状态标签优先级:严重滞后>支出异常偏快>正常(D3 已判,含 C3 轻微缺失)>待补录(C3 且 D 未判)>未到账(C1)
if status_tag == STATUS_NORMAL and not any(i["rule_id"] == "D3" for i in items):
if any(i["rule_id"] == "C3" for i in items):
status_tag = STATUS_INCOMPLETE
elif any(i["rule_id"] == "C1" for i in items):
status_tag = STATUS_NO_FUND
elif spend_rate is None:
status_tag = STATUS_INCOMPLETE
suggestions = [i["suggestion"] for i in items if i.get("suggestion")]
summary = _build_summary(project_no, project_name, total_approved, total_received, total_spent, as_of, spend_rate, time_progress, status_tag, items)
@@ -408,8 +410,17 @@ def _write_report(result: dict[str, Any], batch_no: str) -> str:
return write_report_for_kernel(result=result, batch_no=batch_no)
def run_broadcast(projects_raw: list[dict[str, Any]], as_of: date, *, batch_no: str = "") -> dict[str, Any]:
"""播报内核:逐项目三步计算 → 写库 → 生成《经费执行简报》。"""
def run_broadcast(
projects_raw: list[dict[str, Any]],
as_of: date,
*,
batch_no: str = "",
on_progress: Optional[Any] = None,
) -> dict[str, Any]:
"""播报内核:逐项目三步计算 → 写库 → 生成《经费执行简报》。
``on_progress(index, total, result)``:每完成一个项目回调一次(任务中心增量进度)。
"""
from service.report_service import build_monthly_brief
log = _log()
@@ -427,17 +438,23 @@ def run_broadcast(projects_raw: list[dict[str, Any]], as_of: date, *, batch_no:
conn = get_conn()
try:
cur = conn.cursor()
for project in projects:
total = len(projects)
for index, project in enumerate(projects, start=1):
result = compute_project(project, rules, as_of)
try:
project_id = int(project.get("id")) if project.get("id") not in (None, "") else None
except (TypeError, ValueError):
project_id = None
cur.execute(
"""
INSERT INTO progress_reports
(batch_no, project_no, project_name, pi_name, as_of, spend_rate,
time_progress, deviation, status_tag, summary, suggestions_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
(batch_no, project_id, project_no, project_name, pi_name, as_of, spend_rate,
time_progress, deviation, status_tag, summary, suggestions_json, items_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
batch_no,
project_id,
result["project_no"],
result["project_name"],
result["pi_name"],
@@ -448,6 +465,7 @@ def run_broadcast(projects_raw: list[dict[str, Any]], as_of: date, *, batch_no:
result["status_tag"],
result["summary"],
json.dumps(result["suggestions"], ensure_ascii=False),
json.dumps(result["items"], ensure_ascii=False),
),
)
report_id = int(cur.lastrowid)
@@ -457,6 +475,8 @@ def run_broadcast(projects_raw: list[dict[str, Any]], as_of: date, *, batch_no:
cur.execute("UPDATE progress_reports SET report_file = ? WHERE id = ?", (report_file, report_id))
result["report_file"] = report_file
results.append(result)
if on_progress is not None:
on_progress(index, total, result)
conn.commit()
finally:
conn.close()