Files
luo 17d2eeebc5
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 6m55s
fix: 回调配置, 调整信用位置, 套餐
2026-07-29 18:20:42 +08:00

320 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## ADDED Requirements
### Requirement: Backend-first field semantics
The admin frontend SHALL use backend-provided business facts for real-name status, payment methods, estimated expiry, approval status, task status, balances, available amounts, debt amounts, and asset identifiers. The frontend MUST NOT infer or recalculate these values from partial data.
#### Scenario: Payment methods are displayed from the API
- **WHEN** the asset or recharge API returns `allowed_payment_methods`
- **THEN** the admin or related integration UI uses that list instead of hard-coded payment rules
#### Scenario: Financial amounts are displayed from the API
- **WHEN** a fund or credit response returns calculated balances
- **THEN** the frontend displays the returned values in fen-based data and only converts them for presentation
### Requirement: Admin asset and shop integration
The admin frontend SHALL support the confirmed July filters and fields for shops, IoT cards, devices, orders, exchanges, and assets, including contact-phone search, salesperson data, client login restriction, real-name filters, separate old/new exchange searches, asset identifiers, and estimated final expiry.
#### Scenario: Exchange assets are searched independently
- **WHEN** a user enters old and new asset search terms
- **THEN** the frontend sends `old_asset_keyword` and `new_asset_keyword` independently and preserves AND filtering semantics
#### Scenario: A shop login restriction is edited
- **WHEN** an authorized user updates a shop
- **THEN** the request includes the optional `client_login_disabled` value and the list/detail views display the returned value
### Requirement: Package allocation and series authorization
The admin frontend SHALL use the existing package and series authorization APIs for default expiry settings, override expiry settings, package multi-selection, authorization detail lookup, and package add/update/remove operations.
#### Scenario: A series authorization is edited
- **WHEN** a user opens an existing authorization
- **THEN** the frontend queries by authorization record ID, marks authorized package IDs, and keeps company cost price separate from authorization cost price
#### Scenario: Authorized packages are updated
- **WHEN** a user submits package changes
- **THEN** the frontend sends a 1100 item `packages[]` array, uses `remove=true` for removals, and loads additional package pages when the series contains more than 100 packages
### Requirement: Credit and system configuration integration
The admin frontend SHALL integrate role default credit, shop actual credit, fund summary, and controlled system configuration APIs using fen-based amounts, server-calculated values, optimistic-lock versions, and registered configuration validation metadata.
#### Scenario: A shop credit limit update conflicts
- **WHEN** the credit update is rejected because the wallet `version` is stale
- **THEN** the frontend refreshes the latest fund summary and asks the user to retry with the latest version
#### Scenario: A configuration is read-only
- **WHEN** a configuration item is returned with `readonly=true`
- **THEN** the frontend disables its editing control and does not submit an update for that item
### Requirement: WeCom approval status integration
The admin frontend SHALL provide the confirmed WeCom configuration workflow and SHALL display business approval status as read-only for refund and offline recharge records when the approval provider is WeCom or an approval instance exists.
#### Scenario: A WeCom business record is awaiting approval
- **WHEN** a refund or offline recharge record has `approval_provider=wecom` or an `approval_instance_id`
- **THEN** the frontend displays the backend approval status and hides legacy manual approval actions
#### Scenario: A template mapping is rejected
- **WHEN** the backend rejects a scene template mapping
- **THEN** the frontend preserves the submitted form values and displays the backend validation message
### Requirement: Notifications, asynchronous tasks, exports, and Gateway controls
The admin frontend SHALL integrate low-balance notifications, expiring asset summaries, batch purchase tasks, export tasks, card speed tiers, and device allocation tasks according to the confirmed task, CSV, datasource, and timeout semantics.
#### Scenario: A batch task is restored
- **WHEN** a user reloads or re-enters a batch purchase or device allocation page
- **THEN** the frontend queries the existing task detail using the saved task identifier and does not create a duplicate task
#### Scenario: A speed tier is selected
- **WHEN** a user operates on an IoT card
- **THEN** the frontend submits a fixed speed-tier code from `-1` through `8`; a device page does not expose a speed-tier control
### Requirement: H5 and C-end work is explicitly external
The July integration specification SHALL identify H5/C-end changes as external follow-up work. This change MUST NOT modify H5/C-end pages or their API call implementations.
#### Scenario: A requirement depends on C-end behavior
- **WHEN** a requirement involves C-end login restriction, real-name flow, payment methods, asset display, order renewal, or C-end notifications
- **THEN** the item is marked as an external dependency and excluded from this change's implementation tasks
### Requirement: Permission and error boundaries
The admin frontend SHALL apply confirmed permission codes to new pages, buttons, and row actions, enforce data isolation in the UI, and present stable user-facing errors for authentication, authorization, validation, timeout, conflict, and unknown-result cases.
#### Scenario: A user lacks an action permission
- **WHEN** the current account does not have the configured permission code
- **THEN** the corresponding page action is hidden or disabled and the frontend does not invoke the API
#### Scenario: An asynchronous operation has an unknown result
- **WHEN** a task or Gateway operation times out with an unknown result
- **THEN** the frontend preserves the current data, shows a safe status message, and provides a refresh or manual verification path instead of blindly retrying
### Requirement: Documented API contract alignment
The admin frontend SHALL implement the July integration against the documented API paths and request/response contracts. The documented interface files under `docs/所需接口文档/` SHALL be treated as the source of truth for parameter names, enum values, nullable fields, pagination, permissions, and HTTP error semantics.
#### Scenario: A page loads a documented resource
- **WHEN** a July page queries or mutates a resource
- **THEN** it uses the corresponding documented `/api/admin` endpoint and sends the documented parameter names without translating them to legacy frontend names
#### Scenario: A response contains a nullable or enumerated field
- **WHEN** a documented response returns a nullable field, numeric status, string enum, or `*_name` display field
- **THEN** the frontend preserves the nullable state, uses the documented enum values, and prefers the backend display name instead of inventing a local label
#### Scenario: An API rejects a request
- **WHEN** a documented endpoint returns `400`, `401`, `403`, or `500`
- **THEN** the frontend applies the shared authentication, authorization, validation, server-error, and retry-safe handling without exposing raw transport details as the user-facing message
### Requirement: File upload and batch task contract
The admin frontend SHALL use `POST /api/admin/storage/upload-url` for file-backed July operations, upload to the returned `upload_url` with the requested content type, and pass only the returned `file_key` to the business endpoint. Batch purchase and device allocation CSVs SHALL be UTF-8 single-column files, accept an optional UTF-8 BOM, stay within 10 MB and 1000 data rows, and preserve identifiers as text.
#### Scenario: A batch purchase is submitted
- **WHEN** the user selects one package and one payment method and uploads a valid CSV
- **THEN** the frontend calls `POST /api/admin/asset-package-batch-orders` with `file_key`, `package_id`, `payment_method`, and, for offline payment, 15 `voucher_keys`
#### Scenario: A device allocation is submitted
- **WHEN** a platform user selects an allocation target and uploads a valid device CSV
- **THEN** the frontend calls `POST /api/admin/devices/import/allocations` with `file_key`, `operation_type` set to `assign_shop` or `assign_series`, and the selected `target_id`
#### Scenario: A batch task is resumed
- **WHEN** the page is reloaded while a task is non-terminal
- **THEN** the frontend queries the saved task ID, displays the backend `status_name` and progress/result counts, stops polling at the documented terminal state, and never creates a second task
### Requirement: WeCom configuration API workflow
The admin frontend SHALL implement the documented WeCom configuration sequence: list or save applications through `/api/admin/wecom/applications`, test the application, synchronize and query visible members, set the default creator, bind system accounts through `/api/admin/accounts/{account_id}/wecom-binding`, and save or query the two supported scene mappings.
#### Scenario: A WeCom application is configured
- **WHEN** a super administrator saves valid application credentials
- **THEN** the frontend refreshes the application, allows connection testing, and clearly distinguishes `data.success=true` for access-token acquisition from completed member, template, and callback configuration
#### Scenario: A default creator is selected
- **WHEN** the user sets a default creator
- **THEN** the frontend only submits a `userid` selected from the latest synchronized visible-member list and preserves the real local submitter in business records
#### Scenario: A scene mapping is validated
- **WHEN** the user saves `refund_approval` or `offline_recharge_approval`
- **THEN** the frontend submits `application_id`, `template_id`, `control_mapping`, and `status`, preserves input on validation failure, and displays the returned verification/status fields after success
### Requirement: Approval and financial business API contract
The admin frontend SHALL use the documented refund and agent-recharge APIs for business creation and read-only approval tracking. Amounts and voucher keys SHALL follow the documented units and cardinality, and a record with `approval_provider=wecom` or `approval_instance_id` SHALL not expose legacy manual approval actions.
#### Scenario: An offline agent recharge is created
- **WHEN** a platform user submits an offline recharge
- **THEN** the frontend calls `POST /api/admin/agent-recharges` with the target `shop_id`, amount in fen, `payment_method=offline`, and 15 voucher keys
#### Scenario: A refund is awaiting WeCom approval
- **WHEN** `GET /api/admin/refunds` or `GET /api/admin/refunds/{id}` returns a WeCom approval instance
- **THEN** the frontend displays `approval_status_name`, `submitter_name`, and documented amount fields as read-only and does not show approve/reject actions
#### Scenario: Approval submission has an unknown result
- **WHEN** `approval_status=8` or the documented status name indicates an unknown submission result
- **THEN** the frontend does not recreate the business record and offers refresh or manual verification instead
### Requirement: Documented device allocation permissions
The admin frontend SHALL enforce the documented permission boundary for device allocation task list and detail APIs. Because those APIs are documented as platform-only, agent and enterprise accounts SHALL not see the allocation task entry or be allowed to invoke the task list/detail endpoints from the UI.
#### Scenario: A non-platform user opens device allocation
- **WHEN** an agent or enterprise account loads the device allocation area
- **THEN** the frontend hides the entry and does not call `/api/admin/devices/import/tasks` or `/api/admin/devices/import/tasks/{id}`
### Requirement: Agent Wallet Online Recharge Contract
The admin frontend SHALL support the documented agent wallet online QR recharge flow through `/api/admin/agent-recharges/payment-methods` and `/api/admin/agent-recharges`. The available payment methods and amount limits SHALL come from the payment-methods response; the online create request SHALL contain only `amount` in integer fen, `payment_method` from the returned methods, and a required `request_id`. Online agent recharge requests MUST NOT send `shop_id`, `payment_voucher_key`, `remark`, merchant credentials, or payment configuration identifiers.
#### Scenario: Load available online payment methods
- **WHEN** an agent opens the online recharge dialog
- **THEN** the frontend calls `GET /api/admin/agent-recharges/payment-methods`
- **AND** it displays only the returned `methods` values (`wechat` and/or `alipay`)
- **AND** it validates the integer-fen amount against the returned `min_amount` and `max_amount`
#### Scenario: No online payment method is available
- **WHEN** the payment-methods response returns an empty `methods` array
- **THEN** the frontend hides or disables the submit action
- **AND** it displays that no online payment method is currently available
- **AND** it does not call the online create endpoint
#### Scenario: Create an online QR recharge
- **WHEN** an authorized agent submits a valid online recharge
- **THEN** the frontend calls `POST /api/admin/agent-recharges` with `amount`, `payment_method`, and `request_id`
- **AND** it converts the displayed yuan amount to an integer amount in fen before submission
- **AND** it uses the returned `recharge_id` and does not derive the target shop or wallet from user-editable form fields
#### Scenario: Render the returned QR content
- **WHEN** online recharge creation succeeds with a non-empty `qr_content`
- **THEN** the frontend passes `qr_content` unchanged to the QR-code component
- **AND** it does not parse, concatenate, replace, upload, persist, or log the QR content
### Requirement: Idempotent Online Recharge Submission
The frontend SHALL preserve one `request_id` for the lifetime of one active online recharge submission. A timeout or network interruption MAY retry the same submission once with the same `request_id`, but a user-initiated new recharge MUST generate a new request ID. A conflict caused by reusing the same request ID with a different amount or payment method SHALL preserve the existing form state and require an explicit new recharge attempt.
#### Scenario: Retry after an uncertain create result
- **WHEN** the online create request times out or the network disconnects
- **THEN** the frontend retries at most once with the original `request_id`
- **AND** it does not create a new `request_id` or blindly open a second recharge dialog
#### Scenario: Start a new recharge after closing the dialog
- **WHEN** the user closes the QR dialog and clicks online recharge again
- **THEN** the frontend creates a new `request_id`
- **AND** the new amount and payment method are treated as a new active submission
#### Scenario: Reused request ID conflicts
- **WHEN** the backend rejects a request because the same `request_id` is paired with different recharge parameters
- **THEN** the frontend displays the backend business message
- **AND** it does not overwrite the original request or automatically submit another amount
### Requirement: Online Payment And Wallet Arrival Polling
The online QR recharge dialog SHALL poll only the documented local payment-status endpoint `GET /api/admin/agent-recharges/{id}/payment-status`. While the QR dialog is visible and the recharge is non-terminal, the frontend SHALL poll at approximately three-second intervals. The frontend SHALL stop polling when the dialog is hidden, the page is not visible, or the recharge status is `3` (completed), `4` (closed), `5` (refunded), or `6` (rejected). It MUST treat `status=2` as third-party payment completed but wallet arrival still processing; only `status=3` permits refreshing the wallet balance and recharge list.
#### Scenario: Payment is waiting for the user
- **WHEN** payment-status returns `status=1` and `payment_status=0`
- **THEN** the dialog displays that the user is waiting to scan and pay
- **AND** polling continues while the dialog is visible
#### Scenario: Payment is received but wallet arrival is processing
- **WHEN** payment-status returns `status=2` and `payment_status=1`
- **THEN** the dialog displays payment success and wallet arrival processing
- **AND** the frontend does not increase or refresh the wallet balance as completed
- **AND** polling continues
#### Scenario: Wallet arrival completes
- **WHEN** payment-status returns `status=3`
- **THEN** the frontend displays that the recharge has arrived
- **AND** it stops polling
- **AND** it refreshes the current wallet balance and recharge list
#### Scenario: Online recharge reaches a terminal failure state
- **WHEN** payment-status returns `status=4`, `status=5`, or `status=6`
- **THEN** the frontend displays the backend status name or an appropriate failure message
- **AND** it stops polling without automatically creating a replacement recharge
### Requirement: Recharge Source And Operation Boundaries
The recharge list and detail pages SHALL use `recharge_source` as the only business branch: `agent_online` for agent online self-recharge and `platform_offline` for platform offline代充. They SHALL prefer `recharge_source_name` for display and use the documented list filters `shop_id`, `status`, `recharge_source`, `start_date`, and `end_date`. Online records SHALL show payment information and payment/completion timestamps while hiding offline vouchers and manual approval operations. Offline records SHALL show vouchers, remarks, submitter, and approval status; they SHALL not show QR content or invoke the online payment-status endpoint.
#### Scenario: Filter online recharge records
- **WHEN** a user selects online recharge source or status filters
- **THEN** the frontend requests `GET /api/admin/agent-recharges` with the documented filter names and values
- **AND** it displays the backend recharge source name, amount, payment method, status, submitter, and timestamps
#### Scenario: Display a platform offline recharge
- **WHEN** a platform user opens a record with `recharge_source=platform_offline`
- **THEN** the detail view displays the payment voucher, remark, submitter, and approval status
- **AND** it does not render a QR code or start payment-status polling
#### Scenario: Confirm or reject an offline recharge
- **WHEN** a pending offline recharge has no WeCom approval provider or approval instance
- **THEN** an authorized platform user MAY confirm it through `POST /api/admin/agent-recharges/{id}/offline-pay` with `operation_password`
- **AND** an authorized platform user MAY reject it through `POST /api/admin/agent-recharges/{id}/reject` with a required `rejection_reason` of at most 500 characters
#### Scenario: WeCom approval controls the offline record
- **WHEN** an offline recharge has `approval_provider=wecom` or an `approval_instance_id`
- **THEN** the frontend displays approval progress as read-only
- **AND** it hides confirm and reject operations
### Requirement: Agent Recharge Export Permissions
The agent recharge list SHALL show its export entry only with `agent_recharge:export`. The export-task page SHALL protect the `agent_recharge` detail and download operations with `export_task:agent_recharge_detail` and `export_task:agent_recharge_download`, respectively. The frontend SHALL hide unauthorized controls and SHALL NOT invoke the corresponding API as a fallback.
#### Scenario: An authorized user exports agent recharges
- **WHEN** the current account has `agent_recharge:export`
- **THEN** the agent recharge page displays the export entry and opens the `agent_recharge` export flow
#### Scenario: An unauthorized user accesses export actions
- **WHEN** the current account lacks an export permission
- **THEN** the related entry, detail action, or download action is hidden
- **AND** the frontend does not call the export-task detail or download endpoint