fix: some
Some checks failed
构建并部署前端到测试环境 / build-and-deploy (push) Has been cancelled

This commit is contained in:
luo
2026-09-17 18:37:20 +08:00
parent 068d0c4ac2
commit 68d8f769d1
21 changed files with 1281 additions and 85 deletions

View File

@@ -0,0 +1,39 @@
# Change: 新增资产钱包自动续费配置(后台查询/保存 + 自动续费失败通知适配)
## Why
根据 `docs/产品迭代8月份/资产钱包.md`。后端测试环境(`https://cmp-api.boss160.cn``Authorization: Bearer <token>`)已提供资产钱包自动续费全局配置能力,仅有 2 个后台接口H5 侧零新增:
- 查询资产钱包自动续费配置:`GET /api/admin/asset-auto-renewal-config`
- 保存资产钱包自动续费配置:`PUT /api/admin/asset-auto-renewal-config`
后台需要提供配置页面承载总开关、适用范围、指定主套餐、到期前天数等配置;同时新增通知类型 `asset.auto_renewal.failed` 会复用既有通知接口产生新数据,需要适配展示与跳转。
## What Changes
- 新增能力 `asset-wallet-auto-renewal`
- 配置读取:`GET /api/admin/asset-auto-renewal-config`,返回唯一一份全局配置的 `enabled`+`enabled_name``scope`+`scope_name``package_ids``days_before_expiry``config_version``updater``updated_at`
- 配置保存:`PUT /api/admin/asset-auto-renewal-config`
- 请求体 `{ enabled(0/1必填), scope(all|specified必填), package_ids(uint[],仅 specified 时非空且只能选当前可售主套餐), days_before_expiry(190必填) }`
- 语义:单行配置、无新增/删除;保存即递增 `config_version`,记录操作者与前后值快照;配置变更只影响后续扫描,历史记录不重算。
- 权限:仅超级管理员与平台账号可访问;其他身份(代理/企业/个人客户)`403`,提示「无权限操作该资源或资源不存在」,无权限与不存在不区分。
- 通知适配(复用既有接口,不算新接口):
- 后台站内通知:新增通知类型 `asset.auto_renewal.failed`(类别 `expiry`、级别 `warning`),接收人是业务员/店铺账号。走既有 `GET /api/admin/notifications` + `GET /api/admin/notifications/{id}/target`;确认 `target_type``iot_card` / `device` 在导航白名单;`available=false` 时只显示正文不跳转;计入未读数。
- H5 个人客户通知:同一类型已由后端加入客户可见白名单,出现在既有 `GET /api/c/v1/notifications``unread-count`,客户侧只有文案(资产标识、套餐、原因、到期日),无跳转接口、无需新页面,本仓库无需改动。该类型属 `expiry` 类别,展示期以业务到期时间为准,套餐到期后从列表消失(既有类别行为)。
- 明确不做(避免前端空等):无自动续费记录页/异常记录页接口、无尝试记录查询接口、无「有余额就不停机」开关、无 H5 客户侧新接口。
## Impact
- Affected specs:
- `asset-wallet-auto-renewal` — 新增能力
- Affected code:
- `src/types/api/assetWallet.ts`(新增)
- `src/types/api/index.ts`
- `src/api/modules/assetWallet.ts`(新增)
- `src/api/modules/index.ts`
- `src/views/settings/asset-wallet-auto-renewal/index.vue`(新增)
- `src/router/routesAlias.ts`
- `src/router/routes/asyncRoutes.ts`
- `src/utils/business/notificationNavigation.ts`(确认/补充 `iot_card``device` 白名单)
- `src/components/core/layouts/art-notification/index.vue`(确认 `expiry` 分类覆盖新类型)
- `src/locales/langs/zh.json``src/locales/langs/en.json`

View File

@@ -0,0 +1,94 @@
## ADDED Requirements
### Requirement: 自动续费配置查询
The admin frontend SHALL load the single global asset wallet auto-renewal configuration through `GET /api/admin/asset-auto-renewal-config`. 响应字段 SHALL 包含并展示 `enabled`+`enabled_name``scope`+`scope_name``package_ids``days_before_expiry``config_version``updater``updated_at`
#### Scenario: 读取全局自动续费配置
- **WHEN** 用户进入资产钱包自动续费配置页面
- **THEN** 前端 MUST 调用 `GET /api/admin/asset-auto-renewal-config`
- **AND** 页面 MUST 展示总开关及中文名称、适用范围及中文名称、指定主套餐集合、统一到期前天数、配置版本、最近保存操作者与最近保存时间
### Requirement: 自动续费配置保存
The admin frontend SHALL save the configuration through `PUT /api/admin/asset-auto-renewal-config` with request body `{ enabled, scope, package_ids, days_before_expiry }``enabled` SHALL 取值 0/1 且必填;`scope` SHALL 取值 `all`(全部主套餐)或 `specified`(指定主套餐)且必填;`package_ids` 仅在 `scope=specified` 时必填非空且只能选择当前可售主套餐;`days_before_expiry` SHALL 取值 1 至 90 且必填。配置为单行、无新增/删除;保存 SHALL 递增 `config_version` 并记录操作者与前后值快照;配置变更 SHALL 只影响后续扫描,历史记录不重算。
#### Scenario: 保存全部主套餐范围配置
- **WHEN** 用户设置适用范围为「全部主套餐」并提交
- **THEN** 前端 MUST 调用 `PUT /api/admin/asset-auto-renewal-config`
- **AND** 请求体携带 `enabled``scope=all``days_before_expiry`
- **AND** `package_ids` 以空数组提交
- **AND** 保存成功后页面 MUST 以响应中的 `config_version``updater``updated_at` 刷新展示
#### Scenario: 保存指定主套餐范围配置
- **WHEN** 用户设置适用范围为「指定主套餐」并选择若干当前可售主套餐后提交
- **THEN** `package_ids` MUST 非空且只包含当前可售主套餐
- **AND** 前端 MUST 携带 `scope=specified` 与非空 `package_ids` 提交
#### Scenario: 参数校验
- **WHEN** `enabled` 缺失、`scope` 非法、`scope=specified``package_ids` 为空或包含不可售套餐,或 `days_before_expiry` 超出 190
- **THEN** 前端 MUST 阻止提交并给出校验提示
#### Scenario: 仅影响后续扫描
- **WHEN** 用户修改并保存配置
- **THEN** 历史自动续费记录 MUST NOT 被重算
- **AND** 新配置 MUST 只对保存后的扫描生效
### Requirement: 自动续费配置访问权限
Only super admin and platform accounts SHALL be allowed to query or save the asset wallet auto-renewal configuration. 代理、企业与个人客户等身份访问时 SHALL 返回 `403` 并提示「无权限操作该资源或资源不存在」;无权限与不存在的表现 MUST NOT 可区分。
#### Scenario: 授权访问
- **WHEN** 超级管理员或平台账号访问配置接口
- **THEN** 页面 MUST 正常查询与保存配置
#### Scenario: 无权限访问
- **WHEN** 代理、企业或个人客户账号访问配置接口
- **THEN** 页面 MUST 按 `403` 无权限处理并展示统一提示
- **AND** 前端 MUST NOT 通过响应区分无权限与资源不存在
### Requirement: 自动续费失败后台通知
The admin notification center SHALL display the new notification type `asset.auto_renewal.failed`(类别 `expiry`、级别 `warning`through the existing notification APIs接收人为业务员/店铺账号。目标 `target_type``iot_card``device` SHALL 位于通知导航白名单;当目标 `available=false` 时页面 SHALL 只展示正文且不跳转;该类型通知 SHALL 计入未读数。
#### Scenario: 展示并跳转自动续费失败通知
- **WHEN** 通知列表返回 `asset.auto_renewal.failed` 类型且目标可用
- **THEN** 通知中心 MUST 将该通知归入 `expiry` 类别展示
- **AND** 用户点击后 MUST 通过既有目标接口按 `iot_card` / `device` 跳转到对应资产详情
#### Scenario: 目标不可用
- **WHEN** 目标接口返回 `available=false`
- **THEN** 页面 MUST 只展示通知正文且不发起跳转
### Requirement: H5 客户侧通知边界
This capability SHALL NOT 新增或修改 H5 客户侧接口与页面。`asset.auto_renewal.failed` 类型已由后端加入客户可见白名单,通过既有 `GET /api/c/v1/notifications``unread-count` 返回;客户侧仅展示文案(资产标识、套餐、原因、到期日),无跳转接口、无需新页面。该类型属 `expiry` 类别,展示期以业务到期时间为准,套餐到期后从列表消失(既有类别行为)。
#### Scenario: H5 复用既有接口
- **WHEN** 客户侧通知接口返回该类型
- **THEN** 客户侧 MUST 仅展示文案且不提供跳转
- **AND** 本次变更 MUST NOT 新增 H5 客户侧接口或页面
#### Scenario: 到期展示期
- **WHEN** 对应套餐业务到期
- **THEN** 该通知按 `expiry` 类别既有行为从客户侧列表消失
### Requirement: 功能范围边界
This capability SHALL NOT 包含以下内容:自动续费记录页/异常记录页、尝试记录查询、「有余额就不停机」开关、H5 客户侧新接口。
#### Scenario: 明确不提供的功能
- **WHEN** 前端对接本次资产钱包自动续费能力
- **THEN** 页面 MUST NOT 提供或依赖自动续费记录页、异常记录页、尝试记录查询接口与「有余额就不停机」开关

View File

@@ -0,0 +1,27 @@
# Tasks: 资产钱包自动续费配置
## 1. 类型与 API
- [ ] 1.1 新增 `src/types/api/assetWallet.ts``AssetAutoRenewalConfig``enabled`/`enabled_name`/`scope`/`scope_name`/`package_ids`/`days_before_expiry`/`config_version`/`updater`/`updated_at`)、`UpdateAssetAutoRenewalConfigRequest``enabled`/`scope`/`package_ids`/`days_before_expiry`)等类型
- [ ] 1.2 `src/types/api/index.ts` 导出新类型
- [ ] 1.3 新增 `src/api/modules/assetWallet.ts`
- `getAutoRenewalConfig``GET /api/admin/asset-auto-renewal-config`
- `saveAutoRenewalConfig``PUT /api/admin/asset-auto-renewal-config`
- [ ] 1.4 `src/api/modules/index.ts` 导出新服务
## 2. 配置页面
- [ ] 2.1 `src/router/routesAlias.ts` 新增资产钱包自动续费配置路由别名,`src/router/routes/asyncRoutes.ts` 在设置下注册路由与菜单(仅超级管理员与平台账号可见)
- [ ] 2.2 新增 `src/views/settings/asset-wallet-auto-renewal/index.vue`:读取配置并展示总开关、适用范围、指定主套餐、统一到期前天数、配置版本、最近保存操作者与时间
- [ ] 2.3 实现保存表单总开关0/1、适用范围all/specified、到期前天数190 校验);指定范围时主套餐多选仅支持当前可售主套餐且非空
- [ ] 2.4 保存成功后使用响应刷新 `config_version``updater``updated_at`,不做新增/删除记录操作
## 3. 通知适配
- [ ] 3.1 确认 `src/utils/business/notificationNavigation.ts` 白名单包含 `iot_card``device`,目标可用时跳转资产详情
- [ ] 3.2 确认 `src/components/core/layouts/art-notification/index.vue``expiry` 分类可展示 `asset.auto_renewal.failed``available=false` 时只显示正文不跳转
- [ ] 3.3 确认该类型通知计入未读数与分类汇总
## 4. Verification
- [ ] 4.1 验证 GET/PUT 字段传递与参数校验enabled、scope、package_ids、days_before_expiry
- [ ] 4.2 验证指定范围时 package_ids 非空且仅可售主套餐
- [ ] 4.3 验证通知类型展示、目标跳转与不可用目标处理
- [ ] 4.4 验证非超级管理员/平台账号访问的 403 处理
- [ ] 4.5 运行 lint、类型检查与构建

View File

@@ -0,0 +1,53 @@
# Change: 新增轮询管理 - 优先队列 API 前端对接(列表 + 人工优先入队 + 单项详情)
## Why
根据 `docs/产品迭代8月份/优先队列.md`。后端测试环境(`https://cmp-api.boss160.cn``Authorization: Bearer <token>`)已提供「轮询管理 - 优先队列」能力:
- 查询优先轮询项列表:`GET /api/admin/polling-priority-items`
- 人工优先入队:`POST /api/admin/polling-priority-items`
- 查询优先轮询项详情:`GET /api/admin/polling-priority-items/{id}`
该能力服务于:
1. 查询优先轮询项:分页查询卡轮询优先项,按创建时间倒序,支持卡 `card_id`、任务类型 `task_type`、状态 `status`、触发类型 `trigger_type` 筛选;数据范围按卡所属店铺快照下推:代理只能看到自身及下级店铺资产,平台卡不可见。
2. 人工优先入队:为该卡的全部纳入轮询任务类型(实名/流量/套餐/卡状态)建立或合并优先轮询项,原因必填(最长 500 字符)。`不受` 既有人工触发的每日次数上限与 24 小时去重约束;`不修改` 调度优先级,也 `不绕过` 既有并发上限。
3. 查询优先轮询项详情:查询单条卡轮询优先项,越权与不存在返回同一响应,不产生可枚举差异,`不提供` 优先级分级、有效期与人工重触发入口。
## What Changes
- 新增能力 `polling-priority-queue`,提供:
- 优先轮询项列表:
- 分页:`GET /api/admin/polling-priority-items`,按创建时间倒序;
- 筛选:卡 `card_id`、任务类型 `task_type`、状态 `status``pending/processing/completed/failed`)、触发类型 `trigger_type`(含 `manual_trigger` 人工入队);
- 列表项字段:`id`/`card_id`/`iccid`/`task_type(_name)`/`status(_name)`/`trigger_type(_name)`/`trigger_types`/`trigger_count`/`attempt_count`/`result(_name)`/`failure_reason`/`shop_id_snapshot`/`source_order_id`/`source_package_usage_id`/`manual_operator_id(_name)`/`manual_reason`/`claimed_at`/`last_triggered_at`/`created_at`/`updated_at`
- 人工优先入队:`POST /api/admin/polling-priority-items`
- 请求 `{card_id, reason}``reason` 必填、最长 500 字符;
- 语义:为该卡的全部纳入轮询任务类型(实名/流量/套餐/卡状态)建立或合并优先轮询项;
- 约束:同一卡同一任务类型至多一条活动项,重复入队合并 `trigger_count` 与来源集合;
- 边界:`不受` 既有人工触发的每日次数上限与 24 小时去重约束;
- 边界:`不修改` 调度优先级,也 `不绕过` 既有并发上限;
- 响应:`{card_id, created_count, merged_count, task_types, task_type_names, items[{item_id, task_type(_name), status(_name), trigger_count, created, last_triggered_at}]`
- 优先轮询项详情:`GET /api/admin/polling-priority-items/{id}`
- 越权与不存在返回同一响应,不产生可枚举差异;
- `不提供` 优先级分级、有效期与人工重触发入口。
- 任务类型 `task_type``polling:realname` 实名检查 / `polling:carddata` 流量检查 / `polling:package` 套餐检查 / `polling:card_status` 卡状态检查。
- 状态 `status``pending` 待执行 / `processing` 执行中 / `completed` 已完成 / `failed` 失败出队。
- 触发类型 `trigger_type``purchase_activated` 主套餐购买后立即生效 / `renewal_activated` 续购套餐生效 / `queue_activated` 排队主套餐顺延生效 / `addon_activated` 加油包生效 / `no_valid_package` 资产无有效套餐 / `manual_trigger` 人工入队。
- 执行结果 `result``success` 成功 / `failed` 失败 / 空值表示未出结果。
- 错误响应:`400` 请求参数错误 / `401` 未认证或认证已过期 / `403` 无权访问 / `500` 服务器内部错误。
## Impact
- Affected specs:
- `polling-priority-queue` — 新增能力
- Affected code:
- `src/api/modules/pollingPriorityQueue.ts`(新增)
- `src/api/modules/index.ts`
- `src/types/api/pollingPriorityQueue.ts`(新增)
- `src/types/api/index.ts`
- `src/config/constants/augustIteration.ts`
- `src/views/polling-management/priority-queue/index.vue`(新增)
- `src/router/routesAlias.ts`
- `src/router/routes/asyncRoutes.ts`
- `src/locales/langs/zh.json`
- `src/locales/langs/en.json`

View File

@@ -0,0 +1,79 @@
## ADDED Requirements
### Requirement: 优先轮询项列表
The admin frontend SHALL provide a paginated list of card polling priority items through `GET /api/admin/polling-priority-items`. The query SHALL support `page`(默认 1最小 1`page_size`(默认 20最大 100以及筛选参数 `card_id``task_type``status``trigger_type`,并按创建时间倒序返回。
#### Scenario: 分页查询优先轮询项
- **WHEN** 用户进入优先队列页面或提交筛选条件
- **THEN** 前端 MUST 调用 `GET /api/admin/polling-priority-items`
- **AND** 携带 `page``page_size` 及所选筛选参数
- **AND** 页面 MUST 使用响应中的 `data.items``data.page``data.size``data.total` 渲染列表
#### Scenario: 数据范围下推
- **WHEN** 当前登录账号为代理
- **THEN** 列表 MUST 只返回自身及下级店铺资产对应的优先轮询项
- **AND** 平台卡对应的优先轮询项 MUST NOT 出现在列表中
### Requirement: 人工优先入队
The admin frontend SHALL enqueue a card for priority polling through `POST /api/admin/polling-priority-items` with request body `{ card_id, reason }`. The `reason` SHALL be required and no longer than 500 characters. The operation SHALL create or merge priority items for all in-scope polling task types实名/流量/套餐/卡状态of the card. The frontend SHALL NOT apply any existing manual daily quota or 24-hour deduplication constraints, SHALL NOT modify scheduling priority, and SHALL NOT bypass existing concurrency limits.
#### Scenario: 人工入队成功
- **WHEN** 用户填写卡号与原因并提交人工入队
- **THEN** 前端 MUST 调用 `POST /api/admin/polling-priority-items`
- **AND** 请求体为 `{ "card_id": 卡ID, "reason": "加急原因" }`
- **AND** 页面 MUST 展示响应中的 `created_count``merged_count``task_types``task_type_names``items`
#### Scenario: 重复入队合并
- **WHEN** 同一卡同一任务类型已存在活动优先轮询项且再次入队
- **THEN** 该卡该任务类型至多保持一条活动项
- **AND** 响应 MUST 返回合并后的项并将该次入队计入 `trigger_count`
#### Scenario: 原因校验
- **WHEN** `reason` 为空或超过 500 字符
- **THEN** 前端 MUST 阻止提交并给出校验提示
### Requirement: 优先轮询项详情
The admin frontend SHALL query a single priority polling item through `GET /api/admin/polling-priority-items/{id}`. 越权与不存在的响应 MUST 保持一致,不产生可枚举差异。该能力 SHALL NOT 提供优先级分级、有效期与人工重触发入口。
#### Scenario: 查询单项详情
- **WHEN** 用户查看某条优先轮询项详情
- **THEN** 前端 MUST 调用 `GET /api/admin/polling-priority-items/{id}`
- **AND** 页面 MUST 展示卡、任务类型、状态、触发类型、执行结果、失败原因、操作者、触发与尝试次数及相关时间字段
#### Scenario: 越权与不存在不区分
- **WHEN** 当前账号无权访问该记录或该记录不存在
- **THEN** 前端 MUST 按同一错误处理,不得通过响应差异区分越权与不存在
### Requirement: 枚举与状态映射
The admin frontend SHALL map and display the backend enums for task type, status, trigger type and result with their Chinese labels.
#### Scenario: 任务类型展示
- **WHEN** 列表返回 `task_type``polling:realname` / `polling:carddata` / `polling:package` / `polling:card_status`
- **THEN** 页面 MUST 分别展示为 实名检查 / 流量检查 / 套餐检查 / 卡状态检查
#### Scenario: 状态与触发类型展示
- **WHEN** 列表返回 `status``pending`/`processing`/`completed`/`failed`)、`trigger_type``purchase_activated`/`renewal_activated`/`queue_activated`/`addon_activated`/`no_valid_package`/`manual_trigger`)与 `result``success`/`failed`/空)
- **THEN** 页面 MUST 展示对应的中文名称与枚举值
- **AND** `result` 为空时 MUST 展示为未出结果
### Requirement: 错误响应处理
The admin frontend SHALL handle the documented error responses for the priority queue APIs: `400` 请求参数错误、`401` 未认证或认证已过期、`403` 无权访问、`500` 服务器内部错误。
#### Scenario: 权限与参数错误提示
- **WHEN** 接口返回 `401``403` 或参数校验错误
- **THEN** 页面 MUST 展示对应错误提示且不产生前端异常

View File

@@ -0,0 +1,30 @@
# Tasks: 轮询管理 - 优先队列
## 1. 类型
- [ ] 1.1 新增 `src/types/api/pollingPriorityQueue.ts`:优先轮询项 `PollingPriorityItem``id` / `card_id` / `iccid` / `task_type` / `task_type_name` / `status` / `status_name` / `trigger_type` / `trigger_type_name` / `trigger_types` / `trigger_count` / `attempt_count` / `result` / `result_name` / `failure_reason` / `shop_id_snapshot` / `source_order_id` / `source_package_usage_id` / `manual_operator_id` / `manual_operator_name` / `manual_reason` / `claimed_at` / `last_triggered_at` / `created_at` / `updated_at`
- [ ] 1.2 查询参数、分页响应 `PollingPriorityItemPageResult`、人工入队请求/响应、入队项 `PriorityItemResult` 类型
- [ ] 1.3 `src/types/api/index.ts` 导出新类型
## 2. API
- [ ] 2.1 新增 `src/api/modules/pollingPriorityQueue.ts`
- `getPriorityItems``GET /api/admin/polling-priority-items`,分页查询卡轮询优先项,按创建时间倒序,支持卡 `card_id`、任务类型 `task_type`、状态 `status`、触发类型 `trigger_type` 筛选
- `createPriorityItems``POST /api/admin/polling-priority-items`,人工优先入队,请求 `{ card_id, reason }`
- `getPriorityItemDetail``GET /api/admin/polling-priority-items/{id}`,查询单项详情
- [ ] 2.2 `src/api/modules/index.ts` 导出新服务
## 3. 页面
- [ ] 3.1 `src/router/routesAlias.ts` 新增优先队列路由别名,`src/router/routes/asyncRoutes.ts` 在轮询管理下注册路由与菜单
- [ ] 3.2 新增 `src/views/polling-management/priority-queue/index.vue`:筛选(卡/任务类型/状态/触发类型)、分页、表格展示列表项字段
- [ ] 3.3 实现任务类型、状态、触发类型、执行结果枚举映射与中文展示
- [ ] 3.4 实现人工优先入队弹窗:选择卡、原因必填校验(最长 500 字符),提交后展示 created_count / merged_count / items
- [ ] 3.5 实现单项详情查看(页面或抽屉),越权与不存在按同一错误处理
## 4. 常量与文案
- [ ] 4.1 在 `src/config/constants/augustIteration.ts`(或新增常量文件)登记任务类型/状态/触发类型/结果枚举与中文名称
- [ ] 4.2 `src/locales/langs/zh.json``src/locales/langs/en.json` 补充菜单与页面文案
## 5. Verification
- [ ] 5.1 验证筛选与分页参数正确传递、列表按创建时间倒序
- [ ] 5.2 验证人工入队成功/合并/原因校验(空与超长)
- [ ] 5.3 验证详情接口与越权/不存在不区分
- [ ] 5.4 运行 lint、类型检查与构建