Files
group_fqcd_jr/app/static
lzf_0626 d8e47df20c 推介材料:勾选「展示排名」后无法填写来源 → 必然合规阻断(死锁)的修复
## 现象(你的截图)

任务 `PM-20260914-0007` 的合规检查结果为:

```
业绩排名来源不满足要求
补充三年期以上公开评价数据来源 · block
```

输入快照里的实证:

```json
ranking = {"enabled": true, "ranking_text": null, "public_source": null,
           "institution_name": null, "evaluation_period_years": null}
```

## 根因:勾选框有了,填来源的地方没有

`promotion_compliance.py:82-91` 的规则是:

```python
if ranking.get("enabled"):
    years = _years(ranking.get("evaluation_period_years"))
    if years < 3 or not ranking.get("institution_name") or not ranking.get("public_source"):
        → block "performance.ranking_source_invalid"
```

而前端**只有**一个复选框 `performance_info.ranking.enabled`:

- 表单里**没有** `institution_name` / `public_source` / `evaluation_period_years` /
  `ranking_text` 四个输入(实测枚举全部 `data-promo-field` 只有那一个 ranking 字段);
- `inputsPayload()` 也只提交 `ranking: { enabled }`。

⇒ 运营一旦勾选「展示排名」,来源四项永远是 null,规则必然阻断,
而界面上**无处可填** —— 要么取消勾选,要么永远生成不了。这是**死锁**,不是数据问题。

## 修法(前后端契约字段本就有,只补界面与提交)

1. `promotion/index.html`:在展示选项上方新增一组输入(`data-ranking-source`):
   评价期间(年,需 ≥3)/ 评价机构 / 公开来源 / 排名文本;
2. `promotion/promotion.js`:`inputsPayload()` 的 `ranking` 补上这四个字段
   (契约 `RankingInfo` 本就定义了 `institution_name` / `evaluation_period_years`
   / `ranking_text` / `public_source`,`evaluation_period_years` 是字符串,如 "3年")。

## 验证

- 用**真实任务 0007 的输入**跑 `PromotionComplianceChecker.check_inputs()`:
  - 现状:3 条阻断(`history_short` + **`ranking_source_invalid`** + `data_attachment_missing`);
  - 把来源填全后:**`ranking_source_invalid` 消失**(其余两条由"未上传业绩文件"引起,上传即解);
  - 反例:评价期间改成 2 年 → 仍拦;3 年但缺公开来源 → 仍拦(规则没有被放松);
- 从源文件抽出 `inputsPayload()` 用桩真实调用:四个来源字段都进 payload;未勾选时 `enabled=false`;
- `index.html` 标签净增 +1 `<div>` / +1 `</div>`(结构平衡);`node --check` 通过;
- `pytest tests/unit tests/contract` → 1458 passed, 2 skipped, 0 failed。

## 附带说明

你看到的是"2 条"是因为**点了两次生成**:每次生成都会把该次的 findings 落库,
页面"读取合规结果"会把历史记录一并列出(不是一次调用重复产出)。
2026-09-14 22:45:57 +08:00
..