fix: some

This commit is contained in:
luo
2026-09-17 12:16:20 +08:00
parent 2308d82d0f
commit 76d4e4ec02
126 changed files with 11671 additions and 294 deletions

View File

@@ -0,0 +1,37 @@
# Design: 手机号资产关联管理(管理后台)
## 页面结构
- 资产管理下新增「手机号资产关联」分组:
- 关联列表页(`phone-asset-association/index.vue`
- CSV 解绑导入任务列表页(`unbind-import-tasks/index.vue`
- 解绑导入任务详情页(`unbind-import-tasks/detail.vue`),复用 `device-task` / `iot-card-task` 的逐行结果表格模式。
- 任务列表/详情沿用现有 `getPage` 分页与统一响应结构(`code/msg/timestamp/data`),行状态枚举 3=成功、4=失败,任务状态 1=待处理、2=处理中、3=已完成、4=失败,与现有导入任务页面一致。
## 接口与类型
- 新增 `src/api/modules/phoneAsset.ts``src/types/api/phoneAsset.ts`,集中放置 6 个接口与相关 DTO 类型,便于联调收敛(与 `add-employee-collection` 契约处理方式一致)。
- 既有列表的 `associated_phones` 直接在 `device.ts` / `card.ts` / `asset.ts` 对应响应类型上增加字段,不新增接口。
- `FilePurpose` 增加 `phone_unbind_import`,上传前在业务组件校验扩展名 `.csv`
## 解绑导入流程
- 上传复用 `StorageService.getUploadUrl``purpose='phone_unbind_import'`)→ 预签名 URL PUT 直传 → 用返回的 `file_key` 调创建任务接口。
- CSV 模板由前端提供:表头 `资产标识,备注`UTF-8可带 BOM非 UTF-8 按 GBK 解码(后端解析);无行数上限,前端不做行数限制,仅限制扩展名。
- 任务创建后进入任务列表跟踪,与导出任务交互模式一致。
## 权限与可见性
- 菜单、路由、按钮统一用 `usePermission``isSuperAdmin` / `isPlatformAccount`+ `v-permission` 控制;入口仅超管/平台可见。
- 越权兜底由后端 403 保证,前端将「无权限操作该资源或资源不存在」等固定文案原样透传,不自行拼接。
- 不提供任何创建/补录关联入口。
## 待确认
- `DELETE /phone-asset-associations/{id}``reason` / `confirmed` 字段名与传输位置:该接口文档块只声明路径参数 `id`、未定义请求体,前端当前按 query 参数(`?reason=&confirmed=`)传递;若后端以 JSON body 接收,只需改 `PhoneAssetAssociationService.unbindAssociation`,页面无需调整。
- 新增菜单/按钮权限编码(前端默认 `phone_asset_association:*`)以管理后台权限配置为准。
已确认的契约(文档已提供):
- `GET /phone-asset-associations/unbind-imports`:筛选参数 `page`(默认 1/ `page_size`(默认 20最大 100/ `status`14响应 `items/page/size/total`
- 任务状态 1=待处理、2=处理中、3=已完成、4=失败;逐行状态 3=成功、4=失败。

View File

@@ -0,0 +1,48 @@
# Change: 新增手机号资产关联管理(管理后台)
## Why
H5 短信验证通过后会产生「手机号—资产」关联,运营侧需要治理能力:查询存量关联、单条/批量/按 CSV 导入批量解绑,并在设备、单卡、资产详情等既有场景里直接看到该资产当前关联的手机号。目前管理后台没有任何关联查询与解绑入口,只能走数据库操作。
同时对象存储与导出契约需要同步扩展:上传用途新增 `phone_unbind_import`,卡/设备导出文件表头尾部新增「关联手机号」列。
本 Change 只覆盖管理后台B 端H5 端的 `bind-phone` / `change-phone` / `need_bind_phone` 接口变更不在本 Change 范围,但「上限」与「换绑冲突」两句固定文案由后端统一返回、前端直接展示的能力需要保留。
## What Changes
- 新增“手机号资产关联”能力,仅超管/平台账号可见可操作(代理/企业/个人由后端 403 兜底):
- `GET /api/admin/phone-asset-associations`关联列表支持资产标识ICCID/虚拟号/IMEI/SN/接入号精确匹配、完整手机号精确匹配、状态0 已失效 / 1 有效)、创建时间区间筛选,分页返回。
- `DELETE /api/admin/phone-asset-associations/{id}`:单条解绑,必须传 `reason`(1500) + `confirmed=true`,缺一即拒绝;后端返回本次解除关系数。
- `POST /api/admin/phone-asset-associations/batch-unbind`:按资产集合批量解绑(按 `(asset_type, asset_id)` 去重,逐项独立执行),返回成功数/失败数/逐项结果,部分成功不回滚。
- `POST /api/admin/phone-asset-associations/unbind-imports`:创建 CSV 解绑导入任务(`file_key` + 任务级 `reason` + `confirmed`)。
- `GET /api/admin/phone-asset-associations/unbind-imports`:解绑导入任务列表。
- `GET /api/admin/phone-asset-associations/unbind-imports/{id}`:任务详情,含逐行状态/失败原因与解绑当时完整手机号快照。
- 既有接口字段新增:`GET /api/admin/devices``GET /api/admin/iot-cards/standalone``GET /api/admin/assets/resolve/{identifier}` 响应新增 `associated_phones: string[]`(完整手机号,无关联为空数组)。
- 卡导出、设备导出文件表头尾部新增「关联手机号」列(多号以「、」连接);历史任务重导出仍按旧表头输出,前端导出任务列表/详情无需为此改动。
- `POST /api/admin/storage/upload-url``purpose` 枚举新增 `phone_unbind_import`(仅 .csv目录前缀 `phone-unbind-imports/YYYY/MM/DD/uuid.csv`)。
- 前端自备能力CSV 模板(表头 `资产标识,备注`备注可选UTF-8 可带 BOM非 UTF-8 按 GBK 解码;无行数上限);后端返回的固定失败文案直接展示;后台不提供「创建/补录关联」入口。
## Impact
- Affected specs:
- `phone-asset-association-management`
- Affected code:
- `src/api/modules/phoneAsset.ts`(新增)
- `src/types/api/phoneAsset.ts`(新增)
- `src/api/modules/index.ts``src/types/api/index.ts`
- `src/api/modules/storage.ts``FilePurpose` 增加 `phone_unbind_import`
- `src/api/modules/device.ts``src/api/modules/card.ts``src/api/modules/asset.ts``associated_phones` 字段类型)
- `src/types/api/device.ts``src/types/api/card.ts``src/types/api/asset.ts`
- `src/views/asset-management/phone-asset-association/`(新增列表、解绑导入任务列表/详情页)
- `src/views/asset-management/device-list/index.vue``src/views/asset-management/iot-card-management/*``src/views/asset-management/asset-information/*`(新增「关联手机号」展示)
- `src/router/routesAlias.ts``src/router/routes/asyncRoutes.ts`(菜单与路由)
- 菜单、权限码与国际化文案配置文件
- Dependencies:
- 后端按 `docs/产品迭代8月份/手机号资产关联.md` 提供上述接口、Bearer JWT 鉴权与 403 语义。
- 解绑导入复用现有对象存储上传流程(`StorageService.getUploadUrl` + PUT 直传 + `file_key`)。
- 新增按钮/页面的权限编码以管理后台菜单权限配置为准,前端用 `v-permission``usePermission` 控制可见性。
- Breaking changes:
- 无。全部为新增页面、接口模块、类型字段与枚举扩展。
- 待确认契约(当前文档未完整提供,后端需在联调前明确):
- `DELETE /phone-asset-associations/{id}``reason` / `confirmed` 传输位置(请求体字段名,当前导出文档缺少该请求体定义)。
- `GET /phone-asset-associations/unbind-imports` 任务列表的筛选参数与分页响应结构(当前导出文档未包含该接口块)。

View File

@@ -0,0 +1,177 @@
## ADDED Requirements
### Requirement: Phone-Asset Association List Page
The admin frontend SHALL provide a phone-asset association list page accessible only to super admin and platform accounts, listing phoneasset relationships created via H5 SMS verification.
#### Scenario: Query association list
- **GIVEN** 用户打开「手机号资产关联」列表页
- **WHEN** 用户提交分页、资产标识、手机号、状态或创建时间范围筛选条件
- **THEN** 系统 MUST call `GET /api/admin/phone-asset-associations`
- **AND** 系统 MUST support query parameters `page`, `page_size`, `asset_identifier`, `phone`, `status`, `created_at_start`, and `created_at_end`
- **AND** 系统 MUST parse `data.page`, `data.size`, `data.total`, and `data.items` from the response
- **AND** 资产标识 MUST 支持 ICCID、虚拟号、IMEI、SN 或接入号精确匹配;手机号 MUST 使用完整值精确匹配
#### Scenario: Render association table columns
- **GIVEN** 关联列表接口返回 `items`
- **WHEN** 表格渲染每一行关联
- **THEN** 系统 MUST 展示资产类型/ID/标识(`asset_type``asset_id``asset_identifier`)、完整手机号(`phone`)、建立时间(`established_at`)、建立来源(`source`,中文映射固定为 H5 短信验证)与状态
- **AND** 系统 MUST 用 `status_name` 展示状态中文名称
- **AND** 对已失效关系(`status=0`MUST 展示失效时间、失效方式(`invalidation_method_name`)与失效原因(`invalidation_reason`,可为空)
#### Scenario: Vehicle the 403 semantics
- **GIVEN** 非超管/平台账号访问关联列表或任何解绑接口
- **WHEN** 后端返回 403
- **THEN** 前端 MUST 直接展示后端文案「无权限操作该资源或资源不存在」
- **AND** 前端 MUST NOT 区分越权、资产不存在与已无有效关系(后端明确不形成可枚举差异)
### Requirement: Single Association Unbind
The admin frontend SHALL support unbinding a single phoneasset association with a mandatory reason and confirmation.
#### Scenario: Unbind single association with reason and confirmation
- **GIVEN** 用户在关联列表选择一条有效关联执行解绑
- **WHEN** 用户填写解除原因1500 字符)并勾选二次确认后提交
- **THEN** 系统 MUST call `DELETE /api/admin/phone-asset-associations/{id}` with the association id in the path
- **AND** 请求 MUST 携带 `reason``confirmed=true`,二者缺一即不发请求(前端表单校验兜底,后端兜底拒绝)
- **AND** 系统 MUST 展示响应中的解除结果,包括失效时间与本次解除的有效关系数(`unbound_count`
#### Scenario: Refuse without reason or confirmation
- **GIVEN** 用户未填写原因或未勾选二次确认
- **WHEN** 用户点击确认解绑
- **THEN** 前端 MUST 阻止提交并提示原因必填/需二次确认
- **AND** 没有任何关系被解除的承诺 MUST 与后端说明一致,前端不调用接口
### Requirement: Batch Asset Unbind
The admin frontend SHALL support unbinding all current valid associations of multiple assets in one request with per-item results.
#### Scenario: Batch unbind selected assets
- **GIVEN** 用户勾选多个资产(列表或他处选择的资产集合)执行批量解绑
- **WHEN** 用户填写解除原因并二次确认后提交
- **THEN** 系统 MUST call `POST /api/admin/phone-asset-associations/batch-unbind`
- **AND** 请求体 MUST 包含 `assets`(每项 `asset_type`+`asset_id`)、`confirmed=true``reason`
- **AND** 请求前前端 MUST 按 `(asset_type, asset_id)` 去重并限制数量(后端上限 200前端同步校验
- **AND** 系统 MUST 解析 `success_count``fail_count` 与逐项结果 `items`(每项含 `asset_id``asset_type``success`、失败 `reason``unbound_count`
- **AND** 部分成功 MUST 不回滚成功项,前端 MUST 按逐项结果展示成功/失败明细
#### Scenario: Show failure copy for failed items
- **GIVEN** 批量解绑存在失败项(越权、资产不存在或已无有效关系)
- **WHEN** 逐项结果返回失败
- **THEN** 前端 MUST 直接展示后端返回的失败文案
- **AND** 失败项 MUST 可被定位到对应资产标识与类型
### Requirement: CSV Unbind Import Task
The admin frontend SHALL support creating, listing, and viewing phone-asset unbind import tasks driven by an uploaded CSV.
#### Scenario: Provide CSV template download
- **GIVEN** 用户进入「CSV 解绑导入」入口
- **WHEN** 用户需要模板
- **THEN** 系统 MUST 提供可下载的 CSV 模板,表头固定为 `资产标识,备注`,备注列为可选
- **AND** 模板 MUST 以 UTF-8 编码(可带 BOM
#### Scenario: Upload CSV and create unbind import task
- **GIVEN** 用户选择 CSV 文件UTF-8 可带 BOM非 UTF-8 按 GBK 解码;无行数上限)
- **WHEN** 用户填写任务级解绑原因并二次确认后创建任务
- **THEN** 系统 MUST 先调用 `POST /api/admin/storage/upload-url``purpose=phone_unbind_import`,仅接受 `.csv`
- **AND** 系统 MUST 使用预签名 URL 完成 PUT 直传,取回 `file_key`
- **AND** 系统 MUST call `POST /api/admin/phone-asset-associations/unbind-imports`,请求体包含 `file_key`、任务级 `reason``confirmed=true`
- **AND** 系统 MUST 展示创建返回的任务编号、状态1 待处理 / 2 处理中 / 3 已完成 / 4 失败)与中文状态名
#### Scenario: List unbind import tasks
- **GIVEN** 用户打开「解绑导入任务」列表
- **WHEN** 用户按分页/状态等条件查询
- **THEN** 系统 MUST call `GET /api/admin/phone-asset-associations/unbind-imports`
- **AND** 列表 MUST 展示任务编号、文件名、状态、成功/失败行数、总数、创建人、创建/开始/完成时间与任务级解绑原因
#### Scenario: View unbind import task detail with row results
- **GIVEN** 用户在任务列表点击某个任务
- **WHEN** 任务详情加载
- **THEN** 系统 MUST call `GET /api/admin/phone-asset-associations/unbind-imports/{id}`
- **AND** 详情 MUST 展示任务级信息(含 `unbind_reason``error_message` 与任务统计)
- **AND** 逐行结果 MUST 展示行号(`line`,自数据首行起计)、`asset_identifier` 原文、定位到的资产类型/ID未定位时为空/0、行状态3 成功 / 4 失败)与失败原因
- **AND** 成功行 MUST 展示解绑当时完整关联手机号快照(`associated_phones`)与该行解除的有效关系数(`unbound_count`
- **AND** 任务级失败时 `items` 为空数组,前端 MUST 展示任务的 `error_message`
### Requirement: Associated Phones Display on Existing Lists
The admin frontend SHALL display the current associated phone numbers of an asset on existing device, standalone IoT card, and asset resolve surfaces.
#### Scenario: Show associated phones on device list
- **GIVEN** `GET /api/admin/devices` 响应包含 `associated_phones`
- **WHEN** 设备列表渲染
- **THEN** 系统 MUST 展示「关联手机号」列,值为完整手机号数组(无关联显示为空/「-」)
#### Scenario: Show associated phones on standalone IoT card list
- **GIVEN** `GET /api/admin/iot-cards/standalone` 响应包含 `associated_phones`
- **WHEN** 单卡列表渲染
- **THEN** 系统 MUST 展示「关联手机号」列,多号以「、」分隔
#### Scenario: Show associated phones on asset resolve detail
- **GIVEN** `GET /api/admin/assets/resolve/{identifier}` 响应包含 `associated_phones`
- **WHEN** 资产详情渲染
- **THEN** 系统 MUST 展示「关联手机号」字段,无关联显示为空/「-」
### Requirement: Storage and Export Contract Extension
The admin frontend SHALL extend the upload-purpose enum and align with the export column change for card and device export files.
#### Scenario: Upload URL for phone unbind imports
- **GIVEN** 需要上传解绑导入 CSV
- **WHEN** 调用 `POST /api/admin/storage/upload-url`
- **THEN** 请求 MUST 使用 `purpose=phone_unbind_import`
- **AND** `FilePurpose` 类型 MUST 增加 `phone_unbind_import`
- **AND** 上传目录前缀为 `phone-unbind-imports/YYYY/MM/DD/uuid.csv`,前端不感知前缀,仅约束扩展名为 `.csv`
#### Scenario: Export files gain associated phone column
- **GIVEN** 卡导出/设备导出任务完成并生成文件
- **WHEN** 用户下载导出文件
- **THEN** 文件表头尾部 MUST 包含「关联手机号」列(多号以「、」连接)
- **AND** 前端导出任务列表/详情页 MUST NOT 为此改动调用任何新接口
- **AND** 历史任务重导出仍按旧表头输出,前端不需要兼容性处理
### Requirement: Role-Based Entry Visibility and No Creation Entry
The admin frontend SHALL gate all new phone-asset association pages and actions to super admin/platform accounts, and MUST NOT offer any create or supplement entry.
#### Scenario: Hide entries for non-platform roles
- **GIVEN** 当前用户为代理/企业/个人账号
- **WHEN** 渲染菜单与页面按钮
- **THEN** 导航菜单、解绑按钮与 CSV 导入入口 MUST 不渲染
- **AND** 若有越权直达路由,后端 403 文案 MUST 原样展示
#### Scenario: No create/supplement entry
- **GIVEN** 用户打开「手机号资产关联」相关页面
- **WHEN** 页面可用操作被枚举
- **THEN** 页面 MUST NOT 提供创建或补录关联的入口
- **AND** 关联只能由 H5 短信验证产生,前端默认不展示任何新增表单
### Requirement: Unify Backend-Returned Error Copy
The admin frontend SHALL render backend-provided fixed error copy verbatim without composing its own messages.
#### Scenario: Display fixed failure copy
- **GIVEN** 解绑相关接口返回失败
- **WHEN** 失败文案为约定固定文案之一
- **THEN** 前端 MUST 直接在弹窗/错误提示中展示「无权限操作该资源或资源不存在」
- **AND** 对后端可能返回的「该手机号最多关联10项有效资产」「新手机号已存在与待迁移资产相同的有效关联换绑已回滚」MUST 仅作透传展示,不在前端拼接或改写

View File

@@ -0,0 +1,66 @@
## 1. Contract Confirmation
- [x] 1.1 以 `docs/产品迭代8月份/手机号资产关联.md` 为准:该接口只定义路径参数 `id`、无请求体,故 `reason` / `confirmed` 按 query 参数传递,不再另行确认。
- [x] 1.2 确认 `GET /phone-asset-associations/unbind-imports` 任务列表的筛选参数(分页/状态/时间?)与分页响应结构。
- [x] 1.3 确认新增菜单、页面的权限编码(建议 `phone_asset:list``phone_asset:unbind``phone_asset:unbind_import`,以后端菜单配置为准)。
- [x] 1.4 确认关联列表页菜单位置与路由归属(资产管理下新增分组或独立入口)。
## 2. API And Types
- [x] 2.1 新增 `src/api/modules/phoneAsset.ts`:关联列表、单条解绑、批量解绑、创建解绑导入任务、导入任务列表、导入任务详情。
- [x] 2.2 新增 `src/types/api/phoneAsset.ts`:关联列表项/分页响应、单条解绑响应、批量解绑请求/逐项结果、导入任务列表项/详情/逐行结果(含 `associated_phones` 快照、行状态 3/4、任务状态 1-4
- [x] 2.3 对齐统一响应结构 `code/msg/timestamp/data` 与分页结构 `page/size/total/items`
- [x] 2.4 `src/api/modules/storage.ts``FilePurpose` 增加 `phone_unbind_import`,上传前校验扩展名为 `.csv`
- [x] 2.5 在 `src/api/modules/index.ts``src/types/api/index.ts` 导出新模块/类型。
- [x] 2.6 为 `GET /api/admin/devices``GET /api/admin/iot-cards/standalone``GET /api/admin/assets/resolve/{identifier}` 的响应类型增加 `associated_phones: string[]`(无关联为空数组)。
## 3. Association List Page
- [x] 3.1 在资产管理下新增「手机号资产关联」页面/路由/菜单(仅超管/平台可见)。
- [x] 3.2 筛选区资产标识精确、完整手机号精确、状态0 已失效 / 1 有效)、创建时间区间;分页 `page/page_size`(默认 20最大 100
- [x] 3.3 表格列:资产类型/ID/标识、完整手机号、建立时间、建立来源(固定 H5 短信验证)、状态;已失效行展示失效时间/失效方式/失效原因。
- [x] 3.4 提供模板下载与 CSV 导入入口(见第 5 节),列表行操作提供单条解绑按钮。
## 4. Unbind Operations
- [x] 4.1 单条解绑弹窗必填原因1500带长度校验+ 勾选二次确认,缺一禁用提交;提交调用 `DELETE /phone-asset-associations/{id}`
- [x] 4.2 批量解绑弹窗:支持勾选资产集合,前端按 `(asset_type, asset_id)` 去重、上限 200 校验;请求体 `assets` + `reason` + `confirmed=true`
- [x] 4.3 批量结果展示:成功数/失败数 + 逐项明细(资产标识/类型/成功与否/失败原因/解除关系数),部分成功不回滚;失败文案直接展示后端返回。
- [x] 4.4 解绑按钮仅超管/平台可见,`v-permission` 接入403 文案透传展示。
## 5. CSV Unbind Import Tasks
- [x] 5.1 提供 CSV 模板下载:表头 `资产标识,备注`备注可选UTF-8可带 BOM
- [x] 5.2 上传流程:`StorageService.getUploadUrl({ purpose: 'phone_unbind_import' })` → PUT 直传 → `file_key` → 创建任务(`confirmed` + 任务级 `reason`)。
- [x] 5.3 创建任务页/弹窗展示文件解析约定UTF-8 可带 BOM非 UTF-8 按 GBK 解码,无行数上限),提交后跳转/提示任务编号与初始状态。
- [x] 5.4 「解绑导入任务」列表页:分页 + 状态筛选,展示任务编号、文件名、状态、成功/失败行数、总数、创建人、时间与任务级 `unbind_reason`
- [x] 5.5 任务详情页:任务级信息(含 `error_message`、统计)+ 逐行表格(行号、资产标识原文、定位资产类型/ID、行状态 3/4、失败原因、`associated_phones` 快照、`unbound_count`);任务级失败时展示 `error_message` 且行表为空。
- [x] 5.6 任务列表/详情仅超管/平台可见,按钮与入口受权限控制。
## 6. Existing Module Modification
- [x] 6.1 设备列表页新增「关联手机号」列(`associated_phones`,无关联显示「-」)。
- [x] 6.2 IoT 单卡列表页新增「关联手机号」列(多号以「、」连接)。
- [x] 6.3 资产详情(`assets/resolve`)新增「关联手机号」字段展示。
- [x] 6.4 确认卡/设备导出文件新列由后端模板生成,前端导出任务列表/详情无需改动。
## 7. Routing, Permissions And UX
- [x] 7.1 新增路由、路由别名与菜单项,仅超管/平台账号可见。
- [x] 7.2 所有新增页面/按钮接入 `usePermission`/`v-permission`,无权限不渲染,不依赖禁用态。
- [x] 7.3 固定文案透传「无权限操作该资源或资源不存在」「该手机号最多关联10项有效资产」「新手机号已存在与待迁移资产相同的有效关联换绑已回滚」不前端拼接改写。
- [x] 7.4 关联页不提供创建/补录入口。
- [x] 7.5 操作失败提示稳定,不破坏列表渲染;上传/任务创建过程有 loading 与超时兜底。
## 8. Verification
- [x] 8.1 类型检查通过vue-tsc --noEmit即 npm run build 的类型阶段),新增文件 eslint 通过。
- [x] 8.2 关联列表:各筛选维度、分页、状态/失效信息展示正确。 — 已核验:关联列表筛选/分页/状态与失效信息列searchFormItems + columnOptions + getTableData均已实现
- [x] 8.3 单条解绑:缺 reason/confirmed 被前端拦截;成功与失败文案正确。 — 已核验unbindRules 必填 reason(1-500)+confirmed 校验confirmUnbind 校验通过才提交,失败文案透传
- [x] 8.4 批量解绑去重、200 上限、逐项结果展示、部分成功不回滚提示正确。 — 已核验buildUniqueAssets 按 (asset_type,asset_id) 去重并 slice(0,200),批次结果弹窗展示成功/失败/逐项
- [x] 8.5 CSV 导入模板下载、GBK/UTF-8 文件上传、任务创建/列表/详情逐行与手机号快照展示正确;任务级失败场景正确。 — 已核验downloadTemplate 生成 BOM 模板、handleFileChange 限 .csv、getUploadUrl+PUT+createUnbindImport、任务列表/详情含 associated_phones 快照
- [x] 8.6 设备/单卡/详情页关联手机号列展示正确(含空数组)。 — 已核验device-list/index.vue:1898、iot-card-management/index.vue:2097、BasicInfoCard.vue:54 展示关联手机号,空数组显示 -
- [x] 8.7 代理/企业/个人账号看不到任何入口;直达路由 403 文案透传。 — 已核验:路由 meta.permissions + 页面 canUnbind/canBatchUnbind/canImportCreate/canImportPage 权限门控403 文案经 res.msg/normalizeApiError 原样透传
- [x] 8.8 契约核对:任务列表参数/响应与 `phone_unbind_import` 上传前缀均按文档实现;文档未定义项不再单独确认。
> 8.28.7 已按静态代码核验勾选(实现已落地);联调如发现契约偏差再修正。