- Updated `RiskListAccess` and `ThresholdWriteAccess` to enforce access control in the risk repository and threshold repository, ensuring only authorized roles can perform sensitive operations. - Introduced new methods in `RiskRepository` for counting pending alerts and listing alerts with access checks, improving data security and compliance. - Enhanced the `chat.py` and `deps.py` files to integrate compliance roles into the risk management matrix, allowing for more granular access control. - Updated documentation to reflect the new testing baseline of 825 passed tests, indicating improved stability and functionality across the application. This update significantly strengthens the risk management capabilities, ensuring robust access control and compliance with organizational policies.
130 lines
7.5 KiB
HTML
130 lines
7.5 KiB
HTML
<section class="module" id="module-1">
|
||
<div class="module-inner">
|
||
<p class="eyebrow animate-in">模块 1 · 风控能力</p>
|
||
<h1 class="module-title animate-in">预警、适当性、AML<br>与模拟交易</h1>
|
||
<p class="module-lead animate-in">
|
||
风控专员 Demo <code>STAFF-30001</code> 带 <code>risk_officer</code> + <code>risk_demo</code> 角色:
|
||
能看预警台账、处置、跑 AML 扫描,还能在模拟交易页触发规则引擎。
|
||
本地登录页一键切换,改 JWT 角色后需<strong>重新登录</strong>。
|
||
仓库基线 <code>merger</code> · <code>python -m pytest</code> → <strong>825 passed</strong>。
|
||
</p>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>四条能力线(REST + 对话 Tool 共用底座)</h2>
|
||
<div class="pattern-cards">
|
||
<div class="pattern-card">
|
||
<h3>预警台账 FR-4</h3>
|
||
<p><code>GET /api/risk/alerts</code> 分页查询;<code>POST .../handle</code> 人工处置(仅 risk_officer)。响应固定带 disclaimer。</p>
|
||
</div>
|
||
<div class="pattern-card">
|
||
<h3>适当性校验 R-02 / FR-2</h3>
|
||
<p><code>POST /api/risk/suitability/check</code> 与网关共用 <span class="term" data-definition="Core 只读查询层(core_ro.py):从 jinrong_core 模拟库读 L0/持仓/流水,并做 C×R 适当性矩阵判定。">Core 只读层</span> 的 <code>check_suitability</code>(旧 SUIT-001~008 已退役)。客户本人 / 理财师名下 / 风控全量。</p>
|
||
</div>
|
||
<div class="pattern-card">
|
||
<h3>AML 扫描 FR-5</h3>
|
||
<p><code>POST /api/risk/aml/scan</code>:仅 risk_officer 触发全量扫描,生成 aml 类预警。</p>
|
||
</div>
|
||
<div class="pattern-card">
|
||
<h3>模拟交易 FR-1</h3>
|
||
<p><code>POST /api/simulate/trade</code>:risk_demo 或客户本人可提交;走 模拟写 Core 网关(trade_gateway.py) → 适当性 → 规则引擎(risk_engine.py)。</p>
|
||
</div>
|
||
</div>
|
||
<div class="callout callout-accent">
|
||
<strong>鉴权与问数不同:</strong> 风控 REST 走 <code>get_auth_context</code>,前端 <code>risk.ts</code> 必须带 <code>X-Agent-Type: risk</code> + Bearer。缺头会 401 <code>AUTH_401_MISSING_AGENT_TYPE</code>。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>群聊:风控专员打开预警页</h2>
|
||
<p>浏览器通过 riskHeaders 声明自己在敲风控窗:</p>
|
||
|
||
<div class="chat-window" id="chat-risk-m1">
|
||
<div class="chat-messages">
|
||
<div class="chat-message" data-msg="0" data-sender="browser" style="display:none">
|
||
<div class="chat-avatar" style="background:#D94F30">前</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#D94F30">RiskAlertsPage</span>
|
||
<p>GET /api/risk/alerts · Bearer + X-Agent-Type: risk</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="1" data-sender="api" style="display:none">
|
||
<div class="chat-avatar" style="background:#E87A62">API</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#E87A62">deps.get_auth_context</span>
|
||
<p>验 JWT + 矩阵:STAFF-30001 risk_officer 放行</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="2" data-sender="repo" style="display:none">
|
||
<div class="chat-avatar" style="background:#B83E24">库</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#B83E24">RiskRepository</span>
|
||
<p>list_alerts(status=pending_review) → items + total + disclaimer</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="3" data-sender="ui" style="display:none">
|
||
<div class="chat-avatar" style="background:#D4A843">页</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color:#D4A843">Table + Modal</span>
|
||
<p>展示预警列表;点处置调 handleAlert → POST .../handle</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="chat-typing" id="chat-risk-m1-typing" style="display:none">
|
||
<div class="chat-avatar" id="chat-risk-m1-typing-avatar">…</div>
|
||
<div class="chat-typing-dots"><span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span></div>
|
||
</div>
|
||
<div class="chat-controls">
|
||
<button class="btn chat-next-btn">下一条</button>
|
||
<button class="btn chat-all-btn">全部播放</button>
|
||
<button class="btn chat-reset-btn">重播</button>
|
||
<span class="chat-progress"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<div class="translation-block">
|
||
<div class="translation-code">
|
||
<span class="translation-label">CODE · 模拟交易入口(simulate.py)鉴权</span>
|
||
<pre><code><span class="code-line"><span class="code-keyword">if not</span> (auth.has_role(<span class="code-string">"risk_demo"</span>)</span>
|
||
<span class="code-line"> <span class="code-keyword">or</span> (auth.is_customer()</span>
|
||
<span class="code-line"> <span class="code-keyword">and</span> auth.customer_id == req.customer_id)):</span>
|
||
<span class="code-line"> deny(auth, <span class="code-string">"AUTH_403_ROLE"</span>, ...)</span>
|
||
<span class="code-line"><span class="code-keyword">return</span> submit_trade(req.model_dump(), actor_id=auth.actor_id)</span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">模拟交易不是谁都能点:要么 risk_demo 演示号,要么客户给自己下单。</p>
|
||
<p class="tl">STAFF-30001 有 risk_demo 权限,可在 RiskSimulatePage 代客触发规则。</p>
|
||
<p class="tl">越权会 deny 并写审计,不会悄悄放行。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-risk-m1">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-b"
|
||
data-explanation-right="risk_demo 是 STAFF-30001 的演示权限,允许在模拟交易页提交 trade 请求。"
|
||
data-explanation-wrong="普通 staff 没有 risk_demo;模拟交易专门给演示账号或客户本人。">
|
||
<h3 class="quiz-question">STAFF-30001 能在模拟交易页提交申购吗?</h3>
|
||
<div class="quiz-options">
|
||
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>不能,只有客户本人</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>能,因为有 risk_demo 角色</span>
|
||
</button>
|
||
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
|
||
<div class="quiz-option-radio"></div><span>能,任何 staff 都行</span>
|
||
</button>
|
||
</div>
|
||
<div class="quiz-feedback"></div>
|
||
</div>
|
||
<button class="quiz-check-btn" onclick="checkQuiz('quiz-risk-m1')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-risk-m1')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|