推介材料:勾选「展示排名」后无法填写来源 → 必然合规阻断(死锁)的修复
## 现象(你的截图)
任务 `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 落库,
页面"读取合规结果"会把历史记录一并列出(不是一次调用重复产出)。
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
<div><h3>产品信息</h3><div class="operator-form-grid"><label class="form-field"><span class="form-field__label">基金类型 *</span><select class="form-field__input" data-promo-field="product_info.fund_type" required><option value="">请选择基金类型</option><option value="货币型">货币型</option><option value="债券型">债券型</option><option value="混合型">混合型</option><option value="股票型">股票型</option><option value="期货型">期货型</option></select></label><label class="form-field"><span class="form-field__label">运作方式 *</span><select class="form-field__input" data-promo-field="product_info.operation_mode" required><option value="">请选择运作方式</option><option value="封闭式">封闭式</option><option value="开放式">开放式</option></select></label><label class="form-field"><span class="form-field__label">产品状态</span><select class="form-field__input" data-promo-field="product_info.product_status"><option value="">请选择产品状态</option><option value="募集期">募集期</option><option value="封闭期">封闭期</option><option value="开放期">开放期</option></select></label><label class="form-field"><span class="form-field__label">风险等级</span><select class="form-field__input" data-promo-field="product_info.risk_level" disabled><option value="">请选择</option><option value="R1">R1</option><option value="R2">R2</option><option value="R3">R3</option><option value="R4">R4</option><option value="R5">R5</option></select></label><label class="form-field"><span class="form-field__label">业绩比较基准</span><input class="form-field__input" data-promo-field="product_info.benchmark"></label><label class="form-field"><span class="form-field__label">投资目标 *</span><textarea class="form-field__input" data-promo-field="product_info.investment_objective" rows="2" required></textarea></label></div></div>
|
||||
<div><h3>管理人信息</h3><div class="operator-form-grid"><label class="form-field"><span class="form-field__label">基金经理 *</span><input class="form-field__input" data-promo-field="manager_info.manager_name" required></label><label class="form-field"><span class="form-field__label">管理公司 *</span><input class="form-field__input" data-promo-field="manager_info.management_company" required></label><label class="form-field"><span class="form-field__label">登记编码 *</span><input class="form-field__input" data-promo-field="manager_info.registration_code" required></label><label class="form-field"><span class="form-field__label">从业年限</span><input class="form-field__input" data-promo-field="manager_info.employment_years"></label><label class="form-field"><span class="form-field__label">投资管理经验</span><textarea class="form-field__input" data-promo-field="manager_info.investment_management_experience" rows="2"></textarea></label><label class="form-field"><span class="form-field__label">经理简介</span><textarea class="form-field__input" data-promo-field="manager_info.profile" rows="4"></textarea></label></div></div>
|
||||
<div><h3>团队与策略</h3><div class="operator-form-grid operator-form-grid--wide"><label class="form-field"><span class="form-field__label">团队描述 *</span><textarea class="form-field__input" data-promo-field="team_info.team_description" rows="3" required></textarea></label><label class="form-field"><span class="form-field__label">研究能力</span><textarea class="form-field__input" data-promo-field="team_info.research_capability" rows="3"></textarea></label><label class="form-field"><span class="form-field__label">投资范围 *</span><textarea class="form-field__input" data-promo-field="strategy_info.investment_scope" rows="3" required></textarea></label><label class="form-field"><span class="form-field__label">策略说明 *</span><textarea class="form-field__input" data-promo-field="strategy_info.strategy" rows="3" required></textarea></label><label class="form-field"><span class="form-field__label">投资限制 *</span><textarea class="form-field__input" data-promo-field="strategy_info.restrictions" rows="3" required></textarea></label><label class="form-field"><span class="form-field__label">指数工具属性</span><textarea class="form-field__input" data-promo-field="strategy_info.index_tool_attribute" rows="3"></textarea></label></div></div>
|
||||
<div><h3>费用、业绩与风险</h3><div class="operator-form-grid"><label class="form-field"><span class="form-field__label">申购费</span><input class="form-field__input" data-promo-field="fee_structure.subscription_fee"></label><label class="form-field"><span class="form-field__label">认购费</span><input class="form-field__input" data-promo-field="fee_structure.purchase_fee"></label><label class="form-field"><span class="form-field__label">赎回费</span><input class="form-field__input" data-promo-field="fee_structure.redemption_fee"></label><label class="form-field"><span class="form-field__label">销售服务费</span><input class="form-field__input" data-promo-field="fee_structure.sales_service_fee"></label><label class="form-field"><span class="form-field__label">管理费</span><input class="form-field__input" data-promo-field="fee_structure.management_fee"></label><label class="form-field"><span class="form-field__label">托管费</span><input class="form-field__input" data-promo-field="fee_structure.custody_fee"></label><label class="form-field"><span class="form-field__label">客户维护费</span><input class="form-field__input" data-promo-field="fee_structure.client_maintenance_fee"></label><label class="form-field"><span class="form-field__label">数据截止日期</span><input class="form-field__input" type="date" data-promo-field="performance_info.as_of_date" readonly></label><label class="form-field"><span class="form-field__label">历史月份</span><input class="form-field__input" type="number" min="0" max="600" data-promo-field="performance_info.history_months" readonly></label><label class="form-field"><span class="form-field__label">产品收益</span><input class="form-field__input" data-promo-field="performance_info.product_return" readonly></label><label class="form-field"><span class="form-field__label">最大回撤</span><input class="form-field__input" data-promo-field="performance_info.max_drawdown" readonly></label><label class="form-field"><span class="form-field__label">波动率</span><input class="form-field__input" data-promo-field="performance_info.volatility"></label><label class="form-field"><span class="form-field__label">夏普比率</span><input class="form-field__input" data-promo-field="performance_info.sharpe_ratio"></label><label class="form-field"><span class="form-field__label">特殊风险(每行一项)</span><textarea class="form-field__input" data-promo-field="risk_disclosure.special_risks" rows="2"></textarea></label><label class="form-field"><span class="form-field__label">补充说明</span><textarea class="form-field__input" data-promo-field="risk_disclosure.additional_notes" rows="2"></textarea></label></div><div class="operator-checkboxes"><label><input type="checkbox" data-promo-field="performance_info.show_product_performance"> 展示产品业绩</label><label><input type="checkbox" data-promo-field="performance_info.show_manager_performance"> 展示经理业绩</label><label><input type="checkbox" data-promo-field="performance_info.ranking.enabled"> 展示排名</label></div></div>
|
||||
<div><h3>费用、业绩与风险</h3><div class="operator-form-grid"><label class="form-field"><span class="form-field__label">申购费</span><input class="form-field__input" data-promo-field="fee_structure.subscription_fee"></label><label class="form-field"><span class="form-field__label">认购费</span><input class="form-field__input" data-promo-field="fee_structure.purchase_fee"></label><label class="form-field"><span class="form-field__label">赎回费</span><input class="form-field__input" data-promo-field="fee_structure.redemption_fee"></label><label class="form-field"><span class="form-field__label">销售服务费</span><input class="form-field__input" data-promo-field="fee_structure.sales_service_fee"></label><label class="form-field"><span class="form-field__label">管理费</span><input class="form-field__input" data-promo-field="fee_structure.management_fee"></label><label class="form-field"><span class="form-field__label">托管费</span><input class="form-field__input" data-promo-field="fee_structure.custody_fee"></label><label class="form-field"><span class="form-field__label">客户维护费</span><input class="form-field__input" data-promo-field="fee_structure.client_maintenance_fee"></label><label class="form-field"><span class="form-field__label">数据截止日期</span><input class="form-field__input" type="date" data-promo-field="performance_info.as_of_date" readonly></label><label class="form-field"><span class="form-field__label">历史月份</span><input class="form-field__input" type="number" min="0" max="600" data-promo-field="performance_info.history_months" readonly></label><label class="form-field"><span class="form-field__label">产品收益</span><input class="form-field__input" data-promo-field="performance_info.product_return" readonly></label><label class="form-field"><span class="form-field__label">最大回撤</span><input class="form-field__input" data-promo-field="performance_info.max_drawdown" readonly></label><label class="form-field"><span class="form-field__label">波动率</span><input class="form-field__input" data-promo-field="performance_info.volatility"></label><label class="form-field"><span class="form-field__label">夏普比率</span><input class="form-field__input" data-promo-field="performance_info.sharpe_ratio"></label><label class="form-field"><span class="form-field__label">特殊风险(每行一项)</span><textarea class="form-field__input" data-promo-field="risk_disclosure.special_risks" rows="2"></textarea></label><label class="form-field"><span class="form-field__label">补充说明</span><textarea class="form-field__input" data-promo-field="risk_disclosure.additional_notes" rows="2"></textarea></label></div><div class="operator-form-grid" data-ranking-source><label class="form-field"><span class="form-field__label">评价期间(年,需 ≥3)</span><input class="form-field__input" data-promo-field="performance_info.ranking.evaluation_period_years" placeholder="如 3年"></label><label class="form-field"><span class="form-field__label">评价机构</span><input class="form-field__input" data-promo-field="performance_info.ranking.institution_name" placeholder="出具排名的公开评价机构"></label><label class="form-field"><span class="form-field__label">公开来源</span><input class="form-field__input" data-promo-field="performance_info.ranking.public_source" placeholder="可核查的公开出处(网址/报告名)"></label><label class="form-field"><span class="form-field__label">排名文本</span><input class="form-field__input" data-promo-field="performance_info.ranking.ranking_text" placeholder="材料中展示的排名表述"></label></div><div class="operator-checkboxes"><label><input type="checkbox" data-promo-field="performance_info.show_product_performance"> 展示产品业绩</label><label><input type="checkbox" data-promo-field="performance_info.show_manager_performance"> 展示经理业绩</label><label><input type="checkbox" data-promo-field="performance_info.ranking.enabled"> 展示排名</label></div></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="panel panel--flush">
|
||||
|
||||
@@ -131,7 +131,7 @@ if (requireOperator()) {
|
||||
team_info: { team_description: text('team_info.team_description'), research_capability: nullable('team_info.research_capability') },
|
||||
strategy_info: { investment_scope: text('strategy_info.investment_scope'), strategy: text('strategy_info.strategy'), restrictions: text('strategy_info.restrictions'), index_tool_attribute: nullable('strategy_info.index_tool_attribute') },
|
||||
fee_structure: { subscription_fee: nullable('fee_structure.subscription_fee'), purchase_fee: nullable('fee_structure.purchase_fee'), redemption_fee: nullable('fee_structure.redemption_fee'), sales_service_fee: nullable('fee_structure.sales_service_fee'), management_fee: nullable('fee_structure.management_fee'), custody_fee: nullable('fee_structure.custody_fee'), client_maintenance_fee: nullable('fee_structure.client_maintenance_fee') },
|
||||
performance_info: { as_of_date: nullable('performance_info.as_of_date'), history_months: Number(field('performance_info.history_months')?.value || 0) || null, product_return: nullable('performance_info.product_return'), max_drawdown: nullable('performance_info.max_drawdown'), volatility: nullable('performance_info.volatility'), sharpe_ratio: nullable('performance_info.sharpe_ratio'), show_product_performance: Boolean(field('performance_info.show_product_performance')?.checked), show_manager_performance: Boolean(field('performance_info.show_manager_performance')?.checked), ranking: { enabled: Boolean(field('performance_info.ranking.enabled')?.checked) } },
|
||||
performance_info: { as_of_date: nullable('performance_info.as_of_date'), history_months: Number(field('performance_info.history_months')?.value || 0) || null, product_return: nullable('performance_info.product_return'), max_drawdown: nullable('performance_info.max_drawdown'), volatility: nullable('performance_info.volatility'), sharpe_ratio: nullable('performance_info.sharpe_ratio'), show_product_performance: Boolean(field('performance_info.show_product_performance')?.checked), show_manager_performance: Boolean(field('performance_info.show_manager_performance')?.checked), ranking: { enabled: Boolean(field('performance_info.ranking.enabled')?.checked), institution_name: nullable('performance_info.ranking.institution_name'), evaluation_period_years: nullable('performance_info.ranking.evaluation_period_years'), ranking_text: nullable('performance_info.ranking.ranking_text'), public_source: nullable('performance_info.ranking.public_source') } },
|
||||
risk_disclosure: { special_risks: text('risk_disclosure.special_risks').split('\n').map((item) => item.trim()).filter(Boolean), additional_notes: nullable('risk_disclosure.additional_notes') },
|
||||
source_notes: {},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user