mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-06 01:50:25 +08:00
实现带宽计费套餐
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
// 区域计费设置
|
// NodePriceItem 区域计费设置
|
||||||
type NodePriceItem struct {
|
type NodePriceItem struct {
|
||||||
Id uint32 `field:"id"` // ID
|
Id uint32 `field:"id"` // ID
|
||||||
IsOn uint8 `field:"isOn"` // 是否启用
|
IsOn uint8 `field:"isOn"` // 是否启用
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
// 节点区域
|
// NodeRegion 节点区域
|
||||||
type NodeRegion struct {
|
type NodeRegion struct {
|
||||||
Id uint32 `field:"id"` // ID
|
Id uint32 `field:"id"` // ID
|
||||||
AdminId uint32 `field:"adminId"` // 管理员ID
|
AdminId uint32 `field:"adminId"` // 管理员ID
|
||||||
|
|||||||
@@ -1 +1,18 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
func (this *NodeRegion) DecodePriceMap() map[int64]float64 {
|
||||||
|
var m = map[int64]float64{}
|
||||||
|
if len(this.Prices) == 0 {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal([]byte(this.Prices), &m)
|
||||||
|
if err != nil {
|
||||||
|
// 忽略错误
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func TestNodeTaskDAO_CreateNodeTask(t *testing.T) {
|
|||||||
dbs.NotifyReady()
|
dbs.NotifyReady()
|
||||||
|
|
||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
err := SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, 1, 2, NodeTaskTypeConfigChanged, 0)
|
err := SharedNodeTaskDAO.CreateNodeTask(tx, nodeconfigs.NodeRoleNode, 1, 2, 0, NodeTaskTypeConfigChanged, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ func TestNodeTaskDAO_CreateClusterTask(t *testing.T) {
|
|||||||
dbs.NotifyReady()
|
dbs.NotifyReady()
|
||||||
|
|
||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
err := SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, 1, NodeTaskTypeConfigChanged)
|
err := SharedNodeTaskDAO.CreateClusterTask(tx, nodeconfigs.NodeRoleNode, 1, 0, NodeTaskTypeConfigChanged)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ func TestNodeTaskDAO_ExtractClusterTask(t *testing.T) {
|
|||||||
dbs.NotifyReady()
|
dbs.NotifyReady()
|
||||||
|
|
||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
err := SharedNodeTaskDAO.ExtractNodeClusterTask(tx, 1, NodeTaskTypeConfigChanged)
|
err := SharedNodeTaskDAO.ExtractNodeClusterTask(tx, 1, 0, NodeTaskTypeConfigChanged)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,17 @@ func (this *PlanDAO) FindPlanName(tx *dbs.Tx, id int64) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreatePlan 创建套餐
|
// CreatePlan 创建套餐
|
||||||
func (this *PlanDAO) CreatePlan(tx *dbs.Tx, name string, clusterId int64, trafficLimitJSON []byte, featuresJSON []byte, priceType serverconfigs.PlanPriceType, trafficPriceJSON []byte, monthlyPrice float32, seasonallyPrice float32, yearlyPrice float32) (int64, error) {
|
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()
|
var op = NewPlanOperator()
|
||||||
op.Name = name
|
op.Name = name
|
||||||
op.ClusterId = clusterId
|
op.ClusterId = clusterId
|
||||||
@@ -94,6 +104,9 @@ func (this *PlanDAO) CreatePlan(tx *dbs.Tx, name string, clusterId int64, traffi
|
|||||||
if len(trafficPriceJSON) > 0 {
|
if len(trafficPriceJSON) > 0 {
|
||||||
op.TrafficPrice = trafficPriceJSON
|
op.TrafficPrice = trafficPriceJSON
|
||||||
}
|
}
|
||||||
|
if len(bandwidthPriceJSON) > 0 {
|
||||||
|
op.BandwidthPrice = bandwidthPriceJSON
|
||||||
|
}
|
||||||
if monthlyPrice >= 0 {
|
if monthlyPrice >= 0 {
|
||||||
op.MonthlyPrice = monthlyPrice
|
op.MonthlyPrice = monthlyPrice
|
||||||
}
|
}
|
||||||
@@ -109,7 +122,19 @@ func (this *PlanDAO) CreatePlan(tx *dbs.Tx, name string, clusterId int64, traffi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePlan 修改套餐
|
// 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, monthlyPrice float32, seasonallyPrice float32, yearlyPrice float32) error {
|
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 {
|
if planId <= 0 {
|
||||||
return errors.New("invalid planId")
|
return errors.New("invalid planId")
|
||||||
}
|
}
|
||||||
@@ -138,6 +163,9 @@ func (this *PlanDAO) UpdatePlan(tx *dbs.Tx, planId int64, name string, isOn bool
|
|||||||
if len(trafficPriceJSON) > 0 {
|
if len(trafficPriceJSON) > 0 {
|
||||||
op.TrafficPrice = trafficPriceJSON
|
op.TrafficPrice = trafficPriceJSON
|
||||||
}
|
}
|
||||||
|
if len(bandwidthPriceJSON) > 0 {
|
||||||
|
op.BandwidthPrice = bandwidthPriceJSON
|
||||||
|
}
|
||||||
if monthlyPrice >= 0 {
|
if monthlyPrice >= 0 {
|
||||||
op.MonthlyPrice = monthlyPrice
|
op.MonthlyPrice = monthlyPrice
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ type Plan struct {
|
|||||||
TrafficLimit string `field:"trafficLimit"` // 流量限制
|
TrafficLimit string `field:"trafficLimit"` // 流量限制
|
||||||
Features string `field:"features"` // 允许的功能
|
Features string `field:"features"` // 允许的功能
|
||||||
TrafficPrice string `field:"trafficPrice"` // 流量价格设定
|
TrafficPrice string `field:"trafficPrice"` // 流量价格设定
|
||||||
|
BandwidthPrice string `field:"bandwidthPrice"` // 带宽价格
|
||||||
MonthlyPrice float64 `field:"monthlyPrice"` // 月付
|
MonthlyPrice float64 `field:"monthlyPrice"` // 月付
|
||||||
SeasonallyPrice float64 `field:"seasonallyPrice"` // 季付
|
SeasonallyPrice float64 `field:"seasonallyPrice"` // 季付
|
||||||
YearlyPrice float64 `field:"yearlyPrice"` // 年付
|
YearlyPrice float64 `field:"yearlyPrice"` // 年付
|
||||||
@@ -25,6 +26,7 @@ type PlanOperator struct {
|
|||||||
TrafficLimit interface{} // 流量限制
|
TrafficLimit interface{} // 流量限制
|
||||||
Features interface{} // 允许的功能
|
Features interface{} // 允许的功能
|
||||||
TrafficPrice interface{} // 流量价格设定
|
TrafficPrice interface{} // 流量价格设定
|
||||||
|
BandwidthPrice interface{} // 带宽价格
|
||||||
MonthlyPrice interface{} // 月付
|
MonthlyPrice interface{} // 月付
|
||||||
SeasonallyPrice interface{} // 季付
|
SeasonallyPrice interface{} // 季付
|
||||||
YearlyPrice interface{} // 年付
|
YearlyPrice interface{} // 年付
|
||||||
|
|||||||
@@ -1 +1,38 @@
|
|||||||
package models
|
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([]byte(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([]byte(this.BandwidthPrice), config)
|
||||||
|
if err != nil {
|
||||||
|
// 忽略错误
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|||||||
96
internal/db/models/server_bill_dao.go
Normal file
96
internal/db/models/server_bill_dao.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerBillDAO dbs.DAO
|
||||||
|
|
||||||
|
func NewServerBillDAO() *ServerBillDAO {
|
||||||
|
return dbs.NewDAO(&ServerBillDAO{
|
||||||
|
DAOObject: dbs.DAOObject{
|
||||||
|
DB: Tea.Env,
|
||||||
|
Table: "edgeServerBills",
|
||||||
|
Model: new(ServerBill),
|
||||||
|
PkName: "id",
|
||||||
|
},
|
||||||
|
}).(*ServerBillDAO)
|
||||||
|
}
|
||||||
|
|
||||||
|
var SharedServerBillDAO *ServerBillDAO
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dbs.OnReady(func() {
|
||||||
|
SharedServerBillDAO = NewServerBillDAO()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrUpdateServerBill 创建账单
|
||||||
|
func (this *ServerBillDAO) CreateOrUpdateServerBill(tx *dbs.Tx, userId int64, serverId int64, month string, userPlanId int64, planId int64, totalTrafficBytes int64, bandwidthPercentileBytes int64, bandwidthPercentile int, fee float64) error {
|
||||||
|
fee = math.Floor(fee*100) / 100
|
||||||
|
return this.Query(tx).
|
||||||
|
InsertOrUpdateQuickly(maps.Map{
|
||||||
|
"userId": userId,
|
||||||
|
"serverId": serverId,
|
||||||
|
"month": month,
|
||||||
|
"amount": fee,
|
||||||
|
"userPlanId": userPlanId,
|
||||||
|
"planId": planId,
|
||||||
|
"totalTrafficBytes": totalTrafficBytes,
|
||||||
|
"bandwidthPercentileBytes": bandwidthPercentileBytes,
|
||||||
|
"bandwidthPercentile": bandwidthPercentile,
|
||||||
|
"createdAt": time.Now().Unix(),
|
||||||
|
}, maps.Map{
|
||||||
|
"userId": userId,
|
||||||
|
"amount": fee,
|
||||||
|
"userPlanId": userPlanId,
|
||||||
|
"planId": planId,
|
||||||
|
"totalTrafficBytes": totalTrafficBytes,
|
||||||
|
"bandwidthPercentileBytes": bandwidthPercentileBytes,
|
||||||
|
"bandwidthPercentile": bandwidthPercentile,
|
||||||
|
"createdAt": time.Now().Unix(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumUserMonthlyAmount 计算总费用
|
||||||
|
func (this *ServerBillDAO) SumUserMonthlyAmount(tx *dbs.Tx, userId int64, month string) (float64, error) {
|
||||||
|
return this.Query(tx).
|
||||||
|
Attr("userId", userId).
|
||||||
|
Attr("month", month).
|
||||||
|
Sum("amount", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountServerBills 计算总账单数量
|
||||||
|
func (this *ServerBillDAO) CountServerBills(tx *dbs.Tx, userId int64, month string) (int64, error) {
|
||||||
|
var query = this.Query(tx)
|
||||||
|
if userId > 0 {
|
||||||
|
query.Attr("userId", userId)
|
||||||
|
}
|
||||||
|
if len(month) > 0 {
|
||||||
|
query.Attr("month", month)
|
||||||
|
}
|
||||||
|
return query.Count()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListServerBills 列出单页账单
|
||||||
|
func (this *ServerBillDAO) ListServerBills(tx *dbs.Tx, userId int64, month string, offset int64, size int64) (result []*ServerBill, err error) {
|
||||||
|
var query = this.Query(tx)
|
||||||
|
if userId > 0 {
|
||||||
|
query.Attr("userId", userId)
|
||||||
|
}
|
||||||
|
if len(month) > 0 {
|
||||||
|
query.Attr("month", month)
|
||||||
|
}
|
||||||
|
_, err = query.
|
||||||
|
Desc("serverId").
|
||||||
|
Offset(offset).
|
||||||
|
Limit(size).
|
||||||
|
Slice(&result).
|
||||||
|
FindAll()
|
||||||
|
return
|
||||||
|
}
|
||||||
20
internal/db/models/server_bill_dao_test.go
Normal file
20
internal/db/models/server_bill_dao_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"github.com/iwind/TeaGo/dbs"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerBillDAO_CreateOrUpdateServerBill(t *testing.T) {
|
||||||
|
var dao = NewServerBillDAO()
|
||||||
|
var tx *dbs.Tx
|
||||||
|
var month = timeutil.Format("Y02")
|
||||||
|
err := dao.CreateOrUpdateServerBill(tx, 1, 2, month, 4, 5, 6, 7, 95, 100)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
34
internal/db/models/server_bill_model.go
Normal file
34
internal/db/models/server_bill_model.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// ServerBill 服务账单
|
||||||
|
type ServerBill struct {
|
||||||
|
Id uint64 `field:"id"` // ID
|
||||||
|
UserId uint32 `field:"userId"` // 用户ID
|
||||||
|
ServerId uint32 `field:"serverId"` // 服务ID
|
||||||
|
Amount float64 `field:"amount"` // 金额
|
||||||
|
Month string `field:"month"` // 月份
|
||||||
|
CreatedAt uint64 `field:"createdAt"` // 创建时间
|
||||||
|
UserPlanId uint32 `field:"userPlanId"` // 用户套餐ID
|
||||||
|
PlanId uint32 `field:"planId"` // 套餐ID
|
||||||
|
TotalTrafficBytes uint64 `field:"totalTrafficBytes"` // 总流量
|
||||||
|
BandwidthPercentileBytes uint64 `field:"bandwidthPercentileBytes"` // 带宽百分位字节
|
||||||
|
BandwidthPercentile uint8 `field:"bandwidthPercentile"` // 带宽百分位
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerBillOperator struct {
|
||||||
|
Id interface{} // ID
|
||||||
|
UserId interface{} // 用户ID
|
||||||
|
ServerId interface{} // 服务ID
|
||||||
|
Amount interface{} // 金额
|
||||||
|
Month interface{} // 月份
|
||||||
|
CreatedAt interface{} // 创建时间
|
||||||
|
UserPlanId interface{} // 用户套餐ID
|
||||||
|
PlanId interface{} // 套餐ID
|
||||||
|
TotalTrafficBytes interface{} // 总流量
|
||||||
|
BandwidthPercentileBytes interface{} // 带宽百分位字节
|
||||||
|
BandwidthPercentile interface{} // 带宽百分位
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerBillOperator() *ServerBillOperator {
|
||||||
|
return &ServerBillOperator{}
|
||||||
|
}
|
||||||
1
internal/db/models/server_bill_model_ext.go
Normal file
1
internal/db/models/server_bill_model_ext.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package models
|
||||||
@@ -12,7 +12,9 @@ import (
|
|||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"math"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ func init() {
|
|||||||
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
err := SharedServerDailyStatDAO.Clean(nil, 30) // 只保留N天
|
err := SharedServerDailyStatDAO.Clean(nil, 60) // 只保留 N 天,时间需要长一些,因为需要用来生成账单
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("ServerDailyStatDAO", "clean expired data failed: "+err.Error())
|
logs.Println("ServerDailyStatDAO", "clean expired data failed: "+err.Error())
|
||||||
}
|
}
|
||||||
@@ -130,15 +132,15 @@ func (this *ServerDailyStatDAO) SaveStats(tx *dbs.Tx, stats []*pb.ServerDailySta
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SumUserMonthly 根据用户计算某月合计
|
// SumServerMonthlyWithRegion 根据服务计算某月合计
|
||||||
// month 格式为YYYYMM
|
// month 格式为YYYYMM
|
||||||
func (this *ServerDailyStatDAO) SumUserMonthly(tx *dbs.Tx, userId int64, regionId int64, month string) (int64, error) {
|
func (this *ServerDailyStatDAO) SumServerMonthlyWithRegion(tx *dbs.Tx, serverId int64, regionId int64, month string) (int64, error) {
|
||||||
query := this.Query(tx)
|
query := this.Query(tx)
|
||||||
if regionId > 0 {
|
if regionId > 0 {
|
||||||
query.Attr("regionId", regionId)
|
query.Attr("regionId", regionId)
|
||||||
}
|
}
|
||||||
return query.Between("day", month+"01", month+"32").
|
return query.Between("day", month+"01", month+"32").
|
||||||
Attr("userId", userId).
|
Attr("serverId", serverId).
|
||||||
SumInt64("bytes", 0)
|
SumInt64("bytes", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,16 +158,6 @@ func (this *ServerDailyStatDAO) SumUserMonthlyWithoutPlan(tx *dbs.Tx, userId int
|
|||||||
SumInt64("bytes", 0)
|
SumInt64("bytes", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SumUserMonthlyFee 计算用户某个月费用
|
|
||||||
// month 格式为YYYYMM
|
|
||||||
func (this *ServerDailyStatDAO) SumUserMonthlyFee(tx *dbs.Tx, userId int64, month string) (float64, error) {
|
|
||||||
return this.Query(tx).
|
|
||||||
Attr("userId", userId).
|
|
||||||
Between("day", month+"01", month+"32").
|
|
||||||
Gt("fee", 0).
|
|
||||||
Sum("fee", 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SumUserMonthlyPeek 获取某月带宽峰值
|
// SumUserMonthlyPeek 获取某月带宽峰值
|
||||||
// month 格式为YYYYMM
|
// month 格式为YYYYMM
|
||||||
func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, regionId int64, month string) (int64, error) {
|
func (this *ServerDailyStatDAO) SumUserMonthlyPeek(tx *dbs.Tx, userId int64, regionId int64, month string) (int64, error) {
|
||||||
@@ -195,6 +187,15 @@ func (this *ServerDailyStatDAO) SumUserDaily(tx *dbs.Tx, userId int64, regionId
|
|||||||
SumInt64("bytes", 0)
|
SumInt64("bytes", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SumUserMonthly 获取某月流量总和
|
||||||
|
// month 格式为YYYYMM
|
||||||
|
func (this *ServerDailyStatDAO) SumUserMonthly(tx *dbs.Tx, userId int64, month string) (int64, error) {
|
||||||
|
return this.Query(tx).
|
||||||
|
Between("day", month+"01", month+"31").
|
||||||
|
Attr("userId", userId).
|
||||||
|
SumInt64("bytes", 0)
|
||||||
|
}
|
||||||
|
|
||||||
// SumUserDailyPeek 获取某天带宽峰值
|
// SumUserDailyPeek 获取某天带宽峰值
|
||||||
// day 格式为YYYYMMDD
|
// day 格式为YYYYMMDD
|
||||||
func (this *ServerDailyStatDAO) SumUserDailyPeek(tx *dbs.Tx, userId int64, regionId int64, day string) (int64, error) {
|
func (this *ServerDailyStatDAO) SumUserDailyPeek(tx *dbs.Tx, userId int64, regionId int64, day string) (int64, error) {
|
||||||
@@ -339,6 +340,59 @@ func (this *ServerDailyStatDAO) SumMonthlyStat(tx *dbs.Tx, serverId int64, month
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Query(tx).
|
||||||
|
Result("SUM(bytes) AS bytes").
|
||||||
|
Attr("serverId", serverId).
|
||||||
|
Between("day", month+"01", month+"31").
|
||||||
|
FindInt64Col(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMonthlyPercentile 获取某月内百分位
|
||||||
|
func (this *ServerDailyStatDAO) FindMonthlyPercentile(tx *dbs.Tx, serverId int64, month string, percentile int) (result int64, err error) {
|
||||||
|
if percentile <= 0 {
|
||||||
|
percentile = 95
|
||||||
|
}
|
||||||
|
if percentile > 100 {
|
||||||
|
percentile = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := this.Query(tx).
|
||||||
|
Attr("serverId", serverId).
|
||||||
|
Between("day", month+"01", month+"31").
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if total == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset int64
|
||||||
|
|
||||||
|
if total > 1 {
|
||||||
|
offset = int64(math.Ceil(float64(total) * float64(100-percentile) / 100))
|
||||||
|
}
|
||||||
|
result, err = this.Query(tx).
|
||||||
|
Result("bytes").
|
||||||
|
Attr("serverId", serverId).
|
||||||
|
Between("day", month+"01", month+"31").
|
||||||
|
Desc("bytes").
|
||||||
|
Offset(offset).
|
||||||
|
Limit(1).
|
||||||
|
FindInt64Col(0)
|
||||||
|
|
||||||
|
// 因为是5分钟统计,所以需要除以300
|
||||||
|
result = result / 300
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// FindDailyStats 按天统计
|
// FindDailyStats 按天统计
|
||||||
func (this *ServerDailyStatDAO) FindDailyStats(tx *dbs.Tx, serverId int64, dayFrom string, dayTo string) (result []*ServerDailyStat, err error) {
|
func (this *ServerDailyStatDAO) FindDailyStats(tx *dbs.Tx, serverId int64, dayFrom string, dayTo string) (result []*ServerDailyStat, err error) {
|
||||||
ones, err := this.Query(tx).
|
ones, err := this.Query(tx).
|
||||||
@@ -428,6 +482,25 @@ func (this *ServerDailyStatDAO) FindTopUserStats(tx *dbs.Tx, hourFrom string, ho
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindDistinctServerIds 查找所有有流量的服务ID列表
|
||||||
|
// dayFrom YYYYMMDD
|
||||||
|
// dayTo YYYYMMDD
|
||||||
|
func (this *ServerDailyStatDAO) FindDistinctServerIds(tx *dbs.Tx, dayFrom string, dayTo string) (serverIds []int64, err error) {
|
||||||
|
dayFrom = strings.ReplaceAll(dayFrom, "-", "")
|
||||||
|
dayTo = strings.ReplaceAll(dayTo, "-", "")
|
||||||
|
ones, _, err := this.Query(tx).
|
||||||
|
Result("DISTINCT(serverId) AS serverId").
|
||||||
|
Between("day", dayFrom, dayTo).
|
||||||
|
FindOnes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, one := range ones {
|
||||||
|
serverIds = append(serverIds, one.GetInt64("serverId"))
|
||||||
|
}
|
||||||
|
return serverIds, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateStatFee 设置费用
|
// UpdateStatFee 设置费用
|
||||||
func (this *ServerDailyStatDAO) UpdateStatFee(tx *dbs.Tx, statId int64, fee float32) error {
|
func (this *ServerDailyStatDAO) UpdateStatFee(tx *dbs.Tx, statId int64, fee float32) error {
|
||||||
return this.Query(tx).
|
return this.Query(tx).
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func TestServerDailyStatDAO_SaveStats2(t *testing.T) {
|
|||||||
func TestServerDailyStatDAO_SumUserMonthly(t *testing.T) {
|
func TestServerDailyStatDAO_SumUserMonthly(t *testing.T) {
|
||||||
dbs.NotifyReady()
|
dbs.NotifyReady()
|
||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
bytes, err := NewServerDailyStatDAO().SumUserMonthly(tx, 1, 1, timeutil.Format("Ym"))
|
bytes, err := NewServerDailyStatDAO().SumUserMonthly(tx, 1, timeutil.Format("Ym"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -68,9 +68,28 @@ func TestServerDailyStatDAO_SumMinutelyRequests(t *testing.T) {
|
|||||||
dbs.NotifyReady()
|
dbs.NotifyReady()
|
||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
|
|
||||||
stat, err := NewServerDailyStatDAO().SumMinutelyStat(tx, 23, timeutil.Format("Ymd") + "1435")
|
stat, err := NewServerDailyStatDAO().SumMinutelyStat(tx, 23, timeutil.Format("Ymd")+"1435")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
logs.PrintAsJSON(stat, t)
|
logs.PrintAsJSON(stat, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServerDailyStatDAO_FindDistinctPlanServerIdsBetweenDay(t *testing.T) {
|
||||||
|
var tx *dbs.Tx
|
||||||
|
serverIds, err := NewServerDailyStatDAO().FindDistinctServerIds(tx, timeutil.Format("Ym01"), timeutil.Format("Ymd"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(serverIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerDailyStatDAO_FindMonthlyPercentile(t *testing.T) {
|
||||||
|
var tx *dbs.Tx
|
||||||
|
var dao = NewServerDailyStatDAO()
|
||||||
|
result, err := dao.FindMonthlyPercentile(tx, 23, timeutil.Format("Ym"), 95)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("result:", result)
|
||||||
|
}
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ func (this *ServerDAO) CreateServer(tx *dbs.Tx,
|
|||||||
op.DnsName = dnsName
|
op.DnsName = dnsName
|
||||||
|
|
||||||
op.UserPlanId = userPlanId
|
op.UserPlanId = userPlanId
|
||||||
|
op.LastUserPlanId = userPlanId
|
||||||
|
|
||||||
op.Version = 1
|
op.Version = 1
|
||||||
op.IsOn = 1
|
op.IsOn = 1
|
||||||
@@ -2242,6 +2243,7 @@ func (this *ServerDAO) UpdateServerUserPlanId(tx *dbs.Tx, serverId int64, userPl
|
|||||||
err = this.Query(tx).
|
err = this.Query(tx).
|
||||||
Pk(serverId).
|
Pk(serverId).
|
||||||
Set("userPlanId", userPlanId).
|
Set("userPlanId", userPlanId).
|
||||||
|
Set("lastUserPlanId", userPlanId).
|
||||||
Set("clusterId", plan.ClusterId).
|
Set("clusterId", plan.ClusterId).
|
||||||
UpdateQuickly()
|
UpdateQuickly()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -2250,6 +2252,19 @@ func (this *ServerDAO) UpdateServerUserPlanId(tx *dbs.Tx, serverId int64, userPl
|
|||||||
return this.NotifyUpdate(tx, serverId)
|
return this.NotifyUpdate(tx, serverId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindServerLastUserPlanIdAndUserId 查找最后使用的套餐
|
||||||
|
func (this *ServerDAO) FindServerLastUserPlanIdAndUserId(tx *dbs.Tx, serverId int64) (userPlanId int64, userId int64, err error) {
|
||||||
|
one, err := this.Query(tx).
|
||||||
|
Pk(serverId).
|
||||||
|
Result("lastUserPlanId", "userId").
|
||||||
|
Find()
|
||||||
|
if err != nil || one == nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(one.(*Server).LastUserPlanId), int64(one.(*Server).UserId), nil
|
||||||
|
}
|
||||||
|
|
||||||
// NotifyUpdate 同步集群
|
// NotifyUpdate 同步集群
|
||||||
func (this *ServerDAO) NotifyUpdate(tx *dbs.Tx, serverId int64) error {
|
func (this *ServerDAO) NotifyUpdate(tx *dbs.Tx, serverId int64) error {
|
||||||
// 创建任务
|
// 创建任务
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ type Server struct {
|
|||||||
TrafficLimitStatus string `field:"trafficLimitStatus"` // 流量限制状态
|
TrafficLimitStatus string `field:"trafficLimitStatus"` // 流量限制状态
|
||||||
TotalTraffic float64 `field:"totalTraffic"` // 总流量
|
TotalTraffic float64 `field:"totalTraffic"` // 总流量
|
||||||
UserPlanId uint32 `field:"userPlanId"` // 所属套餐ID
|
UserPlanId uint32 `field:"userPlanId"` // 所属套餐ID
|
||||||
|
LastUserPlanId uint32 `field:"lastUserPlanId"` // 上一次使用的套餐
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerOperator struct {
|
type ServerOperator struct {
|
||||||
@@ -87,6 +88,7 @@ type ServerOperator struct {
|
|||||||
TrafficLimitStatus interface{} // 流量限制状态
|
TrafficLimitStatus interface{} // 流量限制状态
|
||||||
TotalTraffic interface{} // 总流量
|
TotalTraffic interface{} // 总流量
|
||||||
UserPlanId interface{} // 所属套餐ID
|
UserPlanId interface{} // 所属套餐ID
|
||||||
|
LastUserPlanId interface{} // 上一次使用的套餐
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerOperator() *ServerOperator {
|
func NewServerOperator() *ServerOperator {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
"github.com/TeaOSLab/EdgeAPI/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeAPI/internal/utils/numberutils"
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
@@ -23,7 +22,7 @@ func init() {
|
|||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
// 自动生成账单任务
|
// 自动生成账单任务
|
||||||
var ticker = time.NewTicker(1 * time.Minute)
|
var ticker = time.NewTicker(1 * time.Hour)
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
// 是否已经生成了,如果已经生成了就跳过
|
// 是否已经生成了,如果已经生成了就跳过
|
||||||
var lastMonth = timeutil.Format("Ym", time.Now().AddDate(0, -1, 0))
|
var lastMonth = timeutil.Format("Ym", time.Now().AddDate(0, -1, 0))
|
||||||
@@ -136,7 +135,7 @@ func (this *UserBillDAO) FindUnpaidBills(tx *dbs.Tx, size int64) (result []*User
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateBill 创建账单
|
// CreateBill 创建账单
|
||||||
func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType, description string, amount float32, month string) error {
|
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)
|
code, err := this.GenerateBillCode(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -149,9 +148,11 @@ func (this *UserBillDAO) CreateBill(tx *dbs.Tx, userId int64, billType BillType,
|
|||||||
"amount": amount,
|
"amount": amount,
|
||||||
"month": month,
|
"month": month,
|
||||||
"code": code,
|
"code": code,
|
||||||
"isPaid": false,
|
"isPaid": amount == 0,
|
||||||
|
"canPay": canPay,
|
||||||
}, maps.Map{
|
}, maps.Map{
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
|
"canPay": canPay,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,19 +173,16 @@ func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(regions) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
priceItems, err := SharedNodePriceItemDAO.FindAllEnabledRegionPrices(tx, NodePriceTypeTraffic)
|
var priceItems []*NodePriceItem
|
||||||
|
if len(regions) > 0 {
|
||||||
|
priceItems, err = SharedNodePriceItemDAO.FindAllEnabledRegionPrices(tx, NodePriceTypeTraffic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(priceItems) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算套餐费用
|
// 计算服务套餐费用
|
||||||
plans, err := SharedPlanDAO.FindAllEnabledPlans(tx)
|
plans, err := SharedPlanDAO.FindAllEnabledPlans(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -194,54 +192,169 @@ func (this *UserBillDAO) GenerateBills(tx *dbs.Tx, month string) error {
|
|||||||
planMap[int64(plan.Id)] = plan
|
planMap[int64(plan.Id)] = plan
|
||||||
}
|
}
|
||||||
|
|
||||||
stats, err := SharedServerDailyStatDAO.FindMonthlyStatsWithPlan(tx, month)
|
var dayFrom = month + "01"
|
||||||
|
var dayTo = month + "32"
|
||||||
|
serverIds, err := SharedServerDailyStatDAO.FindDistinctServerIds(tx, dayFrom, dayTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, stat := range stats {
|
var cacheMap = utils.NewCacheMap()
|
||||||
plan, ok := planMap[int64(stat.PlanId)]
|
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 {
|
||||||
|
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 {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if plan.PriceType != serverconfigs.PlanPriceTypeTraffic {
|
if price <= 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(plan.TrafficPrice) == 0 {
|
var regionFee = float64(trafficBytes) / 1000 / 1000 / 1000 * 8 * price
|
||||||
continue
|
fee += regionFee
|
||||||
}
|
}
|
||||||
var priceConfig = &serverconfigs.PlanTrafficPrice{}
|
|
||||||
err = json.Unmarshal([]byte(plan.TrafficPrice), priceConfig)
|
// 总流量
|
||||||
|
totalTrafficBytes, err := SharedServerDailyStatDAO.SumMonthlyBytes(tx, serverId, month)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if priceConfig.Base > 0 {
|
|
||||||
var fee = priceConfig.Base * (float32(stat.Bytes) / 1024 / 1024 / 1024)
|
// 百分位
|
||||||
err = SharedServerDailyStatDAO.UpdateStatFee(tx, int64(stat.Id), fee)
|
var percentile = 95
|
||||||
|
percentileBytes, err := SharedServerDailyStatDAO.FindMonthlyPercentile(tx, serverId, month, percentile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 := SharedServerDailyStatDAO.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, 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 := SharedServerDailyStatDAO.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, 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 := SharedServerDailyStatDAO.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, fee)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户
|
// 计算用户费用
|
||||||
offset := int64(0)
|
|
||||||
size := int64(100) // 每次只查询N次,防止由于执行时间过长而锁表
|
|
||||||
for {
|
|
||||||
userIds, err := SharedUserDAO.ListEnabledUserIds(tx, offset, size)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
offset += size
|
|
||||||
if len(userIds) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, userId := range userIds {
|
for _, userId := range userIds {
|
||||||
// CDN流量账单
|
if userId == 0 {
|
||||||
err := this.generateTrafficBill(tx, userId, month, regions, priceItems)
|
continue
|
||||||
|
}
|
||||||
|
amount, err := SharedServerBillDAO.SumUserMonthlyAmount(tx, userId, month)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = SharedUserBillDAO.CreateBill(tx, userId, BillTypeTraffic, "流量带宽费用", amount, month, month < timeutil.Format("Ym"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,74 +369,11 @@ func (this *UserBillDAO) UpdateUserBillIsPaid(tx *dbs.Tx, billId int64, isPaid b
|
|||||||
UpdateQuickly()
|
UpdateQuickly()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成CDN流量账单
|
|
||||||
// month 格式YYYYMM
|
|
||||||
func (this *UserBillDAO) generateTrafficBill(tx *dbs.Tx, userId int64, month string, regions []*NodeRegion, priceItems []*NodePriceItem) error {
|
|
||||||
// 检查是否已经有账单了
|
|
||||||
if month < timeutil.Format("Ym") {
|
|
||||||
b, err := this.ExistBill(tx, userId, BillTypeTraffic, month)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if b {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var cost = float32(0)
|
|
||||||
for _, region := range regions {
|
|
||||||
if len(region.Prices) == 0 || region.Prices == "null" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
priceMap := map[string]float32{}
|
|
||||||
err := json.Unmarshal([]byte(region.Prices), &priceMap)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
trafficBytes, err := SharedServerDailyStatDAO.SumUserMonthlyWithoutPlan(tx, userId, int64(region.Id), month)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if trafficBytes == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
itemId := SharedNodePriceItemDAO.SearchItemsWithBytes(priceItems, trafficBytes)
|
|
||||||
if itemId == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
price, ok := priceMap[numberutils.FormatInt64(itemId)]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算钱
|
|
||||||
// 这里采用1000进制
|
|
||||||
cost += (float32(trafficBytes*8) / 1_000_000_000) * price
|
|
||||||
}
|
|
||||||
|
|
||||||
// 套餐费用
|
|
||||||
planFee, err := SharedServerDailyStatDAO.SumUserMonthlyFee(tx, userId, month)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cost += float32(planFee)
|
|
||||||
|
|
||||||
if cost == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建账单
|
|
||||||
return this.CreateBill(tx, userId, BillTypeTraffic, "按流量计费", cost, month)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BillTypeName 获取账单类型名称
|
// BillTypeName 获取账单类型名称
|
||||||
func (this *UserBillDAO) BillTypeName(billType BillType) string {
|
func (this *UserBillDAO) BillTypeName(billType BillType) string {
|
||||||
switch billType {
|
switch billType {
|
||||||
case BillTypeTraffic:
|
case BillTypeTraffic:
|
||||||
return "流量"
|
return "流量带宽"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ type UserBill struct {
|
|||||||
Type string `field:"type"` // 消费类型
|
Type string `field:"type"` // 消费类型
|
||||||
Description string `field:"description"` // 描述
|
Description string `field:"description"` // 描述
|
||||||
Amount float64 `field:"amount"` // 消费数额
|
Amount float64 `field:"amount"` // 消费数额
|
||||||
|
DayFrom string `field:"dayFrom"` // YYYYMMDD
|
||||||
|
DayTo string `field:"dayTo"` // YYYYMMDD
|
||||||
Month string `field:"month"` // 帐期YYYYMM
|
Month string `field:"month"` // 帐期YYYYMM
|
||||||
|
CanPay uint8 `field:"canPay"` // 是否可以支付
|
||||||
IsPaid uint8 `field:"isPaid"` // 是否已支付
|
IsPaid uint8 `field:"isPaid"` // 是否已支付
|
||||||
PaidAt uint64 `field:"paidAt"` // 支付时间
|
PaidAt uint64 `field:"paidAt"` // 支付时间
|
||||||
Code string `field:"code"` // 账单编号
|
Code string `field:"code"` // 账单编号
|
||||||
@@ -20,7 +23,10 @@ type UserBillOperator struct {
|
|||||||
Type interface{} // 消费类型
|
Type interface{} // 消费类型
|
||||||
Description interface{} // 描述
|
Description interface{} // 描述
|
||||||
Amount interface{} // 消费数额
|
Amount interface{} // 消费数额
|
||||||
|
DayFrom interface{} // YYYYMMDD
|
||||||
|
DayTo interface{} // YYYYMMDD
|
||||||
Month interface{} // 帐期YYYYMM
|
Month interface{} // 帐期YYYYMM
|
||||||
|
CanPay interface{} // 是否可以支付
|
||||||
IsPaid interface{} // 是否已支付
|
IsPaid interface{} // 是否已支付
|
||||||
PaidAt interface{} // 支付时间
|
PaidAt interface{} // 支付时间
|
||||||
Code interface{} // 账单编号
|
Code interface{} // 账单编号
|
||||||
|
|||||||
@@ -95,6 +95,18 @@ func (this *UserDAO) FindEnabledBasicUser(tx *dbs.Tx, id int64) (*User, error) {
|
|||||||
return result.(*User), err
|
return result.(*User), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindBasicUserWithoutState 查找用户基本信息,并忽略状态
|
||||||
|
func (this *UserDAO) FindBasicUserWithoutState(tx *dbs.Tx, id int64) (*User, error) {
|
||||||
|
result, err := this.Query(tx).
|
||||||
|
Pk(id).
|
||||||
|
Result("id", "fullname", "username").
|
||||||
|
Find()
|
||||||
|
if result == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result.(*User), err
|
||||||
|
}
|
||||||
|
|
||||||
// FindUserFullname 获取管理员名称
|
// FindUserFullname 获取管理员名称
|
||||||
func (this *UserDAO) FindUserFullname(tx *dbs.Tx, userId int64) (string, error) {
|
func (this *UserDAO) FindUserFullname(tx *dbs.Tx, userId int64) (string, error) {
|
||||||
return this.Query(tx).
|
return this.Query(tx).
|
||||||
|
|||||||
@@ -85,6 +85,31 @@ func (this *UserPlanDAO) FindEnabledUserPlan(tx *dbs.Tx, userPlanId int64, cache
|
|||||||
return result.(*UserPlan), err
|
return result.(*UserPlan), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindUserPlanWithoutState 查找套餐,并不检查状态
|
||||||
|
// 防止因为删除套餐而导致计费失败
|
||||||
|
func (this *UserPlanDAO) FindUserPlanWithoutState(tx *dbs.Tx, userPlanId int64, cacheMap *utils.CacheMap) (*UserPlan, error) {
|
||||||
|
var cacheKey = this.Table + ":FindUserPlanWithoutState:" + types.String(userPlanId)
|
||||||
|
if cacheMap != nil {
|
||||||
|
cache, ok := cacheMap.Get(cacheKey)
|
||||||
|
if ok {
|
||||||
|
return cache.(*UserPlan), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := this.Query(tx).
|
||||||
|
Pk(userPlanId).
|
||||||
|
Find()
|
||||||
|
if result == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cacheMap != nil {
|
||||||
|
cacheMap.Put(cacheKey, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.(*UserPlan), err
|
||||||
|
}
|
||||||
|
|
||||||
// CountAllEnabledUserPlans 计算套餐数量
|
// CountAllEnabledUserPlans 计算套餐数量
|
||||||
func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32) (int64, error) {
|
func (this *UserPlanDAO) CountAllEnabledUserPlans(tx *dbs.Tx, userId int64, isAvailable bool, isExpired bool, expiringDays int32) (int64, error) {
|
||||||
var query = this.Query(tx).
|
var query = this.Query(tx).
|
||||||
|
|||||||
@@ -348,6 +348,11 @@ func (this *APINode) registerServices(server *grpc.Server) {
|
|||||||
pb.RegisterUserBillServiceServer(server, instance)
|
pb.RegisterUserBillServiceServer(server, instance)
|
||||||
this.rest(instance)
|
this.rest(instance)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
instance := this.serviceInstance(&services.ServerBillService{}).(*services.ServerBillService)
|
||||||
|
pb.RegisterServerBillServiceServer(server, instance)
|
||||||
|
this.rest(instance)
|
||||||
|
}
|
||||||
{
|
{
|
||||||
instance := this.serviceInstance(&services.UserNodeService{}).(*services.UserNodeService)
|
instance := this.serviceInstance(&services.UserNodeService{}).(*services.UserNodeService)
|
||||||
pb.RegisterUserNodeServiceServer(server, instance)
|
pb.RegisterUserNodeServiceServer(server, instance)
|
||||||
|
|||||||
131
internal/rpc/services/service_server_bill.go
Normal file
131
internal/rpc/services/service_server_bill.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerBillService 服务账单相关服务
|
||||||
|
type ServerBillService struct {
|
||||||
|
BaseService
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountAllServerBills 查询服务账单数量
|
||||||
|
func (this *ServerBillService) CountAllServerBills(ctx context.Context, req *pb.CountAllServerBillsRequest) (*pb.RPCCountResponse, error) {
|
||||||
|
_, userId, err := this.ValidateAdminAndUser(ctx, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if userId > 0 {
|
||||||
|
req.UserId = userId
|
||||||
|
}
|
||||||
|
|
||||||
|
var tx = this.NullTx()
|
||||||
|
count, err := models.SharedServerBillDAO.CountServerBills(tx, req.UserId, req.Month)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return this.SuccessCount(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListServerBills 查询服务账单列表
|
||||||
|
func (this *ServerBillService) ListServerBills(ctx context.Context, req *pb.ListServerBillsRequest) (*pb.ListServerBillsResponse, error) {
|
||||||
|
_, userId, err := this.ValidateAdminAndUser(ctx, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if userId > 0 {
|
||||||
|
req.UserId = userId
|
||||||
|
}
|
||||||
|
|
||||||
|
var tx = this.NullTx()
|
||||||
|
serverBills, err := models.SharedServerBillDAO.ListServerBills(tx, req.UserId, req.Month, req.Offset, req.Size)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pbServerBills = []*pb.ServerBill{}
|
||||||
|
var cacheMap = utils.NewCacheMap()
|
||||||
|
for _, bill := range serverBills {
|
||||||
|
// user
|
||||||
|
user, err := models.SharedUserDAO.FindBasicUserWithoutState(tx, int64(bill.UserId))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var pbUser = &pb.User{Id: int64(bill.UserId)}
|
||||||
|
if user != nil {
|
||||||
|
pbUser = &pb.User{
|
||||||
|
Id: int64(bill.UserId),
|
||||||
|
Username: user.Username,
|
||||||
|
Fullname: user.Fullname,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// plan
|
||||||
|
var pbPlan *pb.Plan
|
||||||
|
if bill.PlanId > 0 {
|
||||||
|
plan, err := models.SharedPlanDAO.FindEnabledPlan(tx, int64(bill.PlanId))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if plan != nil {
|
||||||
|
pbPlan = &pb.Plan{
|
||||||
|
Id: int64(plan.Id),
|
||||||
|
Name: plan.Name,
|
||||||
|
PriceType: plan.PriceType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// user plan
|
||||||
|
var pbUserPlan *pb.UserPlan
|
||||||
|
if bill.UserPlanId > 0 {
|
||||||
|
userPlan, err := models.SharedUserPlanDAO.FindEnabledUserPlan(tx, int64(bill.UserPlanId), cacheMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if userPlan != nil {
|
||||||
|
pbUserPlan = &pb.UserPlan{
|
||||||
|
Id: int64(userPlan.Id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// server
|
||||||
|
var pbServer *pb.Server
|
||||||
|
if bill.ServerId > 0 {
|
||||||
|
server, err := models.SharedServerDAO.FindEnabledServerBasic(tx, int64(bill.ServerId))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if server != nil {
|
||||||
|
pbServer = &pb.Server{Id: int64(bill.ServerId), Name: server.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pbServerBills = append(pbServerBills, &pb.ServerBill{
|
||||||
|
Id: int64(bill.Id),
|
||||||
|
UserId: int64(bill.UserId),
|
||||||
|
ServerId: int64(bill.ServerId),
|
||||||
|
Amount: float32(bill.Amount),
|
||||||
|
CreatedAt: int64(bill.CreatedAt),
|
||||||
|
UserPlanId: int64(bill.UserPlanId),
|
||||||
|
PlanId: int64(bill.PlanId),
|
||||||
|
TotalTrafficBytes: int64(bill.TotalTrafficBytes),
|
||||||
|
BandwidthPercentileBytes: int64(bill.BandwidthPercentileBytes),
|
||||||
|
BandwidthPercentile: int32(bill.BandwidthPercentile),
|
||||||
|
User: pbUser,
|
||||||
|
Plan: pbPlan,
|
||||||
|
UserPlan: pbUserPlan,
|
||||||
|
Server: pbServer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pb.ListServerBillsResponse{ServerBills: pbServerBills}, nil
|
||||||
|
}
|
||||||
@@ -406,7 +406,7 @@ func (this *UserService) ComposeUserDashboard(ctx context.Context, req *pb.Compo
|
|||||||
|
|
||||||
// 本月总流量
|
// 本月总流量
|
||||||
month := timeutil.Format("Ym")
|
month := timeutil.Format("Ym")
|
||||||
monthlyTrafficBytes, err := models.SharedServerDailyStatDAO.SumUserMonthly(tx, req.UserId, 0, month)
|
monthlyTrafficBytes, err := models.SharedServerDailyStatDAO.SumUserMonthly(tx, req.UserId, month)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (this *UserBillService) ListUserBills(ctx context.Context, req *pb.ListUser
|
|||||||
}
|
}
|
||||||
result := []*pb.UserBill{}
|
result := []*pb.UserBill{}
|
||||||
for _, bill := range bills {
|
for _, bill := range bills {
|
||||||
user, err := models.SharedUserDAO.FindEnabledBasicUser(tx, int64(bill.UserId))
|
user, err := models.SharedUserDAO.FindBasicUserWithoutState(tx, int64(bill.UserId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -88,12 +88,14 @@ func (this *UserBillService) ListUserBills(ctx context.Context, req *pb.ListUser
|
|||||||
Id: int64(bill.UserId),
|
Id: int64(bill.UserId),
|
||||||
Fullname: user.Fullname,
|
Fullname: user.Fullname,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
|
IsDeleted: user.State == models.UserStateDisabled,
|
||||||
},
|
},
|
||||||
Type: bill.Type,
|
Type: bill.Type,
|
||||||
TypeName: models.SharedUserBillDAO.BillTypeName(bill.Type),
|
TypeName: models.SharedUserBillDAO.BillTypeName(bill.Type),
|
||||||
Description: bill.Description,
|
Description: bill.Description,
|
||||||
Amount: float32(bill.Amount),
|
Amount: float32(bill.Amount),
|
||||||
Month: bill.Month,
|
Month: bill.Month,
|
||||||
|
CanPay: bill.CanPay == 1,
|
||||||
IsPaid: bill.IsPaid == 1,
|
IsPaid: bill.IsPaid == 1,
|
||||||
PaidAt: int64(bill.PaidAt),
|
PaidAt: int64(bill.PaidAt),
|
||||||
Code: bill.Code,
|
Code: bill.Code,
|
||||||
@@ -151,6 +153,7 @@ func (this *UserBillService) FindUserBill(ctx context.Context, req *pb.FindUserB
|
|||||||
Description: bill.Description,
|
Description: bill.Description,
|
||||||
Amount: float32(bill.Amount),
|
Amount: float32(bill.Amount),
|
||||||
Month: bill.Month,
|
Month: bill.Month,
|
||||||
|
CanPay: bill.CanPay == 1,
|
||||||
IsPaid: bill.IsPaid == 1,
|
IsPaid: bill.IsPaid == 1,
|
||||||
PaidAt: int64(bill.PaidAt),
|
PaidAt: int64(bill.PaidAt),
|
||||||
Code: bill.Code,
|
Code: bill.Code,
|
||||||
@@ -194,6 +197,10 @@ func (this *UserBillService) PayUserBill(ctx context.Context, req *pb.PayUserBil
|
|||||||
return models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, req.UserBillId, true)
|
return models.SharedUserBillDAO.UpdateUserBillIsPaid(tx, req.UserBillId, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bill.CanPay == 0 {
|
||||||
|
return errors.New("can not pay now")
|
||||||
|
}
|
||||||
|
|
||||||
// 余额是否足够
|
// 余额是否足够
|
||||||
account, err := accounts.SharedUserAccountDAO.FindUserAccountWithUserId(tx, userId)
|
account, err := accounts.SharedUserAccountDAO.FindUserAccountWithUserId(tx, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -65,6 +65,9 @@ var upgradeFuncs = []*upgradeVersion{
|
|||||||
{
|
{
|
||||||
"0.4.0", upgradeV0_4_0,
|
"0.4.0", upgradeV0_4_0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"0.4.1", upgradeV0_4_1,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpgradeSQLData 升级SQL数据
|
// UpgradeSQLData 升级SQL数据
|
||||||
@@ -569,3 +572,14 @@ func upgradeV0_4_0(db *dbs.DB) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v0.4.1
|
||||||
|
func upgradeV0_4_1(db *dbs.DB) error {
|
||||||
|
// 升级 servers.lastUserPlanId
|
||||||
|
_, err := db.Exec("UPDATE edgeServers SET lastUserPlanId=userPlanId WHERE userPlanId>0")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ func TestUpgradeSQLData_v0_3_7(t *testing.T) {
|
|||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestUpgradeSQLData_v0_4_0(t *testing.T) {
|
func TestUpgradeSQLData_v0_4_0(t *testing.T) {
|
||||||
db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{
|
db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{
|
||||||
Driver: "mysql",
|
Driver: "mysql",
|
||||||
@@ -101,3 +100,19 @@ func TestUpgradeSQLData_v0_4_0(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpgradeSQLData_v0_4_1(t *testing.T) {
|
||||||
|
db, err := dbs.NewInstanceFromConfig(&dbs.DBConfig{
|
||||||
|
Driver: "mysql",
|
||||||
|
Dsn: "root:123456@tcp(127.0.0.1:3306)/db_edge?charset=utf8mb4&timeout=30s",
|
||||||
|
Prefix: "edge",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = upgradeV0_4_1(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("ok")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user