You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
7.1 KiB
224 lines
7.1 KiB
package service
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
|
|
"laic-backend/common"
|
|
"laic-backend/logger"
|
|
"laic-backend/model"
|
|
"laic-backend/tool"
|
|
"laic-backend/vo"
|
|
)
|
|
|
|
type PlatformBillingService struct{}
|
|
|
|
var DefaultPlatformBillingService = &PlatformBillingService{}
|
|
|
|
func (s *PlatformBillingService) GetPool() (*model.PlatformResourcePool, *common.BusiError) {
|
|
var pool model.PlatformResourcePool
|
|
err := common.DB.Order("id ASC").First(&pool).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, common.ErrNotFound
|
|
}
|
|
if err != nil {
|
|
logger.ERROR("查询平台资源池失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
return &pool, nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) CreatePurchase(adminID int64, req *vo.PlatformPurchaseCreateReq) (*model.PlatformResourcePurchase, *common.BusiError) {
|
|
var purchase model.PlatformResourcePurchase
|
|
now := time.Now()
|
|
err := common.DB.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Where("batch_no = ?", req.BatchNo).First(&purchase).Error; err == nil {
|
|
if purchase.Provider == req.Provider && purchase.TotalBytes == req.TotalBytes && purchase.TotalCost == req.TotalCost {
|
|
return gorm.ErrDuplicatedKey
|
|
}
|
|
return common.NewBusiError(common.ParamError, "采购批次号已存在且内容不一致")
|
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
|
|
id, err := tool.NextID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
purchase = model.PlatformResourcePurchase{
|
|
ID: id, BatchNo: req.BatchNo, Provider: req.Provider, TotalBytes: req.TotalBytes,
|
|
UnitCost: req.UnitCost, TotalCost: req.TotalCost, ExpiresAt: req.ExpiresAt,
|
|
Status: "active", Remark: req.Remark, CreatedBy: adminID, CreatedAt: now, UpdatedAt: now,
|
|
}
|
|
if err := tx.Create(&purchase).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
var pool model.PlatformResourcePool
|
|
err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).Order("id ASC").First(&pool).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
pool = model.PlatformResourcePool{ID: mustPlatformID(), Status: "active", CreatedAt: now, UpdatedAt: now}
|
|
if pool.ID == 0 {
|
|
return common.ErrInternal
|
|
}
|
|
if err := tx.Create(&pool).Error; err != nil {
|
|
return err
|
|
}
|
|
err = nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
before := pool.AvailableBytes
|
|
if err := tx.Model(&pool).Updates(map[string]any{
|
|
"total_bytes": gorm.Expr("total_bytes + ?", req.TotalBytes),
|
|
"available_bytes": gorm.Expr("available_bytes + ?", req.TotalBytes),
|
|
"version": gorm.Expr("version + 1"), "updated_at": now,
|
|
}).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Create(&model.TrafficLedger{
|
|
ID: mustPlatformID(), AccountType: "platform", AccountID: pool.ID, Direction: "credit",
|
|
AmountBytes: req.TotalBytes, BalanceBefore: before, BalanceAfter: before + req.TotalBytes,
|
|
SourceType: "purchase", SourceID: req.BatchNo, IdempotencyKey: "purchase:" + req.BatchNo,
|
|
OperatorID: adminID, CreatedAt: now,
|
|
}).Error
|
|
})
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrDuplicatedKey) {
|
|
return &purchase, nil
|
|
}
|
|
if busiErr, ok := err.(*common.BusiError); ok {
|
|
return nil, busiErr
|
|
}
|
|
logger.ERROR("录入平台资源采购失败", err)
|
|
return nil, common.ErrInternal
|
|
}
|
|
return &purchase, nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) GetPurchasePage(req *vo.PlatformPurchasePageReq) (*common.PageResponse[model.PlatformResourcePurchase], *common.BusiError) {
|
|
db := common.DB.Model(&model.PlatformResourcePurchase{})
|
|
if req.Status != "" {
|
|
db = db.Where("status = ?", req.Status)
|
|
}
|
|
if req.Provider != "" {
|
|
db = db.Where("provider = ?", req.Provider)
|
|
}
|
|
if req.Keyword != "" {
|
|
db = db.Where("batch_no LIKE ?", "%"+req.Keyword+"%")
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
var list []model.PlatformResourcePurchase
|
|
if err := db.Scopes(req.Paginate).Order("created_at DESC, id DESC").Find(&list).Error; err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
return common.Page(req.Pagination, total, list), nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) CreatePackage(req *vo.TrafficPackageCreateReq) (*model.TrafficPackage, *common.BusiError) {
|
|
id, err := tool.NextID()
|
|
if err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
now := time.Now()
|
|
pkg := &model.TrafficPackage{ID: id, Code: req.Code, Name: req.Name, AmountBytes: req.AmountBytes, Price: req.Price, ValidityDays: req.ValidityDays, Status: "active", Sort: req.Sort, CreatedAt: now, UpdatedAt: now}
|
|
if err := common.DB.Create(pkg).Error; err != nil {
|
|
if code, _ := common.ParseError(err); code == 1062 {
|
|
return nil, common.NewBusiError(common.ParamError, "套餐编码已存在")
|
|
}
|
|
return nil, common.ErrInternal
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) GetPackagePage(req *vo.TrafficPackagePageReq) (*common.PageResponse[model.TrafficPackage], *common.BusiError) {
|
|
db := common.DB.Model(&model.TrafficPackage{})
|
|
if req.Status != "" {
|
|
db = db.Where("status = ?", req.Status)
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
var list []model.TrafficPackage
|
|
if err := db.Scopes(req.Paginate).Order("sort ASC, id DESC").Find(&list).Error; err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
return common.Page(req.Pagination, total, list), nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) UpdatePackage(id int64, req *vo.TrafficPackageUpdateReq) *common.BusiError {
|
|
updates := map[string]any{"updated_at": time.Now(), "sort": req.Sort}
|
|
if req.Name != "" {
|
|
updates["name"] = req.Name
|
|
}
|
|
if req.AmountBytes > 0 {
|
|
updates["amount_bytes"] = req.AmountBytes
|
|
}
|
|
if req.Price > 0 {
|
|
updates["price"] = req.Price
|
|
}
|
|
if req.ValidityDays > 0 {
|
|
updates["validity_days"] = req.ValidityDays
|
|
}
|
|
if req.Status != "" {
|
|
updates["status"] = req.Status
|
|
}
|
|
res := common.DB.Model(&model.TrafficPackage{}).Where("id = ?", id).Updates(updates)
|
|
if res.Error != nil {
|
|
return common.ErrInternal
|
|
}
|
|
if res.RowsAffected == 0 {
|
|
return common.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) DisablePackage(id int64) *common.BusiError {
|
|
res := common.DB.Model(&model.TrafficPackage{}).Where("id = ?", id).Updates(map[string]any{"status": "inactive", "updated_at": time.Now()})
|
|
if res.Error != nil {
|
|
return common.ErrInternal
|
|
}
|
|
if res.RowsAffected == 0 {
|
|
return common.ErrNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *PlatformBillingService) GetLedgerPage(req *vo.TrafficLedgerPageReq) (*common.PageResponse[model.TrafficLedger], *common.BusiError) {
|
|
db := common.DB.Model(&model.TrafficLedger{})
|
|
if req.AccountType != "" {
|
|
db = db.Where("account_type = ?", req.AccountType)
|
|
}
|
|
if req.AccountID > 0 {
|
|
db = db.Where("account_id = ?", req.AccountID)
|
|
}
|
|
if req.SourceType != "" {
|
|
db = db.Where("source_type = ?", req.SourceType)
|
|
}
|
|
var total int64
|
|
if err := db.Count(&total).Error; err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
var list []model.TrafficLedger
|
|
if err := db.Scopes(req.Paginate).Order("created_at DESC, id DESC").Find(&list).Error; err != nil {
|
|
return nil, common.ErrInternal
|
|
}
|
|
return common.Page(req.Pagination, total, list), nil
|
|
}
|
|
|
|
func mustPlatformID() int64 {
|
|
id, err := tool.NextID()
|
|
if err != nil {
|
|
logger.ERROR("生成平台账务 ID 失败", err)
|
|
return 0
|
|
}
|
|
return id
|
|
}
|
|
|