//go:build plus package models import ( "bytes" "encoding/json" "github.com/TeaOSLab/EdgeAPI/internal/errors" "github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "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/types" ) const ( PlanStateEnabled = 1 // 已启用 PlanStateDisabled = 0 // 已禁用 ) type PlanDAO dbs.DAO func NewPlanDAO() *PlanDAO { return dbs.NewDAO(&PlanDAO{ DAOObject: dbs.DAOObject{ DB: Tea.Env, Table: "edgePlans", Model: new(Plan), PkName: "id", }, }).(*PlanDAO) } var SharedPlanDAO *PlanDAO func init() { dbs.OnReady(func() { SharedPlanDAO = NewPlanDAO() }) } // EnablePlan 启用条目 func (this *PlanDAO) EnablePlan(tx *dbs.Tx, id uint32) error { _, err := this.Query(tx). Pk(id). Set("state", PlanStateEnabled). Update() return err } // DisablePlan 禁用条目 func (this *PlanDAO) DisablePlan(tx *dbs.Tx, planId int64) error { clusterId, err := this.FindPlanClusterId(tx, planId) if err != nil { return err } _, err = this.Query(tx). Pk(planId). Set("state", PlanStateDisabled). Update() if err != nil { return err } err = SharedServerDAO.ResetServersTrafficLimitStatusWithPlanId(tx, planId) if err != nil { return err } return this.NotifyUpdate(tx, planId, clusterId) } // FindEnabledPlan 查找启用中的条目 func (this *PlanDAO) FindEnabledPlan(tx *dbs.Tx, planId int64, cacheMap *utils.CacheMap) (*Plan, error) { var cacheKey = this.Table + ":FindEnabledPlan:" + types.String(planId) if cacheMap != nil { cache, _ := cacheMap.Get(cacheKey) if cache != nil { return cache.(*Plan), nil } } result, err := this.Query(tx). Pk(planId). Attr("state", PlanStateEnabled). Find() if result == nil { return nil, err } if cacheMap != nil { cacheMap.Put(cacheKey, result) } return result.(*Plan), err } // FindBasicPlan 查找套餐基本信息 func (this *PlanDAO) FindBasicPlan(tx *dbs.Tx, planId int64) (*Plan, error) { one, err := this.Query(tx). Pk(planId). State(PlanStateEnabled). Result("id", "name", "isOn"). Find() if err != nil || one == nil { return nil, err } return one.(*Plan), nil } // FindPlanName 根据主键查找名称 func (this *PlanDAO) FindPlanName(tx *dbs.Tx, id int64) (string, error) { return this.Query(tx). Pk(id). Result("name"). FindStringCol("") } // CreatePlan 创建套餐 func (this *PlanDAO) CreatePlan(tx *dbs.Tx, name string, description string, clusterId int64, trafficLimitJSON []byte, bandwidthLimitPerNode []byte, hasFullFeatures bool, featuresJSON []byte, priceType serverconfigs.PlanPriceType, trafficPriceJSON []byte, bandwidthPriceJSON []byte, monthlyPrice float32, seasonallyPrice float32, yearlyPrice float32, totalServers int32, totalServerNames int32, totalServerNamesPerServer int32, dailyRequests int64, monthlyRequests int64, dailyWebsocketConnections int64, monthlyWebsocketConnections int64, maxUploadSizeJSON []byte) (int64, error) { var op = NewPlanOperator() op.Name = name op.Description = description op.ClusterId = clusterId if len(trafficLimitJSON) > 0 { op.TrafficLimit = trafficLimitJSON } if len(bandwidthLimitPerNode) > 0 { op.BandwidthLimitPerNode = bandwidthLimitPerNode } op.HasFullFeatures = hasFullFeatures 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.TotalServers = totalServers op.TotalServerNamesPerServer = totalServerNamesPerServer op.TotalServerNames = totalServerNames op.DailyRequests = dailyRequests op.MonthlyRequests = monthlyRequests op.DailyWebsocketConnections = dailyWebsocketConnections op.MonthlyWebsocketConnections = monthlyWebsocketConnections if maxUploadSizeJSON != nil { op.MaxUploadSize = maxUploadSizeJSON } op.IsOn = true op.State = PlanStateEnabled return this.SaveInt64(tx, op) } // UpdatePlan 修改套餐 func (this *PlanDAO) UpdatePlan(tx *dbs.Tx, planId int64, name string, description string, isOn bool, clusterId int64, trafficLimitJSON []byte, bandwidthLimitPerNode []byte, hasFullFeatures bool, featuresJSON []byte, priceType serverconfigs.PlanPriceType, trafficPriceJSON []byte, bandwidthPriceJSON []byte, monthlyPrice float32, seasonallyPrice float32, yearlyPrice float32, totalServers int32, totalServerNames int32, totalServerNamesPerServer int32, dailyRequests int64, monthlyRequests int64, dailyWebsocketConnections int64, monthlyWebsocketConnections int64, maxUploadSizeJSON []byte) 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 } // 检查限制是否变化 oldPlanOne, err := this.Query(tx). Pk(planId). Find() if err != nil || oldPlanOne == nil { return err } var oldPlan = oldPlanOne.(*Plan) var op = NewPlanOperator() op.Id = planId op.Name = name op.Description = description op.IsOn = isOn op.ClusterId = clusterId if len(trafficLimitJSON) > 0 { op.TrafficLimit = trafficLimitJSON } if len(bandwidthLimitPerNode) > 0 { op.BandwidthLimitPerNode = bandwidthLimitPerNode } op.HasFullFeatures = hasFullFeatures 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 } op.TotalServers = totalServers op.TotalServerNamesPerServer = totalServerNamesPerServer op.TotalServerNames = totalServerNames op.DailyRequests = dailyRequests op.MonthlyRequests = monthlyRequests op.DailyWebsocketConnections = dailyWebsocketConnections op.MonthlyWebsocketConnections = monthlyWebsocketConnections if maxUploadSizeJSON != nil { op.MaxUploadSize = maxUploadSizeJSON } 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 = this.NotifyUpdate(tx, planId, oldClusterId) if err != nil { return err } } err = this.NotifyUpdate(tx, planId, clusterId) if err != nil { return err } // 检查流量、请求数等限制变化 var planLimitChanged = false // 检查流量限制 if !bytes.Equal(oldPlan.TrafficLimit, trafficLimitJSON) { var newTrafficLimitConfig = &serverconfigs.TrafficLimitConfig{} if IsNull(oldPlan.TrafficLimit) || IsNull(trafficLimitJSON) { planLimitChanged = true } else { var oldTrafficLimitConfig = &serverconfigs.TrafficLimitConfig{} err = json.Unmarshal(oldPlan.TrafficLimit, oldTrafficLimitConfig) if err != nil { return err } err = json.Unmarshal(trafficLimitJSON, newTrafficLimitConfig) if err != nil { return err } if !oldTrafficLimitConfig.Equals(newTrafficLimitConfig) { planLimitChanged = true } } } // 检查请求数限制 if !planLimitChanged && (int64(oldPlan.DailyRequests) != dailyRequests || int64(oldPlan.MonthlyRequests) != monthlyRequests || int64(oldPlan.DailyWebsocketConnections) != dailyWebsocketConnections || int64(oldPlan.MonthlyWebsocketConnections) != monthlyWebsocketConnections) { planLimitChanged = true } if planLimitChanged { // 重置限流状态 err = SharedServerDAO.ResetServersTrafficLimitStatusWithPlanId(tx, planId) if err != nil { return err } // 重新计算限流状态 err = SharedUserPlanStatDAO.ResetUserPlanStatsWithPlanId(tx, planId) if err != nil { return err } err = SharedUserPlanStatDAO.ProcessUserPlanStats(tx) if err != nil { return err } } return nil } // CountAllEnabledPlans 计算套餐的数量 func (this *PlanDAO) CountAllEnabledPlans(tx *dbs.Tx) (int64, error) { return this.Query(tx). State(PlanStateEnabled). Count() } // ListEnabledPlans 列出单页套餐 func (this *PlanDAO) ListEnabledPlans(tx *dbs.Tx, offset int64, size int64) (result []*Plan, err error) { _, err = this.Query(tx). State(PlanStateEnabled). Offset(offset). Limit(size). Slice(&result). Desc("order"). AscPk(). FindAll() return } // FindAllEnabledPlans 查找所有可用套餐,包含未启用的套餐 func (this *PlanDAO) FindAllEnabledPlans(tx *dbs.Tx) (result []*Plan, err error) { _, err = this.Query(tx). State(PlanStateEnabled). Slice(&result). FindAll() return } // FindAllAvailablePlans 查找所有可用套餐 // 不包含未启用的套餐 func (this *PlanDAO) FindAllAvailablePlans(tx *dbs.Tx) (result []*Plan, err error) { _, err = this.Query(tx). State(PlanStateEnabled). Attr("isOn", true). Desc("order"). AscPk(). Slice(&result). FindAll() return } // FindAllAvailableBasicPlans 查找所有可用套餐,并只返回基本信息 func (this *PlanDAO) FindAllAvailableBasicPlans(tx *dbs.Tx) (result []*Plan, err error) { _, err = this.Query(tx). State(PlanStateEnabled). Attr("isOn", true). Result("id", "name", "bandwidthLimitPerNode", "trafficLimit", "maxUploadSize"). Slice(&result). FindAll() return } // SortPlans 增加排序 func (this *PlanDAO) SortPlans(tx *dbs.Tx, planIds []int64) error { if len(planIds) == 0 { return nil } var order = len(planIds) for _, planId := range planIds { err := this.Query(tx). Pk(planId). Set("order", order). UpdateQuickly() if err != nil { return err } order-- } return nil } // FindEnabledPlanTrafficLimit 获取套餐的流量限制 func (this *PlanDAO) FindEnabledPlanTrafficLimit(tx *dbs.Tx, planId int64, cacheMap *utils.CacheMap) (*serverconfigs.TrafficLimitConfig, error) { var cacheKey = this.Table + ":FindEnabledPlanTrafficLimit:" + types.String(planId) if cacheMap != nil { cache, _ := cacheMap.Get(cacheKey) if cache != nil { return cache.(*serverconfigs.TrafficLimitConfig), nil } } trafficLimit, err := this.Query(tx). Pk(planId). State(PlanStateEnabled). Result("trafficLimit"). FindStringCol("") if err != nil { return nil, err } if len(trafficLimit) == 0 { return nil, nil } var config = &serverconfigs.TrafficLimitConfig{} err = json.Unmarshal([]byte(trafficLimit), config) if err != nil { return nil, err } if cacheMap != nil { cacheMap.Put(cacheKey, config) } return config, nil } // FindPlanClusterId 查找套餐所属集群 func (this *PlanDAO) FindPlanClusterId(tx *dbs.Tx, planId int64) (clusterId int64, err error) { return this.Query(tx). Pk(planId). Result("clusterId"). FindInt64Col(0) } // NotifyUpdate 通知变更 func (this *PlanDAO) NotifyUpdate(tx *dbs.Tx, planId int64, clusterId int64) error { if clusterId <= 0 { return nil } err := SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, clusterId, 0, 0, NodeTaskTypePlanChanged) if err != nil { return err } return nil }