避免续费报价缺失阻断资产详情
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 1m23s

Constraint: 代理渠道缺少套餐分配记录时不存在可信的当前续费价
Rejected: 回退平台价或历史成交价 | 会与实际下单校验和当前价格口径不一致
Confidence: high
Scope-risk: narrow
Directive: renewal_price 为 null 时前端不得展示价格或发起续费
Tested: go build ./internal/handler/app
Not-tested: 按用户要求未添加或运行测试
This commit is contained in:
2026-07-30 17:14:28 +08:00
parent e2687de338
commit cf2ff0ac1c
3 changed files with 16 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
# C 端资产续费价格
`GET /api/c/v1/asset/info` 在当前套餐信息中返回 `renewal_price`,单位为分;无当前主套餐时返回 `0`
`GET /api/c/v1/asset/info` 在当前套餐信息中返回 `renewal_price`,单位为分;无当前主套餐或当前销售渠道没有有效续费报价时返回 `null`
- 平台自营资产:返回套餐当前生效零售价。
- 代理渠道资产:返回该代理套餐分配记录的当前生效零售价。

View File

@@ -307,24 +307,32 @@ func (h *ClientAssetHandler) getCurrentPackageID(ctx context.Context, usageID *u
return usage.PackageID, nil
}
func (h *ClientAssetHandler) getRenewalPrice(ctx context.Context, packageID, sellerShopID uint) (int64, error) {
func (h *ClientAssetHandler) getRenewalPrice(ctx context.Context, packageID, sellerShopID uint) (*int64, error) {
if packageID == 0 {
return 0, nil
return nil, nil
}
pkg, err := h.packageStore.GetByID(ctx, packageID)
if err != nil {
return 0, errors.Wrap(errors.CodeDatabaseError, err, "查询续费套餐失败")
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询续费套餐失败")
}
if sellerShopID == 0 {
return packageprice.PackageEffectiveRetailPrice(pkg), nil
price := packageprice.PackageEffectiveRetailPrice(pkg)
return &price, nil
}
allocation, err := h.shopPackageAllocationStore.GetByShopAndPackageForSystem(ctx, sellerShopID, packageID)
if err != nil {
return 0, errors.Wrap(errors.CodeDatabaseError, err, "查询续费套餐价格失败")
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询续费套餐价格失败")
}
return packageprice.AllocationEffectiveRetailPrice(allocation), nil
price := packageprice.AllocationEffectiveRetailPrice(allocation)
return &price, nil
}
func (h *ClientAssetHandler) dispatchAssetReadObservations(ctx context.Context, asset *dto.AssetResolveResponse) {

View File

@@ -41,7 +41,7 @@ type AssetInfoResponse struct {
// === 套餐信息(通用) ===
CurrentPackage string `json:"current_package" description:"当前套餐名称(无套餐时为空)"`
CurrentPackageID uint `json:"current_package_id" description:"当前主套餐ID无主套餐时为0可用于发起续费"`
RenewalPrice int64 `json:"renewal_price" description:"当前主套餐续费价格(分,按当前销售渠道的生效零售价计算;无主套餐时为0"`
RenewalPrice *int64 `json:"renewal_price" description:"当前主套餐续费价格(分,按当前销售渠道的生效零售价计算;无主套餐或当前渠道不可续费时为 null"`
CurrentPackageUsageID *uint `json:"current_package_usage_id" description:"当前主套餐的套餐使用记录ID无主套餐时为 null"`
CurrentPackageActivatedAt *time.Time `json:"current_package_activated_at,omitempty" description:"当前主套餐开始时间"`
CurrentPackageExpiresAt *time.Time `json:"current_package_expires_at,omitempty" description:"当前主套餐过期时间"`