From e2687de33856612c30a1fd44be32b991f9182193 Mon Sep 17 00:00:00 2001 From: break Date: Thu, 30 Jul 2026 16:23:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A9=20C=20=E7=AB=AF=E5=9C=A8=E7=BB=AD?= =?UTF-8?q?=E8=B4=B9=E5=89=8D=E5=B1=95=E7=A4=BA=E5=BD=93=E5=89=8D=E5=BA=94?= =?UTF-8?q?=E4=BB=98=E4=BB=B7=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constraint: 续费展示价必须与当前销售渠道的生效零售价一致 Rejected: 新增专用续费报价接口 | 现有资产信息已承载续费入口上下文 Confidence: high Scope-risk: narrow Directive: 下单时仍须重新校验续费资格和最终价格 Tested: go build ./internal/handler/app Not-tested: 按用户要求未添加或运行测试 --- README.md | 1 + docs/client-asset-renewal-price/功能总结.md | 7 ++++++ internal/handler/app/client_asset.go | 25 +++++++++++++++++++++ internal/model/dto/client_asset_dto.go | 1 + 4 files changed, 34 insertions(+) create mode 100644 docs/client-asset-renewal-price/功能总结.md diff --git a/README.md b/README.md index 74b61aa..7e72ffa 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ## 功能文档 - [全局审计当前实现进度](docs/tech-global-audit/当前实现进度.md) +- [C 端资产续费价格](docs/client-asset-renewal-price/功能总结.md) ## 系统简介 diff --git a/docs/client-asset-renewal-price/功能总结.md b/docs/client-asset-renewal-price/功能总结.md new file mode 100644 index 0000000..0c6d0c1 --- /dev/null +++ b/docs/client-asset-renewal-price/功能总结.md @@ -0,0 +1,7 @@ +# C 端资产续费价格 + +`GET /api/c/v1/asset/info` 在当前套餐信息中返回 `renewal_price`,单位为分;无当前主套餐时返回 `0`。 + +- 平台自营资产:返回套餐当前生效零售价。 +- 代理渠道资产:返回该代理套餐分配记录的当前生效零售价。 +- 续费下单仍由原订单创建接口重新校验资格和最终价格,前端展示值不作为下单价格凭证。 diff --git a/internal/handler/app/client_asset.go b/internal/handler/app/client_asset.go index 23a87ab..a04bbc0 100644 --- a/internal/handler/app/client_asset.go +++ b/internal/handler/app/client_asset.go @@ -191,6 +191,10 @@ func (h *ClientAssetHandler) GetAssetInfo(c *fiber.Ctx) error { if err != nil { return err } + renewalPrice, err := h.getRenewalPrice(resolved.SkipPermissionCtx, currentPackageID, resolved.SellerShopID) + if err != nil { + return err + } if h.paymentMethodPolicy == nil { return errors.New(errors.CodeNoPaymentConfig) } @@ -239,6 +243,7 @@ func (h *ClientAssetHandler) GetAssetInfo(c *fiber.Ctx) error { ActivatedAt: resolved.Asset.ActivatedAt, CurrentPackage: resolved.Asset.CurrentPackage, CurrentPackageID: currentPackageID, + RenewalPrice: renewalPrice, CurrentPackageUsageID: resolved.Asset.CurrentPackageUsageID, CurrentPackageActivatedAt: resolved.Asset.CurrentPackageActivatedAt, CurrentPackageExpiresAt: resolved.Asset.CurrentPackageExpiresAt, @@ -302,6 +307,26 @@ 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) { + if packageID == 0 { + return 0, nil + } + + pkg, err := h.packageStore.GetByID(ctx, packageID) + if err != nil { + return 0, errors.Wrap(errors.CodeDatabaseError, err, "查询续费套餐失败") + } + if sellerShopID == 0 { + return packageprice.PackageEffectiveRetailPrice(pkg), nil + } + + allocation, err := h.shopPackageAllocationStore.GetByShopAndPackageForSystem(ctx, sellerShopID, packageID) + if err != nil { + return 0, errors.Wrap(errors.CodeDatabaseError, err, "查询续费套餐价格失败") + } + return packageprice.AllocationEffectiveRetailPrice(allocation), nil +} + func (h *ClientAssetHandler) dispatchAssetReadObservations(ctx context.Context, asset *dto.AssetResolveResponse) { if h.observationSeries == nil || asset == nil { return diff --git a/internal/model/dto/client_asset_dto.go b/internal/model/dto/client_asset_dto.go index bb1bb68..2f41a62 100644 --- a/internal/model/dto/client_asset_dto.go +++ b/internal/model/dto/client_asset_dto.go @@ -41,6 +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)"` 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:"当前主套餐过期时间"`