实现套餐生效条件覆盖与购买快照

This commit is contained in:
2026-07-22 20:51:12 +09:00
parent c7f8b4c702
commit 9818537239
30 changed files with 1092 additions and 149 deletions

View File

@@ -2011,6 +2011,10 @@ components:
type: object
DtoBatchAllocatePackagesRequest:
properties:
expiry_base_override:
description: 分配生效条件覆盖 (null:跟随套餐默认值, from_activation:实名激活时生效, from_purchase:购买即生效)
nullable: true
type: string
one_time_commission_amount:
description: 该代理能拿到的一次性佣金(分)
minimum: 0
@@ -2030,6 +2034,24 @@ components:
- shop_id
- series_id
type: object
DtoBatchAllocatePackagesResponse:
properties:
allocated_count:
description: 成功分配数量
type: integer
allocations:
description: 本次新建套餐分配及其生效条件
items:
$ref: '#/components/schemas/DtoShopPackageAllocationTermsResponse'
nullable: true
type: array
skipped_count:
description: 跳过数量(已存在)
type: integer
total_packages:
description: 总套餐数
type: integer
type: object
DtoBatchPricingSkipped:
properties:
allocation_id:
@@ -3845,6 +3867,10 @@ components:
description: 是否启用代理强充
nullable: true
type: boolean
expiry_base_override:
description: 分配生效条件覆盖 (null:跟随套餐默认值, from_activation:实名激活时生效, from_purchase:购买即生效)
nullable: true
type: string
force_recharge_amount:
description: 代理强充金额(分)
nullable: true
@@ -6085,6 +6111,10 @@ components:
type: object
DtoManageGrantPackagesParams:
properties:
expiry_base_override:
description: 新增分配的生效条件覆盖 (null:跟随套餐默认值, from_activation:实名激活时生效, from_purchase:购买即生效)
nullable: true
type: string
packages:
description: 套餐操作列表
items:
@@ -6605,6 +6635,12 @@ components:
data_reset_cycle:
description: 流量重置周期 (daily:每日, monthly:每月, yearly:每年, none:不重置)
type: string
default_expiry_base:
description: 套餐默认生效条件 (from_activation:实名激活时生效, from_purchase:购买即生效)
type: string
default_expiry_base_name:
description: 套餐默认生效条件名称(中文)
type: string
duration_days:
description: 套餐天数(calendar_type=by_day时有值)
nullable: true
@@ -6612,6 +6648,12 @@ components:
duration_months:
description: 套餐时长(月数)
type: integer
effective_expiry_base:
description: 最终生效条件 (from_activation:实名激活时生效, from_purchase:购买即生效)
type: string
effective_expiry_base_name:
description: 最终生效条件名称(中文)
type: string
effective_retail_price:
description: 生效零售价(分,仅销售链路展示使用)
nullable: true
@@ -6622,6 +6664,13 @@ components:
expiry_base:
description: 到期时间基准 (from_activation:实名激活时起算, from_purchase:购买时起算)
type: string
expiry_base_override:
description: 分配生效条件覆盖null 表示跟随套餐默认值
nullable: true
type: string
expiry_base_override_name:
description: 分配生效条件覆盖名称(中文)
type: string
id:
description: 套餐ID
minimum: 0
@@ -7991,6 +8040,32 @@ components:
description: 总记录数
type: integer
type: object
DtoShopPackageAllocationTermsResponse:
properties:
default_expiry_base:
description: 套餐默认生效条件 (from_activation:实名激活时生效, from_purchase:购买即生效)
type: string
default_expiry_base_name:
description: 套餐默认生效条件名称(中文)
type: string
effective_expiry_base:
description: 最终生效条件 (from_activation:实名激活时生效, from_purchase:购买即生效)
type: string
effective_expiry_base_name:
description: 最终生效条件名称(中文)
type: string
expiry_base_override:
description: 分配生效条件覆盖null 表示跟随套餐默认值
nullable: true
type: string
expiry_base_override_name:
description: 分配生效条件覆盖名称(中文)
type: string
id:
description: 套餐分配ID
minimum: 0
type: integer
type: object
DtoShopPageResult:
properties:
items:
@@ -8153,6 +8228,25 @@ components:
cost_price:
description: 成本价(分)
type: integer
default_expiry_base:
description: 套餐默认生效条件 (from_activation:实名激活时生效, from_purchase:购买即生效)
type: string
default_expiry_base_name:
description: 套餐默认生效条件名称(中文)
type: string
effective_expiry_base:
description: 最终生效条件 (from_activation:实名激活时生效, from_purchase:购买即生效)
type: string
effective_expiry_base_name:
description: 最终生效条件名称(中文)
type: string
expiry_base_override:
description: 分配生效条件覆盖null 表示跟随套餐默认值
nullable: true
type: string
expiry_base_override_name:
description: 分配生效条件覆盖名称(中文)
type: string
package_code:
description: 套餐编码
type: string
@@ -8610,6 +8704,13 @@ components:
nullable: true
type: string
type: object
DtoUpdateAllocationExpiryBaseParams:
properties:
expiry_base_override:
description: 分配生效条件覆盖 (null:跟随套餐默认值, from_activation:实名激活时生效, from_purchase:购买即生效)
nullable: true
type: string
type: object
DtoUpdateAssetPackageExpiresAtRequest:
properties:
expires_at:
@@ -22325,6 +22426,78 @@ paths:
summary: 移除权限
tags:
- 角色
/api/admin/shop-package-allocations/{id}/expiry-base:
patch:
parameters:
- description: ID
in: path
name: id
required: true
schema:
description: ID
minimum: 0
type: integer
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DtoUpdateAllocationExpiryBaseParams'
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoShopPackageAllocationTermsResponse'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 请求参数错误
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 未认证或认证已过期
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 无权访问
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: 服务器内部错误
security:
- BearerAuth: []
summary: 修改套餐分配生效条件覆盖
tags:
- 批量套餐分配
/api/admin/shop-package-batch-allocations:
post:
requestBody:
@@ -22333,6 +22506,32 @@ paths:
schema:
$ref: '#/components/schemas/DtoBatchAllocatePackagesRequest'
responses:
"200":
content:
application/json:
schema:
properties:
code:
description: 响应码
example: 0
type: integer
data:
$ref: '#/components/schemas/DtoBatchAllocatePackagesResponse'
msg:
description: 响应消息
example: success
type: string
timestamp:
description: 时间戳
format: date-time
type: string
required:
- code
- msg
- data
- timestamp
type: object
description: 成功
"400":
content:
application/json:

View File

@@ -0,0 +1,24 @@
# UR#55 套餐分配生效条件覆盖与购买快照
## 实现范围
- 系列首次授权、系列追加授权和批量套餐分配必须显式提交 `expiry_base_override`,支持 `null``from_purchase``from_activation`
- 套餐分配响应返回套餐默认值、分配覆盖值和最终有效值及其中文名称。
- 新增单条分配覆盖修改接口:`PATCH /api/admin/shop-package-allocations/{id}/expiry-base`,显式 `null` 恢复跟随默认,重复提交保持幂等。
- 同步购买和自动购包创建主套餐或加油包时,在同一事务写入生效条件、周期类型、月数和天数快照。
- 实名激活、指定激活、排队接续和退款后的下一套餐接续优先读取购买快照;仅四字段全部为空或零的历史记录允许告警回退套餐当前配置,部分缺失或非法快照直接拒绝。
## 数据库变更
- `tb_shop_package_allocation.expiry_base_override`:可空字符串,`NULL` 表示跟随套餐默认值。
- `tb_package_usage`:新增 `expiry_base_snapshot``calendar_type_snapshot``duration_months_snapshot``duration_days_snapshot`
- 历史套餐使用记录不回填,保留空值和零值作为历史未快照标记。
## 发布与回滚
发布时先部署兼容读取快照和历史回退的应用,再执行 162、163 迁移,并在同一维护窗口切换所有写入路径。回滚迁移只删除本需求新增字段和约束,不修改套餐使用状态或历史业务数据。
## 验证
- 领域测试覆盖覆盖值解析、自然月/按天快照、快照优先、历史回退和异常快照拒绝。
- 已执行全仓 `go test ./...`、OpenAPI 文档生成以及迁移向上、单步向下、重新向上验证。