justsolutionsWebV2 · plans-plus 真實 API 對接實現報告

plan-plus-api分析.html 的方案,把 public/plans-plus.html 單頁訂閱流程從 mock 切換到 AML 後端(iCON.Abp.AMLPortal)真實 API,並在本地 docker-compose-local-dev 環境跑通、逐流程實測(支付除外)。本文記錄實際落地內容與驗證結果。

範圍:AML_Backend(後端 3 項改動 + 配置)· justsolutionsWebV2/server(BFF 真實路由)· docker-compose-local-dev(本地棧 + 數據恢復)

狀態(2026-07-06 實測):P1 只讀目錄 / P2 新購 / P3 續費·加購 / P4 聯絡我們 —— 全部端到端打通並真實入庫;支付與單次查詢真實檢測編排本輪除外(保持 mock)。

目錄

  1. 1. 落地結論速覽
  2. 2. 數據恢復(.bak → AbpAML)
  3. 3. 後端改動(AML_Backend)
  4. 4. BFF 對接(justsolutionsWebV2)
  5. 5. 本地運行與驗證方式
  6. 6. 端到端測試結果
  7. 7. 本輪除外與後續待辦
  8. 8. 變更文件清單

1. 落地結論速覽

plans-plus 依賴的 3 條只讀目錄(editions / plans/catalog / countries)、2 條交互查詢(agents / tenants/lookup)、下單提交(subscribe,含 new/renew/topup)與 聯絡我們(subscribe-offline → CreateFeedback)均已對接真實後端並實測通過。單次查詢與支付本輪除外,仍走 mock 兜底。

前端端點對應後端狀態實測結果
GET /editionsOrder/portal/GetEditionList完成9 行業(過濾 Standard/DC、Others 置末);jQSeparatedEditions 對齊真實 CPA GUID
GET /plans/catalogplan/portal/GetPlanList完成HKG·HKD 目錄;standard/cpa/addons 全帶 planDetailId;KYC 月租 + jQuota 配套
GET /countriesOrder/getCategoryByTypes完成249 國;已改免 token(原用 OAuth,本地無憑證會失敗)
GET /agents/:codeSearchUserByCodeAndType + GetPlanList(agentUserId)完成返回 {code,name,tiers,email,phone,agentorId};tiers 經二次 GetPlanList 推導,空=不過濾
GET /tenants/lookup新增 Order/portal/queryRenewableTenantByEmail完成後端新端點按郵箱聚合 currentSubscription+referrer;cpa1 唯一命中
POST /subscribe newOrder/portal/CreateOrder完成空 BR / 個人主體 / 加值項 / agent 均建單入庫(BR/CI 已放開)
POST /subscribe renew·topupOrder/portal/TenantRenewal完成cpa1 續費 / jQuota 加購均建單;修復內部 token URL 後通過
POST /subscribe-offline
(聯絡我們/推薦人)
customer/CreateFeedback完成訂單摘要序列化為 message;有推薦人則寫 AssignedAgentUserId 路由收件人
/single-query · /payments/*ConsumerPortal(待後端)· 支付方(未定)本輪除外保持 mock 兜底;文檔明確除外

2. 數據恢復(.bak → AbpAML)

docs/AML_Backend/docker-compose-local-dev/AML-local-dev.bak(201MB,SQL Server 備份,內部庫名 AML-local-dev)恢復真實開發數據,取代原「補種子 SQL」路線——恢復後目錄 / 租戶 / 代理計劃齊備,直接可對接。

docker stop aml-httpapi-host → ALTER DATABASE AbpAML SET SINGLE_USER WITH ROLLBACK IMMEDIATE → RESTORE DATABASE AbpAML FROM DISK WITH MOVE(AML-local-dev→AbpAML.mdf, AML-local-dev_log→AbpAML_log.ldf), REPLACE → docker start aml-httpapi-host

恢復為 AbpAML(host 連接的庫),MOVE 邏輯文件到 /var/opt/mssql/data/AbpAML.mdf/_log.ldf;恢復前停 host 釋放連接、置 SINGLE_USER 取得獨佔。

恢復後用途
AMLPortal_Plans / PlanDetails123 / 123方案目錄(按國家分兩套:HKG-HKD 與 JPN-USD,靠 systemCode/countryCode 區分)
SaasEditions12(含真實 CPA 3A1A296B-…所屬行業
SaasTenants / AMLPortal_TenantPropertys42 / 98續費 / 加購測試數據
AMLPortal_AgentUserPlans24agent tiers 過濾
AMLPortal_Orders / AbpUsers142 / 131訂單反查 / 推薦人

catalog 國家過濾:services/catalog.js 默認過濾 HKG(香港市場·HKD,與舊 mock 幣種一致);/plans-jp 的 PAYG 定價另走 JPN

3. 後端改動(AML_Backend)

共 3 項代碼改動 + 2 項配置,全部重建 httpapi-host 鏡像後實測通過。

3.1 BR/CI 非必填 完成

注釋 OrderService.cs CreateOrder 的兩處 BR/CI 必填校驗(前置校驗處 + Organization BR/CI required 處),保留唯一性校驗 ExistsByOrganizationBRCI(對 BR、CI 皆空返回 false,空值安全)。個人主體 / 空 BR 可下單。

3.2 新增 queryRenewableTenantByEmail 端點 新增

POST /api/amlPortal/Order/portal/queryRenewableTenantByEmail AbpAutoAuth("Portal")

解決舊 queryRenewableTenant 只能按租戶名 DB 搜、無法按郵箱過濾的維度錯位。新端點維度反轉

新增 DTO:QueryRenewableTenantByEmailParamRenewableTenantByEmailDto(含 CurrentSubscriptionDto / RenewableReferrerDto)。

3.3 CreateFeedbackAssignedAgentUserId(聯絡推薦人路由)完成

Feedback 實體 + CreateFeedbackDto 加可空 AssignedAgentUserId(AutoMapper 同名自動映射);CustomerService.CreateFeedback 於有值時用 GetUsersByIDs 解析推薦人郵箱,作 SendEmailOnPortalFeedback 的收件人(無郵箱回退平台銷售),給客戶本人的確認郵件不變。

EF 遷移(部署待辦):本地以 ALTER TABLE AMLPortal_Feedbacks ADD AssignedAgentUserId uniqueidentifier NULL 加列(實體屬性映射到該列即可運行)。正式部署需在構建環境跑 dotnet ef migrations add 生成遷移(就一句 AddColumn)——原因:運行時 Database.Migrate() 會讀取遷移的 TargetModel(完整模型快照),手寫遷移不可行。

3.4 對齊 jQSeparatedEditions CPA GUID 完成

appsettings.local.jsonPortal.jQSeparatedEditions.EditionIds 由庫中不存在的 3A1A2969-… 改為真實 CPA edition 3A1A296B-DAB4-4B94-B267-4424683B8916,否則前端選 CPA 不觸發 isCpa 方案集切換。

3.5 修正內部 token URL(本地)完成

實測中發現的坑:AppConfig:General:ApiLocalhostUrl 出廠 https://localhost:44331/,但本地 host 只監聽 HTTP → TenantRenewal / 建租戶等內部 AbpTokenService.connect/token 調用 SSL 握手失敗(500)docker-compose.local.yml host env 已加 AppConfig__General__ApiLocalhostUrl: "http://localhost:44331/"。(CreateOrder 走異步隊列建租戶未同步觸發,故只有 renew/topup 報錯——由此定位。)

4. BFF 對接(justsolutionsWebV2)

採 BFF 範式:瀏覽器只調本站 /api/*,服務端匿名代理後端並裁剪響應。前端 plans-plus.js 零改動

免 token:門戶端點均 [AbpAutoAuth("Portal")]SearchUserByCodeAndType[AllowAnonymous])——後端 AbpAutoAuthMiddleware 服務端注入門戶訪客 token 並覆蓋 Authorization,故 BFF 匿名 POST/GET 即可,無需配 AUTH_*。統一助手 server/services/portal.js

文件職責
services/portal.js 匿名 POST/GET 助手(axios + 自簽證書放行 + {code,msg,data} 拆包)
services/catalog.js GetPlanList → 按國家(默認 HKG)過濾 → filterPlan 拆 standard/cpa/addons → 產出前端契約 + planDetailId 索引;5 分鐘緩存
routes/editions.js GetEditionList → 多語言映射 + 過濾 Standard/DC + Others 置末 + jQSeparatedEditions 鍵名/值轉小寫
routes/plans.js GET /plans/catalog[?countryCode] → catalog 服務
routes/agents.js GET /agents/:code → SearchUser + GetPlanList(agentUserId) 推導 tiers
routes/tenants.js GET /tenants/lookup?email= → queryRenewableTenantByEmail,字段直通 + 命中態 + 日期格式化
routes/subscribe.js POST /subscribe(new→CreateOrder / renew·topup→TenantRenewal,組裝 PlanList 回填 planDetailId、KYC PCS=月數) + POST /subscribe-offline(序列化摘要 + agentCode 解析→AssignedAgentUserId → CreateFeedback)
routes/countries.js 由 OAuth 改免 token(本地無憑證),輸出不變
index.js 真實路由挂在 mock 之前:真實路由處理訂閱流程,mock 只兜底 single-query/options、PAYG-GetPlanList、payments、promos
package.json 補回 start:local 腳本

PlanList 組裝(subscribe 核心)

PlanList = []
if type != 'topup': push({PlanId: plan.planId, PlanDetailId: plan.planDetailId, PCS:1})
if addons.users > 0:  push({PlanId: refs.user.planId,  PlanDetailId: refs.user.planDetailId,  PCS: users})
if addons.kyc:        push({PlanId: refs.kyc.planId,   PlanDetailId: refs.kyc.planDetailId,   PCS: kycRentalMonths})  // KYC 月租
if addons.jquotaPackageId: push({PlanId: jqId, PlanDetailId: planDetailByPlanId[jqId], PCS:1})

5. 本地運行與驗證方式

① 後端棧(docker-compose-local-dev)

cd AML_Backend/docker-compose-local-dev
docker compose -f docker-compose.local.yml build httpapi-host        # 編譯後端改動
docker compose -f docker-compose.local.yml up -d --no-deps httpapi-host   # 只重建 host,不重跑 migrator
# 後端 Swagger: http://localhost:44331/swagger   DB: localhost,11433 (sa / Aml@Local2026)

② BFF(justsolutionsWebV2)

cd justsolutionsWebV2
npm run start:local
# = APP_ENV=dev API_BASE_URL=http://localhost:44331 PLANS_PLUS_MOCK=true PORT=8090
# 頁面: http://localhost:8090/plans-plus  (日本版 /plans-jp)

PLANS_PLUS_MOCK=true 讓 single-query/payments 走 mock 兜底;真實訂閱路由挂在 mock 之前優先處理。

6. 端到端測試結果

以下均為 APP_ENV=dev、指向本地 docker 後端 的真實調用(非 mock),並核對數據庫寫入。

流程驗證
只讀目錄editions=9 行業(CPA 用對齊 GUID)· catalog standard/cpa/addons 全帶 planDetailId · countries=249 · agents/C99099 返回 tiers/email/agentorId
租戶查詢tenants/lookup?email=307736951@qq.com → cpa1 唯一命中,聚合 editionId=CPA / referrer / currentSubscription(kyc=true, expiry 2027)
新購(多變體)4 筆真實訂單入庫:基礎 / 個人主體+空 BR / 全加值項(Standard+jQuota+增加使用人+KYC) / agent;GoodsName 顯示 PlanList 組裝正確
續費 / 加購cpa1 續費(CPA 24 Months) + jQuota 200 加購 均建單入庫(修 token URL 後)
聯絡我們/推薦人subscribe-offline → Feedback 寫入,AssignedAgentUserId=3A09C9F3…(推薦人)、摘要序列化 245 字
前端渲染(無頭 Chrome)行業下拉填充真實 editions、jQuota 真實 HKD 價渲染、無 JS 錯誤

結論:P1–P4 全部端到端打通,真實建單入庫,前端真實渲染。

7. 本輪除外與後續待辦

8. 變更文件清單

AML_Backend

justsolutionsWebV2


本報告基於 2026-07-06 本地 docker 環境實測。配套方案文檔見 plan-plus-api分析.html