mirror of
				https://github.com/bjdgyc/anylink.git
				synced 2025-11-04 19:16:22 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			248 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package dbdata
 | 
						|
 | 
						|
import (
 | 
						|
	"container/list"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/bjdgyc/anylink/base"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	LayoutTimeFormat    = "2006-01-02 15:04:05"
 | 
						|
	LayoutTimeFormatMin = "2006-01-02 15:04"
 | 
						|
	RealTimeMaxSize     = 120 // 实时数据最大保存条数
 | 
						|
)
 | 
						|
 | 
						|
type StatsInfo struct {
 | 
						|
	RealtimeData map[string]*list.List
 | 
						|
	Actions      []string
 | 
						|
	Scopes       []string
 | 
						|
	mux          sync.Mutex
 | 
						|
}
 | 
						|
 | 
						|
type ScopeDetail struct {
 | 
						|
	sTime   time.Time
 | 
						|
	eTime   time.Time
 | 
						|
	minutes int
 | 
						|
	fsTime  string
 | 
						|
	feTime  string
 | 
						|
}
 | 
						|
 | 
						|
var StatsInfoIns *StatsInfo
 | 
						|
 | 
						|
func init() {
 | 
						|
	StatsInfoIns = &StatsInfo{
 | 
						|
		Actions:      []string{"online", "network", "cpu", "mem"},
 | 
						|
		Scopes:       []string{"rt", "1h", "24h", "3d", "7d", "30d"},
 | 
						|
		RealtimeData: make(map[string]*list.List),
 | 
						|
	}
 | 
						|
	for _, v := range StatsInfoIns.Actions {
 | 
						|
		StatsInfoIns.RealtimeData[v] = list.New()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 校验统计类型值
 | 
						|
func (s *StatsInfo) ValidAction(action string) bool {
 | 
						|
	for _, item := range s.Actions {
 | 
						|
		if item == action {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// 校验日期范围值
 | 
						|
func (s *StatsInfo) ValidScope(scope string) bool {
 | 
						|
	for _, item := range s.Scopes {
 | 
						|
		if item == scope {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// 设置实时统计数据
 | 
						|
func (s *StatsInfo) SetRealTime(action string, val interface{}) {
 | 
						|
	s.mux.Lock()
 | 
						|
	defer s.mux.Unlock()
 | 
						|
 | 
						|
	if s.RealtimeData[action].Len() >= RealTimeMaxSize {
 | 
						|
		ele := s.RealtimeData[action].Front()
 | 
						|
		s.RealtimeData[action].Remove(ele)
 | 
						|
	}
 | 
						|
	s.RealtimeData[action].PushBack(val)
 | 
						|
}
 | 
						|
 | 
						|
// 获取实时统计数据
 | 
						|
func (s *StatsInfo) GetRealTime(action string) (res []interface{}) {
 | 
						|
	s.mux.Lock()
 | 
						|
	defer s.mux.Unlock()
 | 
						|
 | 
						|
	for e := s.RealtimeData[action].Front(); e != nil; e = e.Next() {
 | 
						|
		res = append(res, e.Value)
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// 保存数据至数据库
 | 
						|
func (s *StatsInfo) SaveStatsInfo(so StatsOnline, sn StatsNetwork, sc StatsCpu, sm StatsMem) {
 | 
						|
	if so.Num != 0 {
 | 
						|
		_ = Add(so)
 | 
						|
	}
 | 
						|
	if sn.Up != 0 || sn.Down != 0 {
 | 
						|
		_ = Add(sn)
 | 
						|
	}
 | 
						|
	if sc.Percent != 0 {
 | 
						|
		_ = Add(sc)
 | 
						|
	}
 | 
						|
	if sm.Percent != 0 {
 | 
						|
		_ = Add(sm)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 获取统计数据
 | 
						|
func (s *StatsInfo) GetData(action string, scope string) (res []interface{}, err error) {
 | 
						|
	if scope == "rt" {
 | 
						|
		return s.GetRealTime(action), nil
 | 
						|
	}
 | 
						|
	statsMaps := make(map[string]interface{})
 | 
						|
	currSec := fmt.Sprintf("%02d", time.Now().Second())
 | 
						|
 | 
						|
	// 获取时间段数据
 | 
						|
	sd := s.getScopeDetail(scope)
 | 
						|
	timeList := s.getTimeList(sd)
 | 
						|
	res = make([]interface{}, len(timeList))
 | 
						|
 | 
						|
	// 获取数据库查询条件
 | 
						|
	where := s.getStatsWhere(sd)
 | 
						|
	if where == "" {
 | 
						|
		return nil, errors.New("不支持的数据库类型: " + base.Cfg.DbType)
 | 
						|
	}
 | 
						|
	// 查询数据表
 | 
						|
	switch action {
 | 
						|
	case "online":
 | 
						|
		statsRes := []StatsOnline{}
 | 
						|
		FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
 | 
						|
		for _, v := range statsRes {
 | 
						|
			t := v.CreatedAt.Format(LayoutTimeFormatMin)
 | 
						|
			statsMaps[t] = v
 | 
						|
		}
 | 
						|
	case "network":
 | 
						|
		statsRes := []StatsNetwork{}
 | 
						|
		FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
 | 
						|
		for _, v := range statsRes {
 | 
						|
			t := v.CreatedAt.Format(LayoutTimeFormatMin)
 | 
						|
			statsMaps[t] = v
 | 
						|
		}
 | 
						|
	case "cpu":
 | 
						|
		statsRes := []StatsCpu{}
 | 
						|
		FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
 | 
						|
		for _, v := range statsRes {
 | 
						|
			t := v.CreatedAt.Format(LayoutTimeFormatMin)
 | 
						|
			statsMaps[t] = v
 | 
						|
		}
 | 
						|
	case "mem":
 | 
						|
		statsRes := []StatsMem{}
 | 
						|
		FindWhere(&statsRes, 0, 0, where, sd.fsTime, sd.feTime)
 | 
						|
		for _, v := range statsRes {
 | 
						|
			t := v.CreatedAt.Format(LayoutTimeFormatMin)
 | 
						|
			statsMaps[t] = v
 | 
						|
		}
 | 
						|
	}
 | 
						|
	// 整合数据
 | 
						|
	for i, v := range timeList {
 | 
						|
		if mv, ok := statsMaps[v]; ok {
 | 
						|
			res[i] = mv
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		t, _ := time.ParseInLocation(LayoutTimeFormat, v+":"+currSec, time.Local)
 | 
						|
		switch action {
 | 
						|
		case "online":
 | 
						|
			res[i] = StatsOnline{CreatedAt: t}
 | 
						|
		case "network":
 | 
						|
			res[i] = StatsNetwork{CreatedAt: t}
 | 
						|
		case "cpu":
 | 
						|
			res[i] = StatsCpu{CreatedAt: t}
 | 
						|
		case "mem":
 | 
						|
			res[i] = StatsMem{CreatedAt: t}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// 获取日期范围的明细值
 | 
						|
func (s *StatsInfo) getScopeDetail(scope string) (sd *ScopeDetail) {
 | 
						|
	sd = &ScopeDetail{}
 | 
						|
	t := time.Now()
 | 
						|
	sd.eTime = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 59, t.Nanosecond(), time.Local)
 | 
						|
	sd.minutes = 0
 | 
						|
	switch scope {
 | 
						|
	case "1h":
 | 
						|
		sd.sTime = sd.eTime.Add(-time.Minute * 60)
 | 
						|
		sd.minutes = 1
 | 
						|
	case "24h":
 | 
						|
		sd.sTime = sd.eTime.AddDate(0, 0, -1)
 | 
						|
		sd.minutes = 5
 | 
						|
	case "7d":
 | 
						|
		sd.sTime = sd.eTime.AddDate(0, 0, -7)
 | 
						|
		sd.minutes = 30
 | 
						|
	case "30d":
 | 
						|
		sd.sTime = sd.eTime.AddDate(0, 0, -30)
 | 
						|
		sd.minutes = 150
 | 
						|
	}
 | 
						|
	if sd.minutes != 0 {
 | 
						|
		sd.sTime = sd.sTime.Add(-time.Minute * time.Duration(sd.minutes))
 | 
						|
	}
 | 
						|
	sd.fsTime = sd.sTime.Format(LayoutTimeFormat)
 | 
						|
	sd.feTime = sd.eTime.Format(LayoutTimeFormat)
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// 针对日期范围进行拆解
 | 
						|
func (s *StatsInfo) getTimeList(sd *ScopeDetail) []string {
 | 
						|
	subSec := int64(60 * sd.minutes)
 | 
						|
	count := (sd.eTime.Unix()-sd.sTime.Unix())/subSec - 1
 | 
						|
	eTime := sd.eTime.Unix() - subSec
 | 
						|
	timeLists := make([]string, count)
 | 
						|
	for i := count - 1; i >= 0; i-- {
 | 
						|
		timeLists[i] = time.Unix(eTime, 0).Format(LayoutTimeFormatMin)
 | 
						|
		eTime = eTime - subSec
 | 
						|
	}
 | 
						|
	return timeLists
 | 
						|
}
 | 
						|
 | 
						|
// 获取where条件
 | 
						|
func (s *StatsInfo) getStatsWhere(sd *ScopeDetail) (where string) {
 | 
						|
	where = "created_at BETWEEN ? AND ?"
 | 
						|
	min := strconv.Itoa(sd.minutes)
 | 
						|
	switch base.Cfg.DbType {
 | 
						|
	case "mysql":
 | 
						|
		where += " AND floor(TIMESTAMPDIFF(SECOND, created_at, '" + sd.feTime + "') / 60) % " + min + " = 0"
 | 
						|
	case "sqlite3":
 | 
						|
		where += " AND CAST(ROUND((JULIANDAY('" + sd.feTime + "') - JULIANDAY(created_at)) * 86400) / 60 as integer) % " + min + " = 0"
 | 
						|
	case "postgres":
 | 
						|
		where += " AND floor((EXTRACT(EPOCH FROM " + sd.feTime + ") - EXTRACT(EPOCH FROM created_at)) / 60) % " + min + " = 0"
 | 
						|
	default:
 | 
						|
		where = ""
 | 
						|
	}
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (s *StatsInfo) ClearStatsInfo(action string, ts string) (affected int64, err error) {
 | 
						|
	switch action {
 | 
						|
	case "online":
 | 
						|
		affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsOnline{})
 | 
						|
	case "network":
 | 
						|
		affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsNetwork{})
 | 
						|
	case "cpu":
 | 
						|
		affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsCpu{})
 | 
						|
	case "mem":
 | 
						|
		affected, err = xdb.Where("created_at < '" + ts + "'").Delete(&StatsMem{})
 | 
						|
	}
 | 
						|
	return affected, err
 | 
						|
}
 |