文件
hl-api-changelog/api-docs/supplier/供应商详情资源信息分页 API 接口规范-v1.0.html
lc 1eafac1a59
changelog-filename-gate / validate (push) Successful in 2s
docs(supplier): publish resource info page API for #6316
2026-08-25 13:10:51 +08:00

393 行
21 KiB
HTML

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>供应商详情资源信息分页 API 接口规范 · v1.0</title>
<style>
:root {
--ink: #172033;
--muted: #667085;
--line: #dfe5ef;
--soft: #f6f8fb;
--blue: #1677ff;
--blue-soft: #eaf3ff;
--green: #15803d;
--green-soft: #eaf8ef;
--amber: #a15c00;
--amber-soft: #fff7e6;
--red: #b42318;
--code: #101828;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
color: var(--ink);
background: #eef2f7;
font: 14px/1.65 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
}
.page {
width: min(1180px, calc(100% - 40px));
margin: 28px auto 64px;
background: #fff;
border: 1px solid var(--line);
border-radius: 16px;
box-shadow: 0 18px 48px rgba(16, 24, 40, .08);
overflow: hidden;
}
header {
padding: 42px 48px 34px;
color: #fff;
background: linear-gradient(135deg, #123d78, #1677ff 68%, #39a0ff);
}
header h1 { margin: 10px 0 8px; font-size: 30px; line-height: 1.3; }
header p { margin: 0; opacity: .9; }
.eyebrow { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; opacity: .78; }
.badges { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }
.badge {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 3px 10px;
border: 1px solid rgba(255,255,255,.35);
border-radius: 999px;
background: rgba(255,255,255,.14);
font-size: 12px;
}
main { padding: 12px 48px 54px; }
nav {
position: sticky;
top: 0;
z-index: 2;
display: flex;
gap: 20px;
margin: 0 -48px 24px;
padding: 13px 48px;
overflow-x: auto;
background: rgba(255,255,255,.96);
border-bottom: 1px solid var(--line);
backdrop-filter: blur(8px);
}
nav a { color: #344054; text-decoration: none; white-space: nowrap; font-size: 13px; }
nav a:hover { color: var(--blue); }
section { scroll-margin-top: 62px; padding-top: 22px; }
h2 { margin: 0 0 14px; font-size: 22px; }
h3 { margin: 24px 0 10px; font-size: 16px; }
p { margin: 8px 0; }
code {
padding: 2px 5px;
border-radius: 4px;
background: var(--soft);
color: #175cd3;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: .93em;
}
pre {
margin: 12px 0;
padding: 18px 20px;
overflow: auto;
border-radius: 10px;
background: var(--code);
color: #d1e9ff;
font: 12px/1.65 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
pre code { padding: 0; color: inherit; background: transparent; }
table { width: 100%; border-collapse: collapse; margin: 12px 0 18px; }
th, td { padding: 10px 12px; border: 1px solid var(--line); text-align: left; vertical-align: top; }
th { background: var(--soft); color: #344054; font-weight: 600; }
td:first-child code { white-space: nowrap; }
.endpoint {
display: flex;
align-items: center;
gap: 10px;
margin: 12px 0 16px;
padding: 14px 16px;
border: 1px solid #b9d8ff;
border-radius: 10px;
background: var(--blue-soft);
overflow-x: auto;
}
.method { padding: 4px 9px; border-radius: 6px; color: #fff; background: var(--blue); font-weight: 700; }
.path { font: 600 14px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; white-space: nowrap; }
.callout {
margin: 14px 0;
padding: 13px 15px;
border-left: 4px solid var(--blue);
border-radius: 6px;
background: var(--blue-soft);
}
.callout.ok { border-color: var(--green); background: var(--green-soft); }
.callout.warn { border-color: #f59e0b; background: var(--amber-soft); }
.callout strong { display: block; margin-bottom: 2px; }
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 16px 0; }
.card { padding: 15px; border: 1px solid var(--line); border-radius: 10px; background: #fff; }
.card b { display: block; margin-bottom: 4px; color: #344054; }
.card span { color: var(--muted); }
.status-ok { color: var(--green); font-weight: 700; }
.status-pending { color: var(--amber); font-weight: 700; }
ul, ol { padding-left: 22px; }
li + li { margin-top: 5px; }
footer { padding: 24px 48px; border-top: 1px solid var(--line); background: var(--soft); color: var(--muted); }
@media (max-width: 760px) {
.page { width: 100%; margin: 0; border: 0; border-radius: 0; }
header, main, footer { padding-left: 20px; padding-right: 20px; }
nav { margin-left: -20px; margin-right: -20px; padding-left: 20px; padding-right: 20px; }
.grid { grid-template-columns: 1fr; }
table { display: block; overflow-x: auto; white-space: nowrap; }
}
@media print {
@page { size: A4; margin: 14mm; }
body { background: #fff; }
.page { width: 100%; margin: 0; border: 0; box-shadow: none; }
header { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
nav { display: none; }
main { padding: 10px 0 20px; }
footer { padding: 16px 0 0; }
section { break-inside: avoid-page; }
pre { white-space: pre-wrap; word-break: break-word; }
a { color: inherit; text-decoration: none; }
}
</style>
</head>
<body>
<article class="page">
<header>
<div class="eyebrow">HL Supplier API · Admin</div>
<h1>供应商详情资源信息分页 API 接口规范</h1>
<p>v1.0 · Issue #6316 · 2026-08-25</p>
<div class="badges">
<span class="badge">已实现 · 可联调</span>
<span class="badge">后端 deployed</span>
<span class="badge">Gateway verified</span>
<span class="badge">前端 pending</span>
<span class="badge">只读接口</span>
</div>
</header>
<main>
<nav aria-label="文档目录">
<a href="#overview">概览</a>
<a href="#request">请求</a>
<a href="#response">响应</a>
<a href="#fields">字段</a>
<a href="#semantics">业务口径</a>
<a href="#errors">错误码</a>
<a href="#frontend">前端接入</a>
<a href="#verification">验证</a>
<a href="#rollback">撤回</a>
</nav>
<section id="overview">
<h2>1. 接口概览</h2>
<div class="endpoint"><span class="method">GET</span><span class="path">/admin/supplier/items/{supplierId}/resource-info/page</span></div>
<p>用于供应商管理详情页“资源信息”页签,分页展示该供应商当前有效关系对应的资源权威信息。关系来源为本系统 <code>supplier_resource_rel</code>;Resource 提供九类本地资源,Fleet 提供车辆资源。</p>
<div class="grid">
<div class="card"><b>实现状态</b><span class="status-ok">已实现 · 可联调</span></div>
<div class="card"><b>调用方</b><span>管理后台;前端接入待完成</span></div>
<div class="card"><b>数据副作用</b><span>无写库、Redis、MQ、配置或审计副作用</span></div>
</div>
<div class="callout ok"><strong>契约真相</strong>本文以合并提交 <code>4f032cd6f</code> 的 Controller、请求/响应 VO、Service 权限门禁及 TEST Gateway 验收为准。</div>
</section>
<section id="request">
<h2>2. 请求契约</h2>
<h3>2.1 路径与查询参数</h3>
<table>
<thead><tr><th>参数</th><th>位置</th><th>类型</th><th>必填</th><th>约束与默认值</th></tr></thead>
<tbody>
<tr><td><code>supplierId</code></td><td>path</td><td>string</td><td>是</td><td>正整数;Snowflake ID 必须按字符串传递</td></tr>
<tr><td><code>page</code></td><td>query</td><td>integer</td><td>否</td><td>默认 1,最小 1;公共分页兼容 <code>pageNo</code></td></tr>
<tr><td><code>pageSize</code></td><td>query</td><td>integer</td><td>否</td><td>默认 20,范围 1..100</td></tr>
<tr><td><code>resourceModule</code></td><td>query</td><td>string</td><td>否</td><td>为空查询全部;非空按关系冻结模块精确筛选</td></tr>
</tbody>
</table>
<h3>2.2 支持的资源模块</h3>
<table>
<thead><tr><th>编码</th><th>moduleName</th><th>数据归属</th></tr></thead>
<tbody>
<tr><td><code>SCENIC</code></td><td>景区管理</td><td>Resource</td></tr>
<tr><td><code>RESTAURANT</code></td><td>餐厅管理</td><td>Resource</td></tr>
<tr><td><code>SUPPLIES</code></td><td>备品管理</td><td>Resource</td></tr>
<tr><td><code>SUPPLIES_COMBO</code></td><td>组合配品</td><td>Resource</td></tr>
<tr><td><code>ACTIVITY</code></td><td>游玩项目管理</td><td>Resource</td></tr>
<tr><td><code>HOTEL</code></td><td>酒店管理</td><td>Resource</td></tr>
<tr><td><code>SERVICE</code></td><td>服务管理</td><td>Resource</td></tr>
<tr><td><code>COST_ITEM</code></td><td>额外成本</td><td>Resource</td></tr>
<tr><td><code>STAFF</code></td><td>服务人员管理</td><td>Resource</td></tr>
<tr><td><code>VEHICLE</code></td><td>车队管理-车队管理</td><td>Fleet(内部批量聚合)</td></tr>
</tbody>
</table>
<h3>2.3 调用示例</h3>
<pre><code>GET /admin/supplier/items/2091715622923657217/resource-info/page?page=1&amp;pageSize=20&amp;resourceModule=SCENIC
Authorization: Bearer &lt;有效管理端访问令牌&gt;</code></pre>
<div class="callout warn"><strong>权限是双门禁</strong>服务端仅允许 ADMIN、FINANCE、SUPER_ADMIN,并同时要求 <code>supplier:view</code> 与 <code>supplier:resource:view</code>。不能只通过隐藏页签代替服务端授权。</div>
</section>
<section id="response">
<h2>3. 响应结构</h2>
<p>统一返回 <code>Result&lt;PageResult&lt;SupplierResourceInfoRespVO&gt;&gt;</code>。业务失败通常仍是 HTTP 200,调用方必须检查 <code>code</code>、<code>success</code> 与 <code>message</code>。</p>
<pre><code>{
"code": 200,
"message": "success",
"success": true,
"data": {
"records": [
{
"relationId": "2091715622923657218",
"resourceModule": "SCENIC",
"moduleName": "景区管理",
"resourceId": "2091715622923657001",
"resourceName": "示例景区",
"coverUrl": null,
"city": "海拉尔",
"isCharged": true,
"isChargedName": "是",
"settleTypeCode": "CASH",
"settleTypeName": "现付",
"tags": [
{ "tagId": "2084636804090089473", "tagName": "自然风光", "tagColor": "#52C41A" }
],
"seasons": [
{ "seasonCode": "spring", "seasonName": "春" }
],
"statusCode": "ENABLED",
"statusName": "启用",
"enabled": true,
"resourceAvailable": true,
"updateTime": "2026-08-25 10:00:00"
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}</code></pre>
</section>
<section id="fields">
<h2>4. records[] 字段</h2>
<table>
<thead><tr><th>字段</th><th>类型</th><th>可空</th><th>说明</th></tr></thead>
<tbody>
<tr><td><code>relationId</code></td><td>string</td><td>否</td><td>供应商资源关系 ID</td></tr>
<tr><td><code>resourceModule</code></td><td>string</td><td>否</td><td>关系冻结的资源模块编码</td></tr>
<tr><td><code>moduleName</code></td><td>string</td><td>否</td><td>模块中文名</td></tr>
<tr><td><code>resourceId</code></td><td>string</td><td>否</td><td>资源 ID;资源缺失仍保留关系原值</td></tr>
<tr><td><code>resourceName</code></td><td>string</td><td>是</td><td>当前资源名称</td></tr>
<tr><td><code>coverUrl</code></td><td>string</td><td>是</td><td>当前有效封面 URL</td></tr>
<tr><td><code>city</code></td><td>string</td><td>是</td><td>城市展示值</td></tr>
<tr><td><code>isCharged</code></td><td>boolean</td><td>是</td><td>是否收费;不适用/未知为 null</td></tr>
<tr><td><code>isChargedName</code></td><td>string</td><td>是</td><td>是否收费中文名</td></tr>
<tr><td><code>settleTypeCode</code></td><td>string</td><td>是</td><td>结算方式编码</td></tr>
<tr><td><code>settleTypeName</code></td><td>string</td><td>是</td><td>结算方式中文名</td></tr>
<tr><td><code>tags</code></td><td>array</td><td>否</td><td>标签列表;无数据为 []</td></tr>
<tr><td><code>seasons</code></td><td>array</td><td>否</td><td>标准季节列表;无数据为 []</td></tr>
<tr><td><code>statusCode</code></td><td>string</td><td>是</td><td>资源当前权威状态编码</td></tr>
<tr><td><code>statusName</code></td><td>string</td><td>是</td><td>归一化状态中文名</td></tr>
<tr><td><code>enabled</code></td><td>boolean</td><td>是</td><td>归一化启用标记</td></tr>
<tr><td><code>resourceAvailable</code></td><td>boolean</td><td>否</td><td>false 表示关系保留但资源已删除/缺失</td></tr>
<tr><td><code>updateTime</code></td><td>string</td><td>是</td><td>资源本身更新时间;yyyy-MM-dd HH:mm:ss</td></tr>
</tbody>
</table>
<h3>4.1 子结构</h3>
<table>
<thead><tr><th>数组</th><th>字段</th><th>说明</th></tr></thead>
<tbody>
<tr><td><code>tags[]</code></td><td><code>tagId</code>、<code>tagName</code>、<code>tagColor</code></td><td>标签 ID 沿用各资源模块的字符串表达</td></tr>
<tr><td><code>seasons[]</code></td><td><code>seasonCode</code>、<code>seasonName</code></td><td>标准季节编码与中文名</td></tr>
</tbody>
</table>
</section>
<section id="semantics">
<h2>5. 业务口径</h2>
<ul>
<li>只读取当前有效的供应商资源关系,按关系 <code>update_time DESC, rel_id DESC</code> 稳定排序。</li>
<li>响应中的 <code>updateTime</code> 是资源主数据更新时间,不是关系更新时间。</li>
<li>标量字段不适用、未知或资源缺失时返回 <code>null</code>;<code>tags</code> 与 <code>seasons</code> 永远返回数组。</li>
<li>资源被软删除或不存在时不丢弃关系行:<code>resourceAvailable=false</code>,名称、状态、更新时间等当前资源字段为 null。</li>
<li>十类资源以本系统权威数据为准;展示形式可参考现有资源列表,但不要从参考图硬编码字段值或状态。</li>
<li>供应商处于草稿、审批中、合作中、暂停、黑名单、归档等任意生命周期状态时均可只读查询。</li>
<li>Fleet/字典依赖出现空响应、非成功、重复、缺失、额外或非法数据时返回 395039,不降级为部分成功。</li>
</ul>
<div class="callout"><strong>内部依赖说明</strong>Resource 通过内部 Token 调用 <code>POST /internal/fleet/vehicles/supplier-resource-info/batch</code> 聚合车辆信息。该路径不面向管理端,前端不得调用、转发或持有内部 Token。</div>
</section>
<section id="errors">
<h2>6. 错误码</h2>
<table>
<thead><tr><th>业务码</th><th>场景</th><th>管理端建议</th></tr></thead>
<tbody>
<tr><td><code>401</code></td><td>未认证或登录态失效</td><td>按统一登录续期/退出逻辑处理</td></tr>
<tr><td><code>400</code></td><td>supplierId、page 或 pageSize 等参数不合法</td><td>修正请求,不自动重试</td></tr>
<tr><td><code>395001</code></td><td>供应商不存在</td><td>关闭失效详情或刷新列表</td></tr>
<tr><td><code>395034</code></td><td>resourceModule 不受支持</td><td>仅使用本文十个稳定编码</td></tr>
<tr><td><code>395039</code></td><td>Fleet、字典或资源必要依赖不可用/响应不完整</td><td>提示稍后重试,不展示旧数据冒充成功</td></tr>
</tbody>
</table>
<pre><code>{
"code": 395034,
"message": "不支持的资源模块",
"data": null,
"success": false
}</code></pre>
</section>
<section id="frontend">
<h2>7. 管理端接入清单</h2>
<ol>
<li>在供应商详情“账号信息”页签后新增“资源信息”页签;仅在页签打开时加载数据。</li>
<li>建议列:资源名称(封面 + 名称)、资源模块、城市、是否收费、结算方式、标签、季节、状态、更新时间。</li>
<li>所有 ID 按字符串保存、传参和比较,禁止转换为 JavaScript number。</li>
<li>优先展示服务端中文字段;未知字段显示“—”,不得前端猜测状态或字典名称。</li>
<li><code>resourceAvailable=false</code> 时保留行并明确显示“资源已删除/不可用”。</li>
<li>模块筛选或页容量改变时回到第 1 页;pageSize 最大 100。</li>
<li>按 <code>code/success</code> 判断业务结果;不要仅依据 HTTP 200。</li>
<li>页签为只读展示,不增加绑定、改绑、解绑、启停或删除按钮。</li>
</ol>
<h3>7.1 建议验收场景</h3>
<table>
<thead><tr><th>场景</th><th>预期</th></tr></thead>
<tbody>
<tr><td>有资源关系</td><td>按分页显示,中文字段、数组、字符串 ID 正常</td></tr>
<tr><td>无资源关系</td><td>records=[]、total=0,不显示错误空态</td></tr>
<tr><td>按模块筛选</td><td>返回行的 resourceModule 全部等于筛选值</td></tr>
<tr><td>关系存在但资源缺失</td><td>保留关系行,resourceAvailable=false</td></tr>
<tr><td>无双权限/未登录</td><td>服务端拒绝,页面不泄露数据</td></tr>
<tr><td>依赖暂不可用</td><td>展示 395039 对应提示,不展示不完整成功页</td></tr>
</tbody>
</table>
</section>
<section id="verification">
<h2>8. 实现与验收状态</h2>
<ul>
<li><strong>代码:</strong>PR #6330 已合并 <code>dev-v3</code>,合并提交 <code>4f032cd6f6698607a2f1524437533d595975fc9a</code>。</li>
<li><strong>自动化:</strong>Resource 全量 2043 项零失败;Fleet 可运行全量 3853 项零失败;GatewayRouteAuditTest 3 项零失败;独立审计套件再次通过。</li>
<li><strong>TEST:</strong>任务 <code>cc1fe248</code>、<code>2697d374</code> 精确部署同一合并提交,Resource/Fleet 双实例与 Nacos 各 2 实例健康。</li>
<li><strong>真实 Gateway:</strong>有效管理端会话只读查询 3 个供应商,实际取得 1 条 SCENIC 关系;18 字段、字符串 ID、数组、时间和模块筛选均通过。</li>
<li><strong>负向:</strong>395001、395034、page/pageSize 参数 400 与未认证 401 均实测通过。</li>
<li><strong>车辆样本:</strong>TEST 当时无 VEHICLE 供应商关系;未伪造数据,跨服务路径由双服务真实部署健康与内部契约/批量/失败关闭测试覆盖。</li>
<li><strong>环境恢复:</strong>任务 <code>45e843e5</code>、<code>c05eda6e</code> 已回切包含本次合并的 <code>dev-v3</code>;Resource 8082/8182、Fleet 8087/8187 及 Nacos 各 2 个实例健康,临时部署分支已删除。</li>
</ul>
<div class="callout ok"><strong>数据清理</strong>TEST 验收全部为 GET 只读请求,没有创建或修改供应商、资源、数据库、Redis、MQ 或配置数据。</div>
</section>
<section id="rollback">
<h2>9. 撤回方案</h2>
<ol>
<li>从最新 <code>dev-v3</code> 创建回退分支,执行 <code>git revert -m 1 --no-edit 4f032cd6f6698607a2f1524437533d595975fc9a</code>,经独立 PR 合入。</li>
<li>依次重新构建并滚动部署 Resource、Fleet,分别保持双实例可用。</li>
<li>本次无数据库、Redis、MQ、Nacos 或其他配置变更,无需 DDL、DML、缓存清理、消息补偿或配置恢复,也无不可逆影响。</li>
<li>管理端停止调用新增 GET 和读取本次字段;既有供应商详情、账号和资源管理接口不受影响。</li>
<li>经 Gateway 复测新增路径撤回、既有供应商详情正常,并确认两服务双实例和 Nacos 健康。</li>
</ol>
</section>
</main>
<footer>
关联:Issue #6316 · PR #6330 · 后端负责人 @lc · 本文件为可离线、可打印单页接口规范。
</footer>
</article>
</body>
</html>