mirror of
				https://github.com/TeaOSLab/EdgeAPI.git
				synced 2025-11-04 16:00:24 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			207 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package models
 | 
						||
 | 
						||
import (
 | 
						||
	"encoding/json"
 | 
						||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
 | 
						||
	_ "github.com/go-sql-driver/mysql"
 | 
						||
	"github.com/iwind/TeaGo/Tea"
 | 
						||
	"github.com/iwind/TeaGo/dbs"
 | 
						||
)
 | 
						||
 | 
						||
type BillType = string
 | 
						||
 | 
						||
const (
 | 
						||
	BillTypeTraffic BillType = "traffic" // 按流量计费
 | 
						||
)
 | 
						||
 | 
						||
type UserBillDAO dbs.DAO
 | 
						||
 | 
						||
func NewUserBillDAO() *UserBillDAO {
 | 
						||
	return dbs.NewDAO(&UserBillDAO{
 | 
						||
		DAOObject: dbs.DAOObject{
 | 
						||
			DB:     Tea.Env,
 | 
						||
			Table:  "edgeUserBills",
 | 
						||
			Model:  new(UserBill),
 | 
						||
			PkName: "id",
 | 
						||
		},
 | 
						||
	}).(*UserBillDAO)
 | 
						||
}
 | 
						||
 | 
						||
var SharedUserBillDAO *UserBillDAO
 | 
						||
 | 
						||
func init() {
 | 
						||
	dbs.OnReady(func() {
 | 
						||
		SharedUserBillDAO = NewUserBillDAO()
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
// 计算账单数量
 | 
						||
func (this *UserBillDAO) CountAllUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string) (int64, error) {
 | 
						||
	query := this.Query(tx)
 | 
						||
	if isPaid == 0 {
 | 
						||
		query.Attr("isPaid", 0)
 | 
						||
	} else if isPaid > 0 {
 | 
						||
		query.Attr("isPaid", 1)
 | 
						||
	}
 | 
						||
	if userId > 0 {
 | 
						||
		query.Attr("userId", userId)
 | 
						||
	}
 | 
						||
	if len(month) > 0 {
 | 
						||
		query.Attr("month", month)
 | 
						||
	}
 | 
						||
	return query.Count()
 | 
						||
}
 | 
						||
 | 
						||
// 列出单页账单
 | 
						||
func (this *UserBillDAO) ListUserBills(tx *dbs.Tx, isPaid int32, userId int64, month string, offset, size int64) (result []*UserBill, err error) {
 | 
						||
	query := this.Query(tx)
 | 
						||
	if isPaid == 0 {
 | 
						||
		query.Attr("isPaid", 0)
 | 
						||
	} else if isPaid > 0 {
 | 
						||
		query.Attr("isPaid", 1)
 | 
						||
	}
 | 
						||
	if userId > 0 {
 | 
						||
		query.Attr("userId", userId)
 | 
						||
	}
 | 
						||
	if len(month) > 0 {
 | 
						||
		query.Attr("month", month)
 | 
						||
	}
 | 
						||
	_, err = query.
 | 
						||
		Offset(offset).
 | 
						||
		Limit(size).
 | 
						||
		Slice(&result).
 | 
						||
		DescPk().
 | 
						||
		FindAll()
 | 
						||
	return
 | 
						||
}
 | 
						||
 | 
						||
// 创建账单
 | 
						||
func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType, description string, amount float32, month string) (int64, error) {
 | 
						||
	op := NewUserBillOperator()
 | 
						||
	op.UserId = userId
 | 
						||
	op.Type = billType
 | 
						||
	op.Description = description
 | 
						||
	op.Amount = amount
 | 
						||
	op.Month = month
 | 
						||
	op.IsPaid = false
 | 
						||
	return this.SaveInt64(tx, op)
 | 
						||
}
 | 
						||
 | 
						||
// 检查是否有当月账单
 | 
						||
func (this *UserBillDAO) ExistBill(tx *dbs.Tx, userId int64, billType BillType, month string) (bool, error) {
 | 
						||
	return this.Query(tx).
 | 
						||
		Attr("userId", userId).
 | 
						||
		Attr("month", month).
 | 
						||
		Attr("type", billType).
 | 
						||
		Exist()
 | 
						||
}
 | 
						||
 | 
						||
// 生成账单
 | 
						||
// month 格式YYYYMM
 | 
						||
func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
 | 
						||
	// 用户
 | 
						||
	offset := int64(0)
 | 
						||
	size := int64(100) // 每次只查询N次,防止由于执行时间过长而锁表
 | 
						||
	for {
 | 
						||
		userIds, err := SharedUserDAO.ListEnabledUserIds(tx, offset, size)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		offset += size
 | 
						||
		if len(userIds) == 0 {
 | 
						||
			break
 | 
						||
		}
 | 
						||
 | 
						||
		for _, userId := range userIds {
 | 
						||
			// CDN流量账单
 | 
						||
			err := this.generateTrafficBill(tx, userId, month)
 | 
						||
			if err != nil {
 | 
						||
				return err
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return nil
 | 
						||
}
 | 
						||
 | 
						||
// 生成CDN流量账单
 | 
						||
// month 格式YYYYMM
 | 
						||
func (this *UserBillDAO) generateTrafficBill(tx *dbs.Tx, userId int64, month string) error {
 | 
						||
	// 检查是否已经有账单了
 | 
						||
	b, err := this.ExistBill(tx, userId, BillTypeTraffic, month)
 | 
						||
	if err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	if b {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	// TODO 优化使用缓存
 | 
						||
	regions, err := SharedNodeRegionDAO.FindAllEnabledRegionPrices(tx)
 | 
						||
	if err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	if len(regions) == 0 {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	priceItems, err := SharedNodePriceItemDAO.FindAllEnabledRegionPrices(tx, NodePriceTypeTraffic)
 | 
						||
	if err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	if len(priceItems) == 0 {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	cost := float32(0)
 | 
						||
	for _, region := range regions {
 | 
						||
		if len(region.Prices) == 0 || region.Prices == "null" {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
		priceMap := map[string]float32{}
 | 
						||
		err = json.Unmarshal([]byte(region.Prices), &priceMap)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
 | 
						||
		trafficBytes, err := SharedServerDailyStatDAO.SumUserMonthly(tx, userId, int64(region.Id), month)
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		if trafficBytes == 0 {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
 | 
						||
		itemId := SharedNodePriceItemDAO.SearchItemsWithBytes(priceItems, trafficBytes)
 | 
						||
		if itemId == 0 {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
 | 
						||
		price, ok := priceMap[numberutils.FormatInt64(itemId)]
 | 
						||
		if !ok {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
 | 
						||
		// 计算钱
 | 
						||
		// 这里采用1000进制
 | 
						||
		cost += (float32(trafficBytes*8) / 1_000_000_000) * price
 | 
						||
	}
 | 
						||
 | 
						||
	if cost == 0 {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	// 创建账单
 | 
						||
	_, err = this.CreateBill(tx, userId, BillTypeTraffic, "按流量计费", cost, month)
 | 
						||
	return err
 | 
						||
}
 | 
						||
 | 
						||
// 获取账单类型名称
 | 
						||
func (this *UserBillDAO) BillTypeName(billType BillType) string {
 | 
						||
	switch billType {
 | 
						||
	case BillTypeTraffic:
 | 
						||
		return "流量"
 | 
						||
	}
 | 
						||
	return ""
 | 
						||
}
 |