- Introduced `build_all.py` script to automate the assembly of course modules into a single `index.html` file. - Created `index.html` for the main course overview, featuring a structured layout and navigation for various modules. - Developed `_base.html` and `_footer.html` templates for the advisor module, ensuring consistent styling and structure. - Added `build.sh` script for individual module assembly, enhancing modularity and ease of updates. - Implemented multiple module HTML files detailing specific training scenarios and functionalities for advisors, including interactive elements and quizzes. This update significantly enhances the course delivery framework, providing a comprehensive and interactive learning experience for advisors.
127 lines
7.6 KiB
HTML
127 lines
7.6 KiB
HTML
<section class="module" id="module-2">
|
||
<div class="module-inner">
|
||
<p class="eyebrow animate-in">模块 2 · 平台鉴权</p>
|
||
<h1 class="module-title animate-in">get_platform_auth_context:<br>只要 JWT,不要 X-Agent-Type</h1>
|
||
<p class="module-lead animate-in">
|
||
对话线(<code>/api/chat</code>)必须带
|
||
<span class="term" data-definition="HTTP 请求头里的一行,声明这次走 customer/advisor/risk/analyst 哪条 Agent 业务线。">X-Agent-Type</span>;
|
||
平台读 API 故意<strong>不要</strong>——App 和仪表盘只证明「你是谁」,不声明「你在敲哪扇 Agent 窗」。
|
||
</p>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>和 chat 鉴权的对照表</h2>
|
||
<table style="width:100%; border-collapse:collapse; margin: 1.5rem 0; font-size: 0.95rem;">
|
||
<thead><tr><th>场景</th><th>函数</th><th>X-Agent-Type</th><th>归属断言</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>平台 REST</td><td><code>get_platform_auth_context</code></td><td><strong>不要</strong></td><td><code>assert_platform_customer_access</code></td></tr>
|
||
<tr><td>对话 / 风控 REST</td><td><code>get_auth_context</code></td><td><strong>必须</strong></td><td><code>assert_customer_access</code> + 准入矩阵</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="callout callout-warning">
|
||
<strong>指挥 AI 接前端时:</strong> <code>apiFetch</code> 读持仓只带 Bearer;若误加 <code>X-Agent-Type: customer</code>,平台路由会忽略,但混进 chat 路由就会多一层校验甚至 401。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="screen animate-in">
|
||
<h2>群聊:前端问「张三持仓多少」</h2>
|
||
<p>理财师工作台发 GET 持仓——只走平台三层,不进 LangGraph。</p>
|
||
|
||
<div class="chat-window" id="chat-platform-m2">
|
||
<div class="chat-messages">
|
||
<div class="chat-message" data-msg="0" data-sender="frontend" style="display:none">
|
||
<div class="chat-avatar" style="background: var(--color-accent)">🖥</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color: var(--color-accent)">前端</span>
|
||
<p>GET /api/customers/CUST-1001/holdings<br>Authorization: Bearer eyJ…<br><em>(无 X-Agent-Type)</em></p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="1" data-sender="api" style="display:none">
|
||
<div class="chat-avatar" style="background: #5A9DB8">🚪</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color: #5A9DB8">customers.py</span>
|
||
<p>Depends(get_platform_auth_context) → JWT 验签 OK<br>assert_platform_customer_access:STAFF-10086 名下有 CUST-1001 ✓</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="2" data-sender="service" style="display:none">
|
||
<div class="chat-avatar" style="background: #E06B56">📦</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color: #E06B56">platform_service</span>
|
||
<p>list_holdings(customer_id) → prepare_row 脱敏开关检查 → 分页组装 items</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="3" data-sender="core" style="display:none">
|
||
<div class="chat-avatar" style="background: #2D8B55">🗄</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color: #2D8B55">core_ro</span>
|
||
<p>SELECT … FROM holdings WHERE customer_id = ?<br>只读,不写 Core 正式账</p>
|
||
</div>
|
||
</div>
|
||
<div class="chat-message" data-msg="4" data-sender="frontend" style="display:none">
|
||
<div class="chat-avatar" style="background: var(--color-accent)">🖥</div>
|
||
<div class="chat-bubble">
|
||
<span class="chat-sender" style="color: var(--color-accent)">前端</span>
|
||
<p>收到 200 + data.items,渲染持仓表。全程没经过 chat.py。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="chat-typing" style="display:none">
|
||
<div class="chat-avatar" id="chat-platform-m2-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">deps.py</span>
|
||
<pre><code><span class="code-line"><span class="code-keyword">def</span> <span class="code-function">get_platform_auth_context</span>(request: Request) -> AuthContext:</span>
|
||
<span class="code-line"> <span class="code-string">"""平台 API 鉴权:JWT 通道 **不要求** X-Agent-Type。"""</span></span>
|
||
<span class="code-line"> auth_header = request.headers.get(<span class="code-string">"Authorization"</span>, <span class="code-string">""</span>)</span>
|
||
<span class="code-line"> <span class="code-keyword">if</span> auth_header[:<span class="code-number">7</span>].lower() == <span class="code-string">"bearer "</span>:</span>
|
||
<span class="code-line"> claims = verify_token(token)</span>
|
||
<span class="code-line"> <span class="code-keyword">return</span> _bind_state(request, _claims_to_auth(claims))</span>
|
||
<span class="code-line"> <span class="code-comment"># dev:X-Debug-Role / X-Debug-Actor 兜底</span></span></code></pre>
|
||
</div>
|
||
<div class="translation-english">
|
||
<span class="translation-label">白话</span>
|
||
<div class="translation-lines">
|
||
<p class="tl">函数名就写死:平台鉴权,不读 X-Agent-Type。</p>
|
||
<p class="tl">有 Bearer 就验 JWT,解析出 actor_id 和 roles。</p>
|
||
<p class="tl">本地开发没 JWT 时,可用 debug 头冒充身份(仅 development)。</p>
|
||
<p class="tl">和 get_auth_context 不同:后者在 JWT 通道会强制校验 X-Agent-Type + 准入矩阵。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-container" id="quiz-platform-m2">
|
||
<div class="quiz-question-block"
|
||
data-correct="option-a"
|
||
data-explanation-right="问数 POST /api/analyst/chat 走 get_platform_auth_context,前端 analyst.ts 只带 Bearer。"
|
||
data-explanation-wrong="analyst 问数是平台读能力,不是 /api/chat 对话分流,不要 X-Agent-Type。">
|
||
<h3 class="quiz-question">分析员「问数工作台」调 /api/analyst/chat,要带 X-Agent-Type: analyst 吗?</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>要,和 ChatPanel 一样</span>
|
||
</button>
|
||
</div>
|
||
<div class="quiz-feedback"></div>
|
||
</div>
|
||
<button class="quiz-check-btn" onclick="checkQuiz('quiz-platform-m2')">检查答案</button>
|
||
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-platform-m2')">重做</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|