All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m36s
- 新增订单管理、支付回调、购买验证等核心服务 - 实现订单、订单项目的数据存储层和 API 接口 - 添加订单数据库迁移和 DTO 定义 - 更新 API 文档和路由配置 - 同步 3 个新规范到主规范库(订单管理、订单支付、套餐购买验证) - 完成 OpenSpec 变更归档 Ultraworked with Sisyphus
160 lines
4.5 KiB
Go
160 lines
4.5 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.SeriesAllocationID == nil || *card.SeriesAllocationID == 0 {
|
|
return nil, errors.New(errors.CodeInvalidParam, "该卡未关联套餐系列,无法购买套餐")
|
|
}
|
|
|
|
allocation, err := s.seriesAllocationStore.GetByID(ctx, *card.SeriesAllocationID)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, errors.New(errors.CodeInvalidParam, "套餐系列分配不存在")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
if allocation.Status != constants.StatusEnabled {
|
|
return nil, errors.New(errors.CodeInvalidParam, "套餐系列分配已禁用")
|
|
}
|
|
|
|
packages, totalPrice, err := s.validatePackages(ctx, packageIDs, allocation.SeriesID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &PurchaseValidationResult{
|
|
Card: card,
|
|
Packages: packages,
|
|
TotalPrice: totalPrice,
|
|
Allocation: allocation,
|
|
}, 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.SeriesAllocationID == nil || *device.SeriesAllocationID == 0 {
|
|
return nil, errors.New(errors.CodeInvalidParam, "该设备未关联套餐系列,无法购买套餐")
|
|
}
|
|
|
|
allocation, err := s.seriesAllocationStore.GetByID(ctx, *device.SeriesAllocationID)
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, errors.New(errors.CodeInvalidParam, "套餐系列分配不存在")
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
if allocation.Status != constants.StatusEnabled {
|
|
return nil, errors.New(errors.CodeInvalidParam, "套餐系列分配已禁用")
|
|
}
|
|
|
|
packages, totalPrice, err := s.validatePackages(ctx, packageIDs, allocation.SeriesID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &PurchaseValidationResult{
|
|
Device: device,
|
|
Packages: packages,
|
|
TotalPrice: totalPrice,
|
|
Allocation: allocation,
|
|
}, 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
|
|
}
|