fetch(add): 订单管理-企业设备
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 3m30s

This commit is contained in:
sexygoat
2026-01-29 15:43:45 +08:00
parent 1812b7a6c4
commit 841cf0442b
58 changed files with 8948 additions and 1164 deletions

View File

@@ -0,0 +1,332 @@
# Design Document: 套餐管理系统实现
## Context
实现完整的套餐管理系统包括4个核心模块。该系统需要支持多级代理商体系的套餐分配和定价管理。
**背景**
- 项目已有类型定义src/types/api/package.ts但使用不同的字段命名和枚举值
- 后端 API 已实现,使用下划线命名(如 `series_name`
- 前端项目统一使用 CommonStatus 枚举0:禁用, 1:启用)
- 参考实现:`/system/role` 页面使用了组件化架构
**约束**
- 必须保留现有类型定义文件,不能破坏现有代码
- 需要兼容后端 API 的字段命名规范
- 需要适配项目的状态枚举规范
## Goals / Non-Goals
### Goals
1. 实现4个核心模块的完整 CRUD 功能
2. 建立统一的 API 服务层,封装后端接口
3. 实现组件化的页面结构,参考 `/system/role`
4. 支持复杂的定价规则(系列加价 vs 单套餐覆盖)
5. 确保数据隔离和权限控制
### Non-Goals
1. 不重构现有的 package.ts 类型定义
2. 不实现套餐的实时统计和报表功能(后续迭代)
3. 不实现套餐批量导入功能(后续迭代)
4. 不实现套餐的版本管理功能
## Decisions
### Decision 1: API 字段命名策略
**问题**后端使用下划线命名snake_case前端类型通常使用驼峰命名camelCase
**决策**
- API 请求/响应保持下划线命名,与后端保持一致
- 创建新的类型文件 `packageManagement.ts`,使用下划线命名
- 在表单提交和响应处理时不做转换,直接使用下划线字段
**理由**
- 减少转换层的复杂性和错误风险
- 与后端 API 文档保持一致,便于对照
- TypeScript 支持下划线字段名,不影响类型安全
**示例**
```typescript
export interface PackageSeriesResponse {
id: number
series_code: string // 下划线命名
series_name: string
status: number
created_at: string
updated_at: string
}
```
### Decision 2: 状态值映射
**问题**:文档中状态是 `1:启用, 2:禁用`,但项目 CommonStatus 是 `0:禁用, 1:启用`
**决策**
- **在常量配置中定义套餐专用的状态枚举**
- **前端页面使用项目统一的 CommonStatus0/1**
- **在 API 服务层进行状态值映射转换**
**映射规则**
```typescript
// 前端 -> 后端
CommonStatus.ENABLED (1) -> API Status (1)
CommonStatus.DISABLED (0) -> API Status (2)
// 后端 -> 前端
API Status (1) -> CommonStatus.ENABLED (1)
API Status (2) -> CommonStatus.DISABLED (0)
```
**理由**
- 保持前端 UI 的一致性
- 避免混淆项目开发者
- 集中在 API 服务层处理差异
### Decision 3: 模块拆分策略
**问题**:是创建单个 package.ts 服务,还是拆分为多个服务文件?
**决策**拆分为4个独立的服务文件
1. `packageSeries.ts` - 套餐系列管理
2. `package.ts` - 套餐管理
3. `myPackage.ts` - 代理可售套餐
4. `shopPackageAllocation.ts` - 单套餐分配
**理由**
- 每个模块功能独立,职责清晰
- 便于维护和扩展
- 符合单一职责原则
- 便于团队协作(不同开发者负责不同模块)
**替代方案**
- 单个 package.ts 文件 - **拒绝**,文件过大,难以维护
### Decision 4: 定价规则实现
**问题**:代理商的套餐成本价有两种计算方式:系列加价和单套餐覆盖。
**决策**
- **后端负责成本价计算**,前端只展示结果
- 前端接收 `price_source` 字段,标识价格来源
- 单套餐分配创建时,保存 `calculated_cost_price`(系列规则计算的价格)供参考
**数据流**
```
1. 系列分配pricing_mode + pricing_value -> 后端计算 -> cost_price
2. 单套餐分配:直接设置 cost_price覆盖系列规则
3. 前端展示price_source 标识使用了哪种规则
```
**理由**
- 计算逻辑复杂,集中在后端便于维护
- 前端只负责展示,降低复杂度
- 保留 calculated_cost_price 便于调试和审计
### Decision 5: 表单验证策略
**问题**:客户端验证 vs 服务端验证。
**决策****双重验证**
- 客户端:使用 Element Plus 的 FormRules 进行基础验证
- 服务端:后端 API 进行完整验证并返回详细错误
**客户端验证规则**
- 必填字段检查
- 长度限制(如系列名称 1-255 字符)
- 数值范围(如套餐时长 1-120 月)
- 格式验证(如价格必须为正整数)
**理由**
- 客户端验证提升用户体验,即时反馈
- 服务端验证保证数据安全性和完整性
- 符合 Web 应用最佳实践
### Decision 6: 页面组件化结构
**问题**:页面结构如何组织?
**决策**:参考 `/system/role` 页面,使用组件化结构:
```vue
<template>
<ArtTableFullScreen>
<ArtSearchBar /> <!-- 搜索栏 -->
<ElCard>
<ArtTableHeader /> <!-- 表格头部刷新列设置操作按钮 -->
<ArtTable /> <!-- 数据表格 -->
<ElDialog /> <!-- 新增/编辑对话框 -->
</ElCard>
</ArtTableFullScreen>
</template>
```
**理由**
- 与项目现有页面风格一致
- 复用成熟的组件,减少开发工作量
- 便于维护和扩展
## Risks / Trade-offs
### Risk 1: 后端 API 未完成
**风险**:后端接口可能尚未实现或与文档不一致。
**缓解措施**
1. 先实现 API 服务层,使用 TypeScript 类型约束
2. 使用 Mock 数据进行前端开发(已有示例)
3. 与后端团队确认 API 规范和联调时间
4. 预留 API 调试和修正时间
### Risk 2: 状态值映射可能遗漏
**风险**:在某些地方忘记转换状态值,导致显示错误。
**缓解措施**
1. 在 API 服务层统一处理转换
2. 创建工具函数封装映射逻辑
3. 编写单元测试覆盖映射函数
4. Code Review 时重点检查状态相关代码
### Risk 3: 定价规则理解偏差
**风险**:对定价规则的理解与实际业务需求有偏差。
**缓解措施**
1. 在实现前与产品确认定价规则
2. 编写测试用例覆盖各种定价场景
3. 在 UI 上清晰展示价格来源和计算方式
4. 预留调整空间,避免硬编码
### Trade-off 1: 类型定义冗余
**取舍**:保留旧的 package.ts 类型定义,新增 packageManagement.ts。
**代价**
- 存在两套类型定义,可能造成混淆
- 占用额外的代码空间
**收益**
- 不影响现有代码,向后兼容
- 新旧系统可以并存,降低迁移风险
- 未来可以逐步迁移到新类型
### Trade-off 2: 状态值映射增加复杂度
**取舍**:在 API 服务层进行状态值转换。
**代价**
- 增加一层转换逻辑
- 可能影响性能(微小)
**收益**
- 前端 UI 保持一致性
- 业务逻辑更清晰
- 便于后续维护
## Migration Plan
### Phase 1: 基础设施1-2天
1. 创建类型定义文件
2. 创建常量配置文件
3. 设置状态映射工具函数
### Phase 2: API 服务层2-3天
1. 实现4个 API 服务模块
2. 编写单元测试(可选)
3. 使用 Mock 数据测试
### Phase 3: 页面实现4-5天
1. 套餐系列管理页面1天
2. 套餐管理页面1.5天)
3. 代理可售套餐页面1天
4. 单套餐分配页面1.5天)
### Phase 4: 集成测试1-2天
1. 与后端 API 联调
2. 端到端功能测试
3. 修复 Bug 和优化
### Phase 5: 上线1天
1. Code Review
2. 合并代码
3. 部署到测试环境
4. 部署到生产环境
**总计**9-13 个工作日
### Rollback Plan
如果出现严重问题,回滚步骤:
1. 从 Git 回滚到上一个稳定版本
2. 移除新增的路由配置
3. 移除新增的 API 服务导出
4. 通知用户功能暂时不可用
### Decision 7: 错误处理策略
**问题**:如何统一处理各类错误和异常?
**决策**:分层错误处理机制
- **网络错误**axios 拦截器统一捕获,显示通用错误提示
- **401 未认证**:自动跳转到登录页面
- **403 无权限**:显示权限不足提示,不跳转
- **400 业务错误**根据错误信息显示具体提示ElMessage.error
- **表单验证错误**:在表单字段下显示错误提示
**错误提示方式**
```typescript
// 网络错误或服务器错误
ElMessage.error('网络错误,请稍后重试')
// 业务错误(后端返回的具体错误)
ElMessage.error(res.message || '操作失败')
// 操作成功
ElMessage.success('操作成功')
```
**理由**
- 统一的错误处理提升用户体验
- 分层处理避免重复代码
- 清晰的错误提示帮助用户理解问题
### Decision 8: Loading 状态管理
**问题**:如何管理各种操作的加载状态?
**决策**:细粒度的 loading 状态管理
**Loading 状态分类**
```typescript
const loading = ref(false) // 表格数据加载
const submitLoading = ref(false) // 表单提交
const deleteLoading = ref<Record<number, boolean>>({}) // 删除操作(可选)
```
**状态管理规则**
- **列表查询**:表格显示 loading 遮罩
- **新增/编辑提交**:提交按钮显示 loading禁用表单
- **删除操作**:可选择在按钮上显示 loading 或全局 loading
- **状态切换**ElSwitch 自带 loading 效果,先更新 UI 再调用 API
**理由**
- 细粒度控制提供更好的交互反馈
- 防止重复提交
- 清晰标识正在进行的操作
## Open Questions
1. **Q**: 套餐被删除后,历史订单如何处理?
**A**: 待产品确认,可能需要软删除机制
2. **Q**: 代理商可以自行调整套餐售价吗?
**A**: 待产品确认,当前设计只展示建议售价
3. **Q**: 套餐系列和套餐是否支持批量操作(批量启用/禁用)?
**A**: 当前不支持,后续迭代考虑
4. **Q**: 是否需要套餐变更历史记录?
**A**: 后端可能有审计日志,前端暂不展示
5. **Q**: 单套餐分配的"原计算成本价"是否需要实时更新?
**A**: 待确认,当前设计是创建时计算一次,不自动更新

View File

@@ -0,0 +1,143 @@
# Change: 套餐管理系统实现
## Why
根据业务需求文档docs/套餐.md需要实现完整的套餐管理系统包括4个核心模块
1. **套餐系列管理** - 管理套餐的分类和组织
2. **套餐管理** - 管理具体的套餐产品(流量、价格、时长等)
3. **代理可售套餐** - 代理商查看被分配的可售套餐及定价
4. **单套餐分配** - 为代理商分配特定套餐并设置成本价
当前系统虽然有套餐相关的类型定义src/types/api/package.ts和部分页面骨架但缺少完整的 API 对接和业务逻辑实现。此变更将实现完整的套餐管理能力。
## What Changes
### 1. API 层实现
采用模块化设计,拆分为 4 个独立的 API 服务文件:
- **新增**: `src/api/modules/packageSeries.ts` - 套餐系列 API 服务
- 套餐系列列表查询(分页、筛选)
- 创建套餐系列
- 获取套餐系列详情
- 更新套餐系列
- 删除套餐系列
- 更新套餐系列状态
- **新增**: `src/api/modules/package.ts` - 套餐管理 API 服务
- 套餐列表查询(分页、多条件筛选)
- 创建套餐
- 获取套餐详情
- 更新套餐
- 删除套餐
- 更新套餐状态
- 更新套餐上架状态
- 获取系列下拉选项(用于表单选择)
- **新增**: `src/api/modules/myPackage.ts` - 代理可售套餐 API 服务
- 我的可售套餐列表查询
- 获取可售套餐详情
- 我的被分配系列列表
- **新增**: `src/api/modules/shopPackageAllocation.ts` - 单套餐分配 API 服务
- 单套餐分配列表查询
- 创建单套餐分配
- 获取单套餐分配详情
- 更新单套餐分配
- 删除单套餐分配
- 更新单套餐分配状态
- **修改**: `src/api/modules/index.ts` - 导出新增的服务模块
- 导出 PackageSeriesService
- 导出 PackageService
- 导出 MyPackageService
- 导出 ShopPackageAllocationService
### 2. 类型定义增强
- **新增**: `src/types/api/packageManagement.ts` - 完整的套餐管理类型定义
- 匹配文档的 API 字段(下划线命名)
- 包含所有请求/响应类型
- 包含分页结果类型
### 3. 页面实现
**套餐系列管理** (`src/views/package-management/package-series/index.vue`)
- 列表展示(支持名称搜索、状态筛选)
- 新增/编辑套餐系列
- 删除套餐系列
- 状态开关(启用/禁用)
**套餐管理** (`src/views/package-management/package-list/index.vue`)
- 列表展示(支持多条件筛选:名称、系列、状态、上架状态、套餐类型)
- 新增/编辑套餐
- 删除套餐
- 状态开关(启用/禁用)
- 上架状态开关(上架/下架)
**代理可售套餐** (`src/views/package-management/my-packages/index.vue`)
- 查看被分配的套餐列表(支持系列、类型筛选)
- 查看套餐详情(成本价、建议售价、利润空间等)
- 查看被分配系列列表
**单套餐分配** (`src/views/package-management/package-assign/index.vue`)
- 分配列表(支持店铺、套餐、状态筛选)
- 创建分配(选择套餐、店铺、设置成本价)
- 编辑分配(修改成本价)
- 删除分配
- 状态管理(启用/禁用)
### 4. 常量配置
- **新增**: `src/config/constants/package.ts` - 套餐相关常量
- 套餐类型枚举formal/addon
- 流量类型枚举real/virtual
- 上架状态枚举1:上架, 2:下架)
- 定价模式枚举fixed/percent
- 价格来源枚举series_pricing/package_override
### 5. 路由配置
已存在的路由(无需修改):
- `/package-management/package-series` - 套餐系列管理
- `/package-management/package-list` - 套餐管理
- `/package-management/package-assign` - 单套餐分配
需要新增的路由:
- **新增**: `src/router/routesAlias.ts` - 添加路由别名
- `MyPackages = '/package-management/my-packages'` - 代理可售套餐
- **新增**: `src/router/routes/asyncRoutes.ts` - 添加路由配置
- `/package-management/my-packages` - 代理可售套餐页面路由
## Impact
### 受影响的规范
- `package-series-management` - 新增能力
- `package-management` - 新增能力
- `my-packages` - 新增能力
- `shop-package-allocation` - 新增能力
### 受影响的代码
- `src/api/modules/*` - 新增 4 个 API 服务模块
- `src/types/api/*` - 新增类型定义文件
- `src/views/package-management/*` - 4 个页面完整实现
- `src/config/constants/*` - 新增常量配置
- `src/router/routes/asyncRoutes.ts` - 路由配置
### 依赖关系
- 依赖现有的组件库ArtTable、ArtSearchBar、ArtTableHeader 等)
- 依赖现有的 HTTP 请求工具request.ts
- 依赖现有的权限控制和路由守卫
- 依赖 ShopService用于单套餐分配页面的店铺选择器
- 后端 API 需已实现docs/套餐.md 中定义的接口)
**注意事项**
- ShopService 应该已经存在于 src/api/modules/shop.ts
- 如果不存在,需要先实现或使用 Mock 数据
### 风险评估
- **低风险**: 独立模块,不影响现有功能
- **API 依赖**: 需确保后端接口已实现并联调
- **权限控制**: 需配置对应的菜单和按钮权限

View File

@@ -0,0 +1,110 @@
# My Packages (代理可售套餐) Specification
## ADDED Requirements
### Requirement: 我的可售套餐列表查询
系统 SHALL 提供代理商查询被分配套餐的功能。
#### Scenario: 查询当前代理商的可售套餐
- **WHEN** 代理商用户访问可售套餐页面
- **THEN** 系统显示被分配给该代理商的套餐列表
- **AND** 每个套餐包含套餐ID、套餐编码、套餐名称、套餐类型、系列信息
- **AND** 显示成本价cost_price单位
- **AND** 显示建议售价suggested_retail_price单位
- **AND** 显示利润空间profit_margin = 建议售价 - 成本价)
- **AND** 显示价格来源series_pricing:系列加价 / package_override:单套餐覆盖)
- **AND** 显示套餐状态和上架状态
- **AND** 支持分页每页最多100条记录
#### Scenario: 按系列筛选
- **WHEN** 代理商选择特定系列ID筛选
- **THEN** 系统返回该系列下的所有可售套餐
#### Scenario: 按套餐类型筛选
- **WHEN** 代理商选择套餐类型formal/addon筛选
- **THEN** 系统返回该类型的所有可售套餐
### Requirement: 可售套餐详情查询
系统 SHALL 允许代理商查看单个可售套餐的详细信息。
#### Scenario: 查询套餐详情
- **WHEN** 代理商点击查看套餐详情
- **THEN** 系统显示套餐完整信息
- **AND** 包含套餐描述、流量信息、时长等
- **AND** 显示定价详情(成本价、建议售价、利润空间、价格来源)
- **AND** 显示系列信息
#### Scenario: 查询未分配的套餐
- **WHEN** 代理商查询未被分配的套餐ID
- **THEN** 系统返回404错误或无权访问错误
### Requirement: 我的被分配系列列表
系统 SHALL 提供代理商查询被分配系列的功能。
#### Scenario: 查询被分配系列列表
- **WHEN** 代理商访问被分配系列列表
- **THEN** 系统显示分配给该代理商的系列列表
- **AND** 每个系列包含分配ID、系列ID、系列编码、系列名称
- **AND** 显示定价模式fixed:固定金额 / percent:百分比)
- **AND** 显示定价值pricing_value
- **AND** 显示分配者店铺名称
- **AND** 显示可售套餐数量
- **AND** 显示状态
- **AND** 支持分页
### Requirement: 成本价计算规则
系统 SHALL 根据价格来源计算代理商的成本价。
#### Scenario: 系列加价模式series_pricing
- **WHEN** 套餐通过系列分配获得定价
- **AND** 定价模式为 fixed固定金额
- **THEN** 成本价 = 套餐价格 + 固定加价金额
- **WHEN** 定价模式为 percent百分比
- **THEN** 成本价 = 套餐价格 × (1 + 加价百分比)
#### Scenario: 单套餐覆盖模式package_override
- **WHEN** 套餐被单独分配并设置了成本价
- **THEN** 成本价 = 单套餐分配中设置的成本价
- **AND** 价格来源显示为 package_override
### Requirement: 数据隔离
系统 SHALL 确保代理商只能查看被分配给自己的套餐。
#### Scenario: 数据访问隔离
- **WHEN** 代理商查询可售套餐列表
- **THEN** 系统仅返回分配给该代理商的套餐
- **AND** 不显示其他代理商的套餐信息
#### Scenario: 跨代理商访问保护
- **WHEN** 代理商尝试访问未分配给自己的套餐详情
- **THEN** 系统返回403无权访问错误
### Requirement: 权限控制
系统 SHALL 对可售套餐查询功能实施权限控制。
#### Scenario: 仅代理商可访问
- **WHEN** 非代理商用户访问可售套餐接口
- **THEN** 系统返回403无权访问错误
#### Scenario: 未认证用户访问
- **WHEN** 未登录用户访问可售套餐接口
- **THEN** 系统返回401未认证错误

View File

@@ -0,0 +1,159 @@
# Package Management Specification
## ADDED Requirements
### Requirement: 套餐列表查询
系统 SHALL 提供套餐列表查询功能,支持分页和多条件筛选。
#### Scenario: 查询所有套餐
- **WHEN** 用户访问套餐管理页面
- **THEN** 系统显示套餐列表包含套餐编码、套餐名称、系列ID、套餐类型、流量、价格、状态、上架状态等
- **AND** 支持按套餐名称模糊搜索
- **AND** 支持按系列ID筛选
- **AND** 支持按状态筛选(启用/禁用)
- **AND** 支持按上架状态筛选(上架/下架)
- **AND** 支持按套餐类型筛选formal/addon
- **AND** 支持分页每页最多100条记录
#### Scenario: 多条件组合查询
- **WHEN** 用户同时使用多个筛选条件
- **THEN** 系统返回满足所有条件的套餐列表
### Requirement: 创建套餐
系统 SHALL 允许管理员创建新的套餐。
#### Scenario: 成功创建套餐
- **WHEN** 用户填写必填字段(套餐编码、套餐名称、套餐类型、套餐时长、套餐价格)
- **AND** 可选填写系列ID、流量信息、成本价、建议售价等
- **AND** 提交表单
- **THEN** 系统创建套餐,默认状态为启用,默认上架状态为下架
- **AND** 返回创建的套餐详情
#### Scenario: 套餐编码唯一性验证
- **WHEN** 用户使用已存在的套餐编码创建套餐
- **THEN** 系统返回错误提示"套餐编码已存在"
#### Scenario: 验证套餐时长范围
- **WHEN** 套餐时长小于1个月或大于120个月
- **THEN** 系统返回验证错误"套餐时长必须在1-120个月之间"
#### Scenario: 流量类型与流量额度关系
- **WHEN** 流量类型为真流量real
- **THEN** 真流量额度real_data_mb必须大于0
- **AND** 总流量额度data_amount_mb等于真流量额度
- **WHEN** 流量类型为虚流量virtual
- **THEN** 虚流量额度virtual_data_mb必须大于0
- **AND** 真流量额度和虚流量额度之和等于总流量额度
### Requirement: 查看套餐详情
系统 SHALL 允许用户查看单个套餐的详细信息。
#### Scenario: 查询存在的套餐
- **WHEN** 用户通过套餐ID查询详情
- **THEN** 系统返回该套餐的完整信息,包括所有字段
#### Scenario: 查询不存在的套餐
- **WHEN** 用户查询不存在的套餐ID
- **THEN** 系统返回404错误
### Requirement: 更新套餐
系统 SHALL 允许管理员更新套餐信息。
#### Scenario: 成功更新套餐
- **WHEN** 用户修改套餐的可变字段(名称、时长、价格、流量等)
- **AND** 提交更新
- **THEN** 系统更新套餐信息
- **AND** 返回更新后的套餐详情
#### Scenario: 套餐编码不可修改
- **WHEN** 用户尝试修改套餐编码
- **THEN** 系统忽略该字段,不允许修改
### Requirement: 删除套餐
系统 SHALL 允许管理员删除套餐。
#### Scenario: 成功删除套餐
- **WHEN** 用户删除未被使用的套餐
- **THEN** 系统删除该套餐
- **AND** 返回成功状态
#### Scenario: 删除被分配的套餐
- **WHEN** 用户删除已被分配给代理商的套餐
- **THEN** 系统返回错误提示"该套餐已被分配,无法删除"
### Requirement: 套餐状态管理
系统 SHALL 支持套餐启用/禁用状态管理。
#### Scenario: 启用套餐
- **WHEN** 用户将禁用状态的套餐切换为启用
- **THEN** 系统更新状态为启用status=1
#### Scenario: 禁用套餐
- **WHEN** 用户将启用状态的套餐切换为禁用
- **THEN** 系统更新状态为禁用status=2
- **AND** 该套餐将不可用于新的分配或充值
### Requirement: 套餐上架状态管理
系统 SHALL 支持套餐上架/下架状态管理。
#### Scenario: 上架套餐
- **WHEN** 用户将下架状态的套餐切换为上架
- **THEN** 系统更新上架状态为上架shelf_status=1
- **AND** 该套餐将对代理商可见
#### Scenario: 下架套餐
- **WHEN** 用户将上架状态的套餐切换为下架
- **THEN** 系统更新上架状态为下架shelf_status=2
- **AND** 该套餐将对代理商不可见
### Requirement: 套餐类型支持
系统 SHALL 支持两种套餐类型。
#### Scenario: 正式套餐
- **WHEN** 创建套餐类型为 formal 的套餐
- **THEN** 系统记录为正式套餐
#### Scenario: 附加套餐
- **WHEN** 创建套餐类型为 addon 的套餐
- **THEN** 系统记录为附加套餐
### Requirement: 权限控制
系统 SHALL 对套餐管理功能实施权限控制。
#### Scenario: 未认证用户访问
- **WHEN** 未登录用户访问套餐管理接口
- **THEN** 系统返回401未认证错误
#### Scenario: 无权限用户访问
- **WHEN** 已登录但无权限的用户访问套餐管理接口
- **THEN** 系统返回403无权访问错误

View File

@@ -0,0 +1,116 @@
# Package Series Management Specification
## ADDED Requirements
### Requirement: 套餐系列列表查询
系统 SHALL 提供套餐系列列表查询功能,支持分页和条件筛选。
#### Scenario: 查询所有套餐系列
- **WHEN** 用户访问套餐系列管理页面
- **THEN** 系统显示套餐系列列表,包含系列名称、系列编码、描述、状态、创建时间、更新时间
- **AND** 支持按系列名称模糊搜索
- **AND** 支持按状态筛选(启用/禁用)
- **AND** 支持分页每页最多100条记录
#### Scenario: 空列表处理
- **WHEN** 没有符合条件的套餐系列
- **THEN** 系统显示空状态提示
### Requirement: 创建套餐系列
系统 SHALL 允许管理员创建新的套餐系列。
#### Scenario: 成功创建套餐系列
- **WHEN** 用户填写系列编码、系列名称(必填)
- **AND** 可选填写描述最大500字符
- **AND** 提交表单
- **THEN** 系统创建套餐系列,默认状态为启用
- **AND** 返回创建的套餐系列详情
#### Scenario: 系列编码重复
- **WHEN** 用户使用已存在的系列编码创建套餐系列
- **THEN** 系统返回错误提示"系列编码已存在"
#### Scenario: 验证系列名称长度
- **WHEN** 系列名称长度小于1或大于255个字符
- **THEN** 系统返回验证错误
### Requirement: 查看套餐系列详情
系统 SHALL 允许用户查看单个套餐系列的详细信息。
#### Scenario: 查询存在的套餐系列
- **WHEN** 用户通过系列ID查询详情
- **THEN** 系统返回该套餐系列的完整信息
#### Scenario: 查询不存在的套餐系列
- **WHEN** 用户查询不存在的系列ID
- **THEN** 系统返回404错误
### Requirement: 更新套餐系列
系统 SHALL 允许管理员更新套餐系列信息。
#### Scenario: 成功更新套餐系列
- **WHEN** 用户修改系列名称或描述
- **AND** 提交更新
- **THEN** 系统更新套餐系列信息
- **AND** 返回更新后的套餐系列详情
#### Scenario: 系列编码不可修改
- **WHEN** 用户尝试修改系列编码
- **THEN** 系统忽略该字段,不允许修改
### Requirement: 删除套餐系列
系统 SHALL 允许管理员删除套餐系列。
#### Scenario: 成功删除套餐系列
- **WHEN** 用户删除未被套餐使用的系列
- **THEN** 系统删除该套餐系列
- **AND** 返回成功状态
#### Scenario: 删除被使用的套餐系列
- **WHEN** 用户删除已被套餐关联的系列
- **THEN** 系统返回错误提示"该系列下存在套餐,无法删除"
### Requirement: 套餐系列状态管理
系统 SHALL 支持套餐系列状态的开关管理。
#### Scenario: 启用套餐系列
- **WHEN** 用户将禁用状态的系列切换为启用
- **THEN** 系统更新状态为启用status=1
#### Scenario: 禁用套餐系列
- **WHEN** 用户将启用状态的系列切换为禁用
- **THEN** 系统更新状态为禁用status=2
- **AND** 该系列下的套餐可能受到影响(业务规则)
### Requirement: 权限控制
系统 SHALL 对套餐系列管理功能实施权限控制。
#### Scenario: 未认证用户访问
- **WHEN** 未登录用户访问套餐系列管理接口
- **THEN** 系统返回401未认证错误
#### Scenario: 无权限用户访问
- **WHEN** 已登录但无权限的用户访问套餐系列管理接口
- **THEN** 系统返回403无权访问错误

View File

@@ -0,0 +1,164 @@
# Shop Package Allocation (单套餐分配) Specification
## ADDED Requirements
### Requirement: 单套餐分配列表查询
系统 SHALL 提供单套餐分配记录的查询功能。
#### Scenario: 查询所有单套餐分配
- **WHEN** 管理员访问单套餐分配管理页面
- **THEN** 系统显示单套餐分配列表
- **AND** 每条记录包含分配ID、套餐ID、套餐编码、套餐名称
- **AND** 显示被分配的店铺ID和店铺名称
- **AND** 显示覆盖的成本价cost_price单位
- **AND** 显示原计算成本价calculated_cost_price供参考
- **AND** 显示关联的系列分配ID
- **AND** 显示状态、创建时间、更新时间
- **AND** 支持分页每页最多100条记录
#### Scenario: 按店铺筛选
- **WHEN** 管理员选择特定店铺ID筛选
- **THEN** 系统返回该店铺的所有单套餐分配记录
#### Scenario: 按套餐筛选
- **WHEN** 管理员选择特定套餐ID筛选
- **THEN** 系统返回该套餐的所有分配记录
#### Scenario: 按状态筛选
- **WHEN** 管理员选择状态(启用/禁用)筛选
- **THEN** 系统返回符合状态条件的分配记录
### Requirement: 创建单套餐分配
系统 SHALL 允许管理员为店铺分配特定套餐并设置成本价。
#### Scenario: 成功创建单套餐分配
- **WHEN** 管理员选择套餐ID、店铺ID
- **AND** 设置覆盖的成本价必填最小值为0
- **AND** 提交分配
- **THEN** 系统创建单套餐分配记录
- **AND** 计算并保存原计算成本价(基于系列分配规则)
- **AND** 返回创建的分配详情包括关联的系列分配ID
#### Scenario: 重复分配检查
- **WHEN** 管理员为同一店铺分配已分配过的套餐
- **THEN** 系统返回错误提示"该套餐已分配给此店铺"
#### Scenario: 店铺和套餐验证
- **WHEN** 管理员使用不存在的店铺ID或套餐ID
- **THEN** 系统返回错误提示"店铺或套餐不存在"
### Requirement: 查看单套餐分配详情
系统 SHALL 允许管理员查看单个分配记录的详细信息。
#### Scenario: 查询存在的分配记录
- **WHEN** 管理员通过分配ID查询详情
- **THEN** 系统返回该分配记录的完整信息
- **AND** 包含套餐完整信息、店铺信息、定价信息
#### Scenario: 查询不存在的分配记录
- **WHEN** 管理员查询不存在的分配ID
- **THEN** 系统返回404错误
### Requirement: 更新单套餐分配
系统 SHALL 允许管理员更新单套餐分配的成本价。
#### Scenario: 成功更新成本价
- **WHEN** 管理员修改覆盖的成本价
- **AND** 提交更新
- **THEN** 系统更新成本价
- **AND** 返回更新后的分配详情
#### Scenario: 成本价验证
- **WHEN** 管理员设置成本价小于0
- **THEN** 系统返回验证错误"成本价必须大于等于0"
### Requirement: 删除单套餐分配
系统 SHALL 允许管理员删除单套餐分配记录。
#### Scenario: 成功删除分配
- **WHEN** 管理员删除单套餐分配记录
- **THEN** 系统删除该分配记录
- **AND** 该店铺将无法再以此定价售卖该套餐
- **AND** 返回成功状态
#### Scenario: 删除正在使用的分配
- **WHEN** 管理员删除正在被订单使用的分配记录
- **THEN** 系统可能返回警告或阻止删除(根据业务规则)
### Requirement: 单套餐分配状态管理
系统 SHALL 支持单套餐分配的启用/禁用状态管理。
#### Scenario: 启用分配
- **WHEN** 管理员将禁用状态的分配切换为启用
- **THEN** 系统更新状态为启用status=1
- **AND** 该店铺可以使用此定价售卖套餐
#### Scenario: 禁用分配
- **WHEN** 管理员将启用状态的分配切换为禁用
- **THEN** 系统更新状态为禁用status=2
- **AND** 该店铺将无法使用此定价售卖套餐
### Requirement: 成本价优先级规则
系统 SHALL 实现单套餐分配成本价覆盖系列分配规则。
#### Scenario: 单套餐分配优先
- **WHEN** 店铺同时拥有系列分配和单套餐分配
- **THEN** 系统使用单套餐分配的成本价
- **AND** 原计算成本价calculated_cost_price保存系列分配规则计算的价格供参考
#### Scenario: 仅系列分配
- **WHEN** 店铺只有系列分配,没有单套餐分配
- **THEN** 系统使用系列分配规则计算成本价
### Requirement: 关联系列分配追踪
系统 SHALL 追踪单套餐分配与系列分配的关联关系。
#### Scenario: 记录关联的系列分配
- **WHEN** 创建单套餐分配时
- **THEN** 系统记录关联的系列分配IDallocation_id
- **AND** 用于追溯定价来源
### Requirement: 权限控制
系统 SHALL 对单套餐分配管理功能实施权限控制。
#### Scenario: 未认证用户访问
- **WHEN** 未登录用户访问单套餐分配接口
- **THEN** 系统返回401未认证错误
#### Scenario: 无权限用户访问
- **WHEN** 已登录但无权限的用户访问单套餐分配接口
- **THEN** 系统返回403无权访问错误
#### Scenario: 仅管理员可操作
- **WHEN** 非管理员用户尝试创建、更新或删除单套餐分配
- **THEN** 系统返回403无权访问错误

View File

@@ -0,0 +1,156 @@
# Implementation Tasks
## 1. 基础设施准备
- [ ] 1.1 创建套餐管理类型定义文件src/types/api/packageManagement.ts
- [ ] 1.2 创建套餐常量配置文件src/config/constants/package.ts
- [ ] 1.3 导出常量配置到 constants/index.ts
## 2. API 服务层实现
### 2.1 套餐系列 APIpackageSeries.ts
- [ ] 2.1.1 实现 getPackageSeries套餐系列列表
- [ ] 2.1.2 实现 createPackageSeries创建套餐系列
- [ ] 2.1.3 实现 getPackageSeriesDetail获取套餐系列详情
- [ ] 2.1.4 实现 updatePackageSeries更新套餐系列
- [ ] 2.1.5 实现 deletePackageSeries删除套餐系列
- [ ] 2.1.6 实现 updatePackageSeriesStatus更新套餐系列状态
### 2.2 套餐管理 APIpackage.ts
- [ ] 2.2.1 实现 getPackages套餐列表
- [ ] 2.2.2 实现 createPackage创建套餐
- [ ] 2.2.3 实现 getPackageDetail获取套餐详情
- [ ] 2.2.4 实现 updatePackage更新套餐
- [ ] 2.2.5 实现 deletePackage删除套餐
- [ ] 2.2.6 实现 updatePackageStatus更新套餐状态
- [ ] 2.2.7 实现 updatePackageShelfStatus更新套餐上架状态
### 2.3 代理可售套餐 APImyPackage.ts
- [ ] 2.3.1 实现 getMyPackages我的可售套餐列表
- [ ] 2.3.2 实现 getMyPackageDetail获取可售套餐详情
- [ ] 2.3.3 实现 getMySeriesAllocations我的被分配系列列表
### 2.4 单套餐分配 APIshopPackageAllocation.ts
- [ ] 2.4.1 实现 getShopPackageAllocations单套餐分配列表
- [ ] 2.4.2 实现 createShopPackageAllocation创建单套餐分配
- [ ] 2.4.3 实现 getShopPackageAllocationDetail获取单套餐分配详情
- [ ] 2.4.4 实现 updateShopPackageAllocation更新单套餐分配
- [ ] 2.4.5 实现 deleteShopPackageAllocation删除单套餐分配
- [ ] 2.4.6 实现 updateShopPackageAllocationStatus更新单套餐分配状态
- [ ] 2.5 在 src/api/modules/index.ts 中导出所有新服务
## 3. 页面实现
### 3.1 套餐系列管理页面package-series/index.vue
- [ ] 3.1.1 实现列表展示(表格、分页)
- [ ] 3.1.2 实现搜索栏(系列名称、状态筛选)
- [ ] 3.1.3 实现新增对话框(表单验证)
- [ ] 3.1.4 实现编辑功能(复用新增对话框,根据 dialogType 区分新增/编辑)
- [ ] 3.1.5 实现删除功能(二次确认)
- [ ] 3.1.6 实现状态开关(启用/禁用)
- [ ] 3.1.7 集成 API 服务并处理加载状态
### 3.2 套餐管理页面package-list/index.vue
- [ ] 3.2.1 实现列表展示(表格、分页)
- [ ] 3.2.2 实现搜索栏(名称、系列、状态、上架状态、类型筛选)
- [ ] 3.2.3 实现系列下拉选择器(加载套餐系列列表,只显示启用状态)
- [ ] 3.2.4 实现新增对话框(表单验证、系列选择)
- [ ] 3.2.5 实现编辑功能(复用新增对话框,根据 dialogType 区分新增/编辑)
- [ ] 3.2.6 实现删除功能(二次确认)
- [ ] 3.2.7 实现状态开关(启用/禁用)
- [ ] 3.2.8 实现上架状态开关(上架/下架)
- [ ] 3.2.9 集成 API 服务并处理加载状态
### 3.3 代理可售套餐页面my-packages/index.vue
- [ ] 3.3.1 创建页面文件和基本结构
- [ ] 3.3.2 实现列表展示(表格、分页)
- [ ] 3.3.3 实现搜索栏(系列、类型筛选)
- [ ] 3.3.4 实现详情对话框(显示成本价、建议售价、利润空间)
- [ ] 3.3.5 实现被分配系列列表Tab可选
- [ ] 3.3.6 集成 API 服务并处理加载状态
### 3.4 单套餐分配页面package-assign/index.vue
- [ ] 3.4.1 创建页面文件和基本结构
- [ ] 3.4.2 实现列表展示(表格、分页)
- [ ] 3.4.3 实现搜索栏(店铺、套餐、状态筛选)
- [ ] 3.4.4 实现套餐下拉选择器(加载套餐列表,只显示启用且上架的套餐)
- [ ] 3.4.5 实现店铺下拉选择器(使用 ShopService 加载店铺列表)
- [ ] 3.4.6 实现新增对话框(套餐选择、店铺选择、成本价输入)
- [ ] 3.4.7 实现编辑功能(单独对话框或复用新增对话框,只允许修改成本价)
- [ ] 3.4.8 实现删除功能(二次确认)
- [ ] 3.4.9 实现状态管理(启用/禁用开关)
- [ ] 3.4.10 集成 API 服务并处理加载状态
## 4. 路由配置
- [ ] 4.1 在 asyncRoutes.ts 中添加 my-packages 路由配置
- [ ] 4.2 验证路由权限配置正确
## 5. 集成测试
### 5.1 套餐系列管理测试
- [ ] 5.1.1 测试列表查询(空列表、有数据、分页)
- [ ] 5.1.2 测试搜索功能(名称模糊搜索、状态筛选)
- [ ] 5.1.3 测试新增功能(成功、编码重复、字段验证)
- [ ] 5.1.4 测试编辑功能(成功、字段验证)
- [ ] 5.1.5 测试删除功能(成功、有关联套餐时禁止删除)
- [ ] 5.1.6 测试状态切换(启用→禁用、禁用→启用)
- [ ] 5.1.7 测试权限控制(未登录、无权限)
### 5.2 套餐管理测试
- [ ] 5.2.1 测试列表查询(空列表、有数据、分页)
- [ ] 5.2.2 测试多条件筛选(名称、系列、状态、上架状态、类型)
- [ ] 5.2.3 测试系列下拉选择器(只显示启用状态的系列)
- [ ] 5.2.4 测试新增功能(成功、编码重复、时长验证、流量验证)
- [ ] 5.2.5 测试编辑功能(成功、字段验证)
- [ ] 5.2.6 测试删除功能(成功、已分配时禁止删除)
- [ ] 5.2.7 测试状态切换(启用→禁用、禁用→启用)
- [ ] 5.2.8 测试上架状态切换(上架→下架、下架→上架)
- [ ] 5.2.9 测试权限控制(未登录、无权限)
### 5.3 代理可售套餐测试
- [ ] 5.3.1 测试列表查询(空列表、有数据、分页)
- [ ] 5.3.2 测试筛选功能(按系列、按类型)
- [ ] 5.3.3 测试详情查询(显示成本价、建议售价、利润空间、价格来源)
- [ ] 5.3.4 测试数据隔离(只能看到分配给自己的套餐)
- [ ] 5.3.5 测试被分配系列列表(如果实现)
- [ ] 5.3.6 测试权限控制(非代理商用户无法访问)
### 5.4 单套餐分配测试
- [ ] 5.4.1 测试列表查询(空列表、有数据、分页)
- [ ] 5.4.2 测试筛选功能(按店铺、按套餐、按状态)
- [ ] 5.4.3 测试套餐下拉选择器(只显示启用且上架的套餐)
- [ ] 5.4.4 测试店铺下拉选择器(加载店铺列表)
- [ ] 5.4.5 测试新增功能(成功、重复分配、成本价验证)
- [ ] 5.4.6 测试编辑功能(修改成本价)
- [ ] 5.4.7 测试删除功能(成功、有订单时的处理)
- [ ] 5.4.8 测试状态切换(启用→禁用、禁用→启用)
- [ ] 5.4.9 测试价格覆盖规则(单套餐分配优先于系列分配)
- [ ] 5.4.10 测试权限控制(仅管理员可操作)
### 5.5 通用功能测试
- [ ] 5.5.1 测试所有页面的表单验证(必填、长度、格式)
- [ ] 5.5.2 测试所有页面的 loading 状态(列表、提交、删除)
- [ ] 5.5.3 测试所有页面的错误处理(网络错误、业务错误)
- [ ] 5.5.4 测试所有页面的二次确认(删除操作)
- [ ] 5.5.5 测试分页功能(换页、改变每页数量)
- [ ] 5.5.6 测试刷新功能(列表刷新)
- [ ] 5.5.7 测试列显示/隐藏功能
- [ ] 5.5.8 测试状态值映射前端0/1与后端1/2的转换
## 6. 代码优化和文档
- [ ] 6.1 代码格式化和 ESLint 检查
- [ ] 6.2 添加必要的注释
- [ ] 6.3 更新 API 文档(如需要)
- [ ] 6.4 提交代码并创建 PR
## 注意事项
- 所有页面需参考 `/system/role` 页面的组件化结构
- 使用统一的 `CommonStatus` 常量(需要注意文档中的状态值映射)
- API 字段使用下划线命名(如 `series_name`),前端类型使用驼峰命名
- 所有删除操作需要二次确认
- 所有表单需要完整的验证规则
- 统一使用 Element Plus 的 Message 和 MessageBox 组件