treasure.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. package service
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "gadmin/config"
  6. "gadmin/internal/admin/consts"
  7. "gadmin/internal/admin/forms"
  8. "gadmin/internal/gorm/model"
  9. "gadmin/internal/gorm/query"
  10. "gadmin/package/gmdata"
  11. "github.com/gin-gonic/gin"
  12. "github.com/jinzhu/now"
  13. "github.com/sirupsen/logrus"
  14. "github.com/spf13/cast"
  15. "sort"
  16. "strings"
  17. )
  18. var Treasure = new(sTreasure)
  19. type sTreasure struct{}
  20. type TreasureOutput map[string]interface{}
  21. type TreasureReportInfo struct {
  22. Days []string `json:"days"`
  23. Players []int64 `json:"players"`
  24. TreasureOutput
  25. }
  26. type TreasureLogItem struct {
  27. Day string `json:"days"`
  28. Players int64 `json:"players"`
  29. TreasureOutput
  30. }
  31. type TreasureInfo struct {
  32. Info TreasureReportInfo `json:"info"`
  33. Rows []TreasureLogItem `json:"rows"`
  34. }
  35. type ReportDayTreasure struct {
  36. model.ReportDayTreasure
  37. TreasureOutput
  38. ActiveCount int64 `json:"active_count"`
  39. }
  40. // QueryTreasureLog 统计宝物碎片信息
  41. func (s *sTreasure) QueryTreasureLog(params forms.TreasureReportReq) (result []*ReportDayTreasure, err error) {
  42. rdb := query.Use(config.DB).ReportDayTreasure
  43. type ItemCount struct {
  44. ItemID int `json:"item_id"`
  45. TotalCount int64 `json:"total_count"`
  46. }
  47. m := rdb.Where(rdb.Date.Gte(params.Day), rdb.Date.Lt(params.EndDay)).Order(rdb.Date.Desc())
  48. switch params.ChannelId {
  49. case consts.ChannelIdNone:
  50. // 不选择渠道
  51. case consts.ChannelIdAllAdv, consts.ChannelIdAllWx, consts.ChannelIdAllTT:
  52. // 所有的广告渠道
  53. m = m.Where(rdb.ChannelID.In(Channel.GetIdsByType(params.ChannelId)...))
  54. default:
  55. // 指定渠道
  56. m = m.Where(rdb.ChannelID.Eq(params.ChannelId))
  57. }
  58. if params.ServerId > 0 {
  59. m = m.Where(rdb.ServerID.Eq(int32(params.ServerId)))
  60. }
  61. handleOutput := func(v *ReportDayTreasure, row *model.ReportDayTreasure) {
  62. var items []*ItemCount
  63. if row.Output != "" {
  64. if err = json.Unmarshal([]byte(row.Output), &items); err != nil {
  65. logrus.Warnf("Gearitems Unmarshal err :%+v", err)
  66. return
  67. }
  68. }
  69. for _, item := range items {
  70. ck := fmt.Sprintf("d:%v", item.ItemID)
  71. o, ok := v.TreasureOutput[ck]
  72. if ok {
  73. v.TreasureOutput[ck] = cast.ToInt64(o) + item.TotalCount
  74. } else {
  75. v.TreasureOutput[ck] = item.TotalCount
  76. }
  77. }
  78. }
  79. add := func(row *model.ReportDayTreasure) {
  80. var exist bool
  81. for _, v := range result {
  82. if v.Date == row.Date {
  83. exist = true
  84. v.PlayerCount += row.PlayerCount
  85. handleOutput(v, row)
  86. }
  87. }
  88. if exist == false {
  89. v := new(ReportDayTreasure)
  90. v.Date = row.Date
  91. v.ActiveCount = DayBasic.GetPlayerCount(params.ServerId, row.Date, params.ChannelId, -1, "active_count")
  92. v.ChannelID = row.ChannelID
  93. v.PlayerCount += row.PlayerCount
  94. v.TreasureOutput = make(TreasureOutput)
  95. handleOutput(v, row)
  96. result = append(result, v)
  97. }
  98. }
  99. distributes, err := m.Find()
  100. if err != nil {
  101. return
  102. }
  103. for _, distribute := range distributes {
  104. add(distribute)
  105. }
  106. for _, v := range result {
  107. v.Date = strings.ReplaceAll(v.Date, "T00:00:00+08:00", "")
  108. }
  109. return
  110. }
  111. type LevelTreasure struct {
  112. Level int `json:"level"`
  113. TotalTreasures int64 `json:"totalTreasures"`
  114. }
  115. func (s *sTreasure) PlayerLevel(params forms.TreasurePlayerLevelReq) (results []LevelTreasure, err error) {
  116. key := config.UKey("PlayerLevel", params)
  117. if err = config.GetCacheScan(key, &results); err != nil {
  118. return
  119. }
  120. if len(results) != 0 {
  121. return
  122. }
  123. var (
  124. m = config.DB.Model(&model.PlayerCache{}).
  125. Select("`level`, SUM(JSON_LENGTH(treasures)) AS total_treasures").
  126. Where("`level` BETWEEN ? AND ? AND JSON_LENGTH(treasures) > 0", params.StartLevel, params.EndLevel)
  127. )
  128. if params.ServerId > 0 {
  129. m = m.Where("server_id = ?", params.ServerId)
  130. }
  131. m.Group("level").Scan(&results)
  132. results = s.completeMissingLevels(params, results)
  133. if len(results) > 0 {
  134. config.SetCache(key, results, now.BeginningOfDay().Unix()+86400)
  135. }
  136. return
  137. }
  138. func (s *sTreasure) completeMissingLevels(params forms.TreasurePlayerLevelReq, results []LevelTreasure) []LevelTreasure {
  139. var completedResults []LevelTreasure
  140. levels := make(map[int]bool)
  141. for _, result := range results {
  142. levels[result.Level] = true
  143. completedResults = append(completedResults, result)
  144. }
  145. for i := params.StartLevel; i <= params.EndLevel; i++ {
  146. if !levels[int(i)] {
  147. completedResults = append(completedResults, LevelTreasure{Level: int(i), TotalTreasures: 0})
  148. }
  149. }
  150. sort.Slice(completedResults, func(i, j int) bool {
  151. return completedResults[i].Level < completedResults[j].Level // 根据等级从小到大排序
  152. })
  153. return completedResults
  154. }
  155. type TreasureRoleWear struct {
  156. Role int64 `json:"role"`
  157. RoleName string `json:"roleName"`
  158. Treasures map[string]int64 `json:"treasures"`
  159. }
  160. func (s *sTreasure) RoleWear(ctx *gin.Context) (result []*TreasureRoleWear, err error) {
  161. key := "TreasureRoleWear"
  162. if err = config.GetCacheScan(key, &result); err != nil {
  163. return
  164. }
  165. if len(result) != 0 {
  166. return
  167. }
  168. var roles = make(map[int64]map[string]int64)
  169. handle := func(player *model.PlayerCache) (err error) {
  170. var treasures []*gmdata.DropedTreasure
  171. err = json.Unmarshal([]byte(player.Treasures), &treasures)
  172. if err != nil {
  173. return
  174. }
  175. for _, v := range treasures {
  176. if v.RoleID < 1 { // 如果宝物未佩戴,就没有角色
  177. continue
  178. }
  179. if _, ok := roles[v.RoleID]; !ok {
  180. roles[v.RoleID] = make(map[string]int64)
  181. }
  182. tName := gmdata.GetTreasureByTID(v.TID)
  183. if tName == "" {
  184. logrus.Warnf("无效TID:%+v", v)
  185. continue
  186. }
  187. if _, ok2 := roles[v.RoleID][tName]; !ok2 {
  188. roles[v.RoleID][tName] = 1
  189. } else {
  190. roles[v.RoleID][tName] += 1
  191. }
  192. }
  193. return
  194. }
  195. lastID := int64(0)
  196. var forErr error
  197. for {
  198. var players []*model.PlayerCache
  199. // Perform the query with pagination and sorting by id greater than lastID
  200. dao := config.DB.Model(&model.PlayerCache{}).
  201. Select("`id`, `treasures`").
  202. Where("`level` > ? AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物,减少数据查询量做个范围过滤
  203. Where("id > ?", lastID)
  204. list := dao.Limit(1000).Order("id ASC").Scan(&players)
  205. if list.Error != nil {
  206. logrus.Errorf("RoleWear result.Error:%+v", list.Error)
  207. break
  208. }
  209. // If no more rows were fetched, break the loop
  210. if len(players) == 0 {
  211. break
  212. }
  213. // Process the retrieved players here as needed
  214. for _, player := range players {
  215. forErr = handle(player)
  216. if forErr != nil {
  217. return nil, forErr
  218. }
  219. }
  220. // Update the lastID for the next batch
  221. lastID = players[len(players)-1].ID
  222. }
  223. if forErr != nil {
  224. return nil, forErr
  225. }
  226. for role, treasures := range roles {
  227. roleName := gmdata.GetRoleNameByType(role)
  228. if roleName == "" {
  229. continue
  230. }
  231. result = append(result, &TreasureRoleWear{
  232. Role: role,
  233. RoleName: roleName,
  234. Treasures: treasures,
  235. })
  236. }
  237. sort.Slice(result, func(i, j int) bool {
  238. return result[i].Role < result[j].Role // 从小到大排序
  239. })
  240. if len(result) > 0 {
  241. config.SetCache(key, result, now.BeginningOfDay().Unix()+86400)
  242. }
  243. return
  244. }
  245. type LevelTreasure2 struct {
  246. Level int64 `json:"level"`
  247. Treasures map[string]int64 `json:"treasures"`
  248. }
  249. func (s *sTreasure) Level(params forms.TreasureLevelReq) (results []*LevelTreasure2, err error) {
  250. key := fmt.Sprintf("TreasureLevel:ServerId:%v", params.ServerId)
  251. var allResult []*LevelTreasure2
  252. if err = config.GetCacheScan(key, &allResult); err != nil {
  253. return
  254. }
  255. if len(allResult) != 0 {
  256. results = s.filterLevel(params, allResult)
  257. return
  258. }
  259. var levels = make(map[int64]map[string]int64)
  260. handle := func(player *model.PlayerCache) (err error) {
  261. var treasures []*gmdata.DropedTreasure
  262. err = json.Unmarshal([]byte(player.Treasures), &treasures)
  263. if err != nil {
  264. return
  265. }
  266. for _, v := range treasures {
  267. tName := gmdata.GetTreasureByTID(v.TID)
  268. if tName == "" {
  269. logrus.Warnf("Level 无效TID:%+v", v)
  270. continue
  271. }
  272. level := gmdata.GetTreasureLevel(v.TID)
  273. if level < 1 {
  274. logrus.Warnf("Level 无效TID2:%+v", v)
  275. continue
  276. }
  277. _, ok := levels[level]
  278. if !ok {
  279. levels[level] = make(map[string]int64)
  280. }
  281. if _, ok2 := levels[level][tName]; !ok2 {
  282. levels[level][tName] = 1
  283. } else {
  284. levels[level][tName] += 1
  285. }
  286. }
  287. return
  288. }
  289. lastID := int64(0)
  290. var forErr error
  291. for {
  292. var players []*model.PlayerCache
  293. // Perform the query with pagination and sorting by id greater than lastID
  294. dao := config.DB.Model(&model.PlayerCache{}).
  295. Select("`id`, `treasures`").
  296. Where("`level` > ? AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物,减少数据查询量做个范围过滤
  297. Where("id > ?", lastID)
  298. if params.ServerId > 0 {
  299. dao = dao.Where("server_id = ?", params.ServerId)
  300. }
  301. list := dao.Limit(1000).Order("id ASC").Scan(&players)
  302. if list.Error != nil {
  303. logrus.Errorf("RoleWear result.Error:%+v", list.Error)
  304. break
  305. }
  306. // If no more rows were fetched, break the loop
  307. if len(players) == 0 {
  308. break
  309. }
  310. // Process the retrieved players here as needed
  311. for _, player := range players {
  312. forErr = handle(player)
  313. if forErr != nil {
  314. return nil, forErr
  315. }
  316. }
  317. // Update the lastID for the next batch
  318. lastID = players[len(players)-1].ID
  319. }
  320. if forErr != nil {
  321. return nil, forErr
  322. }
  323. for i := 1; i <= 200; i++ {
  324. ni := int64(i)
  325. _, ok := levels[ni]
  326. // 等级不存在就进行补全
  327. if !ok {
  328. levels[ni] = make(map[string]int64)
  329. for _, t := range gmdata.Treasures {
  330. levels[ni][t.Name] = 0
  331. }
  332. continue
  333. }
  334. // 补全宝物
  335. for _, t := range gmdata.Treasures {
  336. _, ok2 := levels[ni][t.Name]
  337. if !ok2 {
  338. levels[ni][t.Name] = 0
  339. }
  340. }
  341. }
  342. for level, treasures := range levels {
  343. allResult = append(allResult, &LevelTreasure2{
  344. Level: level,
  345. Treasures: treasures,
  346. })
  347. }
  348. sort.Slice(allResult, func(i, j int) bool {
  349. return allResult[i].Level < allResult[j].Level // 从小到大排序
  350. })
  351. if len(allResult) > 0 {
  352. config.SetCache(key, allResult, now.BeginningOfDay().Unix()+86400)
  353. }
  354. results = s.filterLevel(params, allResult)
  355. return
  356. }
  357. // filterLevel 过滤等级
  358. func (s *sTreasure) filterLevel(params forms.TreasureLevelReq, allResult []*LevelTreasure2) (result []*LevelTreasure2) {
  359. for _, v := range allResult {
  360. if v.Level >= params.StartLevel && v.Level <= params.EndLevel {
  361. result = append(result, v)
  362. }
  363. }
  364. sort.Slice(result, func(i, j int) bool {
  365. return result[i].Level < result[j].Level // 从小到大排序
  366. })
  367. return
  368. }
  369. type StarTreasure struct {
  370. Star int64 `json:"star"`
  371. Treasures map[string]int64 `json:"treasures"`
  372. }
  373. func (s *sTreasure) Star(params forms.TreasureStarReq) (results []*StarTreasure, err error) {
  374. key := fmt.Sprintf("TreasureStar:ServerId:%v", params.ServerId)
  375. if err = config.GetCacheScan(key, &results); err != nil {
  376. return
  377. }
  378. if len(results) != 0 {
  379. return
  380. }
  381. var stars = make(map[int64]map[string]int64)
  382. handle := func(player *model.PlayerCache) (err error) {
  383. var treasures []*gmdata.DropedTreasure
  384. err = json.Unmarshal([]byte(player.Treasures), &treasures)
  385. if err != nil {
  386. return
  387. }
  388. for _, v := range treasures {
  389. tName := gmdata.GetTreasureByTID(v.TID)
  390. if tName == "" {
  391. logrus.Warnf("Star 无效TID:%+v", v)
  392. continue
  393. }
  394. star := gmdata.GetTreasureStar(v.TID)
  395. if star < 0 {
  396. logrus.Warnf("Star 无效TID2:%+v", v)
  397. continue
  398. }
  399. _, ok := stars[star]
  400. if !ok {
  401. stars[star] = make(map[string]int64)
  402. }
  403. if _, ok2 := stars[star][tName]; !ok2 {
  404. stars[star][tName] = 1
  405. } else {
  406. stars[star][tName] += 1
  407. }
  408. }
  409. return
  410. }
  411. lastID := int64(0)
  412. var forErr error
  413. for {
  414. var players []*model.PlayerCache
  415. // Perform the query with pagination and sorting by id greater than lastID
  416. dao := config.DB.Model(&model.PlayerCache{}).
  417. Select("`id`, `treasures`").
  418. Where("`level` > ? AND JSON_LENGTH(treasures) > 0", 10). // 10级以下基本不可能获得宝物,减少数据查询量做个范围过滤
  419. Where("id > ?", lastID)
  420. if params.ServerId > 0 {
  421. dao = dao.Where("server_id = ?", params.ServerId)
  422. }
  423. list := dao.Limit(1000).Order("id ASC").Scan(&players)
  424. if list.Error != nil {
  425. logrus.Errorf("RoleWear result.Error:%+v", list.Error)
  426. break
  427. }
  428. // If no more rows were fetched, break the loop
  429. if len(players) == 0 {
  430. break
  431. }
  432. // Process the retrieved players here as needed
  433. for _, player := range players {
  434. forErr = handle(player)
  435. if forErr != nil {
  436. return nil, forErr
  437. }
  438. }
  439. // Update the lastID for the next batch
  440. lastID = players[len(players)-1].ID
  441. }
  442. if forErr != nil {
  443. return nil, forErr
  444. }
  445. for i := 1; i <= 20; i++ {
  446. ni := int64(i)
  447. _, ok := stars[ni]
  448. // 等级不存在就进行补全
  449. if !ok {
  450. stars[ni] = make(map[string]int64)
  451. for _, t := range gmdata.Treasures {
  452. stars[ni][t.Name] = 0
  453. }
  454. continue
  455. }
  456. // 补全宝物
  457. for _, t := range gmdata.Treasures {
  458. _, ok2 := stars[ni][t.Name]
  459. if !ok2 {
  460. stars[ni][t.Name] = 0
  461. }
  462. }
  463. }
  464. for star, treasures := range stars {
  465. results = append(results, &StarTreasure{
  466. Star: star,
  467. Treasures: treasures,
  468. })
  469. }
  470. sort.Slice(results, func(i, j int) bool {
  471. return results[i].Star < results[j].Star // 从小到大排序
  472. })
  473. if len(results) > 0 {
  474. config.SetCache(key, results, now.BeginningOfDay().Unix()+86400)
  475. }
  476. return
  477. }