Files
group_xinghuo_jinrong/docs/course/jinrong-module-risk/modules/07-concurrency.html
T
zhanghongyu_0626 21ced4d38f feat(report): Add battery report generation and update course documentation
- Introduced `battery_report.json` for local data analysis, excluding it from the database.
- Enhanced `AGENTS.md` to reflect updated test baseline with 795 passed tests.
- Added new metrics for trade flow in `dict_service.py`, improving transaction data analysis.
- Updated regex patterns in `guardrail.py` to better handle numeric extraction and prevent misinterpretation of tokens.
- Expanded course modules with new content on FR-8/9/10 capabilities and L3 role management.
- Improved concurrency handling in transaction processing to ensure accurate alert generation.

This update enhances data analysis capabilities and improves the overall structure and clarity of course materials.
2026-09-10 14:32:25 +08:00

153 lines
9.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<section class="module" id="module-7">
<div class="module-inner">
<p class="eyebrow animate-in">模块 7 · 写侧并发</p>
<h1 class="module-title animate-in">同一天两笔交易<br>为什么还是一条预警?</h1>
<p class="module-lead animate-in">
进程内聚合锁原语(<code>locks.py</code>)把「同一客户、同一聚合 key」串行化;L3 监测画像写入服务(<code>profile_l3.py</code>)用
<code>computed_at</code> 乐观锁防丢更新。指挥 AI 改写路径时,先分清<strong>悲观锁(串行)</strong>和<strong>乐观锁(版本戳比对)</strong>各管哪一段。
</p>
<div class="screen animate-in">
<h2>两层锁 · 各管什么</h2>
<table style="width:100%; border-collapse:collapse; margin:1.5rem 0; font-size:0.95rem;">
<thead>
<tr><th style="text-align:left;padding:0.5rem;">职责</th><th style="text-align:left;padding:0.5rem;">代码入口</th><th style="text-align:left;padding:0.5rem;">机制</th></tr>
</thead>
<tbody>
<tr>
<td style="padding:0.5rem;vertical-align:top;">预警同日聚合串行</td>
<td style="padding:0.5rem;vertical-align:top;">预警处置与聚合服务(<code>alert_service.py</code>)+ 锁原语(<code>locks.py</code> · <code>run_locked</code>)</td>
<td style="padding:0.5rem;vertical-align:top;">单进程 <code>threading.Lock</code>;拿锁超时降级执行,冲突由锁内重查兜底</td>
</tr>
<tr>
<td style="padding:0.5rem;vertical-align:top;">L3 写侧防覆盖</td>
<td style="padding:0.5rem;vertical-align:top;">L3 写入(<code>profile_l3.py</code>)+ 仓储更新(<code>risk_repository.update_l3</code>)</td>
<td style="padding:0.5rem;vertical-align:top;">乐观锁:<code>WHERE computed_at = :expected</code>,失败重读重试(最多 3 次)</td>
</tr>
<tr>
<td style="padding:0.5rem;vertical-align:top;">处置 + 审计同事务</td>
<td style="padding:0.5rem;vertical-align:top;">人工处置(<code>handle_alert</code>)+ 风控仓储(<code>risk_repository</code>)</td>
<td style="padding:0.5rem;vertical-align:top;">状态变更与 audit 同连接提交(B7 挂账⑦ 已收口)</td>
</tr>
</tbody>
</table>
<div class="callout callout-accent">
<strong>不做:</strong> 风控线不接 L1/L2 画像 Redis 热缓存(归客服/顾问);L3 读侧已有 <code>profile:l3:{customer_id}</code> cache-aside + 写后 DEL。
</div>
</div>
<div class="screen animate-in">
<div class="translation-block">
<div class="translation-code">
<span class="translation-label">锁原语 · 预警聚合 key 示例</span>
<pre><code><span class="code-line"><span class="code-comment"># alert_service:同日同客户事件类预警</span></span>
<span class="code-line">run_locked(</span>
<span class="code-line"> <span class="code-string">f"agg:event:{customer_id}:{today}"</span>,</span>
<span class="code-line"> <span class="code-keyword">lambda</span> locked: _merge_or_insert(...),</span>
<span class="code-line">)</span>
<span class="code-line"></span>
<span class="code-line"><span class="code-comment"># profile_l3:同一客户 L3 upsert</span></span>
<span class="code-line">run_locked(<span class="code-string">f"l3:{customer_id}"</span>, _write_with_optimistic_retry)</span></code></pre>
</div>
<div class="translation-english">
<span class="translation-label">白话</span>
<p>想象收银台<strong>叫号器</strong>:同一客户、同一天的「首单聚合」必须排队进窗口,窗口里再查「今天是否已有单」——两笔并发进来也不会开出两张重复首单。</p>
<p>L3 则是<strong>贴便签前先对表上的时间戳</strong>:若别人刚改过,你的 UPDATE 打不中,就重读再合并(只升不降监测档)。</p>
</div>
</div>
</div>
<div class="screen animate-in">
<h2>群聊:并发两笔 · 聚合与 L3</h2>
<div class="chat-window" id="chat-risk-m7">
<div class="chat-messages">
<div class="chat-message" data-msg="0" data-sender="t1" style="display:none">
<div class="chat-avatar" style="background:#D94F30">T1</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#D94F30">trade #1</span>
<p>引擎出单 → 请求 <code>run_locked(agg:event:…)</code></p>
</div>
</div>
<div class="chat-message" data-msg="1" data-sender="t2" style="display:none">
<div class="chat-avatar" style="background:#E87A62">T2</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#E87A62">trade #2</span>
<p>同时到达 → 在锁外等待或超时降级;锁内重查并入同一 <code>risk_alert</code></p>
</div>
</div>
<div class="chat-message" data-msg="2" data-sender="l3" style="display:none">
<div class="chat-avatar" style="background:#B83E24">L3</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#B83E24">profile_l3</span>
<p>upsert 前读 <code>computed_at</code> → UPDATE 带 expected → 失败则重试</p>
</div>
</div>
<div class="chat-message" data-msg="3" data-sender="audit" style="display:none">
<div class="chat-avatar" style="background:#D4A843">审</div>
<div class="chat-bubble">
<span class="chat-sender" style="color:#D4A843">audit</span>
<p>处置 handle:改 status + 写 audit_log <strong>同一事务</strong>(避免「状态变了没审计」)</p>
</div>
</div>
</div>
<div class="chat-typing" id="chat-risk-m7-typing" style="display:none">
<div class="chat-avatar" id="chat-risk-m7-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">
<h2>数据流:写侧三条线</h2>
<div class="flow-animation" data-steps='[
{"highlight":"flow-w1","label":"交易事件进引擎 → alert_service 拿 agg 锁"},
{"highlight":"flow-w2","label":"锁内查锚点 → 首单 INSERT / 后续 append 事件","packet":true,"from":"1","to":"2"},
{"highlight":"flow-w3","label":"profile_l3 upsert:run_locked + 乐观锁 retry","packet":true,"from":"2","to":"3"},
{"highlight":"flow-w1","label":"officer handle:status + audit 同事务","packet":true,"from":"3","to":"1"}
]'>
<div class="flow-actors">
<div class="flow-actor" id="flow-w1"><span class="flow-actor-icon">🔒</span><span>聚合锁</span></div>
<div class="flow-actor" id="flow-w2"><span class="flow-actor-icon">📋</span><span>risk_alert</span></div>
<div class="flow-actor" id="flow-w3"><span class="flow-actor-icon">📊</span><span>L3 + audit</span></div>
</div>
<p class="flow-step-label">点击「下一步」</p>
<div class="flow-controls">
<button class="btn flow-next-btn">下一步</button>
<button class="btn flow-reset-btn">重来</button>
</div>
</div>
</div>
<div class="screen animate-in">
<div class="quiz-container" id="quiz-risk-m7">
<div class="quiz-question-block"
data-correct="option-b"
data-explanation-right="对。L1/L2 Redis 热缓存是客服/顾问画像线;风控写侧是 L3 + 聚合锁 + 审计同事务。"
data-explanation-wrong="风控不做 L1/L2 Redis;L3 已有 cache-aside,写侧靠锁与乐观锁。">
<h3 class="quiz-question">指挥 AI「给风控加 L1 Redis 热读」合理吗?</h3>
<div class="quiz-options">
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>合理,四个 Agent 画像都应同一套 Redis</span>
</button>
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>不合理;风控只需 L3 cache-aside,L1/L2 归客服/顾问</span>
</button>
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>合理,用 Redis 替代所有 MySQL 写</span>
</button>
</div>
<div class="quiz-feedback"></div>
</div>
<button class="quiz-check-btn" onclick="checkQuiz('quiz-risk-m7')">检查答案</button>
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-risk-m7')">重做</button>
</div>
</div>
</div>
</section>