fetch(modify):修改原来的bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m53s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m53s
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
The system currently has a basic device-import page but lacks comprehensive device management capabilities. Devices are critical assets that need to be tracked throughout their lifecycle - from import, allocation to shops, binding with SIM cards, to eventual recall. This change adds full device management to complement the existing card management features.
|
||||
|
||||
Key integration points:
|
||||
|
||||
- **Existing Card System**: Devices must bind with cards from the existing card-list module
|
||||
- **Shop System**: Devices are allocated to shops using the existing ShopService
|
||||
- **Object Storage**: Imports use the existing StorageService for large file uploads
|
||||
@@ -25,16 +26,19 @@ Key integration points:
|
||||
**Choice**: Use StorageService.getUploadUrl → uploadFile → DeviceService.importDevices
|
||||
|
||||
**Rationale**:
|
||||
|
||||
- Handles large files (thousands of devices) without timeout issues
|
||||
- Decouples upload from processing (backend can process asynchronously)
|
||||
- Consistent with modern cloud architecture patterns
|
||||
- Allows progress tracking through task management
|
||||
|
||||
**Alternatives Considered**:
|
||||
|
||||
- Direct multipart upload to backend (rejected: not scalable for large files)
|
||||
- Two-step process without pre-signed URL (rejected: less secure, more backend load)
|
||||
|
||||
**Implementation Notes**:
|
||||
|
||||
- Frontend only handles upload to object storage, not file parsing
|
||||
- Backend processes the file asynchronously and creates task records
|
||||
- Task management provides visibility into import progress
|
||||
@@ -44,16 +48,19 @@ Key integration points:
|
||||
**Choice**: Store binding with explicit slot_position (1-4) in device_cards table
|
||||
|
||||
**Rationale**:
|
||||
|
||||
- IoT devices have physical SIM slots that need explicit identification
|
||||
- Each device can have 1-4 slots (max_sim_slots)
|
||||
- One card can only bind to one device slot (enforced by backend)
|
||||
- Slot position is critical for physical device configuration
|
||||
|
||||
**Alternatives Considered**:
|
||||
|
||||
- Auto-assign slot positions (rejected: operators need to know physical slot numbers)
|
||||
- Allow one card to bind to multiple devices (rejected: not realistic for physical SIMs)
|
||||
|
||||
**Implementation Notes**:
|
||||
|
||||
- Device detail page shows a 4-slot grid (empty slots show "Bind Card" button)
|
||||
- Binding dialog requires explicit slot selection
|
||||
- Unbinding updates bound_card_count and frees the slot
|
||||
@@ -63,16 +70,19 @@ Key integration points:
|
||||
**Choice**: Extend existing task-management to support device import tasks
|
||||
|
||||
**Rationale**:
|
||||
|
||||
- Reuses existing task infrastructure
|
||||
- Provides consistent UX for all import operations
|
||||
- Avoids duplicate task tracking logic
|
||||
- Allows unified search/filter across task types
|
||||
|
||||
**Alternatives Considered**:
|
||||
|
||||
- Separate device task management page (rejected: creates UX fragmentation)
|
||||
- Embed task tracking only in device-import page (rejected: limited visibility)
|
||||
|
||||
**Implementation Notes**:
|
||||
|
||||
- Add task_type field to distinguish ICCID vs Device imports
|
||||
- Task detail page renders different content based on task_type
|
||||
- Device tasks show device_no and bound ICCIDs instead of just ICCID
|
||||
@@ -82,35 +92,41 @@ Key integration points:
|
||||
**Choice**: Show detailed results in dialog after batch allocation/recall
|
||||
|
||||
**Rationale**:
|
||||
|
||||
- Operations may partially succeed (some devices succeed, others fail)
|
||||
- Operators need to know exactly which devices failed and why
|
||||
- Allows retry of failed operations without re-selecting all devices
|
||||
|
||||
**Alternatives Considered**:
|
||||
|
||||
- Just show toast notification (rejected: insufficient detail for partial failures)
|
||||
- Navigate to separate results page (rejected: disrupts workflow)
|
||||
|
||||
**Implementation Notes**:
|
||||
|
||||
- Dialog shows summary: total, success count, failure count
|
||||
- Expandable table shows failed devices with error messages
|
||||
- Success closes dialog, partial failure keeps it open for review
|
||||
|
||||
### Decision 5: Component Reuse Strategy
|
||||
|
||||
**Choice**: Use existing Art* components (ArtTableFullScreen, ArtSearchBar, ArtTable, ArtButtonTable)
|
||||
**Choice**: Use existing Art\* components (ArtTableFullScreen, ArtSearchBar, ArtTable, ArtButtonTable)
|
||||
|
||||
**Rationale**:
|
||||
|
||||
- Maintains UI consistency across the application
|
||||
- Reduces development time
|
||||
- Leverages tested, stable components
|
||||
- Easier for users familiar with other pages
|
||||
|
||||
**Reference Implementation**:
|
||||
|
||||
- Device-list follows role/index.vue pattern
|
||||
- Device-detail follows card-list detail modal pattern
|
||||
- Search form follows enterprise-customer search pattern
|
||||
|
||||
**Implementation Notes**:
|
||||
|
||||
- Use CommonStatus for status values (ENABLED/DISABLED)
|
||||
- Use ElSwitch for status toggles
|
||||
- Use ArtButtonTable for action buttons
|
||||
@@ -119,6 +135,7 @@ Key integration points:
|
||||
## Architecture Diagrams
|
||||
|
||||
### Device Import Flow
|
||||
|
||||
```
|
||||
┌─────────┐ 1. Select CSV ┌──────────────┐
|
||||
│ Admin │ ──────────────────> │ device-import│
|
||||
@@ -155,6 +172,7 @@ Key integration points:
|
||||
```
|
||||
|
||||
### Device-Card Binding
|
||||
|
||||
```
|
||||
┌─────────┐ ┌──────────────┐
|
||||
│ Device │ ───── bound to ────> │ Card │
|
||||
@@ -175,12 +193,14 @@ Key integration points:
|
||||
## Data Flow
|
||||
|
||||
### Device List Page Load
|
||||
|
||||
1. User navigates to /asset-management/device-list
|
||||
2. Vue component mounts, calls DeviceService.getDevices(params)
|
||||
3. Backend returns paginated device list with bound_card_count
|
||||
4. Table renders with status switches and action buttons
|
||||
|
||||
### Batch Allocation Flow
|
||||
|
||||
1. User selects devices, clicks "Batch Allocate"
|
||||
2. Dialog opens with shop selector
|
||||
3. User selects shop, adds remarks, confirms
|
||||
@@ -189,6 +209,7 @@ Key integration points:
|
||||
6. Dialog shows summary and failed device details
|
||||
|
||||
### Card Binding Flow
|
||||
|
||||
1. User opens device detail page
|
||||
2. Clicks "Bind Card" for an empty slot
|
||||
3. Dialog shows card search and slot selection
|
||||
@@ -202,11 +223,13 @@ Key integration points:
|
||||
### Updating Existing Device Import Page
|
||||
|
||||
**Current State** (`src/views/batch/device-import/index.vue`):
|
||||
|
||||
- Uses ElUpload with drag-and-drop
|
||||
- Mock data for import records
|
||||
- No real API integration
|
||||
|
||||
**Migration Steps**:
|
||||
|
||||
1. Replace ElUpload with three-step upload logic
|
||||
- Add getUploadUrl call
|
||||
- Add uploadFile to object storage
|
||||
@@ -216,6 +239,7 @@ Key integration points:
|
||||
4. Update CSV format instructions
|
||||
|
||||
**Backward Compatibility**:
|
||||
|
||||
- This is a new feature area with no existing production data
|
||||
- No API breaking changes
|
||||
- UI changes are additive (improve existing page)
|
||||
@@ -223,16 +247,19 @@ Key integration points:
|
||||
### Extending Task Management
|
||||
|
||||
**Current State**:
|
||||
|
||||
- Only handles ICCID import tasks
|
||||
- Single task type rendering
|
||||
|
||||
**Migration Steps**:
|
||||
|
||||
1. Add task_type filter dropdown (default: show all)
|
||||
2. Add task_type badge in task list
|
||||
3. Task detail page: switch rendering based on task_type
|
||||
4. Add device-specific fields to task detail view
|
||||
|
||||
**Backward Compatibility**:
|
||||
|
||||
- Existing ICCID tasks continue to work unchanged
|
||||
- Filter defaults to showing all types
|
||||
- No database schema changes required (task_type already exists)
|
||||
@@ -240,23 +267,27 @@ Key integration points:
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Testing
|
||||
|
||||
- DeviceService API methods with mock responses
|
||||
- Form validation logic
|
||||
- Utility functions (formatters, validators)
|
||||
|
||||
### Integration Testing
|
||||
|
||||
- Device list search and pagination
|
||||
- Batch allocation with partial failures
|
||||
- Card binding/unbinding workflow
|
||||
- Import task creation and status tracking
|
||||
|
||||
### E2E Testing Scenarios
|
||||
|
||||
1. Import devices via CSV → verify task created → check task detail
|
||||
2. Search devices → select multiple → allocate to shop → verify shop assignment
|
||||
3. View device detail → bind card to slot 2 → unbind → verify empty slot
|
||||
4. Batch recall devices → verify shop cleared → check operation history
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- Device list pagination (default 20 per page)
|
||||
- Debounced search input (300ms delay)
|
||||
- Batch operation result pagination (if >100 results)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Why
|
||||
|
||||
当前系统只有设备导入页面,缺少完整的设备管理能力。需要提供设备的全生命周期管理,包括:
|
||||
|
||||
- 查看和搜索设备列表
|
||||
- 查看设备详情和绑定的卡信息
|
||||
- 管理设备与卡的绑定关系
|
||||
@@ -14,6 +15,7 @@
|
||||
## What Changes
|
||||
|
||||
### 新增功能
|
||||
|
||||
- **设备列表页面**: 支持多条件搜索、分页、列筛选、批量操作(分配、回收、删除)
|
||||
- **设备详情页面**: 展示设备基本信息、绑定的卡列表、操作历史
|
||||
- **卡绑定管理**: 在设备详情页绑定/解绑卡
|
||||
@@ -23,10 +25,12 @@
|
||||
- **导入任务管理**: 任务列表和详情查看
|
||||
|
||||
### API 集成
|
||||
|
||||
- DeviceService: 11个API接口
|
||||
- 类型定义: Device, DeviceBinding, ImportTask 等
|
||||
|
||||
### UI 组件
|
||||
|
||||
- 遵循现有组件模式 (ArtTableFullScreen, ArtSearchBar等)
|
||||
- 复用 CommonStatus 统一状态变量
|
||||
- 使用 ElDescriptions、ElTag、ElSwitch 等组件
|
||||
@@ -34,12 +38,14 @@
|
||||
## Impact
|
||||
|
||||
### 影响的功能模块
|
||||
|
||||
- **新增**: 资产管理 / 设备列表(主列表页)
|
||||
- **新增**: 资产管理 / 设备详情
|
||||
- **改进**: 批量操作 / 设备导入(改为对象存储模式)
|
||||
- **新增**: 批量操作 / 导入任务列表(独立页面)
|
||||
|
||||
### 影响的代码
|
||||
|
||||
- `src/api/modules/device.ts` (新增)
|
||||
- `src/types/api/device.ts` (新增)
|
||||
- `src/views/asset-management/device-list/index.vue` (新增)
|
||||
@@ -54,6 +60,7 @@
|
||||
- `src/types/api/index.ts` (导出 Device 类型)
|
||||
|
||||
### 依赖关系
|
||||
|
||||
- 依赖现有的 StorageService (对象存储)
|
||||
- 依赖现有的 ShopService (店铺选择)
|
||||
- 设备与卡的关联管理
|
||||
|
||||
@@ -7,140 +7,140 @@
|
||||
The system SHALL provide a searchable device list with multi-condition filtering, pagination, and batch operations.
|
||||
|
||||
#### Scenario: Search devices by multiple criteria
|
||||
**WHEN** an administrator enters search criteria (device number, device name, status, shop, batch number, device type, manufacturer, creation time range)
|
||||
**THEN** the system shall display only devices matching all specified criteria with pagination support
|
||||
|
||||
**WHEN** an administrator enters search criteria (device number, device name, status, shop, batch number, device type, manufacturer, creation time range) **THEN** the system shall display only devices matching all specified criteria with pagination support
|
||||
|
||||
#### Scenario: View device list with bound card count
|
||||
**WHEN** the device list is displayed
|
||||
**THEN** each device row shall show: ID, device number, device name, device model, device type, manufacturer, max slots, bound card count, status, shop, batch number, activation time, creation time
|
||||
|
||||
**WHEN** the device list is displayed **THEN** each device row shall show: ID, device number, device name, device model, device type, manufacturer, max slots, bound card count, status, shop, batch number, activation time, creation time
|
||||
|
||||
#### Scenario: Toggle device status
|
||||
**WHEN** an administrator clicks the status switch for a device
|
||||
**THEN** the system shall update the device status between ENABLED and DISABLED and refresh the display
|
||||
|
||||
**WHEN** an administrator clicks the status switch for a device **THEN** the system shall update the device status between ENABLED and DISABLED and refresh the display
|
||||
|
||||
#### Scenario: Delete a device
|
||||
**WHEN** an administrator clicks the delete button and confirms the deletion
|
||||
**THEN** the system shall delete the device and refresh the list
|
||||
|
||||
**WHEN** an administrator clicks the delete button and confirms the deletion **THEN** the system shall delete the device and refresh the list
|
||||
|
||||
#### Scenario: Select multiple devices for batch operations
|
||||
**WHEN** an administrator checks multiple device rows
|
||||
**THEN** the system shall enable batch operation buttons (Allocate, Recall) and track selected device IDs
|
||||
|
||||
**WHEN** an administrator checks multiple device rows **THEN** the system shall enable batch operation buttons (Allocate, Recall) and track selected device IDs
|
||||
|
||||
### Requirement: Device Detail Viewing
|
||||
|
||||
The system SHALL display comprehensive device information including basic properties and bound SIM cards.
|
||||
|
||||
#### Scenario: View device basic information
|
||||
**WHEN** an administrator navigates to a device detail page
|
||||
**THEN** the system shall display all device properties in a descriptive layout: device number, name, model, type, manufacturer, max SIM slots, status, shop, batch number, activation time, creation time
|
||||
|
||||
**WHEN** an administrator navigates to a device detail page **THEN** the system shall display all device properties in a descriptive layout: device number, name, model, type, manufacturer, max SIM slots, status, shop, batch number, activation time, creation time
|
||||
|
||||
#### Scenario: View bound SIM cards with slot positions
|
||||
**WHEN** viewing device details
|
||||
**THEN** the system shall display a table of bound cards showing: slot position (1-4), ICCID, phone number, carrier, card status, and binding time
|
||||
|
||||
**WHEN** viewing device details **THEN** the system shall display a table of bound cards showing: slot position (1-4), ICCID, phone number, carrier, card status, and binding time
|
||||
|
||||
#### Scenario: Empty slots display
|
||||
**WHEN** a device has fewer than max_sim_slots cards bound
|
||||
**THEN** empty slot positions shall be clearly indicated with "Bind Card" action buttons
|
||||
|
||||
**WHEN** a device has fewer than max_sim_slots cards bound **THEN** empty slot positions shall be clearly indicated with "Bind Card" action buttons
|
||||
|
||||
### Requirement: Device-Card Binding Management
|
||||
|
||||
The system SHALL allow administrators to bind and unbind SIM cards to specific device slots.
|
||||
|
||||
#### Scenario: Bind a card to a device slot
|
||||
**WHEN** an administrator selects a card and slot position (1-4) in the binding dialog
|
||||
**THEN** the system shall create the binding, update the bound card count, and refresh the card list
|
||||
|
||||
**WHEN** an administrator selects a card and slot position (1-4) in the binding dialog **THEN** the system shall create the binding, update the bound card count, and refresh the card list
|
||||
|
||||
#### Scenario: Prevent duplicate slot binding
|
||||
**WHEN** an administrator attempts to bind a card to an already occupied slot
|
||||
**THEN** the system shall show an error message and prevent the binding
|
||||
|
||||
**WHEN** an administrator attempts to bind a card to an already occupied slot **THEN** the system shall show an error message and prevent the binding
|
||||
|
||||
#### Scenario: Unbind a card from device
|
||||
**WHEN** an administrator clicks unbind for a bound card and confirms
|
||||
**THEN** the system shall remove the binding, decrement the bound card count, and refresh the card list
|
||||
|
||||
**WHEN** an administrator clicks unbind for a bound card and confirms **THEN** the system shall remove the binding, decrement the bound card count, and refresh the card list
|
||||
|
||||
#### Scenario: Validate slot range
|
||||
**WHEN** an administrator selects a slot position
|
||||
**THEN** the system shall only allow values from 1 to the device's max_sim_slots value
|
||||
|
||||
**WHEN** an administrator selects a slot position **THEN** the system shall only allow values from 1 to the device's max_sim_slots value
|
||||
|
||||
### Requirement: Batch Device Allocation
|
||||
|
||||
The system SHALL support batch allocation of devices to shops with result tracking.
|
||||
|
||||
#### Scenario: Allocate multiple devices to a shop
|
||||
**WHEN** an administrator selects multiple devices, chooses a target shop, adds optional remarks, and confirms allocation
|
||||
**THEN** the system shall allocate all selected devices to the shop and display success/failure results for each device
|
||||
|
||||
**WHEN** an administrator selects multiple devices, chooses a target shop, adds optional remarks, and confirms allocation **THEN** the system shall allocate all selected devices to the shop and display success/failure results for each device
|
||||
|
||||
#### Scenario: Show allocation results
|
||||
**WHEN** the batch allocation completes
|
||||
**THEN** the system shall display: total count, success count, failure count, and detailed failure reasons for each failed device
|
||||
|
||||
**WHEN** the batch allocation completes **THEN** the system shall display: total count, success count, failure count, and detailed failure reasons for each failed device
|
||||
|
||||
#### Scenario: Prevent allocation of already allocated devices
|
||||
**WHEN** the batch allocation includes devices already allocated to a shop
|
||||
**THEN** the system shall show warnings for those devices but proceed with allocating unallocated devices
|
||||
|
||||
**WHEN** the batch allocation includes devices already allocated to a shop **THEN** the system shall show warnings for those devices but proceed with allocating unallocated devices
|
||||
|
||||
### Requirement: Batch Device Recall
|
||||
|
||||
The system SHALL support batch recall of devices from shops with result tracking.
|
||||
|
||||
#### Scenario: Recall multiple devices
|
||||
**WHEN** an administrator selects multiple devices, adds optional remarks, and confirms recall
|
||||
**THEN** the system shall recall all selected devices from their shops and display success/failure results
|
||||
|
||||
**WHEN** an administrator selects multiple devices, adds optional remarks, and confirms recall **THEN** the system shall recall all selected devices from their shops and display success/failure results
|
||||
|
||||
#### Scenario: Show recall results
|
||||
**WHEN** the batch recall completes
|
||||
**THEN** the system shall display: total count, success count, failure count, and detailed failure reasons for each failed device
|
||||
|
||||
**WHEN** the batch recall completes **THEN** the system shall display: total count, success count, failure count, and detailed failure reasons for each failed device
|
||||
|
||||
#### Scenario: Prevent recall of unallocated devices
|
||||
**WHEN** the batch recall includes devices not allocated to any shop
|
||||
**THEN** the system shall show warnings for those devices but proceed with recalling allocated devices
|
||||
|
||||
**WHEN** the batch recall includes devices not allocated to any shop **THEN** the system shall show warnings for those devices but proceed with recalling allocated devices
|
||||
|
||||
### Requirement: Device Import via Object Storage
|
||||
|
||||
The system SHALL support CSV-based device import using a three-step object storage upload process.
|
||||
|
||||
#### Scenario: Import devices with three-step process
|
||||
**WHEN** an administrator uploads a CSV file
|
||||
**THEN** the system shall:
|
||||
|
||||
**WHEN** an administrator uploads a CSV file **THEN** the system shall:
|
||||
|
||||
1. Get upload URL from StorageService.getUploadUrl
|
||||
2. Upload file to object storage using StorageService.uploadFile
|
||||
3. Call DeviceService.importDevices with the file_key
|
||||
**AND** display the task number for tracking
|
||||
3. Call DeviceService.importDevices with the file_key **AND** display the task number for tracking
|
||||
|
||||
#### Scenario: Validate CSV format
|
||||
**WHEN** the CSV file is uploaded
|
||||
**THEN** the system shall validate the presence of required columns: device_no, device_name, device_model, device_type, max_sim_slots, manufacturer
|
||||
|
||||
**WHEN** the CSV file is uploaded **THEN** the system shall validate the presence of required columns: device_no, device_name, device_model, device_type, max_sim_slots, manufacturer
|
||||
|
||||
#### Scenario: Import devices with card bindings
|
||||
**WHEN** the CSV includes iccid_1, iccid_2, iccid_3, iccid_4 columns
|
||||
**THEN** the system shall attempt to bind the specified cards to slots 1-4 respectively during import
|
||||
|
||||
**WHEN** the CSV includes iccid_1, iccid_2, iccid_3, iccid_4 columns **THEN** the system shall attempt to bind the specified cards to slots 1-4 respectively during import
|
||||
|
||||
#### Scenario: Handle import errors
|
||||
**WHEN** the import process encounters errors (duplicate device_no, invalid ICCID, etc.)
|
||||
**THEN** the system shall record failed rows in the import task with detailed error messages
|
||||
|
||||
**WHEN** the import process encounters errors (duplicate device_no, invalid ICCID, etc.) **THEN** the system shall record failed rows in the import task with detailed error messages
|
||||
|
||||
### Requirement: Import Task Management
|
||||
|
||||
The system SHALL track device import tasks with detailed status and record information.
|
||||
|
||||
#### Scenario: List import tasks with type filter
|
||||
**WHEN** an administrator views the task management page
|
||||
**THEN** the system shall display both ICCID import tasks and Device import tasks with a type filter dropdown
|
||||
|
||||
**WHEN** an administrator views the task management page **THEN** the system shall display both ICCID import tasks and Device import tasks with a type filter dropdown
|
||||
|
||||
#### Scenario: View device import task details
|
||||
**WHEN** an administrator clicks on a device import task
|
||||
**THEN** the system shall display: task ID, status, total count, success count, failed count, skipped count, created time, completed time
|
||||
|
||||
**WHEN** an administrator clicks on a device import task **THEN** the system shall display: task ID, status, total count, success count, failed count, skipped count, created time, completed time
|
||||
|
||||
#### Scenario: View successful import records
|
||||
**WHEN** viewing a device import task detail
|
||||
**THEN** the system shall show a table of successfully imported devices with: device_no, device_name, bound ICCIDs
|
||||
|
||||
**WHEN** viewing a device import task detail **THEN** the system shall show a table of successfully imported devices with: device_no, device_name, bound ICCIDs
|
||||
|
||||
#### Scenario: View failed import records
|
||||
**WHEN** viewing a device import task detail with failures
|
||||
**THEN** the system shall show a table of failed records with: row number, device_no, failure reason
|
||||
|
||||
**WHEN** viewing a device import task detail with failures **THEN** the system shall show a table of failed records with: row number, device_no, failure reason
|
||||
|
||||
#### Scenario: Navigate from import page to task detail
|
||||
**WHEN** a device import completes successfully
|
||||
**THEN** the system shall display the task number with a link to view task details
|
||||
|
||||
**WHEN** a device import completes successfully **THEN** the system shall display the task number with a link to view task details
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
@@ -151,9 +151,9 @@ Task management SHALL support multiple task types including ICCID import and Dev
|
||||
**Previous behavior**: Task management only supported ICCID import tasks.
|
||||
|
||||
#### Scenario: Filter tasks by type
|
||||
**WHEN** an administrator selects a task type filter (ICCID Import / Device Import)
|
||||
**THEN** the system shall display only tasks of the selected type
|
||||
|
||||
**WHEN** an administrator selects a task type filter (ICCID Import / Device Import) **THEN** the system shall display only tasks of the selected type
|
||||
|
||||
#### Scenario: Display task type badges
|
||||
**WHEN** viewing the task list
|
||||
**THEN** each task shall display a type badge indicating whether it's an ICCID import or Device import task
|
||||
|
||||
**WHEN** viewing the task list **THEN** each task shall display a type badge indicating whether it's an ICCID import or Device import task
|
||||
|
||||
@@ -15,18 +15,21 @@
|
||||
新增企业设备授权管理功能,包括:
|
||||
|
||||
### 类型定义
|
||||
|
||||
- 新增 `src/types/api/enterpriseDevice.ts` 文件
|
||||
- 定义设备列表项、查询参数、分页结果类型
|
||||
- 定义授权/撤销请求和响应类型
|
||||
- 在 `src/types/api/index.ts` 中导出新类型
|
||||
|
||||
### API 服务层
|
||||
|
||||
- 扩展 `EnterpriseService` 类,新增3个方法:
|
||||
- `allocateDevices(enterpriseId, data)` - POST 授权设备
|
||||
- `getEnterpriseDevices(enterpriseId, params)` - GET 设备列表
|
||||
- `recallDevices(enterpriseId, data)` - POST 撤销授权
|
||||
|
||||
### 视图层
|
||||
|
||||
- 新增 `src/views/asset-management/enterprise-devices/index.vue` 页面
|
||||
- 实现设备列表展示 (表格、分页、搜索)
|
||||
- 实现授权设备对话框 (支持批量输入设备号)
|
||||
@@ -34,10 +37,12 @@
|
||||
- 实现操作结果展示 (成功/失败统计)
|
||||
|
||||
### 路由配置
|
||||
|
||||
- 在 `src/router/routesAlias.ts` 添加路由别名
|
||||
- 在 `src/router/routes/asyncRoutes.ts` 的资产管理模块下添加子路由
|
||||
|
||||
### 国际化
|
||||
|
||||
- 在 `src/locales/langs/zh.json` 和 `en.json` 添加中英文翻译
|
||||
- 包含菜单、表单、表格、对话框、提示消息等所有文案
|
||||
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
#### Scenario: 定义企业设备列表项类型
|
||||
|
||||
**Given** 需要展示企业设备列表
|
||||
**When** 定义 `EnterpriseDeviceItem` 接口
|
||||
**Then** 接口必须包含以下字段:
|
||||
**Given** 需要展示企业设备列表 **When** 定义 `EnterpriseDeviceItem` 接口 **Then** 接口必须包含以下字段:
|
||||
|
||||
- `device_id: number` - 设备ID
|
||||
- `device_no: string` - 设备号
|
||||
- `device_name: string` - 设备名称
|
||||
@@ -24,52 +23,49 @@
|
||||
|
||||
#### Scenario: 定义设备列表查询参数
|
||||
|
||||
**Given** 需要查询和搜索企业设备
|
||||
**When** 定义 `EnterpriseDeviceListParams` 接口
|
||||
**Then** 接口必须包含以下可选字段:
|
||||
**Given** 需要查询和搜索企业设备 **When** 定义 `EnterpriseDeviceListParams` 接口 **Then** 接口必须包含以下可选字段:
|
||||
|
||||
- `page?: number` - 页码
|
||||
- `page_size?: number` - 每页数量
|
||||
- `device_no?: string` - 设备号模糊搜索
|
||||
|
||||
#### Scenario: 定义授权设备请求类型
|
||||
|
||||
**Given** 需要授权设备给企业
|
||||
**When** 定义 `AllocateDevicesRequest` 接口
|
||||
**Then** 接口必须包含:
|
||||
**Given** 需要授权设备给企业 **When** 定义 `AllocateDevicesRequest` 接口 **Then** 接口必须包含:
|
||||
|
||||
- `device_nos: string[]` - 设备号列表 (nullable, 最多100个)
|
||||
- `remark?: string` - 授权备注
|
||||
|
||||
#### Scenario: 定义授权设备响应类型
|
||||
|
||||
**Given** 授权操作需要返回详细结果
|
||||
**When** 定义 `AllocateDevicesResponse` 接口
|
||||
**Then** 接口必须包含:
|
||||
**Given** 授权操作需要返回详细结果 **When** 定义 `AllocateDevicesResponse` 接口 **Then** 接口必须包含:
|
||||
|
||||
- `success_count: number` - 成功数量
|
||||
- `fail_count: number` - 失败数量
|
||||
- `authorized_devices: AuthorizedDeviceItem[]` - 已授权设备列表 (nullable)
|
||||
- `failed_items: FailedDeviceItem[]` - 失败项列表 (nullable)
|
||||
|
||||
**And** `AuthorizedDeviceItem` 包含:
|
||||
|
||||
- `device_id: number` - 设备ID
|
||||
- `device_no: string` - 设备号
|
||||
- `card_count: number` - 绑定卡数量
|
||||
|
||||
**And** `FailedDeviceItem` 包含:
|
||||
|
||||
- `device_no: string` - 设备号
|
||||
- `reason: string` - 失败原因
|
||||
|
||||
#### Scenario: 定义撤销授权请求类型
|
||||
|
||||
**Given** 需要撤销设备授权
|
||||
**When** 定义 `RecallDevicesRequest` 接口
|
||||
**Then** 接口必须包含:
|
||||
**Given** 需要撤销设备授权 **When** 定义 `RecallDevicesRequest` 接口 **Then** 接口必须包含:
|
||||
|
||||
- `device_nos: string[]` - 设备号列表 (nullable, 最多100个)
|
||||
|
||||
#### Scenario: 定义撤销授权响应类型
|
||||
|
||||
**Given** 撤销操作需要返回结果统计
|
||||
**When** 定义 `RecallDevicesResponse` 接口
|
||||
**Then** 接口必须包含:
|
||||
**Given** 撤销操作需要返回结果统计 **When** 定义 `RecallDevicesResponse` 接口 **Then** 接口必须包含:
|
||||
|
||||
- `success_count: number` - 成功数量
|
||||
- `fail_count: number` - 失败数量
|
||||
- `failed_items: FailedDeviceItem[]` - 失败项列表 (nullable)
|
||||
@@ -82,28 +78,15 @@
|
||||
|
||||
#### Scenario: 授权设备给企业
|
||||
|
||||
**Given** 运营人员需要授权设备给企业客户
|
||||
**When** 调用 `EnterpriseService.allocateDevices(enterpriseId, data)`
|
||||
**Then** 必须发送 POST 请求到 `/api/admin/enterprises/{id}/allocate-devices`
|
||||
**And** 请求体必须包含设备号列表和可选备注
|
||||
**And** 返回授权结果,包含成功/失败统计和详细列表
|
||||
**Given** 运营人员需要授权设备给企业客户 **When** 调用 `EnterpriseService.allocateDevices(enterpriseId, data)` **Then** 必须发送 POST 请求到 `/api/admin/enterprises/{id}/allocate-devices` **And** 请求体必须包含设备号列表和可选备注 **And** 返回授权结果,包含成功/失败统计和详细列表
|
||||
|
||||
#### Scenario: 获取企业设备列表
|
||||
|
||||
**Given** 需要查看企业的设备列表
|
||||
**When** 调用 `EnterpriseService.getEnterpriseDevices(enterpriseId, params)`
|
||||
**Then** 必须发送 GET 请求到 `/api/admin/enterprises/{id}/devices`
|
||||
**And** 支持分页参数 (page, page_size)
|
||||
**And** 支持设备号模糊搜索
|
||||
**And** 返回设备列表和总数
|
||||
**Given** 需要查看企业的设备列表 **When** 调用 `EnterpriseService.getEnterpriseDevices(enterpriseId, params)` **Then** 必须发送 GET 请求到 `/api/admin/enterprises/{id}/devices` **And** 支持分页参数 (page, page_size) **And** 支持设备号模糊搜索 **And** 返回设备列表和总数
|
||||
|
||||
#### Scenario: 撤销设备授权
|
||||
|
||||
**Given** 需要撤销企业的设备授权
|
||||
**When** 调用 `EnterpriseService.recallDevices(enterpriseId, data)`
|
||||
**Then** 必须发送 POST 请求到 `/api/admin/enterprises/{id}/recall-devices`
|
||||
**And** 请求体必须包含设备号列表
|
||||
**And** 返回撤销结果,包含成功/失败统计和失败原因
|
||||
**Given** 需要撤销企业的设备授权 **When** 调用 `EnterpriseService.recallDevices(enterpriseId, data)` **Then** 必须发送 POST 请求到 `/api/admin/enterprises/{id}/recall-devices` **And** 请求体必须包含设备号列表 **And** 返回撤销结果,包含成功/失败统计和失败原因
|
||||
|
||||
---
|
||||
|
||||
@@ -113,10 +96,8 @@
|
||||
|
||||
#### Scenario: 显示企业设备列表
|
||||
|
||||
**Given** 用户访问企业设备列表页面
|
||||
**When** 页面加载完成
|
||||
**Then** 必须显示设备列表表格
|
||||
**And** 表格必须包含以下列:
|
||||
**Given** 用户访问企业设备列表页面 **When** 页面加载完成 **Then** 必须显示设备列表表格 **And** 表格必须包含以下列:
|
||||
|
||||
- 设备ID
|
||||
- 设备号
|
||||
- 设备名称
|
||||
@@ -124,56 +105,32 @@
|
||||
- 绑定卡数量
|
||||
- 授权时间
|
||||
|
||||
**And** 必须支持分页功能
|
||||
**And** 必须显示加载状态
|
||||
**And** 必须支持分页功能 **And** 必须显示加载状态
|
||||
|
||||
#### Scenario: 搜索企业设备
|
||||
|
||||
**Given** 设备列表已加载
|
||||
**When** 用户在搜索框输入设备号
|
||||
**And** 点击搜索按钮
|
||||
**Then** 必须根据设备号模糊查询设备
|
||||
**And** 必须更新设备列表显示
|
||||
**And** 必须重置到第一页
|
||||
**Given** 设备列表已加载 **When** 用户在搜索框输入设备号 **And** 点击搜索按钮 **Then** 必须根据设备号模糊查询设备 **And** 必须更新设备列表显示 **And** 必须重置到第一页
|
||||
|
||||
#### Scenario: 授权设备对话框
|
||||
|
||||
**Given** 用户点击"授权设备"按钮
|
||||
**When** 授权设备对话框打开
|
||||
**Then** 必须显示设备号输入框 (textarea)
|
||||
**And** 必须显示备注输入框 (可选)
|
||||
**And** 必须提示支持的输入格式 (换行或逗号分隔)
|
||||
**And** 必须提示最多100个设备号限制
|
||||
**And** 必须有表单验证 (设备号必填)
|
||||
**Given** 用户点击"授权设备"按钮 **When** 授权设备对话框打开 **Then** 必须显示设备号输入框 (textarea) **And** 必须显示备注输入框 (可选) **And** 必须提示支持的输入格式 (换行或逗号分隔) **And** 必须提示最多100个设备号限制 **And** 必须有表单验证 (设备号必填)
|
||||
|
||||
#### Scenario: 提交授权设备
|
||||
|
||||
**Given** 用户在对话框中输入了设备号列表
|
||||
**When** 用户点击提交按钮
|
||||
**Then** 必须解析设备号列表 (支持换行和逗号分隔)
|
||||
**And** 必须去除空白字符和空行
|
||||
**And** 必须验证设备号数量不超过100个
|
||||
**And** 必须调用授权 API
|
||||
**And** 必须显示加载状态
|
||||
**And** 授权完成后必须展示结果:
|
||||
**Given** 用户在对话框中输入了设备号列表 **When** 用户点击提交按钮 **Then** 必须解析设备号列表 (支持换行和逗号分隔) **And** 必须去除空白字符和空行 **And** 必须验证设备号数量不超过100个 **And** 必须调用授权 API **And** 必须显示加载状态 **And** 授权完成后必须展示结果:
|
||||
|
||||
- 成功数量
|
||||
- 失败数量
|
||||
- 失败设备列表及原因
|
||||
|
||||
**And** 如果有成功授权的设备,必须刷新设备列表
|
||||
**And** 必须关闭对话框
|
||||
**And** 如果有成功授权的设备,必须刷新设备列表 **And** 必须关闭对话框
|
||||
|
||||
#### Scenario: 撤销设备授权
|
||||
|
||||
**Given** 用户选中了要撤销的设备
|
||||
**When** 用户点击"撤销授权"按钮
|
||||
**Then** 必须显示二次确认对话框
|
||||
**And** 确认对话框必须显示将要撤销的设备数量
|
||||
**Given** 用户选中了要撤销的设备 **When** 用户点击"撤销授权"按钮 **Then** 必须显示二次确认对话框 **And** 确认对话框必须显示将要撤销的设备数量
|
||||
|
||||
**When** 用户确认撤销 **Then** 必须调用撤销 API **And** 必须显示加载状态 **And** 撤销完成后必须展示结果:
|
||||
|
||||
**When** 用户确认撤销
|
||||
**Then** 必须调用撤销 API
|
||||
**And** 必须显示加载状态
|
||||
**And** 撤销完成后必须展示结果:
|
||||
- 成功数量
|
||||
- 失败数量
|
||||
- 失败设备列表及原因
|
||||
@@ -182,19 +139,11 @@
|
||||
|
||||
#### Scenario: 错误处理
|
||||
|
||||
**Given** API 调用可能失败
|
||||
**When** API 返回错误
|
||||
**Then** 必须显示友好的错误提示消息
|
||||
**And** 必须在控制台记录错误详情
|
||||
**And** 必须停止加载状态
|
||||
**Given** API 调用可能失败 **When** API 返回错误 **Then** 必须显示友好的错误提示消息 **And** 必须在控制台记录错误详情 **And** 必须停止加载状态
|
||||
|
||||
#### Scenario: 分页切换
|
||||
|
||||
**Given** 设备列表超过一页
|
||||
**When** 用户切换页码或每页数量
|
||||
**Then** 必须保持当前的搜索条件
|
||||
**And** 必须重新加载设备列表
|
||||
**And** 必须显示加载状态
|
||||
**Given** 设备列表超过一页 **When** 用户切换页码或每页数量 **Then** 必须保持当前的搜索条件 **And** 必须重新加载设备列表 **And** 必须显示加载状态
|
||||
|
||||
---
|
||||
|
||||
@@ -204,13 +153,8 @@
|
||||
|
||||
#### Scenario: 注册企业设备列表路由
|
||||
|
||||
**Given** 需要访问企业设备列表页面
|
||||
**When** 配置路由
|
||||
**Then** 必须在资产管理模块 (`/asset-management`) 下添加子路由
|
||||
**And** 路由路径必须为 `enterprise-devices`
|
||||
**And** 路由名称必须为 `EnterpriseDevices`
|
||||
**And** 必须使用路由别名 `RoutesAlias.EnterpriseDevices`
|
||||
**And** 必须配置 meta 信息:
|
||||
**Given** 需要访问企业设备列表页面 **When** 配置路由 **Then** 必须在资产管理模块 (`/asset-management`) 下添加子路由 **And** 路由路径必须为 `enterprise-devices` **And** 路由名称必须为 `EnterpriseDevices` **And** 必须使用路由别名 `RoutesAlias.EnterpriseDevices` **And** 必须配置 meta 信息:
|
||||
|
||||
- `title: 'menus.assetManagement.enterpriseDevices'`
|
||||
- `keepAlive: true`
|
||||
|
||||
@@ -222,9 +166,8 @@
|
||||
|
||||
#### Scenario: 中文翻译
|
||||
|
||||
**Given** 系统语言设置为中文
|
||||
**When** 访问企业设备相关页面
|
||||
**Then** 所有文本必须显示中文,包括:
|
||||
**Given** 系统语言设置为中文 **When** 访问企业设备相关页面 **Then** 所有文本必须显示中文,包括:
|
||||
|
||||
- 菜单标题: "企业设备列表"
|
||||
- 搜索表单标签和占位符
|
||||
- 表格列名
|
||||
@@ -234,9 +177,8 @@
|
||||
|
||||
#### Scenario: 英文翻译
|
||||
|
||||
**Given** 系统语言设置为英文
|
||||
**When** 访问企业设备相关页面
|
||||
**Then** 所有文本必须显示英文,包括:
|
||||
**Given** 系统语言设置为英文 **When** 访问企业设备相关页面 **Then** 所有文本必须显示英文,包括:
|
||||
|
||||
- 菜单标题: "Enterprise Devices"
|
||||
- 搜索表单标签和占位符
|
||||
- 表格列名
|
||||
@@ -252,37 +194,28 @@
|
||||
|
||||
#### Scenario: 批量输入设备号
|
||||
|
||||
**Given** 用户需要授权多个设备
|
||||
**When** 在设备号输入框中输入
|
||||
**Then** 必须支持以下输入方式:
|
||||
**Given** 用户需要授权多个设备 **When** 在设备号输入框中输入 **Then** 必须支持以下输入方式:
|
||||
|
||||
- 每行一个设备号
|
||||
- 逗号分隔的设备号
|
||||
- 混合使用换行和逗号
|
||||
|
||||
**And** 系统必须能正确解析所有格式
|
||||
**And** 必须自动去除首尾空白字符
|
||||
**And** 必须过滤空行
|
||||
**And** 系统必须能正确解析所有格式 **And** 必须自动去除首尾空白字符 **And** 必须过滤空行
|
||||
|
||||
#### Scenario: 操作结果展示
|
||||
|
||||
**Given** 批量操作完成
|
||||
**When** 显示操作结果
|
||||
**Then** 必须清晰展示:
|
||||
**Given** 批量操作完成 **When** 显示操作结果 **Then** 必须清晰展示:
|
||||
|
||||
- 总共处理的数量
|
||||
- 成功的数量
|
||||
- 失败的数量
|
||||
- 每个失败项的设备号和失败原因
|
||||
|
||||
**And** 如果全部成功,必须显示成功提示
|
||||
**And** 如果部分失败,必须显示警告提示
|
||||
**And** 如果全部失败,必须显示错误提示
|
||||
**And** 如果全部成功,必须显示成功提示 **And** 如果部分失败,必须显示警告提示 **And** 如果全部失败,必须显示错误提示
|
||||
|
||||
#### Scenario: 表格列管理
|
||||
|
||||
**Given** 设备列表表格已显示
|
||||
**When** 用户点击列管理按钮
|
||||
**Then** 必须能够选择显示/隐藏的列
|
||||
**And** 列配置必须被保存
|
||||
**Given** 设备列表表格已显示 **When** 用户点击列管理按钮 **Then** 必须能够选择显示/隐藏的列 **And** 列配置必须被保存
|
||||
|
||||
## Related Specs
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
### Phase 1: Type Definitions (Foundation)
|
||||
|
||||
1. **创建企业设备类型定义文件**
|
||||
|
||||
- 创建 `src/types/api/enterpriseDevice.ts`
|
||||
- 定义 `EnterpriseDeviceItem` 接口 (设备列表项)
|
||||
- 定义 `EnterpriseDeviceListParams` 接口 (查询参数)
|
||||
@@ -38,6 +39,7 @@
|
||||
### Phase 3: Internationalization
|
||||
|
||||
4. **添加中文翻译**
|
||||
|
||||
- 在 `src/locales/langs/zh.json` 的 `menus.assetManagement` 下添加 `enterpriseDevices` 条目
|
||||
- 在 `src/locales/langs/zh.json` 添加 `enterpriseDevices` 模块的所有中文文案:
|
||||
- 页面标题和搜索表单
|
||||
@@ -55,6 +57,7 @@
|
||||
### Phase 4: Routing
|
||||
|
||||
6. **添加路由别名**
|
||||
|
||||
- 在 `src/router/routesAlias.ts` 添加 `EnterpriseDevices = '/asset-management/enterprise-devices'`
|
||||
- **验证**: 确认导出正确
|
||||
|
||||
@@ -66,6 +69,7 @@
|
||||
### Phase 5: UI Components
|
||||
|
||||
8. **创建企业设备列表页面**
|
||||
|
||||
- 创建 `src/views/asset-management/enterprise-devices/index.vue`
|
||||
- 实现页面基础结构:
|
||||
- 使用 `ArtTableFullScreen` 布局
|
||||
@@ -75,6 +79,7 @@
|
||||
- **验证**: 页面能正常渲染,无控制台错误
|
||||
|
||||
9. **实现设备列表查询功能**
|
||||
|
||||
- 实现 `loadDeviceList()` 方法调用 API
|
||||
- 实现搜索和重置功能
|
||||
- 实现分页功能
|
||||
@@ -82,6 +87,7 @@
|
||||
- **验证**: 能正确展示设备列表数据,分页工作正常
|
||||
|
||||
10. **实现授权设备对话框**
|
||||
|
||||
- 创建授权设备对话框
|
||||
- 使用 `ElForm` + `ElInput` (textarea) 输入设备号列表
|
||||
- 支持多行输入或逗号分隔
|
||||
@@ -90,6 +96,7 @@
|
||||
- **验证**: 对话框显示正常,表单验证工作
|
||||
|
||||
11. **实现授权设备提交逻辑**
|
||||
|
||||
- 实现 `handleAllocateDevices()` 方法
|
||||
- 解析设备号列表 (处理换行和逗号分隔)
|
||||
- 调用 `EnterpriseService.allocateDevices()` API
|
||||
@@ -99,6 +106,7 @@
|
||||
- **验证**: 能成功授权设备,正确处理部分成功/失败情况
|
||||
|
||||
12. **实现撤销授权对话框**
|
||||
|
||||
- 创建撤销授权对话框
|
||||
- 使用表格多选模式选择要撤销的设备
|
||||
- 或者使用输入框输入设备号列表
|
||||
@@ -116,18 +124,21 @@
|
||||
### Phase 6: Polish & Testing
|
||||
|
||||
14. **完善表格列配置**
|
||||
|
||||
- 配置表格列 (设备ID,设备号,设备名称,设备型号,绑定卡数量,授权时间)
|
||||
- 实现列显示/隐藏功能
|
||||
- 添加时间格式化
|
||||
- **验证**: 表格数据展示完整美观
|
||||
|
||||
15. **添加错误处理**
|
||||
|
||||
- 为所有 API 调用添加 try-catch
|
||||
- 添加友好的错误提示消息
|
||||
- 处理网络错误和业务错误
|
||||
- **验证**: 各种错误场景都有适当提示
|
||||
|
||||
16. **样式调整**
|
||||
|
||||
- 确保页面样式与系统其他页面一致
|
||||
- 响应式布局适配
|
||||
- 对话框尺寸和布局优化
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Why
|
||||
|
||||
The IoT management platform currently lacks order management capabilities. Users need to:
|
||||
|
||||
- View and query orders created by customers (personal and agent)
|
||||
- Track order payment status and details
|
||||
- Create orders for single card or device purchases
|
||||
@@ -20,6 +21,7 @@ This capability is essential for financial tracking, commission calculation, and
|
||||
- **NEW**: Router configuration for order management module
|
||||
|
||||
The order management module will support:
|
||||
|
||||
- Listing orders with filters (payment status, order type, date range, order number)
|
||||
- Viewing order details including buyer information, order items (packages), and payment details
|
||||
- Creating orders for single card or device purchases with package selection
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
实现完整的套餐管理系统,包括4个核心模块。该系统需要支持多级代理商体系的套餐分配和定价管理。
|
||||
|
||||
**背景**:
|
||||
|
||||
- 项目已有类型定义(src/types/api/package.ts),但使用不同的字段命名和枚举值
|
||||
- 后端 API 已实现,使用下划线命名(如 `series_name`)
|
||||
- 前端项目统一使用 CommonStatus 枚举(0:禁用, 1:启用)
|
||||
- 参考实现:`/system/role` 页面使用了组件化架构
|
||||
|
||||
**约束**:
|
||||
|
||||
- 必须保留现有类型定义文件,不能破坏现有代码
|
||||
- 需要兼容后端 API 的字段命名规范
|
||||
- 需要适配项目的状态枚举规范
|
||||
@@ -18,6 +20,7 @@
|
||||
## Goals / Non-Goals
|
||||
|
||||
### Goals
|
||||
|
||||
1. 实现4个核心模块的完整 CRUD 功能
|
||||
2. 建立统一的 API 服务层,封装后端接口
|
||||
3. 实现组件化的页面结构,参考 `/system/role`
|
||||
@@ -25,6 +28,7 @@
|
||||
5. 确保数据隔离和权限控制
|
||||
|
||||
### Non-Goals
|
||||
|
||||
1. 不重构现有的 package.ts 类型定义
|
||||
2. 不实现套餐的实时统计和报表功能(后续迭代)
|
||||
3. 不实现套餐批量导入功能(后续迭代)
|
||||
@@ -37,20 +41,23 @@
|
||||
**问题**:后端使用下划线命名(snake_case),前端类型通常使用驼峰命名(camelCase)。
|
||||
|
||||
**决策**:
|
||||
|
||||
- API 请求/响应保持下划线命名,与后端保持一致
|
||||
- 创建新的类型文件 `packageManagement.ts`,使用下划线命名
|
||||
- 在表单提交和响应处理时不做转换,直接使用下划线字段
|
||||
|
||||
**理由**:
|
||||
|
||||
- 减少转换层的复杂性和错误风险
|
||||
- 与后端 API 文档保持一致,便于对照
|
||||
- TypeScript 支持下划线字段名,不影响类型安全
|
||||
|
||||
**示例**:
|
||||
|
||||
```typescript
|
||||
export interface PackageSeriesResponse {
|
||||
id: number
|
||||
series_code: string // 下划线命名
|
||||
series_code: string // 下划线命名
|
||||
series_name: string
|
||||
status: number
|
||||
created_at: string
|
||||
@@ -63,11 +70,13 @@ export interface PackageSeriesResponse {
|
||||
**问题**:文档中状态是 `1:启用, 2:禁用`,但项目 CommonStatus 是 `0:禁用, 1:启用`。
|
||||
|
||||
**决策**:
|
||||
|
||||
- **在常量配置中定义套餐专用的状态枚举**
|
||||
- **前端页面使用项目统一的 CommonStatus(0/1)**
|
||||
- **在 API 服务层进行状态值映射转换**
|
||||
|
||||
**映射规则**:
|
||||
|
||||
```typescript
|
||||
// 前端 -> 后端
|
||||
CommonStatus.ENABLED (1) -> API Status (1)
|
||||
@@ -79,6 +88,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
```
|
||||
|
||||
**理由**:
|
||||
|
||||
- 保持前端 UI 的一致性
|
||||
- 避免混淆项目开发者
|
||||
- 集中在 API 服务层处理差异
|
||||
@@ -88,18 +98,21 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**问题**:是创建单个 package.ts 服务,还是拆分为多个服务文件?
|
||||
|
||||
**决策**:拆分为4个独立的服务文件:
|
||||
|
||||
1. `packageSeries.ts` - 套餐系列管理
|
||||
2. `package.ts` - 套餐管理
|
||||
3. `myPackage.ts` - 代理可售套餐
|
||||
4. `shopPackageAllocation.ts` - 单套餐分配
|
||||
|
||||
**理由**:
|
||||
|
||||
- 每个模块功能独立,职责清晰
|
||||
- 便于维护和扩展
|
||||
- 符合单一职责原则
|
||||
- 便于团队协作(不同开发者负责不同模块)
|
||||
|
||||
**替代方案**:
|
||||
|
||||
- 单个 package.ts 文件 - **拒绝**,文件过大,难以维护
|
||||
|
||||
### Decision 4: 定价规则实现
|
||||
@@ -107,11 +120,13 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**问题**:代理商的套餐成本价有两种计算方式:系列加价和单套餐覆盖。
|
||||
|
||||
**决策**:
|
||||
|
||||
- **后端负责成本价计算**,前端只展示结果
|
||||
- 前端接收 `price_source` 字段,标识价格来源
|
||||
- 单套餐分配创建时,保存 `calculated_cost_price`(系列规则计算的价格)供参考
|
||||
|
||||
**数据流**:
|
||||
|
||||
```
|
||||
1. 系列分配:pricing_mode + pricing_value -> 后端计算 -> cost_price
|
||||
2. 单套餐分配:直接设置 cost_price(覆盖系列规则)
|
||||
@@ -119,6 +134,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
```
|
||||
|
||||
**理由**:
|
||||
|
||||
- 计算逻辑复杂,集中在后端便于维护
|
||||
- 前端只负责展示,降低复杂度
|
||||
- 保留 calculated_cost_price 便于调试和审计
|
||||
@@ -128,16 +144,19 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**问题**:客户端验证 vs 服务端验证。
|
||||
|
||||
**决策**:**双重验证**
|
||||
|
||||
- 客户端:使用 Element Plus 的 FormRules 进行基础验证
|
||||
- 服务端:后端 API 进行完整验证并返回详细错误
|
||||
|
||||
**客户端验证规则**:
|
||||
|
||||
- 必填字段检查
|
||||
- 长度限制(如系列名称 1-255 字符)
|
||||
- 数值范围(如套餐时长 1-120 月)
|
||||
- 格式验证(如价格必须为正整数)
|
||||
|
||||
**理由**:
|
||||
|
||||
- 客户端验证提升用户体验,即时反馈
|
||||
- 服务端验证保证数据安全性和完整性
|
||||
- 符合 Web 应用最佳实践
|
||||
@@ -147,20 +166,26 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**问题**:页面结构如何组织?
|
||||
|
||||
**决策**:参考 `/system/role` 页面,使用组件化结构:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<ArtSearchBar /> <!-- 搜索栏 -->
|
||||
<ArtSearchBar />
|
||||
<!-- 搜索栏 -->
|
||||
<ElCard>
|
||||
<ArtTableHeader /> <!-- 表格头部:刷新、列设置、操作按钮 -->
|
||||
<ArtTable /> <!-- 数据表格 -->
|
||||
<ElDialog /> <!-- 新增/编辑对话框 -->
|
||||
<ArtTableHeader />
|
||||
<!-- 表格头部:刷新、列设置、操作按钮 -->
|
||||
<ArtTable />
|
||||
<!-- 数据表格 -->
|
||||
<ElDialog />
|
||||
<!-- 新增/编辑对话框 -->
|
||||
</ElCard>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
```
|
||||
|
||||
**理由**:
|
||||
|
||||
- 与项目现有页面风格一致
|
||||
- 复用成熟的组件,减少开发工作量
|
||||
- 便于维护和扩展
|
||||
@@ -172,6 +197,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**风险**:后端接口可能尚未实现或与文档不一致。
|
||||
|
||||
**缓解措施**:
|
||||
|
||||
1. 先实现 API 服务层,使用 TypeScript 类型约束
|
||||
2. 使用 Mock 数据进行前端开发(已有示例)
|
||||
3. 与后端团队确认 API 规范和联调时间
|
||||
@@ -182,6 +208,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**风险**:在某些地方忘记转换状态值,导致显示错误。
|
||||
|
||||
**缓解措施**:
|
||||
|
||||
1. 在 API 服务层统一处理转换
|
||||
2. 创建工具函数封装映射逻辑
|
||||
3. 编写单元测试覆盖映射函数
|
||||
@@ -192,6 +219,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**风险**:对定价规则的理解与实际业务需求有偏差。
|
||||
|
||||
**缓解措施**:
|
||||
|
||||
1. 在实现前与产品确认定价规则
|
||||
2. 编写测试用例覆盖各种定价场景
|
||||
3. 在 UI 上清晰展示价格来源和计算方式
|
||||
@@ -202,10 +230,12 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**取舍**:保留旧的 package.ts 类型定义,新增 packageManagement.ts。
|
||||
|
||||
**代价**:
|
||||
|
||||
- 存在两套类型定义,可能造成混淆
|
||||
- 占用额外的代码空间
|
||||
|
||||
**收益**:
|
||||
|
||||
- 不影响现有代码,向后兼容
|
||||
- 新旧系统可以并存,降低迁移风险
|
||||
- 未来可以逐步迁移到新类型
|
||||
@@ -215,10 +245,12 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**取舍**:在 API 服务层进行状态值转换。
|
||||
|
||||
**代价**:
|
||||
|
||||
- 增加一层转换逻辑
|
||||
- 可能影响性能(微小)
|
||||
|
||||
**收益**:
|
||||
|
||||
- 前端 UI 保持一致性
|
||||
- 业务逻辑更清晰
|
||||
- 便于后续维护
|
||||
@@ -226,27 +258,32 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: 基础设施(1-2天)
|
||||
|
||||
1. 创建类型定义文件
|
||||
2. 创建常量配置文件
|
||||
3. 设置状态映射工具函数
|
||||
|
||||
### Phase 2: API 服务层(2-3天)
|
||||
|
||||
1. 实现4个 API 服务模块
|
||||
2. 编写单元测试(可选)
|
||||
3. 使用 Mock 数据测试
|
||||
|
||||
### Phase 3: 页面实现(4-5天)
|
||||
|
||||
1. 套餐系列管理页面(1天)
|
||||
2. 套餐管理页面(1.5天)
|
||||
3. 代理可售套餐页面(1天)
|
||||
4. 单套餐分配页面(1.5天)
|
||||
|
||||
### Phase 4: 集成测试(1-2天)
|
||||
|
||||
1. 与后端 API 联调
|
||||
2. 端到端功能测试
|
||||
3. 修复 Bug 和优化
|
||||
|
||||
### Phase 5: 上线(1天)
|
||||
|
||||
1. Code Review
|
||||
2. 合并代码
|
||||
3. 部署到测试环境
|
||||
@@ -257,6 +294,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
### Rollback Plan
|
||||
|
||||
如果出现严重问题,回滚步骤:
|
||||
|
||||
1. 从 Git 回滚到上一个稳定版本
|
||||
2. 移除新增的路由配置
|
||||
3. 移除新增的 API 服务导出
|
||||
@@ -267,6 +305,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
**问题**:如何统一处理各类错误和异常?
|
||||
|
||||
**决策**:分层错误处理机制
|
||||
|
||||
- **网络错误**:axios 拦截器统一捕获,显示通用错误提示
|
||||
- **401 未认证**:自动跳转到登录页面
|
||||
- **403 无权限**:显示权限不足提示,不跳转
|
||||
@@ -274,6 +313,7 @@ API Status (2) -> CommonStatus.DISABLED (0)
|
||||
- **表单验证错误**:在表单字段下显示错误提示
|
||||
|
||||
**错误提示方式**:
|
||||
|
||||
```typescript
|
||||
// 网络错误或服务器错误
|
||||
ElMessage.error('网络错误,请稍后重试')
|
||||
@@ -286,6 +326,7 @@ ElMessage.success('操作成功')
|
||||
```
|
||||
|
||||
**理由**:
|
||||
|
||||
- 统一的错误处理提升用户体验
|
||||
- 分层处理避免重复代码
|
||||
- 清晰的错误提示帮助用户理解问题
|
||||
@@ -297,36 +338,34 @@ ElMessage.success('操作成功')
|
||||
**决策**:细粒度的 loading 状态管理
|
||||
|
||||
**Loading 状态分类**:
|
||||
|
||||
```typescript
|
||||
const loading = ref(false) // 表格数据加载
|
||||
const submitLoading = ref(false) // 表单提交
|
||||
const loading = ref(false) // 表格数据加载
|
||||
const submitLoading = ref(false) // 表单提交
|
||||
const deleteLoading = ref<Record<number, boolean>>({}) // 删除操作(可选)
|
||||
```
|
||||
|
||||
**状态管理规则**:
|
||||
|
||||
- **列表查询**:表格显示 loading 遮罩
|
||||
- **新增/编辑提交**:提交按钮显示 loading,禁用表单
|
||||
- **删除操作**:可选择在按钮上显示 loading 或全局 loading
|
||||
- **状态切换**:ElSwitch 自带 loading 效果,先更新 UI 再调用 API
|
||||
|
||||
**理由**:
|
||||
|
||||
- 细粒度控制提供更好的交互反馈
|
||||
- 防止重复提交
|
||||
- 清晰标识正在进行的操作
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Q**: 套餐被删除后,历史订单如何处理?
|
||||
**A**: 待产品确认,可能需要软删除机制
|
||||
1. **Q**: 套餐被删除后,历史订单如何处理? **A**: 待产品确认,可能需要软删除机制
|
||||
|
||||
2. **Q**: 代理商可以自行调整套餐售价吗?
|
||||
**A**: 待产品确认,当前设计只展示建议售价
|
||||
2. **Q**: 代理商可以自行调整套餐售价吗? **A**: 待产品确认,当前设计只展示建议售价
|
||||
|
||||
3. **Q**: 套餐系列和套餐是否支持批量操作(批量启用/禁用)?
|
||||
**A**: 当前不支持,后续迭代考虑
|
||||
3. **Q**: 套餐系列和套餐是否支持批量操作(批量启用/禁用)? **A**: 当前不支持,后续迭代考虑
|
||||
|
||||
4. **Q**: 是否需要套餐变更历史记录?
|
||||
**A**: 后端可能有审计日志,前端暂不展示
|
||||
4. **Q**: 是否需要套餐变更历史记录? **A**: 后端可能有审计日志,前端暂不展示
|
||||
|
||||
5. **Q**: 单套餐分配的"原计算成本价"是否需要实时更新?
|
||||
**A**: 待确认,当前设计是创建时计算一次,不自动更新
|
||||
5. **Q**: 单套餐分配的"原计算成本价"是否需要实时更新? **A**: 待确认,当前设计是创建时计算一次,不自动更新
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
采用模块化设计,拆分为 4 个独立的 API 服务文件:
|
||||
|
||||
- **新增**: `src/api/modules/packageSeries.ts` - 套餐系列 API 服务
|
||||
|
||||
- 套餐系列列表查询(分页、筛选)
|
||||
- 创建套餐系列
|
||||
- 获取套餐系列详情
|
||||
@@ -26,6 +27,7 @@
|
||||
- 更新套餐系列状态
|
||||
|
||||
- **新增**: `src/api/modules/package.ts` - 套餐管理 API 服务
|
||||
|
||||
- 套餐列表查询(分页、多条件筛选)
|
||||
- 创建套餐
|
||||
- 获取套餐详情
|
||||
@@ -36,11 +38,13 @@
|
||||
- 获取系列下拉选项(用于表单选择)
|
||||
|
||||
- **新增**: `src/api/modules/myPackage.ts` - 代理可售套餐 API 服务
|
||||
|
||||
- 我的可售套餐列表查询
|
||||
- 获取可售套餐详情
|
||||
- 我的被分配系列列表
|
||||
|
||||
- **新增**: `src/api/modules/shopPackageAllocation.ts` - 单套餐分配 API 服务
|
||||
|
||||
- 单套餐分配列表查询
|
||||
- 创建单套餐分配
|
||||
- 获取单套餐分配详情
|
||||
@@ -64,12 +68,14 @@
|
||||
### 3. 页面实现
|
||||
|
||||
**套餐系列管理** (`src/views/package-management/package-series/index.vue`)
|
||||
|
||||
- 列表展示(支持名称搜索、状态筛选)
|
||||
- 新增/编辑套餐系列
|
||||
- 删除套餐系列
|
||||
- 状态开关(启用/禁用)
|
||||
|
||||
**套餐管理** (`src/views/package-management/package-list/index.vue`)
|
||||
|
||||
- 列表展示(支持多条件筛选:名称、系列、状态、上架状态、套餐类型)
|
||||
- 新增/编辑套餐
|
||||
- 删除套餐
|
||||
@@ -77,11 +83,13 @@
|
||||
- 上架状态开关(上架/下架)
|
||||
|
||||
**代理可售套餐** (`src/views/package-management/my-packages/index.vue`)
|
||||
|
||||
- 查看被分配的套餐列表(支持系列、类型筛选)
|
||||
- 查看套餐详情(成本价、建议售价、利润空间等)
|
||||
- 查看被分配系列列表
|
||||
|
||||
**单套餐分配** (`src/views/package-management/package-assign/index.vue`)
|
||||
|
||||
- 分配列表(支持店铺、套餐、状态筛选)
|
||||
- 创建分配(选择套餐、店铺、设置成本价)
|
||||
- 编辑分配(修改成本价)
|
||||
@@ -100,12 +108,15 @@
|
||||
### 5. 路由配置
|
||||
|
||||
已存在的路由(无需修改):
|
||||
|
||||
- `/package-management/package-series` - 套餐系列管理
|
||||
- `/package-management/package-list` - 套餐管理
|
||||
- `/package-management/package-assign` - 单套餐分配
|
||||
|
||||
需要新增的路由:
|
||||
|
||||
- **新增**: `src/router/routesAlias.ts` - 添加路由别名
|
||||
|
||||
- `MyPackages = '/package-management/my-packages'` - 代理可售套餐
|
||||
|
||||
- **新增**: `src/router/routes/asyncRoutes.ts` - 添加路由配置
|
||||
@@ -114,12 +125,14 @@
|
||||
## Impact
|
||||
|
||||
### 受影响的规范
|
||||
|
||||
- `package-series-management` - 新增能力
|
||||
- `package-management` - 新增能力
|
||||
- `my-packages` - 新增能力
|
||||
- `shop-package-allocation` - 新增能力
|
||||
|
||||
### 受影响的代码
|
||||
|
||||
- `src/api/modules/*` - 新增 4 个 API 服务模块
|
||||
- `src/types/api/*` - 新增类型定义文件
|
||||
- `src/views/package-management/*` - 4 个页面完整实现
|
||||
@@ -127,6 +140,7 @@
|
||||
- `src/router/routes/asyncRoutes.ts` - 路由配置
|
||||
|
||||
### 依赖关系
|
||||
|
||||
- 依赖现有的组件库(ArtTable、ArtSearchBar、ArtTableHeader 等)
|
||||
- 依赖现有的 HTTP 请求工具(request.ts)
|
||||
- 依赖现有的权限控制和路由守卫
|
||||
@@ -134,10 +148,12 @@
|
||||
- 后端 API 需已实现(docs/套餐.md 中定义的接口)
|
||||
|
||||
**注意事项**:
|
||||
|
||||
- ShopService 应该已经存在于 src/api/modules/shop.ts
|
||||
- 如果不存在,需要先实现或使用 Mock 数据
|
||||
|
||||
### 风险评估
|
||||
|
||||
- **低风险**: 独立模块,不影响现有功能
|
||||
- **API 依赖**: 需确保后端接口已实现并联调
|
||||
- **权限控制**: 需配置对应的菜单和按钮权限
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
## 2. API 服务层实现
|
||||
|
||||
### 2.1 套餐系列 API(packageSeries.ts)
|
||||
|
||||
- [ ] 2.1.1 实现 getPackageSeries(套餐系列列表)
|
||||
- [ ] 2.1.2 实现 createPackageSeries(创建套餐系列)
|
||||
- [ ] 2.1.3 实现 getPackageSeriesDetail(获取套餐系列详情)
|
||||
@@ -17,6 +18,7 @@
|
||||
- [ ] 2.1.6 实现 updatePackageSeriesStatus(更新套餐系列状态)
|
||||
|
||||
### 2.2 套餐管理 API(package.ts)
|
||||
|
||||
- [ ] 2.2.1 实现 getPackages(套餐列表)
|
||||
- [ ] 2.2.2 实现 createPackage(创建套餐)
|
||||
- [ ] 2.2.3 实现 getPackageDetail(获取套餐详情)
|
||||
@@ -26,11 +28,13 @@
|
||||
- [ ] 2.2.7 实现 updatePackageShelfStatus(更新套餐上架状态)
|
||||
|
||||
### 2.3 代理可售套餐 API(myPackage.ts)
|
||||
|
||||
- [ ] 2.3.1 实现 getMyPackages(我的可售套餐列表)
|
||||
- [ ] 2.3.2 实现 getMyPackageDetail(获取可售套餐详情)
|
||||
- [ ] 2.3.3 实现 getMySeriesAllocations(我的被分配系列列表)
|
||||
|
||||
### 2.4 单套餐分配 API(shopPackageAllocation.ts)
|
||||
|
||||
- [ ] 2.4.1 实现 getShopPackageAllocations(单套餐分配列表)
|
||||
- [ ] 2.4.2 实现 createShopPackageAllocation(创建单套餐分配)
|
||||
- [ ] 2.4.3 实现 getShopPackageAllocationDetail(获取单套餐分配详情)
|
||||
@@ -43,6 +47,7 @@
|
||||
## 3. 页面实现
|
||||
|
||||
### 3.1 套餐系列管理页面(package-series/index.vue)
|
||||
|
||||
- [ ] 3.1.1 实现列表展示(表格、分页)
|
||||
- [ ] 3.1.2 实现搜索栏(系列名称、状态筛选)
|
||||
- [ ] 3.1.3 实现新增对话框(表单验证)
|
||||
@@ -52,6 +57,7 @@
|
||||
- [ ] 3.1.7 集成 API 服务并处理加载状态
|
||||
|
||||
### 3.2 套餐管理页面(package-list/index.vue)
|
||||
|
||||
- [ ] 3.2.1 实现列表展示(表格、分页)
|
||||
- [ ] 3.2.2 实现搜索栏(名称、系列、状态、上架状态、类型筛选)
|
||||
- [ ] 3.2.3 实现系列下拉选择器(加载套餐系列列表,只显示启用状态)
|
||||
@@ -63,6 +69,7 @@
|
||||
- [ ] 3.2.9 集成 API 服务并处理加载状态
|
||||
|
||||
### 3.3 代理可售套餐页面(my-packages/index.vue)
|
||||
|
||||
- [ ] 3.3.1 创建页面文件和基本结构
|
||||
- [ ] 3.3.2 实现列表展示(表格、分页)
|
||||
- [ ] 3.3.3 实现搜索栏(系列、类型筛选)
|
||||
@@ -71,6 +78,7 @@
|
||||
- [ ] 3.3.6 集成 API 服务并处理加载状态
|
||||
|
||||
### 3.4 单套餐分配页面(package-assign/index.vue)
|
||||
|
||||
- [ ] 3.4.1 创建页面文件和基本结构
|
||||
- [ ] 3.4.2 实现列表展示(表格、分页)
|
||||
- [ ] 3.4.3 实现搜索栏(店铺、套餐、状态筛选)
|
||||
@@ -90,6 +98,7 @@
|
||||
## 5. 集成测试
|
||||
|
||||
### 5.1 套餐系列管理测试
|
||||
|
||||
- [ ] 5.1.1 测试列表查询(空列表、有数据、分页)
|
||||
- [ ] 5.1.2 测试搜索功能(名称模糊搜索、状态筛选)
|
||||
- [ ] 5.1.3 测试新增功能(成功、编码重复、字段验证)
|
||||
@@ -99,6 +108,7 @@
|
||||
- [ ] 5.1.7 测试权限控制(未登录、无权限)
|
||||
|
||||
### 5.2 套餐管理测试
|
||||
|
||||
- [ ] 5.2.1 测试列表查询(空列表、有数据、分页)
|
||||
- [ ] 5.2.2 测试多条件筛选(名称、系列、状态、上架状态、类型)
|
||||
- [ ] 5.2.3 测试系列下拉选择器(只显示启用状态的系列)
|
||||
@@ -110,6 +120,7 @@
|
||||
- [ ] 5.2.9 测试权限控制(未登录、无权限)
|
||||
|
||||
### 5.3 代理可售套餐测试
|
||||
|
||||
- [ ] 5.3.1 测试列表查询(空列表、有数据、分页)
|
||||
- [ ] 5.3.2 测试筛选功能(按系列、按类型)
|
||||
- [ ] 5.3.3 测试详情查询(显示成本价、建议售价、利润空间、价格来源)
|
||||
@@ -118,6 +129,7 @@
|
||||
- [ ] 5.3.6 测试权限控制(非代理商用户无法访问)
|
||||
|
||||
### 5.4 单套餐分配测试
|
||||
|
||||
- [ ] 5.4.1 测试列表查询(空列表、有数据、分页)
|
||||
- [ ] 5.4.2 测试筛选功能(按店铺、按套餐、按状态)
|
||||
- [ ] 5.4.3 测试套餐下拉选择器(只显示启用且上架的套餐)
|
||||
@@ -130,6 +142,7 @@
|
||||
- [ ] 5.4.10 测试权限控制(仅管理员可操作)
|
||||
|
||||
### 5.5 通用功能测试
|
||||
|
||||
- [ ] 5.5.1 测试所有页面的表单验证(必填、长度、格式)
|
||||
- [ ] 5.5.2 测试所有页面的 loading 状态(列表、提交、删除)
|
||||
- [ ] 5.5.3 测试所有页面的错误处理(网络错误、业务错误)
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
## Context
|
||||
|
||||
当前系统使用简单的定价模式(pricing_mode/pricing_value)和一次性佣金(one_time_commission_*)来管理套餐系列分配。随着业务发展,需要更复杂的佣金系统:
|
||||
当前系统使用简单的定价模式(pricing*mode/pricing_value)和一次性佣金(one_time_commission*\*)来管理套餐系列分配。随着业务发展,需要更复杂的佣金系统:
|
||||
|
||||
- 支持基础返佣(固定金额或百分比)
|
||||
- 支持梯度返佣(根据销量或销售额分档返佣)
|
||||
- 更清晰的数据模型和API接口
|
||||
|
||||
**背景约束**:
|
||||
|
||||
- 前后端需要同步部署(Breaking Change)
|
||||
- 需要数据迁移方案
|
||||
- 影响现有的套餐系列分配功能
|
||||
@@ -15,12 +17,14 @@
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals**:
|
||||
|
||||
- 实现新的佣金配置模型,支持基础返佣和梯度返佣
|
||||
- 提供清晰的UI界面让用户配置复杂的返佣规则
|
||||
- 保证数据一致性和类型安全
|
||||
- 提供良好的用户体验(表单验证、错误提示)
|
||||
|
||||
**Non-Goals**:
|
||||
|
||||
- 不处理历史数据的完整性验证(由后端负责)
|
||||
- 不实现佣金计算逻辑(由后端负责)
|
||||
- 不处理佣金结算流程(属于其他模块)
|
||||
@@ -32,11 +36,13 @@
|
||||
**决策**: 采用嵌套对象结构表示佣金配置
|
||||
|
||||
**理由**:
|
||||
|
||||
- `base_commission: { mode, value }` - 清晰表达基础返佣的两个维度
|
||||
- `tier_config: { period_type, tier_type, tiers[] }` - 梯度配置与基础配置分离,可选性强
|
||||
- `tiers: [{ threshold, mode, value }]` - 每个档位都有独立的返佣模式和值
|
||||
|
||||
**替代方案考虑**:
|
||||
|
||||
- ❌ 平铺所有字段 - 会导致字段过多,语义不清晰
|
||||
- ❌ 使用JSON字符串存储配置 - 失去类型安全,不利于表单编辑
|
||||
|
||||
@@ -45,6 +51,7 @@
|
||||
**决策**: 使用渐进式表单设计
|
||||
|
||||
**表单结构**:
|
||||
|
||||
```
|
||||
1. 基础返佣配置 (必填)
|
||||
- 返佣模式: 单选 (固定金额/百分比)
|
||||
@@ -63,11 +70,13 @@
|
||||
```
|
||||
|
||||
**理由**:
|
||||
|
||||
- 渐进式设计降低初始复杂度
|
||||
- 只有启用梯度返佣时才显示相关配置
|
||||
- 动态档位列表提供灵活性
|
||||
|
||||
**替代方案考虑**:
|
||||
|
||||
- ❌ 全部平铺展示 - 对不需要梯度返佣的用户造成困扰
|
||||
- ❌ 使用向导模式 - 增加操作步骤,不适合编辑场景
|
||||
|
||||
@@ -76,7 +85,9 @@
|
||||
**决策**: 分层验证 + 条件验证
|
||||
|
||||
**验证规则**:
|
||||
|
||||
1. 基础返佣配置:
|
||||
|
||||
- mode: 必选
|
||||
- value: 必填,>= 0
|
||||
|
||||
@@ -91,6 +102,7 @@
|
||||
- 档位阈值必须递增
|
||||
|
||||
**实现方式**:
|
||||
|
||||
- 使用 Element Plus 的表单验证
|
||||
- 自定义validator处理档位阈值递增验证
|
||||
- 使用 computed 动态生成验证规则
|
||||
@@ -100,11 +112,13 @@
|
||||
**决策**: 统一使用 `list` 字段名,添加适配层
|
||||
|
||||
**理由**:
|
||||
|
||||
- 后端统一规范使用 `list` 而非 `items`
|
||||
- 添加 `total_pages` 字段提供更完整的分页信息
|
||||
- 保持前端代码与后端规范一致
|
||||
|
||||
**迁移策略**:
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const res = await getShopSeriesAllocations(params)
|
||||
@@ -122,6 +136,7 @@ allocationList.value = res.data.list || []
|
||||
**风险**: 前后端必须同时部署,否则会出现接口不兼容
|
||||
|
||||
**缓解措施**:
|
||||
|
||||
- 与后端团队协调部署窗口
|
||||
- 准备回退方案(前端代码分支)
|
||||
- 在测试环境充分验证
|
||||
@@ -131,6 +146,7 @@ allocationList.value = res.data.list || []
|
||||
**风险**: 梯度返佣配置较复杂,用户可能不理解
|
||||
|
||||
**缓解措施**:
|
||||
|
||||
- 提供清晰的字段说明
|
||||
- 添加示例或帮助文档链接
|
||||
- 使用默认值简化初次配置
|
||||
@@ -140,6 +156,7 @@ allocationList.value = res.data.list || []
|
||||
**风险**: 旧数据无法完全转换为新模型
|
||||
|
||||
**缓解措施**:
|
||||
|
||||
- 要求后端提供数据迁移脚本和验证
|
||||
- 在迁移后检查数据完整性
|
||||
- 保留旧数据备份
|
||||
@@ -147,22 +164,26 @@ allocationList.value = res.data.list || []
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: 准备阶段
|
||||
|
||||
1. 与后端确认API变更细节和时间表
|
||||
2. 在开发环境实现前端变更
|
||||
3. 与后端在测试环境联调
|
||||
|
||||
### Phase 2: 测试阶段
|
||||
|
||||
1. 功能测试: 创建、编辑、删除、列表展示
|
||||
2. 集成测试: 与后端API集成
|
||||
3. 用户验收测试: 业务人员验证
|
||||
|
||||
### Phase 3: 部署阶段
|
||||
|
||||
1. 准备回退方案
|
||||
2. 与后端协调部署窗口
|
||||
3. 同步部署前后端
|
||||
4. 验证生产环境功能
|
||||
|
||||
### Phase 4: 监控阶段
|
||||
|
||||
1. 监控API错误率
|
||||
2. 收集用户反馈
|
||||
3. 修复遗留问题
|
||||
@@ -170,6 +191,7 @@ allocationList.value = res.data.list || []
|
||||
### Rollback Plan
|
||||
|
||||
如果部署后发现严重问题:
|
||||
|
||||
1. 前端回退到上一版本
|
||||
2. 后端回退API(如果可能)
|
||||
3. 通知用户暂时不可用
|
||||
@@ -178,12 +200,15 @@ allocationList.value = res.data.list || []
|
||||
## Open Questions
|
||||
|
||||
1. **Q**: 梯度返佣的档位数量是否有上限?
|
||||
|
||||
- **A**: 待后端确认,前端可以先不限制或设置合理上限(如10个)
|
||||
|
||||
2. **Q**: 返佣值的单位和精度如何处理?
|
||||
|
||||
- **A**: 固定金额使用"分"为单位,百分比使用千分比(如200=20%),待后端确认
|
||||
|
||||
3. **Q**: 是否需要在列表页显示梯度返佣信息?
|
||||
|
||||
- **A**: 暂时只显示基础返佣,梯度信息在详情或编辑时查看
|
||||
|
||||
4. **Q**: 旧数据如何映射到新模型?
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Why
|
||||
|
||||
当前套餐系列分配使用简单的定价模式(固定加价/百分比加价)和一次性佣金配置,不支持复杂的返佣规则。新的业务需求要求支持:
|
||||
|
||||
- 基础返佣配置(固定金额或百分比)
|
||||
- 梯度返佣系统(根据销量或销售额分档返佣)
|
||||
- 更灵活的佣金计算模型
|
||||
@@ -32,12 +33,14 @@
|
||||
## Breaking Changes
|
||||
|
||||
1. **API响应结构变更**:
|
||||
|
||||
- 列表接口响应从 `{ items, page, page_size, total }` 改为 `{ list, page, page_size, total, total_pages }`
|
||||
- 移除 `pricing_mode`, `pricing_value`, `calculated_cost_price` 字段
|
||||
- 移除一次性佣金相关字段
|
||||
- 新增 `base_commission`, `enable_tier_commission` 字段
|
||||
|
||||
2. **API请求结构变更**:
|
||||
|
||||
- 创建/更新接口需要新的 `base_commission` 对象
|
||||
- 支持可选的 `enable_tier_commission` 和 `tier_config`
|
||||
|
||||
|
||||
@@ -129,15 +129,17 @@
|
||||
**Reason**: 旧的定价模式 (pricing_mode, pricing_value) 已被新的佣金模型替代
|
||||
|
||||
**Migration**: 旧数据通过后端迁移脚本转换为基础返佣配置:
|
||||
|
||||
- pricing_mode="fixed" → base_commission.mode="fixed"
|
||||
- pricing_mode="percentage" → base_commission.mode="percent"
|
||||
- pricing_value → base_commission.value (需要单位转换)
|
||||
|
||||
### Requirement: 一次性佣金配置
|
||||
|
||||
**Reason**: 一次性佣金配置 (one_time_commission_*) 已被梯度返佣系统替代
|
||||
**Reason**: 一次性佣金配置 (one*time_commission*\*) 已被梯度返佣系统替代
|
||||
|
||||
**Migration**: 旧的一次性佣金通过以下方式迁移:
|
||||
|
||||
- 如果设置了一次性佣金,转换为单档位的梯度返佣
|
||||
- trigger → tier_type mapping (first_activation → sales_count, cumulative_recharge → sales_amount)
|
||||
- threshold → tiers[0].threshold
|
||||
@@ -165,7 +167,7 @@
|
||||
|
||||
- **WHEN** base_commission.mode = "percent"
|
||||
- **THEN** base_commission.value 表示返佣百分比的千分比 (如200表示20%)
|
||||
- **AND** 每笔交易返佣 = 交易金额 * (value / 1000)
|
||||
- **AND** 每笔交易返佣 = 交易金额 \* (value / 1000)
|
||||
|
||||
### Requirement: 梯度返佣配置
|
||||
|
||||
|
||||
Reference in New Issue
Block a user