fix: 轮询按运营商账户限流
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 55s

This commit is contained in:
luo
2026-10-26 14:31:15 +08:00
parent 049f8257fb
commit 06684e1fc0
88 changed files with 7919 additions and 486 deletions

View File

@@ -0,0 +1,128 @@
# 轮询账户限流:后台接入交付
本次仅修改本后台前端,未修改独立 C 端、未推送 Git 或部署生产、未配置数据库菜单。真实接口权限和调度行为仍需测试环境联调。本地写操作全部通过 mock 验证。
## 后台配置与使用
新增菜单、隐藏详情、按钮编码和平台账号限制见 [权限增量清单](./权限.md#轮询管理运营商账户限流与调度2026-10-26-增量)。原有网关、轮询配置预览/统计、停复机记录 URL 和权限保留。仅超级管理员的维护项不列入普通配置清单。
UI 沿用店铺列表表头、可选列、分页和店铺详情返回/标题/两列布局;窄屏详情单列。时间统一店铺格式,运营商账户按名称远程搜索,搜索条件 >=4 才出现展开。
新增配置字段现可保存,旧文档“仅预览”已失效。四项间隔:空继承、0 禁止且不再继承、正数执行。编辑清空使用 clear_fields,保存 warnings 单独展示;不再发送保护期间隔。
## C 端需要同步哪些
**必须修改(文档明确要求)**
1. C 端资产详情调用 `GET /api/c/v1/asset/info` 后,不再展示保护期状态(`device_protect_status`),包括标签、说明、倒计时和相关状态分支(如有)。
2. 废弃字段可能仍返回固定 `none`,也可能缺失;兼容解析,但不能再依赖它决定页面展示或是否允许操作。不要把 `none` 当成新的异常状态。
**随新后端回归验证(不是要求 C 端增加后台功能)**
- 订单/套餐生效、有效期变化后,重新读取资产实际 `enable_polling`;后台可能自动恢复轮询,不要仅凭旧前端缓存维持“已关闭”。
- 指令队列启用时,业务受理不等于上游已完成停复机。若 C 端有即时完成提示,改为区分“已受理/处理中”和实际状态,并在合理刷新后确认。
- 资产详情读入口的请求检查、延迟确认及冷却由后端场景配置控制,C 端不要自行复制调度逻辑或高频循环触发。
C 端不需要新增本次后台菜单、告警/调度参数/导入接口;本仓库没有修改任何 C 端代码。若 C 端本来没有保护期 UI 或逻辑,第一项只需核查删除残留引用。
## 接口覆盖
文档 46 个新增操作中,已有 GatewayService 17 个、PollingConfigService 7 个,本次 PollingSchedulerService 补 22 个。自动测试逐条解析原文附录的 46 个方法/路径,与实际服务调用对照,不以数量代替路径验收。
下表路径省略 `/api/admin`。精确 URL、Query/Body 测试位于 `gateway.test.mjs`、`pollingConfigExtensions.test.mjs`、`pollingScheduler.test.mjs`。
| HTTP 操作 | 服务方法 | 后台入口 |
| --- | --- | --- |
| `DELETE /gateway-error-classifications/{id}` | `GatewayService.deleteRule` | 上游错误分类 |
| `DELETE /polling-carrier-params/{carrier_id}` | `PollingSchedulerService.resetCarrierParam` | 账户参数维护 |
| `DELETE /polling-rate-limit-groups/{id}` | `GatewayService.deleteGroup` | 网关限流组维护 |
| `GET /card-polling-pauses` | `PollingSchedulerService.getPauses` | 不轮询清单 |
| `GET /card-stop-resume-records` | `GatewayService.getRecords` | 停复机记录 |
| `GET /gateway-error-classifications` | `GatewayService.getRules` | 上游错误分类 |
| `GET /iot-cards/{id}/polling-state` | `PollingSchedulerService.getCardState` | 单卡/设备绑定卡/告警详情 |
| `GET /iot-cards/{id}/stop-resume-records` | `GatewayService.getCardRecords` | 单卡/告警详情 |
| `GET /polling-carrier-params` | `PollingSchedulerService.getCarrierParams` | 运营商账户调度参数 |
| `GET /polling-carrier-params/{carrier_id}` | `PollingSchedulerService.getCarrierParam` | 账户参数详情 |
| `GET /polling-check-scenes` | `PollingSchedulerService.getScenes` | 请求检查场景 |
| `GET /polling-configs/match-preview` | `PollingConfigService.previewMatch` | 配置匹配预览 |
| `GET /polling-configs/recalculate-due/{task_id}` | `PollingConfigService.getRecalculateTask` | 重算进度 |
| `GET /polling-failure-alerts` | `PollingSchedulerService.getAlerts` | 告警普通/停机被拒 |
| `GET /polling-failure-alerts/summary` | `PollingSchedulerService.getAlertSummary` | 告警账户汇总 |
| `GET /polling-failure-alerts/{id}` | `PollingSchedulerService.getAlert` | 告警详情 |
| `GET /polling-options` | `GatewayService.getOptions` | 网关各页枚举 |
| `GET /polling-rate-limit-groups` | `GatewayService.getGroups` | 网关限流组 |
| `GET /polling-rate-limit-groups/{id}` | `GatewayService.getGroup` | 网关限流组详情 |
| `GET /polling-stats/carrier-queues` | `PollingSchedulerService.getQueues` | 账户队列监控 |
| `GET /polling-stats/carriers` | `GatewayService.getCarrierStats` | 网关汇总/限流模式 |
| `GET /polling-stats/carriers/{carrier_id}/trend` | `GatewayService.getCarrierTrend` | 账户调用趋势 |
| `GET /polling-stats/pre-reset` | `PollingConfigService.getPreResetStats` | 重置日前统计 |
| `GET /polling-stats/tiers` | `PollingConfigService.getTierStats` | 分层统计 |
| `GET /polling-status-imports` | `PollingSchedulerService.getImports` | 导入任务列表 |
| `GET /polling-status-imports/{id}` | `PollingSchedulerService.getImport` | 导入逐行结果 |
| `GET /polling-status/impact` | `PollingSchedulerService.getImpact` | 单项/批量/导入确认 |
| `GET /stop-resume-rule/dry-run` | `PollingSchedulerService.dryRun` | 规则演练(只读) |
| `POST /devices/batch-update-polling-status` | `PollingSchedulerService.toggleDevices` | 设备批量/清单恢复 |
| `POST /gateway-error-classifications` | `GatewayService.createRule` | 上游错误分类 |
| `POST /gateway-error-classifications/test` | `GatewayService.testRule` | 错误分类测试 |
| `POST /iot-cards/batch-update-polling-status` | `PollingSchedulerService.toggleCards` | 卡批量/清单恢复 |
| `POST /polling-configs/match-preview` | `PollingConfigService.previewDraft` | 配置草稿预览 |
| `POST /polling-configs/recalculate-due` | `PollingConfigService.recalculateDue` | 重算到期 |
| `POST /polling-failure-alerts/close-polling` | `PollingSchedulerService.closeAlertPolling` | 告警批量关闭卡轮询 |
| `POST /polling-rate-limit-groups` | `GatewayService.createGroup` | 网关限流组维护 |
| `POST /polling-status-imports` | `PollingSchedulerService.createImport` | 创建导入 |
| `POST /stop-resume-rule/dry-run/export` | `PollingSchedulerService.exportDryRun` | 演练导出 |
| `PUT /gateway-error-classifications/{id}` | `GatewayService.updateRule` | 上游错误分类 |
| `PUT /gateway-error-classifications/{id}/status` | `GatewayService.setRuleStatus` | 上游错误分类 |
| `PUT /polling-carrier-params/{carrier_id}` | `PollingSchedulerService.saveCarrierParam` | 账户参数维护 |
| `PUT /polling-check-scenes/{scene}` | `PollingSchedulerService.saveScene` | 请求检查场景维护 |
| `PUT /polling-configs/priorities` | `PollingConfigService.updatePriorities` | 整体优先级(拖动/上移下移) |
| `PUT /polling-failure-alerts/{id}/status` | `PollingSchedulerService.handleAlert` | 告警处理 |
| `PUT /polling-rate-limit-groups/{id}` | `GatewayService.updateGroup` | 网关限流组维护 |
| `PUT /polling-rate-limit-groups/{id}/status` | `GatewayService.setGroupStatus` | 网关限流组维护 |
### 16 个既有接口改动的处理
| 既有接口 | 接入结果 |
| --- | --- |
| GET /polling-configs | 列表新增资产/套餐条件、重置日前窗口和间隔列 |
| GET /polling-configs/:id | 编辑读入全部新增字段,废弃保护间隔不再参与 |
| POST /polling-configs | 白名单保存新增匹配及窗口字段、三态间隔,展示 warnings |
| PUT /polling-configs/:id | clear_fields 显式清空,0 保留,展示 warnings |
| GET /assets/resolve/:identifier | 不再映射废弃保护期展示;其余资产身份不变 |
| GET /assets/:identifier/realtime-status | 不再更新保护期 UI 字段 |
| POST /assets/:identifier/refresh | 沿用刷新读取,界面无保护期展示 |
| GET /api/c/v1/asset/info | 独立 C 端同步项,本仓库不改 |
| PUT /carriers/:id | 0→1 首次不带确认;展示后端实际影响后确认重提 |
| GET /system-configs | 新增 polling 模块,复用 string/int/bool/json 编辑与校验 |
| GET /iot-cards/standalone | 三态是否轮询筛选,保留 false |
| GET /devices | 新增筛选与是否轮询列 |
| PATCH /assets/:identifier/polling-status | 先读 impact 确认,拒绝/取消恢复 UI,不提供强制参数 |
| POST /storage/upload-url | purpose=polling_status_import;校验 key 前缀后上传,成功才建任务 |
| POST /export-tasks | 三个新增场景;卡/设备保留 enable_polling 筛选 |
| GET /export-tasks | 三个场景列表、共用详情与下载权限;历史文件不做客户端改写 |
`PUT /system-configs/:key` 继续走原编辑能力,新增键来自服务器;本次不会自动修改 scheduler、rate_limit_mode、queue_enabled。
## 验证与限制
本地复现:
```powershell
$tests = @(rg --files scripts/tests -g '*.test.mjs')
node --import ./scripts/tests/register-esbuild.mjs --test $tests
.\node_modules\.bin\vue-tsc.cmd --noEmit
node scripts/tests/verifyBuild.mjs
node scripts/tests/verifyBuild.mjs --serve --scheduler
```
最后一项在 `http://127.0.0.1:5190` 启动本地 mock,所有 /api 被拦截;可使用 local-scheduler-platform/admin/agent/enterprise 模拟 token。该结果不等于线上后端已联调。
已覆盖:46 路径映射、22 新方法请求结构、null/0/false、清空与废弃字段、权限/身份、500 条和 CSV 50000 行边界、引号换行、部分成功、影响说明失败/取消、上传失败、迟到请求/账号范围切换/卸载、模板编译。旧实名管控回归改成后端实际文案确认。
生产构建的既有循环依赖、常量 v-model 和大 chunk 警告仍存在;未在本次跨范围重构。后端若将实名确认提示升级为独立错误码,可替换当前的语义识别,避免依赖提示中的“实名/确认”关键词。当前未知网络错误或不相关错误不会触发确认重试。
2026-10-26 最终回归:全量 191 项通过(本次调度接入新增 17 项,保留并升级原有回归)。类型检查、生产构建、针对性 ESLint / Prettier / 新增组件样式检查、全仓 UTF-8 编码检查通过。
浏览器使用本地 mock 验证了平台账号入口、特殊告警视图、批量 impact 确认与四类逐项结果、CSV 上传建任务/详情、演练汇总与明细、时间格式、网关 80% 成功率及 p95 超桶显示;检查了浅/深色与 375/390/768/1024/1440 宽度布局。开发热更新仍可能遇到项目既有 API 循环依赖,刷新后进行验收,不把热更新错误当作真实接口结果。
真实后端账号、数据范围、错误码、异步终态和导出文件内容需由部署后的联调确认;本次未进行线上写入、Git 推送或部署。

View File

@@ -258,3 +258,111 @@
| 停复机记录 | 资产管理 → 单卡信息,设备绑定卡操作区 | `device_info:stop_resume_records` |
查询、重置、分页与刷新使用页面权限,无需另配按钮。运营商名称搜索下拉需要当前账号能读取 `/api/admin/carriers`;这些前端编码不替代后端独立鉴权。
## 轮询管理:轮询配置预览与统计
本次只需新增一个菜单;已有「轮询配置」`/polling-management/config` 保留,不改旧 CRUD 权限。以下仅列普通账号需要配置的项。
| 菜单名称 | URL | 父菜单 | 菜单权限编码 |
| --- | --- | --- | --- |
| 轮询配置统计 | `/polling-management/config-stats` | 轮询管理 `/polling-management` | `polling_config_stats:page` |
| 按钮名称 | 所属菜单 | 按钮权限编码 |
| --- | --- | --- |
| 匹配预览 / 草稿预览 / 预览未保存修改 | 轮询配置 `/polling-management/config` | `polling_config:match_preview` |
| 查询重算进度 | 轮询配置 `/polling-management/config` | `polling_config:recalculate_progress` |
| 查看重置日前加密查流量效果统计 | 轮询配置统计 | `polling_config_stats:pre_reset` |
| 查看分层轮询统计 | 轮询配置统计 | `polling_config_stats:tiers` |
统计菜单权限和区域权限需同时授权;若仅授权其中一个统计区域,另一区域不会请求接口。进度查询权限只允许查询已有任务,不授予创建任务的能力。上述编码是前端配置约定,后端独立鉴权。
### 接入说明
- 当前配置预览使用 GET;草稿预览使用 POST,并在 Query 和 Body 同时传入同一 ICCID。多条草稿可覆盖已有配置或以 id=0 新增,预览不调用持久化 CRUD。
- 草稿中实名、流量、套餐、卡状态检查区分继承(null)、禁止且不再继承(0)、指定秒数(正数)。保护期已下线,表单和请求均移除。重置日前加密需明确开启,窗口和窗口内流量间隔成对设置。
- 以 2026-10-26 完整接口文档为准:资产状态、套餐状态、重置日前窗口字段现已支持创建/编辑保存;编辑清空通过 `clear_fields`,保存成功展示 `warnings`。
- 时间使用与店铺列表相同的 `formatDateTime`。运营商账户按名称远程搜索,仍提交数字 ID;搜索条件 >=4 时才显示展开按钮。
- 任务进度每 3 秒查询一次,终态、失败、关闭、路由离开和卸载后停止;保留真实 task_id、任务状态及 string 类型的响应账户 ID。
- 统计周期为 null 时显示「无周期数据」,真实 0 保留。分层统计标明 5 分钟缓存,未匹配配置的在库卡和已销售无套餐卡突出告警;刷新不宣称清除服务器缓存。
### 本地验证
```powershell
node --test scripts/tests/pollingConfigExtensions.test.mjs
.\node_modules\.bin\vue-tsc.cmd --noEmit
node scripts/tests/verifyBuild.mjs
node scripts/tests/verifyBuild.mjs --serve --polling
```
最后一条命令启动 `http://127.0.0.1:5190` 的本地模拟接口页面;所有 `/api` 请求被拦截,不会对真实后端保存配置或创建任务。当前未进行真实后端联调、菜单数据写入、Git 推送或生产部署。
2026-10-26 验证:新增轮询配置测试 20 项、全量回归 174 项通过;类型、生产构建、针对性 lint / 格式 / 编码检查通过。浏览器验证了当前预览、草稿控件、异步进度、空周期/零值统计、普通账号入口及小屏布局。生产构建仍有项目既有循环依赖、其他页面常量绑定与大 chunk 警告。
## 轮询管理:运营商账户限流与调度(2026-10-26 增量)
本节在前两次配置的基础上新增,旧菜单和旧权限不必重建。仅列非超级管理员专属的菜单/按钮;标注“平台”的项只允许 user_type=1/2,权限码不能突破身份限制。前端编码是本项目约定,后端仍需独立授权。
### 新增可见菜单
| 菜单名称 | URL | 父菜单 | 菜单权限 |
| --- | --- | --- | --- |
| 运营商账户调度参数 | `/polling-management/carrier-params` | 轮询管理 `/polling-management` | `polling_carrier_params:page` |
| 请求检查场景 | `/polling-management/check-scenes` | 轮询管理 | `polling_check_scene:page` |
| 失败告警 | `/polling-management/failure-alerts` | 轮询管理 | `polling_failure_alert:page` |
| 账户队列监控 | `/polling-management/carrier-queues` | 轮询管理 | `polling_carrier_queue:page` |
| 停复机规则演练(平台) | `/polling-management/stop-resume-dry-run` | 轮询管理 | `stop_resume_dry_run:page` |
| 不轮询清单(平台) | `/polling-management/polling-pauses` | 轮询管理 | `polling_pause:page` |
| 轮询开关导入(平台) | `/polling-management/polling-status-imports` | 轮询管理 | `polling_status_import:page` |
| 导出不轮询清单(平台) | `/asset-management/export-task-management/export-polling-pause` | 导出管理 `/asset-management/export-task-management` | `export_task:polling_pause_page` |
| 导出停机被拒告警(平台) | `/asset-management/export-task-management/export-polling-stop-refused-alert` | 导出管理 | `export_task:polling_stop_refused_alert_page` |
| 导出停复机规则演练(平台) | `/asset-management/export-task-management/export-stop-resume-rule-dry-run` | 导出管理 | `export_task:stop_resume_rule_dry_run_page` |
### 新增隐藏详情菜单
这些是独立页面,不是按钮编码,需配置隐藏路由;URL 参数保留,不替换为具体 ID。
| 菜单名称 | URL | 父菜单 | 菜单权限 |
| --- | --- | --- | --- |
| 运营商账户调度参数详情 | `/polling-management/carrier-params/detail/:carrier_id` | 轮询管理 | `polling_carrier_params:detail_page` |
| 失败告警详情 | `/polling-management/failure-alerts/detail/:id` | 轮询管理 | `polling_failure_alert:detail_page` |
| 轮询开关导入详情(平台) | `/polling-management/polling-status-imports/detail/:id` | 轮询管理 | `polling_status_import:detail_page` |
三个导出场景共用已有隐藏详情 `/asset-management/export-task-management/export-task-detail?id=任务ID`,沿用 `export_task:detail_page`,不再新增详情路由。
### 新增按钮权限编码
| 所属页面 | 按钮/能力 | 编码 |
| --- | --- | --- |
| 运营商账户调度参数 | 查看详情 | `polling_carrier_params:detail` |
| 失败告警 | 查看详情 | `polling_failure_alert:detail` |
| 失败告警 | 账户汇总 | `polling_failure_alert:summary` |
| 失败告警 | 标记已处理(仅未处理状态) | `polling_failure_alert:handle` |
| 失败告警 | 批量关闭卡轮询(平台) | `polling_failure_alert:close_polling` |
| 失败告警 | 导出停机被拒(平台) | `polling_failure_alert:export` |
| 停复机规则演练 | 导出演练明细(平台) | `stop_resume_dry_run:export` |
| 不轮询清单 | 批量开启轮询(平台) | `polling_pause:restore` |
| 不轮询清单 | 创建导出任务(平台) | `polling_pause:export` |
| 轮询开关导入 | 上传并创建任务(平台) | `polling_status_import:create` |
| 轮询开关导入 | 查看任务与逐行结果(平台) | `polling_status_import:detail` |
| IoT 卡列表 | 批量开关轮询(平台/代理) | `iot_card:batch_polling` |
| 设备列表 | 批量开关轮询(平台/代理) | `device:batch_polling` |
| 单卡资产详情 / 卡级告警详情 | 查询单卡轮询状态 | `iot_info:polling_state` |
| 设备资产详情的绑定卡 | 查询该绑定卡轮询状态 | `device_info:polling_state` |
| 导出不轮询清单 | 查看详情 / 下载 | `export_task:polling_pause_detail` / `export_task:polling_pause_download` |
| 导出停机被拒告警 | 查看详情 / 下载 | `export_task:polling_stop_refused_alert_detail` / `export_task:polling_stop_refused_alert_download` |
| 导出停复机规则演练 | 查看详情 / 下载 | `export_task:stop_resume_rule_dry_run_detail` / `export_task:stop_resume_rule_dry_run_download` |
“详情”按钮及对应隐藏详情菜单需同时授权。告警详情内查看停复机记录复用已有 `iot_info:stop_resume_records`;资产单项轮询开关复用 `iot_info:auto_polling` / `device_info:auto_polling`,无需新编码。请求检查场景、队列监控无新增普通写按钮。系统配置沿用旧菜单与权限,仅新增 polling 模块选项。
### 操作边界
- 批量只提交当前勾选 ID,最多 500 条,不扩展成全量筛选;原因必填,先读 impact 再确认。四类逐项结果完整保留,部分成功不回滚。
- 企业账号不开放批量;代理资产范围由后端校验。风险停机、已销户不提供“强制开启”。
- 账户级告警 card_id=0 不请求单卡接口、不允许勾选关闭卡轮询;停机被拒视图隐藏并剔除不支持的筛选。
- 导入仅 CSV,首行为表头,首列保留文本,最多 50000 数据行;上传成功后才创建任务。任务终态/请求失败停止自动刷新。
- 演练仅判断不执行;明细 result 不改变汇总,导出也不带 result;演练允许每页 500,其他列表最大 100。
- 调度、限流、指令队列开关不会因本次前端接入自动修改。旧并发配置仅 legacy 模式有效。
C 端同步项和接口覆盖/验证见 [本次接入交付说明](./2026-10-26-轮询账户限流-后台接入交付.md)。
本次验证:191 项回归通过;类型、生产构建、代码/样式/格式与 UTF-8 检查通过。仅本地 mock 验收,没有写入菜单数据、推送或部署;生产构建仍有项目既有循环依赖与大 chunk 警告。