feat:更换企业授权
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 3m41s

This commit is contained in:
luo
2026-06-22 10:36:33 +08:00
parent 866a2587b3
commit d3b5f55c64
34 changed files with 2478 additions and 3335 deletions

View File

@@ -0,0 +1,83 @@
## ADDED Requirements
### Requirement: Device Enterprise Authorization Filters
The device management page SHALL support filtering devices by effective enterprise authorization.
#### Scenario: Filter devices by authorized enterprise ID
- **GIVEN** 用户在设备管理搜索栏选择或输入企业 ID
- **WHEN** 用户执行搜索
- **THEN** 系统 MUST send `authorized_enterprise_id` to the device list API
- **AND** the list MUST only show devices currently authorized to that enterprise according to the backend response
#### Scenario: Filter devices by enterprise authorization status
- **GIVEN** 用户在设备管理搜索栏选择企业授权状态
- **WHEN** 用户选择“已授权”并搜索
- **THEN** 系统 MUST send `is_authorized_to_enterprise=true`
- **WHEN** 用户选择“未授权”并搜索
- **THEN** 系统 MUST send `is_authorized_to_enterprise=false`
- **AND** clearing the filter MUST omit `is_authorized_to_enterprise` from the request
### Requirement: Device Enterprise Authorization Actions
The device management page SHALL provide enterprise device authorization actions directly in the table header before the export button.
#### Scenario: Show permission-controlled device authorization buttons
- **GIVEN** 用户访问设备管理页面
- **WHEN** the table header renders
- **THEN** the “授权设备给企业” button MUST appear to the left of the “导出” button only when the user has permission `devices:authorize_enterprise`
- **AND** the “撤销设备授权” button MUST appear to the left of the “导出” button only when the user has permission `devices:recall_enterprise_authorization`
#### Scenario: Authorize selected devices to an enterprise
- **GIVEN** 用户在设备管理列表选中了设备
- **WHEN** 用户点击“授权设备给企业”并选择目标企业
- **AND** 用户选择 `list` 选取方式
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/allocate-devices`
- **AND** the request body MUST include `selection_type: 'list'`
- **AND** the request body MUST include selected row device identifiers as `device_nos`
- **AND** identifiers MUST come from list response fields such as `virtual_no` when available
- **AND** the UI MUST display success count, fail count, authorized devices, and failed item reasons from the response
- **AND** the device list MUST refresh after any successful authorization
#### Scenario: Authorize devices by filters
- **GIVEN** 用户打开“授权设备给企业” dialog
- **WHEN** 用户选择 `filter` 选取方式并确认
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/allocate-devices`
- **AND** the request body MUST include `selection_type: 'filter'`
- **AND** filter fields MUST be populated from the dialog filter form or current list filters when applicable, including available values such as `batch_no`, `shop_id`, and `virtual_no`
- **AND** empty filter fields MUST be omitted
#### Scenario: Authorize devices filter selects shop from cascade tree
- **GIVEN** 用户在“授权设备给企业” dialog 中选择 `filter` 选取方式
- **WHEN** the shop filter field renders
- **THEN** the field MUST use the same lazy cascade tree pattern as the “上级店铺” field in the add-shop dialog
- **AND** it MUST load children through `ShopService.getShopsCascade({ parent_id })`
- **AND** it MUST allow selecting any level with `checkStrictly: true`
- **AND** when submitted, the request MUST use the last selected cascade node ID as `shop_id`
- **AND** clearing the cascade field MUST omit `shop_id`
#### Scenario: Recall selected device authorization
- **GIVEN** 用户在设备管理列表选中了 currently enterprise-authorized devices
- **WHEN** 用户点击“撤销设备授权”
- **THEN** the UI MUST require a source enterprise ID unless it can safely infer one from `authorized_enterprise_id` or selected row data returned by the list API
- **WHEN** 用户 confirms `list` recall
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/recall-devices`
- **AND** the request body MUST include `selection_type: 'list'` and selected row device identifiers as `device_nos`
- **AND** the UI MUST display success count, fail count, and failed item reasons
- **AND** the device list MUST refresh after any successful recall
#### Scenario: Recall device authorization by filters
- **GIVEN** 用户打开“撤销设备授权” dialog
- **WHEN** 用户选择 `filter` selection mode and confirms
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/recall-devices`
- **AND** the request body MUST include `selection_type: 'filter'`
- **AND** filter fields MUST follow the contract in `docs/授权、回收.md`, including available values such as `batch_no` and `virtual_no`
- **AND** empty filter fields MUST be omitted

View File

@@ -0,0 +1,22 @@
## ADDED Requirements
### Requirement: Enterprise Customer Asset Authorization Entry Removal
The enterprise customer management page SHALL NOT provide enterprise card or device authorization entry points, and the replaced “企业卡管理” and “企业设备列表” pages SHALL be removed because these operations move to the IoT card management and device management list pages.
#### Scenario: Enterprise customer action column no longer shows asset authorization
- **GIVEN** 用户访问企业客户管理页面
- **WHEN** the action column renders for an enterprise customer row
- **THEN** the UI MUST NOT show “卡授权”
- **AND** the UI MUST NOT show “设备授权”
#### Scenario: Replaced enterprise asset authorization pages are removed
- **GIVEN** the application routes are registered
- **WHEN** route configuration is loaded
- **THEN** `/asset-management/enterprise-cards` MUST NOT be registered
- **AND** `/asset-management/enterprise-devices` MUST NOT be registered
- **AND** route aliases for `EnterpriseCards` and `EnterpriseDevices` MUST NOT remain
- **AND** `src/views/asset-management/enterprise-cards/index.vue` MUST be deleted
- **AND** `src/views/asset-management/enterprise-devices/index.vue` MUST be deleted

View File

@@ -0,0 +1,88 @@
## ADDED Requirements
### Requirement: IoT Card Enterprise Authorization Filters
The IoT card management page SHALL support filtering standalone cards by effective enterprise authorization.
#### Scenario: Filter cards by authorized enterprise ID
- **GIVEN** 用户在 IoT 卡管理搜索栏选择或输入企业 ID
- **WHEN** 用户执行搜索
- **THEN** 系统 MUST send `authorized_enterprise_id` to the IoT card list API
- **AND** the list MUST only show cards currently authorized to that enterprise according to the backend response
#### Scenario: Filter cards by enterprise authorization status
- **GIVEN** 用户在 IoT 卡管理搜索栏选择企业授权状态
- **WHEN** 用户选择“已授权”并搜索
- **THEN** 系统 MUST send `is_authorized_to_enterprise=true`
- **WHEN** 用户选择“未授权”并搜索
- **THEN** 系统 MUST send `is_authorized_to_enterprise=false`
- **AND** clearing the filter MUST omit `is_authorized_to_enterprise` from the request
### Requirement: IoT Card Enterprise Authorization Actions
The IoT card management page SHALL provide enterprise card authorization actions directly in the table header before the export button.
#### Scenario: Show permission-controlled card authorization buttons
- **GIVEN** 用户访问 IoT 卡管理页面
- **WHEN** the table header renders
- **THEN** the “授权卡给企业” button MUST appear to the left of the “导出” button only when the user has permission `iot_card:authorize_enterprise`
- **AND** the “回收卡授权” button MUST appear to the left of the “导出” button only when the user has permission `iot_card:recall_enterprise_authorization`
#### Scenario: Authorize selected cards to an enterprise
- **GIVEN** 用户在 IoT 卡管理列表选中了卡
- **WHEN** 用户点击“授权卡给企业”并选择目标企业
- **AND** 用户选择 `list` 选取方式
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/allocate-cards`
- **AND** the request body MUST include `selection_type: 'list'`
- **AND** the request body MUST include selected row ICCIDs as `iccids`
- **AND** the UI MUST display success count, fail count, and failed item reasons from the response
- **AND** the card list MUST refresh after any successful authorization
#### Scenario: Authorize cards by range
- **GIVEN** 用户打开“授权卡给企业” dialog
- **WHEN** 用户选择 `range` 选取方式并填写起始和结束 ICCID
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/allocate-cards`
- **AND** the request body MUST include `selection_type: 'range'`, `iccid_start`, `iccid_end`, and optional `remark`
#### Scenario: Authorize cards by filters
- **GIVEN** 用户打开“授权卡给企业” dialog
- **WHEN** 用户选择 `filter` 选取方式并确认
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/allocate-cards`
- **AND** the request body MUST include `selection_type: 'filter'`
- **AND** filter fields MUST be populated from the dialog filter form or current list filters when applicable, including available values such as `carrier_id`, `shop_id`, `shop_ids`, `batch_no`, and `iccid`
- **AND** empty filter fields MUST be omitted
#### Scenario: Authorize cards filter selects shop from cascade tree
- **GIVEN** 用户在“授权卡给企业” dialog 中选择 `filter` 选取方式
- **WHEN** the shop filter field renders
- **THEN** the field MUST use the same lazy cascade tree pattern as the “上级店铺” field in the add-shop dialog
- **AND** it MUST load children through `ShopService.getShopsCascade({ parent_id })`
- **AND** it MUST allow selecting any level with `checkStrictly: true`
- **AND** when submitted, the request MUST use the last selected cascade node ID as `shop_id`
- **AND** clearing the cascade field MUST omit `shop_id`
#### Scenario: Recall selected card authorization
- **GIVEN** 用户在 IoT 卡管理列表选中了 currently enterprise-authorized cards
- **WHEN** 用户点击“回收卡授权”
- **THEN** the UI MUST require a source enterprise ID unless it can safely infer one from `authorized_enterprise_id` or selected row data returned by the list API
- **WHEN** 用户 confirms `list` recall
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/recall-cards`
- **AND** the request body MUST include `selection_type: 'list'` and selected row ICCIDs as `iccids`
- **AND** the UI MUST display success count, fail count, failed item reasons, and any `recalled_devices`
- **AND** the card list MUST refresh after any successful recall
#### Scenario: Recall card authorization by range or filters
- **GIVEN** 用户打开“回收卡授权” dialog
- **WHEN** 用户选择 `range` or `filter` selection mode and confirms
- **THEN** 系统 MUST call `POST /api/admin/enterprises/{id}/recall-cards`
- **AND** the request body MUST match the selected mode using the contract in `docs/授权、回收.md`
- **AND** empty filter fields MUST be omitted