## ADDED Requirements ### Requirement: H5 运营弹窗配置列表 后台 MUST 提供 H5 运营弹窗配置的分页列表(GET /api/admin/h5-popup-configurations),支持 `page`(1–10000)/ `page_size`(1–100)/ `enabled` 筛选,按最近更新时间倒序返回 `items/page/size/total`;列表项 MUST 包含标题、命中页面、范围(店铺/设备类型/卡类型)、优先级、频率(`frequency_text`)、启停(`enabled_text`)、受控动作、生效起止时间、版本、创建/更新人与时间。 #### Scenario: 按启停状态筛选并分页 - **GIVEN** 超管/平台账号进入「H5运营弹窗配置」列表 - **WHEN** 以 page/page_size/enabled 发起查询 - **THEN** 前端 MUST 展示分页结果(后端按最近更新时间倒序) - **AND** 切换 enabled 筛选后按新条件重新请求 #### Scenario: 范围空数组显示全部 - **GIVEN** 某条配置的 shop_ids/device_types/card_types 为空数组 - **THEN** 列表 MUST 在范围列展示「全部」 #### Scenario: 越权访问 - **GIVEN** 代理/企业/个人账号访问列表接口或直达路由 - **THEN** 后端 MUST 返回 403,前端 MUST NOT 渲染入口 - **AND** 失败文案 MUST 按后端返回原文透传 ### Requirement: 创建弹窗配置 后台 MUST 支持创建 H5 运营弹窗配置(POST /api/admin/h5-popup-configurations),`title`/`content`/`pages`/`frequency`/`starts_at`/`ends_at` 必填;`priority`/`enabled`/`action_type`/`shop_ids`/`device_types`/`card_types` 可选,范围集合空数组 = 全量。 #### Scenario: 必填校验 - **GIVEN** 用户提交创建表单 - **WHEN** title/content/pages/frequency/starts_at/ends_at 任一缺失 - **THEN** 前端 MUST 拦截并提示必填,MUST NOT 发送请求 #### Scenario: 范围集合为空数组表示全量 - **GIVEN** 用户未选择店铺/设备类型/卡类型范围 - **THEN** 请求体对应数组 MUST 传空数组,后端按全量投放处理 ### Requirement: 弹窗配置详情 后台 MUST 提供弹窗配置详情(GET /api/admin/h5-popup-configurations/{id}),返回完整配置含 `version`、`frequency_text`、`enabled_text`、`creator`/`updater`。 #### Scenario: 查看详情 - **GIVEN** 用户点击列表行「详情」 - **WHEN** 请求详情成功 - **THEN** 前端 MUST 展示完整配置要素(含版本与启停/频率中文名) ### Requirement: 更新弹窗配置 后台 MUST 支持更新弹窗配置(PUT /api/admin/h5-popup-configurations/{id}),成功即版本 +1;新版本可向原命中客户按频率重新投放一次,旧版本已投放通知的内容与快照不被改写。除 `id` 外所有字段均可选,**不传保持原值**;`action_type` 传空字符串 = 清除受控动作;`shop_ids`/`device_types`/`card_types` 传空数组 = 改为全量;`pages` 传空数组非法(页面必选);`enabled` 不传保持原值(启停刷新最近更新时间);`ends_at` 不得早于 `starts_at`;`content` 1–2000 字符、`title` 1–100 字符、`priority` 0–1000000。 #### Scenario: 更新成功版本递增并重新投放 - **GIVEN** 用户保存编辑 - **WHEN** 更新成功 - **THEN** 前端 MUST 提示「每次更新版本递增,旧版本通知保留原快照,新版本可向原命中客户按频率重新投放一次」并刷新列表 - **AND** 列表/详情版本号较更新前 +1,旧版本已投放通知内容不被改写 #### Scenario: 范围清空表示改为全量 - **GIVEN** 编辑时清空某范围集合 - **THEN** 提交对应数组 MUST 为空数组,后端按全量处理 #### Scenario: 受控动作清除 - **GIVEN** 编辑时把 action_type 从 package_purchase 切换为「无」 - **THEN** 请求体 action_type MUST 传空字符串,后端清除受控动作 #### Scenario: 页面集合不得为空 - **GIVEN** 编辑时清空全部命中页面 - **THEN** 前端 MUST 拦截提示命中页面必选,MUST NOT 传空 pages 数组 #### Scenario: 未传字段保持原值 - **GIVEN** 请求体省略某字段(如 enabled) - **THEN** 后端 MUST 保持原值;前端编辑表单整表提交,范围清空按空数组、动作清除按空字符串处理 ### Requirement: 启用与停用弹窗配置 后台 MUST 支持启用/停用弹窗配置(POST /api/admin/h5-popup-configurations/{id}/enable、/{id}/disable,请求体 `{ id }`,返回更新后的配置),停用停止新投放且历史通知在展示期内仍可见,启用参与候选匹配;两者均刷新最近更新时间(影响同优先级排序)且不递增版本。 #### Scenario: 停用需二次确认 - **GIVEN** 用户点击某启用的配置「停用」 - **WHEN** 二次确认提交 - **THEN** 前端 MUST 调用 disable 并提示「停用后停止新投放,历史通知在展示期内仍可见」 - **AND** 以返回的更新后配置刷新当前行(状态为停用、最近更新时间更新、版本不变) #### Scenario: 启用 - **GIVEN** 用户点击某停用的配置「启用」 - **THEN** 前端 MUST 调用 enable 并提示「启用后参与候选匹配」 - **AND** 以返回的更新后配置刷新当前行(状态为启用、最近更新时间更新、版本不变) ### Requirement: 角色权限与交互约定 弹窗配置入口 MUST 仅对超级管理员与平台账号可见;前端权限码固定为 `h5_popup_configuration:list`(菜单/列表)、`:create`、`:update`、`:enable`、`:disable`;页面 MUST NOT 提供删除入口(文档无 DELETE 接口);停用不清数据,列表保留历史配置。 #### Scenario: 入口按角色隐藏 - **GIVEN** 代理/企业/个人账号登录 - **WHEN** 系统渲染菜单 - **THEN** MUST NOT 展示「H5运营弹窗配置」菜单项 #### Scenario: 按钮权限码控制 - **GIVEN** 账号具备列表权限但缺 enable/disable 权限 - **THEN** 前端 MUST 只渲染具备权限的操作按钮 #### Scenario: 不提供删除入口 - **GIVEN** 用户查看列表或详情 - **THEN** 页面 MUST NOT 出现删除操作,仅提供启用/停用