Implement code changes to enhance functionality and improve performance
parent
9ea206d1b4
commit
a0baa7a181
|
|
@ -90,6 +90,7 @@
|
|||
.callout.note { background: var(--note-bg); border-color: var(--note-border); }
|
||||
.callout.gap { background: var(--gap-bg); border-color: var(--gap-border); }
|
||||
.callout.ok { background: var(--ok-bg); border-color: var(--ok-border); }
|
||||
.callout.newreq { background:#fbefff; border-color:#d8b9ff; }
|
||||
.callout .lbl { font-weight: 700; }
|
||||
.pill { display:inline-block; font-size:11px; font-weight:700; padding:1px 8px; border-radius:20px; }
|
||||
.pill.done { background: var(--ok-bg); color:#1a7f37; border:1px solid var(--ok-border);}
|
||||
|
|
@ -119,6 +120,10 @@
|
|||
<p class="meta">參照:旧站 <code>justsolutionsWeb</code> 的 <code>PlanService</code>(同一套後端端點) · mock 契約權威來源 <code>server/mock/plans-plus.js</code></p>
|
||||
<p class="meta updated">本次更新(2026-07):修正 BR/CI 校驗現狀、補入 <b>單次查詢</b>/<b>線下對接</b>/<b>agent tiers</b>/<b>KYC 租賃</b>/<b>主體類型</b> 等前端新契約,並新增「本地開發環境對接」與「本地種子數據缺口」章節。</p>
|
||||
<p class="meta updated">補充更新(2026-07):釐清「<b>聯絡我們/聯絡推薦人</b>」語義——<b>不創建任何訂單/租戶/線索</b>,而是把當前<b>訂單摘要</b>經<b>既有 contact us API(<code>POST /api/contact</code> → 後端 <code>customer/CreateFeedback</code>)</b>發送給平台管理員;若已填有效推薦人代碼,則改送該推薦人(其郵箱)。原「線下對接下單」定位(5.8)已據此改寫。</p>
|
||||
<p class="meta updated">更正(2026-07):<b>BR/CI 非必填</b>(業務決策)——去除 <code>CreateOrder</code> 的 <code>OrderService.cs:154</code>/<code>:190</code> 兩處必填校驗(保留唯一性校驗)。文檔原「仍必填 / 阻塞 / 需業務決策」表述作廢;第 1/5.7/6.3/7-9 章已統一。<b>核對:當前 working copy 這兩處仍為 active,需確認放開已在對接/部署環境應用。</b></p>
|
||||
<p class="meta updated">再次更新(2026-07):<b>單次查詢(隨付即用 · Pay-As-You-Go)後端已支持</b>——由 AML 主模塊 <code>ConsumerPortal</code>「即時檢測」承接。<b>調用方已確認為 AML 後端內部編排</b>:<b>訂單支付完成後</b>,由後端在 2C 租戶上下文內依次 <code>CreateConsumerLink</code>(建鏈接、拿 accessToken)→ <code>CreateConsumerOrder</code>(建訂單、即時檢測、結果郵件);<b>justsolutionsWebV2 只負責下單 + 收款,不調這兩個接口</b>(既非 V2 BFF、也非瀏覽器發起)。原 5.6「後端零支持」結論<b>作廢</b>,第 1/2/4/5.6/7-9 章已據此改寫。</p>
|
||||
<p class="meta updated">租戶查詢落定(2026-07):<b><code>GET /tenants/lookup?email=</code> 需後端新增「按管理員郵箱查可續費租戶」端點</b>。現 <code>queryRenewableTenant</code> 在 DB 層<b>只能按租戶名 <code>Contains</code> 搜</b>,<code>TenantAdminEmail</code> 是查完後遍歷<b>全部租戶用戶</b>逐條回填(<code>OrderService.cs:2372-2379</code>),<b>無法按郵箱在 DB 過濾</b>。BFF「以空 keyword 拉全部可續費租戶、再本地按郵箱篩」的變通<b>不採用</b>(現端點按租戶名搜、返回整張租戶列表,查詢維度與聚合歸屬都更該在後端);<b>後端按郵箱全庫掃描本身可接受</b>(租戶數少、低頻交互,可復用現 <code>GetAllUsers()</code> 全庫掃描、改按郵箱過濾)。第 1 / 5.5 / 7 / 8 / 9 章已據此改寫,並補入新端點的具體設計(入參、反查邏輯、聚合返回)。</p>
|
||||
<p class="meta updated">單次查詢定價修正(2026-07):<b>隨付即用(PAYG)的收款金額改由 AML <code>GetPlanList</code> 按 <code>countryCode</code> 返回的 PAYG(P2G) 方案價決定</b>(不再是 V2 <code>/single-query/options</code> 分項單價之和);<b>檢測內容為固定 4 項、純展示、用戶不可勾選/取消</b>——<code>證件核驗(V)</code>、<code>名單篩查(ES=E)</code>、<code>AI 增強型篩查(A)</code>、<code>信貸記錄篩查(失信人=D)</code>(叫法後續可能再調整)。頁面 <code>/plans-jp</code>(日本別名,<code>server/index.js</code> 直接以 <code>plans-plus.html</code> 承接、URL 不變;日本 IP 訪 <code>/plans</code> 自動 302 至此)對應 <code>countryCode:"JPN"</code>(<b>目前 <code>countryCode</code> 僅日本一國</b>;PAYG 為<b>單一整包價</b>,非分項之和)。原 5.6「op→功能碼非 1:1 / 公司查冊歸屬 / V2 分項單價與後端 jQ 計費對齊」等缺口據此<b>收斂</b>;第 1 / 3.1 / 5.6 / 7 / 8 / 9 章已改寫。</p>
|
||||
</header>
|
||||
|
||||
<div class="toc">
|
||||
|
|
@ -134,7 +139,7 @@
|
|||
<li><a href="#s5-3">5.3 GET /countries(註冊地)</a></li>
|
||||
<li><a href="#s5-4">5.4 GET /agents/:code(推薦人 + tiers)</a></li>
|
||||
<li><a href="#s5-5">5.5 GET /tenants/lookup(按郵箱查租戶)</a></li>
|
||||
<li><a href="#s5-6">5.6 單次查詢 single-query(新)</a></li>
|
||||
<li><a href="#s5-6">5.6 單次查詢 single-query(ConsumerPortal 即時檢測)</a></li>
|
||||
<li><a href="#s5-7">5.7 POST /subscribe(下單)</a></li>
|
||||
<li><a href="#s5-8">5.8 聯絡我們/推薦人(發訂單摘要·不下單)</a></li>
|
||||
<li><a href="#s5-9">5.9 POST /payments/create(支付·暫緩)</a></li>
|
||||
|
|
@ -156,9 +161,9 @@
|
|||
<tr><td><code>GET /plans/catalog</code></td><td><code>plan/portal/GetPlanList</code></td><td><span class="pill partial">改造</span></td><td>復刻 filterPlan;<b>KYC 改月租</b>、jQuota 改配套;<code>bestValue</code>/<code>note</code>/<code>nameJP</code> 後端無;<b>本地庫 0 條 Plan</b></td></tr>
|
||||
<tr><td><code>GET /countries</code></td><td><code>Order/getCategoryByTypes</code></td><td><span class="pill done">已實現</span></td><td>—(<code>routes/countries.js</code> 已可用)</td></tr>
|
||||
<tr><td><code>GET /agents/:code</code></td><td><code>SearchUserByCodeAndType</code> + <code>GetPlanList(agentUserId)</code></td><td><span class="pill partial">改造</span></td><td><b>新增 <code>tiers</code></b>(可售方案級別)、<code>email</code>/<code>phone</code>、隱藏 <code>agentorId</code></td></tr>
|
||||
<tr><td><code>GET /tenants/lookup</code></td><td><code>Order/portal/queryRenewableTenant</code></td><td><span class="pill todo">缺口大</span></td><td>後端按<b>租戶名</b>搜;前端<b>按郵箱、唯一命中</b>(不再多租戶消歧);需重建 <code>currentSubscription</code> + <code>referrer</code></td></tr>
|
||||
<tr><td><code>GET /single-query/options</code><br><code>POST /single-query</code></td><td><span class="pill newreq">無對應後端</span></td><td><span class="pill todo">缺口大</span></td><td><b>全新按次查詢流程,後端完全無端點</b>(見 5.6)</td></tr>
|
||||
<tr><td><code>POST /subscribe</code></td><td><code>CreateOrder</code> / <code>TenantRenewal</code></td><td><span class="pill partial">改造</span></td><td>補 <code>planDetailId</code>;<b>BR/CI 仍必填(更正)</b>;<code>topup</code>/個人主體待定</td></tr>
|
||||
<tr><td><code>GET /tenants/lookup</code></td><td><code>queryRenewableTenant</code> ⟶ <b>新增</b> <code>queryRenewableTenantByEmail</code></td><td><span class="pill todo">需後端新增</span></td><td><b>已定:後端新增按郵箱端點</b>。現端點 DB 層只按<b>租戶名 <code>Contains</code></b> 搜、郵箱查後回填→不能按郵箱過濾;前端<b>按郵箱、唯一命中</b>(不再多租戶消歧);新端點直接聚合 <code>currentSubscription</code> + <code>referrer</code>(見 5.5)</td></tr>
|
||||
<tr><td><code>GET /single-query/options</code><br><code>POST /single-query</code></td><td>V2 只到「下單+收款」;<br>支付後 <b>AML 後端內部</b> <code>CreateConsumerLink</code>+<code>CreateConsumerOrder</code></td><td><span class="pill partial">改造</span></td><td><b>更正:後端已支持</b>(AML 即時檢測);兩步為<b>後端內部編排</b>,V2/瀏覽器均不調;<b>PAYG 定價改由 <code>GetPlanList</code>(countryCode) 的 PAYG 方案價</b>、檢測內容固定 4 項(V/E/A/D)純展示不可選(見 5.6)</td></tr>
|
||||
<tr><td><code>POST /subscribe</code></td><td><code>CreateOrder</code> / <code>TenantRenewal</code></td><td><span class="pill partial">改造</span></td><td>補 <code>planDetailId</code>;<b>BR/CI 非必填(已定)</b>;<code>topup</code>/個人主體待定</td></tr>
|
||||
<tr><td><code>POST /contact</code><br><span class="small">(聯絡我們/推薦人)</span></td><td><code>customer/CreateFeedback</code></td><td><span class="pill done">復用現有</span></td><td><b>不創建訂單</b>:訂單摘要經 contact us API 發管理員;填了推薦人則發推薦人(見 5.8)</td></tr>
|
||||
<tr><td><code>POST /payments/create</code></td><td>—</td><td><span class="tag">暫緩</span></td><td><b>本輪支付除外</b>:前端提交後直接顯示「已提交成功」(見 5.9)</td></tr>
|
||||
</tbody>
|
||||
|
|
@ -166,8 +171,8 @@
|
|||
|
||||
<div class="callout gap">
|
||||
<p><span class="lbl">本次三個最重要的更正 / 硬缺口:</span></p>
|
||||
<p>① <b>BR/CI 必填(舊版結論已過時,本輪已放開)</b>:<code>CreateOrder</code> 兩處「BR 或 CI 至少一個」校驗(<code>OrderService.cs:154</code> 與 <code>:190</code>)原本仍生效(舊版文檔稱 2026-06 已放開與現狀不符)。<span class="pill done">已改</span> 本輪按決策<b>已注釋這兩處必填校驗</b>(保留唯一性校驗),並 rebuild 生效——個人主體/空 BR 已可下單。<b>注意:此為全局改動,旧站共用 <code>CreateOrder</code>,其「空 BR 不再攔截」影響待業務確認(見第 9 章 Q1)。</b></p>
|
||||
<p>② <b>單次查詢(single-query)後端零支持</b>:全庫 grep 無任何按次查詢 / pay-per-use 概念。<code>/single-query/options</code> 與 <code>/single-query</code> 需新增後端,或本地繼續走 mock。</p>
|
||||
<p>① <b>BR/CI 非必填(業務決策,已定)</b>:plans-plus 新購(含個人主體 / 空 BR/CI)需能下單 ⇒ <b>BR/CI 不作必填</b>。實現=注释 <code>CreateOrder</code> 兩處必填校驗(<code>OrderService.cs:154</code> 與 <code>:190</code>);唯一性校驗 <code>ExistsByOrganizationBRCI</code> 對「BR、CI 皆空」返回 <code>false</code>(<code>:1124</code>),<b>空值安全通過,無需其它改動</b>。<span class="pill todo">核對</span> <b>當前 working copy 這兩處仍為 active</b>——若對接/部署環境尚未放開,空 BR/CI 仍會被攔,需確認該改動已應用。注意此為全局改動(旧站共用 <code>CreateOrder</code>),「空 BR 不再攔截」影響請業務知悉(見第 9 章 Q1)。</p>
|
||||
<p>② <b>單次查詢(single-query)後端已支持(本輪更正,原「零支持」結論作廢)</b>:走 AML 主模塊的 <b>ConsumerPortal 即時檢測</b>,且<b>調用方為 AML 後端內部編排</b>——<b>訂單支付完成後</b>,後端在 2C 租戶上下文內 <code>CreateConsumerLink</code>(建鏈接、拿 accessToken)→ <code>CreateConsumerOrder</code>(建訂單、即時檢測、結果郵件)。<b>V2 只下單 + 收款,不調這兩個接口</b>。<b>本輪再定:PAYG 檢測內容固定為 <code>證件核驗 / 名單篩查(ES) / AI 增強型篩查 / 信貸記錄篩查(失信人)</code> 4 項純展示(用戶不可勾選/取消,確定性映射 <code>V/E/A/D</code>),收款金額取自 <code>GetPlanList</code> 按 <code>countryCode</code>(<code>/plans-jp→JPN</code>)返回的 PAYG(P2G) 方案價</b>——原「op→功能碼非 1:1 / 公司查冊無碼 / V2 分項單價與後端 jQ 對齊」缺口收斂。<code>CreateConsumerLink</code> 非匿名(<code>RealtimeScreeningManagement</code> + <code>AML.ConsumerPortal.Enable</code>)在後端 2C 租戶上下文中天然滿足,故 <b>V2 BFF 無需持該租戶憑證</b>。<span class="pill partial">改造</span> 詳見 5.6。</p>
|
||||
<p>③ <b>本地種子數據缺口</b>:本地 docker 庫 <code>AMLPortal_Plans</code> / <code>AMLPortal_PlanDetails</code> 均為 <b>0 條</b>,<code>SaasEditions</code> 僅 1 條 <code>Standard</code>,<code>AMLPortal_AgentUserPlans</code> 為 0。⇒ 直接對接會拿到空目錄,<b>必須先補種子數據</b>才能跑通(見第 6 章)。</p>
|
||||
</div>
|
||||
<div class="callout ok">
|
||||
|
|
@ -193,7 +198,10 @@
|
|||
<p><span class="lbl">關鍵差異(與旧站):</span>旧站 Angular 直接從瀏覽器調 <code>/api/amlPortal/*</code>,token 存 localStorage。V2 改為<b>瀏覽器不直接接觸後端</b>,由服務端 BFF 持有憑證、收口後端調用並裁剪響應。因此本文每個端點都拆成「前端契約」與「BFF→後端映射」兩層。</p>
|
||||
</div>
|
||||
<div class="callout warn">
|
||||
<p><span class="lbl">支付本輪除外:</span>當前 <code>plans-plus.js</code> 的 <code>submit()</code> 已臨時改為——<code>POST /subscribe</code> 成功後<b>直接顯示「已提交成功」</b>(<code>showSubmitted()</code>),<b>不再</b>調 <code>/payments/create</code>、不跳收銀台(源碼注釋標明「臨時改動…還原方法」)。<code>submitSingle()</code> 仍保留支付鏈,但單次查詢後端未就緒。⇒ 本輪落地只需打通到 <code>/subscribe</code> 為止;支付見 5.9 暫緩。</p>
|
||||
<p><span class="lbl">支付本輪除外:</span>當前 <code>plans-plus.js</code> 的 <code>submit()</code> 已臨時改為——<code>POST /subscribe</code> 成功後<b>直接顯示「已提交成功」</b>(<code>showSubmitted()</code>),<b>不再</b>調 <code>/payments/create</code>、不跳收銀台(源碼注釋標明「臨時改動…還原方法」)。⇒ 本輪落地只需打通到 <code>/subscribe</code> 為止;支付見 5.9 暫緩。</p>
|
||||
</div>
|
||||
<div class="callout note">
|
||||
<p><span class="lbl">single-query 是 BFF 範式的例外:</span>其餘端點都是「瀏覽器 → V2 <code>/api/*</code> → BFF 代理後端」;但單次查詢的<b>即時檢測觸發(CreateConsumerLink/CreateConsumerOrder)不走 BFF 代理</b>——由 <b>AML 後端在支付完成後內部編排</b>(2C 租戶上下文),V2 只到「下單 + 收款」為止。詳見 5.6。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -212,7 +220,12 @@ GET /plans/catalog→ { success, data:{ standard:[Plan], cpa:[Plan], addons:{
|
|||
jquota:{ packages:[{id, jq, price, name*}] } } } }
|
||||
GET /countries → { success, data:[{code, name, nameTC, nameSC, nameJP, phoneCode}] }
|
||||
GET /single-query/options → { success, data:{ operations:[
|
||||
{id, price, nameCN/EN/JP, descCN/EN/JP}] } } // ← 新
|
||||
{id, nameCN/EN/JP, descCN/EN/JP}] } } // ← 固定 4 項純展示,不可勾選/取消;price 不再逐項計
|
||||
// 固定 4 項:證件核驗(V) / 名單篩查ES(E) / AI 增強型篩查(A) / 信貸記錄篩查失信人(D)
|
||||
// PAYG 收款價 → 取 GetPlanList(countryCode) 的 PAYG(P2G) 方案 price(單一整包價,非分項之和)
|
||||
POST /amlPortal/plan/portal/GetPlanList body = { // ← PAYG 定價來源(沿用旧站請求體 + countryCode)
|
||||
pageIndex:0, pageSize:100, filter:"", getAllItems:false,
|
||||
tag1List:[], tag2List:[], tag3List:[], countryCode:"JPN" } // 目前 countryCode 僅日本一國
|
||||
|
||||
Plan = { planId, tag2Code, nameCN, nameEN, nameJP, periodMonths,
|
||||
price, originalPrice, qCount(-1=無限), userCountLimit,
|
||||
|
|
@ -220,6 +233,9 @@ Plan = { planId, tag2Code, nameCN, nameEN, nameJP, periodMonths,
|
|||
<div class="callout note">
|
||||
<p><span class="lbl">KYC 改為「設備月租」:</span>catalog 的 <code>addons.kyc</code> 不再是 <code>unitPrice</code>,而是 <code>monthlyPrice</code>。前端 <code>kycUnit()</code> = <code>monthlyPrice × 所選方案 periodMonths</code>(隨方案期數自動變動,一次付清、無套餐優惠價,見 <code>plans-plus.js</code> <code>kycPriceForMonths()</code>)。jQuota 亦由「按量」改為「選配套」(<code>jquotaPackageId</code>)。</p>
|
||||
</div>
|
||||
<div class="callout note">
|
||||
<p><span class="lbl">單次查詢(PAYG)目錄改為「固定展示 + 外部定價」:</span><code>/single-query/options</code> 的 4 項<b>不再是可勾選的計費項</b>,而是<b>固定信息展示</b>(<code>證件核驗 / 名單篩查(ES) / AI 增強型篩查 / 信貸記錄篩查(失信人)</code>,用戶不可選/取消;名稱後續可能調整)——可由前端硬編碼、或後端返回固定 4 項,<b>不含 <code>price</code></b>。PAYG 的<b>收款金額</b>單獨取自 <code>GetPlanList</code> 按 <code>countryCode</code> 返回的 PAYG(P2G) 方案價:<code>/plans-jp</code>(日本別名頁)傳 <code>countryCode:"JPN"</code>——<b>目前 <code>countryCode</code> 僅日本一國</b>(後端 <code>GetPlanListParam.CountryCode</code> 支持「該國專屬 + 全球通用(CountryCode=null)」過濾,將來擴國時沿用同一機制)。該價為<b>單一整包價</b>。⇒ 前端需依 URL/地域推導 <code>countryCode</code> 並在載入期取價(見 5.6)。</p>
|
||||
</div>
|
||||
|
||||
<h3>3.2 交互期(按需)</h3>
|
||||
<pre><code>GET /tenants/lookup?email= // ← 只按郵箱;郵箱全局唯一 → 至多命中 1 個租戶
|
||||
|
|
@ -272,7 +288,7 @@ POST /single-query body = {
|
|||
<!-- ───────────── 4 ───────────── -->
|
||||
<section id="s4">
|
||||
<h2 class="sec">4. 後端真實端點清單(AMLPortal)</h2>
|
||||
<p>以下端點均在 <code>AML_Backend/modules/iCON.Abp.AMLPortal</code>,並已被旧站 <code>justsolutionsWeb/PlanService</code> 使用驗證過。前綴 <code>/api/amlPortal/*</code>(agent 校驗端點在 Identity 模塊 <code>/api/identity/*</code>)。</p>
|
||||
<p>1–11 均在 <code>AML_Backend/modules/iCON.Abp.AMLPortal</code>,並已被旧站 <code>justsolutionsWeb/PlanService</code> 使用驗證過。前綴 <code>/api/amlPortal/*</code>(agent 校驗端點在 Identity 模塊 <code>/api/identity/*</code>)。<b>12–13 為單次查詢用到的方法,位於 AML 主模塊 <code>iCON.Abp.AML</code>(<code>/api/aml/ConsumerPortal/*</code>);但<u>由 AML 後端在支付完成後內部編排調用</u>,V2 BFF 不代理、瀏覽器不直調</b>(見 5.6)。</p>
|
||||
<table>
|
||||
<thead><tr><th>#</th><th>方法 / 路由</th><th>用途</th><th>位置</th></tr></thead>
|
||||
<tbody>
|
||||
|
|
@ -287,10 +303,11 @@ POST /single-query body = {
|
|||
<tr><td>9</td><td><span class="method post">POST</span> <code>/api/amlPortal/Order/portal/TenantRenewal</code></td><td>租戶續費下單</td><td>OrderController:321 · OrderService:2197</td></tr>
|
||||
<tr><td>10</td><td><span class="method post">POST</span> <code>/api/amlPortal/Order/portal/PaymentWebhook</code></td><td>支付回調(支付方服務端調,非前端)</td><td>OrderController:163</td></tr>
|
||||
<tr><td>11</td><td><span class="method post">POST</span> <code>/api/amlPortal/customer/CreateFeedback</code></td><td>聯絡我們/推薦人:接收訂單摘要(<b>不下單</b>);<b>擬</b>加 <code>AssignedAgentUserId</code> 收件人路由(見 5.8)</td><td>CustomerController · <code>routes/contact.js</code> 已封裝</td></tr>
|
||||
<tr><td>—</td><td><span class="method new">缺</span> 單次查詢(options + 下單)</td><td>後端<b>無對應端點</b>(見 5.6)</td><td>—</td></tr>
|
||||
<tr><td>12</td><td><span class="method post">POST</span> <code>/api/aml/ConsumerPortal/CreateConsumerLink</code><br><span class="small">(後端內部編排調用)</span></td><td>單次查詢第 1 步:建即時檢測鏈接(回 <code>accessToken</code>)<b>·非匿名</b>(RealtimeScreeningManagement + <code>AML.ConsumerPortal.Enable</code>)——在後端 2C 租戶上下文內滿足</td><td>ConsumerPortalController:340 · ConsumerPortalService:964</td></tr>
|
||||
<tr><td>13</td><td><span class="method post">POST</span> <code>/api/aml/ConsumerPortal/CreateConsumerOrder</code><br><span class="small">(後端內部編排調用)</span></td><td>單次查詢第 2 步:憑 <code>accessToken</code> 建訂單並即時檢測(異步跑模塊 + 結果郵件)<b>·匿名</b></td><td>ConsumerPortalController:78 · ConsumerPortalService:325</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="small">DTO 位置:<code>Application.Contracts/PlanAppLayer/*</code>(GetPlanListParam、PlanDto、PlanDetailDto)、<code>Application.Contracts/OrderAppLayer/*</code>(CreateOrderParam、SelectPlanItem、TenantRenewalParam、QueryRenewableTenantParam、TenantPropertyDto)。agent 返回 DTO:<code>iCON.Abp.FX.Users/AppUserDto</code>。</p>
|
||||
<p class="small">DTO 位置:<code>Application.Contracts/PlanAppLayer/*</code>(GetPlanListParam、PlanDto、PlanDetailDto)、<code>Application.Contracts/OrderAppLayer/*</code>(CreateOrderParam、SelectPlanItem、TenantRenewalParam、QueryRenewableTenantParam、TenantPropertyDto)。agent 返回 DTO:<code>iCON.Abp.FX.Users/AppUserDto</code>。單次查詢 DTO:<code>iCON.Abp.AML/Application.Contracts/ConsumerPortalAppLayer/*</code>(CreateConsumerLinkParam、CreateConsumerCustomerDto、CreateConsumerLinkDto、CreateConsumerOrderParam、ConsumerLinkDto)+ <code>IndividualAppLayer/CreateIndividualDto</code>、<code>OrgLayer/CreateOrganizationDto</code>。</p>
|
||||
</section>
|
||||
|
||||
<!-- ───────────── 5 ───────────── -->
|
||||
|
|
@ -398,16 +415,23 @@ POST /single-query body = {
|
|||
</div>
|
||||
|
||||
<!-- 5.5 -->
|
||||
<h3 id="s5-5">5.5 GET /tenants/lookup(按郵箱查租戶) <span class="pill todo">缺口大</span></h3>
|
||||
<div class="endpoint-head"><span class="method get">GET</span> /api/tenants/lookup?email= → <span class="method post">POST</span> /api/amlPortal/Order/portal/queryRenewableTenant</div>
|
||||
<h3 id="s5-5">5.5 GET /tenants/lookup(按郵箱查租戶)<span class="pill todo">需後端新增端點</span></h3>
|
||||
<div class="endpoint-head"><span class="method get">GET</span> /api/tenants/lookup?email= → <span class="method new">NEW</span> /api/amlPortal/Order/portal/<b>queryRenewableTenantByEmail</b></div>
|
||||
<p>前端按<b>郵箱</b>查租戶,返回 <code>match:'none'|'unique'</code> 兩態(<b>不再</b>多租戶消歧——註釋明確「租戶名與郵箱均全局唯一 → 一郵箱至多 1 個租戶」)。命中後帶出 <code>currentSubscription</code> + <code>referrer</code> 預填續費/加購表單。</p>
|
||||
<p>但後端 <code>queryRenewableTenant</code> 只接受 <code>{keyword}</code> 且按 <b><code>TenantName.Contains(keyword)</code></b> 搜索(<code>OrderService.cs:2362</code>),返回 <code>List<TenantPropertyDto></code>——<code>TenantAdminEmail</code> 是查完後再逐條用 admin 用戶郵箱回填(<code>:2375-2379</code>),<b>不能在 DB 層按郵箱過濾</b>。</p>
|
||||
<h4>三個不匹配點</h4>
|
||||
<ol class="tight" style="padding-left:20px">
|
||||
<li><b>查詢維度不符</b>:前端給郵箱、後端按名字搜。短期 BFF 變通:以空/寬 <code>keyword</code> 拉全部可續費租戶,在服務端按 <code>TenantAdminEmail===email</code> 過濾(⚠️ 全量拉取、性能與越權風險,僅臨時)。正解:<b>後端新增「按管理員郵箱查可續費租戶」端點</b>。</li>
|
||||
<li><b>currentSubscription 需重建</b>:前端的 <code>currentSubscription</code>(planId、name、periodMonths、price、qCount、userCountLimit、startDate、expiryDate、usedQuota、addons)並非 <code>TenantPropertyDto</code> 直接字段,需由 <code>TenantProperty + 其 Order/OrderDetail</code> 推導。可參考 <code>GetCurrServiceInfo</code>(<code>OrderService.cs:398</code>,<code>CurrServiceInfoDto</code>)。</li>
|
||||
<li><b><code>referrer</code> 新增</b>:前端續費頁展示「註冊時填寫的推薦人」。<code>TenantPropertyDto</code> 已有 <code>AgentorCode</code>/<code>AgentorName</code> ⇒ 映射為 <code>referrer:{code:AgentorCode, name:AgentorName}</code>。</li>
|
||||
</ol>
|
||||
<p>但現有後端 <code>queryRenewableTenant</code> 只接受 <code>{keyword}</code> 且按 <b><code>TenantName.Contains(keyword)</code></b> 在 DB 層搜索(<code>OrderService.cs:2362-2364</code>),返回 <code>List<TenantPropertyDto></code>——<code>TenantAdminEmail</code> 是查完命中租戶後、再 <code>GetAllUsers()</code> 遍歷<b>全庫用戶</b>按 <code>UserName=='admin' && TenantId</code> 逐條回填(<code>:2369-2379</code>),<b>邏輯上「先有租戶、後有郵箱」,無法在 DB 層按郵箱過濾</b>。⇒ 前端「給郵箱、要唯一命中」的查詢維度與現端點<b>根本錯位</b>。</p>
|
||||
<div class="callout gap">
|
||||
<p><span class="lbl">已定決策:後端新增「按管理員郵箱查可續費租戶」端點。</span>不採用 BFF 變通(以空 <code>keyword</code> 拉全部可續費租戶、再服務端按 <code>TenantAdminEmail===email</code> 過濾)——現端點按<b>租戶名</b>搜、返回<b>整張可續費租戶列表</b>,讓 BFF 承接全表列表並自拼 <code>currentSubscription</code>/<code>referrer</code> 的訂單反查,既非其職責、也易錯;<b>查詢維度與聚合歸屬都更該落在後端</b>。<b>(注:問題不在掃描成本——後端側按郵箱全庫掃描本身可接受,租戶數少、低頻交互;而在查詢維度錯位與聚合歸屬。)</b>新端點把查詢維度反過來(<b>先按郵箱定位 admin 用戶 → 再取其租戶</b>),順帶一次性聚合 plans-plus 續費/加購頁所需的 <code>currentSubscription</code> + <code>referrer</code>,避免 BFF 拼裝易錯的訂單反查。</p>
|
||||
</div>
|
||||
<h4>新增端點設計(供後端實作參照)</h4>
|
||||
<div class="endpoint-head"><span class="method new">NEW</span> <span class="method post">POST</span> /api/amlPortal/Order/portal/queryRenewableTenantByEmail <span class="tag">[AbpAutoAuth("Portal")]</span></div>
|
||||
<ul class="tight">
|
||||
<li><b>入參</b> <code>QueryRenewableTenantByEmailParam { string Email }</code>(新 DTO,置於 <code>Application.Contracts/OrderAppLayer/</code>,與 <code>QueryRenewableTenantParam</code> 並列)。鑑權沿用現 <code>queryRenewableTenant</code> 的 <code>[AbpAutoAuth("Portal")]</code>,BFF 免自帶 token(見第 6 章)。</li>
|
||||
<li><b>反查邏輯(維度反轉,且省掉全庫掃描)</b>:<br>
|
||||
① 禁用多租戶過濾 <code>_dataFilter.Disable<IMultiTenant>()</code>,按郵箱定位租戶管理員用戶——沿用現端點「admin 郵箱 = <code>UserName=='admin'</code> 用戶的 Email」語義:可直接<b>復用現 <code>GetAllUsers()</code> 全庫掃描</b>(與現 <code>queryRenewableTenant</code> 一致),改在內存按 <code>u.UserName=='admin' && u.Email==Email</code> 過濾即可(郵箱全局唯一 → 至多 1 條)。<b>全庫掃描在此可接受</b>——租戶 / 管理員用戶數量少、續費查詢為低頻交互,<b>無需為此下推 DB 過濾或加索引</b>。無命中 → 回空 ⇒ 前端 <code>match:'none'</code>。<br>
|
||||
② 取該用戶 <code>TenantId</code> 對應的 <b>active</b> <code>TenantProperty</code>(<code>IsActive && TargetTenantID==tenantId</code>,按 <code>CreationTime</code> 取最新一條),並校驗其可續費(<code>TenantStatus</code> / <code>EffectiveEndTime</code>——過期仍返回,由前端做「過期/臨期」展示,見 5.7 topup 阻斷)。<br>
|
||||
③ 組裝聚合返回:<code>referrer</code> 直取 <code>AgentorCode/AgentorName</code>;<code>currentSubscription</code> 明細(planId / 名稱 / periodMonths / price / addons)沿用 <code>GetCurrServiceInfo</code>(<code>OrderService.cs:398-418</code>)的 <b>Order → OrderDetails → Plan/PlanDetail</b> 反查思路,區別是它以 <code>CurrentTenant.Id</code> 為基、此處以顯式 <code>TargetTenantID</code> 為基(Portal 上下文 + 禁多租戶過濾)。</li>
|
||||
<li><b>返回</b>:建議新增聚合 DTO(<code>RenewableTenantDto</code> = <code>TenantPropertyDto</code> 關鍵字段 + <code>referrer</code> + 明細化 <code>currentSubscription</code>),讓 BFF 幾乎<b>直通轉發</b>,不必自己再拼訂單明細。</li>
|
||||
</ul>
|
||||
<h4>TenantPropertyDto → 前端 Tenant 映射(可得部分)</h4>
|
||||
<table>
|
||||
<thead><tr><th>前端</th><th>TenantPropertyDto</th><th>備註</th></tr></thead>
|
||||
|
|
@ -425,20 +449,112 @@ POST /single-query body = {
|
|||
<tr><td><code>currentSubscription.addons.users</code>/<code>jquotaPackageId</code></td><td>經訂單明細反查</td><td><b>推導</b></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="callout gap">
|
||||
<p><span class="lbl">建議後端改動:</span>新增 <code>queryRenewableTenantByEmail(email)</code>,直接返回 plans-plus 所需的「租戶 + currentSubscription(含 planId/上期價/已購加值項)+ referrer」聚合結構,避免在 BFF 拼裝易錯的訂單反查。</p>
|
||||
<div class="callout note">
|
||||
<p><span class="lbl">聚合由誰拼裝:</span>上表「可得部分」是 <code>TenantPropertyDto</code> 直出字段;而 <code>currentSubscription</code> 的 <b>planId / 名稱 / 上期 price / periodMonths</b> 與 <b>addons.users / jquotaPackageId</b> 均需經 <code>Order → OrderDetails</code>(按 <code>Tag1Code</code>:B=基礎、AdlU=增購用戶、jQ/j=jQuota、KYC)反查、<code>usedQuota = QCountPurchase − QCountLeft</code>。按本次決策,<b>這段反查應落在新端點 <code>queryRenewableTenantByEmail</code> 內(服務端一次算好)</b>,而非 BFF——後端已有 <code>GetCurrServiceInfo</code>(<code>OrderService.cs:398</code>)反查 <code>ActiveBPlans</code> 的現成範式可借用,只需擴出加值項明細與上期價。BFF <code>routes/tenants.js</code> 因此退化為<b>字段直通 + 命中態(none/unique)包裝</b>。</p>
|
||||
</div>
|
||||
|
||||
<!-- 5.6 -->
|
||||
<h3 id="s5-6">5.6 單次查詢 single-query(按次付費) <span class="pill newreq">新增需求 · 後端零支持</span></h3>
|
||||
<div class="endpoint-head"><span class="method get">GET</span> /api/single-query/options · <span class="method post">POST</span> /api/single-query → <span class="method new">無對應後端</span></div>
|
||||
<p>plans-plus 新增第 4 種流程 <code>type='single'</code>:無需訂閱、填「結果接收郵箱 + 查詢主體 + 勾選查詢項目」→ 付款 → 結果郵件發送。前端契約見 3.1/3.3。全庫 grep <b>無任何按次查詢 / pay-per-use 端點或實體</b>。</p>
|
||||
<h3 id="s5-6">5.6 單次查詢 single-query(隨付即用 · Pay-As-You-Go) <span class="pill partial">後端已支持 · 後端內部編排</span></h3>
|
||||
<div class="endpoint-head"><span class="method get">GET</span> /api/single-query/options(固定展示) · <span class="method post">POST</span> /amlPortal/plan/portal/GetPlanList <span class="small">{countryCode}(PAYG 定價)</span> · <span class="method post">POST</span> /api/single-query <span class="small">(收單 + 支付)</span> ⟶ 支付完成 ⟶ <span class="tag">AML 後端內部</span> CreateConsumerLink → CreateConsumerOrder</div>
|
||||
|
||||
<div class="callout newreq">
|
||||
<p><span class="lbl">本輪定案(PAYG 定價 + 固定檢測項):</span>隨付即用的<b>檢測內容固定為 4 項、純信息展示,用戶不可勾選/取消</b>——<code>證件核驗</code>、<code>名單篩查(即 ES 檢測)</code>、<code>AI 增強型篩查</code>、<code>信貸記錄篩查(即失信人檢測)</code>(叫法後續可能再調整),確定性映射後端功能碼 <code>V / E / A / D</code>(無 <code>S</code> 風險評估問卷、<code>R</code> CDD 報告、<code>O</code> OCR)。<b>收款金額不再由 V2 分項單價相加,而是取自 AML <code>GetPlanList</code> 按 <code>countryCode</code> 返回的 PAYG(P2G) 方案價</b>:<code>/plans-jp</code> 頁傳 <code>countryCode:"JPN"</code>(<b>目前 <code>countryCode</code> 僅日本一國</b>,前端依 URL/地域推導),該價為<b>單一整包價</b>(非分項之和)。⇒ 原「op→功能碼非 1:1、公司查冊無獨立碼、V2 分項單價與後端 jQ 對齊」三處缺口<b>收斂</b>:檢測項固定 4 = E/A/V/D,定價口徑統一到 <code>GetPlanList</code>。</p>
|
||||
</div>
|
||||
|
||||
<div class="callout ok">
|
||||
<p><span class="lbl">本輪更正(原「後端零支持」結論作廢;並釐清調用方):</span>單次查詢由 <b>AML 主模塊的 ConsumerPortal「即時檢測」</b>承接(<code>iCON.Abp.AML</code>)。<b>調用方已確認:這兩步不是 V2 BFF、也不是瀏覽器發起,而是 AML 後端在「訂單支付完成後」、於 2C 租戶上下文<u>內部編排</u>執行</b>——<code>CreateConsumerLink</code>(建鏈接拿 <code>accessToken</code>)→ <code>CreateConsumerOrder</code>(建訂單、即時檢測、結果郵件)。<b>justsolutionsWebV2 的職責止於「下單 + 收款」,完全不觸碰這兩個接口。</b></p>
|
||||
</div>
|
||||
|
||||
<div class="callout note">
|
||||
<p><span class="lbl">為何不能放在 V2 BFF:</span><code>CreateConsumerLink</code> <b>非匿名</b>(<code>[Authorize(RealtimeScreeningManagement)]</code> + <code>[RequiresFeature(AML.ConsumerPortal.Enable)]</code>,<code>ConsumerPortalController.cs:337-340</code>),且在 <code>CurrentTenant</code> 下建鏈接。V2 BFF 只持「門戶訪客」憑證(<code>__tenant=Portal</code>),既無實時篩查權限、也非 2C 租戶上下文 ⇒ 調不動。放到後端內部後,2C 租戶上下文與功能開關天然滿足,<b>鑑權不再是 BFF 的問題</b>。<code>CreateConsumerOrder</code> 雖是 <code>[AllowAnonymous]</code>(<code>:76-78</code>,憑 <code>accessToken</code>),但既然第 1 步已在後端,第 2 步同處後端內部串接最自然。</p>
|
||||
</div>
|
||||
|
||||
<h4>職責邊界與調用鏈</h4>
|
||||
<div class="flow">[V2 · 瀏覽器 + BFF]
|
||||
GET /api/single-query/options ……… 查詢項目目錄(V2 自定義,見 3.1)
|
||||
POST /api/single-query ………………… 建「待支付」單次查詢單(存 郵箱/主體/所選項目/金額)→ orderId
|
||||
POST /api/payments/create …………… 收款(見 5.9;本輪支付除外)
|
||||
──────────────── 支付完成(webhook / 回調)────────────────
|
||||
[AML 後端 · 2C 租戶上下文內部編排](非 V2、非瀏覽器發起)
|
||||
固定 4 項檢測 → functionCodes = E/A/V/D(證件核驗V·名單篩查ES=E·AI增強A·失信D;無 S/R/O)
|
||||
① CreateConsumerLink { 郵箱, functionCodes:"EAVD", validHours } → accessToken
|
||||
② CreateConsumerOrder { accessToken, objectType, individual|organization, functionCodes:["E","A","V","D"] }
|
||||
→ 建訂單(直接 Paid) + 每碼一條 DetectionTask → MQ 異步跑檢測 → 完成發結果郵件</div>
|
||||
|
||||
<div class="callout warn">
|
||||
<p><span class="lbl">待後端明確的銜接點(由後端定義,非 V2 落地):</span>「支付完成」如何把<b>單次查詢單的資料(郵箱/主體/所選項目)+ 支付結果</b>交給後端這段內部編排,常見兩種接法:<br>
|
||||
① <b>後端持單</b>:V2 在 <code>POST /single-query</code> 時即把單推給後端一個「2C 收單/意向」端點落庫;支付 webhook 直接打到<b>後端</b>,後端據單觸發 ①②。<br>
|
||||
② <b>V2 持單、支付後通知後端</b>:V2 收到支付成功後,調後端一個<b>專門的 2C 觸發端點</b>(其鑑權/形態由後端定義),該端點內部再跑 ①②。<br>
|
||||
無論哪種,<b>①② 本身都是後端內部行為</b>,V2 始終不直接調 <code>CreateConsumerLink</code>/<code>CreateConsumerOrder</code>。</p>
|
||||
</div>
|
||||
|
||||
<h4>後端內部兩步(payload 供參考)</h4>
|
||||
<p><b>第 1 步 · 建即時檢測鏈接</b> <code>CreateConsumerLink</code>(<code>ConsumerPortalService.cs:964</code>):以「結果接收郵箱」upsert <code>ConsumerCustomer</code>,在當前 2C 租戶下建鏈接 → 回 <code>ConsumerLinkDto</code>(含 <code>accessToken</code>)。</p>
|
||||
<pre><code>{ "createConsumerCustomerDto": { "email":"result@company.com", "name":null, "phone":null, "remark":null },
|
||||
"createConsumerLinkDto": { "functionCodes":"EAVD", "validHours":72,
|
||||
"validEndTime":"2026-07-09T06:00:47.000Z" } }
|
||||
// 回:OperationDto.Success(ConsumerLinkDto{ accessToken, functionCodes, expireTime, tenantId, … })</code></pre>
|
||||
|
||||
<p><b>第 2 步 · 建訂單並即時檢測</b> <code>CreateConsumerOrder</code>(<code>:325</code>):據 token 定位鏈接與租戶 → 計價(jQ) → 建 Individual/Organization 實體 → 建 <code>ConsumerPortalOrder</code>(<b>直接置 PaymentStatus=Paid、OrderStatus=Pendding</b>,<code>:388</code>)→ 每個 functionCode 建一條 <code>DetectionTask</code> → 發 RabbitMQ 消息,由 <code>ProcessConsumerPortalOrder</code> <b>異步</b>跑檢測模塊,完成後 <code>SendConsumerLinkResultMail</code> 發結果郵件。</p>
|
||||
<pre><code>{ "accessToken":"xxx", "objectType":"Individual",
|
||||
"individual": { "fullNameEN":"张三", "fullNameZH":"", "dateOfBirth":{"year":0,"month":0,"day":0},
|
||||
"address":"", "addressOfLiving":"", "nationalityCode":null,
|
||||
"documentRequiredFileIds":[], "identityDocumentTypeCode":"", "identityDocumentNumber":"",
|
||||
"gender":"", "phone":"", "email":"", "occupation":"", "entityIdentityDocuments":[] },
|
||||
"organization": null,
|
||||
"functionCodes": ["E","A","V","D"] } // ← 固定 4 項(PAYG 檢測內容不可選)
|
||||
// 回:OperationDto.Success({ entity, order }) // order.Id / order.OrderCode 可作結果查詢鍵</code></pre>
|
||||
|
||||
<h4>V2 收集字段 ↔ 後端入參映射</h4>
|
||||
<p class="small">V2 通過 <code>POST /single-query</code>(或銜接端點)把下列字段交給後端;後端在內部編排時據此組裝上面兩個 payload。</p>
|
||||
<table>
|
||||
<thead><tr><th>V2 收集字段</th><th>後端入參</th><th>說明</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>email</code>(結果接收郵箱)</td><td><code>createConsumerCustomerDto.email</code></td><td>必填;後端按 email upsert <code>ConsumerCustomer</code>,並作結果郵件收件人</td></tr>
|
||||
<tr><td><code>subjectType</code> <code>'individual'|'company'</code></td><td><code>objectType</code> <code>'Individual'|'Organization'</code></td><td>枚舉 <code>ObjectTypeEnum</code>(Individual=0 / Organization=1);company→Organization</td></tr>
|
||||
<tr><td><code>subject</code>(單一名稱字段)</td><td><code>individual.fullNameEN</code> 或 <code>organization.fullNameEN</code></td><td>⚠️ V2 只收一個名稱;後端 <code>CreateIndividualDto/CreateOrganizationDto</code> 字段眾多但均可空 → <b>最小可用只填全名</b>,其餘留空/默認</td></tr>
|
||||
<tr><td>固定 4 項檢測(V2 不收選擇)</td><td>兩處 <code>functionCodes</code>(鏈接=字符串 <code>"EAVD"</code>、訂單=數組 <code>["E","A","V","D"]</code>)</td><td>✅ <b>固定映射</b>:證件核驗→V、名單篩查ES→E、AI 增強→A、失信→D;兩步一致</td></tr>
|
||||
<tr><td>(鏈接有效期)</td><td><code>createConsumerLinkDto.validHours</code></td><td>後端據此算 <code>ExpireTime</code>;<b>樣例中的 <code>validEndTime</code> 後端 DTO 無此字段、被忽略</b></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="callout ok">
|
||||
<p><span class="lbl">功能碼映射(本輪固定,非 1:1 問題消除):</span>PAYG 檢測內容<b>固定 4 項</b>、用戶不可選,直接確定性對應後端功能碼(後端只認 <b>E/A/V/D/S/R</b>,O=OCR 排除;PAYG 僅用前 4 個):</p>
|
||||
<ul class="tight">
|
||||
<li><code>證件核驗</code> → <code>V</code>(證件核驗)</li>
|
||||
<li><code>名單篩查(即 ES 檢測)</code> → <code>E</code>(ES 實體篩查)</li>
|
||||
<li><code>AI 增強型篩查</code> → <code>A</code>(AI 檢測) <span class="small">(E、A 在後端合併為一次 <code>EntitiesInvestigation</code> 計費,<code>ConsumerPortalService.cs:277</code>)</span></li>
|
||||
<li><code>信貸記錄篩查(即失信人檢測)</code> → <code>D</code>(失信查詢)</li>
|
||||
</ul>
|
||||
<p>⇒ 原「多個 op 坍縮為同一 E/A 計費」「公司查冊無獨立碼」的映射難點<b>不復存在</b>(新方案無「公司查冊」項,S/R 不在 PAYG)。<b>定價口徑亦統一</b>:V2 收款金額 = <code>GetPlanList</code> 按 <code>countryCode</code> 的 PAYG(P2G) 方案價,不再與後端逐碼 jQ 扣費逐項對賬——後端 <code>CreateConsumerOrder</code> 仍按 2C 租戶 jQ 計費/扣減(僅 CHN/HKG 計 KYC jQ,<code>:297</code>),但那是後端內部帳,與 V2 對外報價解耦。</p>
|
||||
</div>
|
||||
|
||||
<h4>鑑權(均在後端內部滿足)</h4>
|
||||
<table>
|
||||
<thead><tr><th>方法</th><th>鑑權</th><th>後端內部編排下的落地</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>CreateConsumerLink</code></td><td><b>非匿名</b>:<code>[Authorize(RealtimeScreeningManagement)]</code> + <code>[RequiresFeature(AML.ConsumerPortal.Enable)]</code>(<code>ConsumerPortalController.cs:337-340</code>)</td><td>在後端 2C 租戶上下文內執行 ⇒ <b>租戶上下文與功能開關天然滿足,無需 V2 憑證</b>。待確認的只是「用哪個 2C 租戶」及其 ConsumerPortal 功能/權限已開</td></tr>
|
||||
<tr><td><code>CreateConsumerOrder</code></td><td><b>匿名</b> <code>[AllowAnonymous]</code>(<code>:76-78</code>)</td><td>憑 <code>accessToken</code> 定位租戶;後端內部緊接第 1 步串行調用</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4>結果回取(可選)</h4>
|
||||
<ul class="tight">
|
||||
<li><code>GET /single-query/options</code>:返回查詢項目目錄 <code>operations:[{id, price, name*, desc*}]</code>(制裁篩查、PEP、負面新聞、公司查冊、身份核實…)。</li>
|
||||
<li><code>POST /single-query</code>:建按次查詢訂單,返回 <code>orderId</code>,本擬走與訂閱相同的支付鏈(本輪支付除外)。</li>
|
||||
<li>檢測<b>異步</b>完成後後端自動發結果郵件(滿足 V2「結果將發送至此郵箱」文案),V2 可不輪詢。</li>
|
||||
<li>若 V2 要頁內展示:<code>POST /api/aml/ConsumerPortal/Get2CRecordDetail/{accessToken}</code> 或 <code>/Get2CRecordDetailByOrderId/{orderId}</code>(均匿名)取檢測記錄;<code>/CheckAndGenerateReport</code> 生成 CDD 報告 PDF。<span class="small">(此類<b>只讀</b>回取因匿名,才可能由 V2/瀏覽器直取。)</span></li>
|
||||
</ul>
|
||||
|
||||
<div class="callout gap">
|
||||
<p><span class="lbl">落地選項:</span>① <b>短期保持 mock</b>(<code>PLANS_PLUS_MOCK=true</code> 或單獨掛 <code>mock/plans-plus.js</code> 的這兩條),其餘端點走真實後端;② 後端新增「按次查詢」下單 + 計費 + 結果投遞(涉及檢測引擎 iCS,工作量大)。<b>本輪建議選 ①</b>:single-query 端點繼續 mock,優先打通訂閱三態(new/renew/topup)的真實對接。</p>
|
||||
<p><span class="lbl">仍存在的缺口 / 落地要點:</span></p>
|
||||
<p>① <b>銜接點</b>:V2「支付完成」與後端內部編排的對接(上方 ①/② 兩種接法)由後端定義端點/webhook 承接。</p>
|
||||
<p>② <b>op→功能碼映射</b><span class="pill done">已收斂</span>:檢測內容固定 4 項、確定性映射 <code>E/A/V/D</code>(無「公司查冊」、無 S/R);V2 只需固定傳 <code>functionCodes="EAVD"</code>(或後端直接寫死該 4 項)。</p>
|
||||
<p>③ <b>2C 租戶</b>:確認/建立隨付即用專用租戶、開 <code>AML.ConsumerPortal.Enable</code> 功能、配實時篩查權限——供後端內部編排落上下文(<b>不再需要在 V2 BFF 配該租戶憑證</b>)。</p>
|
||||
<p>④ <b>對外定價</b><span class="pill done">已確認</span>:V2 收款金額 = <code>GetPlanList</code> 按 <code>countryCode</code>(<code>/plans-jp→JPN</code>)返回的 PAYG(P2G) 方案價,<b>為單一整包價</b>(非分項之和),前端載入期取價、純展示 4 項檢測。後端 <code>CreateConsumerOrder</code> 仍把訂單直接置 <code>Paid</code> 並按 2C 租戶 jQ 扣費(<code>GetPriceTwoC</code>/線上支付分支已注釋 <code>:196</code>)——屬後端內部帳,與 V2 對外報價解耦。<b>現階段 <code>countryCode</code> 僅日本(JPN)</b>:即只有 <code>/plans-jp</code> 有 PAYG 定價,其餘國家待種 P2G 方案 + PlanDetail 後再開(機制沿用,不需改前端契約)。</p>
|
||||
<p>⑤ <b>本地環境</b>:需該租戶 + 檢測引擎(iCS)/RabbitMQ 就緒,<code>ProcessConsumerPortalOrder</code> 才能真正跑出結果;否則本地 single-query 可繼續走 mock,僅在對接環境驗證真調用。</p>
|
||||
</div>
|
||||
|
||||
<div class="callout note">
|
||||
<p><span class="lbl">V2 側建議形態:</span><code>/single-query/options</code> 收斂為<b>固定 4 項純展示</b>(可前端硬編碼或後端返回固定項,<b>不含 price</b>);PAYG 收款價由前端載入期<b>依 URL/地域推導 <code>countryCode</code> 調 <code>GetPlanList</code></b>取 PAYG(P2G) 方案價(或經 BFF <code>/plans/catalog</code> 帶 countryCode 一併取回)。<code>/single-query</code>(收單,返回 orderId)契約不變,<b>不新增對 ConsumerPortal 的代理路由</b>。V2 只需把「支付完成」與後端銜接(見上 ①/②),<b>兩步真調用全部在後端內部</b>——前端與 BFF 無需感知 CreateConsumerLink/CreateConsumerOrder,也<b>不傳檢測項選擇</b>(固定 4 項在後端寫死或由 V2 固定傳 <code>EAVD</code>)。</p>
|
||||
</div>
|
||||
|
||||
<!-- 5.7 -->
|
||||
|
|
@ -455,7 +571,7 @@ POST /single-query body = {
|
|||
<tr><td><code>TenantAdminEmail</code></td><td><code>email</code></td><td>必填;後端 <code>CheckEmailExists</code> 校驗未註冊(<code>:159</code>)</td></tr>
|
||||
<tr><td><code>Jurisdiction</code></td><td><code>jurisdiction</code></td><td>必填(<code>:153</code>)</td></tr>
|
||||
<tr><td><code>OrganizationReference</code></td><td><code>edition</code>(Guid)</td><td>必填;校驗 edition 存在(<code>:200</code>)</td></tr>
|
||||
<tr><td><code>OrganizationBR</code> / <code>OrganizationCI</code></td><td><code>br</code> / —(前端不收 CI)</td><td><span class="pill todo">阻塞</span> <b>後端仍要求 BR 或 CI 至少一個</b>(見下)</td></tr>
|
||||
<tr><td><code>OrganizationBR</code> / <code>OrganizationCI</code></td><td><code>br</code> / —(前端不收 CI)</td><td><span class="pill done">非必填</span> <b>BR/CI 已定非必填</b>,空值可下單(見下)</td></tr>
|
||||
<tr><td><code>EffectiveStartTime</code></td><td><code>startDate</code></td><td></td></tr>
|
||||
<tr><td><code>ContactPerson</code></td><td><code>contact</code></td><td>個人主體 = <code>company</code></td></tr>
|
||||
<tr><td><code>CompanyAddress</code></td><td><code>address</code></td><td>可選(個人主體為空)</td></tr>
|
||||
|
|
@ -465,11 +581,9 @@ POST /single-query body = {
|
|||
<tr><td><code>IsOfflinePayment</code>/<code>IsAgentBehalf</code></td><td>—</td><td>在線下單固定 <code>false</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="callout gap">
|
||||
<p><span class="lbl">BR/CI 阻塞(本次更正):</span><code>CreateOrder</code> 兩處校驗 <b>當前源碼仍生效</b>:<code>OrderService.cs:154</code> 與 <code>:190</code>——<code>if (BR 空 && CI 空) return "OrganizationBR or OrganizationCI is required"</code>。緊隨的唯一性校驗 <code>ExistsByOrganizationBRCI</code>(<code>:191</code>)兩者皆空時返回 false,但<b>走不到那步就先被必填校驗擋下</b>。⇒ plans-plus:<br>
|
||||
· <b>corp 主體</b>:BR 標「可選」,但空 BR 會下單失敗;<br>
|
||||
· <b>individual 主體</b>:<code>br</code> 恒為空、無 CI ⇒ <b>必然失敗</b>。<br>
|
||||
<b>三種對策(擇一):</b>(a) 後端真正去除該必填校驗(若業務允許空 BR/CI);(b) 前端把 corp 的 BR 改為必填、且暫不放行 individual 新購;(c) BFF 為空 BR/CI 注入占位值(不推薦,污染合規欄)。<b>需業務決策。</b></p>
|
||||
<div class="callout ok">
|
||||
<p><span class="lbl">BR/CI 非必填(已定決策):</span>plans-plus 新購允許空 BR/CI——含 <b>corp 主體</b>(BR 維持「可選」、可空)與 <b>individual 主體</b>(<code>br</code> 恒空、無 CI)。實現=注释 <code>CreateOrder</code> 的 <code>OrderService.cs:154</code> 與 <code>:190</code> 兩處必填校驗(<code>if (BR 空 && CI 空) return "…required"</code>);緊隨的唯一性校驗 <code>ExistsByOrganizationBRCI</code> 對「BR、CI 皆空」返回 <code>false</code>(<code>:1124</code>),<b>空值安全通過,無需其它改動</b>。</p>
|
||||
<p>⚠️ <b>核對現狀</b>:當前 working copy 的 <code>:154</code>/<code>:190</code> 仍為 <b>active</b>;若對接/部署環境尚未放開,空 BR/CI 會被攔,需先應用該改動。</p>
|
||||
</div>
|
||||
|
||||
<h4>② type='renew' → TenantRenewal</h4>
|
||||
|
|
@ -593,14 +707,15 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
<thead><tr><th>項</th><th>狀態</th><th>說明</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Edition + Plan/PlanDetail 種子</td><td><span class="pill done">已補</span></td><td><code>docker-compose-local-dev/seed-plans-plus.sql</code>(幂等,鏡像 mock 目錄;CPA edition GUID 對齊 jQSeparatedEditions,<code>down -v</code> 後重跑)</td></tr>
|
||||
<tr><td>後端 BR/CI 必填放開</td><td><span class="pill done">已改</span></td><td><code>OrderService.cs:154,190</code> 兩處必填校驗已注釋;唯一性校驗保留(rebuild 已生效)</td></tr>
|
||||
<tr><td>後端 BR/CI 必填放開</td><td><span class="pill partial">待應用/核對</span></td><td>決策=非必填:注释 <code>OrderService.cs:154,190</code> 兩處必填校驗(保留 :191 唯一性,空值安全)。<b>當前 working copy 這兩處仍 active</b>——需確認已在對接/部署環境放開</td></tr>
|
||||
<tr><td>BFF 真實路由</td><td><span class="pill done">已寫</span></td><td>新增 <code>routes/editions.js · plans.js · agents.js · tenants.js · subscribe.js</code> + <code>services/catalog.js</code>;<code>countries.js</code> 改為免 token;<code>index.js</code> 掛載於 mock/代理之前</td></tr>
|
||||
<tr><td>訂閱三態</td><td><span class="pill done">new 已驗</span> <span class="pill partial">renew/topup 待數據</span></td><td>new(corp/個人/CPA/加值項)實測返回 orderId、PlanPrice 正確(含 KYC 月租 PCS=月數);renew/topup 路由已寫,但本地無可續費租戶數據,待後端「按郵箱查」或先建租戶</td></tr>
|
||||
<tr><td>single-query / payments</td><td><span class="pill partial">mock</span></td><td>按決策暫留 mock(<code>PLANS_PLUS_MOCK=true</code> 兜底);支付本輪除外</td></tr>
|
||||
<tr><td>single-query(PAYG 隨付即用)</td><td><span class="pill done">前端+mock 已實現</span> <span class="pill partial">後端對接待接</span></td><td>本輪落地<b>新模型</b>:<b>檢測內容固定 4 項純展示、用戶不可選</b>(<code>/single-query/options</code> 去單價、加 <code>code</code>=V/E/A/D);<b>收款價取 <code>GetPlanList</code>(countryCode) 的 PAYG(P2G) 單一整包價</b>——新增原始端點 <code>POST /amlPortal/plan/portal/GetPlanList</code> mock,返回結構<b>與真後端逐字段一致</b>(<code>{code,msg,data:{totalCount,items:[…planDetails[].price]},version}</code>),切真後端免改前端。<code>/plans-jp→JPN</code>(目前唯一有價國家,mock ¥3,000 佔位);提交帶 <code>functionCodes="EAVD"</code>、幣種隨方案(JPY)。改動:<code>public/js/plans-plus.js · plans-plus.html · translations.js</code> + <code>server/mock/plans-plus.js</code>。支付本輪除外</td></tr>
|
||||
<tr><td>聯絡我們(不下單)</td><td><span class="pill partial">改接 contact</span></td><td>復用 <code>routes/contact.js</code>→<code>CreateFeedback</code>;前端調用點由 <code>/subscribe-offline</code> 改 <code>/contact</code>,推薦人路由由 BFF 補</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="small">端到端驗證(<code>npm run start:local</code> + 本地 docker 後端):<code>/editions</code>(10 行業 + CPA 切換)· <code>/plans/catalog</code>(standard/cpa/addons 全對)· <code>/countries</code>(249)· <code>/agents/:code</code>(無數據 found:false)· <code>/subscribe</code>(new 各變體均返回 orderId、訂單入庫)。頁面 <code>GET /plans-plus</code> HTTP 200。</p>
|
||||
<p class="small"><b>PAYG(single-query)本輪驗證(<code>APP_ENV=test</code> mock 模式):</b><code>/single-query/options</code> 返固定 4 項、無單價;<code>POST /amlPortal/plan/portal/GetPlanList{countryCode:"JPN"}</code>→P2G ¥3,000(JPY),<code>{HKG}</code>→空目錄(價格不可用);抽取 <code>plans-plus.js</code> 實際 <code>extractPaygPlan/paygPlanPrice/fmtMoneyCur</code> 跑真實響應:<code>P2G / 3000 / JPY / ¥3,000</code> 通過、HKG→<code>null/0</code>(禁用提交)。<code>GET /plans-jp</code> HTTP 200、含 <code>ppSqPriceValue</code> 橫幅。<span style="color:var(--muted)">(真後端 GetPlanList 對接與 ConsumerPortal 支付後編排見 5.6,仍待後端。)</span></p>
|
||||
</section>
|
||||
|
||||
<!-- ───────────── 7 ───────────── -->
|
||||
|
|
@ -609,15 +724,15 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
<table>
|
||||
<thead><tr><th>#</th><th>新增/變更</th><th>對接影響</th><th>狀態</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>1</td><td><b>單次查詢 single-query</b>(按次付費,第 4 種流程)</td><td>後端零支持,需新增或保留 mock</td><td><span class="pill todo">需後端/mock</span></td></tr>
|
||||
<tr><td>1</td><td><b>單次查詢 single-query</b>(隨付即用,第 4 種流程)</td><td><b>後端已支持</b>(ConsumerPortal 即時檢測);<b>兩步由 AML 後端在支付完成後內部編排</b>(CreateConsumerLink→CreateConsumerOrder),V2 只下單+收款。<b>檢測內容固定 4 項純展示不可選</b>(V/E/A/D);<b>定價取 <code>GetPlanList</code>(countryCode) 的 PAYG 方案價</b>(<code>/plans-jp→JPN</code>),映射與 2C 租戶均在後端(見 5.6)</td><td><span class="pill partial">改造</span></td></tr>
|
||||
<tr><td>2</td><td><b>聯絡我們/推薦人</b>(發訂單摘要 · <b>不創建訂單</b>)</td><td>復用既有 <code>contact</code>/<code>CreateFeedback</code>;「送推薦人」需後端加 <code>AssignedAgentUserId</code> 收件人字段(小改)+ BFF 拼裝 <code>message</code></td><td><span class="pill partial">復用+後端小改</span></td></tr>
|
||||
<tr><td>3</td><td><b>agent tiers</b>(推薦人可售級別過濾方案)</td><td>SearchUser 不含 tiers,需經 GetPlanList(agentUserId) 推導</td><td><span class="pill partial">BFF</span></td></tr>
|
||||
<tr><td>4</td><td><b>agent email/phone</b>(聯絡推薦人)</td><td>AppUserDto 已含 Email/PhoneNumber,BFF 帶出</td><td><span class="pill partial">BFF</span></td></tr>
|
||||
<tr><td>5</td><td><b>主體類型 corp/individual</b>(新租戶)</td><td>個人主體無 BR/CI ⇒ 撞後端 BR/CI 必填</td><td><span class="pill todo">阻塞</span></td></tr>
|
||||
<tr><td>6</td><td><b>BR/CI 仍必填</b>(更正舊版「已放開」結論)</td><td><code>OrderService.cs:154,190</code> 校驗仍生效</td><td><span class="pill todo">需業務決策</span></td></tr>
|
||||
<tr><td>5</td><td><b>主體類型 corp/individual</b>(新租戶)</td><td>個人主體無 BR/CI;BR/CI 已定非必填 ⇒ 不再阻塞(待 :154/:190 放開已應用)</td><td><span class="pill done">非阻塞</span></td></tr>
|
||||
<tr><td>6</td><td><b>BR/CI 非必填</b>(已定)</td><td>注释 <code>OrderService.cs:154,190</code>(保留唯一性);當前 working copy 仍 active,待確認已放開</td><td><span class="pill partial">待應用/核對</span></td></tr>
|
||||
<tr><td>7</td><td><b>KYC 改設備月租</b>(monthlyPrice × 月數)</td><td>後端 KYC 單價 ⇒ 下單 PCS=月數(見 5.2)</td><td><span class="pill partial">BFF/後端確認</span></td></tr>
|
||||
<tr><td>8</td><td><b>jQuota 改選配套</b>(package,非按量)</td><td>後端 jQ plan 天然離散,BFF 映 packages</td><td><span class="pill partial">BFF</span></td></tr>
|
||||
<tr><td>9</td><td><b>按郵箱查租戶 + 郵箱唯一單命中</b></td><td>後端按名字搜,需按郵箱端點;不再多租戶消歧</td><td><span class="pill todo">需後端</span></td></tr>
|
||||
<tr><td>9</td><td><b>按郵箱查租戶 + 郵箱唯一單命中</b></td><td><b>已定:後端新增 <code>queryRenewableTenantByEmail</code></b>(現端點 DB 層只按租戶名搜、郵箱查後回填→不能按郵箱過濾);不再多租戶消歧;新端點聚合 currentSubscription+referrer(見 5.5)</td><td><span class="pill todo">需後端新增</span></td></tr>
|
||||
<tr><td>10</td><td><b>續費頁展示 referrer</b></td><td>TenantPropertyDto.AgentorCode/Name 映射</td><td><span class="pill partial">BFF</span></td></tr>
|
||||
<tr><td>11</td><td><b>續費續價 / 顯示舊方案</b>(Req 9)</td><td>續價後端不認前端 price;舊方案卡屬前端渲染</td><td><span class="pill todo">後端確認</span></td></tr>
|
||||
<tr><td>12</td><td><b>過期加購阻斷</b>(須先續費)</td><td>純前端門檻,無對接影響</td><td><span class="pill done">無影響</span></td></tr>
|
||||
|
|
@ -639,7 +754,7 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
<tr><td><code>routes/plans.js</code>(新)</td><td><code>GET /plans/catalog</code> + filterPlan 拆分 + KYC 月租/jQ 配套 + 緩存 planDetailId</td><td>GetPlanList</td></tr>
|
||||
<tr><td><code>routes/countries.js</code></td><td>已存在,直接掛載</td><td>getCategoryByTypes</td></tr>
|
||||
<tr><td><code>routes/agents.js</code>(新)</td><td><code>GET /agents/:code</code> → {code,name,tiers,email,phone,agentorId}</td><td>SearchUserByCodeAndType (+GetPlanList)</td></tr>
|
||||
<tr><td><code>routes/tenants.js</code>(新)</td><td><code>GET /tenants/lookup?email=</code> + currentSubscription 重建 + referrer</td><td>queryRenewableTenant(ByEmail)</td></tr>
|
||||
<tr><td><code>routes/tenants.js</code>(新)</td><td><code>GET /tenants/lookup?email=</code> → 字段直通 + 命中態(none/unique) 包裝(currentSubscription/referrer 聚合由後端算好)</td><td><b>新增</b> queryRenewableTenant<b>ByEmail</b></td></tr>
|
||||
<tr><td><code>routes/subscribe.js</code>(新)</td><td><code>POST /subscribe</code> 分流 + PlanList 組裝(含 KYC PCS=月數)</td><td>CreateOrder / TenantRenewal / (Topup)</td></tr>
|
||||
<tr><td><code>routes/contact.js</code></td><td>已存在;「聯絡我們」復用之——把結構化 payload 序列化為 <code>message</code> 摘要 + 透傳 <code>agentUserId</code>,轉 <code>CreateFeedback</code>(收件人路由由後端 <code>AssignedAgentUserId</code> 承接)</td><td>CreateFeedback</td></tr>
|
||||
<tr><td><code>mock/plans-plus.js</code></td><td>single-query / payments <b>暫留 mock</b>;<code>subscribe-offline</code> 改由 contact 承接(可保留為兼容別名)</td><td>—</td></tr>
|
||||
|
|
@ -650,11 +765,11 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
|
||||
<h3>8.2 可能的後端改動(與後端團隊確認)</h3>
|
||||
<ul class="tight">
|
||||
<li><b>BR/CI 必填</b>:是否按 plans-plus 需求去除 <code>CreateOrder</code> 的 BR/CI 必填校驗(或僅對個人主體放開)。</li>
|
||||
<li><b>按郵箱查可續費租戶</b>端點,返回聚合 currentSubscription(含 planId / 上期 price / 已購加值項)+ referrer。</li>
|
||||
<li><b>BR/CI 非必填</b>:已定去除 <code>CreateOrder</code> 的 BR/CI 必填校驗(注释 :154/:190,保留唯一性);請確認已在對接/部署環境應用(當前 working copy 仍 active)。</li>
|
||||
<li><b>按郵箱查可續費租戶</b>:<span class="pill todo">已定 · 待實現</span> 新增 <code>POST Order/portal/queryRenewableTenantByEmail</code>(入參 <code>{Email}</code>,<code>[AbpAutoAuth("Portal")]</code>)——先按 <code>UserName=='admin' && Email==</code> 定位租戶(可復用現 <code>GetAllUsers()</code> 全庫掃描、改按郵箱過濾,掃描成本可接受),返回聚合 currentSubscription(planId / 上期 price / 已購加值項,反查思路借用 <code>GetCurrServiceInfo</code>)+ referrer(AgentorCode/Name)。詳見 5.5。</li>
|
||||
<li><b>topup 語義</b>:確認 <code>TenantRenewal</code>(PlanList 僅加值項)是否「只疊配額不延期」,或新增 <code>TenantTopup</code>。</li>
|
||||
<li><b>KYC 計費口徑</b>:確認 KYC plan 是否可按 <code>PCS=月數</code> 計月租。</li>
|
||||
<li><b>single-query</b>:是否新增後端按次查詢端點,還是短期 mock。</li>
|
||||
<li><b>single-query</b>:已用 ConsumerPortal 即時檢測,<b>兩步由後端內部編排</b>(見 5.6)。<b>檢測內容固定 4 項(V/E/A/D)、定價取 <code>GetPlanList</code>(countryCode) 的 PAYG 方案價</b>。待後端確認:① V2「支付完成」與後端編排的銜接端點/webhook;② 隨付即用專用「2C 租戶」+ <code>AML.ConsumerPortal.Enable</code> 功能 + 實時篩查權限(供後端落上下文,<b>V2 無需持該租戶憑證</b>);③ <s>op→功能碼映射</s><b>已定固定 EAVD</b>——僅需確認後端是「V2 固定傳 EAVD」還是「後端寫死」;④ <s>收款金額對齊</s><b>已確認</b>:PAYG = <b>單一整包價</b>;<b>目前 <code>countryCode</code> 僅日本(JPN)</b>,僅 <code>/plans-jp</code> 有 P2G 方案,餘國待種 PlanDetail 後再開。</li>
|
||||
<li><b>聯絡我們送推薦人</b>:<span class="pill todo">待實現</span> 已定方案——後端 <code>CreateFeedback</code> 增 <code>AssignedAgentUserId</code> 收件人字段(DTO + 實體 + 服務解析郵箱 + EF 遷移):有值發推薦人、否則發平台銷售。</li>
|
||||
<li>(可選)Plan 增 <code>bestValue</code>/多語言 <code>note</code>/<code>nameJP</code>;edition 增多語言名稱。</li>
|
||||
</ul>
|
||||
|
|
@ -665,9 +780,9 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
<tbody>
|
||||
<tr><td>P0 · 本地種子</td><td>補 Edition + Plan/PlanDetail 種子,對齊配置(第 6 章)</td><td>後端返回非空目錄,可對接</td></tr>
|
||||
<tr><td>P1 · 只讀目錄</td><td>editions + plans/catalog + countries(已就緒)+ agents</td><td>頁面渲染方案/行業/國家,校驗推薦人</td></tr>
|
||||
<tr><td>P2 · 新購下單</td><td>subscribe(new)(先解 BR/CI 阻塞)</td><td>新租戶下單至「已提交成功」(支付除外)</td></tr>
|
||||
<tr><td>P3 · 續費/加購</td><td>tenants/lookup(後端新端點)+ subscribe(renew/topup)</td><td>續費/加購閉環(支付除外)</td></tr>
|
||||
<tr><td>P4 · 單次查詢/聯絡/支付</td><td>single-query + 聯絡我們(接 contact API + 推薦人路由)+ payments(後端就緒後)</td><td>完整流程 + 在線支付</td></tr>
|
||||
<tr><td>P2 · 新購下單</td><td>subscribe(new)(BR/CI 已定非必填:注释 :154/:190)</td><td>新租戶下單至「已提交成功」(支付除外)</td></tr>
|
||||
<tr><td>P3 · 續費/加購</td><td>tenants/lookup(依賴後端新端點 <code>queryRenewableTenantByEmail</code>)+ subscribe(renew/topup)</td><td>續費/加購閉環(支付除外);<b>阻塞於後端新端點</b></td></tr>
|
||||
<tr><td>P4 · 單次查詢/聯絡/支付</td><td>single-query(支付後接 ConsumerPortal 兩步:CreateConsumerLink→CreateConsumerOrder)+ 聯絡我們(接 contact API + 推薦人路由)+ payments(後端就緒後)</td><td>完整流程 + 在線支付</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
|
@ -676,10 +791,10 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
<section id="s9">
|
||||
<h2 class="sec">9. 待確認問題清單</h2>
|
||||
<ol class="tight" style="padding-left:20px">
|
||||
<li><b>BR/CI 必填:</b><span class="pill todo">關鍵</span> 後端 <code>CreateOrder</code> 仍要求 BR 或 CI 至少一個(<code>OrderService.cs:154,190</code>)。plans-plus 新購(含個人主體)需不填也能下單——是否去除該校驗?還是前端把 corp BR 改必填、個人主體暫不開放?</li>
|
||||
<li><b>單次查詢:</b>後端是否新增按次查詢(options + 下單 + 計費 + 結果投遞)?本輪是否先保留 mock?</li>
|
||||
<li><b>BR/CI:</b><span class="pill done">已定非必填</span> plans-plus 新購(含個人主體 / 空 BR/CI)可下單。實現=注释 <code>CreateOrder</code> 的 <code>OrderService.cs:154,190</code>(保留 :191 唯一性,空值安全)。<b>待辦</b>:確認該放開已在對接/部署環境生效(<b>當前 working copy 兩處仍 active</b>);並知悉全局影響(旧站共用 <code>CreateOrder</code>,空 BR 不再攔截)。</li>
|
||||
<li><b>單次查詢:</b><span class="pill partial">已定方向</span> 後端<b>已支持</b>——ConsumerPortal 即時檢測;<b>調用方=AML 後端內部編排</b>(支付完成後 <code>CreateConsumerLink</code>→<code>CreateConsumerOrder</code>,V2 只下單+收款,見 5.6)。<b>本輪再定</b>:檢測內容<b>固定 4 項純展示不可選</b>(證件核驗V / 名單篩查ES=E / AI 增強A / 失信D);<b>收款金額取 <code>GetPlanList</code> 按 <code>countryCode</code> 的 PAYG(P2G) 方案價</b>(<code>/plans-jp→JPN</code>)。<b>待後端確認</b>:① V2「支付完成」與後端編排的銜接方式(後端持單 + 收 webhook,或 V2 支付後調後端專門 2C 觸發端點);② 隨付即用專用「2C 租戶」+ <code>AML.ConsumerPortal.Enable</code> + 實時篩查權限(供後端落上下文,<b>V2 無需該租戶憑證</b>);③ <code>functionCodes="EAVD"</code> 由 V2 固定傳、還是後端寫死;④ <span class="pill done">已確認</span> PAYG = <b>單一整包價</b>(非分項之和);<b>目前 <code>countryCode</code> 僅日本(JPN)</b>,僅 <code>/plans-jp</code> 有 P2G 方案 + PlanDetail,餘國後續再種。</li>
|
||||
<li><b>聯絡我們(不下單):</b><span class="pill done">已定方案</span> 復用 <code>contact</code>/<code>CreateFeedback</code> 發送訂單摘要(不創建訂單/線索);「送推薦人」擬由後端 <code>CreateFeedback.AssignedAgentUserId</code> 承接(<span class="pill todo">待實現</span>,見 5.8)。<b>待辦</b>:後端加字段 + 服務路由 + EF 遷移;前端調用點由 <code>/subscribe-offline</code> 改 <code>POST /api/contact</code> 並透傳 <code>agentUserId</code>;BFF 映為 <code>AssignedAgentUserId</code>。</li>
|
||||
<li><b>租戶查詢:</b>能否新增「按管理員郵箱查可續費租戶」,直接帶 currentSubscription(planId/上期價/已購加值項)+ referrer?</li>
|
||||
<li><b>租戶查詢:</b><span class="pill done">已定新增後端端點</span> <code>queryRenewableTenantByEmail(email)</code> 直接返回「租戶 + currentSubscription(planId/上期價/已購加值項)+ referrer」聚合(見 5.5)。<b>待後端確認</b>:① 「admin 郵箱」是否恆等於 <code>UserName=='admin'</code> 用戶的 Email(現 <code>queryRenewableTenant</code> 即此語義)——若存在非 <code>admin</code> 用戶名的租戶管理員,需改按角色定位;② 過期租戶是否照樣返回(供前端做臨期/過期展示 + topup 阻斷);③ 聚合 DTO 形態(新 <code>RenewableTenantDto</code> vs 擴 <code>TenantPropertyDto</code>)。</li>
|
||||
<li><b>topup:</b>純加購走 <code>TenantRenewal</code>(PlanList 僅加值項)後端是否接受、語義是否「只疊配額不延期」?</li>
|
||||
<li><b>KYC 月租:</b>KYC plan 是否可按 <code>PCS=租賃月數</code> 計費(planDetail.price 當月租單價)?</li>
|
||||
<li><b>續費續價:</b>後端 <code>TenantRenewal</code> 是否支持沿用上期價/自定義價?否則前端續費折扣僅展示、實際按目錄價。</li>
|
||||
|
|
@ -692,7 +807,7 @@ cross-env APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true P
|
|||
</section>
|
||||
|
||||
<p class="small" style="text-align:center;margin-top:48px;color:var(--muted)">
|
||||
本分析基於源碼靜態閱讀 + 本地 docker 庫實測(2026-07):<code>plans-plus.js</code> / <code>server/*</code>(含 mock 契約)/ <code>AMLPortal</code> Controllers 與 Service / <code>CustomIdentityUserController</code> / 旧站 <code>PlanService</code> / <code>appsettings.local.json</code> 與本地數據庫。涉及後端行為(BR/CI 校驗、續費續價、topup / KYC 計費語義)以實際接口 + 後端確認為準。
|
||||
本分析基於源碼靜態閱讀 + 本地 docker 庫實測(2026-07):<code>plans-plus.js</code> / <code>server/*</code>(含 mock 契約)/ <code>AMLPortal</code> Controllers 與 Service / <code>iCON.Abp.AML</code> 的 <code>ConsumerPortalController</code> 與 <code>ConsumerPortalService</code>(單次查詢即時檢測)/ <code>CustomIdentityUserController</code> / 旧站 <code>PlanService</code> / <code>appsettings.local.json</code> 與本地數據庫。涉及後端行為(BR/CI 校驗、續費續價、topup / KYC 計費語義、單次查詢 2C 計費與鑑權)以實際接口 + 後端確認為準。
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue