新增状态筛选条件,同时修复价格未正常写入的情况
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m35s

This commit is contained in:
2026-04-23 09:57:39 +08:00
parent a207c51bfb
commit 10a35625a5
7 changed files with 35 additions and 16 deletions

View File

@@ -1,6 +1,8 @@
package admin package admin
import ( import (
"strconv"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
dto "github.com/break/junhong_cmp_fiber/internal/model/dto" dto "github.com/break/junhong_cmp_fiber/internal/model/dto"
@@ -119,8 +121,17 @@ func (h *AssetHandler) Packages(c *fiber.Ctx) error {
page := c.QueryInt("page", 1) page := c.QueryInt("page", 1)
pageSize := c.QueryInt("page_size", 50) pageSize := c.QueryInt("page_size", 50)
statusRaw := c.Query("status")
var status *int
if statusRaw != "" {
parsedStatus, parseErr := strconv.Atoi(statusRaw)
if parseErr != nil || parsedStatus < constants.PackageUsageStatusPending || parsedStatus > constants.PackageUsageStatusInvalidated {
return errors.New(errors.CodeInvalidParam)
}
status = &parsedStatus
}
result, err := h.assetService.GetPackages(c.UserContext(), asset.AssetType, asset.AssetID, page, pageSize) result, err := h.assetService.GetPackages(c.UserContext(), asset.AssetType, asset.AssetID, page, pageSize, status)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -223,7 +223,7 @@ func (h *ClientAssetHandler) GetAvailablePackages(c *fiber.Ctx) error {
return errors.New(errors.CodeNoAvailablePackage, "当前无可购买套餐") return errors.New(errors.CodeNoAvailablePackage, "当前无可购买套餐")
} }
packagesResult, err := h.assetService.GetPackages(resolved.SkipPermissionCtx, resolved.Asset.AssetType, resolved.Asset.AssetID, 1, 50) packagesResult, err := h.assetService.GetPackages(resolved.SkipPermissionCtx, resolved.Asset.AssetType, resolved.Asset.AssetID, 1, 50, nil)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -140,12 +140,12 @@ type AssetTypeIDRequest struct {
ID uint `path:"id" description:"资产ID" required:"true"` ID uint `path:"id" description:"资产ID" required:"true"`
} }
// AssetPackagesRequest 资产套餐列表请求(路径参数 + 分页) // AssetPackagesRequest 资产套餐列表请求(路径参数 + 分页 + 状态筛选
type AssetPackagesRequest struct { type AssetPackagesRequest struct {
AssetType string `path:"asset_type" description:"资产类型card 或 device" required:"true"` Identifier string `path:"identifier" description:"资产标识符(虚拟号/ICCID/IMEI/SN/MSISDN" required:"true"`
ID uint `path:"id" description:"资产ID" required:"true"` Page int `query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码默认1"`
Page int `query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码默认1"` PageSize int `query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页条数默认50最大100"`
PageSize int `query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页条数默认50最大100"` Status *int `query:"status" validate:"omitempty,min=0,max=4" minimum:"0" maximum:"4" description:"套餐状态 (0:待生效, 1:生效中, 2:已用完, 3:已过期, 4:已失效)"`
} }
// DeviceIDRequest 设备ID请求路径参数 // DeviceIDRequest 设备ID请求路径参数

View File

@@ -41,9 +41,9 @@ func registerAssetRoutes(router fiber.Router, handler *admin.AssetHandler, walle
Register(assets, doc, groupPath, "GET", "/:identifier/packages", handler.Packages, RouteSpec{ Register(assets, doc, groupPath, "GET", "/:identifier/packages", handler.Packages, RouteSpec{
Summary: "资产套餐列表", Summary: "资产套餐列表",
Description: "查询该资产所有套餐记录,含虚流量换算结果。支持分页。", Description: "查询该资产所有套餐记录,含虚流量换算结果。支持分页与 status 状态筛选。",
Tags: []string{"资产管理"}, Tags: []string{"资产管理"},
Input: new(dto.AssetIdentifierRequest), Input: new(dto.AssetPackagesRequest),
Output: new(dto.AssetPackagesResult), Output: new(dto.AssetPackagesResult),
Auth: true, Auth: true,
}) })

View File

@@ -648,9 +648,9 @@ func parseGatewayTime(raw gateway.FlexString) *time.Time {
return &t return &t
} }
// GetPackages 获取资产的所有套餐列表(支持分页) // GetPackages 获取资产的所有套餐列表(支持分页和状态筛选
// page 默认 1pageSize 默认 50pageSize 最大 100 // page 默认 1pageSize 默认 50pageSize 最大 100
func (s *Service) GetPackages(ctx context.Context, assetType string, id uint, page, pageSize int) (*dto.AssetPackagesResult, error) { func (s *Service) GetPackages(ctx context.Context, assetType string, id uint, page, pageSize int, status *int) (*dto.AssetPackagesResult, error) {
// 分页参数边界处理 // 分页参数边界处理
if page < 1 { if page < 1 {
page = 1 page = 1
@@ -661,6 +661,9 @@ func (s *Service) GetPackages(ctx context.Context, assetType string, id uint, pa
if pageSize > 100 { if pageSize > 100 {
pageSize = 100 pageSize = 100
} }
if status != nil && (*status < constants.PackageUsageStatusPending || *status > constants.PackageUsageStatusInvalidated) {
return nil, errors.New(errors.CodeInvalidParam, "套餐状态非法")
}
// assetType 对应 Store 中的 carrierTypecard→iot_card, device→device // assetType 对应 Store 中的 carrierTypecard→iot_card, device→device
carrierType := assetType carrierType := assetType
@@ -668,7 +671,7 @@ func (s *Service) GetPackages(ctx context.Context, assetType string, id uint, pa
carrierType = "iot_card" carrierType = "iot_card"
} }
usages, err := s.packageUsageStore.ListByCarrier(ctx, carrierType, id) usages, err := s.packageUsageStore.ListByCarrier(ctx, carrierType, id, status)
if err != nil { if err != nil {
return nil, errors.Wrap(errors.CodeInternalError, err, "查询套餐使用记录失败") return nil, errors.Wrap(errors.CodeInternalError, err, "查询套餐使用记录失败")
} }
@@ -706,7 +709,6 @@ func (s *Service) GetPackages(ctx context.Context, assetType string, id uint, pa
pkgType = pkg.PackageType pkgType = pkg.PackageType
enableVirtualData = pkg.EnableVirtualData enableVirtualData = pkg.EnableVirtualData
} }
item := &dto.AssetPackageResponse{ item := &dto.AssetPackageResponse{
PackageUsageID: u.ID, PackageUsageID: u.ID,
PackageID: u.PackageID, PackageID: u.PackageID,
@@ -784,7 +786,6 @@ func (s *Service) GetCurrentPackage(ctx context.Context, assetType string, id ui
pkgType = pkg.PackageType pkgType = pkg.PackageType
enableVirtualData = pkg.EnableVirtualData enableVirtualData = pkg.EnableVirtualData
} }
return &dto.AssetPackageResponse{ return &dto.AssetPackageResponse{
PackageUsageID: usage.ID, PackageUsageID: usage.ID,
PackageID: usage.PackageID, PackageID: usage.PackageID,

View File

@@ -1452,6 +1452,10 @@ func (s *Service) HandlePaymentCallback(ctx context.Context, orderNo string, pay
} }
} }
// 回调更新成功后,同步内存对象,确保激活套餐时写入 paid_amount 快照
actualPaidAmountSnapshot := actualPaidAmount
order.ActualPaidAmount = &actualPaidAmountSnapshot
return s.activatePackage(ctx, tx, order) return s.activatePackage(ctx, tx, order)
}) })

View File

@@ -212,8 +212,8 @@ func (s *PackageUsageStore) GetActiveOrDepletedByCarrier(ctx context.Context, ca
return &pkg, nil return &pkg, nil
} }
// ListByCarrier 查询载体的所有套餐使用记录(包括所有状态 // ListByCarrier 查询载体的套餐使用记录(可按状态筛选
func (s *PackageUsageStore) ListByCarrier(ctx context.Context, carrierType string, carrierID uint) ([]*model.PackageUsage, error) { func (s *PackageUsageStore) ListByCarrier(ctx context.Context, carrierType string, carrierID uint, status *int) ([]*model.PackageUsage, error) {
var usages []*model.PackageUsage var usages []*model.PackageUsage
query := s.db.WithContext(ctx).Order("priority ASC, created_at DESC") query := s.db.WithContext(ctx).Order("priority ASC, created_at DESC")
@@ -222,6 +222,9 @@ func (s *PackageUsageStore) ListByCarrier(ctx context.Context, carrierType strin
} else if carrierType == "device" { } else if carrierType == "device" {
query = query.Where("device_id = ?", carrierID) query = query.Where("device_id = ?", carrierID)
} }
if status != nil {
query = query.Where("status = ?", *status)
}
if err := query.Find(&usages).Error; err != nil { if err := query.Find(&usages).Error; err != nil {
return nil, err return nil, err