Files
junhong_cmp_fiber/internal/service/purchase_validation/service.go
huang dfcf16f548
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m36s
feat: 实现订单支付功能模块
- 新增订单管理、支付回调、购买验证等核心服务
- 实现订单、订单项目的数据存储层和 API 接口
- 添加订单数据库迁移和 DTO 定义
- 更新 API 文档和路由配置
- 同步 3 个新规范到主规范库(订单管理、订单支付、套餐购买验证)
- 完成 OpenSpec 变更归档

Ultraworked with Sisyphus
2026-01-28 22:12:15 +08:00

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
}