You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
22 KiB
329 lines
22 KiB
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>架构审查 · 第四轮 · 2026-09-07</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
<script>mermaid.initialize({ startOnLoad: true, theme: 'dark', themeVariables: { fontSize: '13px', primaryColor: '#1e293b', primaryTextColor: '#e2e8f0', primaryBorderColor: '#475569', lineColor: '#64748b', secondaryColor: '#0f172a', tertiaryColor: '#0f172a' } });</script>
|
|
<style>
|
|
body { font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; }
|
|
.mono { font-family: "JetBrains Mono", Consolas, monospace; }
|
|
.card { background: #fff; border-radius: 14px; box-shadow: 0 8px 30px rgba(0,0,0,.35); }
|
|
</style>
|
|
</head>
|
|
<body class="bg-slate-900 text-slate-100">
|
|
|
|
<!-- ============ HEADER ============ -->
|
|
<header class="max-w-6xl mx-auto px-6 pt-10 pb-6">
|
|
<div class="flex items-baseline gap-4 flex-wrap">
|
|
<h1 class="text-3xl font-bold tracking-tight">架构审查 · 第四轮</h1>
|
|
<span class="text-slate-400 text-sm mono">2026-09-07 09:26 · /improve-codebase-architecture</span>
|
|
</div>
|
|
<p class="mt-3 text-slate-300 leading-relaxed max-w-4xl">
|
|
扫描范围:昨晚第三轮两候选(<span class="mono text-xs">CustomerCatalogPort</span> / <span class="mono text-xs">OpportunitySubService 按 Tab 拆分</span>)的落地验讫 + 最近两周最热区深扫——
|
|
crm-customer 返工票 09-13 收尾与缺陷修复链、客户交割(票 07)、归属流转(票 04)、导入执行器、商机拆分后的缝合处。
|
|
本轮沿用 codebase-design 词汇:<b>module / interface / depth / seam / adapter / leverage / locality / deletion test</b>。
|
|
</p>
|
|
<div class="mt-4 flex flex-wrap gap-2 text-xs">
|
|
<span class="bg-slate-800 border border-slate-600 rounded-full px-3 py-1">新候选 ×1</span>
|
|
<span class="bg-slate-800 border border-slate-600 rounded-full px-3 py-1">承接候选 ×1(昨晚 #2 未动)</span>
|
|
<span class="bg-slate-800 border border-slate-600 rounded-full px-3 py-1">已核验无恙 ×6</span>
|
|
<span class="bg-slate-800 border border-slate-600 rounded-full px-3 py-1">ADR 冲突:无</span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- ============ LANDED VERIFICATION ============ -->
|
|
<section class="max-w-6xl mx-auto px-6 pb-8">
|
|
<h2 class="text-sm font-semibold text-emerald-300 mb-3 tracking-widest">昨轮候选落地验讫(2026-09-06 第三轮 → 今晨)</h2>
|
|
<div class="grid md:grid-cols-2 gap-4">
|
|
<div class="card p-5 text-slate-800">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="bg-emerald-100 text-emerald-800 text-xs font-bold px-2 py-0.5 rounded">已落地 ✓</span>
|
|
<span class="font-bold">候选 #1 → ADR-0033 CustomerCatalogPort</span>
|
|
</div>
|
|
<p class="text-sm text-slate-600 leading-relaxed">
|
|
<span class="mono text-xs">port/CustomerCatalogPort</span>(3 方法 + 随包 record + <span class="mono text-xs">CustomerCatalogException</span>)与
|
|
<span class="mono text-xs">CustomerCatalogPortImplIntegrationTest</span>(含「裸宽服务返全部、走 port 只返有效」对照断言)均已就位;
|
|
商机侧 <span class="mono text-xs">OpportunityCustomerServiceImpl</span> 只注入 port。落地形状与 ADR 决策一致,归档口径由端口钉死。
|
|
</p>
|
|
</div>
|
|
<div class="card p-5 text-slate-800">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<span class="bg-emerald-100 text-emerald-800 text-xs font-bold px-2 py-0.5 rounded">已落地 ✓</span>
|
|
<span class="font-bold">候选 #3 → OpportunitySubService 按 Tab 拆分</span>
|
|
</div>
|
|
<p class="text-sm text-slate-600 leading-relaxed">
|
|
<span class="mono text-xs">sub/</span> 七子包(客户/跟进/勘察/附件/团队/日志/工作计划)各持小 interface + 自家 impl;
|
|
聚合接口已删,<span class="mono text-xs">OpportunitySubController</span> 306 行薄适配层注入七服务,端点未动。拆分干净、无跨子域泄漏。
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ============ CANDIDATES ============ -->
|
|
<section id="candidates" class="max-w-6xl mx-auto px-6 space-y-10">
|
|
|
|
<!-- ========== CANDIDATE 1 ========== -->
|
|
<div class="card p-0 overflow-hidden">
|
|
<div class="bg-gradient-to-r from-emerald-800 to-emerald-600 px-6 py-3">
|
|
<div class="flex items-center justify-between flex-wrap gap-2">
|
|
<div class="text-xs text-emerald-100 mb-0">候选 #1 · 客户「换主协议」五处手搓 —— 归属流转无单一事实源</div>
|
|
<span class="bg-emerald-950 text-emerald-300 border border-emerald-400 text-xs font-bold px-3 py-1 rounded-full">Strong</span>
|
|
</div>
|
|
</div>
|
|
<div class="p-6 space-y-5">
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">FILES</h3>
|
|
<div class="text-sm text-slate-700 space-y-1 mono text-xs leading-relaxed">
|
|
<div>crm-customer/service/impl/<b>CustomerServiceImpl</b> — applyOwner(L319)、在职守卫(L330)、nextCustomerNo(L358)</div>
|
|
<div>crm-customer/service/impl/<b>CustomerOwnershipServiceImpl</b> — applyOwnerSnapshot(L213)、validateTargetUser(L230)、casUpdate(L244)</div>
|
|
<div>crm-customer/service/impl/<b>CustomerTransferServiceImpl</b> — initiate 内联换主循环(L167)、applyAssigneeSnapshot(L336)、在职守卫 ×3(L301/320/399)、nextTransferNo(L387)</div>
|
|
<div>crm-customer/task/<b>CustomerImportExecutor</b> — createCustomer 内联快照(L240)、锚点(L253)、nextCustomerNo(L330)</div>
|
|
<div>crm-customer/job/<b>CustomerReminderJob</b> — 锚点消费方(L79 isNotNull 过滤静默剔除无锚点客户)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">PROBLEM</h3>
|
|
<p class="text-slate-700 leading-relaxed text-sm">
|
|
客户域的「换主」是一次<b>协议</b>而非一次赋值:owner 四列快照(userId/nameSnapshot/deptId/deptNameSnapshot)+ 部门名查表
|
|
+ 在职守卫(enabled + employmentStatus=active)+ D25 锚点规则(<span class="mono text-xs">last_valid_follow_time</span> 空则赋当下、
|
|
<span class="mono text-xs">enter_pool_time</span> 清空)+ CAS 空列清空(MyBatis-Plus NOT_NULL 策略需 wrapper set null)+ 编号续号。
|
|
这串协议在 <b>5 个文件手搓了 5 份</b>(新建 / 领取·分配 / 交割发起 / 交割分配 / 导入建档),且<b>漂移已经发生</b>:
|
|
</p>
|
|
<div class="mt-3 grid md:grid-cols-3 gap-3">
|
|
<div class="bg-red-50 border border-red-200 rounded-lg p-3">
|
|
<div class="text-xs font-bold text-red-700 mb-1">漂移实证 ①:nextCustomerNo 两份分叉</div>
|
|
<p class="text-xs text-slate-600 leading-relaxed">CustomerServiceImpl 版 catch NumberFormatException → 脏序号重起 1;
|
|
CustomerImportExecutor 版<b>不 catch</b>(脏数据直接炸行)且封顶 9999。同一编号规则,两种边界行为。</p>
|
|
</div>
|
|
<div class="bg-red-50 border border-red-200 rounded-lg p-3">
|
|
<div class="text-xs font-bold text-red-700 mb-1">漂移实证 ②:锚点规则 4 种形态</div>
|
|
<p class="text-xs text-slate-600 leading-relaxed">create 恒 now;claim/assign「空则 now」;initiate「空则 now」;
|
|
<b>applyAssigneeSnapshot 不设锚点</b>——依赖「initiate 必先设过」的时间链不变量,仅存在于注释里。</p>
|
|
</div>
|
|
<div class="bg-red-50 border border-red-200 rounded-lg p-3">
|
|
<div class="text-xs font-bold text-red-700 mb-1">静默失败模式</div>
|
|
<p class="text-xs text-slate-600 leading-relaxed">新增换主点忘设锚点 → CustomerReminderJob 的
|
|
<span class="mono">isNotNull(last_valid_follow_time)</span> 扫描过滤<b>静默剔除</b>该客户。不报错、不留日志——客户只是永远不被提醒。</p>
|
|
</div>
|
|
</div>
|
|
<p class="text-slate-700 leading-relaxed text-sm mt-3">
|
|
同一「在职守卫」还长出 <b>4 个错误码族</b>(CODE_CUST_INVALID / CODE_ASSIGN_INVALID / CODE_TRANSFER_FORBIDDEN / CODE_MEMBER_INVALID),
|
|
5 份部门名查表 <span class="mono text-xs">deptService.getDeptNames(Set.of(...)).get(...)</span>。
|
|
<b>Deletion test</b>:若删掉一个假想的归属流转 module,快照+锚点+守卫+编号协议必须重新各找住处——复杂度<b>收敛</b>而非转移,测试成立。
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">SOLUTION</h3>
|
|
<p class="text-slate-700 leading-relaxed text-sm">
|
|
crm-customer 立「<b>归属流转</b>」深 module(对称线索域既有形状 <span class="mono text-xs">crm-lead/owner/OwnerSnapshotResolver</span>,
|
|
2026-08-20 审查轮落地):换主动作(快照 + 锚点 + 守卫 + CAS 空列收一处)、编号续号收一处,五个调用点各缩成一行;
|
|
D25 锚点语义从 javadoc 注释升格为 interface 契约。预设形态是 grilling 议题(守卫错误码归一 vs 保留语义分族、编号是否并入同 module、
|
|
导入执行器的影子建档路径接法),本报告不预设 interface。
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">BENEFITS</h3>
|
|
<div class="grid md:grid-cols-3 gap-3 text-sm">
|
|
<div class="bg-emerald-50 border border-emerald-200 rounded-lg p-3">
|
|
<div class="text-xs font-bold text-emerald-700 mb-1">Locality</div>
|
|
<p class="text-xs text-slate-600 leading-relaxed">锚点 + 快照 + 守卫协议住一个文件;「已归档/已离职还能不能换主」这类口径从隐式变一行契约(同 ADR-0033 requireLive 的赢法)。</p>
|
|
</div>
|
|
<div class="bg-emerald-50 border border-emerald-200 rounded-lg p-3">
|
|
<div class="text-xs font-bold text-emerald-700 mb-1">Leverage</div>
|
|
<p class="text-xs text-slate-600 leading-relaxed">A5 项目模块的 owner 同步、客户合并、未来批量换主——每个新换主点从「复刻 30 行协议」变一行调用。seam 每早立一天,协议就少复制一份。</p>
|
|
</div>
|
|
<div class="bg-emerald-50 border border-emerald-200 rounded-lg p-3">
|
|
<div class="text-xs font-bold text-emerald-700 mb-1">Test surface</div>
|
|
<p class="text-xs text-slate-600 leading-relaxed">锚点契约一份 module 级测试,替代今天散在 4 个流程测试里的 per-flow 断言(WorkspaceOwnership L400/425、Transfer L282、Import L722);「忘设锚点」从静默变红。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">BEFORE / AFTER</h3>
|
|
<div class="grid md:grid-cols-2 gap-4">
|
|
<div class="bg-slate-900 rounded-lg p-3">
|
|
<div class="text-xs text-red-300 font-bold mb-2">BEFORE — 协议平铺 5 份,锚点消费方靠运气</div>
|
|
<pre class="mermaid">
|
|
graph TD
|
|
subgraph 手搓协议×5
|
|
A1[CustomerServiceImpl<br/>create→applyOwner]
|
|
A2[OwnershipServiceImpl<br/>claim/assign→applyOwnerSnapshot]
|
|
A3[TransferServiceImpl<br/>initiate 内联循环]
|
|
A4[TransferServiceImpl<br/>applyAssigneeSnapshot<br/>锚点靠时间链不变量]
|
|
A5[ImportExecutor<br/>createCustomer 内联]
|
|
end
|
|
A1 & A2 & A3 & A4 & A5 -->|各写各的| C[(customer 表<br/>owner 4列 + 锚点)]
|
|
C -->|isNotNull 过滤| J[CustomerReminderJob<br/>无锚点=静默不提醒]
|
|
style A4 fill:#7f1d1d
|
|
style J fill:#7f1d1d
|
|
</pre>
|
|
</div>
|
|
<div class="bg-slate-900 rounded-lg p-3">
|
|
<div class="text-xs text-emerald-300 font-bold mb-2">AFTER — 一个深 module,接口即契约</div>
|
|
<pre class="mermaid">
|
|
graph TD
|
|
A1[create] --> M
|
|
A2[claim/assign] --> M
|
|
A3[交割两段] --> M
|
|
A5[导入建档] --> M
|
|
M[归属流转 module<br/>接口 2-3 方法<br/>快照+锚点+守卫+CAS+编号<br/>全收实现]
|
|
M -->|一行调用| C[(customer 表)]
|
|
M -.->|契约钉死锚点| J[ReminderJob]
|
|
T[module 契约测试<br/>锚点/快照/守卫] -.-> M
|
|
style M fill:#065f46
|
|
style T fill:#064e3b
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
<div class="text-center font-bold text-[11px] mt-2 text-slate-600">
|
|
「换主时锚点动不动」从 5 份注释 + 1 个时间链不变量,变成一个 interface 的契约 + 一份测试
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border-t border-slate-200 pt-3 text-xs text-slate-500 leading-relaxed">
|
|
<b>ADR 关系(非冲突)</b>:与 ADR-0022(线索读写分离深模块)同向——线索侧 <span class="mono">OwnerSnapshotResolver</span> 正是本候选的先行样本;
|
|
与 ADR-0033(归档口径收进 port)同族——都是「把隐式口径升格为显式契约」。
|
|
同形修法在本仓已赢两次:lead 的 OwnerSnapshotResolver(2026-08-20)与客户自己的 CustomerPendingNoticeWriter(薄写入器收写口径)。
|
|
顺序耦合:与候选 #2 在 ImportExecutor 交汇——先立本候选,#2 收影子建档路径时才有单一事实源可接。
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========== CANDIDATE 2 ========== -->
|
|
<div class="card p-0 overflow-hidden">
|
|
<div class="bg-gradient-to-r from-amber-700 to-amber-500 px-6 py-3">
|
|
<div class="flex items-center justify-between flex-wrap gap-2">
|
|
<div class="text-xs text-amber-50">候选 #2 · 客户导入「同口径」靠复制与字符串前缀维持 <span class="opacity-75">(昨晚 #2 承接,未动)</span></div>
|
|
<span class="bg-amber-950 text-amber-300 border border-amber-300 text-xs font-bold px-3 py-1 rounded-full">Worth exploring</span>
|
|
</div>
|
|
</div>
|
|
<div class="p-6 space-y-5">
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">FILES</h3>
|
|
<div class="text-sm text-slate-700 space-y-1 mono text-xs leading-relaxed">
|
|
<div>crm-customer/service/impl/<b>CustomerImportServiceImpl</b> — upload(L100-140):EasyExcel 双 sheet 解析 + verdict 计数 switch</div>
|
|
<div>crm-customer/task/<b>CustomerImportExecutor</b> — doImport(L89-97):同一解析逐字复制 + 计数第二份;L193 reason.startsWith("疑似重复")</div>
|
|
<div>crm-customer/service/impl/<b>CustomerImportAnalyzer</b> — 判定语义单点(已对,deletion test 通过)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">PROBLEM</h3>
|
|
<p class="text-slate-700 leading-relaxed text-sm">
|
|
判定语义已正确单点化(analyzer 深模块成立),但机制四周边缘仍是<b>约定接缝</b>:workbook 解析两处逐字复制(sheet0 必读 + sheet1
|
|
缺席容错,改一处另一处必漂);verdict 计数两份 switch(upload 的 insert/update/fail/suspect vs executor 再加 unchanged);
|
|
「他客户电话软提示」靠 reason 字符串前缀跨方法传协议(<span class="mono text-xs">startsWith("疑似重复")</span>——改文案即断功能)。
|
|
模板演进(加列、加 sheet)须同时改对四处。昨晚已报,今晨验讫:原样未动。
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">SOLUTION / BENEFITS</h3>
|
|
<p class="text-slate-700 leading-relaxed text-sm">
|
|
抽 <span class="mono text-xs">CustomerImportReader</span>(bytes → 两 sheet 行 + 模板表头校验单点),upload 与 executor 共用;
|
|
ContactPlan 加显式 softSuspect 结构化契约替代字符串前缀;计数 tally 单点。<b>收敛面克制</b>:不收判定语义(analyzer 已对)、
|
|
不收行落库(executor 的真差异)。Benefits:预览与执行「同口径」从 javadoc 变同一份代码;模板演进只改一处;
|
|
<span class="mono text-xs">CustomerImportIntegrationTest</span>(40KB)的守恒断言获得单一被测面。
|
|
<b>与 #1 的交汇</b>:executor 的 createCustomer 是影子建档路径(手搓 owner 快照 + 锚点 + 编号)——先落 #1 再收此处,导入建档接归属流转 module 即可。
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<h3 class="text-xs font-bold text-slate-500 tracking-widest mb-2">BEFORE / AFTER</h3>
|
|
<div class="grid md:grid-cols-2 gap-4">
|
|
<div class="bg-slate-900 rounded-lg p-3">
|
|
<div class="text-xs text-red-300 font-bold mb-2">BEFORE — 机制边缘四处约定</div>
|
|
<pre class="mermaid">
|
|
graph TD
|
|
U[upload] --> P1[EasyExcel 解析<br/>副本1]
|
|
E[executor] --> P2[EasyExcel 解析<br/>副本2 逐字复制]
|
|
U --> T1[计数 switch 1]
|
|
E --> T2[计数 switch 2]
|
|
AN[Analyzer 判定<br/>单点 ✓] -.->|reason 字符串前缀<br/>疑似重复…| E
|
|
style P2 fill:#7f1d1d
|
|
style T2 fill:#7f1d1d
|
|
</pre>
|
|
</div>
|
|
<div class="bg-slate-900 rounded-lg p-3">
|
|
<div class="text-xs text-emerald-300 font-bold mb-2">AFTER — 机制单点,判定仍归 analyzer</div>
|
|
<pre class="mermaid">
|
|
graph TD
|
|
U[upload] --> R[CustomerImportReader<br/>bytes→两sheet行+表头校验]
|
|
E[executor] --> R
|
|
U --> T[tally 单点]
|
|
E --> T
|
|
AN[Analyzer 判定单点] -->|softSuspect 布尔<br/>结构化契约| E
|
|
style R fill:#065f46
|
|
style T fill:#064e3b
|
|
</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border-t border-slate-200 pt-3 text-xs text-slate-500 leading-relaxed">
|
|
<b>ADR 关系</b>:无冲突。昨晚报告原文承接,无新证据改变评级。
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</section>
|
|
|
|
<!-- ============ CHECKED AND FINE ============ -->
|
|
<section class="max-w-6xl mx-auto px-6 py-10">
|
|
<h2 class="text-sm font-semibold text-slate-400 mb-3 tracking-widest">已核验无恙(本轮扫过、不立票)</h2>
|
|
<div class="grid md:grid-cols-2 gap-3 text-xs">
|
|
<div class="bg-slate-800/60 border border-slate-700 rounded-lg p-3 text-slate-300">
|
|
<b class="text-slate-100">商机拆分后缝合处</b> — <span class="mono">OpportunitySubController</span> 注入七小服务,薄适配无泄漏;sub/customer 走 CustomerCatalogPort。
|
|
</div>
|
|
<div class="bg-slate-800/60 border border-slate-700 rounded-lg p-3 text-slate-300">
|
|
<b class="text-slate-100">CustomerWorkspaceServiceImpl</b> — 深 module:公共 WHERE 在 CustomerMapper 常量单源,saved-view 翻译、公海部门折算、看板分组白名单各就各位。
|
|
</div>
|
|
<div class="bg-slate-800/60 border border-slate-700 rounded-lg p-3 text-slate-300">
|
|
<b class="text-slate-100">四情形可见性平行实现</b> — CustomerScopeEvaluator ↔ DataScopeInterceptor 口径平行,但 exhaustive switch 编译期互锁(Kind 加枚举值即编译红),且有文档锚点。类型系统看住了,不立票。
|
|
</div>
|
|
<div class="bg-slate-800/60 border border-slate-700 rounded-lg p-3 text-slate-300">
|
|
<b class="text-slate-100">SavedViewFilter 双样本</b> — 商机/客户两个翻译器,代码内明记「rule of three:商机样本 1 + 客户样本 2」,自觉管理中,等第三样本再议。
|
|
</div>
|
|
<div class="bg-slate-800/60 border border-slate-700 rounded-lg p-3 text-slate-300">
|
|
<b class="text-slate-100">CustomerReminderJob</b> — 正确使用深引擎 PendingNoticeWriter;提醒链推导与锚点失效判定留域内(真差异),三层结构清爽。
|
|
</div>
|
|
<div class="bg-slate-800/60 border border-slate-700 rounded-lg p-3 text-slate-300">
|
|
<b class="text-slate-100">线索写侧编排</b> — LeadServiceImpl 25KB 但编排委托齐整(Transition/Deadlines/CreationPlanner/HistoryRecorder/OwnerSnapshotResolver 各就各位),ADR-0022 形态保持。
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ============ TOP RECOMMENDATION ============ -->
|
|
<section id="top-recommendation" class="max-w-6xl mx-auto px-6 pb-14">
|
|
<div class="rounded-2xl bg-gradient-to-br from-emerald-900 via-emerald-800 to-slate-900 border border-emerald-700 p-8">
|
|
<div class="text-xs text-emerald-300 tracking-widest font-bold mb-2">TOP RECOMMENDATION</div>
|
|
<h2 class="text-2xl font-bold text-white mb-3">候选 #1 · 客户归属流转深 module</h2>
|
|
<p class="text-emerald-50/90 text-sm leading-relaxed">
|
|
理由:<b>(a) 同形修法在本仓已赢两次</b>——线索 OwnerSnapshotResolver(2026-08-20 三件套之一)与客户 CustomerPendingNoticeWriter,
|
|
都是「散落写路径协议收一处」的照抄棋;<b>(b) 漂移已发生</b>——两份 nextCustomerNo 对脏序号行为分叉、锚点规则 4 形态,
|
|
这不是理论风险而是现存分叉;<b>(c) 失败模式静默</b>——忘设锚点的客户被提醒 Job 无声剔除,比报错难抓一个量级;
|
|
<b>(d) 消费方还在长</b>——A5 项目模块的 owner 同步、客户合并都在路上,seam 每早立一天协议就少复制一份;
|
|
<b>(e) 顺序红利</b>——与 #2 在导入执行器交汇,先立 #1,#2 收影子建档路径时直接接 seam,两票变成接力而非交叉。
|
|
</p>
|
|
<p class="text-emerald-50/60 text-xs mt-4">下一步:选定后进入 /grilling 走决策树(interface 形状 / 守卫错误码归一 / 编号归属 / 导入接法 / 测试面存亡),决策落 ADR + CONTEXT.md 术语同步。</p>
|
|
</div>
|
|
</section>
|
|
|
|
<footer class="max-w-6xl mx-auto px-6 pb-10 text-xs text-slate-500">
|
|
第四轮架构审查 · 2026-09-07 · 词汇表:module / interface / depth / seam / adapter / leverage / locality / deletion test(/codebase-design)
|
|
· 领域语言取自 crm-customer/CONTEXT.md(销售负责人 / D25 锚点 / 客户交割 / 客户公海)
|
|
</footer>
|
|
|
|
</body>
|
|
</html>
|
|
|