245 lines
5.6 KiB
Go
245 lines
5.6 KiB
Go
package model
|
|
|
|
import (
|
|
"backend/util"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type statisticsInfoCacheEntry struct {
|
|
data map[string]interface{}
|
|
expireAt time.Time
|
|
}
|
|
|
|
var statisticsInfoCache = struct {
|
|
mu sync.RWMutex
|
|
items map[int]statisticsInfoCacheEntry
|
|
}{
|
|
items: make(map[int]statisticsInfoCacheEntry),
|
|
}
|
|
|
|
const statisticsInfoCacheTTL = time.Minute
|
|
|
|
type Statistics struct {
|
|
AppId int `json:"AppId"`
|
|
ServerList []int `json:"ServerList"`
|
|
Emit []string `json:"Emit"`
|
|
}
|
|
|
|
func (s *Statistics) StatisticsLevel() (interface{}, error) {
|
|
AppConfig, err := util.GetAppConfig(s.AppId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res := make([]map[string]interface{}, 0)
|
|
res1 := make(map[int]int)
|
|
type dbres struct {
|
|
Sum int `db:"sum"`
|
|
Level int `db:"level"`
|
|
}
|
|
for _, v := range s.ServerList {
|
|
Db := util.MPool.GetMysqlDB(AppConfig, v)
|
|
if Db == nil {
|
|
return nil, fmt.Errorf("failed to get mysql database")
|
|
}
|
|
var result []dbres
|
|
err = Db.Select(&result, "select count(*) as sum , `level` from t_player_baseinfo group by `level`")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get level count: %v", err)
|
|
}
|
|
for r := range result {
|
|
res1[result[r].Level] += result[r].Sum
|
|
}
|
|
Db.Close()
|
|
}
|
|
keys := make([]int, 0, len(res1))
|
|
for k := range res1 {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Ints(keys)
|
|
for _, k := range keys {
|
|
res = append(res, map[string]interface{}{
|
|
"Level": k,
|
|
"Sum": res1[k],
|
|
})
|
|
}
|
|
return map[string]interface{}{
|
|
"data": res,
|
|
"total": len(res),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Statistics) StatisticsInfo() (interface{}, error) {
|
|
statisticsInfoCache.mu.RLock()
|
|
if entry, ok := statisticsInfoCache.items[s.AppId]; ok && time.Now().Before(entry.expireAt) {
|
|
cached := make(map[string]interface{}, len(entry.data))
|
|
for k, v := range entry.data {
|
|
cached[k] = v
|
|
}
|
|
statisticsInfoCache.mu.RUnlock()
|
|
return cached, nil
|
|
}
|
|
statisticsInfoCache.mu.RUnlock()
|
|
|
|
AppConfig, err := util.GetAppConfig(s.AppId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
overview, err := util.CountStatisticsOverview(s.AppId, AppConfig.OpenTime, AppConfig.Tz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rechargeFloat, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", overview.Recharge), 64)
|
|
totalRechargeFloat, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", overview.TotalRecharge), 64)
|
|
result := map[string]interface{}{
|
|
"register": overview.Register,
|
|
"totalRegister": overview.TotalRegister,
|
|
"recharge": rechargeFloat,
|
|
"totalRecharge": totalRechargeFloat,
|
|
"rechargeUser": overview.RechargeUser,
|
|
"totalRechargeUser": overview.TotalRechargeUser,
|
|
"remain": 0,
|
|
}
|
|
|
|
statisticsInfoCache.mu.Lock()
|
|
statisticsInfoCache.items[s.AppId] = statisticsInfoCacheEntry{
|
|
data: result,
|
|
expireAt: time.Now().Add(statisticsInfoCacheTTL),
|
|
}
|
|
statisticsInfoCache.mu.Unlock()
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Statistics) StatisticsHeat() (interface{}, error) {
|
|
AppConfig, err := util.GetAppConfig(s.AppId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
trend, err := util.CountStatisticsHeat7Days(s.AppId, AppConfig.Tz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"key": trend.Key,
|
|
"value": trend.DailyActive,
|
|
"value2": trend.Register,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Statistics) StatisticsOrder() (interface{}, error) {
|
|
AppConfig, err := util.GetAppConfig(s.AppId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
LogDb := util.MPool.GetTopicDB(AppConfig.Topic)
|
|
defer LogDb.Close()
|
|
if LogDb == nil {
|
|
return nil, fmt.Errorf("failed to get mysql database")
|
|
}
|
|
// 订单统计
|
|
var result []struct {
|
|
Param string `db:"Param"`
|
|
}
|
|
err = LogDb.Select(&result, "select Param from log_event where `Event` = 'logout' and Timestamp <= ?", util.Now()-86400*7)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get order count: %v", err)
|
|
}
|
|
type r struct {
|
|
count int
|
|
m map[int]int
|
|
}
|
|
hard := r{}
|
|
hard.m = make(map[int]int)
|
|
mid := r{}
|
|
mid.m = make(map[int]int)
|
|
for _, v := range result {
|
|
paramMap := util.ParseParam(v.Param)
|
|
orderList := paramMap["order_list"].(map[string]interface{})
|
|
if paramMap["after_level"].(float64) < 12 {
|
|
continue
|
|
}
|
|
hardNum := 0
|
|
midNum := 0
|
|
for _, order := range orderList {
|
|
orderMap := order.(map[string]interface{})
|
|
MergeList := make([]string, 0)
|
|
if mergeIDs, ok := orderMap["MergeId"].([]any); ok && mergeIDs != nil {
|
|
for _, m := range mergeIDs {
|
|
mInt := fmt.Sprintf("%v", m)
|
|
MergeList = append(MergeList, mInt)
|
|
}
|
|
}
|
|
// 检测是否包含难度符文
|
|
if int(orderMap["Diff"].(float64)) == 3 && util.CheckContainChess(MergeList, s.Emit) {
|
|
hardNum++
|
|
}
|
|
if int(orderMap["Diff"].(float64)) == 2 && util.CheckContainChess(MergeList, s.Emit) {
|
|
midNum++
|
|
}
|
|
}
|
|
if hardNum > 0 {
|
|
hard.count++
|
|
hard.m[hardNum]++
|
|
}
|
|
if midNum > 0 {
|
|
mid.count++
|
|
mid.m[midNum]++
|
|
}
|
|
}
|
|
type rr struct {
|
|
Type string `json:"Type"`
|
|
Num int `json:"Num"`
|
|
Sum int `json:"Sum"`
|
|
Prop float64 `json:"Prop"`
|
|
}
|
|
hardData := make([]rr, 10)
|
|
for i := 0; i <= 4; i++ {
|
|
num := 0
|
|
if i == 0 {
|
|
num = hard.count
|
|
}
|
|
if v, ok := hard.m[i]; ok {
|
|
num = v
|
|
}
|
|
prop := 0.0
|
|
if hard.count > 0 {
|
|
prop = util.FloatDecimals(100*float64(num)/float64(hard.count), 2)
|
|
}
|
|
hardData[i] = rr{
|
|
Type: "高难度",
|
|
Num: i,
|
|
Sum: num,
|
|
Prop: prop,
|
|
}
|
|
}
|
|
for i := 5; i <= 9; i++ {
|
|
num := 0
|
|
if i-5 == 0 {
|
|
num = mid.count
|
|
}
|
|
if v, ok := mid.m[i-5]; ok {
|
|
num = v
|
|
}
|
|
prop := 0.0
|
|
if mid.count > 0 {
|
|
prop = util.FloatDecimals(100*float64(num)/float64(mid.count), 2)
|
|
}
|
|
hardData[i] = rr{
|
|
Type: "中难度",
|
|
Num: i - 5,
|
|
Sum: num,
|
|
Prop: prop,
|
|
}
|
|
}
|
|
return map[string]interface{}{
|
|
"data": hardData,
|
|
"data2": mid,
|
|
}, nil
|
|
}
|