From acda0d82c840995db0d304e863f14e9a17d91db3 Mon Sep 17 00:00:00 2001 From: luo Date: Fri, 3 Jul 2026 12:03:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E5=B7=B2=E4=BD=BF=E7=94=A8=E6=B5=81=E9=87=8F=E5=92=8C=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E6=80=BB=E6=B5=81=E9=87=8F=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proposal.md | 37 ++++++++++++ .../specs/asset-information/spec.md | 60 +++++++++++++++++++ .../tasks.md | 19 ++++++ src/api/modules/asset.ts | 9 ++- src/types/api/asset.ts | 6 ++ .../components/BasicInfoCard.vue | 19 ++++++ .../composables/useAssetInfo.ts | 4 +- .../asset-information/types.ts | 2 + .../iot-card-management/index.vue | 1 + .../series-grants/packages.vue | 3 +- 10 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 openspec/changes/update-asset-resolve-usage-summary/proposal.md create mode 100644 openspec/changes/update-asset-resolve-usage-summary/specs/asset-information/spec.md create mode 100644 openspec/changes/update-asset-resolve-usage-summary/tasks.md diff --git a/openspec/changes/update-asset-resolve-usage-summary/proposal.md b/openspec/changes/update-asset-resolve-usage-summary/proposal.md new file mode 100644 index 0000000..6f55fd3 --- /dev/null +++ b/openspec/changes/update-asset-resolve-usage-summary/proposal.md @@ -0,0 +1,37 @@ +# Change: 更新资产解析流量汇总展示 + +## Why + +后台“资产信息”页需要展示当前世代下所有套餐的虚流量已用量和剩余量,方便运营人员在查看资产基础信息时快速判断整体流量消耗情况。 + +后端通过 `GET /api/admin/assets/resolve/:identifier` 新增可选查询参数控制是否返回汇总字段;前端需要明确请求契约、类型契约和展示位置,避免默认查询缺失汇总数据。 + +## What Changes + +- 更新资产解析接口请求契约: + - `GET /api/admin/assets/resolve/:identifier` 支持可选 Query 参数 `include_usage_summary: boolean` + - 参数含义为是否返回当前世代流量汇总字段 `total_virtual_used_mb` / `total_virtual_remaining_mb` + - 后端未传时默认不返回汇总数据;后台“资产信息”页发起解析请求时默认传 `include_usage_summary=true` +- 更新资产解析接口响应契约: + - 新增 `total_virtual_used_mb?: number | null`,表示所有套餐已用量(MB),未请求时为 `null` + - 新增 `total_virtual_remaining_mb?: number | null`,表示所有套餐剩余量(MB),未请求时为 `null` +- 更新后台“资产信息”基础信息展示: + - 卡资产在“运营商状态”字段后新增“所有套餐已用量”和“所有套餐剩余量” + - 设备资产在“SN”字段后新增“所有套餐已用量”和“所有套餐剩余量” + - 字段值使用上述两个响应字段,并按流量大小格式化展示 + - 字段为空或未请求时显示稳定占位内容 + +## Impact + +- Affected specs: + - `asset-information` +- Affected code: + - `src/api/modules/asset.ts` + - `src/types/api/asset.ts` + - `src/views/asset-management/asset-information/types.ts` + - `src/views/asset-management/asset-information/composables/useAssetInfo.ts` + - `src/views/asset-management/asset-information/components/BasicInfoCard.vue` +- Dependencies: + - `GET /api/admin/assets/resolve/:identifier` 后端支持 `include_usage_summary` Query 参数及响应字段 +- Breaking changes: + - 无;新增请求参数和响应字段均为可选,前端资产信息页仅主动请求并展示新增数据 diff --git a/openspec/changes/update-asset-resolve-usage-summary/specs/asset-information/spec.md b/openspec/changes/update-asset-resolve-usage-summary/specs/asset-information/spec.md new file mode 100644 index 0000000..2316f44 --- /dev/null +++ b/openspec/changes/update-asset-resolve-usage-summary/specs/asset-information/spec.md @@ -0,0 +1,60 @@ +## ADDED Requirements + +### Requirement: Admin Asset Resolve Usage Summary Contract + +The admin asset information integration SHALL request usage summary data from `GET /api/admin/assets/resolve/:identifier` by default and preserve the returned current-generation virtual usage summary fields. + +#### Scenario: Asset information requests usage summary by default + +- **GIVEN** 用户在后台“资产信息”页查询资产标识符 +- **WHEN** 前端调用 `GET /api/admin/assets/resolve/:identifier` +- **THEN** 请求 Query MUST include `include_usage_summary=true` +- **AND** `include_usage_summary` MUST be treated as a boolean query parameter + +#### Scenario: Preserve usage summary fields from resolve response + +- **GIVEN** `GET /api/admin/assets/resolve/:identifier?include_usage_summary=true` 返回 `total_virtual_used_mb` 和 `total_virtual_remaining_mb` +- **WHEN** 前端解析资产详情响应 +- **THEN** 页面状态 MUST preserve `total_virtual_used_mb` as a `number | null` field +- **AND** 页面状态 MUST preserve `total_virtual_remaining_mb` as a `number | null` field +- **AND** `total_virtual_used_mb` MUST represent 所有套餐已用量(MB) +- **AND** `total_virtual_remaining_mb` MUST represent 所有套餐剩余量(MB) + +#### Scenario: Usage summary not requested + +- **GIVEN** a caller invokes `GET /api/admin/assets/resolve/:identifier` without `include_usage_summary=true` +- **WHEN** the response is parsed +- **THEN** `total_virtual_used_mb` MAY be absent or `null` +- **AND** `total_virtual_remaining_mb` MAY be absent or `null` + +### Requirement: Admin Asset Usage Summary Display + +The admin asset information view SHALL display current-generation virtual usage summary fields in both card and device asset information sections using asset-type-specific placement. + +#### Scenario: Show card usage summary after carrier status + +- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情 +- **AND** 当前资产详情状态中存在 `total_virtual_used_mb` 和 `total_virtual_remaining_mb` +- **WHEN** 页面渲染“卡信息”区域 +- **THEN** 页面 MUST keep `运营商状态` visible +- **AND** 页面 MUST display `所有套餐已用量` immediately after `运营商状态`, using `total_virtual_used_mb` +- **AND** 页面 MUST display `所有套餐剩余量` immediately after `所有套餐已用量`, using `total_virtual_remaining_mb` +- **AND** both values MUST be formatted as traffic sizes based on MB values + +#### Scenario: Show device usage summary after SN + +- **GIVEN** 用户正在查看后台“资产信息”中的设备资产详情 +- **AND** 当前资产详情状态中存在 `total_virtual_used_mb` 和 `total_virtual_remaining_mb` +- **WHEN** 页面渲染“设备信息”区域 +- **THEN** 页面 MUST keep `SN` visible +- **AND** 页面 MUST display `所有套餐已用量` immediately after `SN`, using `total_virtual_used_mb` +- **AND** 页面 MUST display `所有套餐剩余量` immediately after `所有套餐已用量`, using `total_virtual_remaining_mb` +- **AND** both values MUST be formatted as traffic sizes based on MB values + +#### Scenario: Show placeholder when usage summary is unavailable + +- **GIVEN** 用户正在查看后台“资产信息”中的卡资产详情或设备资产详情 +- **AND** `total_virtual_used_mb` or `total_virtual_remaining_mb` is `null` or absent +- **WHEN** 页面渲染对应汇总字段 +- **THEN** 页面 MUST display `-` for the unavailable field +- **AND** 页面 MUST NOT display `0` solely because the field is unavailable diff --git a/openspec/changes/update-asset-resolve-usage-summary/tasks.md b/openspec/changes/update-asset-resolve-usage-summary/tasks.md new file mode 100644 index 0000000..70d897c --- /dev/null +++ b/openspec/changes/update-asset-resolve-usage-summary/tasks.md @@ -0,0 +1,19 @@ +## 1. Contract + +- [x] 1.1 扩展资产解析接口方法,支持传入 `include_usage_summary` Query 参数。 +- [x] 1.2 扩展资产解析响应类型,新增 `total_virtual_used_mb?: number | null` 和 `total_virtual_remaining_mb?: number | null`。 +- [x] 1.3 确保后台“资产信息”页调用 `GET /api/admin/assets/resolve/:identifier` 时默认传 `include_usage_summary=true`。 + +## 2. State And Display + +- [x] 2.1 在资产信息页面状态中保留 `total_virtual_used_mb` 和 `total_virtual_remaining_mb`。 +- [x] 2.2 在卡资产“卡信息”区域的“运营商状态”后新增“所有套餐已用量”和“所有套餐剩余量”。 +- [x] 2.3 在设备资产“设备信息”区域的“SN”后新增“所有套餐已用量”和“所有套餐剩余量”。 +- [x] 2.4 新增字段按流量大小格式化展示;当响应字段为 `null`、`undefined` 或未请求时显示 `-`。 + +## 3. Verification + +- [ ] 3.1 验证资产信息页请求解析接口时 URL 包含 `include_usage_summary=true`。 +- [ ] 3.2 验证解析接口返回两个汇总字段时,卡资产详情在“运营商状态”后正确展示。 +- [ ] 3.3 验证解析接口返回两个汇总字段时,设备资产详情在“SN”后正确展示。 +- [ ] 3.4 验证两个汇总字段为 `null` 或缺失时,页面显示 `-` 且不报错。 diff --git a/src/api/modules/asset.ts b/src/api/modules/asset.ts index 39a6960..5e83a46 100644 --- a/src/api/modules/asset.ts +++ b/src/api/modules/asset.ts @@ -11,6 +11,7 @@ import { } from '@/utils/business/apiRateLimit' import type { BaseResponse, + AssetResolveParams, AssetResolveResponse, AssetRealtimeStatusResponse, AssetRefreshResponse, @@ -49,9 +50,13 @@ export class AssetService extends BaseService { * 支持虚拟号、ICCID、IMEI、SN、MSISDN * GET /api/admin/assets/resolve/:identifier * @param identifier 资产标识符(虚拟号、ICCID、IMEI、SN、MSISDN) + * @param params 查询参数 */ - static resolveAsset(identifier: string): Promise> { - return this.getOne(`/api/admin/assets/resolve/${identifier}`) + static resolveAsset( + identifier: string, + params?: AssetResolveParams + ): Promise> { + return this.getOne(`/api/admin/assets/resolve/${identifier}`, params) } /** diff --git a/src/types/api/asset.ts b/src/types/api/asset.ts index cdcc64e..304092a 100644 --- a/src/types/api/asset.ts +++ b/src/types/api/asset.ts @@ -42,6 +42,10 @@ export type UsageType = 'single_card' | 'device' // ========== 资产详情响应 ========== +export interface AssetResolveParams { + include_usage_summary?: boolean // 是否返回当前世代流量汇总字段 +} + /** * 资产解析/详情响应 * 对应接口:GET /api/admin/assets/resolve/:identifier @@ -67,6 +71,8 @@ export interface AssetResolveResponse { real_used_mb: number // 真流量已用 virtual_total_mb: number // 业务停机阈值(展示用) virtual_used_mb: number // 展示已用量 + total_virtual_used_mb?: number | null // 所有套餐已用量(MB),未请求时为 null + total_virtual_remaining_mb?: number | null // 所有套餐剩余量(MB),未请求时为 null reduction_pct: number // 展示增幅比例(小数,如 0.428571) device_protect_status?: DeviceProtectStatus // 保护期状态:none / stop / start(仅 device) activated_at: string // 激活时间 diff --git a/src/views/asset-management/asset-information/components/BasicInfoCard.vue b/src/views/asset-management/asset-information/components/BasicInfoCard.vue index 55da2be..2a46a4d 100644 --- a/src/views/asset-management/asset-information/components/BasicInfoCard.vue +++ b/src/views/asset-management/asset-information/components/BasicInfoCard.vue @@ -82,6 +82,12 @@ {{ cardInfo?.gateway_extend || '-' }} + + {{ formatUsageSummaryMb(cardInfo?.total_virtual_used_mb) }} + + + {{ formatUsageSummaryMb(cardInfo?.total_virtual_remaining_mb) }} + @@ -532,6 +544,8 @@ supplier?: string gateway_card_imei?: string gateway_extend?: string + total_virtual_used_mb?: number | null + total_virtual_remaining_mb?: number | null last_sync_time?: string | null last_data_check_at?: string | null last_real_name_check_at?: string | null @@ -710,6 +724,11 @@ props.cardInfo?.last_card_status_check_at !== undefined ) + const formatUsageSummaryMb = (value?: number | null) => { + if (value === undefined || value === null) return '-' + return formatDataSize(value) + } + // 处理轮询开关变化 const handlePollingChange = (value: string | number | boolean) => { emit('update:pollingEnabled', Boolean(value)) diff --git a/src/views/asset-management/asset-information/composables/useAssetInfo.ts b/src/views/asset-management/asset-information/composables/useAssetInfo.ts index 0740646..3118f0d 100644 --- a/src/views/asset-management/asset-information/composables/useAssetInfo.ts +++ b/src/views/asset-management/asset-information/composables/useAssetInfo.ts @@ -92,7 +92,7 @@ export function useAssetInfo() { const fetchAssetDetail = async (identifier: string) => { try { loading.value = true - const response = await AssetService.resolveAsset(identifier) + const response = await AssetService.resolveAsset(identifier, { include_usage_summary: true }) if (response.code === 0 && response.data) { const data = response.data @@ -136,6 +136,8 @@ export function useAssetInfo() { gateway_card_imei: resolvedGatewayCardImei, resolved_gateway_card_imei: resolvedGatewayCardImei, gateway_extend: data.gateway_extend ?? '', + total_virtual_used_mb: data.total_virtual_used_mb ?? null, + total_virtual_remaining_mb: data.total_virtual_remaining_mb ?? null, bound_device_id: data.bound_device_id, bound_device_no: data.bound_device_no || '', bound_device_name: data.bound_device_name || '', diff --git a/src/views/asset-management/asset-information/types.ts b/src/views/asset-management/asset-information/types.ts index 81672b5..2da0e19 100644 --- a/src/views/asset-management/asset-information/types.ts +++ b/src/views/asset-management/asset-information/types.ts @@ -30,6 +30,8 @@ export interface AssetInfo { status?: number gateway_card_imei?: string gateway_extend?: string + total_virtual_used_mb?: number | null + total_virtual_remaining_mb?: number | null last_sync_time?: string | null last_data_check_at?: string | null last_real_name_check_at?: string | null diff --git a/src/views/asset-management/iot-card-management/index.vue b/src/views/asset-management/iot-card-management/index.vue index ce76fbe..3d8260d 100644 --- a/src/views/asset-management/iot-card-management/index.vue +++ b/src/views/asset-management/iot-card-management/index.vue @@ -991,6 +991,7 @@ :identifier="operationLogsIdentifier" download-permission="iot_card:download_log_file" /> +