Files
junhong_cmp_fiber/docs/7月迭代/需求05-套餐分配生效条件.md
2026-07-13 12:01:18 +09:00

159 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 需求05套餐分配生效条件ExpiryBase 覆盖)
---
## 背景
`Package.ExpiryBase` 已存在(`from_activation` / `from_purchase`),在套餐创建时设定,控制套餐何时开始计时。
需求:分配套餐给代理时,可以对单条分配记录二次覆盖这个值。
---
## 快照链设计
```
ShopPackageAllocation.expiry_base_override分配时设置覆盖值
↓ 订单创建时读取取有效值override 优先,否则用套餐默认)
PackageUsage.expiry_base_snapshot快照进去
↓ 激活时读取
activation_service 读 usage.ExpiryBaseSnapshot不再直接读 pkg.ExpiryBase
```
遗留数据兜底:`ExpiryBaseSnapshot` 为空(旧数据)时,回退读 `pkg.ExpiryBase`,行为不变。
---
## 数据库变更
### 1. ShopPackageAllocation 新增覆盖字段
```sql
ALTER TABLE tb_shop_package_allocation
ADD COLUMN expiry_base_override VARCHAR(30) DEFAULT NULL
COMMENT '生效条件覆盖NULL=使用套餐默认值, from_activation=实名即生效, from_purchase=购买即生效)';
```
### 2. PackageUsage 新增快照字段
```sql
ALTER TABLE tb_package_usage
ADD COLUMN expiry_base_snapshot VARCHAR(30) NOT NULL DEFAULT ''
COMMENT '生效条件快照(创建时从分配记录取有效值写入,空字符串=旧数据兜底读套餐原值)';
```
旧数据不回填,默认空字符串,激活时自动兜底。
---
## Model 变更
### ShopPackageAllocation`internal/model/shop_package_allocation.go`
```go
// ExpiryBaseOverride 生效条件覆盖
// NULL = 使用宿主套餐的 ExpiryBase有值 = 分配时指定,不受套餐后续修改影响
ExpiryBaseOverride *string `gorm:"column:expiry_base_override;type:varchar(30);comment:生效条件覆盖 NULL=使用套餐默认 from_activation=实名即生效 from_purchase=购买即生效" json:"expiry_base_override"`
```
### PackageUsage`internal/model/package.go`
```go
// ExpiryBaseSnapshot 生效条件快照(创建订单时写入,空字符串=旧数据兜底读套餐原值)
ExpiryBaseSnapshot string `gorm:"column:expiry_base_snapshot;type:varchar(30);not null;default:'';comment:生效条件快照 创建时从分配记录取有效值" json:"expiry_base_snapshot"`
```
---
## 业务逻辑变更
### 1. 订单创建时快照(`internal/service/order/service.go`
订单创建已通过 `GetByShopAndPackage` 查询分配记录(现有逻辑),在此基础上追加:
```go
// 取生效条件有效值:分配覆盖 > 套餐默认
expiryBase := pkg.ExpiryBase
if allocation.ExpiryBaseOverride != nil && *allocation.ExpiryBaseOverride != "" {
expiryBase = *allocation.ExpiryBaseOverride
}
// 创建 PackageUsage 时写入快照
usage.ExpiryBaseSnapshot = expiryBase
```
### 2. 激活时读快照(`internal/service/package/activation_service.go`
```go
// 修改前:直接读套餐原值
expiryBase := pkg.ExpiryBase
// 修改后:优先读快照,旧数据兜底
expiryBase := pkg.ExpiryBase // 兜底(旧数据 ExpiryBaseSnapshot 为空)
if usage.ExpiryBaseSnapshot != "" {
expiryBase = usage.ExpiryBaseSnapshot
}
```
同文件另一处(`activation_service.go:556`)同理修改。
`internal/service/order/service.go:2290` 中后台囤货路径的 `ExpiryBase` 判断也需同样改为读快照(兜底逻辑一致)。
---
## API 变更
### 1. 分配套餐接口(新增参数)
```
POST /admin/shop-package-allocations
```
请求 DTO 新增字段:
```go
ExpiryBaseOverride *string `json:"expiry_base_override" validate:"omitempty,oneof=from_activation from_purchase" description:"生效条件覆盖(不传=使用套餐默认, from_activation=实名即生效, from_purchase=购买即生效)"`
```
### 2. 修改已分配套餐的生效条件(新接口)
```
PATCH /admin/shop-package-allocations/{id}/expiry-base
```
请求 DTO
```go
type UpdateAllocationExpiryBaseRequest struct {
ExpiryBaseOverride *string `json:"expiry_base_override" validate:"omitempty,oneof=from_activation from_purchase" description:"生效条件null=恢复套餐默认, from_activation=实名即生效, from_purchase=购买即生效)"`
}
```
> 注意:修改已有分配记录的覆盖值,**不影响**已创建的 PackageUsage快照已定只影响后续新建的订单。
---
## 前端对接
### 套餐分配弹框
新增"生效条件"选择项:
```
生效条件:
○ 跟随套餐默认(默认选中,不传 expiry_base_override
○ 购买即生效from_purchase
○ 实名即生效from_activation
```
### 已分配套餐列表
列表新增"生效条件"列:
| 值 | 展示 |
|----|------|
| NULL | 套餐默认 |
| `from_activation` | 实名即生效(已覆盖) |
| `from_purchase` | 购买即生效(已覆盖) |
操作列增加"修改生效条件"按钮,调用 `PATCH /admin/shop-package-allocations/{id}/expiry-base`