AML/docs/justsolutionsWeb/api-calls.html

428 lines
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>justsolutionsWeb — 后端 API 调用清单</title>
<style>
:root {
--bg: #f6f8fa;
--card: #ffffff;
--text: #24292f;
--muted: #57606a;
--border: #d0d7de;
--accent: #0969da;
--accent-soft: #ddf4ff;
--code-bg: #f0f3f6;
--th-bg: #f0f3f6;
--row-alt: #fafbfc;
--warn-bg: #fff8c5;
--warn-border: #d4a72c;
--note-bg: #ddf4ff;
--note-border: #54aeff;
--get: #1a7f37;
--post: #9a6700;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Helvetica, Arial, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.65;
font-size: 15px;
}
.wrap {
max-width: 1040px;
margin: 0 auto;
padding: 32px 24px 80px;
}
header.page {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 28px 32px;
margin-bottom: 28px;
}
header.page h1 { margin: 0 0 8px; font-size: 26px; }
header.page p { margin: 4px 0; color: var(--muted); }
.toc {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 18px 28px;
margin-bottom: 28px;
}
.toc h2 { font-size: 15px; margin: 0 0 10px; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.toc ol { margin: 0; padding-left: 20px; columns: 2; column-gap: 36px; }
.toc li { margin: 4px 0; break-inside: avoid; }
.toc a { color: var(--accent); text-decoration: none; }
.toc a:hover { text-decoration: underline; }
section {
background: var(--card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px 32px;
margin-bottom: 22px;
}
h2 { font-size: 21px; margin: 0 0 16px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
h3 { font-size: 17px; margin: 24px 0 12px; }
p { margin: 12px 0; }
table {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
font-size: 14px;
}
th, td {
border: 1px solid var(--border);
padding: 8px 12px;
text-align: left;
vertical-align: top;
}
th { background: var(--th-bg); font-weight: 600; }
tbody tr:nth-child(even) { background: var(--row-alt); }
code {
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
background: var(--code-bg);
padding: 1px 6px;
border-radius: 5px;
font-size: 13px;
color: #cf222e;
word-break: break-word;
}
td code { color: #0550ae; }
.method { font-weight: 700; font-size: 12px; letter-spacing: .5px; }
.method.get { color: var(--get); }
.method.post { color: var(--post); }
.num { text-align: center; font-variant-numeric: tabular-nums; color: var(--muted); }
.callout {
border-radius: 8px;
padding: 12px 16px;
margin: 16px 0;
border-left: 4px solid;
font-size: 14px;
}
.callout.warn { background: var(--warn-bg); border-color: var(--warn-border); }
.callout.note { background: var(--note-bg); border-color: var(--note-border); }
.callout p { margin: 6px 0; }
ul.tight li { margin: 5px 0; }
.pill {
display: inline-block;
background: var(--accent-soft);
color: var(--accent);
border-radius: 999px;
padding: 1px 10px;
font-size: 12px;
font-weight: 600;
margin-left: 4px;
}
footer { text-align: center; color: var(--muted); font-size: 13px; margin-top: 30px; }
.small { font-size: 13px; color: var(--muted); }
</style>
</head>
<body>
<div class="wrap">
<header class="page">
<h1>justsolutionsWeb — 后端 API 调用清单</h1>
<p>前端:<code>justsolutions-web</code> v1.2.0 · Angular 16 · Angular CLI 16.2.14</p>
<p class="small">基于源码静态分析(<code>src/app</code> 下所有 <code>*.ts</code>)整理。共 14 个后端端点。</p>
</header>
<nav class="toc">
<h2>目录</h2>
<ol>
<li><a href="#arch">调用架构概览</a></li>
<li><a href="#list">调用清单(按端点汇总)</a></li>
<li><a href="#auth">鉴权OAuth Token</a></li>
<li><a href="#amlquery">AML 查询模块</a></li>
<li><a href="#contact">联系我们模块</a></li>
<li><a href="#plans">套餐与下单模块</a></li>
<li><a href="#prefix">端点前缀分布</a></li>
<li><a href="#flow">速查 · 典型流程</a></li>
</ol>
</nav>
<section id="arch">
<h2>1. 调用架构概览</h2>
<p>所有业务 API 调用都收口在统一的 <strong><code>HttpService</code></strong><code>src/app/services/http.service.ts</code></p>
<ul class="tight">
<li><strong>基地址</strong>:请求时把相对路径拼到 <code>environment.apis.url</code> 之后,即 <code>environment.apis.url + request.url</code></li>
<li><strong>鉴权</strong>:自动从 <code>localStorage</code><code>sessionStorage</code> 读取 <code>aml_access_token</code>,存在时加上请求头 <code>Authorization: Bearer &lt;token&gt;</code></li>
<li><strong>请求模型</strong>:统一使用 <code>Http.Request&lt;T&gt;</code><code>src/app/models/http.ts</code>),字段含 <code>method</code> / <code>url</code> / <code>body</code> / <code>params</code> / <code>headers</code> / <code>responseType</code> 等。</li>
</ul>
<div class="callout note">
<p><strong>例外</strong>:支付跳转直接 <code>location.href</code> 重定向到 QFPay 外部网关(见第 6 节),不经过 <code>HttpService</code></p>
</div>
<h3>1.1 环境与基地址配置</h3>
<table>
<thead>
<tr><th>环境文件</th><th><code>apis.url</code>API 基地址)</th><th><code>apis.amlUrl</code>AML 门户来源)</th><th><code>apis.test</code></th></tr>
</thead>
<tbody>
<tr><td><code>environment.ts</code>(本地默认)</td><td><code>https://api-aml.iconsz.com</code></td><td><code>https://aml.iconsz.com</code></td><td><code>true</code></td></tr>
<tr><td><code>environment.dev.ts</code></td><td><code>https://api-aml.iconsz.com</code></td><td><code>https://aml.iconsz.com</code></td><td><code>true</code></td></tr>
<tr><td><code>environment.prod.ts</code></td><td><code>https://api.justsolutions.ai</code></td><td><code>https://aml.justsolutions.ai</code></td><td><code>false</code></td></tr>
</tbody>
</table>
<ul class="tight">
<li><code>apis.amlUrl</code>:仅用于 <code>plans</code> 页面与父窗口AML 门户 iframe 宿主)之间的 <code>postMessage</code> 通信,<strong>并非 HTTP 端点</strong></li>
<li><code>apis.test</code>:为 <code>true</code> 时支付金额固定写死为 <code>10</code>(分),用于测试支付(见第 6 节)。</li>
</ul>
</section>
<section id="list">
<h2>2. 调用清单(按端点汇总)</h2>
<table>
<thead>
<tr><th class="num">#</th><th>方法</th><th>端点(相对 <code>apis.url</code></th><th>用途</th><th>前端方法</th><th>所在文件</th></tr>
</thead>
<tbody>
<tr>
<td class="num">1</td><td><span class="method post">POST</span></td>
<td><code>/connect/token?__tenant=2C</code></td>
<td>OAuth 取 access token</td>
<td><code>getToken()</code></td>
<td><code>aml-query.service.ts</code></td>
</tr>
<tr>
<td class="num">2</td><td><span class="method get">GET</span></td>
<td><code>/api/aml/ConsumerPortal/GetPrice/{inputCode}</code></td>
<td>查询消费者查询价格</td>
<td><code>getPrice()</code></td>
<td><code>aml-query.service.ts</code></td>
</tr>
<tr>
<td class="num">3</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/customer/CreateFeedback</code></td>
<td>提交联系表单反馈</td>
<td><code>sendContactEmailAndReturnFeedback()</code></td>
<td><code>contact.service.ts</code></td>
</tr>
<tr>
<td class="num">4</td><td><span class="method post">POST</span></td>
<td><code>/api/aml/emailQueue/CreateEmailQueue</code></td>
<td>发送通知邮件(管理员 + 用户回执两次调用)</td>
<td><code>sendContactEmail()</code> / <code>sendContactByReturnEmail()</code></td>
<td><code>contact.service.ts</code></td>
</tr>
<tr>
<td class="num">5</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/plan/portal/GetPlanList</code></td>
<td>获取套餐列表(基础/jQ/加人/KYC</td>
<td><code>getAllPlanList()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">6</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/portal/GetEditionList</code></td>
<td>获取系统定义的 edition所属行业列表</td>
<td><code>getEditionList()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">7</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/getCategoryByTypes</code></td>
<td>获取国家/地区列表(<code>typeCodes: ['COUNTRY']</code></td>
<td><code>getCountryList()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">8</td><td><span class="method get">GET</span></td>
<td><code>/api/identity/users/SearchUserByCodeAndType/{userCode}/true</code></td>
<td>校验优惠码/代理商是否存在</td>
<td><code>getAgentor()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">9</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/portal/queryRenewableTenant</code></td>
<td>查询可续费租户</td>
<td><code>queryTenant()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">10</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/portal/ExistsByOrganizationBRCI</code></td>
<td>校验商业登记号(BR)/公司编号(CI)是否已存在</td>
<td><code>checkBRCI()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">11</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/customer/portal/CheckEmailExists/{email}</code></td>
<td>校验管理员邮箱是否已存在</td>
<td><code>checkEmailExist()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">12</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/portal/CreateOrder</code></td>
<td>新租户下单</td>
<td><code>createOrder()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">13</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/portal/TenantRenewal</code></td>
<td>租户续费下单</td>
<td><code>tenantRenewal()</code></td>
<td><code>plan.service.ts</code></td>
</tr>
<tr>
<td class="num">14</td><td><span class="method post">POST</span></td>
<td><code>/api/amlPortal/Order/portal/PaymentWebhook</code></td>
<td>支付完成回调(作为 <code>notify_url</code> 传给 QFPay由其服务端回调<strong>非前端直接调用</strong></td>
<td></td>
<td><code>plan4.component.ts</code></td>
</tr>
</tbody>
</table>
</section>
<section id="auth">
<h2>3. 鉴权OAuth Token</h2>
<p><strong>端点 1</strong><code>POST /connect/token?__tenant=2C</code></p>
<ul class="tight">
<li>位置:<code>AmlQueryService</code>。在该服务<strong>构造函数中自动触发</strong>:当 <code>localStorage</code><code>aml_access_token</code> 时,调用 <code>getToken()</code> 并把返回的 <code>access_token</code> 写入 <code>localStorage</code></li>
<li>请求头:<code>Content-Type: application/x-www-form-urlencoded</code></li>
</ul>
<p>Body<code>x-www-form-urlencoded</code>,硬编码凭证):</p>
<table>
<thead><tr><th>字段</th><th></th></tr></thead>
<tbody>
<tr><td><code>grant_type</code></td><td><code>password</code></td></tr>
<tr><td><code>response_type</code></td><td><code>token</code></td></tr>
<tr><td><code>username</code></td><td><code>customer1</code></td></tr>
<tr><td><code>password</code></td><td><code>1qaz@WSX</code></td></tr>
<tr><td><code>scope</code></td><td><code>FX</code></td></tr>
<tr><td><code>client_id</code></td><td><code>customer1_client</code></td></tr>
<tr><td><code>client_secret</code></td><td><code>1qaz@WSX</code></td></tr>
</tbody>
</table>
<div class="callout warn">
<p>⚠️ 用户名/密码/client secret 以明文硬编码在前端源码中(<code>aml-query.service.ts:47-51</code>),属于公开网站内置的固定访客凭证。</p>
</div>
</section>
<section id="amlquery">
<h2>4. AML 查询模块aml-query</h2>
<p><strong>端点 2</strong><code>GET /api/aml/ConsumerPortal/GetPrice/{inputCode}</code></p>
<ul class="tight">
<li>调用方:<code>aml-query-layout.component.ts</code><code>ngOnInit</code> 时以 <code>getPrice('E')</code> 拉取价格)。</li>
<li>价格的真正计算逻辑在 <code>AmlQueryService.calculatePrice()</code>(本地计算,不走接口)。</li>
</ul>
<div class="callout note">
<p><code>aml-query/pages/result/result.component.ts</code>(评估报告页)当前使用<strong>本地 mock 数据 / <code>Math.random()</code></strong> 生成风险评估结果PDF 下载指向 <code>assets/sample-report.pdf</code><code>apiIntegration()</code> 弹窗里的 <code>api.credit-risk.example.com</code> 等仅为示例文案,<strong>不是真实后端调用</strong></p>
</div>
</section>
<section id="contact">
<h2>5. 联系我们模块contact</h2>
<p>调用方:<code>contact/pages/contact/contact.component.ts</code> 提交表单时触发。</p>
<ul class="tight">
<li><strong>端点 3</strong> <code>POST /api/amlPortal/customer/CreateFeedback</code>:保存反馈记录。
<br><span class="small">Body<code>CreateFeedbackParam</code><code>companyName</code><code>customerName</code><code>customerEmail</code><code>typeOfQuery</code><code>message</code><code>userHost</code><code>destEmail</code><code>emailSubject</code><code>emailBody</code></span>
</li>
<li><strong>端点 4</strong> <code>POST /api/aml/emailQueue/CreateEmailQueue</code>:进入邮件发送队列,被调用<strong>两次</strong>
<ul class="tight">
<li><code>sendContactEmail()</code> → 通知管理员(收件人 <code>environment.email.contact</code>)。</li>
<li><code>sendContactByReturnEmail()</code> → 给提交者发送回执邮件(收件人为用户填写邮箱)。</li>
</ul>
<span class="small">Body<code>CreateEmailQueueParam</code><code>subject</code><code>to</code><code>body</code>(HTML)、<code>businessType: 2100</code><code>businessId: ''</code></span>
</li>
</ul>
</section>
<section id="plans">
<h2>6. 套餐与下单模块plans / Order</h2>
<p><code>PlanService</code> 集中了套餐查询、校验与下单接口。各端点的页面调用关系:</p>
<table>
<thead><tr><th>端点</th><th>调用页面</th></tr></thead>
<tbody>
<tr><td><code>getAllPlanList()</code><span class="pill">端点 5</span></td><td><code>plans.component.ts</code>(进入套餐页 / 收到父窗口 <code>postMessage</code> 后)</td></tr>
<tr><td><code>getEditionList()</code><span class="pill">端点 6</span></td><td><code>plans.component.ts</code><code>plan3.component.ts</code></td></tr>
<tr><td><code>getCountryList()</code><span class="pill">端点 7</span></td><td><code>plans.component.ts</code><code>plan3.component.ts</code></td></tr>
<tr><td><code>getAgentor()</code><span class="pill">端点 8</span></td><td><code>plan3.component.ts</code>(输入优惠码校验)</td></tr>
<tr><td><code>queryTenant()</code><span class="pill">端点 9</span></td><td><code>plan1.component.ts</code><code>plan3.component.ts</code>(续费选租户)</td></tr>
<tr><td><code>checkBRCI()</code><span class="pill">端点 10</span></td><td><code>plan3.component.ts</code></td></tr>
<tr><td><code>checkEmailExist()</code><span class="pill">端点 11</span></td><td><code>plan1.component.ts</code><code>plan3.component.ts</code></td></tr>
<tr><td><code>createOrder()</code><span class="pill">端点 12</span></td><td><code>plan4.component.ts</code><code>type === 'new'</code></td></tr>
<tr><td><code>tenantRenewal()</code><span class="pill">端点 13</span></td><td><code>plan4.component.ts</code><code>type === 'renew'</code></td></tr>
</tbody>
</table>
<h3>关键请求体</h3>
<ul class="tight">
<li><strong><code>getAllPlanList</code></strong><code>GetPlanListParam</code><code>pageIndex: 0</code><code>pageSize: 9999</code><code>tag1List: ['B','jQ','j','AdlU','KYC']</code><code>tag2List</code>/<code>tag3List</code>(由调用方传入)、<code>agentUserId</code></li>
<li><strong><code>getCountryList</code></strong><code>{ typeCodes: ['COUNTRY'] }</code></li>
<li><strong><code>checkBRCI</code></strong>:通过 <code>params</code><code>br</code> / <code>ci</code>query string</li>
<li><strong><code>createOrder</code></strong><code>CreateOrderParam</code><code>planList[]</code><code>planId</code>/<code>planDetailId</code>/<code>pcs</code>)、<code>tenantName</code><code>tenantAdminEmail</code><code>jurisdiction</code><code>organizationBR</code><code>organizationCI</code><code>organizationReference</code><code>contactPerson</code><code>companyAddress</code><code>phone</code><code>effectiveStartTime</code><code>agentorId</code><code>isOfflinePayment: false</code><code>isAgentBehalf</code></li>
<li><strong><code>tenantRenewal</code></strong><code>TenantRenewalParam</code><code>targetTenantID</code><code>planList[]</code><code>agentorId</code><code>tenantAdminEmail</code><code>isOfflinePayment: false</code><code>isAgentBehalf</code></li>
</ul>
<h3>6.1 支付跳转QFPay 外部网关,不经过 HttpService</h3>
<p>位置:<code>plan4.component.ts</code><code>payFn()</code>。下单成功(<code>createOrder</code>/<code>tenantRenewal</code> 返回 <code>code === 0</code>)后:</p>
<ul class="tight">
<li>若订单 <code>paymentStatus === 200</code><code>txamt === 0</code> → 直接跳转 <code>plans-plus/payment-success</code></li>
<li>否则通过 <code>location.href</code> 重定向到 QFPay 收银台:<br>
<code>https://openapi-hk.qfapi.com/checkstand/#/?&lt;参数&gt;&amp;sign=&lt;sha256&gt;</code></li>
</ul>
<p>主要参数:</p>
<table>
<thead><tr><th>参数</th><th></th></tr></thead>
<tbody>
<tr><td><code>appcode</code></td><td><code>environment.qfpay.appcode</code>(固定值)</td></tr>
<tr><td><code>goods_name</code></td><td>下单返回的 <code>planName</code></td></tr>
<tr><td><code>out_trade_no</code></td><td>下单返回的 <code>orderCode</code></td></tr>
<tr><td><code>paysource</code></td><td><code>remotepay_checkout</code></td></tr>
<tr><td><code>return_url</code></td><td><code>{baseUrl}/plans-plus/payment-success</code></td></tr>
<tr><td><code>failed_url</code></td><td><code>{baseUrl}/plans-plus/payment-failure</code></td></tr>
<tr><td><code>notify_url</code></td><td><code>{apis.url}/api/amlPortal/Order/portal/PaymentWebhook</code>(端点 14QFPay 服务端回调后端)</td></tr>
<tr><td><code>txamt</code></td><td><code>apis.test ? 10 : planPrice * 100</code>(单位:分)</td></tr>
<tr><td><code>txcurrcd</code></td><td><code>HKD</code></td></tr>
<tr><td><code>sign</code></td><td><code>sha256(&lt;排序后的参数串&gt; + api_key)</code></td></tr>
</tbody>
</table>
</section>
<section id="prefix">
<h2>7. 端点前缀分布</h2>
<table>
<thead><tr><th>前缀</th><th>含义</th><th>涉及端点</th></tr></thead>
<tbody>
<tr><td><code>/connect/*</code></td><td>OpenIddict/IdentityServer OAuth</td><td>端点 1</td></tr>
<tr><td><code>/api/aml/*</code></td><td>AML 主模块(消费者查询、邮件队列)</td><td>端点 2、4</td></tr>
<tr><td><code>/api/amlPortal/*</code></td><td>AML 门户模块(客户、套餐、订单)</td><td>端点 3、5、6、7、9、10、11、12、13、14</td></tr>
<tr><td><code>/api/identity/*</code></td><td>ABP Identity用户</td><td>端点 8</td></tr>
</tbody>
</table>
</section>
<section id="flow">
<h2>8. 速查 · 典型流程(按调用次序)</h2>
<ul class="tight">
<li><strong>应用启动</strong><code>AmlQueryService</code> 构造 → 端点 1 取 token。</li>
<li><strong>AML 查询页加载</strong>:端点 2 取价格。</li>
<li><strong>联系页提交</strong>:端点 3 + 端点 4×2</li>
<li><strong>套餐购买流程</strong>
<ol>
<li>进入套餐页:端点 5、6、7。</li>
<li>填写租户/续费信息:端点 8优惠码、9租户、10BR/CI、11邮箱</li>
<li>确认下单:端点 12新购或 13续费</li>
<li>跳转 QFPay 支付 → 支付完成后 QFPay 回调端点 14。</li>
</ol>
</li>
</ul>
</section>
<footer>
justsolutionsWeb 后端 API 调用清单 · 基于源码静态分析整理
</footer>
</div>
</body>
</html>