fix: bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m4s

This commit is contained in:
sexygoat
2026-05-11 15:51:50 +08:00
parent 766497dc31
commit 1990a3ccba
40 changed files with 832 additions and 127 deletions

View File

@@ -0,0 +1,45 @@
# Change: 更新批量套餐系列绑定接口契约
## Why
当前批量设置套餐系列的两个接口仍然主要按“显式已选列表”思路使用:
- `PATCH /api/admin/devices/series-binding`
- `PATCH /api/admin/iot-cards/series-binding`
但最新业务要求这两个接口的使用方式需要尽量对齐现有“批量分配”流程,支持按列表、范围、筛选条件三种方式选择目标资产,而不是只能依赖表格勾选后的 ID/ICCID 列表。
如果接口契约不统一,前端无法复用批量分配/批量回收的选择模型,设备管理和单卡管理也会继续保留两套不同的批量操作交互与参数拼装逻辑。
## What Changes
- 为设备批量套餐系列绑定接口补充与批量操作一致的目标选择模型:
- 支持 `selection_type=list|range|filter`
- 支持 `device_ids``virtual_no_start` / `virtual_no_end`、以及设备筛选条件三类入参
- 在设备筛选条件中的“店铺名称”选择器复用 `GET /api/admin/shops/cascade?exclude_self=true` 级联店铺查询接口,并按最后一级节点提交 `shop_id`
- 保留 `series_id=0` 作为清除套餐系列绑定的语义
- 保留旧版兼容:未传 `selection_type` 时按 `device_ids` 处理
- 为单卡批量套餐系列绑定接口补充与单卡批量分配/回收一致的目标选择模型:
- 支持 `selection_type=list|range|filter`
- 支持 `iccids``iccid_start` / `iccid_end`、以及单卡筛选条件三类入参
- 保留 `series_id=0` 作为清除套餐系列绑定的语义
- 保留旧版兼容:未传 `selection_type` 时按 `iccids` 处理
- 两个接口均保持现有 PATCH 路径不变
- 两个接口继续返回批处理结果摘要与失败明细,不在本提案中引入新的异步任务机制或新的资源路径
## Impact
- Affected specs:
- `device-series-binding`
- `iot-card-series-binding`
- Affected code:
- `src/api/modules/device.ts`
- `src/api/modules/card.ts`
- `src/api/modules/shop.ts`
- `src/types/api/device.ts`
- `src/types/api/card.ts`
- `src/views/asset-management/device-list/index.vue`
- `src/views/asset-management/iot-card-management/index.vue`
- Reference docs:
- `docs/mvp/批量分配套餐系列.md`
- `docs/1-24下午-新功能.md`

View File

@@ -0,0 +1,81 @@
## ADDED Requirements
### Requirement: Device Batch Series Binding Selection Contract
The system SHALL expose `PATCH /api/admin/devices/series-binding` using a batch target-selection contract consistent with other batch asset operations.
The request MUST include `series_id`. The request MAY omit `selection_type` only for backward compatibility, and in that case the system MUST treat the request as `selection_type=list`.
When `selection_type=list`, the request MUST use `device_ids` to identify the target devices and MUST support up to 1000 device IDs.
When `selection_type=range`, the request MUST use `virtual_no_start` and `virtual_no_end` to identify the target device range.
When `selection_type=filter`, the request MUST support selecting the full filtered result set by the same device query dimensions used for batch operations, including `virtual_no`, `device_name`, `device_type`, `manufacturer`, `batch_no`, `status`, `shop_id`, `shop_ids`, `filter_series_id`, `created_at_start`, and `created_at_end`.
`series_id=0` MUST mean clearing the current series binding instead of binding a new series.
#### Scenario: Bind device series by explicit device ID list
- **GIVEN** 用户已经明确选中了多台设备
- **WHEN** 系统调用 `PATCH /api/admin/devices/series-binding`
- **AND** 请求体包含 `selection_type="list"``series_id=12``device_ids=[1001,1002,1003]`
- **THEN** 系统 MUST 按 `device_ids` 对这批设备设置目标套餐系列绑定
#### Scenario: Bind device series by virtual number range
- **GIVEN** 用户希望按设备虚拟号区间批量设置套餐系列
- **WHEN** 系统调用 `PATCH /api/admin/devices/series-binding`
- **AND** 请求体包含 `selection_type="range"``series_id=12``virtual_no_start="V000001"``virtual_no_end="V000100"`
- **THEN** 系统 MUST 按虚拟号范围选出目标设备并设置套餐系列绑定
#### Scenario: Clear device series binding by filter result set
- **GIVEN** 用户希望对满足筛选条件的全部设备清除当前套餐系列关联
- **WHEN** 系统调用 `PATCH /api/admin/devices/series-binding`
- **AND** 请求体包含 `selection_type="filter"``series_id=0``status=1``shop_ids=[1,2]`
- **THEN** 系统 MUST 对完整筛选结果集执行清除操作
- **AND** 系统 MUST NOT 只处理当前分页内已渲染的设备
#### Scenario: Omitted selection type remains compatible with legacy callers
- **GIVEN** 旧版调用方只提交 `device_ids``series_id`
- **WHEN** 系统调用 `PATCH /api/admin/devices/series-binding`
- **AND** 请求体包含 `device_ids=[1001,1002]``series_id=8`
- **THEN** 系统 MUST 将该请求视为 `selection_type="list"`
- **AND** 系统 MUST 继续按 `device_ids` 处理目标设备
### Requirement: Device Batch Series Binding Shop Filter Selector
The system SHALL use the cascade shop query endpoint for the shop selector in device batch series binding filter mode.
When the user filters devices by shop in the batch series binding dialog, the UI MUST load shop options from `GET /api/admin/shops/cascade` with `exclude_self=true`, present them as a cascade selector, and submit the last selected node as `shop_id`.
#### Scenario: Shop filter uses cascade endpoint with self excluded
- **GIVEN** 用户打开“批量设置设备套餐系列绑定”弹窗
- **AND** 用户选择 `selection_type="filter"`
- **WHEN** 用户展开“店铺名称”选择器
- **THEN** 系统 MUST 调用 `GET /api/admin/shops/cascade`
- **AND** 请求参数 MUST 包含 `exclude_self=true`
- **AND** 系统 MUST 按级联树结构展示可选店铺
#### Scenario: Shop cascade selection submits the last selected node
- **GIVEN** 用户在“店铺名称”级联选择器中依次选择某条店铺路径
- **WHEN** 用户确认提交设备批量套餐系列绑定
- **THEN** 系统 MUST 取该路径最后一级节点的 ID 作为请求中的 `shop_id`
### Requirement: Device Batch Series Binding Result Summary
The system SHALL return a batch processing summary for device series binding requests.
The result data MUST include `success_count`, `fail_count`, and `failed_items`. Each entry in `failed_items` MUST include `device_id`, `virtual_no`, and `reason`.
#### Scenario: Partial device binding failure returns itemized reasons
- **GIVEN** 某次设备批量套餐系列绑定同时存在成功项和失败项
- **WHEN** 接口返回处理结果
- **THEN** 响应数据 MUST 包含成功数量和失败数量
- **AND** 每条失败明细 MUST 返回对应的 `device_id`
- **AND** 每条失败明细 MUST 返回对应的 `virtual_no`
- **AND** 每条失败明细 MUST 返回可展示的失败原因 `reason`

View File

@@ -0,0 +1,59 @@
## ADDED Requirements
### Requirement: IoT Card Batch Series Binding Selection Contract
The system SHALL expose `PATCH /api/admin/iot-cards/series-binding` using the same batch target-selection pattern as standalone card batch allocation and recall.
The request MUST include `series_id`. The request MAY omit `selection_type` only for backward compatibility, and in that case the system MUST treat the request as `selection_type=list`.
When `selection_type=list`, the request MUST use `iccids` to identify the target cards and MUST support up to 1000 ICCIDs.
When `selection_type=range`, the request MUST use `iccid_start` and `iccid_end` to identify the target ICCID range.
When `selection_type=filter`, the request MUST support the same primary filter dimensions used by standalone card batch allocation and recall, including `carrier_id`, `status`, and `batch_no`.
`series_id=0` MUST mean clearing the current series binding instead of binding a new series.
#### Scenario: Bind card series by ICCID list
- **GIVEN** 用户已经明确选中了多张单卡
- **WHEN** 系统调用 `PATCH /api/admin/iot-cards/series-binding`
- **AND** 请求体包含 `selection_type="list"``series_id=12``iccids=["8986000000000000001","8986000000000000002"]`
- **THEN** 系统 MUST 按 `iccids` 对这批卡设置目标套餐系列绑定
#### Scenario: Bind card series by ICCID range
- **GIVEN** 用户希望按 ICCID 号段批量设置套餐系列
- **WHEN** 系统调用 `PATCH /api/admin/iot-cards/series-binding`
- **AND** 请求体包含 `selection_type="range"``series_id=12``iccid_start="8986000000000000001"``iccid_end="8986000000000000100"`
- **THEN** 系统 MUST 按 ICCID 范围选出目标卡并设置套餐系列绑定
#### Scenario: Clear card series binding by standalone-card filters
- **GIVEN** 用户希望对满足单卡筛选条件的全部卡清除当前套餐系列关联
- **WHEN** 系统调用 `PATCH /api/admin/iot-cards/series-binding`
- **AND** 请求体包含 `selection_type="filter"``series_id=0``carrier_id=1``status=1`
- **THEN** 系统 MUST 对完整筛选结果集执行清除操作
- **AND** 系统 MUST NOT 只处理当前分页内已渲染的卡
#### Scenario: Omitted selection type remains compatible with legacy callers
- **GIVEN** 旧版调用方只提交 `iccids``series_id`
- **WHEN** 系统调用 `PATCH /api/admin/iot-cards/series-binding`
- **AND** 请求体包含 `iccids=["8986000000000000001"]``series_id=8`
- **THEN** 系统 MUST 将该请求视为 `selection_type="list"`
- **AND** 系统 MUST 继续按 `iccids` 处理目标卡
### Requirement: IoT Card Batch Series Binding Result Summary
The system SHALL return a batch processing summary for IoT card series binding requests.
The result data MUST include `success_count`, `fail_count`, and `failed_items`. Each entry in `failed_items` MUST include `iccid` and `reason`.
#### Scenario: Partial card binding failure returns itemized reasons
- **GIVEN** 某次单卡批量套餐系列绑定同时存在成功项和失败项
- **WHEN** 接口返回处理结果
- **THEN** 响应数据 MUST 包含成功数量和失败数量
- **AND** 每条失败明细 MUST 返回对应的 `iccid`
- **AND** 每条失败明细 MUST 返回可展示的失败原因 `reason`

View File

@@ -0,0 +1,8 @@
## 1. Implementation
- [x] 1.1 更新设备批量套餐系列绑定的请求类型与 API 方法签名,使其支持 `list` / `range` / `filter` 三种选择方式。
- [x] 1.2 更新单卡批量套餐系列绑定的请求类型与 API 方法签名,使其对齐单卡批量分配/回收的选择方式。
- [x] 1.3 调整设备管理页面的“批量设置套餐系列”交互,使其支持与批量操作一致的目标选择模型,并让店铺筛选复用 `shops/cascade?exclude_self=true` 级联接口。
- [x] 1.4 调整单卡管理页面的“批量设置套餐系列”交互,使其支持与批量分配/回收一致的目标选择模型。
- [ ] 1.5 验证 `series_id=0` 的批量清除和单条清除入口仍然可用。
- [ ] 1.6 验证未传 `selection_type` 的旧调用仍按列表模式兼容处理。