package service import ( "encoding/json" "fmt" "gadmin/config" "gadmin/internal/admin/consts" "gadmin/internal/admin/forms" "gadmin/internal/gorm/model" "gadmin/internal/gorm/query" "gadmin/package/gmdata" "github.com/gin-gonic/gin" "github.com/jinzhu/now" "github.com/sirupsen/logrus" "github.com/spf13/cast" "sort" "strings" ) var Treasure = new(sTreasure) type sTreasure struct{} type TreasureOutput map[string]interface{} type TreasureReportInfo struct { Days []string `json:"days"` Players []int64 `json:"players"` TreasureOutput } type TreasureLogItem struct { Day string `json:"days"` Players int64 `json:"players"` TreasureOutput } type TreasureInfo struct { Info TreasureReportInfo `json:"info"` Rows []TreasureLogItem `json:"rows"` } type ReportDayTreasure struct { model.ReportDayTreasure TreasureOutput ActiveCount int64 `json:"active_count"` } // QueryTreasureLog 统计宝物碎片信息 func (s *sTreasure) QueryTreasureLog(params forms.TreasureReportReq) (result []*ReportDayTreasure, err error) { rdb := query.Use(config.DB).ReportDayTreasure type ItemCount struct { ItemID int `json:"item_id"` TotalCount int64 `json:"total_count"` } m := rdb.Where(rdb.Date.Gte(params.Day), rdb.Date.Lt(params.EndDay)).Order(rdb.Date.Desc()) switch params.ChannelId { case consts.ChannelIdNone: // 不选择渠道 case consts.ChannelIdAllAdv, consts.ChannelIdAllWx, consts.ChannelIdAllTT: // 所有的广告渠道 m = m.Where(rdb.ChannelID.In(Channel.GetIdsByType(params.ChannelId)...)) default: // 指定渠道 m = m.Where(rdb.ChannelID.Eq(params.ChannelId)) } if params.ServerId > 0 { m = m.Where(rdb.ServerID.Eq(int32(params.ServerId))) } handleOutput := func(v *ReportDayTreasure, row *model.ReportDayTreasure) { var items []*ItemCount if row.Output != "" { if err = json.Unmarshal([]byte(row.Output), &items); err != nil { logrus.Warnf("Gearitems Unmarshal err :%+v", err) return } } for _, item := range items { ck := fmt.Sprintf("d:%v", item.ItemID) o, ok := v.TreasureOutput[ck] if ok { v.TreasureOutput[ck] = cast.ToInt64(o) + item.TotalCount } else { v.TreasureOutput[ck] = item.TotalCount } } } add := func(row *model.ReportDayTreasure) { var exist bool for _, v := range result { if v.Date == row.Date { exist = true v.PlayerCount += row.PlayerCount handleOutput(v, row) } } if exist == false { v := new(ReportDayTreasure) v.Date = row.Date v.ActiveCount = DayBasic.GetPlayerCount(params.ServerId, row.Date, params.ChannelId, -1, "active_count") v.ChannelID = row.ChannelID v.PlayerCount += row.PlayerCount v.TreasureOutput = make(TreasureOutput) handleOutput(v, row) result = append(result, v) } } distributes, err := m.Find() if err != nil { return } for _, distribute := range distributes { add(distribute) } for _, v := range result { v.Date = strings.ReplaceAll(v.Date, "T00:00:00+08:00", "") } return } type LevelTreasure struct { Level int `json:"level"` TotalTreasures int64 `json:"totalTreasures"` } func (s *sTreasure) PlayerLevel(params forms.TreasurePlayerLevelReq) (results []LevelTreasure, err error) { key := config.UKey("PlayerLevel", params) if err = config.GetCacheScan(key, &results); err != nil { return } if len(results) != 0 { return } var ( m = config.DB.Model(&model.PlayerCache{}). Select("`level`, SUM(JSON_LENGTH(treasures)) AS total_treasures"). Where("`level` BETWEEN ? AND ? AND JSON_LENGTH(treasures) > 0", params.StartLevel, params.EndLevel) ) if params.ServerId > 0 { m = m.Where("server_id = ?", params.ServerId) } m.Group("level").Scan(&results) results = s.completeMissingLevels(params, results) if len(results) > 0 { config.SetCache(key, results, now.BeginningOfDay().Unix()+86400) } return } func (s *sTreasure) completeMissingLevels(params forms.TreasurePlayerLevelReq, results []LevelTreasure) []LevelTreasure { var completedResults []LevelTreasure levels := make(map[int]bool) for _, result := range results { levels[result.Level] = true completedResults = append(completedResults, result) } for i := params.StartLevel; i <= params.EndLevel; i++ { if !levels[int(i)] { completedResults = append(completedResults, LevelTreasure{Level: int(i), TotalTreasures: 0}) } } sort.Slice(completedResults, func(i, j int) bool { return completedResults[i].Level < completedResults[j].Level // 根据等级从小到大排序 }) return completedResults } type TreasureRoleWear struct { Role int64 `json:"role"` RoleName string `json:"roleName"` Treasures map[string]int64 `json:"treasures"` } func (s *sTreasure) RoleWear(ctx *gin.Context) (result []*TreasureRoleWear, err error) { key := "TreasureRoleWear" if err = config.GetCacheScan(key, &result); err != nil { return } if len(result) != 0 { return } var roles = make(map[int64]map[string]int64) handle := func(player *model.PlayerCache) (err error) { var treasures []*gmdata.DropedTreasure err = json.Unmarshal([]byte(player.Treasures), &treasures) if err != nil { return } for _, v := range treasures { if v.RoleID < 1 { // 如果宝物未佩戴,就没有角色 continue } if _, ok := roles[v.RoleID]; !ok { roles[v.RoleID] = make(map[string]int64) } tName := gmdata.GetTreasureByTID(v.TID) if tName == "" { logrus.Warnf("无效TID:%+v", v) continue } if _, ok2 := roles[v.RoleID][tName]; !ok2 { roles[v.RoleID][tName] = 1 } else { roles[v.RoleID][tName] += 1 } } return } lastID := int64(0) var forErr error for { var players []*model.PlayerCache // Perform the query with pagination and sorting by id greater than lastID dao := config.DB.Model(&model.PlayerCache{}). Select("`id`, `treasures`"). Where("`level` > ? AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物,减少数据查询量做个范围过滤 Where("id > ?", lastID) list := dao.Limit(1000).Order("id ASC").Scan(&players) if list.Error != nil { logrus.Errorf("RoleWear result.Error:%+v", list.Error) break } // If no more rows were fetched, break the loop if len(players) == 0 { break } // Process the retrieved players here as needed for _, player := range players { forErr = handle(player) if forErr != nil { return nil, forErr } } // Update the lastID for the next batch lastID = players[len(players)-1].ID } if forErr != nil { return nil, forErr } for role, treasures := range roles { roleName := gmdata.GetRoleNameByType(role) if roleName == "" { continue } result = append(result, &TreasureRoleWear{ Role: role, RoleName: roleName, Treasures: treasures, }) } sort.Slice(result, func(i, j int) bool { return result[i].Role < result[j].Role // 从小到大排序 }) if len(result) > 0 { config.SetCache(key, result, now.BeginningOfDay().Unix()+86400) } return } type LevelTreasure2 struct { Level int64 `json:"level"` Treasures map[string]int64 `json:"treasures"` } func (s *sTreasure) Level(params forms.TreasureLevelReq) (results []*LevelTreasure2, err error) { key := fmt.Sprintf("TreasureLevel:ServerId:%v", params.ServerId) var allResult []*LevelTreasure2 if err = config.GetCacheScan(key, &allResult); err != nil { return } if len(allResult) != 0 { results = s.filterLevel(params, allResult) return } var levels = make(map[int64]map[string]int64) handle := func(player *model.PlayerCache) (err error) { var treasures []*gmdata.DropedTreasure err = json.Unmarshal([]byte(player.Treasures), &treasures) if err != nil { return } for _, v := range treasures { tName := gmdata.GetTreasureByTID(v.TID) if tName == "" { logrus.Warnf("Level 无效TID:%+v", v) continue } level := gmdata.GetTreasureLevel(v.TID) if level < 1 { logrus.Warnf("Level 无效TID2:%+v", v) continue } _, ok := levels[level] if !ok { levels[level] = make(map[string]int64) } if _, ok2 := levels[level][tName]; !ok2 { levels[level][tName] = 1 } else { levels[level][tName] += 1 } } return } lastID := int64(0) var forErr error for { var players []*model.PlayerCache // Perform the query with pagination and sorting by id greater than lastID dao := config.DB.Model(&model.PlayerCache{}). Select("`id`, `treasures`"). Where("`level` > ? AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物,减少数据查询量做个范围过滤 Where("id > ?", lastID) if params.ServerId > 0 { dao = dao.Where("server_id = ?", params.ServerId) } list := dao.Limit(1000).Order("id ASC").Scan(&players) if list.Error != nil { logrus.Errorf("RoleWear result.Error:%+v", list.Error) break } // If no more rows were fetched, break the loop if len(players) == 0 { break } // Process the retrieved players here as needed for _, player := range players { forErr = handle(player) if forErr != nil { return nil, forErr } } // Update the lastID for the next batch lastID = players[len(players)-1].ID } if forErr != nil { return nil, forErr } for i := 1; i <= 200; i++ { ni := int64(i) _, ok := levels[ni] // 等级不存在就进行补全 if !ok { levels[ni] = make(map[string]int64) for _, t := range gmdata.Treasures { levels[ni][t.Name] = 0 } continue } // 补全宝物 for _, t := range gmdata.Treasures { _, ok2 := levels[ni][t.Name] if !ok2 { levels[ni][t.Name] = 0 } } } for level, treasures := range levels { allResult = append(allResult, &LevelTreasure2{ Level: level, Treasures: treasures, }) } sort.Slice(allResult, func(i, j int) bool { return allResult[i].Level < allResult[j].Level // 从小到大排序 }) if len(allResult) > 0 { config.SetCache(key, allResult, now.BeginningOfDay().Unix()+86400) } results = s.filterLevel(params, allResult) return } // filterLevel 过滤等级 func (s *sTreasure) filterLevel(params forms.TreasureLevelReq, allResult []*LevelTreasure2) (result []*LevelTreasure2) { for _, v := range allResult { if v.Level >= params.StartLevel && v.Level <= params.EndLevel { result = append(result, v) } } sort.Slice(result, func(i, j int) bool { return result[i].Level < result[j].Level // 从小到大排序 }) return } type StarTreasure struct { Star int64 `json:"star"` Treasures map[string]int64 `json:"treasures"` } func (s *sTreasure) Star(params forms.TreasureStarReq) (results []*StarTreasure, err error) { key := fmt.Sprintf("TreasureStar:ServerId:%v", params.ServerId) if err = config.GetCacheScan(key, &results); err != nil { return } if len(results) != 0 { return } var stars = make(map[int64]map[string]int64) handle := func(player *model.PlayerCache) (err error) { var treasures []*gmdata.DropedTreasure err = json.Unmarshal([]byte(player.Treasures), &treasures) if err != nil { return } for _, v := range treasures { tName := gmdata.GetTreasureByTID(v.TID) if tName == "" { logrus.Warnf("Star 无效TID:%+v", v) continue } star := gmdata.GetTreasureStar(v.TID) if star < 0 { logrus.Warnf("Star 无效TID2:%+v", v) continue } _, ok := stars[star] if !ok { stars[star] = make(map[string]int64) } if _, ok2 := stars[star][tName]; !ok2 { stars[star][tName] = 1 } else { stars[star][tName] += 1 } } return } lastID := int64(0) var forErr error for { var players []*model.PlayerCache // Perform the query with pagination and sorting by id greater than lastID dao := config.DB.Model(&model.PlayerCache{}). Select("`id`, `treasures`"). Where("`level` > ? AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物,减少数据查询量做个范围过滤 Where("id > ?", lastID) if params.ServerId > 0 { dao = dao.Where("server_id = ?", params.ServerId) } list := dao.Limit(1000).Order("id ASC").Scan(&players) if list.Error != nil { logrus.Errorf("RoleWear result.Error:%+v", list.Error) break } // If no more rows were fetched, break the loop if len(players) == 0 { break } // Process the retrieved players here as needed for _, player := range players { forErr = handle(player) if forErr != nil { return nil, forErr } } // Update the lastID for the next batch lastID = players[len(players)-1].ID } if forErr != nil { return nil, forErr } for i := 1; i <= 20; i++ { ni := int64(i) _, ok := stars[ni] // 等级不存在就进行补全 if !ok { stars[ni] = make(map[string]int64) for _, t := range gmdata.Treasures { stars[ni][t.Name] = 0 } continue } // 补全宝物 for _, t := range gmdata.Treasures { _, ok2 := stars[ni][t.Name] if !ok2 { stars[ni][t.Name] = 0 } } } for star, treasures := range stars { results = append(results, &StarTreasure{ Star: star, Treasures: treasures, }) } sort.Slice(results, func(i, j int) bool { return results[i].Star < results[j].Star // 从小到大排序 }) if len(results) > 0 { config.SetCache(key, results, now.BeginningOfDay().Unix()+86400) } return }