This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type NodeClusterTrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedNodeClusterTrafficDailyStatDAO.CleanDefaultDays(nil, 30) // 只保留N天
if err != nil {
remotelogs.Error("NodeClusterTrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewNodeClusterTrafficDailyStatDAO() *NodeClusterTrafficDailyStatDAO {
return dbs.NewDAO(&NodeClusterTrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNodeClusterTrafficDailyStats",
Model: new(NodeClusterTrafficDailyStat),
PkName: "id",
},
}).(*NodeClusterTrafficDailyStatDAO)
}
var SharedNodeClusterTrafficDailyStatDAO *NodeClusterTrafficDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedNodeClusterTrafficDailyStatDAO = NewNodeClusterTrafficDailyStatDAO()
})
}
// IncreaseDailyStat 增加统计数据
func (this *NodeClusterTrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, clusterId int64, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindDailyStats 获取日期之间统计
func (this *NodeClusterTrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, clusterId int64, dayFrom string, dayTo string) (result []*NodeClusterTrafficDailyStat, err error) {
ones, err := this.Query(tx).
Attr("clusterId", clusterId).
Between("day", dayFrom, dayTo).
FindAll()
if err != nil {
return nil, err
}
dayMap := map[string]*NodeClusterTrafficDailyStat{} // day => Stat
for _, one := range ones {
stat := one.(*NodeClusterTrafficDailyStat)
dayMap[stat.Day] = stat
}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := dayMap[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeClusterTrafficDailyStat{Day: day})
}
}
return result, nil
}
// SumDailyStat 计算当月总流量
func (this *NodeClusterTrafficDailyStatDAO) SumDailyStat(tx *dbs.Tx, clusterId int64, dayFrom string, dayTo string) (*NodeClusterTrafficDailyStat, error) {
one, err := 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").
Attr("clusterId", clusterId).
Between("day", dayFrom, dayTo).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*NodeClusterTrafficDailyStat), nil
}
// CleanDays 清理历史数据
func (this *NodeClusterTrafficDailyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}
func (this *NodeClusterTrafficDailyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.NodeClusterTrafficDailyStat.Clean.Days > 0 {
defaultDays = databaseConfig.NodeClusterTrafficDailyStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 30
}
return this.CleanDays(tx, defaultDays)
}

View File

@@ -0,0 +1,5 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
)

View File

@@ -0,0 +1,30 @@
package stats
// NodeClusterTrafficDailyStat 总的流量统计(按天)
type NodeClusterTrafficDailyStat struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
Day string `field:"day"` // YYYYMMDD
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type NodeClusterTrafficDailyStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
Day interface{} // YYYYMMDD
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
}
func NewNodeClusterTrafficDailyStatOperator() *NodeClusterTrafficDailyStatOperator {
return &NodeClusterTrafficDailyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,224 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type NodeTrafficHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedNodeTrafficHourlyStatDAO.CleanDefaultDays(nil, 15) // 只保留N天
if err != nil {
remotelogs.Error("NodeTrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewNodeTrafficHourlyStatDAO() *NodeTrafficHourlyStatDAO {
return dbs.NewDAO(&NodeTrafficHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeNodeTrafficHourlyStats",
Model: new(NodeTrafficHourlyStat),
PkName: "id",
},
}).(*NodeTrafficHourlyStatDAO)
}
var SharedNodeTrafficHourlyStatDAO *NodeTrafficHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedNodeTrafficHourlyStatDAO = NewNodeTrafficHourlyStatDAO()
})
}
// IncreaseHourlyStat 增加统计数据
func (this *NodeTrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, role string, nodeId int64, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"role": role,
"nodeId": nodeId,
"hour": hour,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindHourlyStatsWithClusterId 获取小时之间统计
func (this *NodeTrafficHourlyStatDAO) FindHourlyStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
ones, err := this.Query(tx).
Attr("clusterId", clusterId).
Between("hour", hourFrom, hourTo).
Result("hour, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("hour").
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*NodeTrafficHourlyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*NodeTrafficHourlyStat)
hourMap[stat.Hour] = stat
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := hourMap[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeTrafficHourlyStat{Hour: hour})
}
}
return result, nil
}
// FindTopNodeStats 取得一定时间内的节点排行数据
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStats(tx *dbs.Tx, role string, hourFrom string, hourTo string, size int64) (result []*NodeTrafficHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("role", role).
Between("hour", hourFrom, hourTo).
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("nodeId").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindTopNodeStatsWithAttack 取得防火墙相关的节点排行数据
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStatsWithAttack(tx *dbs.Tx, role string, hourFrom string, hourTo string, size int64) (result []*NodeTrafficHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("role", role).
Gt("countAttackRequests", 0).
Between("hour", hourFrom, hourTo).
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("nodeId").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindTopNodeStatsWithClusterId 取得集群一定时间内的节点排行数据
func (this *NodeTrafficHourlyStatDAO) FindTopNodeStatsWithClusterId(tx *dbs.Tx, role string, clusterId int64, hourFrom string, hourTo string, size int64) (result []*NodeTrafficHourlyStat, err error) {
// TODO 节点如果已经被删除,则忽略
_, err = this.Query(tx).
Attr("role", role).
Attr("clusterId", clusterId).
Between("hour", hourFrom, hourTo).
Result("nodeId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("nodeId").
Desc("countRequests").
Limit(size).
Slice(&result).
FindAll()
return
}
// FindHourlyStatsWithNodeId 获取节点小时之间统计
func (this *NodeTrafficHourlyStatDAO) FindHourlyStatsWithNodeId(tx *dbs.Tx, role string, nodeId int64, hourFrom string, hourTo string) (result []*NodeTrafficHourlyStat, err error) {
ones, err := this.Query(tx).
Attr("role", role).
Attr("nodeId", nodeId).
Between("hour", hourFrom, hourTo).
Result("hour, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("hour").
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*NodeTrafficHourlyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*NodeTrafficHourlyStat)
hourMap[stat.Hour] = stat
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := hourMap[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &NodeTrafficHourlyStat{Hour: hour})
}
}
return result, nil
}
// CleanDays 清理历史数据
func (this *NodeTrafficHourlyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}
func (this *NodeTrafficHourlyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.NodeTrafficHourlyStat.Clean.Days > 0 {
defaultDays = databaseConfig.NodeTrafficHourlyStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 15
}
return this.CleanDays(tx, defaultDays)
}

View File

@@ -0,0 +1,6 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,34 @@
package stats
// NodeTrafficHourlyStat 总的流量统计(按天)
type NodeTrafficHourlyStat struct {
Id uint64 `field:"id"` // ID
Role string `field:"role"` // 节点角色
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 集群ID
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存的请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type NodeTrafficHourlyStatOperator struct {
Id interface{} // ID
Role interface{} // 节点角色
ClusterId interface{} // 集群ID
NodeId interface{} // 集群ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存的请求数
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
}
func NewNodeTrafficHourlyStatOperator() *NodeTrafficHourlyStatOperator {
return &NodeTrafficHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,95 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerClientBrowserMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("ServerClientBrowserMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerClientBrowserMonthlyStatDAO dbs.DAO
func NewServerClientBrowserMonthlyStatDAO() *ServerClientBrowserMonthlyStatDAO {
return dbs.NewDAO(&ServerClientBrowserMonthlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerClientBrowserMonthlyStats",
Model: new(ServerClientBrowserMonthlyStat),
PkName: "id",
},
}).(*ServerClientBrowserMonthlyStatDAO)
}
var SharedServerClientBrowserMonthlyStatDAO *ServerClientBrowserMonthlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerClientBrowserMonthlyStatDAO = NewServerClientBrowserMonthlyStatDAO()
})
}
// IncreaseMonthlyCount 增加数量
func (this *ServerClientBrowserMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, browserId int64, version string, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"browserId": browserId,
"version": version,
"month": month,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// ListStats 查找单页数据
func (this *ServerClientBrowserMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerClientBrowserMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("month", month).
Offset(offset).
Limit(size).
Slice(&result).
Desc("count")
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerClientBrowserMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -0,0 +1,28 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"testing"
)
func TestServerClientBrowserMonthlyStatDAO_IncreaseMonthlyCount(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := SharedServerClientBrowserMonthlyStatDAO.IncreaseMonthlyCount(tx, 1, 1, "1.0", "202101", 1)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}
func TestServerClientBrowserMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerClientBrowserMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,24 @@
package stats
// 浏览器统计(按月)
type ServerClientBrowserMonthlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
BrowserId uint32 `field:"browserId"` // 浏览器ID
Month string `field:"month"` // YYYYMM
Version string `field:"version"` // 主版本号
Count uint64 `field:"count"` // 数量
}
type ServerClientBrowserMonthlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
BrowserId interface{} // 浏览器ID
Month interface{} // YYYYMM
Version interface{} // 主版本号
Count interface{} // 数量
}
func NewServerClientBrowserMonthlyStatOperator() *ServerClientBrowserMonthlyStatOperator {
return &ServerClientBrowserMonthlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,95 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerClientSystemMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("ServerClientSystemMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerClientSystemMonthlyStatDAO dbs.DAO
func NewServerClientSystemMonthlyStatDAO() *ServerClientSystemMonthlyStatDAO {
return dbs.NewDAO(&ServerClientSystemMonthlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerClientSystemMonthlyStats",
Model: new(ServerClientSystemMonthlyStat),
PkName: "id",
},
}).(*ServerClientSystemMonthlyStatDAO)
}
var SharedServerClientSystemMonthlyStatDAO *ServerClientSystemMonthlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerClientSystemMonthlyStatDAO = NewServerClientSystemMonthlyStatDAO()
})
}
// IncreaseMonthlyCount 增加数量
func (this *ServerClientSystemMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, systemId int64, version string, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"systemId": systemId,
"version": version,
"month": month,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// ListStats 查找单页数据
func (this *ServerClientSystemMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerClientSystemMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("month", month).
Offset(offset).
Limit(size).
Slice(&result).
Desc("count")
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerClientSystemMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -0,0 +1,15 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestServerClientSystemMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerClientSystemMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,24 @@
package stats
// 操作系统统计(按月)
type ServerClientSystemMonthlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
SystemId uint32 `field:"systemId"` // 系统ID
Version string `field:"version"` // 主版本号
Month string `field:"month"` // YYYYMM
Count uint64 `field:"count"` // 数量
}
type ServerClientSystemMonthlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
SystemId interface{} // 系统ID
Version interface{} // 主版本号
Month interface{} // YYYYMM
Count interface{} // 数量
}
func NewServerClientSystemMonthlyStatOperator() *ServerClientSystemMonthlyStatOperator {
return &ServerClientSystemMonthlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,399 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"sort"
"strings"
"sync"
"time"
)
type ServerDomainHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerDomainHourlyStatDAO.CleanDefaultDays(nil, 7) // 只保留 N 天
if err != nil {
remotelogs.Error("ServerDomainHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewServerDomainHourlyStatDAO() *ServerDomainHourlyStatDAO {
return dbs.NewDAO(&ServerDomainHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerDomainHourlyStats",
Model: new(ServerDomainHourlyStat),
PkName: "id",
},
}).(*ServerDomainHourlyStatDAO)
}
var SharedServerDomainHourlyStatDAO *ServerDomainHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerDomainHourlyStatDAO = NewServerDomainHourlyStatDAO()
})
}
// PartitionTable 获取分区表格名称
func (this *ServerDomainHourlyStatDAO) PartitionTable(domain string) string {
if len(domain) == 0 {
return this.Table + "_0"
}
if (domain[0] >= '0' && domain[0] <= '9') || (domain[0] >= 'a' && domain[0] <= 'z') || (domain[0] >= 'A' && domain[0] <= 'Z') {
return this.Table + "_" + strings.ToLower(string(domain[0]))
}
return this.Table + "_0"
}
// FindAllPartitionTables 获取所有表格名称
func (this *ServerDomainHourlyStatDAO) FindAllPartitionTables() []string {
var tables = []string{}
for i := '0'; i <= '9'; i++ {
tables = append(tables, this.Table+"_"+string(i))
}
for i := 'a'; i <= 'z'; i++ {
tables = append(tables, this.Table+"_"+string(i))
}
return tables
}
// IncreaseHourlyStat 增加统计数据
func (this *ServerDomainHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, clusterId int64, nodeId int64, serverId int64, domain string, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
if len(domain) == 0 || len(domain) > 64 {
return nil
}
err := this.Query(tx).
Table(this.PartitionTable(domain)).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"clusterId": clusterId,
"nodeId": nodeId,
"serverId": serverId,
"hour": hour,
"domain": domain,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindTopDomainStats 取得一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStats(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
var tables = this.FindAllPartitionTables()
var wg = sync.WaitGroup{}
wg.Add(len(tables))
var locker = sync.Mutex{}
for _, table := range tables {
go func(table string) {
defer wg.Done()
var topResults = []*ServerDomainHourlyStat{}
// TODO 节点如果已经被删除,则忽略
_, err := this.Query(tx).
Table(table).
Between("hour", hourFrom, hourTo).
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&topResults).
FindAll()
if err != nil {
resultErr = err
return
}
if len(topResults) > 0 {
locker.Lock()
result = append(result, topResults...)
locker.Unlock()
}
}(table)
}
wg.Wait()
sort.Slice(result, func(i, j int) bool {
return result[i].CountRequests > result[j].CountRequests
})
if len(result) > types.Int(size) {
result = result[:types.Int(size)]
}
return
}
// FindTopDomainStatsWithAttack 取得一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithAttack(tx *dbs.Tx, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
var tables = this.FindAllPartitionTables()
var wg = sync.WaitGroup{}
wg.Add(len(tables))
var locker = sync.Mutex{}
for _, table := range tables {
go func(table string) {
defer wg.Done()
var topResults = []*ServerDomainHourlyStat{}
// TODO 节点如果已经被删除,则忽略
_, err := this.Query(tx).
Table(table).
Gt("countAttackRequests", 0).
Between("hour", hourFrom, hourTo).
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&topResults).
FindAll()
if err != nil {
resultErr = err
return
}
if len(topResults) > 0 {
locker.Lock()
result = append(result, topResults...)
locker.Unlock()
}
}(table)
}
wg.Wait()
sort.Slice(result, func(i, j int) bool {
return result[i].CountRequests > result[j].CountRequests
})
if len(result) > types.Int(size) {
result = result[:types.Int(size)]
}
return
}
// FindTopDomainStatsWithClusterId 取得集群上的一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithClusterId(tx *dbs.Tx, clusterId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
var tables = this.FindAllPartitionTables()
var wg = sync.WaitGroup{}
wg.Add(len(tables))
var locker = sync.Mutex{}
for _, table := range tables {
go func(table string) {
defer wg.Done()
var topResults = []*ServerDomainHourlyStat{}
// TODO 节点如果已经被删除,则忽略
_, err := this.Query(tx).
Table(table).
Attr("clusterId", clusterId).
Between("hour", hourFrom, hourTo).
UseIndex("hour").
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&topResults).
FindAll()
if err != nil {
resultErr = err
return
}
if len(topResults) > 0 {
locker.Lock()
result = append(result, topResults...)
locker.Unlock()
}
}(table)
}
wg.Wait()
sort.Slice(result, func(i, j int) bool {
return result[i].CountRequests > result[j].CountRequests
})
if len(result) > types.Int(size) {
result = result[:types.Int(size)]
}
return
}
// FindTopDomainStatsWithNodeId 取得节点上的一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithNodeId(tx *dbs.Tx, nodeId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
var tables = this.FindAllPartitionTables()
var wg = sync.WaitGroup{}
wg.Add(len(tables))
var locker = sync.Mutex{}
for _, table := range tables {
go func(table string) {
defer wg.Done()
var topResults = []*ServerDomainHourlyStat{}
// TODO 节点如果已经被删除,则忽略
_, err := this.Query(tx).
Table(table).
Attr("nodeId", nodeId).
Between("hour", hourFrom, hourTo).
UseIndex("hour").
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&topResults).
FindAll()
if err != nil {
resultErr = err
return
}
if len(topResults) > 0 {
locker.Lock()
result = append(result, topResults...)
locker.Unlock()
}
}(table)
}
wg.Wait()
sort.Slice(result, func(i, j int) bool {
return result[i].CountRequests > result[j].CountRequests
})
if len(result) > types.Int(size) {
result = result[:types.Int(size)]
}
return
}
// FindTopDomainStatsWithServerId 取得某个服务的一定时间内的域名排行数据
func (this *ServerDomainHourlyStatDAO) FindTopDomainStatsWithServerId(tx *dbs.Tx, serverId int64, hourFrom string, hourTo string, size int64) (result []*ServerDomainHourlyStat, resultErr error) {
var tables = this.FindAllPartitionTables()
var wg = sync.WaitGroup{}
wg.Add(len(tables))
var locker = sync.Mutex{}
for _, table := range tables {
go func(table string) {
defer wg.Done()
var topResults = []*ServerDomainHourlyStat{}
// TODO 节点如果已经被删除,则忽略
_, err := this.Query(tx).
Table(table).
Attr("serverId", serverId).
Between("hour", hourFrom, hourTo).
UseIndex("serverId", "hour").
Result("domain, MIN(serverId) AS serverId, SUM(bytes) AS bytes, SUM(cachedBytes) AS cachedBytes, SUM(countRequests) AS countRequests, SUM(countCachedRequests) AS countCachedRequests, SUM(countAttackRequests) AS countAttackRequests, SUM(attackBytes) AS attackBytes").
Group("domain").
Desc("countRequests").
Limit(size).
Slice(&topResults).
FindAll()
if err != nil {
resultErr = err
return
}
if len(topResults) > 0 {
locker.Lock()
result = append(result, topResults...)
locker.Unlock()
}
}(table)
}
wg.Wait()
sort.Slice(result, func(i, j int) bool {
return result[i].CountRequests > result[j].CountRequests
})
if len(result) > types.Int(size) {
result = result[:types.Int(size)]
}
return
}
// CleanDays 清理历史数据
func (this *ServerDomainHourlyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
for _, table := range this.FindAllPartitionTables() {
_, err := this.Query(tx).
Table(table).
Lt("hour", hour).
Delete()
if err != nil {
return err
}
}
return nil
}
func (this *ServerDomainHourlyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.ServerDomainHourlyStat.Clean.Days > 0 {
defaultDays = databaseConfig.ServerDomainHourlyStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 7
}
return this.CleanDays(tx, defaultDays)
}

View File

@@ -0,0 +1,78 @@
package stats
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/assert"
_ "github.com/iwind/TeaGo/bootstrap"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
"time"
)
func TestServerDomainHourlyStatDAO_PartitionTable(t *testing.T) {
var a = assert.NewAssertion(t)
var dao = NewServerDomainHourlyStatDAO()
a.IsTrue(dao.PartitionTable("") == "edgeServerDomainHourlyStats_0")
a.IsTrue(dao.PartitionTable("a1") == "edgeServerDomainHourlyStats_a")
a.IsTrue(dao.PartitionTable("Y1") == "edgeServerDomainHourlyStats_y")
a.IsTrue(dao.PartitionTable("z1") == "edgeServerDomainHourlyStats_z")
a.IsTrue(dao.PartitionTable("A1") == "edgeServerDomainHourlyStats_a")
a.IsTrue(dao.PartitionTable("Z1") == "edgeServerDomainHourlyStats_z")
a.IsTrue(dao.PartitionTable("中国") == "edgeServerDomainHourlyStats_0")
a.IsTrue(dao.PartitionTable("_") == "edgeServerDomainHourlyStats_0")
a.IsTrue(dao.PartitionTable(" ") == "edgeServerDomainHourlyStats_0")
}
func TestServerDomainHourlyStatDAO_FindAllPartitionTables(t *testing.T) {
var dao = NewServerDomainHourlyStatDAO()
t.Log(dao.FindAllPartitionTables())
}
func TestServerDomainHourlyStatDAO_InsertManyHourlyStat(t *testing.T) {
dbs.NotifyReady()
var count = 1
for i := 0; i < count; i++ {
var f = string([]rune{int32(rands.Int('0', '9'))})
if i%30 > 0 {
f = string([]rune{int32(rands.Int('a', 'z'))})
}
err := NewServerDomainHourlyStatDAO().IncreaseHourlyStat(nil, 18, 48, 23, f+"rand"+types.String(i%500_000)+".com", timeutil.Format("Ymd")+fmt.Sprintf("%02d", rands.Int(0, 23)), 1, 1, 1, 1, 1, 1)
if err != nil {
t.Fatal(err)
}
if i%10000 == 0 {
t.Log(i)
}
}
}
func TestServerDomainHourlyStatDAO_FindTopDomainStats(t *testing.T) {
var dao = NewServerDomainHourlyStatDAO()
var before = time.Now()
defer func() {
t.Log(time.Since(before).Seconds()*1000, "ms")
}()
stats, err := dao.FindTopDomainStats(nil, timeutil.Format("Ymd00"), timeutil.Format("Ymd23"), 10)
if err != nil {
t.Fatal(err)
}
for _, stat := range stats {
t.Log(stat.Domain, stat.CountRequests)
}
}
func TestServerDomainHourlyStatDAO_Clean(t *testing.T) {
var dao = NewServerDomainHourlyStatDAO()
err := dao.CleanDays(nil, 10)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,36 @@
package stats
// ServerDomainHourlyStat 服务域名统计
type ServerDomainHourlyStat struct {
Id uint64 `field:"id"` // ID
ClusterId uint32 `field:"clusterId"` // 集群ID
NodeId uint32 `field:"nodeId"` // 节点ID
ServerId uint32 `field:"serverId"` // 服务ID
Domain string `field:"domain"` // 域名
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击请求数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type ServerDomainHourlyStatOperator struct {
Id interface{} // ID
ClusterId interface{} // 集群ID
NodeId interface{} // 节点ID
ServerId interface{} // 服务ID
Domain interface{} // 域名
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求
CountAttackRequests interface{} // 攻击请求数
AttackBytes interface{} // 攻击流量
}
func NewServerDomainHourlyStatOperator() *ServerDomainHourlyStatOperator {
return &ServerDomainHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,139 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type ServerHTTPFirewallDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerHTTPFirewallDailyStatDAO.Clean(nil, 30) // 只保留N天
if err != nil {
remotelogs.Error("ServerHTTPFirewallDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewServerHTTPFirewallDailyStatDAO() *ServerHTTPFirewallDailyStatDAO {
return dbs.NewDAO(&ServerHTTPFirewallDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerHTTPFirewallDailyStats",
Model: new(ServerHTTPFirewallDailyStat),
PkName: "id",
},
}).(*ServerHTTPFirewallDailyStatDAO)
}
var SharedServerHTTPFirewallDailyStatDAO *ServerHTTPFirewallDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerHTTPFirewallDailyStatDAO = NewServerHTTPFirewallDailyStatDAO()
})
}
// IncreaseDailyCount 增加数量
func (this *ServerHTTPFirewallDailyStatDAO) IncreaseDailyCount(tx *dbs.Tx, serverId int64, firewallRuleGroupId int64, action string, day string, count int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"day": day,
"httpFirewallRuleGroupId": firewallRuleGroupId,
"action": action,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// SumDailyCount 计算某天的数据
func (this *ServerHTTPFirewallDailyStatDAO) SumDailyCount(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (int64, error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
if len(action) > 0 {
query.Attr("action", action)
}
return query.SumInt64("count", 0)
}
// GroupDailyCount 查询规则分组数量
func (this *ServerHTTPFirewallDailyStatDAO) GroupDailyCount(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string, offset int64, size int64) (result []*ServerHTTPFirewallDailyStat, err error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
_, err = query.Group("httpFirewallRuleGroupId").
Result("httpFirewallRuleGroupId, SUM(count) AS count").
Desc("count").
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// FindDailyStats 查询某个日期段内的记录
func (this *ServerHTTPFirewallDailyStatDAO) FindDailyStats(tx *dbs.Tx, userId int64, serverId int64, actions []string, dayFrom string, dayTo string) (result []*ServerHTTPFirewallDailyStat, err error) {
if len(actions) == 0 {
return nil, nil
}
query := this.Query(tx).
Between("day", dayFrom, dayTo).
Attr("action", actions)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
_, err = query.Group("day").
Result("day, SUM(count) AS count").
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerHTTPFirewallDailyStatDAO) Clean(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}

View File

@@ -0,0 +1,6 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,24 @@
package stats
// WAF统计
type ServerHTTPFirewallDailyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
Day string `field:"day"` // YYYYMMDD
HttpFirewallRuleGroupId uint32 `field:"httpFirewallRuleGroupId"` // WAF分组ID
Action string `field:"action"` // 采取的动作
Count uint64 `field:"count"` // 数量
}
type ServerHTTPFirewallDailyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
Day interface{} // YYYYMMDD
HttpFirewallRuleGroupId interface{} // WAF分组ID
Action interface{} // 采取的动作
Count interface{} // 数量
}
func NewServerHTTPFirewallDailyStatOperator() *ServerHTTPFirewallDailyStatOperator {
return &ServerHTTPFirewallDailyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,137 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type ServerHTTPFirewallHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerHTTPFirewallHourlyStatDAO.Clean(nil, 15) // 只保留N天
if err != nil {
remotelogs.Error("ServerHTTPFirewallHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewServerHTTPFirewallHourlyStatDAO() *ServerHTTPFirewallHourlyStatDAO {
return dbs.NewDAO(&ServerHTTPFirewallHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerHTTPFirewallHourlyStats",
Model: new(ServerHTTPFirewallHourlyStat),
PkName: "id",
},
}).(*ServerHTTPFirewallHourlyStatDAO)
}
var SharedServerHTTPFirewallHourlyStatDAO *ServerHTTPFirewallHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerHTTPFirewallHourlyStatDAO = NewServerHTTPFirewallHourlyStatDAO()
})
}
// IncreaseHourlyCount 增加数量
func (this *ServerHTTPFirewallHourlyStatDAO) IncreaseHourlyCount(tx *dbs.Tx, serverId int64, firewallRuleGroupId int64, action string, hour string, count int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"day": hour[:8],
"hour": hour,
"httpFirewallRuleGroupId": firewallRuleGroupId,
"action": action,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// SumHourlyCount 计算某天的数据
func (this *ServerHTTPFirewallHourlyStatDAO) SumHourlyCount(tx *dbs.Tx, userId int64, serverId int64, action string, dayFrom string, dayTo string) (int64, error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
if len(action) > 0 {
query.Attr("action", action)
}
return query.SumInt64("count", 0)
}
// GroupHourlyCount 查询规则分组数量
func (this *ServerHTTPFirewallHourlyStatDAO) GroupHourlyCount(tx *dbs.Tx, userId int64, serverId int64, dayFrom string, dayTo string, offset int64, size int64) (result []*ServerHTTPFirewallHourlyStat, err error) {
query := this.Query(tx).
Between("day", dayFrom, dayTo)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
_, err = query.Group("httpFirewallRuleGroupId").
Result("httpFirewallRuleGroupId, SUM(count) AS count").
Desc("count").
Offset(offset).
Limit(size).
Slice(&result).
FindAll()
return
}
// FindHourlyStats 查询某个日期段内的记录
func (this *ServerHTTPFirewallHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, userId int64, serverId int64, action string, hourFrom string, hourTo string) (result []*ServerHTTPFirewallHourlyStat, err error) {
query := this.Query(tx).
Between("hour", hourFrom, hourTo).
Attr("action", action)
if serverId > 0 {
query.Attr("serverId", serverId)
} else if userId > 0 {
query.Where("serverId IN (SELECT id FROM "+models.SharedServerDAO.Table+" WHERE userId=:userId AND state=1)").
Param("userId", userId)
}
_, err = query.Group("hour").
Result("hour, SUM(count) AS count").
Slice(&result).
FindAll()
return
}
// Clean 清理历史数据
func (this *ServerHTTPFirewallHourlyStatDAO) Clean(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}

View File

@@ -0,0 +1,6 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
_ "github.com/iwind/TeaGo/bootstrap"
)

View File

@@ -0,0 +1,26 @@
package stats
// ServerHTTPFirewallHourlyStat WAF统计
type ServerHTTPFirewallHourlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
Day string `field:"day"` // YYYYMMDD
Hour string `field:"hour"` // YYYYMMDDHH
HttpFirewallRuleGroupId uint32 `field:"httpFirewallRuleGroupId"` // WAF分组ID
Action string `field:"action"` // 采取的动作
Count uint64 `field:"count"` // 数量
}
type ServerHTTPFirewallHourlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
Day interface{} // YYYYMMDD
Hour interface{} // YYYYMMDDHH
HttpFirewallRuleGroupId interface{} // WAF分组ID
Action interface{} // 采取的动作
Count interface{} // 数量
}
func NewServerHTTPFirewallHourlyStatOperator() *ServerHTTPFirewallHourlyStatOperator {
return &ServerHTTPFirewallHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,103 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionCityMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionCityMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionCityMonthlyStatDAO dbs.DAO
func NewServerRegionCityMonthlyStatDAO() *ServerRegionCityMonthlyStatDAO {
return dbs.NewDAO(&ServerRegionCityMonthlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerRegionCityMonthlyStats",
Model: new(ServerRegionCityMonthlyStat),
PkName: "id",
},
}).(*ServerRegionCityMonthlyStatDAO)
}
var SharedServerRegionCityMonthlyStatDAO *ServerRegionCityMonthlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerRegionCityMonthlyStatDAO = NewServerRegionCityMonthlyStatDAO()
})
}
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionCityMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, cityId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"cityId": cityId,
"month": month,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// ListStats 查找单页数据
func (this *ServerRegionCityMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, countryId int64, provinceId int64, offset int64, size int64) (result []*ServerRegionCityMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("month", month).
Offset(offset).
Limit(size).
Slice(&result).
Desc("count")
if countryId > 0 {
query.Where("cityId IN (SELECT id FROM "+regions.SharedRegionCityDAO.Table+" WHERE provinceId IN (SELECT id FROM "+regions.SharedRegionProvinceDAO.Table+" WHERE countryId=:countryId AND state=1) AND state=1)").
Param("countryId", countryId)
}
if provinceId > 0 {
query.Where("cityId IN (SELECT id FROM "+regions.SharedRegionCityDAO.Table+" WHERE provinceId=:provinceId AND state=1)").
Param("provinceId", provinceId)
}
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionCityMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -0,0 +1,15 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestServerRegionCityMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerRegionCityMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,22 @@
package stats
// 服务用户省份分布统计(按天)
type ServerRegionCityMonthlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
CityId uint32 `field:"cityId"` // 城市ID
Month string `field:"month"` // 月份YYYYMM
Count uint64 `field:"count"` // 数量
}
type ServerRegionCityMonthlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
CityId interface{} // 城市ID
Month interface{} // 月份YYYYMM
Count interface{} // 数量
}
func NewServerRegionCityMonthlyStatOperator() *ServerRegionCityMonthlyStatOperator {
return &ServerRegionCityMonthlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,173 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionCountryDailyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("ServerRegionCountryDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionCountryDailyStatDAO dbs.DAO
func NewServerRegionCountryDailyStatDAO() *ServerRegionCountryDailyStatDAO {
return dbs.NewDAO(&ServerRegionCountryDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerRegionCountryDailyStats",
Model: new(ServerRegionCountryDailyStat),
PkName: "id",
},
}).(*ServerRegionCountryDailyStatDAO)
}
var SharedServerRegionCountryDailyStatDAO *ServerRegionCountryDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerRegionCountryDailyStatDAO = NewServerRegionCountryDailyStatDAO()
})
}
// IncreaseDailyStat 增加统计
func (this *ServerRegionCountryDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, serverId int64, countryId int64, day string, bytes int64, countRequests int64, attackBytes int64, countAttackRequests int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("countRequests", countRequests).
Param("attackBytes", attackBytes).
Param("countAttackRequests", countAttackRequests).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"countryId": countryId,
"day": day,
"bytes": bytes,
"attackBytes": attackBytes,
"countRequests": countRequests,
"countAttackRequests": countAttackRequests,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
})
if err != nil {
return err
}
return nil
}
// ListServerStats 查找单页数据
func (this *ServerRegionCountryDailyStatDAO) ListServerStats(tx *dbs.Tx, serverId int64, day string, orderField string, offset int64, size int64) (result []*ServerRegionCountryDailyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("day", day).
Offset(offset).
Limit(size).
Slice(&result)
switch orderField {
case "bytes":
query.Desc("bytes")
case "countRequests":
query.Desc("countRequests")
case "attackBytes":
query.Desc("attackBytes")
query.Gt("attackBytes", 0)
case "countAttackRequests":
query.Desc("countAttackRequests")
query.Gt("countAttackRequests", 0)
}
_, err = query.FindAll()
return
}
// ListSumStats 查找总体数据
func (this *ServerRegionCountryDailyStatDAO) ListSumStats(tx *dbs.Tx, day string, orderField string, offset int64, size int64) (result []*ServerRegionCountryDailyStat, err error) {
query := this.Query(tx).
Attr("day", day).
Result("countryId", "SUM(bytes) AS bytes", "SUM(countRequests) AS countRequests", "SUM(attackBytes) AS attackBytes", "SUM(countAttackRequests) AS countAttackRequests").
Group("countryId").
Offset(offset).
Limit(size).
Slice(&result)
switch orderField {
case "bytes":
query.Desc("bytes")
case "countRequests":
query.Desc("countRequests")
case "attackBytes":
query.Desc("attackBytes")
query.Gt("attackBytes", 0)
case "countAttackRequests":
query.Desc("countAttackRequests")
query.Gt("countAttackRequests", 0)
}
_, err = query.FindAll()
return
}
// SumDailyTotalBytes 计算总流量
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalBytes(tx *dbs.Tx, day string) (int64, error) {
return this.Query(tx).
Attr("day", day).
SumInt64("bytes", 0)
}
// SumDailyTotalAttackRequests 计算总攻击次数
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalAttackRequests(tx *dbs.Tx, day string) (int64, error) {
return this.Query(tx).
Attr("day", day).
SumInt64("countAttackRequests", 0)
}
// SumDailyTotalBytesWithServerId 计算单个服务的总流量
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalBytesWithServerId(tx *dbs.Tx, day string, serverId int64) (int64, error) {
return this.Query(tx).
Attr("day", day).
Attr("serverId", serverId).
SumInt64("bytes", 0)
}
// SumDailyTotalAttackRequestsWithServerId 计算单个服务的总攻击次数
func (this *ServerRegionCountryDailyStatDAO) SumDailyTotalAttackRequestsWithServerId(tx *dbs.Tx, day string, serverId int64) (int64, error) {
return this.Query(tx).
Attr("day", day).
Attr("serverId", serverId).
SumInt64("countAttackRequests", 0)
}
// Clean 清理统计数据
func (this *ServerRegionCountryDailyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留7天的
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -7))
_, err := this.Query(tx).
Lte("day", day).
Delete()
return err
}

View File

@@ -0,0 +1,34 @@
package stats
import (
"encoding/json"
_ "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 TestServerRegionCountryDailyStatDAO_IncreaseDailyStat(t *testing.T) {
var tx *dbs.Tx
err := NewServerRegionCountryDailyStatDAO().IncreaseDailyStat(tx, 1, 3, timeutil.Format("Ymd"), 2, 2, 1, 1)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}
func TestServerRegionCountryDailyStatDAO_ListSumStats(t *testing.T) {
var tx *dbs.Tx
stats, err := NewServerRegionCountryDailyStatDAO().ListSumStats(tx, timeutil.Format("Ymd"), "countAttackRequests", 0, 10)
if err != nil {
t.Fatal(err)
}
for _, stat := range stats {
statJSON, err := json.Marshal(stat)
if err != nil {
t.Fatal(err)
}
t.Log(string(statJSON))
}
}

View File

@@ -0,0 +1,28 @@
package stats
// ServerRegionCountryDailyStat 服务用户区域分布统计(按天)
type ServerRegionCountryDailyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
CountryId uint32 `field:"countryId"` // 国家/区域ID
Day string `field:"day"` // 日期YYYYMMDD
CountRequests uint64 `field:"countRequests"` // 请求数量
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数量
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
Bytes uint64 `field:"bytes"` // 总流量
}
type ServerRegionCountryDailyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
CountryId interface{} // 国家/区域ID
Day interface{} // 日期YYYYMMDD
CountRequests interface{} // 请求数量
CountAttackRequests interface{} // 攻击数量
AttackBytes interface{} // 攻击流量
Bytes interface{} // 总流量
}
func NewServerRegionCountryDailyStatOperator() *ServerRegionCountryDailyStatOperator {
return &ServerRegionCountryDailyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,94 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionCountryMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionCountryMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionCountryMonthlyStatDAO dbs.DAO
func NewServerRegionCountryMonthlyStatDAO() *ServerRegionCountryMonthlyStatDAO {
return dbs.NewDAO(&ServerRegionCountryMonthlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerRegionCountryMonthlyStats",
Model: new(ServerRegionCountryMonthlyStat),
PkName: "id",
},
}).(*ServerRegionCountryMonthlyStatDAO)
}
var SharedServerRegionCountryMonthlyStatDAO *ServerRegionCountryMonthlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerRegionCountryMonthlyStatDAO = NewServerRegionCountryMonthlyStatDAO()
})
}
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionCountryMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, countryId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"countryId": countryId,
"month": month,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// ListStats 查找单页数据
func (this *ServerRegionCountryMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerRegionCountryMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("month", month).
Offset(offset).
Limit(size).
Slice(&result).
Desc("count")
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionCountryMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -0,0 +1,5 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
)

View File

@@ -0,0 +1,22 @@
package stats
// 服务用户区域分布统计(按天)
type ServerRegionCountryMonthlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
CountryId uint32 `field:"countryId"` // 国家/区域ID
Month string `field:"month"` // 月份YYYYMM
Count uint64 `field:"count"` // 数量
}
type ServerRegionCountryMonthlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
CountryId interface{} // 国家/区域ID
Month interface{} // 月份YYYYMM
Count interface{} // 数量
}
func NewServerRegionCountryMonthlyStatOperator() *ServerRegionCountryMonthlyStatOperator {
return &ServerRegionCountryMonthlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,94 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionProviderMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionProviderMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionProviderMonthlyStatDAO dbs.DAO
func NewServerRegionProviderMonthlyStatDAO() *ServerRegionProviderMonthlyStatDAO {
return dbs.NewDAO(&ServerRegionProviderMonthlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerRegionProviderMonthlyStats",
Model: new(ServerRegionProviderMonthlyStat),
PkName: "id",
},
}).(*ServerRegionProviderMonthlyStatDAO)
}
var SharedServerRegionProviderMonthlyStatDAO *ServerRegionProviderMonthlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerRegionProviderMonthlyStatDAO = NewServerRegionProviderMonthlyStatDAO()
})
}
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionProviderMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, providerId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"providerId": providerId,
"month": month,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// ListStats 查找单页数据
func (this *ServerRegionProviderMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, offset int64, size int64) (result []*ServerRegionProviderMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("month", month).
Offset(offset).
Limit(size).
Slice(&result).
Desc("count")
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionProviderMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -0,0 +1,15 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
"testing"
)
func TestServerRegionProviderMonthlyStatDAO_Clean(t *testing.T) {
var dao = NewServerRegionProviderMonthlyStatDAO()
err := dao.Clean(nil)
if err != nil {
t.Fatal(err)
}
t.Log("ok")
}

View File

@@ -0,0 +1,22 @@
package stats
// 服务用户省份分布统计(按天)
type ServerRegionProviderMonthlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
ProviderId uint32 `field:"providerId"` // 运营商ID
Month string `field:"month"` // 月份YYYYMM
Count uint64 `field:"count"` // 数量
}
type ServerRegionProviderMonthlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
ProviderId interface{} // 运营商ID
Month interface{} // 月份YYYYMM
Count interface{} // 数量
}
func NewServerRegionProviderMonthlyStatOperator() *ServerRegionProviderMonthlyStatOperator {
return &ServerRegionProviderMonthlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,100 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models/regions"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedServerRegionProvinceMonthlyStatDAO.Clean(nil)
if err != nil {
remotelogs.Error("SharedServerRegionProvinceMonthlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
type ServerRegionProvinceMonthlyStatDAO dbs.DAO
func NewServerRegionProvinceMonthlyStatDAO() *ServerRegionProvinceMonthlyStatDAO {
return dbs.NewDAO(&ServerRegionProvinceMonthlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeServerRegionProvinceMonthlyStats",
Model: new(ServerRegionProvinceMonthlyStat),
PkName: "id",
},
}).(*ServerRegionProvinceMonthlyStatDAO)
}
var SharedServerRegionProvinceMonthlyStatDAO *ServerRegionProvinceMonthlyStatDAO
func init() {
dbs.OnReady(func() {
SharedServerRegionProvinceMonthlyStatDAO = NewServerRegionProvinceMonthlyStatDAO()
})
}
// IncreaseMonthlyCount 增加数量
func (this *ServerRegionProvinceMonthlyStatDAO) IncreaseMonthlyCount(tx *dbs.Tx, serverId int64, provinceId int64, month string, count int64) error {
if len(month) != 6 {
return errors.New("invalid month '" + month + "'")
}
err := this.Query(tx).
Param("count", count).
InsertOrUpdateQuickly(maps.Map{
"serverId": serverId,
"provinceId": provinceId,
"month": month,
"count": count,
}, maps.Map{
"count": dbs.SQL("count+:count"),
})
if err != nil {
return err
}
return nil
}
// ListStats 查找单页数据
func (this *ServerRegionProvinceMonthlyStatDAO) ListStats(tx *dbs.Tx, serverId int64, month string, countryId int64, offset int64, size int64) (result []*ServerRegionProvinceMonthlyStat, err error) {
query := this.Query(tx).
Attr("serverId", serverId).
Attr("month", month).
Offset(offset).
Limit(size).
Slice(&result).
Desc("count")
if countryId > 0 {
query.Where("id IN (SELECT id FROM "+regions.SharedRegionProvinceDAO.Table+" WHERE countryId=:countryId AND state=1)").
Param("countryId", countryId)
}
_, err = query.FindAll()
return
}
// Clean 清理统计数据
func (this *ServerRegionProvinceMonthlyStatDAO) Clean(tx *dbs.Tx) error {
// 只保留两个月的
var month = timeutil.Format("Ym", time.Now().AddDate(0, -2, 0))
_, err := this.Query(tx).
Lte("month", month).
Delete()
return err
}

View File

@@ -0,0 +1,5 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
)

View File

@@ -0,0 +1,22 @@
package stats
// 服务用户省份分布统计(按天)
type ServerRegionProvinceMonthlyStat struct {
Id uint64 `field:"id"` // ID
ServerId uint32 `field:"serverId"` // 服务ID
ProvinceId uint32 `field:"provinceId"` // 省份ID
Month string `field:"month"` // 月份YYYYMM
Count uint64 `field:"count"` // 数量
}
type ServerRegionProvinceMonthlyStatOperator struct {
Id interface{} // ID
ServerId interface{} // 服务ID
ProvinceId interface{} // 省份ID
Month interface{} // 月份YYYYMM
Count interface{} // 数量
}
func NewServerRegionProvinceMonthlyStatOperator() *ServerRegionProvinceMonthlyStatOperator {
return &ServerRegionProvinceMonthlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,167 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type TrafficDailyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedTrafficDailyStatDAO.CleanDefaultDays(nil, 30) // 只保留N天
if err != nil {
remotelogs.Error("TrafficDailyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewTrafficDailyStatDAO() *TrafficDailyStatDAO {
return dbs.NewDAO(&TrafficDailyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeTrafficDailyStats",
Model: new(TrafficDailyStat),
PkName: "id",
},
}).(*TrafficDailyStatDAO)
}
var SharedTrafficDailyStatDAO *TrafficDailyStatDAO
func init() {
dbs.OnReady(func() {
SharedTrafficDailyStatDAO = NewTrafficDailyStatDAO()
})
}
// IncreaseDailyStat 增加统计数据
func (this *TrafficDailyStatDAO) IncreaseDailyStat(tx *dbs.Tx, day string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"day": day,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// IncreaseIPs 增加独立IP统计数据
func (this *TrafficDailyStatDAO) IncreaseIPs(tx *dbs.Tx, day string, countIPs int64) error {
if len(day) != 8 {
return errors.New("invalid day '" + day + "'")
}
return this.Query(tx).
Param("countIPs", countIPs).
InsertOrUpdateQuickly(maps.Map{
"day": day,
"countIPs": countIPs,
}, maps.Map{
"countIPs": dbs.SQL("countIPs+:countIPs"),
})
}
// FindDailyStats 获取日期之间统计
func (this *TrafficDailyStatDAO) FindDailyStats(tx *dbs.Tx, dayFrom string, dayTo string) (result []*TrafficDailyStat, err error) {
ones, err := this.Query(tx).
Between("day", dayFrom, dayTo).
FindAll()
if err != nil {
return nil, err
}
dayMap := map[string]*TrafficDailyStat{} // day => Stat
for _, one := range ones {
stat := one.(*TrafficDailyStat)
dayMap[stat.Day] = stat
}
days, err := utils.RangeDays(dayFrom, dayTo)
if err != nil {
return nil, err
}
for _, day := range days {
stat, ok := dayMap[day]
if ok {
result = append(result, stat)
} else {
result = append(result, &TrafficDailyStat{Day: day})
}
}
return result, nil
}
// FindDailyStat 查找某天的统计
func (this *TrafficDailyStatDAO) FindDailyStat(tx *dbs.Tx, day string) (*TrafficDailyStat, error) {
one, err := this.Query(tx).
Attr("day", day).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*TrafficDailyStat), nil
}
// CleanDays 清理历史数据
func (this *TrafficDailyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("day", day).
Delete()
return err
}
func (this *TrafficDailyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.TrafficDailyStat.Clean.Days > 0 {
defaultDays = databaseConfig.TrafficDailyStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 30
}
return this.CleanDays(tx, defaultDays)
}

View File

@@ -0,0 +1,30 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/dbs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
"time"
)
func TestTrafficDailyStatDAO_IncreaseDayBytes(t *testing.T) {
dbs.NotifyReady()
var now = time.Now()
err := SharedTrafficDailyStatDAO.IncreaseDailyStat(nil, timeutil.Format("Ymd"), 1, 1, 1, 1, 1, 1)
if err != nil {
t.Fatal(err)
}
t.Log("ok", time.Since(now).Seconds()*1000, "ms")
}
func TestTrafficDailyStatDAO_IncreaseIPs(t *testing.T) {
dbs.NotifyReady()
var tx *dbs.Tx
err := SharedTrafficDailyStatDAO.IncreaseIPs(tx, timeutil.Format("Ymd"), 123)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -0,0 +1,44 @@
package stats
import "github.com/iwind/TeaGo/dbs"
const (
TrafficDailyStatField_Id dbs.FieldName = "id" // ID
TrafficDailyStatField_Day dbs.FieldName = "day" // YYYYMMDD
TrafficDailyStatField_CachedBytes dbs.FieldName = "cachedBytes" // 缓存流量
TrafficDailyStatField_Bytes dbs.FieldName = "bytes" // 流量字节
TrafficDailyStatField_CountRequests dbs.FieldName = "countRequests" // 请求数
TrafficDailyStatField_CountCachedRequests dbs.FieldName = "countCachedRequests" // 缓存请求数
TrafficDailyStatField_CountAttackRequests dbs.FieldName = "countAttackRequests" // 攻击量
TrafficDailyStatField_AttackBytes dbs.FieldName = "attackBytes" // 攻击流量
TrafficDailyStatField_CountIPs dbs.FieldName = "countIPs" // 独立IP数
)
// TrafficDailyStat 总的流量统计(按天)
type TrafficDailyStat struct {
Id uint64 `field:"id"` // ID
Day string `field:"day"` // YYYYMMDD
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
Bytes uint64 `field:"bytes"` // 流量字节
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击量
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
CountIPs uint64 `field:"countIPs"` // 独立IP数
}
type TrafficDailyStatOperator struct {
Id any // ID
Day any // YYYYMMDD
CachedBytes any // 缓存流量
Bytes any // 流量字节
CountRequests any // 请求数
CountCachedRequests any // 缓存请求数
CountAttackRequests any // 攻击量
AttackBytes any // 攻击流量
CountIPs any // 独立IP数
}
func NewTrafficDailyStatOperator() *TrafficDailyStatOperator {
return &TrafficDailyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats

View File

@@ -0,0 +1,164 @@
package stats
import (
"github.com/TeaOSLab/EdgeAPI/internal/db/models"
"github.com/TeaOSLab/EdgeAPI/internal/errors"
"github.com/TeaOSLab/EdgeAPI/internal/goman"
"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
"github.com/TeaOSLab/EdgeAPI/internal/utils"
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/dbs"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/rands"
timeutil "github.com/iwind/TeaGo/utils/time"
"time"
)
type TrafficHourlyStatDAO dbs.DAO
func init() {
dbs.OnReadyDone(func() {
// 清理数据任务
var ticker = time.NewTicker(time.Duration(rands.Int(24, 48)) * time.Hour)
goman.New(func() {
for range ticker.C {
err := SharedTrafficHourlyStatDAO.CleanDefaultDays(nil, 15) // 只保留N天
if err != nil {
remotelogs.Error("TrafficHourlyStatDAO", "clean expired data failed: "+err.Error())
}
}
})
})
}
func NewTrafficHourlyStatDAO() *TrafficHourlyStatDAO {
return dbs.NewDAO(&TrafficHourlyStatDAO{
DAOObject: dbs.DAOObject{
DB: Tea.Env,
Table: "edgeTrafficHourlyStats",
Model: new(TrafficHourlyStat),
PkName: "id",
},
}).(*TrafficHourlyStatDAO)
}
var SharedTrafficHourlyStatDAO *TrafficHourlyStatDAO
func init() {
dbs.OnReady(func() {
SharedTrafficHourlyStatDAO = NewTrafficHourlyStatDAO()
})
}
// IncreaseHourlyStat 增加流量
func (this *TrafficHourlyStatDAO) IncreaseHourlyStat(tx *dbs.Tx, hour string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttackRequests int64, attackBytes int64) error {
if len(hour) != 10 {
return errors.New("invalid hour '" + hour + "'")
}
err := this.Query(tx).
Param("bytes", bytes).
Param("cachedBytes", cachedBytes).
Param("countRequests", countRequests).
Param("countCachedRequests", countCachedRequests).
Param("countAttackRequests", countAttackRequests).
Param("attackBytes", attackBytes).
InsertOrUpdateQuickly(maps.Map{
"hour": hour,
"bytes": bytes,
"cachedBytes": cachedBytes,
"countRequests": countRequests,
"countCachedRequests": countCachedRequests,
"countAttackRequests": countAttackRequests,
"attackBytes": attackBytes,
}, maps.Map{
"bytes": dbs.SQL("bytes+:bytes"),
"cachedBytes": dbs.SQL("cachedBytes+:cachedBytes"),
"countRequests": dbs.SQL("countRequests+:countRequests"),
"countCachedRequests": dbs.SQL("countCachedRequests+:countCachedRequests"),
"countAttackRequests": dbs.SQL("countAttackRequests+:countAttackRequests"),
"attackBytes": dbs.SQL("attackBytes+:attackBytes"),
})
if err != nil {
return err
}
return nil
}
// FindHourlyStats 获取小时之间统计
func (this *TrafficHourlyStatDAO) FindHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (result []*TrafficHourlyStat, err error) {
ones, err := this.Query(tx).
Between("hour", hourFrom, hourTo).
FindAll()
if err != nil {
return nil, err
}
hourMap := map[string]*TrafficHourlyStat{} // hour => Stat
for _, one := range ones {
stat := one.(*TrafficHourlyStat)
hourMap[stat.Hour] = stat
}
hours, err := utils.RangeHours(hourFrom, hourTo)
if err != nil {
return nil, err
}
for _, hour := range hours {
stat, ok := hourMap[hour]
if ok {
result = append(result, stat)
} else {
result = append(result, &TrafficHourlyStat{Hour: hour})
}
}
return result, nil
}
// FindHourlyStat 查FindHourlyStat 找单个小时的统计
func (this *TrafficHourlyStatDAO) FindHourlyStat(tx *dbs.Tx, hour string) (*TrafficHourlyStat, error) {
one, err := this.Query(tx).
Attr("hour", hour).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*TrafficHourlyStat), err
}
// SumHourlyStats 计算多个小时的统计总和
func (this *TrafficHourlyStatDAO) SumHourlyStats(tx *dbs.Tx, hourFrom string, hourTo string) (*TrafficHourlyStat, error) {
one, err := 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").
Between("hour", hourFrom, hourTo).
Find()
if err != nil || one == nil {
return nil, err
}
return one.(*TrafficHourlyStat), nil
}
// CleanDays 清理历史数据
func (this *TrafficHourlyStatDAO) CleanDays(tx *dbs.Tx, days int) error {
var hour = timeutil.Format("Ymd00", time.Now().AddDate(0, 0, -days))
_, err := this.Query(tx).
Lt("hour", hour).
Delete()
return err
}
func (this *TrafficHourlyStatDAO) CleanDefaultDays(tx *dbs.Tx, defaultDays int) error {
databaseConfig, err := models.SharedSysSettingDAO.ReadDatabaseConfig(tx)
if err != nil {
return err
}
if databaseConfig != nil && databaseConfig.TrafficHourlyStat.Clean.Days > 0 {
defaultDays = databaseConfig.TrafficHourlyStat.Clean.Days
}
if defaultDays <= 0 {
defaultDays = 15
}
return this.CleanDays(tx, defaultDays)
}

View File

@@ -0,0 +1,20 @@
package stats
import (
_ "github.com/go-sql-driver/mysql"
"github.com/iwind/TeaGo/dbs"
timeutil "github.com/iwind/TeaGo/utils/time"
"testing"
"time"
)
func TestTrafficHourlyStatDAO_IncreaseDayBytes(t *testing.T) {
dbs.NotifyReady()
now := time.Now()
err := SharedTrafficHourlyStatDAO.IncreaseHourlyStat(nil, timeutil.Format("YmdH"), 1, 1, 1, 1, 1, 1)
if err != nil {
t.Fatal(err)
}
t.Log("ok", time.Since(now).Seconds()*1000, "ms")
}

View File

@@ -0,0 +1,28 @@
package stats
// TrafficHourlyStat 总的流量统计(按小时)
type TrafficHourlyStat struct {
Id uint64 `field:"id"` // ID
Hour string `field:"hour"` // YYYYMMDDHH
Bytes uint64 `field:"bytes"` // 流量字节
CachedBytes uint64 `field:"cachedBytes"` // 缓存流量
CountRequests uint64 `field:"countRequests"` // 请求数
CountCachedRequests uint64 `field:"countCachedRequests"` // 缓存请求数
CountAttackRequests uint64 `field:"countAttackRequests"` // 攻击数
AttackBytes uint64 `field:"attackBytes"` // 攻击流量
}
type TrafficHourlyStatOperator struct {
Id interface{} // ID
Hour interface{} // YYYYMMDDHH
Bytes interface{} // 流量字节
CachedBytes interface{} // 缓存流量
CountRequests interface{} // 请求数
CountCachedRequests interface{} // 缓存请求数
CountAttackRequests interface{} // 攻击数
AttackBytes interface{} // 攻击流量
}
func NewTrafficHourlyStatOperator() *TrafficHourlyStatOperator {
return &TrafficHourlyStatOperator{}
}

View File

@@ -0,0 +1 @@
package stats