Files
junhong_cmp_fiber/internal/service/purchase_validation/service.go
huang 37f43d2e2d 重构: 将卡/设备的套餐系列绑定从分配ID改为系列ID
- 数据库: 重命名 series_allocation_id → series_id
- Model: IotCard 和 Device 字段重命名
- DTO: 所有请求/响应字段统一为 series_id
- Store: 方法重命名,新增 GetByShopAndSeries 查询
- Service: 业务逻辑优化,系列验证和权限验证分离
- 测试: 更新所有测试用例,新增 shop_series_allocation_store_test.go
- 文档: 更新 API 文档说明参数变更

BREAKING CHANGE: API 参数从 series_allocation_id 改为 series_id
2026-02-02 12:09:53 +08:00

134 lines
3.7 KiB
Go

package purchase_validation
import (
"context"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"gorm.io/gorm"
)
type Service struct {
db *gorm.DB
iotCardStore *postgres.IotCardStore
deviceStore *postgres.DeviceStore
packageStore *postgres.PackageStore
seriesAllocationStore *postgres.ShopSeriesAllocationStore
}
func New(
db *gorm.DB,
iotCardStore *postgres.IotCardStore,
deviceStore *postgres.DeviceStore,
packageStore *postgres.PackageStore,
seriesAllocationStore *postgres.ShopSeriesAllocationStore,
) *Service {
return &Service{
db: db,
iotCardStore: iotCardStore,
deviceStore: deviceStore,
packageStore: packageStore,
seriesAllocationStore: seriesAllocationStore,
}
}
type PurchaseValidationResult struct {
Card *model.IotCard
Device *model.Device
Packages []*model.Package
TotalPrice int64
Allocation *model.ShopSeriesAllocation
}
func (s *Service) ValidateCardPurchase(ctx context.Context, cardID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
card, err := s.iotCardStore.GetByID(ctx, cardID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeIotCardNotFound, "IoT卡不存在")
}
return nil, err
}
if card.SeriesID == nil || *card.SeriesID == 0 {
return nil, errors.New(errors.CodeInvalidParam, "该卡未关联套餐系列,无法购买套餐")
}
packages, totalPrice, err := s.validatePackages(ctx, packageIDs, *card.SeriesID)
if err != nil {
return nil, err
}
return &PurchaseValidationResult{
Card: card,
Packages: packages,
TotalPrice: totalPrice,
}, nil
}
func (s *Service) ValidateDevicePurchase(ctx context.Context, deviceID uint, packageIDs []uint) (*PurchaseValidationResult, error) {
device, err := s.deviceStore.GetByID(ctx, deviceID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "设备不存在")
}
return nil, err
}
if device.SeriesID == nil || *device.SeriesID == 0 {
return nil, errors.New(errors.CodeInvalidParam, "该设备未关联套餐系列,无法购买套餐")
}
packages, totalPrice, err := s.validatePackages(ctx, packageIDs, *device.SeriesID)
if err != nil {
return nil, err
}
return &PurchaseValidationResult{
Device: device,
Packages: packages,
TotalPrice: totalPrice,
}, nil
}
func (s *Service) validatePackages(ctx context.Context, packageIDs []uint, seriesID uint) ([]*model.Package, int64, error) {
if len(packageIDs) == 0 {
return nil, 0, errors.New(errors.CodeInvalidParam, "请选择至少一个套餐")
}
var packages []*model.Package
var totalPrice int64
for _, pkgID := range packageIDs {
pkg, err := s.packageStore.GetByID(ctx, pkgID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, 0, errors.New(errors.CodeInvalidParam, "套餐不存在")
}
return nil, 0, err
}
if pkg.SeriesID != seriesID {
return nil, 0, errors.New(errors.CodeInvalidParam, "套餐不在可购买范围内")
}
if pkg.Status != constants.StatusEnabled {
return nil, 0, errors.New(errors.CodeInvalidParam, "套餐已禁用")
}
if pkg.ShelfStatus != constants.ShelfStatusOn {
return nil, 0, errors.New(errors.CodeInvalidParam, "套餐已下架")
}
packages = append(packages, pkg)
totalPrice += pkg.SuggestedRetailPrice
}
return packages, totalPrice, nil
}
func (s *Service) GetPurchasePrice(ctx context.Context, pkg *model.Package, buyerType string) int64 {
return pkg.SuggestedRetailPrice
}