feat: 完善接口19-顶部通知铃铛与站内通知中心
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m5s

This commit is contained in:
luo
2026-07-25 10:20:53 +08:00
parent eb13763020
commit 9289a6e940
37 changed files with 1264 additions and 226 deletions

View File

@@ -0,0 +1,35 @@
## Context
临期结果由后端根据资产当前套餐及排队套餐计算。管理端需要在独立列表、普通资产列表、代理首页和通知中心保持相同的临期语义。本变更只覆盖管理端C 端资产和续费能力不纳入实现范围。
## Goals / Non-Goals
- Goals: 统一消费 `estimated_final_expires_at``days_until_final_expiry``expiry_level``expiry_level_name``can_renew`;提供临期查询、排序、高亮、统计、通知跳转和管理端续费入口。
- Goals: 由后端负责临期边界、预计到期结果和通知触发,前端只负责展示和路由。
- Non-Goals: 不计算套餐接续到期时间,不修改历史订单或已购套餐,不实现 C 端入口,不展示企微临期消息。
## Decisions
- Decision: 临期独立列表使用 `GET /api/admin/expiring-assets`,查询参数保持文档约定的 `asset_type``keyword``shop_id``package_id``days_min``days_max``expires_from``expires_to``page``size`
- Decision: 临期接口负责跨分页排序,返回结果按 0-3 天优先、其余按 `estimated_final_expires_at` 升序排列;前端只保留接口顺序,不改变普通资产列表原始接口排序。
- Decision: 颜色只按剩余天数映射8-15 天粉红、4-7 天紫色、0-3 天红色。已过期和不可预计记录由接口排除,前端不把它们补入临期列表。
- Decision: 续费入口由 `can_renew` 控制,具体续费动作复用现有管理端续费/充值路由和接口,不新增 C 端逻辑。
- Decision: 通知中心继续复用现有通知接口和目标跳转协议;临期通知由后端按 15 天、7 天、3 天生成,前端按通知目标跳转到临期列表或对应资产详情。
- Alternatives considered: 前端从普通资产列表筛选临期记录。Rejected because it无法保证全量、统一排序和后端预计最终到期结果的一致性。
## Risks / Trade-offs
- 风险代理首页当前概览接口未必已有临期计数字段。Mitigation在现有首页/概览响应中增加 `expiring_card_count``expiring_device_count`,避免前端分页统计。
- 风险不同资产列表的字段结构不完全一致。Mitigation在各自 API 类型中复用同一组临期字段语义,并通过统一展示格式处理空值和等级。
- 风险续费入口的现有路由可能依赖资产类型。Mitigation`asset_type``asset_id` 选择对应管理端续费入口,按钮仅在 `can_renew=true` 时展示。
## Migration Plan
1. 先扩展管理端 API 类型和服务,确认列表、首页统计和通知目标字段。
2. 上线临期列表及普通列表高亮,再接入首页统计和续费入口。
3. 最后验证通知中心过滤、已读和跳转,不修改 C 端功能。
## Open Questions
- 代理首页现有概览接口的具体路径和响应字段名称,需要以后端接口实现为准确认。
- 管理端卡/设备续费入口的现有路由是否统一,需实现时复用当前权限和路由定义。

View File

@@ -0,0 +1,35 @@
# Change: 增加管理端临期资产高亮、通知与续费入口
## Why
管理端目前缺少统一的临期资产查询入口,运营人员需要在普通资产列表中自行识别临近到期资产,且无法从临期记录快速进入续费操作。需要基于后端统一计算的预计最终到期结果,在管理端集中展示临期资产、标记普通列表并接入站内通知。
## What Changes
- 新增管理端临期资产列表,支持资产类型、关键字、店铺、套餐、剩余天数和预计到期时间筛选。
- 临期列表按 0-3 天固定置顶,其余按预计最终到期时间升序;已过期和不可预计资产不进入列表。
- 在卡列表、设备列表和资产相关列表中增加临期颜色高亮,但不改变普通列表原有排序。
- 在代理首页增加临期卡数量和临期设备数量展示,并可跳转临期列表。
- 在临期列表提供后端返回 `can_renew` 控制的续费入口,复用现有管理端续费/充值流程。
- 接入通知中心中的 15 天、7 天、3 天临期提醒及通知跳转;管理端不展示企微临期消息。
- 所有页面直接使用后端返回的预计最终到期字段和临期等级,不在前端重新计算最终到期时间。
## Impact
- Affected specs:
- `admin-expiring-assets`
- Affected code:
- `src/api/modules``src/types/api` 中的临期资产接口及类型
- 管理端临期资产列表页面和路由
- 网卡列表、设备列表及资产信息展示组件
- 代理首页统计组件
- 通知中心跳转处理
- API contracts:
- `GET /api/admin/expiring-assets`
- 现有代理首页/概览接口增加临期卡、设备数量字段,或提供等价的管理端统计响应
- 复用现有通知查询、已读和目标跳转接口
- Out of scope:
- C 端资产页临期展示
- C 端续费按钮和 C 端续费流程
- 企微临期消息展示或企微通知改造
- 前端根据套餐明细自行推导预计最终到期时间

View File

@@ -0,0 +1,111 @@
## ADDED Requirements
### Requirement: Admin Expiring Asset Query
The admin frontend SHALL provide an expiring asset list backed by `GET /api/admin/expiring-assets`. The query SHALL support `asset_type`, `keyword`, `shop_id`, `package_id`, `days_min`, `days_max`, `expires_from`, `expires_to`, `page`, and `size`.
#### Scenario: Filter expiring assets
- **WHEN** 管理端用户打开临期资产列表并提交筛选条件
- **THEN** 前端 MUST send the documented query parameters to `GET /api/admin/expiring-assets`
- **AND** 页面 MUST display the paginated response items
#### Scenario: Exclude unavailable assets
- **WHEN** 接口返回临期列表
- **THEN** 页面 MUST not add expired assets or assets without an estimable final expiry to the list on the client side
### Requirement: Expiring Asset Fields
Each expiring asset item SHALL preserve and display the backend fields `asset_type`, `asset_id`, `identifier`, `shop_name`, `package_name`, `estimated_final_expires_at`, `days_until_final_expiry`, `expiry_level`, `expiry_level_name`, and `can_renew`. The frontend MUST use backend-provided expiry results and MUST NOT calculate the estimated final expiry from package details.
#### Scenario: Display an estimable asset
- **WHEN** 临期接口返回资产及 `estimated_final_expires_at`
- **THEN** 页面 MUST display the asset identifier, shop, package, estimated final expiry, remaining days, and backend expiry level name
#### Scenario: Preserve backend result
- **WHEN** 临期接口返回 `estimated_final_expires_at``days_until_final_expiry`
- **THEN** 前端 MUST display the returned values
- **AND** 前端 MUST NOT derive or replace them from current or queued package data
### Requirement: Expiring List Ordering And Highlighting
The expiring asset API and standalone admin list SHALL return and preserve records with 0-3 remaining days first, then order the remaining records by `estimated_final_expires_at` ascending. The list SHALL apply pink highlighting to 8-15 days, purple highlighting to 4-7 days, and red highlighting to 0-3 days.
#### Scenario: Order critical assets first
- **GIVEN** the response contains assets in multiple expiry ranges
- **WHEN** 页面渲染临期列表
- **THEN** the API response MUST place assets with 0-3 remaining days before assets with more remaining days
- **AND** the API response MUST order records in the remaining ranges by estimated final expiry ascending
- **AND** the frontend MUST preserve that cross-page ordering
#### Scenario: Highlight by remaining days
- **WHEN** 页面渲染临期资产
- **THEN** 8-15 days MUST use the pink visual treatment
- **AND** 4-7 days MUST use the purple visual treatment
- **AND** 0-3 days MUST use the red visual treatment
### Requirement: Ordinary Admin Asset List Highlighting
The admin card and device lists SHALL apply the same expiry color treatment to returned expiring asset fields without changing their existing server or client sort order.
#### Scenario: Highlight ordinary asset rows
- **WHEN** 卡列表或设备列表返回临期字段
- **THEN** 页面 MUST apply the matching expiry color treatment
- **AND** 页面 MUST preserve the list's existing ordering
### Requirement: Admin Expiring Asset Renewal Entry
The admin expiring asset list SHALL display a renewal action only when the backend item has `can_renew=true`. The action SHALL navigate to the existing management-side renewal or recharge flow for the asset type.
#### Scenario: Renewable asset
- **GIVEN** 临期资产项返回 `can_renew=true`
- **WHEN** 用户查看临期列表
- **THEN** 页面 MUST provide the management-side renewal entry
- **AND** the entry MUST target the corresponding asset and preserve asset type context
#### Scenario: Non-renewable asset
- **GIVEN** 临期资产项返回 `can_renew=false`
- **WHEN** 用户查看临期列表
- **THEN** 页面 MUST NOT display an enabled renewal action
### Requirement: Agent Dashboard Expiry Counts
The agent-facing admin home SHALL display the number of expiring cards and expiring devices using backend-provided summary fields and SHALL provide navigation to the standalone expiring asset list.
#### Scenario: Display expiry counts
- **WHEN** 代理首页概览数据返回临期卡和设备数量
- **THEN** 页面 MUST display the expiring card count and expiring device count
- **AND** 页面 MUST not calculate the counts from a paginated asset list
### Requirement: Admin Expiry Notifications
The notification center SHALL display backend-generated admin expiry reminders for 15 days, 7 days, and 3 days through the existing notification API and target navigation. The admin frontend SHALL not display WeCom expiry messages.
#### Scenario: Display expiry reminder
- **WHEN** 通知接口返回 15 天、7 天或 3 天的临期提醒
- **THEN** 通知中心 MUST classify it as a 临期提醒
- **AND** 用户点击通知后 MUST navigate to the returned management-side target
#### Scenario: Exclude WeCom expiry message
- **WHEN** 管理端加载通知中心
- **THEN** 页面 MUST not add or render a separate WeCom expiry message channel
### Requirement: Admin-Only Scope
This capability SHALL be limited to the management frontend. It SHALL NOT add or modify C-end asset expiry display, C-end renewal buttons, C-end renewal APIs, or C-end notification behavior.
#### Scenario: Keep C-end unchanged
- **WHEN** this change is implemented
- **THEN** C-end asset pages and renewal flows MUST remain outside the change scope

View File

@@ -0,0 +1,35 @@
## 1. API Contract
- [x] 1.1 增加 `GET /api/admin/expiring-assets` 的查询参数、分页响应和临期资产项类型。
- [x] 1.2 为卡列表、设备列表、资产信息和首页概览类型补充统一临期字段及临期计数字段。
- [x] 1.3 增加临期资产 API 服务方法,并确认续费入口复用现有管理端服务和权限。
## 2. Expiring Asset List
- [x] 2.1 增加或接入 `/operations/expiring-assets` 管理端路由和页面。
- [x] 2.2 实现资产类型、关键字、店铺、套餐、剩余天数和预计到期时间筛选。
- [x] 2.3 展示资产、店铺、当前套餐、预计最终到期、剩余天数和后端临期等级名称。
- [x] 2.4 实现 0-3 天置顶、其余按预计到期时间升序,并排除已过期和不可预计记录。
- [x] 2.5 按 8-15 天粉红、4-7 天紫色、0-3 天红色实现行或到期字段高亮。
- [x] 2.6 根据 `can_renew` 展示管理端续费入口,并跳转现有续费/充值流程。
## 3. Existing Admin Pages
- [x] 3.1 在网卡普通列表增加临期字段展示和颜色高亮,不改变原排序。
- [x] 3.2 在设备普通列表增加临期字段展示和颜色高亮,不改变原排序。
- [x] 3.3 在资产信息详情复用同一临期展示规则,禁止前端重新计算预计最终到期时间。
- [x] 3.4 在代理首页展示临期卡数量和临期设备数量,并支持跳转临期列表。
## 4. Notifications
- [x] 4.1 在通知中心复用临期分类,展示后端生成的 15 天、7 天、3 天临期提醒。
- [x] 4.2 使用现有通知目标跳转到临期列表或对应管理端资产详情。
- [x] 4.3 确认管理端不展示企微临期消息,不新增 C 端通知入口。
## 5. Verification
- [x] 5.1 验证临期字段直接使用后端值,前端不根据套餐明细推导最终到期时间。
- [x] 5.2 验证 0-3 天置顶、日期升序、颜色映射和普通列表原排序保持不变。
- [x] 5.3 验证 `can_renew=false` 不展示续费入口,且各资产类型使用正确管理端入口。
- [x] 5.4 验证通知中心临期分类、已读状态和跳转行为。
- [x] 5.5 运行类型检查、相关 ESLint/Stylelint、构建和 `openspec validate add-admin-expiring-assets-notifications --strict`

View File

@@ -0,0 +1,36 @@
## Context
后端通知接口已经提供稳定的管理端 REST 合约。前端需要移除对旧字段和旧响应形态的依赖,统一使用真实接口返回的数据,并把通知目标解析限制在前端已注册的内部目标映射中。
## Goals / Non-Goals
- Goals: 顶部铃铛、通知抽屉、通知中心和共享 Pinia 状态使用同一套真实 API 类型。
- Goals: 保证未读数、分类汇总、列表已读状态在单条已读和全部已读后与后端同步。
- Goals: 受控目标只能通过 `target_type``target_id``target_key` 白名单映射到内部页面。
- Non-Goals: 不实现 C 端通知,不支持任意 URL 跳转,不改变通知产生规则。
## Decisions
- Decision: 通知列表请求使用 `is_read?: boolean`,不再以 `read_status` 字符串代替后端参数。
- Decision: 通知正文统一使用 `body`;必要时仅在迁移读取阶段兼容旧数据,不再把旧 `content` 作为 API 契约类型。
- Decision: 顶部徽标优先使用 `unread-count.data.display_count`,数值统计使用 `data.count`;不在前端重新格式化徽标文本。
- Decision: 未读汇总只保存四个固定分类计数和总数抽屉最近10条通过 `GET /api/admin/notifications?page=1&page_size=10` 获取,避免把分类汇总误当通知列表。
- Decision: 全部已读服务方法接收可选 `category`,请求体始终为对象;通知中心的“全部已读”传空对象,分类操作可传具体类别。
- Decision: `target_type``target_key` 由前端白名单映射到已注册路由,`target_id` 仅作为参数;未知、无权限或已失效目标只展示正文,不跳转。
- Alternatives considered: 继续保留旧字段的宽松兼容类型。Rejected because it会掩盖真实 API 字段错误,并导致筛选和已读状态请求不符合后端契约。
## Risks / Trade-offs
- 风险后端新增通知类型。Mitigation类型字段保持 string页面对未知类型采用原值展示。
- 风险受控目标类型新增。Mitigation未知目标默认不可跳转不执行后端返回的任意路径。
- 风险列表与汇总请求并行时数据短暂不一致。Mitigation单条已读/全部已读成功后刷新未读数、汇总和当前列表。
## Migration Plan
1. 先更新类型、API service 和通知 store 的真实响应结构。
2. 再更新顶部抽屉和通知中心字段、筛选及已读交互。
3. 最后替换目标跳转映射并验证未知目标安全降级。
## Open Questions
- `target_type``target_key` 的完整白名单值需要以后端实际返回样例补充;未确认值统一按不可跳转处理。

View File

@@ -0,0 +1,40 @@
# Change: 按实际接口更新管理端通知中心
## Why
通知中心已有前端链路,但当前类型和页面仍兼容旧字段,例如 `content``read_status`、旧的未读摘要结构和可路由目标结构。后端接口文档已更新为实际可用的管理端 API需要按真实响应字段和受控目标协议修正通知铃铛、抽屉、通知中心及状态同步逻辑。
## What Changes
- 按实际接口更新通知项字段:`body``is_read``read_at``ref_type``ref_id``ref_key`
- 按实际接口更新通知列表筛选参数:使用 `is_read` boolean支持 `category``type``severity``page``page_size`
- 接入真实未读数响应 `{ count, display_count }`,顶部徽标直接展示后端 `display_count`
- 接入真实未读分类汇总 `{ approval, expiry, sync, system, total }`抽屉最近通知通过通知列表接口获取最多展示10条。
- 全部已读调用 `PUT /api/admin/notifications/read-all`,支持可选 `category` 请求体。
- 受控目标按 `target_type``target_id``target_key` 建立前端白名单映射,不接受或执行任意 URL/路由字段。
- 通知中心展示正文 `body`、读取状态 `is_read`、首次已读时间 `read_at`,并支持余额、审批、临期、同步/系统分类。
- 本变更只覆盖管理端,明确不接入 C 端 `/api/c/v1/notifications`
## Impact
- Affected specs:
- `notification-center`
- Affected code:
- `src/types/api/notification.ts`
- `src/api/modules/notification.ts`
- `src/store/modules/notification.ts`
- `src/components/core/layouts/art-header-bar/index.vue`
- `src/components/core/layouts/art-notification/index.vue`
- `src/views/notifications/index.vue`
- `src/utils/business/notificationNavigation.ts`
- API contracts:
- `GET /api/admin/notifications`
- `PUT /api/admin/notifications/{id}/read`
- `PUT /api/admin/notifications/read-all`
- `GET /api/admin/notifications/unread-count`
- `GET /api/admin/notifications/unread-summary`
- `GET /api/admin/notifications/{id}/target`
- Out of scope:
- C 端 `/api/c/v1/notifications` 接口和页面
- 创建、编辑或删除通知
- WebSocket、浏览器推送和新的实时推送机制

View File

@@ -0,0 +1,84 @@
## ADDED Requirements
### Requirement: Admin Notification Contract
The admin notification frontend SHALL use the actual notification contract returned by `GET /api/admin/notifications`. Each item SHALL preserve `id`, `title`, `body`, `category`, `type`, `severity`, `is_read`, `created_at`, `read_at`, `ref_type`, `ref_id`, and `ref_key`.
#### Scenario: Display actual notification fields
- **WHEN** 管理端通知列表返回通知项
- **THEN** 页面 MUST display `title` and `body`
- **AND** 页面 MUST use `is_read` for read state
- **AND** 页面 MUST preserve `read_at` and `ref_key`
### Requirement: Admin Notification List Filters
The notification center SHALL query `GET /api/admin/notifications` with `category`, `type`, `severity`, `is_read`, `page`, and `page_size`. The frontend SHALL not send `read_status` as a replacement for `is_read`.
#### Scenario: Filter unread notifications
- **WHEN** 用户选择未读筛选
- **THEN** 前端 MUST send `is_read=false`
- **AND** 页面 MUST render the paginated `data.items` response
#### Scenario: Filter read notifications
- **WHEN** 用户选择已读筛选
- **THEN** 前端 MUST send `is_read=true`
### Requirement: Unread Count And Summary
The admin header and notification drawer SHALL use `GET /api/admin/notifications/unread-count` and `GET /api/admin/notifications/unread-summary`. The unread count response SHALL preserve `count` and `display_count`; the summary response SHALL preserve `approval`, `expiry`, `sync`, `system`, and `total`.
#### Scenario: Display backend unread badge
- **WHEN** 未读数接口返回 `count` and `display_count`
- **THEN** 顶部铃铛 MUST display the backend `display_count`
- **AND** shared notification state MUST preserve the numeric `count`
#### Scenario: Load drawer notifications
- **WHEN** 用户打开顶部通知抽屉
- **THEN** 前端 MUST use the summary endpoint for category counts
- **AND** 前端 MUST query the notification list endpoint with `page=1` and `page_size=10` for recent notifications
### Requirement: Read State Operations
The admin frontend SHALL mark notifications read through `PUT /api/admin/notifications/{id}/read` and mark notifications read in bulk through `PUT /api/admin/notifications/read-all`. The bulk request SHALL support an optional JSON `category` field.
#### Scenario: Mark one notification read
- **WHEN** 用户点击通知
- **THEN** 前端 MUST call the single-read PUT endpoint before target resolution
- **AND** after success MUST refresh the shared unread count and relevant notification state
#### Scenario: Mark all notifications read
- **WHEN** 用户点击全部已读
- **THEN** 前端 MUST call `PUT /api/admin/notifications/read-all` with an object body
- **AND** after success MUST refresh the list, summary and unread count
### Requirement: Controlled Notification Target
The admin frontend SHALL resolve notification navigation through `GET /api/admin/notifications/{id}/target`. It SHALL map only recognized `target_type` or `target_key` values to registered internal routes and SHALL use `target_id` as a route parameter when required.
#### Scenario: Navigate to recognized target
- **WHEN** 目标接口返回可用的受支持 `target_type``target_key``target_id`
- **THEN** 前端 MUST navigate to the corresponding registered internal route
- **AND** 前端 MUST not execute an arbitrary URL from the response
#### Scenario: Handle unknown target safely
- **WHEN** 目标类型未知、目标不可用或当前路由未注册
- **THEN** 前端 MUST not navigate
- **AND** 页面 MUST display the notification body or an unavailable-target message
### Requirement: Admin-Only Notification Scope
This notification capability SHALL apply only to the admin APIs under `/api/admin/notifications` and the admin `/notifications` interface. It SHALL not add or modify C-end `/api/c/v1/notifications` APIs or pages.
#### Scenario: Exclude C-end notifications
- **WHEN** this change is implemented
- **THEN** C-end notification APIs and pages MUST remain outside the change scope

View File

@@ -0,0 +1,34 @@
## 1. Contract And Types
- [x] 1.1 按实际接口更新通知项、列表响应、未读数、未读汇总和受控目标类型。
- [x] 1.2 将通知列表筛选参数从 `read_status` 改为 `is_read?: boolean`
- [x] 1.3 更新通知 service未读数、未读汇总、列表、单条已读、全部已读和目标接口的请求/响应类型。
## 2. Shared Notification State
- [x] 2.1 使用 `data.count``data.display_count` 同步未读数量及顶部徽标。
- [x] 2.2 使用 `{ approval, expiry, sync, system, total }` 保存分类汇总并通过通知列表接口加载抽屉最近10条。
- [x] 2.3 单条已读和全部已读成功后刷新真实未读数、汇总和相关通知状态,避免仅修改本地计数。
## 3. Header And Notification Center
- [x] 3.1 顶部铃铛展示后端 `display_count`支持0、普通数量和 `99+`
- [x] 3.2 抽屉展示最近10条真实通知支持全部、审批、临期、同步/系统分类和进入通知中心。
- [x] 3.3 通知中心使用 `body``is_read``read_at` 展示正文、状态和已读时间。
- [x] 3.4 通知中心筛选使用 `category``type``severity``is_read``page``page_size`
- [x] 3.5 严重级别支持 `info``warning``error``critical`,未知值稳定展示。
- [x] 3.6 全部已读和单条已读严格按实际 PUT 接口调用。
## 4. Controlled Navigation And Scope
- [x] 4.1 根据 `target_type``target_key``target_id`实现前端内部目标白名单映射。
- [x] 4.2 未知、无权限或不可用目标只展示通知正文,不执行任意 URL 或未注册路由跳转。
- [x] 4.3 保留管理端通知中心路由和顶部入口,不新增 C 端通知接口、页面或逻辑。
## 5. Verification
- [x] 5.1 验证真实通知字段 `body``is_read``read_at``ref_key` 正确展示和保存。
- [x] 5.2 验证未读徽标、分类汇总、抽屉列表和通知中心状态一致。
- [x] 5.3 验证 `is_read=true/false` 筛选及全部已读 `category` 请求体。
- [x] 5.4 验证受控目标白名单和未知目标安全降级。
- [x] 5.5 运行类型检查、lint、相关测试、构建和 `openspec validate update-admin-notification-center-api --strict`

View File

@@ -7,7 +7,7 @@
## What Changes
- 在店铺新建和编辑表单新增可清空、可搜索的“平台业务员”选择器。
- 候选业务员复用启用的平台账号查询接口,展示账号名和手机号摘要。
- 候选业务员使用店铺业务员候选接口,展示账号名和手机号摘要。
- 扩展店铺创建、更新、列表查询、列表返回和详情返回的数据契约,支持 `business_owner_account_id` 及业务员展示字段。
- 在店铺列表新增业务员列和按业务员筛选项,在店铺详情展示业务员名称和手机号摘要。
- 已停用的已绑定业务员仅用于历史列表和详情展示,不能作为新建或编辑表单的候选项。
@@ -26,4 +26,5 @@
- `POST /api/admin/shops`
- `PUT /api/admin/shops/{id}`
- `GET /api/admin/shops`
- `GET /api/admin/accounts?account_type=platform&status=1`
- `GET /api/admin/shops/{id}`
- `GET /api/admin/shops/business-owner-candidates`

View File

@@ -27,7 +27,9 @@ The system SHALL allow users to assign or clear an optional platform salesperson
#### Scenario: Do not expose disabled accounts as editable candidates
- **WHEN** 用户打开或搜索“平台业务员”选择器
- **THEN** 系统 MUST 仅查询 `account_type=platform``status=1` 的账号
- **THEN** 系统 MUST query `GET /api/admin/shops/business-owner-candidates`
- **AND** 请求 MUST support `keyword``page``page_size`
- **AND** 候选项 MUST use `id``username` and `phone_summary`
- **AND** 已停用平台账号 MUST NOT 作为新建或编辑候选项显示
#### Scenario: No eligible salesperson exists
@@ -45,7 +47,7 @@ The shop list SHALL display the assigned salesperson and support filtering by `b
- **WHEN** 用户查看店铺列表
- **THEN** 列表 MUST 显示业务员列
- **AND** 已绑定业务员的行 MUST 展示 `business_owner_name``business_owner_phone_masked`
- **AND** 已绑定业务员的行 MUST 展示 `business_owner_username``business_owner_phone_summary`
#### Scenario: Filter shops by salesperson
@@ -57,7 +59,7 @@ The shop list SHALL display the assigned salesperson and support filtering by `b
- **GIVEN** 店铺历史上已绑定的平台业务员后来被停用
- **WHEN** 用户查看店铺列表
- **THEN** 列表 MUST 继续展示该店铺返回的 `business_owner_name``business_owner_phone_masked`
- **THEN** 列表 MUST 继续展示该店铺返回的 `business_owner_username``business_owner_phone_summary`
### Requirement: Shop Detail Salesperson Display
@@ -72,7 +74,8 @@ The shop detail SHALL display the assigned salesperson name and masked phone fro
- **GIVEN** 店铺绑定的业务员已停用
- **WHEN** 用户查看该店铺详情
- **THEN** 页面 MUST 继续使用详情响应中的 `business_owner_name``business_owner_phone_masked` 展示历史业务员信息
- **THEN** 页面 MUST request `GET /api/admin/shops/{id}`
- **AND** 页面 MUST 继续使用详情响应中的 `business_owner_username``business_owner_phone_summary` 展示历史业务员信息
#### Scenario: Do not add distribution or commission semantics

View File

@@ -3,7 +3,7 @@
- [x] 1.1 更新店铺创建和编辑请求类型,支持可空的 `business_owner_account_id`
- [x] 1.2 更新店铺列表查询类型,支持 `business_owner_account_id` 筛选参数。
- [x] 1.3 更新店铺列表和详情响应类型,读取业务员 ID、名称和脱敏手机号字段。
- [x] 1.4 复用启用平台账号查询接口,读取 `account_id``account_name``phone_masked` 作为候选项。
- [x] 1.4 接入店铺业务员候选接口,读取 `id``username``phone_summary` 作为候选项。
## 2. Shop Form UI