mirror of
				https://github.com/TeaOSLab/EdgeAPI.git
				synced 2025-11-04 16:00:24 +08:00 
			
		
		
		
	优化代码/删除不需要的代码
This commit is contained in:
		@@ -1,134 +0,0 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	NodePriceItemStateEnabled  = 1 // 已启用
 | 
			
		||||
	NodePriceItemStateDisabled = 0 // 已禁用
 | 
			
		||||
 | 
			
		||||
	NodePriceTypeTraffic = "traffic" // 价格类型之流量
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type NodePriceItemDAO dbs.DAO
 | 
			
		||||
 | 
			
		||||
func NewNodePriceItemDAO() *NodePriceItemDAO {
 | 
			
		||||
	return dbs.NewDAO(&NodePriceItemDAO{
 | 
			
		||||
		DAOObject: dbs.DAOObject{
 | 
			
		||||
			DB:     Tea.Env,
 | 
			
		||||
			Table:  "edgeNodePriceItems",
 | 
			
		||||
			Model:  new(NodePriceItem),
 | 
			
		||||
			PkName: "id",
 | 
			
		||||
		},
 | 
			
		||||
	}).(*NodePriceItemDAO)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var SharedNodePriceItemDAO *NodePriceItemDAO
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	dbs.OnReady(func() {
 | 
			
		||||
		SharedNodePriceItemDAO = NewNodePriceItemDAO()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnableNodePriceItem 启用条目
 | 
			
		||||
func (this *NodePriceItemDAO) EnableNodePriceItem(tx *dbs.Tx, id int64) error {
 | 
			
		||||
	_, err := this.Query(tx).
 | 
			
		||||
		Pk(id).
 | 
			
		||||
		Set("state", NodePriceItemStateEnabled).
 | 
			
		||||
		Update()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisableNodePriceItem 禁用条目
 | 
			
		||||
func (this *NodePriceItemDAO) DisableNodePriceItem(tx *dbs.Tx, id int64) error {
 | 
			
		||||
	_, err := this.Query(tx).
 | 
			
		||||
		Pk(id).
 | 
			
		||||
		Set("state", NodePriceItemStateDisabled).
 | 
			
		||||
		Update()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindEnabledNodePriceItem 查找启用中的条目
 | 
			
		||||
func (this *NodePriceItemDAO) FindEnabledNodePriceItem(tx *dbs.Tx, id int64) (*NodePriceItem, error) {
 | 
			
		||||
	result, err := this.Query(tx).
 | 
			
		||||
		Pk(id).
 | 
			
		||||
		Attr("state", NodePriceItemStateEnabled).
 | 
			
		||||
		Find()
 | 
			
		||||
	if result == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return result.(*NodePriceItem), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindNodePriceItemName 根据主键查找名称
 | 
			
		||||
func (this *NodePriceItemDAO) FindNodePriceItemName(tx *dbs.Tx, id int64) (string, error) {
 | 
			
		||||
	return this.Query(tx).
 | 
			
		||||
		Pk(id).
 | 
			
		||||
		Result("name").
 | 
			
		||||
		FindStringCol("")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateItem 创建价格
 | 
			
		||||
func (this *NodePriceItemDAO) CreateItem(tx *dbs.Tx, name string, itemType string, bitsFrom, bitsTo int64) (int64, error) {
 | 
			
		||||
	var op = NewNodePriceItemOperator()
 | 
			
		||||
	op.Name = name
 | 
			
		||||
	op.Type = itemType
 | 
			
		||||
	op.BitsFrom = bitsFrom
 | 
			
		||||
	op.BitsTo = bitsTo
 | 
			
		||||
	op.IsOn = true
 | 
			
		||||
	op.State = NodePriceItemStateEnabled
 | 
			
		||||
	return this.SaveInt64(tx, op)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateItem 修改价格
 | 
			
		||||
func (this *NodePriceItemDAO) UpdateItem(tx *dbs.Tx, itemId int64, name string, bitsFrom, bitsTo int64) error {
 | 
			
		||||
	if itemId <= 0 {
 | 
			
		||||
		return errors.New("invalid itemId")
 | 
			
		||||
	}
 | 
			
		||||
	var op = NewNodePriceItemOperator()
 | 
			
		||||
	op.Id = itemId
 | 
			
		||||
	op.Name = name
 | 
			
		||||
	op.BitsFrom = bitsFrom
 | 
			
		||||
	op.BitsTo = bitsTo
 | 
			
		||||
	return this.Save(tx, op)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAllEnabledRegionPrices 列出某个区域的所有价格
 | 
			
		||||
func (this *NodePriceItemDAO) FindAllEnabledRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
 | 
			
		||||
	_, err = this.Query(tx).
 | 
			
		||||
		Attr("type", priceType).
 | 
			
		||||
		State(NodePriceItemStateEnabled).
 | 
			
		||||
		Asc("bitsFrom").
 | 
			
		||||
		Slice(&result).
 | 
			
		||||
		FindAll()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAllEnabledAndOnRegionPrices 列出某个区域的所有启用的价格
 | 
			
		||||
func (this *NodePriceItemDAO) FindAllEnabledAndOnRegionPrices(tx *dbs.Tx, priceType string) (result []*NodePriceItem, err error) {
 | 
			
		||||
	_, err = this.Query(tx).
 | 
			
		||||
		Attr("type", priceType).
 | 
			
		||||
		State(NodePriceItemStateEnabled).
 | 
			
		||||
		Attr("isOn", true).
 | 
			
		||||
		Asc("bitsFrom").
 | 
			
		||||
		Slice(&result).
 | 
			
		||||
		FindAll()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SearchItemsWithBytes 根据字节查找付费项目
 | 
			
		||||
func (this *NodePriceItemDAO) SearchItemsWithBytes(items []*NodePriceItem, bytes int64) int64 {
 | 
			
		||||
	bytes *= 8
 | 
			
		||||
 | 
			
		||||
	for _, item := range items {
 | 
			
		||||
		if bytes >= int64(item.BitsFrom) && (bytes < int64(item.BitsTo) || item.BitsTo == 0) {
 | 
			
		||||
			return int64(item.Id)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
)
 | 
			
		||||
@@ -11,20 +11,20 @@ type NodeRegion struct {
 | 
			
		||||
	Description string   `field:"description"` // 描述
 | 
			
		||||
	Order       uint32   `field:"order"`       // 排序
 | 
			
		||||
	CreatedAt   uint64   `field:"createdAt"`   // 创建时间
 | 
			
		||||
	Prices      dbs.JSON `field:"prices"`      // 价格
 | 
			
		||||
	Prices      dbs.JSON `field:"prices"`      // 流量价格
 | 
			
		||||
	State       uint8    `field:"state"`       // 状态
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NodeRegionOperator struct {
 | 
			
		||||
	Id          interface{} // ID
 | 
			
		||||
	AdminId     interface{} // 管理员ID
 | 
			
		||||
	IsOn        interface{} // 是否启用
 | 
			
		||||
	Name        interface{} // 名称
 | 
			
		||||
	Description interface{} // 描述
 | 
			
		||||
	Order       interface{} // 排序
 | 
			
		||||
	CreatedAt   interface{} // 创建时间
 | 
			
		||||
	Prices      interface{} // 价格
 | 
			
		||||
	State       interface{} // 状态
 | 
			
		||||
	Id          any // ID
 | 
			
		||||
	AdminId     any // 管理员ID
 | 
			
		||||
	IsOn        any // 是否启用
 | 
			
		||||
	Name        any // 名称
 | 
			
		||||
	Description any // 描述
 | 
			
		||||
	Order       any // 排序
 | 
			
		||||
	CreatedAt   any // 创建时间
 | 
			
		||||
	Prices      any // 流量价格
 | 
			
		||||
	State       any // 状态
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewNodeRegionOperator() *NodeRegionOperator {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
//go:build !plus
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
@@ -79,129 +80,6 @@ func (this *PlanDAO) FindPlanName(tx *dbs.Tx, id int64) (string, error) {
 | 
			
		||||
		FindStringCol("")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreatePlan 创建套餐
 | 
			
		||||
func (this *PlanDAO) CreatePlan(tx *dbs.Tx,
 | 
			
		||||
	name string,
 | 
			
		||||
	clusterId int64,
 | 
			
		||||
	trafficLimitJSON []byte,
 | 
			
		||||
	featuresJSON []byte,
 | 
			
		||||
	priceType serverconfigs.PlanPriceType,
 | 
			
		||||
	trafficPriceJSON []byte,
 | 
			
		||||
	bandwidthPriceJSON []byte,
 | 
			
		||||
	monthlyPrice float32,
 | 
			
		||||
	seasonallyPrice float32,
 | 
			
		||||
	yearlyPrice float32) (int64, error) {
 | 
			
		||||
	var op = NewPlanOperator()
 | 
			
		||||
	op.Name = name
 | 
			
		||||
	op.ClusterId = clusterId
 | 
			
		||||
	if len(trafficLimitJSON) > 0 {
 | 
			
		||||
		op.TrafficLimit = trafficLimitJSON
 | 
			
		||||
	}
 | 
			
		||||
	if len(featuresJSON) > 0 {
 | 
			
		||||
		op.Features = featuresJSON
 | 
			
		||||
	}
 | 
			
		||||
	op.PriceType = priceType
 | 
			
		||||
	if len(trafficPriceJSON) > 0 {
 | 
			
		||||
		op.TrafficPrice = trafficPriceJSON
 | 
			
		||||
	}
 | 
			
		||||
	if len(bandwidthPriceJSON) > 0 {
 | 
			
		||||
		op.BandwidthPrice = bandwidthPriceJSON
 | 
			
		||||
	}
 | 
			
		||||
	if monthlyPrice >= 0 {
 | 
			
		||||
		op.MonthlyPrice = monthlyPrice
 | 
			
		||||
	}
 | 
			
		||||
	if seasonallyPrice >= 0 {
 | 
			
		||||
		op.SeasonallyPrice = seasonallyPrice
 | 
			
		||||
	}
 | 
			
		||||
	if yearlyPrice >= 0 {
 | 
			
		||||
		op.YearlyPrice = yearlyPrice
 | 
			
		||||
	}
 | 
			
		||||
	op.IsOn = true
 | 
			
		||||
	op.State = PlanStateEnabled
 | 
			
		||||
	return this.SaveInt64(tx, op)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdatePlan 修改套餐
 | 
			
		||||
func (this *PlanDAO) UpdatePlan(tx *dbs.Tx,
 | 
			
		||||
	planId int64,
 | 
			
		||||
	name string,
 | 
			
		||||
	isOn bool,
 | 
			
		||||
	clusterId int64,
 | 
			
		||||
	trafficLimitJSON []byte,
 | 
			
		||||
	featuresJSON []byte,
 | 
			
		||||
	priceType serverconfigs.PlanPriceType,
 | 
			
		||||
	trafficPriceJSON []byte,
 | 
			
		||||
	bandwidthPriceJSON []byte,
 | 
			
		||||
	monthlyPrice float32,
 | 
			
		||||
	seasonallyPrice float32,
 | 
			
		||||
	yearlyPrice float32) error {
 | 
			
		||||
	if planId <= 0 {
 | 
			
		||||
		return errors.New("invalid planId")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 检查集群有无变化
 | 
			
		||||
	oldClusterId, err := this.Query(tx).
 | 
			
		||||
		Pk(planId).
 | 
			
		||||
		Result("clusterId").
 | 
			
		||||
		FindInt64Col(0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var op = NewPlanOperator()
 | 
			
		||||
	op.Id = planId
 | 
			
		||||
	op.Name = name
 | 
			
		||||
	op.IsOn = isOn
 | 
			
		||||
	op.ClusterId = clusterId
 | 
			
		||||
	if len(trafficLimitJSON) > 0 {
 | 
			
		||||
		op.TrafficLimit = trafficLimitJSON
 | 
			
		||||
	}
 | 
			
		||||
	if len(featuresJSON) > 0 {
 | 
			
		||||
		op.Features = featuresJSON
 | 
			
		||||
	}
 | 
			
		||||
	op.PriceType = priceType
 | 
			
		||||
	if len(trafficPriceJSON) > 0 {
 | 
			
		||||
		op.TrafficPrice = trafficPriceJSON
 | 
			
		||||
	}
 | 
			
		||||
	if len(bandwidthPriceJSON) > 0 {
 | 
			
		||||
		op.BandwidthPrice = bandwidthPriceJSON
 | 
			
		||||
	}
 | 
			
		||||
	if monthlyPrice >= 0 {
 | 
			
		||||
		op.MonthlyPrice = monthlyPrice
 | 
			
		||||
	} else {
 | 
			
		||||
		op.MonthlyPrice = 0
 | 
			
		||||
	}
 | 
			
		||||
	if seasonallyPrice >= 0 {
 | 
			
		||||
		op.SeasonallyPrice = seasonallyPrice
 | 
			
		||||
	} else {
 | 
			
		||||
		op.SeasonallyPrice = 0
 | 
			
		||||
	}
 | 
			
		||||
	if yearlyPrice >= 0 {
 | 
			
		||||
		op.YearlyPrice = yearlyPrice
 | 
			
		||||
	} else {
 | 
			
		||||
		op.YearlyPrice = 0
 | 
			
		||||
	}
 | 
			
		||||
	err = this.Save(tx, op)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if oldClusterId != clusterId {
 | 
			
		||||
		// 修改服务所属集群
 | 
			
		||||
		err = SharedServerDAO.UpdateServersClusterIdWithPlanId(tx, planId, clusterId)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = SharedNodeClusterDAO.NotifyUpdate(tx, oldClusterId)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return this.NotifyUpdate(tx, planId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountAllEnabledPlans 计算套餐的数量
 | 
			
		||||
func (this *PlanDAO) CountAllEnabledPlans(tx *dbs.Tx) (int64, error) {
 | 
			
		||||
	return this.Query(tx).
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
)
 | 
			
		||||
@@ -1,38 +1 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DecodeTrafficPrice 流量价格配置
 | 
			
		||||
func (this *Plan) DecodeTrafficPrice() *serverconfigs.PlanTrafficPriceConfig {
 | 
			
		||||
	var config = &serverconfigs.PlanTrafficPriceConfig{}
 | 
			
		||||
 | 
			
		||||
	if len(this.TrafficPrice) == 0 {
 | 
			
		||||
		return config
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := json.Unmarshal(this.TrafficPrice, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// 忽略错误
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeBandwidthPrice 带宽价格配置
 | 
			
		||||
func (this *Plan) DecodeBandwidthPrice() *serverconfigs.PlanBandwidthPriceConfig {
 | 
			
		||||
	var config = &serverconfigs.PlanBandwidthPriceConfig{}
 | 
			
		||||
 | 
			
		||||
	if len(this.BandwidthPrice) == 0 {
 | 
			
		||||
		return config
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := json.Unmarshal(this.BandwidthPrice, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// 忽略错误
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return config
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/goman"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
@@ -14,7 +15,6 @@ import (
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"math"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -218,11 +218,10 @@ func (this *ServerBandwidthStatDAO) FindBandwidthStatsBetweenDays(tx *dbs.Tx, se
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var dayReg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	if !dayReg.MatchString(dayFrom) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayFrom) {
 | 
			
		||||
		return nil, errors.New("invalid dayFrom '" + dayFrom + "'")
 | 
			
		||||
	}
 | 
			
		||||
	if !dayReg.MatchString(dayTo) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayTo) {
 | 
			
		||||
		return nil, errors.New("invalid dayTo '" + dayTo + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,18 +5,20 @@ type ServerBandwidthStat struct {
 | 
			
		||||
	Id       uint64 `field:"id"`       // ID
 | 
			
		||||
	UserId   uint64 `field:"userId"`   // 用户ID
 | 
			
		||||
	ServerId uint64 `field:"serverId"` // 服务ID
 | 
			
		||||
	RegionId uint32 `field:"regionId"` // 区域ID
 | 
			
		||||
	Day      string `field:"day"`      // 日期YYYYMMDD
 | 
			
		||||
	TimeAt   string `field:"timeAt"`   // 时间点HHMM
 | 
			
		||||
	Bytes    uint64 `field:"bytes"`    // 带宽字节
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ServerBandwidthStatOperator struct {
 | 
			
		||||
	Id       interface{} // ID
 | 
			
		||||
	UserId   interface{} // 用户ID
 | 
			
		||||
	ServerId interface{} // 服务ID
 | 
			
		||||
	Day      interface{} // 日期YYYYMMDD
 | 
			
		||||
	TimeAt   interface{} // 时间点HHMM
 | 
			
		||||
	Bytes    interface{} // 带宽字节
 | 
			
		||||
	Id       any // ID
 | 
			
		||||
	UserId   any // 用户ID
 | 
			
		||||
	ServerId any // 服务ID
 | 
			
		||||
	RegionId any // 区域ID
 | 
			
		||||
	Day      any // 日期YYYYMMDD
 | 
			
		||||
	TimeAt   any // 时间点HHMM
 | 
			
		||||
	Bytes    any // 带宽字节
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewServerBandwidthStatOperator() *ServerBandwidthStatOperator {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/goman"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
@@ -195,7 +196,7 @@ func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, reg
 | 
			
		||||
// SumUserDaily 获取某天流量总和
 | 
			
		||||
// day 格式为YYYYMMDD
 | 
			
		||||
func (this *ServerDailyStatDAO) SumUserDaily(tx *dbs.Tx, userId int64, regionId int64, day string) (int64, error) {
 | 
			
		||||
	query := this.Query(tx)
 | 
			
		||||
	var query = this.Query(tx)
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		query.Attr("regionId", regionId)
 | 
			
		||||
	}
 | 
			
		||||
@@ -205,6 +206,27 @@ func (this *ServerDailyStatDAO) SumUserDaily(tx *dbs.Tx, userId int64, regionId
 | 
			
		||||
		SumInt64("bytes", 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SumUserTrafficBytesBetweenDays 获取用户某个日期段内的流量总和
 | 
			
		||||
func (this *ServerDailyStatDAO) SumUserTrafficBytesBetweenDays(tx *dbs.Tx, userId int64, regionId int64, dayFrom string, dayTo string) (int64, error) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayFrom) {
 | 
			
		||||
		return 0, errors.New("invalid 'dayFrom':" + dayFrom)
 | 
			
		||||
	}
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayTo) {
 | 
			
		||||
		return 0, errors.New("invalid 'dayTo':" + dayTo)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var query = this.Query(tx)
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		query.Attr("regionId", regionId)
 | 
			
		||||
	} else if regionId < 0 { // 表示没有分配区域的流量
 | 
			
		||||
		query.Attr("regionId", 0)
 | 
			
		||||
	}
 | 
			
		||||
	return query.
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Between("day", dayFrom, dayTo).
 | 
			
		||||
		SumInt64("bytes", 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SumUserMonthly 获取某月流量总和
 | 
			
		||||
// month 格式为YYYYMM
 | 
			
		||||
func (this *ServerDailyStatDAO) SumUserMonthly(tx *dbs.Tx, userId int64, month string) (int64, error) {
 | 
			
		||||
@@ -299,18 +321,17 @@ func (this *ServerDailyStatDAO) SumHourlyStat(tx *dbs.Tx, serverId int64, hour s
 | 
			
		||||
// SumDailyStat 获取某天内的流量
 | 
			
		||||
// dayFrom 格式为YYYYMMDD
 | 
			
		||||
// dayTo 格式为YYYYMMDD
 | 
			
		||||
func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string) (stat *pb.ServerDailyStat, err error) {
 | 
			
		||||
func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, userId int64, serverId int64, regionId int64, dayFrom string, dayTo string) (stat *pb.ServerDailyStat, err error) {
 | 
			
		||||
	stat = &pb.ServerDailyStat{}
 | 
			
		||||
 | 
			
		||||
	if userId <= 0 && serverId <= 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	if !reg.MatchString(dayFrom) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayFrom) {
 | 
			
		||||
		return nil, errors.New("invalid dayFrom '" + dayFrom + "'")
 | 
			
		||||
	}
 | 
			
		||||
	if !reg.MatchString(dayTo) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayTo) {
 | 
			
		||||
		return nil, errors.New("invalid dayTo '" + dayTo + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -329,6 +350,10 @@ func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, userId int64, serverId
 | 
			
		||||
		query.Attr("serverId", serverId)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		query.Attr("regionId", regionId)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dayFrom == dayTo {
 | 
			
		||||
		query.Attr("day", dayFrom)
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -360,7 +385,7 @@ func (this *ServerDailyStatDAO) SumDailyStat(tx *dbs.Tx, userId int64, serverId
 | 
			
		||||
func (this *ServerDailyStatDAO) SumDailyStatBeforeMinute(tx *dbs.Tx, serverId int64, day string, minute string) (stat *pb.ServerDailyStat, err error) {
 | 
			
		||||
	stat = &pb.ServerDailyStat{}
 | 
			
		||||
 | 
			
		||||
	if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(day) {
 | 
			
		||||
		return nil, errors.New("invalid day '" + day + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -392,7 +417,7 @@ func (this *ServerDailyStatDAO) SumDailyStatBeforeMinute(tx *dbs.Tx, serverId in
 | 
			
		||||
func (this *ServerDailyStatDAO) SumMonthlyStat(tx *dbs.Tx, serverId int64, month string) (stat *pb.ServerDailyStat, err error) {
 | 
			
		||||
	stat = &pb.ServerDailyStat{}
 | 
			
		||||
 | 
			
		||||
	if !regexp.MustCompile(`^\d{6}$`).MatchString(month) {
 | 
			
		||||
	if !regexputils.YYYYMM.MatchString(month) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -421,7 +446,7 @@ func (this *ServerDailyStatDAO) SumMonthlyStat(tx *dbs.Tx, serverId int64, month
 | 
			
		||||
// SumMonthlyBytes 获取某月内的流量
 | 
			
		||||
// month 格式为YYYYMM
 | 
			
		||||
func (this *ServerDailyStatDAO) SumMonthlyBytes(tx *dbs.Tx, serverId int64, month string) (result int64, err error) {
 | 
			
		||||
	if !regexp.MustCompile(`^\d{6}$`).MatchString(month) {
 | 
			
		||||
	if !regexputils.YYYYMM.MatchString(month) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -467,15 +492,18 @@ func (this *ServerDailyStatDAO) FindDailyStats(tx *dbs.Tx, serverId int64, dayFr
 | 
			
		||||
 | 
			
		||||
// FindStatsWithDay 按天查找5分钟级统计
 | 
			
		||||
// day YYYYMMDD
 | 
			
		||||
// timeFrom HHII00
 | 
			
		||||
// timeTo HHII59
 | 
			
		||||
func (this *ServerDailyStatDAO) FindStatsWithDay(tx *dbs.Tx, serverId int64, day string, timeFrom string, timeTo string) (result []*ServerDailyStat, err error) {
 | 
			
		||||
	if !regexp.MustCompile(`^\d{8}$`).MatchString(day) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(day) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var query = this.Query(tx).
 | 
			
		||||
		Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes", "day", "timeFrom", "MIN(timeTo) AS timeTo").
 | 
			
		||||
		Attr("serverId", serverId).
 | 
			
		||||
		Attr("day", day).
 | 
			
		||||
		DescPk()
 | 
			
		||||
		Group("day").Group("timeFrom", dbs.QueryOrderDesc)
 | 
			
		||||
 | 
			
		||||
	if len(timeFrom) > 0 {
 | 
			
		||||
		query.Gte("timeFrom", timeFrom)
 | 
			
		||||
@@ -487,13 +515,17 @@ func (this *ServerDailyStatDAO) FindStatsWithDay(tx *dbs.Tx, serverId int64, day
 | 
			
		||||
	_, err = query.
 | 
			
		||||
		Slice(&result).
 | 
			
		||||
		FindAll()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindStatsBetweenDays 查找日期段内的5分钟统计
 | 
			
		||||
func (this *ServerDailyStatDAO) FindStatsBetweenDays(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string) (result []*ServerDailyStat, err error) {
 | 
			
		||||
	var dayReg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	if !dayReg.MatchString(dayFrom) || !dayReg.MatchString(dayTo) {
 | 
			
		||||
func (this *ServerDailyStatDAO) FindStatsBetweenDays(tx *dbs.Tx, userId int64, serverId int64, regionId int64, dayFrom string, dayTo string) (result []*ServerDailyStat, err error) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayFrom) || !regexputils.YYYYMMDD.MatchString(dayTo) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -514,7 +546,13 @@ func (this *ServerDailyStatDAO) FindStatsBetweenDays(tx *dbs.Tx, userId int64, s
 | 
			
		||||
		query.Attr("serverId", serverId)
 | 
			
		||||
	} else {
 | 
			
		||||
		query.Result("SUM(bytes) AS bytes", "SUM(cachedBytes) AS cachedBytes", "SUM(countRequests) AS countRequests", "SUM(countCachedRequests) AS countCachedRequests", "SUM(countAttackRequests) AS countAttackRequests", "SUM(attackBytes) AS attackBytes", "MIN(day) AS day", "MIN(timeFrom) AS timeFrom", "MIN(timeTo) AS timeTo")
 | 
			
		||||
		query.Group("CONCAT(day,timeFrom)")
 | 
			
		||||
		query.Group("day").Group("timeFrom")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		query.Attr("regionId", regionId)
 | 
			
		||||
	} else if regionId < 0 { // 表示未分配区域的流量
 | 
			
		||||
		query.Attr("regionId", 0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 不需要排序
 | 
			
		||||
@@ -528,7 +566,18 @@ func (this *ServerDailyStatDAO) FindStatsBetweenDays(tx *dbs.Tx, userId int64, s
 | 
			
		||||
 | 
			
		||||
	var m = map[string]*ServerDailyStat{} // day @ timeFrom => *ServerDailyStat
 | 
			
		||||
	for _, stat := range result {
 | 
			
		||||
		m[stat.Day+"@"+stat.TimeFrom] = stat
 | 
			
		||||
		var key = stat.Day + "@" + stat.TimeFrom
 | 
			
		||||
		mStat, ok := m[key]
 | 
			
		||||
		if ok {
 | 
			
		||||
			mStat.Bytes += stat.Bytes
 | 
			
		||||
			mStat.CachedBytes += stat.CachedBytes
 | 
			
		||||
			mStat.AttackBytes += stat.AttackBytes
 | 
			
		||||
			mStat.CountRequests += stat.CountRequests
 | 
			
		||||
			mStat.CountAttackRequests += stat.CountAttackRequests
 | 
			
		||||
			mStat.CountCachedRequests += stat.CountCachedRequests
 | 
			
		||||
		} else {
 | 
			
		||||
			m[key] = stat
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 填充空白
 | 
			
		||||
@@ -649,6 +698,23 @@ func (this *ServerDailyStatDAO) FindDistinctServerIds(tx *dbs.Tx, dayFrom string
 | 
			
		||||
	return serverIds, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindDistinctUserIds 查找所有有流量的用户ID
 | 
			
		||||
func (this *ServerDailyStatDAO) FindDistinctUserIds(tx *dbs.Tx, dayFrom string, dayTo string) (userIds []int64, err error) {
 | 
			
		||||
	dayFrom = strings.ReplaceAll(dayFrom, "-", "")
 | 
			
		||||
	dayTo = strings.ReplaceAll(dayTo, "-", "")
 | 
			
		||||
	ones, _, err := this.Query(tx).
 | 
			
		||||
		Result("DISTINCT(userId) AS userId").
 | 
			
		||||
		Between("day", dayFrom, dayTo).
 | 
			
		||||
		FindOnes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, one := range ones {
 | 
			
		||||
		userIds = append(userIds, one.GetInt64("userId"))
 | 
			
		||||
	}
 | 
			
		||||
	return userIds, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateStatFee 设置费用
 | 
			
		||||
func (this *ServerDailyStatDAO) UpdateStatFee(tx *dbs.Tx, statId int64, fee float32) error {
 | 
			
		||||
	return this.Query(tx).
 | 
			
		||||
 
 | 
			
		||||
@@ -87,7 +87,7 @@ func TestServerDailyStatDAO_FindDistinctPlanServerIdsBetweenDay(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestServerDailyStatDAO_FindStatsBetweenDays(t *testing.T) {
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	stats, err := NewServerDailyStatDAO().FindStatsBetweenDays(tx, 1, 0, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -1)), timeutil.Format("Ymd"))
 | 
			
		||||
	stats, err := NewServerDailyStatDAO().FindStatsBetweenDays(tx, 1, 0, 0, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -1)), timeutil.Format("Ymd"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -95,3 +95,15 @@ func TestServerDailyStatDAO_FindStatsBetweenDays(t *testing.T) {
 | 
			
		||||
		t.Log(stat.Day, stat.TimeFrom, stat.TimeTo, stat.Bytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestServerDailyStatDAO_FindStatsWithDay(t *testing.T) {
 | 
			
		||||
	var dao = NewServerDailyStatDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	stats, err := dao.FindStatsWithDay(tx, 23, timeutil.Format("Ymd"), "000000", "235900")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, stat := range stats {
 | 
			
		||||
		t.Log(stat.TimeFrom, stat.TimeTo, stat.Bytes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2617,11 +2617,29 @@ func (this *ServerDAO) UpdateServerBandwidth(tx *dbs.Tx, serverId int64, fullTim
 | 
			
		||||
	if bandwidthBytes < 0 {
 | 
			
		||||
		bandwidthBytes = 0
 | 
			
		||||
	}
 | 
			
		||||
	return this.Query(tx).
 | 
			
		||||
 | 
			
		||||
	bandwidthTime, err := this.Query(tx).
 | 
			
		||||
		Pk(serverId).
 | 
			
		||||
		Set("bandwidthTime", fullTime).
 | 
			
		||||
		Set("bandwidthBytes", bandwidthBytes).
 | 
			
		||||
		UpdateQuickly()
 | 
			
		||||
		Result("bandwidthTime").
 | 
			
		||||
		FindStringCol("")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if bandwidthTime != fullTime {
 | 
			
		||||
		return this.Query(tx).
 | 
			
		||||
			Pk(serverId).
 | 
			
		||||
			Set("bandwidthTime", fullTime).
 | 
			
		||||
			Set("bandwidthBytes", bandwidthBytes).
 | 
			
		||||
			UpdateQuickly()
 | 
			
		||||
	} else {
 | 
			
		||||
		return this.Query(tx).
 | 
			
		||||
			Pk(serverId).
 | 
			
		||||
			Set("bandwidthTime", fullTime).
 | 
			
		||||
			Set("bandwidthBytes", dbs.SQL("bandwidthBytes+:bytes")).
 | 
			
		||||
			Param("bytes", bandwidthBytes).
 | 
			
		||||
			UpdateQuickly()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotifyUpdate 同步服务所在的集群
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import (
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -322,6 +323,15 @@ func TestServerDAO_FindBool(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestServerDAO_UpdateServerBandwidth(t *testing.T) {
 | 
			
		||||
	var dao = models.NewServerDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	err := dao.UpdateServerBandwidth(tx, 1, timeutil.FormatTime("YmdHi", time.Now().Unix()/300*300), 1024)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkServerDAO_CountAllEnabledServers(b *testing.B) {
 | 
			
		||||
	models.SharedServerDAO = models.NewServerDAO()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -142,42 +142,6 @@ func (this *SysSettingDAO) ReadGlobalConfig(tx *dbs.Tx) (*serverconfigs.GlobalCo
 | 
			
		||||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadUserServerConfig 读取用户服务配置
 | 
			
		||||
func (this *SysSettingDAO) ReadUserServerConfig(tx *dbs.Tx) (*userconfigs.UserServerConfig, error) {
 | 
			
		||||
	valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserServerConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(valueJSON) == 0 {
 | 
			
		||||
		return userconfigs.DefaultUserServerConfig(), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var config = userconfigs.DefaultUserServerConfig()
 | 
			
		||||
	err = json.Unmarshal(valueJSON, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadUserFinanceConfig 读取用户服务配置
 | 
			
		||||
func (this *SysSettingDAO) ReadUserFinanceConfig(tx *dbs.Tx) (*userconfigs.UserFinanceConfig, error) {
 | 
			
		||||
	valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserFinanceConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(valueJSON) == 0 {
 | 
			
		||||
		return userconfigs.DefaultUserFinanceConfig(), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var config = userconfigs.DefaultUserFinanceConfig()
 | 
			
		||||
	err = json.Unmarshal(valueJSON, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadAdminUIConfig 读取管理员界面配置
 | 
			
		||||
func (this *SysSettingDAO) ReadAdminUIConfig(tx *dbs.Tx, cacheMap *utils.CacheMap) (*systemconfigs.AdminUIConfig, error) {
 | 
			
		||||
	var cacheKey = this.Table + ":ReadAdminUIConfig"
 | 
			
		||||
@@ -228,3 +192,21 @@ func (this *SysSettingDAO) NotifyUpdate(tx *dbs.Tx, code string) error {
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadUserServerConfig 读取用户服务配置
 | 
			
		||||
func (this *SysSettingDAO) ReadUserServerConfig(tx *dbs.Tx) (*userconfigs.UserServerConfig, error) {
 | 
			
		||||
	valueJSON, err := this.ReadSetting(tx, systemconfigs.SettingCodeUserServerConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(valueJSON) == 0 {
 | 
			
		||||
		return userconfigs.DefaultUserServerConfig(), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var config = userconfigs.DefaultUserServerConfig()
 | 
			
		||||
	err = json.Unmarshal(valueJSON, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,10 +11,10 @@ type SysSetting struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SysSettingOperator struct {
 | 
			
		||||
	Id     interface{} // ID
 | 
			
		||||
	UserId interface{} // 用户ID
 | 
			
		||||
	Code   interface{} // 代号
 | 
			
		||||
	Value  interface{} // 配置值
 | 
			
		||||
	Id     any // ID
 | 
			
		||||
	UserId any // 用户ID
 | 
			
		||||
	Code   any // 代号
 | 
			
		||||
	Value  any // 配置值
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSysSettingOperator() *SysSettingOperator {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/goman"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
@@ -14,7 +15,7 @@ import (
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"math"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -60,7 +61,7 @@ func init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateUserBandwidth 写入数据
 | 
			
		||||
func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64, day string, timeAt string, bytes int64) error {
 | 
			
		||||
func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64, regionId int64, day string, timeAt string, bytes int64) error {
 | 
			
		||||
	if userId <= 0 {
 | 
			
		||||
		// 如果用户ID不大于0,则说明服务不属于任何用户,此时不需要处理
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -70,10 +71,11 @@ func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64,
 | 
			
		||||
		Table(this.partialTable(userId)).
 | 
			
		||||
		Param("bytes", bytes).
 | 
			
		||||
		InsertOrUpdateQuickly(maps.Map{
 | 
			
		||||
			"userId": userId,
 | 
			
		||||
			"day":    day,
 | 
			
		||||
			"timeAt": timeAt,
 | 
			
		||||
			"bytes":  bytes,
 | 
			
		||||
			"userId":   userId,
 | 
			
		||||
			"regionId": regionId,
 | 
			
		||||
			"day":      day,
 | 
			
		||||
			"timeAt":   timeAt,
 | 
			
		||||
			"bytes":    bytes,
 | 
			
		||||
		}, maps.Map{
 | 
			
		||||
			"bytes": dbs.SQL("bytes+:bytes"),
 | 
			
		||||
		})
 | 
			
		||||
@@ -84,9 +86,12 @@ func (this *UserBandwidthStatDAO) UpdateUserBandwidth(tx *dbs.Tx, userId int64,
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInMonth(tx *dbs.Tx, userId int64, month string) (*UserBandwidthStat, error) {
 | 
			
		||||
	one, err := this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId)).
 | 
			
		||||
		Result("MIN(id) AS id", "MIN(userId) AS userId", "day", "timeAt", "SUM(bytes) AS bytes").
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Between("day", month+"01", month+"31").
 | 
			
		||||
		Desc("bytes").
 | 
			
		||||
		Group("day").
 | 
			
		||||
		Group("timeAt").
 | 
			
		||||
		Find()
 | 
			
		||||
	if err != nil || one == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -95,7 +100,8 @@ func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInMonth(tx *dbs.Tx, userI
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindPercentileBetweenDays 获取日期段内内百分位
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId int64, dayFrom string, dayTo string, percentile int32) (result *UserBandwidthStat, err error) {
 | 
			
		||||
// regionId 如果为 -1 表示没有区域的带宽;如果为 0 表示所有区域的带宽
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId int64, regionId int64, dayFrom string, dayTo string, percentile int32) (result *UserBandwidthStat, err error) {
 | 
			
		||||
	if dayFrom > dayTo {
 | 
			
		||||
		dayFrom, dayTo = dayTo, dayFrom
 | 
			
		||||
	}
 | 
			
		||||
@@ -106,11 +112,20 @@ func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId i
 | 
			
		||||
 | 
			
		||||
	// 如果是100%以上,则快速返回
 | 
			
		||||
	if percentile >= 100 {
 | 
			
		||||
		one, err := this.Query(tx).
 | 
			
		||||
			Table(this.partialTable(userId)).
 | 
			
		||||
		var query = this.Query(tx).
 | 
			
		||||
			Table(this.partialTable(userId))
 | 
			
		||||
		if regionId > 0 {
 | 
			
		||||
			query.Attr("regionId", regionId)
 | 
			
		||||
		} else if regionId < 0 {
 | 
			
		||||
			query.Attr("regionId", 0)
 | 
			
		||||
		}
 | 
			
		||||
		one, err := query.
 | 
			
		||||
			Result("MIN(id) AS id", "MIN(userId) AS userId", "day", "timeAt", "SUM(bytes) AS bytes").
 | 
			
		||||
			Attr("userId", userId).
 | 
			
		||||
			Between("day", dayFrom, dayTo).
 | 
			
		||||
			Desc("bytes").
 | 
			
		||||
			Group("day").
 | 
			
		||||
			Group("timeAt").
 | 
			
		||||
			Find()
 | 
			
		||||
		if err != nil || one == nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
@@ -120,11 +135,17 @@ func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId i
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 总数量
 | 
			
		||||
	total, err := this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId)).
 | 
			
		||||
	var totalQuery = this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId))
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		totalQuery.Attr("regionId", regionId)
 | 
			
		||||
	} else if regionId < 0 {
 | 
			
		||||
		totalQuery.Attr("regionId", 0)
 | 
			
		||||
	}
 | 
			
		||||
	total, err := totalQuery.
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Between("day", dayFrom, dayTo).
 | 
			
		||||
		Count()
 | 
			
		||||
		CountAttr("DISTINCT day, timeAt")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -139,11 +160,20 @@ func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId i
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 查询 nth 位置
 | 
			
		||||
	one, err := this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId)).
 | 
			
		||||
	var query = this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId))
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		query.Attr("regionId", regionId)
 | 
			
		||||
	} else if regionId < 0 {
 | 
			
		||||
		query.Attr("regionId", 0)
 | 
			
		||||
	}
 | 
			
		||||
	one, err := query.
 | 
			
		||||
		Result("MIN(id) AS id", "MIN(userId) AS userId", "day", "timeAt", "SUM(bytes) AS bytes").
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Between("day", dayFrom, dayTo).
 | 
			
		||||
		Desc("bytes").
 | 
			
		||||
		Group("day").
 | 
			
		||||
		Group("timeAt").
 | 
			
		||||
		Offset(offset).
 | 
			
		||||
		Find()
 | 
			
		||||
	if err != nil || one == nil {
 | 
			
		||||
@@ -158,9 +188,11 @@ func (this *UserBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, userId i
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInDay(tx *dbs.Tx, userId int64, day string) (*UserBandwidthStat, error) {
 | 
			
		||||
	one, err := this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId)).
 | 
			
		||||
		Result("MIN(id) AS id", "MIN(userId) AS userId", "MIN(day) AS day", "timeAt", "SUM(bytes) AS bytes").
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Attr("day", day).
 | 
			
		||||
		Desc("bytes").
 | 
			
		||||
		Group("timeAt").
 | 
			
		||||
		Find()
 | 
			
		||||
	if err != nil || one == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -171,16 +203,15 @@ func (this *UserBandwidthStatDAO) FindUserPeekBandwidthInDay(tx *dbs.Tx, userId
 | 
			
		||||
// FindUserBandwidthStatsBetweenDays 查找日期段内的带宽峰值
 | 
			
		||||
// dayFrom YYYYMMDD
 | 
			
		||||
// dayTo YYYYMMDD
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindUserBandwidthStatsBetweenDays(tx *dbs.Tx, userId int64, dayFrom string, dayTo string) (result []*pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat, err error) {
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindUserBandwidthStatsBetweenDays(tx *dbs.Tx, userId int64, regionId int64, dayFrom string, dayTo string) (result []*pb.FindDailyServerBandwidthStatsBetweenDaysResponse_Stat, err error) {
 | 
			
		||||
	if userId <= 0 {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var dayReg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	if !dayReg.MatchString(dayFrom) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayFrom) {
 | 
			
		||||
		return nil, errors.New("invalid dayFrom '" + dayFrom + "'")
 | 
			
		||||
	}
 | 
			
		||||
	if !dayReg.MatchString(dayTo) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(dayTo) {
 | 
			
		||||
		return nil, errors.New("invalid dayTo '" + dayTo + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -188,11 +219,17 @@ func (this *UserBandwidthStatDAO) FindUserBandwidthStatsBetweenDays(tx *dbs.Tx,
 | 
			
		||||
		dayFrom, dayTo = dayTo, dayFrom
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ones, _, err := this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId)).
 | 
			
		||||
		Result("bytes", "day", "timeAt").
 | 
			
		||||
	var query = this.Query(tx).
 | 
			
		||||
		Table(this.partialTable(userId))
 | 
			
		||||
	if regionId > 0 {
 | 
			
		||||
		query.Attr("regionId", regionId)
 | 
			
		||||
	}
 | 
			
		||||
	ones, _, err := query.
 | 
			
		||||
		Result("SUM(bytes) AS bytes", "day", "timeAt").
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Between("day", dayFrom, dayTo).
 | 
			
		||||
		Group("day").
 | 
			
		||||
		Group("timeAt").
 | 
			
		||||
		FindOnes()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -248,6 +285,33 @@ func (this *UserBandwidthStatDAO) FindUserBandwidthStatsBetweenDays(tx *dbs.Tx,
 | 
			
		||||
	return result, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindDistinctUserIds 获取所有有带宽的用户ID
 | 
			
		||||
// dayFrom YYYYMMDD
 | 
			
		||||
// dayTo YYYYMMDD
 | 
			
		||||
func (this *UserBandwidthStatDAO) FindDistinctUserIds(tx *dbs.Tx, dayFrom string, dayTo string) (userIds []int64, err error) {
 | 
			
		||||
	dayFrom = strings.ReplaceAll(dayFrom, "-", "")
 | 
			
		||||
	dayTo = strings.ReplaceAll(dayTo, "-", "")
 | 
			
		||||
 | 
			
		||||
	err = this.runBatch(func(table string, locker *sync.Mutex) error {
 | 
			
		||||
		ones, err := this.Query(tx).
 | 
			
		||||
			Table(table).
 | 
			
		||||
			Between("day", dayFrom, dayTo).
 | 
			
		||||
			Result("DISTINCT userId").
 | 
			
		||||
			FindAll()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, one := range ones {
 | 
			
		||||
			locker.Lock()
 | 
			
		||||
			userIds = append(userIds, int64(one.(*UserBandwidthStat).UserId))
 | 
			
		||||
			locker.Unlock()
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clean 清理过期数据
 | 
			
		||||
func (this *UserBandwidthStatDAO) Clean(tx *dbs.Tx) error {
 | 
			
		||||
	var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -100)) // 保留大约3个月的数据
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,38 @@ import (
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestUserBandwidthStatDAO_FindUserPeekBandwidthInMonth(t *testing.T) {
 | 
			
		||||
	var dao = models.NewUserBandwidthStatDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	stat, err := dao.FindUserPeekBandwidthInMonth(tx, 1, timeutil.Format("Ym"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logs.PrintAsJSON(stat, t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUserBandwidthStatDAO_FindUserPeekBandwidthInDay(t *testing.T) {
 | 
			
		||||
	var dao = models.NewUserBandwidthStatDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	stat, err := dao.FindUserPeekBandwidthInDay(tx, 1, timeutil.Format("Ymd"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logs.PrintAsJSON(stat, t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUserBandwidthStatDAO_UpdateServerBandwidth(t *testing.T) {
 | 
			
		||||
	var dao = models.NewUserBandwidthStatDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	err := dao.UpdateUserBandwidth(tx, 1, timeutil.Format("Ymd"), timeutil.Format("Hi"), 1024)
 | 
			
		||||
	err := dao.UpdateUserBandwidth(tx, 1, 0, timeutil.Format("Ymd"), timeutil.Format("Hi"), 1024)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -33,7 +56,7 @@ func TestUserBandwidthStatDAO_Clean(t *testing.T) {
 | 
			
		||||
func TestUserBandwidthStatDAO_FindBandwidthStatsBetweenDays(t *testing.T) {
 | 
			
		||||
	var dao = models.NewUserBandwidthStatDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	stats, err := dao.FindUserBandwidthStatsBetweenDays(tx, 1, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -2)), timeutil.Format("Ymd"))
 | 
			
		||||
	stats, err := dao.FindUserBandwidthStatsBetweenDays(tx, 1, 0, timeutil.Format("Ymd", time.Now().AddDate(0, 0, -2)), timeutil.Format("Ymd"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -46,3 +69,13 @@ func TestUserBandwidthStatDAO_FindBandwidthStatsBetweenDays(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("data count:", dataCount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUserBandwidthStatDAO_FindPercentileBetweenDays(t *testing.T) {
 | 
			
		||||
	var dao = models.NewUserBandwidthStatDAO()
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
	stat, err := dao.FindPercentileBetweenDays(tx, 1, 0, timeutil.Format("Ymd"), timeutil.Format("Ymd"), 95)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	logs.PrintAsJSON(stat, t)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,19 +2,21 @@ package models
 | 
			
		||||
 | 
			
		||||
// UserBandwidthStat 用户月带宽峰值
 | 
			
		||||
type UserBandwidthStat struct {
 | 
			
		||||
	Id     uint64 `field:"id"`     // ID
 | 
			
		||||
	UserId uint64 `field:"userId"` // 用户ID
 | 
			
		||||
	Day    string `field:"day"`    // 日期YYYYMMDD
 | 
			
		||||
	TimeAt string `field:"timeAt"` // 时间点HHII
 | 
			
		||||
	Bytes  uint64 `field:"bytes"`  // 带宽
 | 
			
		||||
	Id       uint64 `field:"id"`       // ID
 | 
			
		||||
	UserId   uint64 `field:"userId"`   // 用户ID
 | 
			
		||||
	Day      string `field:"day"`      // 日期YYYYMMDD
 | 
			
		||||
	TimeAt   string `field:"timeAt"`   // 时间点HHII
 | 
			
		||||
	Bytes    uint64 `field:"bytes"`    // 带宽
 | 
			
		||||
	RegionId uint32 `field:"regionId"` // 区域ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserBandwidthStatOperator struct {
 | 
			
		||||
	Id     interface{} // ID
 | 
			
		||||
	UserId interface{} // 用户ID
 | 
			
		||||
	Day    interface{} // 日期YYYYMMDD
 | 
			
		||||
	TimeAt interface{} // 时间点HHII
 | 
			
		||||
	Bytes  interface{} // 带宽
 | 
			
		||||
	Id       any // ID
 | 
			
		||||
	UserId   any // 用户ID
 | 
			
		||||
	Day      any // 日期YYYYMMDD
 | 
			
		||||
	TimeAt   any // 时间点HHII
 | 
			
		||||
	Bytes    any // 带宽
 | 
			
		||||
	RegionId any // 区域ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserBandwidthStatOperator() *UserBandwidthStatOperator {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +1,9 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/goman"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	"github.com/iwind/TeaGo/lists"
 | 
			
		||||
	"github.com/iwind/TeaGo/maps"
 | 
			
		||||
	"github.com/iwind/TeaGo/rands"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	dbs.OnReadyDone(func() {
 | 
			
		||||
		var generatedMonths = []string{}
 | 
			
		||||
 | 
			
		||||
		goman.New(func() {
 | 
			
		||||
			// 自动生成账单任务
 | 
			
		||||
			var ticker = time.NewTicker(1 * time.Hour)
 | 
			
		||||
			for range ticker.C {
 | 
			
		||||
				// 是否已经生成了,如果已经生成了就跳过
 | 
			
		||||
				var lastMonth = timeutil.Format("Ym", time.Now().AddDate(0, -1, 0))
 | 
			
		||||
				if lists.ContainsString(generatedMonths, lastMonth) {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				err := SharedUserBillDAO.GenerateBills(nil, lastMonth)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					remotelogs.Error("UserBillDAO", "generate bills failed: "+err.Error())
 | 
			
		||||
				} else {
 | 
			
		||||
					generatedMonths = append(generatedMonths, lastMonth)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type BillType = string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	BillTypeTraffic BillType = "traffic" // 按流量计费
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UserBillDAO dbs.DAO
 | 
			
		||||
@@ -67,415 +26,3 @@ func init() {
 | 
			
		||||
		SharedUserBillDAO = NewUserBillDAO()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindUserBill 查找单个账单
 | 
			
		||||
func (this *UserBillDAO) FindUserBill(tx *dbs.Tx, billId int64) (*UserBill, error) {
 | 
			
		||||
	one, err := this.Query(tx).
 | 
			
		||||
		Pk(billId).
 | 
			
		||||
		Find()
 | 
			
		||||
	if err != nil || one == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return one.(*UserBill), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CountAllUserBills 计算账单数量
 | 
			
		||||
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()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListUserBills 列出单页账单
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindUnpaidBills 查找未支付订单
 | 
			
		||||
func (this *UserBillDAO) FindUnpaidBills(tx *dbs.Tx, size int64) (result []*UserBill, err error) {
 | 
			
		||||
	if size <= 0 {
 | 
			
		||||
		size = 10000
 | 
			
		||||
	}
 | 
			
		||||
	_, err = this.Query(tx).
 | 
			
		||||
		Attr("isPaid", false).
 | 
			
		||||
		Lt("month", timeutil.Format("Ym")). //当月的不能支付,因为当月还没过完
 | 
			
		||||
		Limit(size).
 | 
			
		||||
		DescPk().
 | 
			
		||||
		Slice(&result).
 | 
			
		||||
		FindAll()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateBill 创建账单
 | 
			
		||||
func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType, description string, amount float64, month string, canPay bool) error {
 | 
			
		||||
	code, err := this.GenerateBillCode(tx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return this.Query(tx).
 | 
			
		||||
		InsertOrUpdateQuickly(maps.Map{
 | 
			
		||||
			"userId":      userId,
 | 
			
		||||
			"type":        billType,
 | 
			
		||||
			"description": description,
 | 
			
		||||
			"amount":      amount,
 | 
			
		||||
			"month":       month,
 | 
			
		||||
			"code":        code,
 | 
			
		||||
			"isPaid":      amount == 0,
 | 
			
		||||
			"canPay":      canPay,
 | 
			
		||||
		}, maps.Map{
 | 
			
		||||
			"amount": amount,
 | 
			
		||||
			"canPay": canPay,
 | 
			
		||||
		})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExistBill 检查是否有当月账单
 | 
			
		||||
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()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenerateBills 生成账单
 | 
			
		||||
// month 格式YYYYMM
 | 
			
		||||
func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
 | 
			
		||||
	// 区域价格
 | 
			
		||||
	regions, err := SharedNodeRegionDAO.FindAllEnabledRegionPrices(tx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var priceItems []*NodePriceItem
 | 
			
		||||
	if len(regions) > 0 {
 | 
			
		||||
		priceItems, err = SharedNodePriceItemDAO.FindAllEnabledRegionPrices(tx, NodePriceTypeTraffic)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 默认计费方式
 | 
			
		||||
	userFinanceConfig, err := SharedSysSettingDAO.ReadUserFinanceConfig(tx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 计算服务套餐费用
 | 
			
		||||
	plans, err := SharedPlanDAO.FindAllEnabledPlans(tx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	var planMap = map[int64]*Plan{}
 | 
			
		||||
	for _, plan := range plans {
 | 
			
		||||
		planMap[int64(plan.Id)] = plan
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var dayFrom = month + "01"
 | 
			
		||||
	var dayTo = month + "32"
 | 
			
		||||
	serverIds, err := SharedServerDailyStatDAO.FindDistinctServerIds(tx, dayFrom, dayTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	var cacheMap = utils.NewCacheMap()
 | 
			
		||||
	var userIds = []int64{}
 | 
			
		||||
	for _, serverId := range serverIds {
 | 
			
		||||
		// 套餐类型
 | 
			
		||||
		userPlanId, userId, err := SharedServerDAO.FindServerLastUserPlanIdAndUserId(tx, serverId)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if userId == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		userIds = append(userIds, userId)
 | 
			
		||||
		if userPlanId == 0 {
 | 
			
		||||
			// 总流量
 | 
			
		||||
			totalTrafficBytes, err := SharedServerDailyStatDAO.SumMonthlyBytes(tx, serverId, month)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 默认计费方式
 | 
			
		||||
			if userFinanceConfig != nil && userFinanceConfig.IsOn { // 默认计费方式
 | 
			
		||||
				switch userFinanceConfig.PriceType {
 | 
			
		||||
				case serverconfigs.PlanPriceTypeTraffic:
 | 
			
		||||
					var config = userFinanceConfig.TrafficPriceConfig
 | 
			
		||||
					var fee float64 = 0
 | 
			
		||||
					if config != nil && config.Base > 0 {
 | 
			
		||||
						fee = float64(totalTrafficBytes) / 1024 / 1024 / 1024 * float64(config.Base)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// 百分位
 | 
			
		||||
					var percentile = 95
 | 
			
		||||
					percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, userId, serverId, month, userPlanId, 0, totalTrafficBytes, percentileBytes, percentile, userFinanceConfig.PriceType, fee)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				case serverconfigs.PlanPriceTypeBandwidth:
 | 
			
		||||
					// 百分位
 | 
			
		||||
					var percentile = 95
 | 
			
		||||
					var config = userFinanceConfig.BandwidthPriceConfig
 | 
			
		||||
					if config != nil {
 | 
			
		||||
						percentile = config.Percentile
 | 
			
		||||
						if percentile <= 0 {
 | 
			
		||||
							percentile = 95
 | 
			
		||||
						} else if percentile > 100 {
 | 
			
		||||
							percentile = 100
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					var mb = float32(percentileBytes) / 1024 / 1024
 | 
			
		||||
					var price float32
 | 
			
		||||
					if config != nil {
 | 
			
		||||
						price = config.LookupPrice(mb)
 | 
			
		||||
					}
 | 
			
		||||
					var fee = float64(price)
 | 
			
		||||
					err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, userId, serverId, month, userPlanId, 0, totalTrafficBytes, percentileBytes, percentile, userFinanceConfig.PriceType, fee)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else { // 区域流量计费
 | 
			
		||||
				var fee float64
 | 
			
		||||
 | 
			
		||||
				for _, region := range regions {
 | 
			
		||||
					var regionId = int64(region.Id)
 | 
			
		||||
					var pricesMap = region.DecodePriceMap()
 | 
			
		||||
					if len(pricesMap) == 0 {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					trafficBytes, err := SharedServerDailyStatDAO.SumServerMonthlyWithRegion(tx, serverId, regionId, month)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
					if trafficBytes == 0 {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					var itemId = SharedNodePriceItemDAO.SearchItemsWithBytes(priceItems, trafficBytes)
 | 
			
		||||
					if itemId == 0 {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					price, ok := pricesMap[itemId]
 | 
			
		||||
					if !ok {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					if price <= 0 {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					var regionFee = float64(trafficBytes) / 1000 / 1000 / 1000 * 8 * price
 | 
			
		||||
					fee += regionFee
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// 百分位
 | 
			
		||||
				var percentile = 95
 | 
			
		||||
				percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, userId, serverId, month, userPlanId, 0, totalTrafficBytes, percentileBytes, percentile, "", fee)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			userPlan, err := SharedUserPlanDAO.FindUserPlanWithoutState(tx, userPlanId, cacheMap)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if userPlan == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			plan, ok := planMap[int64(userPlan.PlanId)]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 总流量
 | 
			
		||||
			totalTrafficBytes, err := SharedServerDailyStatDAO.SumMonthlyBytes(tx, serverId, month)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch plan.PriceType {
 | 
			
		||||
			case serverconfigs.PlanPriceTypePeriod:
 | 
			
		||||
				// 已经在购买套餐的时候付过费,这里不再重复计费
 | 
			
		||||
				var fee float64 = 0
 | 
			
		||||
 | 
			
		||||
				// 百分位
 | 
			
		||||
				var percentile = 95
 | 
			
		||||
				percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, int64(userPlan.UserId), serverId, month, userPlanId, int64(userPlan.PlanId), totalTrafficBytes, percentileBytes, percentile, plan.PriceType, fee)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			case serverconfigs.PlanPriceTypeTraffic:
 | 
			
		||||
				var config = plan.DecodeTrafficPrice()
 | 
			
		||||
				var fee float64 = 0
 | 
			
		||||
				if config != nil && config.Base > 0 {
 | 
			
		||||
					fee = float64(totalTrafficBytes) / 1024 / 1024 / 1024 * float64(config.Base)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// 百分位
 | 
			
		||||
				var percentile = 95
 | 
			
		||||
				percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, int64(userPlan.UserId), serverId, month, userPlanId, int64(userPlan.PlanId), totalTrafficBytes, percentileBytes, percentile, plan.PriceType, fee)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			case serverconfigs.PlanPriceTypeBandwidth:
 | 
			
		||||
				// 百分位
 | 
			
		||||
				var percentile = 95
 | 
			
		||||
				var config = plan.DecodeBandwidthPrice()
 | 
			
		||||
				if config != nil {
 | 
			
		||||
					percentile = config.Percentile
 | 
			
		||||
					if percentile <= 0 {
 | 
			
		||||
						percentile = 95
 | 
			
		||||
					} else if percentile > 100 {
 | 
			
		||||
						percentile = 100
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				percentileBytes, err := SharedServerBandwidthStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				var mb = float32(percentileBytes) / 1024 / 1024
 | 
			
		||||
				var price float32
 | 
			
		||||
				if config != nil {
 | 
			
		||||
					price = config.LookupPrice(mb)
 | 
			
		||||
				}
 | 
			
		||||
				var fee = float64(price)
 | 
			
		||||
				err = SharedServerBillDAO.CreateOrUpdateServerBill(tx, int64(userPlan.UserId), serverId, month, userPlanId, int64(userPlan.PlanId), totalTrafficBytes, percentileBytes, percentile, plan.PriceType, fee)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 计算用户费用
 | 
			
		||||
	for _, userId := range userIds {
 | 
			
		||||
		if userId == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		amount, err := SharedServerBillDAO.SumUserMonthlyAmount(tx, userId, month)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		err = SharedUserBillDAO.CreateBill(tx, userId, BillTypeTraffic, "流量带宽费用", amount, month, month < timeutil.Format("Ym"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateUserBillIsPaid 设置账单已支付
 | 
			
		||||
func (this *UserBillDAO) UpdateUserBillIsPaid(tx *dbs.Tx, billId int64, isPaid bool) error {
 | 
			
		||||
	return this.Query(tx).
 | 
			
		||||
		Pk(billId).
 | 
			
		||||
		Set("isPaid", isPaid).
 | 
			
		||||
		UpdateQuickly()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BillTypeName 获取账单类型名称
 | 
			
		||||
func (this *UserBillDAO) BillTypeName(billType BillType) string {
 | 
			
		||||
	switch billType {
 | 
			
		||||
	case BillTypeTraffic:
 | 
			
		||||
		return "流量带宽"
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenerateBillCode 生成账单编号
 | 
			
		||||
func (this *UserBillDAO) GenerateBillCode(tx *dbs.Tx) (string, error) {
 | 
			
		||||
	var code = timeutil.Format("YmdHis") + types.String(rands.Int(100000, 999999))
 | 
			
		||||
	exists, err := this.Query(tx).
 | 
			
		||||
		Attr("code", code).
 | 
			
		||||
		Exist()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return code, nil
 | 
			
		||||
	}
 | 
			
		||||
	return this.GenerateBillCode(tx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckUserBill 检查用户账单
 | 
			
		||||
func (this *UserBillDAO) CheckUserBill(tx *dbs.Tx, userId int64, billId int64) error {
 | 
			
		||||
	if userId <= 0 || billId <= 0 {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
	}
 | 
			
		||||
	exists, err := this.Query(tx).
 | 
			
		||||
		Pk(billId).
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Exist()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !exists {
 | 
			
		||||
		return ErrNotFound
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SumUnpaidUserBill 计算某个用户未支付总额
 | 
			
		||||
func (this *UserBillDAO) SumUnpaidUserBill(tx *dbs.Tx, userId int64) (float32, error) {
 | 
			
		||||
	sum, err := this.Query(tx).
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Attr("isPaid", 0).
 | 
			
		||||
		Sum("amount", 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return float32(sum), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +0,0 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestUserBillDAO_GenerateBills(t *testing.T) {
 | 
			
		||||
	dbs.NotifyReady()
 | 
			
		||||
 | 
			
		||||
	var tx *dbs.Tx
 | 
			
		||||
 | 
			
		||||
	err := SharedUserBillDAO.GenerateBills(tx, timeutil.Format("Ym"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
@@ -15,22 +15,26 @@ type UserBill struct {
 | 
			
		||||
	PaidAt      uint64  `field:"paidAt"`      // 支付时间
 | 
			
		||||
	Code        string  `field:"code"`        // 账单编号
 | 
			
		||||
	CreatedAt   uint64  `field:"createdAt"`   // 创建时间
 | 
			
		||||
	PricePeriod string  `field:"pricePeriod"` // 计费周期
 | 
			
		||||
	State       uint8   `field:"state"`       // 状态
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserBillOperator struct {
 | 
			
		||||
	Id          interface{} // ID
 | 
			
		||||
	UserId      interface{} // 用户ID
 | 
			
		||||
	Type        interface{} // 消费类型
 | 
			
		||||
	Description interface{} // 描述
 | 
			
		||||
	Amount      interface{} // 消费数额
 | 
			
		||||
	DayFrom     interface{} // YYYYMMDD
 | 
			
		||||
	DayTo       interface{} // YYYYMMDD
 | 
			
		||||
	Month       interface{} // 帐期YYYYMM
 | 
			
		||||
	CanPay      interface{} // 是否可以支付
 | 
			
		||||
	IsPaid      interface{} // 是否已支付
 | 
			
		||||
	PaidAt      interface{} // 支付时间
 | 
			
		||||
	Code        interface{} // 账单编号
 | 
			
		||||
	CreatedAt   interface{} // 创建时间
 | 
			
		||||
	Id          any // ID
 | 
			
		||||
	UserId      any // 用户ID
 | 
			
		||||
	Type        any // 消费类型
 | 
			
		||||
	Description any // 描述
 | 
			
		||||
	Amount      any // 消费数额
 | 
			
		||||
	DayFrom     any // YYYYMMDD
 | 
			
		||||
	DayTo       any // YYYYMMDD
 | 
			
		||||
	Month       any // 帐期YYYYMM
 | 
			
		||||
	CanPay      any // 是否可以支付
 | 
			
		||||
	IsPaid      any // 是否已支付
 | 
			
		||||
	PaidAt      any // 支付时间
 | 
			
		||||
	Code        any // 账单编号
 | 
			
		||||
	CreatedAt   any // 创建时间
 | 
			
		||||
	PricePeriod any // 计费周期
 | 
			
		||||
	State       any // 状态
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserBillOperator() *UserBillOperator {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@ type User struct {
 | 
			
		||||
	IsVerified      bool     `field:"isVerified"`      // 是否验证通过
 | 
			
		||||
	RequirePlans    uint8    `field:"requirePlans"`    // 是否需要购买套餐
 | 
			
		||||
	Modules         dbs.JSON `field:"modules"`         // 用户模块
 | 
			
		||||
	PriceType       string   `field:"priceType"`       // 计费类型:traffic|bandwidth
 | 
			
		||||
	PricePeriod     string   `field:"pricePeriod"`     // 结算周期
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserOperator struct {
 | 
			
		||||
@@ -55,6 +57,8 @@ type UserOperator struct {
 | 
			
		||||
	IsVerified      any // 是否验证通过
 | 
			
		||||
	RequirePlans    any // 是否需要购买套餐
 | 
			
		||||
	Modules         any // 用户模块
 | 
			
		||||
	PriceType       any // 计费类型:traffic|bandwidth
 | 
			
		||||
	PricePeriod     any // 结算周期
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserOperator() *UserOperator {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//go:build !plus
 | 
			
		||||
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -7,8 +9,6 @@ import (
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -112,59 +112,11 @@ func (this *UserPlanDAO) FindUserPlanWithoutState(tx *dbs.Tx, userPlanId int64,
 | 
			
		||||
 | 
			
		||||
// CountAllEnabledUserPlans 计算套餐数量
 | 
			
		||||
func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32) (int64, error) {
 | 
			
		||||
	var query = this.Query(tx).
 | 
			
		||||
		State(UserPlanStateEnabled).
 | 
			
		||||
		Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
 | 
			
		||||
 | 
			
		||||
	if userId > 0 {
 | 
			
		||||
		query.Attr("userId", userId)
 | 
			
		||||
	} else {
 | 
			
		||||
		query.Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var today = timeutil.Format("Y-m-d")
 | 
			
		||||
	if isAvailable {
 | 
			
		||||
		query.Gte("dayTo", today)
 | 
			
		||||
	}
 | 
			
		||||
	if isExpired {
 | 
			
		||||
		query.Lt("dayTo", today)
 | 
			
		||||
	}
 | 
			
		||||
	if expiringDays > 0 {
 | 
			
		||||
		var expiringDay = timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, int(expiringDays)))
 | 
			
		||||
		query.Gte("dayTo", today)
 | 
			
		||||
		query.Lte("dayTo", expiringDay)
 | 
			
		||||
	}
 | 
			
		||||
	return query.Count()
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ListEnabledUserPlans 列出单页套餐
 | 
			
		||||
func (this *UserPlanDAO) ListEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32, offset int64, size int64) (result []*UserPlan, err error) {
 | 
			
		||||
	var query = this.Query(tx).
 | 
			
		||||
		State(UserPlanStateEnabled).
 | 
			
		||||
		Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
 | 
			
		||||
	if userId > 0 {
 | 
			
		||||
		query.Attr("userId", userId)
 | 
			
		||||
	} else {
 | 
			
		||||
		query.Where("userId IN (SELECT id FROM " + SharedUserDAO.Table + " WHERE state=1)")
 | 
			
		||||
	}
 | 
			
		||||
	var today = timeutil.Format("Y-m-d")
 | 
			
		||||
	if isAvailable {
 | 
			
		||||
		query.Gte("dayTo", today)
 | 
			
		||||
	}
 | 
			
		||||
	if isExpired {
 | 
			
		||||
		query.Lt("dayTo", today)
 | 
			
		||||
	}
 | 
			
		||||
	if expiringDays > 0 {
 | 
			
		||||
		var expiringDay = timeutil.Format("Y-m-d", time.Now().AddDate(0, 0, int(expiringDays)))
 | 
			
		||||
		query.Gte("dayTo", today)
 | 
			
		||||
		query.Lte("dayTo", expiringDay)
 | 
			
		||||
	}
 | 
			
		||||
	_, err = query.
 | 
			
		||||
		DescPk().
 | 
			
		||||
		Offset(offset).
 | 
			
		||||
		Limit(size).
 | 
			
		||||
		Slice(&result).
 | 
			
		||||
		FindAll()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -215,20 +167,6 @@ func (this *UserPlanDAO) UpdateUserPlanDayTo(tx *dbs.Tx, userPlanId int64, dayTo
 | 
			
		||||
 | 
			
		||||
// FindAllEnabledPlansForServer 列出服务可用的套餐
 | 
			
		||||
func (this *UserPlanDAO) FindAllEnabledPlansForServer(tx *dbs.Tx, userId int64, serverId int64) (result []*UserPlan, err error) {
 | 
			
		||||
	var query = this.Query(tx).
 | 
			
		||||
		State(UserPlanStateEnabled).
 | 
			
		||||
		Attr("userId", userId).
 | 
			
		||||
		Where("planId IN (SELECT id FROM " + SharedPlanDAO.Table + " WHERE state=1)")
 | 
			
		||||
	if serverId > 0 {
 | 
			
		||||
		query.Where("id NOT IN (SELECT userPlanId FROM " + SharedServerDAO.Table + " WHERE state=1 AND id!=:serverId)")
 | 
			
		||||
		query.Param("serverId", serverId)
 | 
			
		||||
	} else {
 | 
			
		||||
		query.Where("id NOT IN (SELECT userPlanId FROM " + SharedServerDAO.Table + " WHERE state=1)")
 | 
			
		||||
	}
 | 
			
		||||
	_, err = query.
 | 
			
		||||
		DescPk().
 | 
			
		||||
		Slice(&result).
 | 
			
		||||
		FindAll()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +0,0 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	_ "github.com/go-sql-driver/mysql"
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										30
									
								
								internal/db/models/user_traffic_bill_model.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/db/models/user_traffic_bill_model.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
// UserTrafficBill 用户流量/带宽账单
 | 
			
		||||
type UserTrafficBill struct {
 | 
			
		||||
	Id           uint64  `field:"id"`           // ID
 | 
			
		||||
	BillId       uint64  `field:"billId"`       // 主账单ID
 | 
			
		||||
	RegionId     uint32  `field:"regionId"`     // 区域ID
 | 
			
		||||
	Amount       float64 `field:"amount"`       // 金额
 | 
			
		||||
	BandwidthMB  float64 `field:"bandwidthMB"`  // 带宽MB
 | 
			
		||||
	TrafficGB    float64 `field:"trafficGB"`    // 流量GB
 | 
			
		||||
	PricePerUnit float64 `field:"pricePerUnit"` // 单位价格
 | 
			
		||||
	PriceType    string  `field:"priceType"`    // 计费方式:traffic|bandwidth
 | 
			
		||||
	State        uint8   `field:"state"`        // 状态
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserTrafficBillOperator struct {
 | 
			
		||||
	Id           any // ID
 | 
			
		||||
	BillId       any // 主账单ID
 | 
			
		||||
	RegionId     any // 区域ID
 | 
			
		||||
	Amount       any // 金额
 | 
			
		||||
	BandwidthMB  any // 带宽MB
 | 
			
		||||
	TrafficGB    any // 流量GB
 | 
			
		||||
	PricePerUnit any // 单位价格
 | 
			
		||||
	PriceType    any // 计费方式:traffic|bandwidth
 | 
			
		||||
	State        any // 状态
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserTrafficBillOperator() *UserTrafficBillOperator {
 | 
			
		||||
	return &UserTrafficBillOperator{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								internal/db/models/user_traffic_bill_model_ext.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								internal/db/models/user_traffic_bill_model_ext.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
package models
 | 
			
		||||
@@ -227,11 +227,6 @@ func (this *APINode) registerServices(server *grpc.Server) {
 | 
			
		||||
		pb.RegisterNodeRegionServiceServer(server, instance)
 | 
			
		||||
		this.rest(instance)
 | 
			
		||||
	}
 | 
			
		||||
	{
 | 
			
		||||
		var instance = this.serviceInstance(&services.NodePriceItemService{}).(*services.NodePriceItemService)
 | 
			
		||||
		pb.RegisterNodePriceItemServiceServer(server, instance)
 | 
			
		||||
		this.rest(instance)
 | 
			
		||||
	}
 | 
			
		||||
	{
 | 
			
		||||
		var instance = this.serviceInstance(&services.ServerGroupService{}).(*services.ServerGroupService)
 | 
			
		||||
		pb.RegisterServerGroupServiceServer(server, instance)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,10 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/db/models"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	"github.com/iwind/TeaGo/lists"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -168,7 +168,7 @@ func (this *HTTPAccessLogService) FindHTTPAccessLogPartitions(ctx context.Contex
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !regexp.MustCompile(`^\d{8}$`).MatchString(req.Day) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(req.Day) {
 | 
			
		||||
		return nil, errors.New("invalid 'day': " + req.Day)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,142 +0,0 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/db/models"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NodePriceItemService 节点区域价格相关服务
 | 
			
		||||
type NodePriceItemService struct {
 | 
			
		||||
	BaseService
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateNodePriceItem 创建区域价格
 | 
			
		||||
func (this *NodePriceItemService) CreateNodePriceItem(ctx context.Context, req *pb.CreateNodePriceItemRequest) (*pb.CreateNodePriceItemResponse, error) {
 | 
			
		||||
	_, err := this.ValidateAdmin(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	itemId, err := models.SharedNodePriceItemDAO.CreateItem(tx, req.Name, req.Type, req.BitsFrom, req.BitsTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &pb.CreateNodePriceItemResponse{NodePriceItemId: itemId}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateNodePriceItem 修改区域价格
 | 
			
		||||
func (this *NodePriceItemService) UpdateNodePriceItem(ctx context.Context, req *pb.UpdateNodePriceItemRequest) (*pb.RPCSuccess, error) {
 | 
			
		||||
	_, err := this.ValidateAdmin(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	err = models.SharedNodePriceItemDAO.UpdateItem(tx, req.NodePriceItemId, req.Name, req.BitsFrom, req.BitsTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return this.Success()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteNodePriceItem 删除区域价格
 | 
			
		||||
func (this *NodePriceItemService) DeleteNodePriceItem(ctx context.Context, req *pb.DeleteNodePriceItemRequest) (*pb.RPCSuccess, error) {
 | 
			
		||||
	_, err := this.ValidateAdmin(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	err = models.SharedNodePriceItemDAO.DisableNodePriceItem(tx, req.NodePriceItemId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return this.Success()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAllEnabledNodePriceItems 查找所有区域价格
 | 
			
		||||
func (this *NodePriceItemService) FindAllEnabledNodePriceItems(ctx context.Context, req *pb.FindAllEnabledNodePriceItemsRequest) (*pb.FindAllEnabledNodePriceItemsResponse, error) {
 | 
			
		||||
	_, err := this.ValidateAdmin(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	prices, err := models.SharedNodePriceItemDAO.FindAllEnabledRegionPrices(tx, req.Type)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	result := []*pb.NodePriceItem{}
 | 
			
		||||
	for _, price := range prices {
 | 
			
		||||
		result = append(result, &pb.NodePriceItem{
 | 
			
		||||
			Id:       int64(price.Id),
 | 
			
		||||
			IsOn:     price.IsOn,
 | 
			
		||||
			Name:     price.Name,
 | 
			
		||||
			Type:     price.Type,
 | 
			
		||||
			BitsFrom: int64(price.BitsFrom),
 | 
			
		||||
			BitsTo:   int64(price.BitsTo),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &pb.FindAllEnabledNodePriceItemsResponse{NodePriceItems: result}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindAllAvailableNodePriceItems 查找所有启用的区域价格
 | 
			
		||||
func (this *NodePriceItemService) FindAllAvailableNodePriceItems(ctx context.Context, req *pb.FindAllAvailableNodePriceItemsRequest) (*pb.FindAllAvailableNodePriceItemsResponse, error) {
 | 
			
		||||
	_, err := this.ValidateAdmin(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	prices, err := models.SharedNodePriceItemDAO.FindAllEnabledAndOnRegionPrices(tx, req.Type)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	result := []*pb.NodePriceItem{}
 | 
			
		||||
	for _, price := range prices {
 | 
			
		||||
		result = append(result, &pb.NodePriceItem{
 | 
			
		||||
			Id:       int64(price.Id),
 | 
			
		||||
			IsOn:     price.IsOn,
 | 
			
		||||
			Name:     price.Name,
 | 
			
		||||
			Type:     price.Type,
 | 
			
		||||
			BitsFrom: int64(price.BitsFrom),
 | 
			
		||||
			BitsTo:   int64(price.BitsTo),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &pb.FindAllAvailableNodePriceItemsResponse{NodePriceItems: result}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindEnabledNodePriceItem 查找单个区域信息
 | 
			
		||||
func (this *NodePriceItemService) FindEnabledNodePriceItem(ctx context.Context, req *pb.FindEnabledNodePriceItemRequest) (*pb.FindEnabledNodePriceItemResponse, error) {
 | 
			
		||||
	_, err := this.ValidateAdmin(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	price, err := models.SharedNodePriceItemDAO.FindEnabledNodePriceItem(tx, req.NodePriceItemId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if price == nil {
 | 
			
		||||
		return &pb.FindEnabledNodePriceItemResponse{NodePriceItem: nil}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return &pb.FindEnabledNodePriceItemResponse{NodePriceItem: &pb.NodePriceItem{
 | 
			
		||||
		Id:       int64(price.Id),
 | 
			
		||||
		IsOn:     price.IsOn,
 | 
			
		||||
		Name:     price.Name,
 | 
			
		||||
		Type:     price.Type,
 | 
			
		||||
		BitsFrom: int64(price.BitsFrom),
 | 
			
		||||
		BitsTo:   int64(price.BitsTo),
 | 
			
		||||
	}}, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -8,16 +8,16 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/db/models"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/goman"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var serverBandwidthStatsMap = map[string]*pb.ServerBandwidthStat{} // key => bandwidth
 | 
			
		||||
var serverBandwidthStatsMap = map[string]*pb.ServerBandwidthStat{} // server key => bandwidth
 | 
			
		||||
var serverBandwidthStatsLocker = &sync.Mutex{}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
@@ -74,9 +74,9 @@ func init() {
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						// 更新服务的带宽峰值
 | 
			
		||||
						// 更新用户的带宽峰值
 | 
			
		||||
						if stat.UserId > 0 {
 | 
			
		||||
							err = models.SharedUserBandwidthStatDAO.UpdateUserBandwidth(tx, stat.UserId, stat.Day, stat.TimeAt, stat.Bytes)
 | 
			
		||||
							err = models.SharedUserBandwidthStatDAO.UpdateUserBandwidth(tx, stat.UserId, stat.RegionId, stat.Day, stat.TimeAt, stat.Bytes)
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								remotelogs.Error("SharedUserBandwidthStatDAO", "dump bandwidth stats failed: "+err.Error())
 | 
			
		||||
							}
 | 
			
		||||
@@ -89,18 +89,18 @@ func init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ServerBandwidthCacheKey 组合缓存Key
 | 
			
		||||
func ServerBandwidthCacheKey(serverId int64, day string, timeAt string) string {
 | 
			
		||||
	return types.String(serverId) + "@" + day + "@" + timeAt
 | 
			
		||||
func ServerBandwidthCacheKey(serverId int64, regionId int64, day string, timeAt string) string {
 | 
			
		||||
	return types.String(serverId) + "@" + types.String(regionId) + "@" + day + "@" + timeAt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ServerBandwidthGetCacheBytes(serverId int64, day string, timeAt string) int64 {
 | 
			
		||||
	var key = ServerBandwidthCacheKey(serverId, day, timeAt)
 | 
			
		||||
func ServerBandwidthGetCacheBytes(serverId int64, timeAt string) int64 {
 | 
			
		||||
	var bytes int64 = 0
 | 
			
		||||
 | 
			
		||||
	serverBandwidthStatsLocker.Lock()
 | 
			
		||||
	stat, ok := serverBandwidthStatsMap[key]
 | 
			
		||||
	if ok {
 | 
			
		||||
		bytes = stat.Bytes
 | 
			
		||||
	for _, stat := range serverBandwidthStatsMap {
 | 
			
		||||
		if stat.ServerId == serverId && stat.TimeAt == timeAt {
 | 
			
		||||
			bytes += stat.Bytes
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	serverBandwidthStatsLocker.Unlock()
 | 
			
		||||
 | 
			
		||||
@@ -119,7 +119,7 @@ func (this *ServerBandwidthStatService) UploadServerBandwidthStats(ctx context.C
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, stat := range req.ServerBandwidthStats {
 | 
			
		||||
		var key = ServerBandwidthCacheKey(stat.ServerId, stat.Day, stat.TimeAt)
 | 
			
		||||
		var key = ServerBandwidthCacheKey(stat.ServerId, stat.RegionId, stat.Day, stat.TimeAt)
 | 
			
		||||
		serverBandwidthStatsLocker.Lock()
 | 
			
		||||
		oldStat, ok := serverBandwidthStatsMap[key]
 | 
			
		||||
		if ok {
 | 
			
		||||
@@ -127,6 +127,7 @@ func (this *ServerBandwidthStatService) UploadServerBandwidthStats(ctx context.C
 | 
			
		||||
		} else {
 | 
			
		||||
			serverBandwidthStatsMap[key] = &pb.ServerBandwidthStat{
 | 
			
		||||
				Id:       0,
 | 
			
		||||
				RegionId: stat.RegionId,
 | 
			
		||||
				UserId:   stat.UserId,
 | 
			
		||||
				ServerId: stat.ServerId,
 | 
			
		||||
				Day:      stat.Day,
 | 
			
		||||
@@ -244,11 +245,10 @@ func (this *ServerBandwidthStatService) FindDailyServerBandwidthStatsBetweenDays
 | 
			
		||||
	req.DayFrom = strings.ReplaceAll(req.DayFrom, "-", "")
 | 
			
		||||
	req.DayTo = strings.ReplaceAll(req.DayTo, "-", "")
 | 
			
		||||
 | 
			
		||||
	var dayReg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	if !dayReg.MatchString(req.DayFrom) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(req.DayFrom) {
 | 
			
		||||
		return nil, errors.New("invalid dayFrom '" + req.DayFrom + "'")
 | 
			
		||||
	}
 | 
			
		||||
	if !dayReg.MatchString(req.DayTo) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(req.DayTo) {
 | 
			
		||||
		return nil, errors.New("invalid dayTo '" + req.DayTo + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -271,10 +271,10 @@ func (this *ServerBandwidthStatService) FindDailyServerBandwidthStatsBetweenDays
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else { // 用户统计
 | 
			
		||||
		pbStats, err = models.SharedUserBandwidthStatDAO.FindUserBandwidthStatsBetweenDays(tx, req.UserId, req.DayFrom, req.DayTo)
 | 
			
		||||
		pbStats, err = models.SharedUserBandwidthStatDAO.FindUserBandwidthStatsBetweenDays(tx, req.UserId, req.RegionId, req.DayFrom, req.DayTo)
 | 
			
		||||
 | 
			
		||||
		// nth
 | 
			
		||||
		stat, err := models.SharedUserBandwidthStatDAO.FindPercentileBetweenDays(tx, req.UserId, req.DayFrom, req.DayTo, req.Percentile)
 | 
			
		||||
		stat, err := models.SharedUserBandwidthStatDAO.FindPercentileBetweenDays(tx, req.UserId, req.RegionId, req.DayFrom, req.DayTo, req.Percentile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,11 @@ import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/db/models/stats"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
	"math"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -239,7 +239,7 @@ func (this *ServerDailyStatService) FindLatestServerDailyStats(ctx context.Conte
 | 
			
		||||
	if req.Days > 0 {
 | 
			
		||||
		for i := int32(0); i < req.Days; i++ {
 | 
			
		||||
			dayString := timeutil.Format("Ymd", time.Now().AddDate(0, 0, -int(i)))
 | 
			
		||||
			stat, err := models.SharedServerDailyStatDAO.SumDailyStat(tx, 0, req.ServerId, dayString, dayString)
 | 
			
		||||
			stat, err := models.SharedServerDailyStatDAO.SumDailyStat(tx, 0, req.ServerId, req.RegionId, dayString, dayString)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
@@ -277,17 +277,16 @@ func (this *ServerDailyStatService) FindServerDailyStatsBetweenDays(ctx context.
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var reg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	req.DayFrom = strings.ReplaceAll(req.DayFrom, "-", "")
 | 
			
		||||
	req.DayTo = strings.ReplaceAll(req.DayTo, "-", "")
 | 
			
		||||
	if !reg.MatchString(req.DayFrom) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(req.DayFrom) {
 | 
			
		||||
		return nil, errors.New("invalid dayFrom '" + req.DayFrom + "'")
 | 
			
		||||
	}
 | 
			
		||||
	if !reg.MatchString(req.DayTo) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(req.DayTo) {
 | 
			
		||||
		return nil, errors.New("invalid dayTo '" + req.DayTo + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dailyStats, err := models.SharedServerDailyStatDAO.FindStatsBetweenDays(tx, req.UserId, req.ServerId, req.DayFrom, req.DayTo)
 | 
			
		||||
	dailyStats, err := models.SharedServerDailyStatDAO.FindStatsBetweenDays(tx, req.UserId, req.ServerId, req.RegionId, req.DayFrom, req.DayTo)
 | 
			
		||||
	var pbStats = []*pb.FindServerDailyStatsBetweenDaysResponse_Stat{}
 | 
			
		||||
	for _, stat := range dailyStats {
 | 
			
		||||
		// 防止数据出错
 | 
			
		||||
@@ -378,19 +377,18 @@ func (this *ServerDailyStatService) SumServerDailyStats(ctx context.Context, req
 | 
			
		||||
	req.DayFrom = strings.ReplaceAll(req.DayFrom, "-", "")
 | 
			
		||||
	req.DayTo = strings.ReplaceAll(req.DayTo, "-", "")
 | 
			
		||||
 | 
			
		||||
	var dayReg = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	if len(req.Day) > 0 {
 | 
			
		||||
		if !dayReg.MatchString(req.Day) {
 | 
			
		||||
		if !regexputils.YYYYMMDD.MatchString(req.Day) {
 | 
			
		||||
			return nil, errors.New("invalid day '" + req.Day + "'")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		req.DayFrom = req.Day
 | 
			
		||||
		req.DayTo = req.Day
 | 
			
		||||
	} else if len(req.DayFrom) > 0 && len(req.DayTo) > 0 {
 | 
			
		||||
		if !dayReg.MatchString(req.DayFrom) {
 | 
			
		||||
		if !regexputils.YYYYMMDD.MatchString(req.DayFrom) {
 | 
			
		||||
			return nil, errors.New("invalid dayFrom '" + req.DayFrom + "'")
 | 
			
		||||
		}
 | 
			
		||||
		if !dayReg.MatchString(req.DayTo) {
 | 
			
		||||
		if !regexputils.YYYYMMDD.MatchString(req.DayTo) {
 | 
			
		||||
			return nil, errors.New("invalid dayTo '" + req.DayTo + "'")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -398,7 +396,7 @@ func (this *ServerDailyStatService) SumServerDailyStats(ctx context.Context, req
 | 
			
		||||
		req.DayTo = req.DayFrom
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stat, err := models.SharedServerDailyStatDAO.SumDailyStat(tx, req.UserId, req.ServerId, req.DayFrom, req.DayTo)
 | 
			
		||||
	stat, err := models.SharedServerDailyStatDAO.SumDailyStat(tx, req.UserId, req.ServerId, req.RegionId, req.DayFrom, req.DayTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -439,7 +437,7 @@ func (this *ServerDailyStatService) SumServerMonthlyStats(ctx context.Context, r
 | 
			
		||||
 | 
			
		||||
	// 某月统计
 | 
			
		||||
	var month = timeutil.Format("Ym")
 | 
			
		||||
	if regexp.MustCompile(`^\d{6}$`).MatchString(req.Month) {
 | 
			
		||||
	if regexputils.YYYYMM.MatchString(req.Month) {
 | 
			
		||||
		month = req.Month
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -487,7 +487,7 @@ func (this *ServerStatBoardService) ComposeServerStatBoard(ctx context.Context,
 | 
			
		||||
			if ok {
 | 
			
		||||
				pbBandwidthStats = append(pbBandwidthStats, stat)
 | 
			
		||||
			} else {
 | 
			
		||||
				var bytes = ServerBandwidthGetCacheBytes(req.ServerId, minute.Day, minute.Minute) // 从当前缓存中读取
 | 
			
		||||
				var bytes = ServerBandwidthGetCacheBytes(req.ServerId, minute.Minute) // 从当前缓存中读取
 | 
			
		||||
				pbBandwidthStats = append(pbBandwidthStats, &pb.ServerBandwidthStat{
 | 
			
		||||
					Id:       0,
 | 
			
		||||
					ServerId: req.ServerId,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,7 @@ package services
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/db/models"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/lists"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SysSettingService struct {
 | 
			
		||||
@@ -35,28 +32,12 @@ func (this *SysSettingService) UpdateSysSetting(ctx context.Context, req *pb.Upd
 | 
			
		||||
// ReadSysSetting 读取配置
 | 
			
		||||
func (this *SysSettingService) ReadSysSetting(ctx context.Context, req *pb.ReadSysSettingRequest) (*pb.ReadSysSettingResponse, error) {
 | 
			
		||||
	// 校验请求
 | 
			
		||||
	_, userId, err := this.ValidateAdminAndUser(ctx, false)
 | 
			
		||||
	_, _, err := this.ValidateAdminAndUser(ctx, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tx = this.NullTx()
 | 
			
		||||
 | 
			
		||||
	// 检查权限
 | 
			
		||||
	if userId > 0 {
 | 
			
		||||
		// TODO 限制用户只能为专有用户,比如1_000_000_000
 | 
			
		||||
		if !lists.ContainsString([]string{
 | 
			
		||||
			systemconfigs.SettingCodeUserRegisterConfig,
 | 
			
		||||
			systemconfigs.SettingCodeUserServerConfig,
 | 
			
		||||
			systemconfigs.SettingCodeUserUIConfig,
 | 
			
		||||
			systemconfigs.SettingCodeNSUserConfig,
 | 
			
		||||
			systemconfigs.SettingCodeUserOrderConfig,
 | 
			
		||||
			systemconfigs.SettingCodeServerGlobalConfig,
 | 
			
		||||
		}, req.Code) {
 | 
			
		||||
			return nil, errors.New("can not read setting code '" + req.Code + "'")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	valueJSON, err := models.SharedSysSettingDAO.ReadSetting(tx, req.Code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -85,6 +85,9 @@ var upgradeFuncs = []*upgradeVersion{
 | 
			
		||||
	{
 | 
			
		||||
		"v0.5.3", upgradeV0_5_3,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		"v0.5.6", upgradeV0_5_6,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpgradeSQLData 升级SQL数据
 | 
			
		||||
@@ -666,68 +669,6 @@ func upgradeV0_4_8(db *dbs.DB) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// v0.4.9
 | 
			
		||||
func upgradeV0_4_9(db *dbs.DB) error {
 | 
			
		||||
	// 升级用户UI配置
 | 
			
		||||
	{
 | 
			
		||||
		one, err := db.FindOne("SELECT value FROM edgeSysSettings WHERE code=?", systemconfigs.SettingCodeUserUIConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if one != nil {
 | 
			
		||||
			var valueJSON = one.GetBytes("value")
 | 
			
		||||
			if len(valueJSON) > 0 {
 | 
			
		||||
				var config = &systemconfigs.UserUIConfig{}
 | 
			
		||||
				err = json.Unmarshal(valueJSON, config)
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					config.ShowTrafficCharts = true
 | 
			
		||||
					config.ShowBandwidthCharts = true
 | 
			
		||||
					config.BandwidthUnit = systemconfigs.BandwidthUnitBit
 | 
			
		||||
					configJSON, err := json.Marshal(config)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return errors.New("encode UserUIConfig failed: " + err.Error())
 | 
			
		||||
					} else {
 | 
			
		||||
						_, err := db.Exec("UPDATE edgeSysSettings SET value=? WHERE code=?", configJSON, systemconfigs.SettingCodeUserUIConfig)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 升级管理配置
 | 
			
		||||
	{
 | 
			
		||||
		one, err := db.FindOne("SELECT value FROM edgeSysSettings WHERE code=?", systemconfigs.SettingCodeAdminSecurityConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if one != nil {
 | 
			
		||||
			var valueJSON = one.GetBytes("value")
 | 
			
		||||
			if len(valueJSON) > 0 {
 | 
			
		||||
				var config = &systemconfigs.SecurityConfig{}
 | 
			
		||||
				err = json.Unmarshal(valueJSON, config)
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					config.DenySearchEngines = true
 | 
			
		||||
					config.DenySpiders = true
 | 
			
		||||
					configJSON, err := json.Marshal(config)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return errors.New("encode SecurityConfig failed: " + err.Error())
 | 
			
		||||
					} else {
 | 
			
		||||
						_, err := db.Exec("UPDATE edgeSysSettings SET value=? WHERE code=?", configJSON, systemconfigs.SettingCodeAdminSecurityConfig)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// v0.4.11
 | 
			
		||||
func upgradeV0_4_11(db *dbs.DB) error {
 | 
			
		||||
	// 升级ns端口
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,9 @@ package setup
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/dbs"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	"regexp"
 | 
			
		||||
@@ -57,6 +59,39 @@ func upgradeV0_2_8_1(db *dbs.DB) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// v0.4.9
 | 
			
		||||
func upgradeV0_4_9(db *dbs.DB) error {
 | 
			
		||||
	// 升级管理配置
 | 
			
		||||
	{
 | 
			
		||||
		one, err := db.FindOne("SELECT value FROM edgeSysSettings WHERE code=?", systemconfigs.SettingCodeAdminSecurityConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if one != nil {
 | 
			
		||||
			var valueJSON = one.GetBytes("value")
 | 
			
		||||
			if len(valueJSON) > 0 {
 | 
			
		||||
				var config = &systemconfigs.SecurityConfig{}
 | 
			
		||||
				err = json.Unmarshal(valueJSON, config)
 | 
			
		||||
				if err == nil {
 | 
			
		||||
					config.DenySearchEngines = true
 | 
			
		||||
					config.DenySpiders = true
 | 
			
		||||
					configJSON, err := json.Marshal(config)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return errors.New("encode SecurityConfig failed: " + err.Error())
 | 
			
		||||
					} else {
 | 
			
		||||
						_, err := db.Exec("UPDATE edgeSysSettings SET value=? WHERE code=?", configJSON, systemconfigs.SettingCodeAdminSecurityConfig)
 | 
			
		||||
						if err != nil {
 | 
			
		||||
							return err
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// v0.5.3
 | 
			
		||||
func upgradeV0_5_3(db *dbs.DB) error {
 | 
			
		||||
	// 升级集群服务配置
 | 
			
		||||
@@ -103,3 +138,8 @@ func upgradeV0_5_3(db *dbs.DB) error {
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// v0.5.6
 | 
			
		||||
func upgradeV0_5_6(db *dbs.DB) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -251,4 +251,3 @@ func TestUpgradeSQLData_v0_5_3(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
	t.Log("ok")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
package numberutils
 | 
			
		||||
 | 
			
		||||
import "strconv"
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func FormatInt64(value int64) string {
 | 
			
		||||
	return strconv.FormatInt(value, 10)
 | 
			
		||||
@@ -39,3 +41,20 @@ func Min[T int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 |
 | 
			
		||||
	}
 | 
			
		||||
	return min
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FloorFloat32(f float32, decimal int) float32 {
 | 
			
		||||
	if decimal < 0 {
 | 
			
		||||
		decimal = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < decimal; i++ {
 | 
			
		||||
		f *= 10
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f = float32(int64(f))
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < decimal; i++ {
 | 
			
		||||
		f /= 10
 | 
			
		||||
	}
 | 
			
		||||
	return f
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ package numberutils_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
 | 
			
		||||
	"math"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -18,3 +19,16 @@ func TestMin(t *testing.T) {
 | 
			
		||||
	t.Log(numberutils.Min[int32](1, 2, 3))
 | 
			
		||||
	t.Log(numberutils.Min[float32](1.2, 2.3, 3.4))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMaxFloat32(t *testing.T) {
 | 
			
		||||
	t.Logf("%f", math.MaxFloat32/(1<<100))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFloorFloat32(t *testing.T) {
 | 
			
		||||
	t.Logf("%f", numberutils.FloorFloat32(123.456, -1))
 | 
			
		||||
	t.Logf("%f", numberutils.FloorFloat32(123.456, 0))
 | 
			
		||||
	t.Logf("%f, %f", numberutils.FloorFloat32(123.456, 1), 123.456*10)
 | 
			
		||||
	t.Logf("%f, %f", numberutils.FloorFloat32(123.456, 2), 123.456*10*10)
 | 
			
		||||
	t.Logf("%f, %f", numberutils.FloorFloat32(123.456, 3), 123.456*10*10*10)
 | 
			
		||||
	t.Logf("%f, %f", numberutils.FloorFloat32(123.456, 4), 123.456*10*10*10*10)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								internal/utils/regexputils/expr.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								internal/utils/regexputils/expr.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package regexputils
 | 
			
		||||
 | 
			
		||||
import "regexp"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	YYYYMMDD = regexp.MustCompile(`^\d{8}$`)
 | 
			
		||||
	YYYYMM   = regexp.MustCompile(`^\d{6}$`)
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										19
									
								
								internal/utils/regexputils/expr_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								internal/utils/regexputils/expr_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
			
		||||
 | 
			
		||||
package regexputils_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/iwind/TeaGo/assert"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestExpr(t *testing.T) {
 | 
			
		||||
	var a = assert.NewAssertion(t)
 | 
			
		||||
 | 
			
		||||
	a.IsTrue(regexputils.YYYYMMDD.MatchString("20221011"))
 | 
			
		||||
	a.IsFalse(regexputils.YYYYMMDD.MatchString("202210"))
 | 
			
		||||
 | 
			
		||||
	a.IsTrue(regexputils.YYYYMM.MatchString("202210"))
 | 
			
		||||
	a.IsFalse(regexputils.YYYYMM.MatchString("20221011"))
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ package utils
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils"
 | 
			
		||||
	"github.com/iwind/TeaGo/lists"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	timeutil "github.com/iwind/TeaGo/utils/time"
 | 
			
		||||
@@ -25,18 +26,12 @@ type timeDayMinuteRange struct {
 | 
			
		||||
 | 
			
		||||
// RangeDays 计算日期之间的所有日期,格式为YYYYMMDD
 | 
			
		||||
func RangeDays(dayFrom string, dayTo string) ([]string, error) {
 | 
			
		||||
	ok, err := regexp.MatchString(`^\d{8}$`, dayFrom)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var ok = regexputils.YYYYMMDD.MatchString(dayFrom)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("invalid 'dayFrom'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ok, err = regexp.MatchString(`^\d{8}$`, dayTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	ok = regexputils.YYYYMMDD.MatchString(dayTo)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("invalid 'dayTo'")
 | 
			
		||||
	}
 | 
			
		||||
@@ -73,18 +68,12 @@ func RangeDays(dayFrom string, dayTo string) ([]string, error) {
 | 
			
		||||
 | 
			
		||||
// RangeMonths 计算日期之间的所有月份,格式为YYYYMM
 | 
			
		||||
func RangeMonths(dayFrom string, dayTo string) ([]string, error) {
 | 
			
		||||
	ok, err := regexp.MatchString(`^\d{8}$`, dayFrom)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	var ok = regexputils.YYYYMMDD.MatchString(dayFrom)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("invalid 'dayFrom'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ok, err = regexp.MatchString(`^\d{8}$`, dayTo)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	ok = regexputils.YYYYMMDD.MatchString(dayTo)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.New("invalid 'dayTo'")
 | 
			
		||||
	}
 | 
			
		||||
@@ -276,3 +265,32 @@ func Range24HourTimes(everyMinutes int32) ([]string, error) {
 | 
			
		||||
 | 
			
		||||
	return RangeTimes("0000", "2359", everyMinutes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LastDayInMonth 某月的最后一天
 | 
			
		||||
// month: YYYYMM
 | 
			
		||||
// 返回 YYYYMMDD
 | 
			
		||||
func LastDayInMonth(month string) (string, error) {
 | 
			
		||||
	if !regexputils.YYYYMM.MatchString(month) {
 | 
			
		||||
		return "", errors.New("invalid month '" + month + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var year = types.Int(month[:4])
 | 
			
		||||
	var monthInt = types.Int(month[4:])
 | 
			
		||||
	return month + timeutil.Format("t", time.Date(year, time.Month(monthInt), 1, 0, 0, 0, 0, time.Local)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FixMonthMaxDay 修正日期最大值
 | 
			
		||||
func FixMonthMaxDay(day string) (string, error) {
 | 
			
		||||
	if !regexputils.YYYYMMDD.MatchString(day) {
 | 
			
		||||
		return "", errors.New("invalid day '" + day + "'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	maxDay, err := LastDayInMonth(day[:6])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if day > maxDay {
 | 
			
		||||
		return maxDay, nil
 | 
			
		||||
	}
 | 
			
		||||
	return day, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -97,3 +97,27 @@ func TestGroupMinuteRanges(t *testing.T) {
 | 
			
		||||
		t.Log(minutes)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLastDayInMonth(t *testing.T) {
 | 
			
		||||
	t.Log(utils.LastDayInMonth("202209"))
 | 
			
		||||
	t.Log(utils.LastDayInMonth("202210"))
 | 
			
		||||
	t.Log(utils.LastDayInMonth("202202"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFixMonthMaxDay(t *testing.T) {
 | 
			
		||||
	for _, day := range []string{
 | 
			
		||||
		"20220930",
 | 
			
		||||
		"20220929",
 | 
			
		||||
		"20220931",
 | 
			
		||||
		"20220932",
 | 
			
		||||
		"20220222",
 | 
			
		||||
		"20220228",
 | 
			
		||||
		"20220229",
 | 
			
		||||
	} {
 | 
			
		||||
		afterDay, err := utils.FixMonthMaxDay(day)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		t.Log(day, "=>", afterDay)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user