12 KiB
Device Management Design
Context
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
- Task System: Import tasks must integrate with the existing task-management infrastructure
Goals
- Complete Lifecycle Management: Support all device operations from import to deletion
- Seamless Integration: Reuse existing UI patterns, services, and components
- Scalable Import: Handle large CSV imports efficiently using object storage
- Operation Traceability: Track all device operations through task management
- Consistent UX: Match the look and feel of existing card-list and enterprise-customer pages
Key Decisions
Decision 1: Three-Step Object Storage Import
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
Decision 2: Device-Card Binding Model
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
Decision 3: Unified Task Management
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
Decision 4: Batch Operation Result Display
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)
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
- Follow ElDescriptions layout for detail pages
Architecture Diagrams
Device Import Flow
┌─────────┐ 1. Select CSV ┌──────────────┐
│ Admin │ ──────────────────> │ device-import│
│ │ │ (Vue) │
└─────────┘ └──────┬───────┘
│
│ 2. getUploadUrl()
v
┌──────────────┐
│ Storage │
│ Service │
└──────┬───────┘
│
│ 3. Upload to OSS
v
┌──────────────┐
│ Object │
│ Storage │
└──────────────┘
│
│ 4. importDevices(file_key)
v
┌──────────────┐
│ Device │
│ Service │
└──────┬───────┘
│
│ 5. Create Task
v
┌──────────────┐
│ Task │
│ Management │
└──────────────┘
Device-Card Binding
┌─────────┐ ┌──────────────┐
│ Device │ ───── bound to ────> │ Card │
│ │ (1 to N) │ │
└─────────┘ └──────────────┘
│ │
│ has_many bindings │ has_one binding
v v
┌─────────────────────────────────────────────┐
│ DeviceCardBinding │
│ - device_id │
│ - card_id │
│ - slot_position (1-4) │
│ - bound_at │
└─────────────────────────────────────────────┘
Data Flow
Device List Page Load
- User navigates to /asset-management/device-list
- Vue component mounts, calls DeviceService.getDevices(params)
- Backend returns paginated device list with bound_card_count
- Table renders with status switches and action buttons
Batch Allocation Flow
- User selects devices, clicks "Batch Allocate"
- Dialog opens with shop selector
- User selects shop, adds remarks, confirms
- Call DeviceService.allocateDevices({ device_ids, shop_id, remarks })
- Backend processes each device, returns results array
- Dialog shows summary and failed device details
Card Binding Flow
- User opens device detail page
- Clicks "Bind Card" for an empty slot
- Dialog shows card search and slot selection
- User selects card and slot (e.g., slot 3)
- Call DeviceService.bindCard(device_id, { card_id, slot_position: 3 })
- Backend validates slot is empty, creates binding
- Refresh device detail to show new binding
Migration Plan
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:
- Replace ElUpload with three-step upload logic
- Add getUploadUrl call
- Add uploadFile to object storage
- Add importDevices call with file_key
- Remove mock import records
- Link to task-management for tracking
- 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)
Extending Task Management
Current State:
- Only handles ICCID import tasks
- Single task type rendering
Migration Steps:
- Add task_type filter dropdown (default: show all)
- Add task_type badge in task list
- Task detail page: switch rendering based on task_type
- 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)
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
- Import devices via CSV → verify task created → check task detail
- Search devices → select multiple → allocate to shop → verify shop assignment
- View device detail → bind card to slot 2 → unbind → verify empty slot
- 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)
- Large CSV imports handled asynchronously (no frontend timeout)
Security Considerations
- Authorization: All device operations require admin role
- Input Validation:
- Device number format validation
- Slot position range (1 to max_sim_slots)
- CSV file size limits
- Object Storage Security: Pre-signed URLs expire after 15 minutes
- Batch Operation Limits: Backend enforces max 1000 devices per batch
- XSS Prevention: All user inputs sanitized before rendering
Open Questions
None at this time. All major design decisions have been made based on existing system patterns and API specifications.