From 9599a4d52be09ca84989cf360e5c53f832340f3d Mon Sep 17 00:00:00 2001 From: luo Date: Mon, 10 Aug 2026 17:27:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BB=A3=E7=90=86=E7=B3=BB=E5=88=97?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E5=92=8C=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=8E=BB=E6=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proposal.md | 18 + .../specs/asset-audit-log-navigation/spec.md | 12 + .../tasks.md | 10 + .../proposal.md | 17 + .../specs/package-series-allocation/spec.md | 17 + .../tasks.md | 14 + src/api/modules/asset.ts | 20 - src/api/modules/shopSeriesGrant.ts | 15 + .../business/OperationLogsDialog.vue | 45 -- src/types/api/asset.ts | 48 -- src/types/api/packageManagement.ts | 54 ++ src/types/components.d.ts | 1 - .../components/OperationLogsCard.vue | 692 ------------------ .../asset-information/index.vue | 10 - .../asset-management/device-list/index.vue | 20 - .../iot-card-management/index.vue | 22 - .../series-grants/index.vue | 75 +- 17 files changed, 207 insertions(+), 883 deletions(-) create mode 100644 openspec/changes/remove-legacy-asset-operation-logs/proposal.md create mode 100644 openspec/changes/remove-legacy-asset-operation-logs/specs/asset-audit-log-navigation/spec.md create mode 100644 openspec/changes/remove-legacy-asset-operation-logs/tasks.md create mode 100644 openspec/changes/update-series-grant-package-options-api/proposal.md create mode 100644 openspec/changes/update-series-grant-package-options-api/specs/package-series-allocation/spec.md create mode 100644 openspec/changes/update-series-grant-package-options-api/tasks.md delete mode 100644 src/components/business/OperationLogsDialog.vue delete mode 100644 src/views/asset-management/asset-information/components/OperationLogsCard.vue diff --git a/openspec/changes/remove-legacy-asset-operation-logs/proposal.md b/openspec/changes/remove-legacy-asset-operation-logs/proposal.md new file mode 100644 index 0000000..1b07578 --- /dev/null +++ b/openspec/changes/remove-legacy-asset-operation-logs/proposal.md @@ -0,0 +1,18 @@ +# Change: 移除旧资产操作审计日志入口 + +## Why + +IoT 卡管理、设备管理和资产信息页仍展示旧的资产操作审计日志入口与内容,与当前审计调查能力重复,且不再需要向用户提供该旧日志交互。 + +## What Changes + +- 移除 IoT 卡管理列表的“操作审计日志”操作项及其抽屉弹窗。 +- 移除设备管理列表的“操作审计日志”操作项及其抽屉弹窗。 +- 移除资产信息页内嵌的操作审计日志卡片。 +- 清理上述页面对旧日志组件的引用;全局无其他前端调用时,删除旧日志组件、前端 API 方法及专用类型;仍有调用时保留共享实现。 +- 不改变后端历史日志数据或接口。 + +## Impact + +- Affected specs: `asset-audit-log-navigation` +- Affected code: `src/views/asset-management/iot-card-management/index.vue`, `src/views/asset-management/device-list/index.vue`, `src/views/asset-management/asset-information/index.vue`, and, only if unused, `src/components/business/OperationLogsDialog.vue`, `src/views/asset-management/asset-information/components/OperationLogsCard.vue`, `src/api/modules/asset.ts`, `src/types/api/asset.ts` diff --git a/openspec/changes/remove-legacy-asset-operation-logs/specs/asset-audit-log-navigation/spec.md b/openspec/changes/remove-legacy-asset-operation-logs/specs/asset-audit-log-navigation/spec.md new file mode 100644 index 0000000..7b5190b --- /dev/null +++ b/openspec/changes/remove-legacy-asset-operation-logs/specs/asset-audit-log-navigation/spec.md @@ -0,0 +1,12 @@ +## REMOVED Requirements + +### Requirement: 旧资产操作审计日志入口 + +**Reason**: 旧资产操作审计日志交互已由当前审计调查能力替代,页面不再提供重复入口。 +**Migration**: 需要审计调查的用户通过当前审计中心和资源审计时间线访问记录。 + +#### Scenario: 查看 IoT 卡、设备或资产信息 + +- **WHEN** 用户打开 IoT 卡管理、设备管理或资产信息页面 +- **THEN** 页面不显示旧的“操作审计日志”入口、弹窗或内嵌日志卡片 +- **AND** 前端不因这些页面加载或操作而请求旧资产操作日志接口 diff --git a/openspec/changes/remove-legacy-asset-operation-logs/tasks.md b/openspec/changes/remove-legacy-asset-operation-logs/tasks.md new file mode 100644 index 0000000..9043076 --- /dev/null +++ b/openspec/changes/remove-legacy-asset-operation-logs/tasks.md @@ -0,0 +1,10 @@ +## 1. Remove User Interfaces + +- [x] 1.1 Remove the IoT card management operation-log action, state and dialog. +- [x] 1.2 Remove the device management operation-log action, state and dialog. +- [x] 1.3 Remove the embedded operation-log card from asset information. + +## 2. Cleanup and Verification + +- [x] 2.1 Search remaining consumers of the legacy log component, front-end API method and dedicated types; delete only implementations with no consumers. +- [x] 2.2 Run lint and type checks for changed files. diff --git a/openspec/changes/update-series-grant-package-options-api/proposal.md b/openspec/changes/update-series-grant-package-options-api/proposal.md new file mode 100644 index 0000000..de26014 --- /dev/null +++ b/openspec/changes/update-series-grant-package-options-api/proposal.md @@ -0,0 +1,17 @@ +# Change: 使用授权套餐候选项接口创建代理系列授权 + +## Why + +新增代理系列授权时,通用套餐分页接口不能按当前操作者、目标店铺和套餐系列返回真正可授权的套餐,也不能提供目标店铺已授权状态。 + +## What Changes + +- 新增代理系列授权弹窗的套餐选择改用 `GET /api/admin/shop-series-grants/package-options`。 +- 仅在已选择目标店铺和套餐系列后请求候选项接口,并原样传递 `shop_id` 与 `series_id`。 +- 以接口候选项作为可选集合,保留候选项的授权状态,且不再由前端额外过滤赠送套餐。 +- 为候选项接口增加前端 API 方法与类型定义,并用其价格字段初始化套餐成本价和建议零售价校验数据。 + +## Impact + +- Affected specs: `package-series-allocation` +- Affected code: `src/api/modules/shopSeriesGrant.ts`, `src/types/api/packageManagement.ts`, `src/views/package-management/series-grants/index.vue` diff --git a/openspec/changes/update-series-grant-package-options-api/specs/package-series-allocation/spec.md b/openspec/changes/update-series-grant-package-options-api/specs/package-series-allocation/spec.md new file mode 100644 index 0000000..cbd246b --- /dev/null +++ b/openspec/changes/update-series-grant-package-options-api/specs/package-series-allocation/spec.md @@ -0,0 +1,17 @@ +## ADDED Requirements + +### Requirement: 授权创建使用店铺系列套餐候选项 + +系统 SHALL 在新增代理系列授权时,使用 `GET /api/admin/shop-series-grants/package-options` 获取套餐候选项。前端 MUST 在目标店铺和套餐系列均已选择后,原样传递非空的 `shop_id` 与 `series_id`;接口返回的候选项 SHALL 作为可选套餐及其价格、授权状态的权威来源。 + +#### Scenario: 选择店铺和套餐系列后加载候选项 + +- **WHEN** 用户在新增代理系列授权弹窗中已选择目标店铺和套餐系列 +- **THEN** 前端请求 `/api/admin/shop-series-grants/package-options` 并携带所选 `shop_id` 与 `series_id` +- **AND** 前端展示返回的候选套餐而不再调用通用套餐分页接口作为该弹窗的候选来源 + +#### Scenario: 更换店铺或套餐系列 + +- **WHEN** 用户更换目标店铺或套餐系列 +- **THEN** 前端清空此前的套餐候选项和已选择套餐 +- **AND** 仅使用新组合返回的候选项进行后续选择 diff --git a/openspec/changes/update-series-grant-package-options-api/tasks.md b/openspec/changes/update-series-grant-package-options-api/tasks.md new file mode 100644 index 0000000..d494ff8 --- /dev/null +++ b/openspec/changes/update-series-grant-package-options-api/tasks.md @@ -0,0 +1,14 @@ +## 1. API and Types + +- [x] 1.1 Add the package-options API method and response types defined by the August interface document. + +## 2. Create Grant Dialog + +- [x] 2.1 Load package candidates only after both shop and series are selected, passing their IDs unchanged. +- [x] 2.2 Use returned candidates for selection, package names, cost prices and price validation. +- [x] 2.3 Reset candidate and selected-package state when the shop or series changes. + +## 3. Verification + +- [x] 3.1 Verify request parameters, candidate rendering and selected-package submission. +- [x] 3.2 Run lint and type checks for changed files. diff --git a/src/api/modules/asset.ts b/src/api/modules/asset.ts index fa55000..1ca0810 100644 --- a/src/api/modules/asset.ts +++ b/src/api/modules/asset.ts @@ -27,8 +27,6 @@ import type { AssetOrdersResponse, UpdateAssetRealnameStatusRequest, DtoUpdateAssetRealnameStatusResponse, - AssetOperationLogsResponse, - AssetOperationLogsParams, AssetPackageUsageRecord, UpdateAssetPackageUsedDataRequest, UpdateAssetPackageExpiresAtRequest, @@ -311,22 +309,4 @@ export class AssetService extends BaseService { data ) } - - // ========== 资产操作审计日志 ========== - - /** - * 查询资产操作审计日志 - * GET /api/admin/assets/:identifier/operation-logs - * @param identifier 资产标识符 - * @param params 查询参数 - */ - static getOperationLogs( - identifier: string, - params?: AssetOperationLogsParams - ): Promise> { - return this.get>( - `/api/admin/assets/${identifier}/operation-logs`, - params - ) - } } diff --git a/src/api/modules/shopSeriesGrant.ts b/src/api/modules/shopSeriesGrant.ts index 3f1d1f5..09e7134 100644 --- a/src/api/modules/shopSeriesGrant.ts +++ b/src/api/modules/shopSeriesGrant.ts @@ -9,6 +9,7 @@ import type { CreateShopSeriesGrantRequest, UpdateShopSeriesGrantRequest, ManageGrantPackagesRequest, + ShopSeriesGrantPackageOptionsResponse, BaseResponse, PaginationResponse } from '@/types/api' @@ -45,6 +46,20 @@ export class ShopSeriesGrantService extends BaseService { return this.getOne(`/api/admin/shop-series-grants/${id}`) } + /** + * 获取代理系列授权可选套餐 + * GET /api/admin/shop-series-grants/package-options + */ + static getPackageOptions( + shopId: number, + seriesId: number + ): Promise> { + return this.getOne( + '/api/admin/shop-series-grants/package-options', + { shop_id: shopId, series_id: seriesId } + ) + } + /** * 更新代理系列授权 * PUT /api/admin/shop-series-grants/{id} diff --git a/src/components/business/OperationLogsDialog.vue b/src/components/business/OperationLogsDialog.vue deleted file mode 100644 index 0fca9a8..0000000 --- a/src/components/business/OperationLogsDialog.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - diff --git a/src/types/api/asset.ts b/src/types/api/asset.ts index c5bc18a..8293e59 100644 --- a/src/types/api/asset.ts +++ b/src/types/api/asset.ts @@ -594,51 +594,3 @@ export interface DtoUpdateAssetRealnameStatusResponse { real_name_status: number // 更新后的实名状态 (0:未实名, 1:已实名) real_name_status_name: string // 实名状态名称(中文) } - -// ========== 资产操作审计日志 ========== - -/** - * 操作审计日志项 - */ -export interface AssetOperationLogItem { - id: number // 日志ID - asset_id: number // 资产ID - asset_identifier: string // 资产标识符 - asset_type: string // 资产类型 - operation_type: string // 操作类型 - operation_desc: string // 操作描述 - result_status: 'success' | 'failed' | 'denied' // 执行结果 - created_at: string // 创建时间 - operator_id?: number // 操作人ID - operator_name: string // 操作人名称 - operator_type: string // 操作人类型 - before_data: Record // 操作前数据(原始) - after_data: Record // 操作后数据(原始) - operation_content_before?: Record // 操作内容(变更前) - operation_content_after?: Record // 操作内容(变更后) - operation_fields_desc?: Record // 字段中文说明 - batch_total: number // 批量总数 - success_count: number // 成功数量 - fail_count: number // 失败数量 - failed_items?: Array<{ iccid?: string; reason?: string }> // 失败明细 - reason?: string // 原因说明 -} - -/** - * 资产操作审计日志响应 - */ -export interface AssetOperationLogsResponse { - items: AssetOperationLogItem[] // 日志列表 - page: number // 当前页码 - page_size: number // 每页数量 - total: number // 总记录数 -} - -/** - * 资产操作审计日志查询参数 - */ -export interface AssetOperationLogsParams { - page?: number // 页码,默认1 - page_size?: number // 每页条数,默认20,最大100 - result_status?: 'success' | 'failed' | 'denied' // 执行结果 -} diff --git a/src/types/api/packageManagement.ts b/src/types/api/packageManagement.ts index 5285474..b770916 100644 --- a/src/types/api/packageManagement.ts +++ b/src/types/api/packageManagement.ts @@ -325,6 +325,60 @@ export interface ShopSeriesGrantQueryParams extends PaginationParams { status?: number // 状态筛选 } +/** + * 代理系列授权套餐候选项 + */ +export interface ShopSeriesGrantPackageOption { + authorized: boolean + id: number + package_code: string + package_name: string + package_type: 'formal' | 'addon' + series_id: number | null + series_name: string | null + is_gift: boolean + status: number + status_name: string + shelf_status: number + shelf_status_name: string + calendar_type: 'natural_month' | 'by_day' + duration_days: number | null + duration_months: number + real_data_mb: number + virtual_data_mb: number + enable_virtual_data: boolean + virtual_ratio: number + data_reset_cycle: string + cost_price: number + retail_price: number | null + suggested_retail_price: number | null + effective_retail_price: number | null + expiry_base: string + expiry_base_override: PackageAllocationExpiryBaseOverride | null + expiry_base_override_name: string + default_expiry_base: string + default_expiry_base_name: string + effective_expiry_base: string + effective_expiry_base_name: string + price_config_status: number + price_config_status_name: string + retail_price_config_status: number | null + current_commission_rate: string + profit_margin: number | null + one_time_commission_amount: number | null + tier_info?: { + current_rate?: string + next_rate?: string + next_threshold?: number | null + } + created_at: string + updated_at: string +} + +export interface ShopSeriesGrantPackageOptionsResponse { + items: ShopSeriesGrantPackageOption[] | null +} + /** * 创建代理系列授权请求 */ diff --git a/src/types/components.d.ts b/src/types/components.d.ts index c20fcf4..6680882 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -148,7 +148,6 @@ declare module 'vue' { LoginLeftView: typeof import('./../components/core/views/login/LoginLeftView.vue')['default'] MenuLayoutSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue')['default'] MenuStyleSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/MenuStyleSettings.vue')['default'] - OperationLogsDialog: typeof import('./../components/business/OperationLogsDialog.vue')['default'] OperatorSelect: typeof import('./../components/business/OperatorSelect.vue')['default'] PackageSelector: typeof import('./../components/business/PackageSelector.vue')['default'] PaymentVoucherDialog: typeof import('./../components/business/PaymentVoucherDialog.vue')['default'] diff --git a/src/views/asset-management/asset-information/components/OperationLogsCard.vue b/src/views/asset-management/asset-information/components/OperationLogsCard.vue deleted file mode 100644 index f906fe6..0000000 --- a/src/views/asset-management/asset-information/components/OperationLogsCard.vue +++ /dev/null @@ -1,692 +0,0 @@ - - - - - diff --git a/src/views/asset-management/asset-information/index.vue b/src/views/asset-management/asset-information/index.vue index 2627843..96e09c4 100644 --- a/src/views/asset-management/asset-information/index.vue +++ b/src/views/asset-management/asset-information/index.vue @@ -96,15 +96,6 @@ @navigate-to-device="handleNavigateToDevice" /> - - -
- -
@@ -204,7 +195,6 @@ import CurrentPackageCard from './components/CurrentPackageCard.vue' import PackageListCard from './components/PackageListCard.vue' import WalletTransactionCard from './components/WalletTransactionCard.vue' - import OperationLogsCard from './components/OperationLogsCard.vue' // 引入对话框组件 import { diff --git a/src/views/asset-management/device-list/index.vue b/src/views/asset-management/device-list/index.vue index 4491d03..c43ba5b 100644 --- a/src/views/asset-management/device-list/index.vue +++ b/src/views/asset-management/device-list/index.vue @@ -703,12 +703,6 @@ @confirm="handleConfirmBatchRealnamePolicy" /> - - ([]) const shopCascadeProps = { @@ -3167,17 +3158,6 @@ }) } - if (hasAuth('device:operation_logs')) { - moreActions.push({ - label: '操作审计日志', - handler: () => { - operationLogsIdentifier.value = row.virtual_no - operationLogsDialogVisible.value = true - }, - type: 'primary' - }) - } - const userType = Number(userStore.getUserInfo.user_type) const auditPermission = userType === 3 diff --git a/src/views/asset-management/iot-card-management/index.vue b/src/views/asset-management/iot-card-management/index.vue index ef810e3..bb1d843 100644 --- a/src/views/asset-management/iot-card-management/index.vue +++ b/src/views/asset-management/iot-card-management/index.vue @@ -1001,13 +1001,6 @@ @success="handleUpdateRealnameStatusSuccess" /> - - - { - operationLogsIdentifier.value = row.iccid - operationLogsDialogVisible.value = true - }, - type: 'primary' - }) - } - const userType = Number(userStore.getUserInfo.user_type) const auditPermission = userType === 3 diff --git a/src/views/package-management/series-grants/index.vue b/src/views/package-management/series-grants/index.vue index c72d500..fbfb594 100644 --- a/src/views/package-management/series-grants/index.vue +++ b/src/views/package-management/series-grants/index.vue @@ -126,8 +126,6 @@ multiple :multiple-limit="100" filterable - remote - :remote-method="searchPackages" :loading="packageLoading" clearable > @@ -139,8 +137,9 @@
首次创建必须选择 1~100 个不重复套餐
@@ -626,7 +625,14 @@ const currentStep = ref(0) // 当前步骤,0 为第一步,1 为第二步 const seriesOptions = ref([]) - const packageOptions = ref([]) + type GrantPackageOptionView = { + id: number + package_name: string + cost_price: number + suggested_retail_price?: number | null + authorized: boolean + } + const packageOptions = ref([]) const selectedPackageIds = ref([]) const searchSeriesOptions = ref([]) const searchShopOptions = ref([]) @@ -1082,7 +1088,7 @@ isFirstActivation = false }) - // 加载套餐选项(用于新增对话框) + // 加载套餐选项(用于编辑对话框) const loadPackageOptions = async (packageName?: string) => { packageLoading.value = true try { @@ -1104,8 +1110,17 @@ page += 1 } while (allPackages.length < total) - // 过滤掉赠送套餐 - packageOptions.value = allPackages.filter((pkg) => !pkg.is_gift) + packageOptions.value = allPackages + .filter((pkg) => !pkg.is_gift) + .map( + (pkg): GrantPackageOptionView => ({ + id: pkg.id, + package_name: pkg.package_name, + cost_price: pkg.cost_price ?? 0, + suggested_retail_price: pkg.suggested_retail_price, + authorized: false + }) + ) } catch (error) { console.error('加载套餐选项失败:', error) } finally { @@ -1113,12 +1128,21 @@ } } - // 搜索套餐 - const searchPackages = (query: string) => { - if (query) { - loadPackageOptions(query) - } else { - loadPackageOptions() + const loadGrantPackageOptions = async () => { + const shopId = form.shop_id + const seriesId = form.series_id + if (dialogType.value !== 'add' || !shopId || !seriesId) return + + packageLoading.value = true + try { + const res = await ShopSeriesGrantService.getPackageOptions(shopId, seriesId) + if (res.code === 0 && form.shop_id === shopId && form.series_id === seriesId) { + packageOptions.value = res.data.items || [] + } + } catch (error) { + console.error('加载授权套餐候选项失败:', error) + } finally { + packageLoading.value = false } } @@ -1263,8 +1287,8 @@ } } - // 重新加载该系列下的套餐 - loadPackageOptions() + // 仅在已选择目标店铺后加载该组合的授权套餐候选项 + loadGrantPackageOptions() } else { // 未选择系列,重置佣金类型 form.series_name = '' @@ -1278,6 +1302,17 @@ } ) + watch( + () => form.shop_id, + (newShopId, oldShopId) => { + if (dialogType.value !== 'add' || newShopId === oldShopId) return + form.packages = [] + selectedPackageIds.value = [] + packageOptions.value = [] + loadGrantPackageOptions() + } + ) + // 监听启用强制充值开关,自动设置默认强充金额 watch( () => form.enable_force_recharge, @@ -1778,16 +1813,6 @@ } if (packageItems.length > 0) { - // 验证是否包含赠送套餐 - for (const pkg of form.packages) { - const pkgInfo = packageOptions.value.find((p) => p.id === pkg.package_id) - if (pkgInfo?.is_gift) { - ElMessage.error(`套餐 ${getPackageName(pkg.package_id)} 是赠送套餐,不能参与授权`) - submitLoading.value = false - return - } - } - // 验证成本价是否在允许范围内 for (const pkg of form.packages) { const costPrice = pkg.cost_price ?? 0