chapter.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. package service
  2. import (
  3. "fmt"
  4. "gadmin/config"
  5. "gadmin/internal/admin/consts"
  6. "gadmin/internal/admin/forms"
  7. "gadmin/internal/elastic/eapi"
  8. "gadmin/internal/gorm/model"
  9. "gadmin/internal/gorm/query"
  10. "gadmin/utility"
  11. "github.com/gin-gonic/gin"
  12. "github.com/sirupsen/logrus"
  13. "os"
  14. "strings"
  15. )
  16. // Chapter 章节服务
  17. var Chapter = new(sChapter)
  18. type sChapter struct{}
  19. //func (s *sChapter) OrdinaryList(ctx *gin.Context, req forms.ChapterOrdinaryListReq) (resp serializer.Response) {
  20. // type passData struct {
  21. // Id int32 `json:"id"`
  22. // Name string `json:"name"`
  23. // RoomCount int32 `json:"roomCount"`
  24. // Count int64 `json:"count"`
  25. // Difficulty int64 `json:"difficulty"`
  26. // Pass bool `json:"pass"`
  27. // IsActivity bool `json:"isActivity"`
  28. // }
  29. //
  30. // DB, err := player.GetDBByUserId(req.PlayerId)
  31. // if err != nil {
  32. // return serializer.Err(consts.CodeParamErr, "GetDBByUserId err", err)
  33. // }
  34. //
  35. // var (
  36. // p = query.Use(config.GDBGroup[DB]).PlayerMaterial
  37. // passDatas []passData
  38. // models forms.ListRes
  39. // )
  40. // material, err := p.WithContext(ctx).Where(p.Playerid.Eq(req.PlayerId)).First()
  41. // if err != nil {
  42. // return serializer.Err(consts.CodeParamErr, "查询出错 Find", err)
  43. // }
  44. //
  45. // if material == nil || material.ID == 0 || material.PassRoom == "null" {
  46. // return serializer.Suc(models)
  47. // }
  48. //
  49. // if err := json.Unmarshal([]byte(material.PassRoom), &passDatas); err != nil {
  50. // return serializer.Err(consts.CodeParamErr, "Unmarshal err", err)
  51. // }
  52. //
  53. // tmp := make([]passData, 0)
  54. // for k, v := range passDatas {
  55. // if v.Id == 0 || v.Id > 99 {
  56. // continue
  57. // }
  58. // data := gmdata.GetChapterById(v.Id)
  59. // if data == nil {
  60. // data = &gmdata.Chapter{
  61. // Name: `未知`,
  62. // }
  63. // }
  64. // passDatas[k].Name = data.Name
  65. // passDatas[k].RoomCount = data.RoomCount
  66. // tmp = append(tmp, passDatas[k])
  67. // }
  68. //
  69. // // 使用自定义的 Less 函数来指定排序规则
  70. // sort.SliceStable(tmp, func(i, j int) bool {
  71. // if tmp[i].Id != tmp[j].Id {
  72. // return tmp[i].Id < tmp[j].Id // 根据 Id 升序排列
  73. // }
  74. // return tmp[i].Difficulty < tmp[j].Difficulty // 如果 Id 相同,则根据 Difficulty 升序排列
  75. // })
  76. //
  77. // models.List = tmp
  78. //
  79. // return serializer.Suc(models)
  80. //}
  81. //func (s *sChapter) ActivityList(ctx *gin.Context, req forms.ChapterActivityListReq) (resp serializer.Response) {
  82. // type passData struct {
  83. // Id int32 `json:"id"`
  84. // Difficulty int32 `json:"difficulty"`
  85. // DifficultyName string `json:"difficultyName"`
  86. // Name string `json:"name"`
  87. // Degree int32 `json:"degree"`
  88. // RewardMulti float64 `json:"rewardMulti"`
  89. // RewardEffectTime int64 `json:"rewardEffectTime"`
  90. // ChallengeCount int64 `json:"challenge_count"`
  91. // ClearanceCount int64 `json:"clearance_count"`
  92. // FailCount int64 `json:"fail_count"`
  93. // }
  94. //
  95. // DB, err := player.GetDBByUserId(req.PlayerId)
  96. // if err != nil {
  97. // return serializer.Err(consts.CodeParamErr, "GetDBByUserId err", err)
  98. // }
  99. //
  100. // var (
  101. // c = query.Use(config.DB).Chapter
  102. // passDatas []passData
  103. // models forms.ListRes
  104. // z = query.Use(config.GDBGroup[DB]).ZoneActivity
  105. // )
  106. // chapters, err := c.WithContext(ctx).Where(c.PlayerID.Eq(req.PlayerId)).Find()
  107. // if err != nil {
  108. // return serializer.Err(consts.CodeParamErr, "查询出错 Find", err)
  109. // }
  110. //
  111. // if len(chapters) == 0 {
  112. // return serializer.Suc(models)
  113. // }
  114. //
  115. // zone, err := z.WithContext(ctx).Where(z.Playerid.Eq(req.PlayerId)).First()
  116. // if err != nil {
  117. // return serializer.Err(consts.CodeParamErr, "查询出错 Find", err)
  118. // }
  119. //
  120. // // BonusProperties 加成属性
  121. // type BonusProperties struct {
  122. // DifScale int `json:"difScale"` // 难度系数
  123. // RewardMulti float64 `json:"rewardMulti"` // 奖励倍数
  124. // RewardEffectTime int64 `json:"rewardEffectTime"` // 奖励倍数生效时间
  125. // }
  126. //
  127. // bonusProperties := make(map[string]*BonusProperties)
  128. // //difficulty := make(map[string]int32)
  129. // if zone != nil {
  130. // if err = json.Unmarshal([]byte(zone.BonusProperties), &bonusProperties); err != nil {
  131. // return serializer.Err(consts.CodeParamErr, "Unmarshal err", err)
  132. // }
  133. // }
  134. //
  135. // logrus.Warnf("bonusProperties:%+v", bonusProperties)
  136. //
  137. // //chapterIds := []int64{100, 101, 102, 103}
  138. // //var difficultys = []int32{0, 1, 2}
  139. //
  140. // for _, v := range chapters {
  141. // var data = gmdata.GetChapterById(v.ChapterID)
  142. // if data == nil {
  143. // data = &gmdata.Chapter{
  144. // Name: `未知`,
  145. // }
  146. // }
  147. //
  148. // //var ds = []int32{0, 1, 2}
  149. // //if v.ChapterID == 103 {
  150. // // ds = []int32{0}
  151. // //}
  152. // //
  153. // //for _, difficulty := range ds {
  154. // //
  155. // //
  156. // //}
  157. //
  158. // bs, ok := bonusProperties[fmt.Sprintf("%v_%v", v.ChapterID, v.Difficulty)]
  159. // if ok {
  160. // passDatas = append(passDatas, passData{
  161. // Id: v.ChapterID,
  162. // Difficulty: v.Difficulty,
  163. // DifficultyName: gmdata.GetDifficultName(int64(v.Difficulty)),
  164. // Name: data.Name,
  165. // Degree: int32(bs.DifScale),
  166. // RewardMulti: bs.RewardMulti,
  167. // RewardEffectTime: bs.RewardEffectTime,
  168. // ChallengeCount: int64(v.ClearanceCount + v.FailCount),
  169. // ClearanceCount: int64(v.ClearanceCount),
  170. // FailCount: int64(v.FailCount),
  171. // })
  172. // } else {
  173. // // 没有数据视为没有参与过,直接不显示
  174. // //passDatas = append(passDatas, passData{
  175. // // Id: v.ChapterID,
  176. // // Difficulty: difficulty,
  177. // // Name: data.Name,
  178. // // Degree: 100, // 这里写死了100,其实有的初始值并不是100,需要知道
  179. // // RewardMulti: 1,
  180. // // RewardEffectTime: 0,
  181. // // ChallengeCount: int64(v.ClearanceCount + v.FailCount),
  182. // // ClearanceCount: int64(v.ClearanceCount),
  183. // // FailCount: int64(v.FailCount),
  184. // //})
  185. // }
  186. //
  187. // }
  188. //
  189. // // 远征
  190. // var (
  191. // cf = query.Use(config.GDBGroup[DB]).Climbfloor
  192. // )
  193. // cfd, _ := cf.WithContext(ctx).Where(cf.Playerid.Eq(req.PlayerId)).First()
  194. // //if err != nil {
  195. // // return serializer.Err(consts.CodeParamErr, "查询出错 Climbfloor Find", err)
  196. // //}
  197. //
  198. // if cfd == nil {
  199. // passDatas = append(passDatas, passData{
  200. // Id: 105,
  201. // Name: "远征",
  202. // Degree: 0,
  203. // DifficultyName: gmdata.GetDifficultName(int64(0)),
  204. // //ChallengeCount: int64(v.ClearanceCount + v.FailCount),
  205. // //ClearanceCount: int64(v.ClearanceCount),
  206. // //FailCount: int64(v.FailCount),
  207. // })
  208. // } else {
  209. // passDatas = append(passDatas, passData{
  210. // Id: 105,
  211. // Name: "远征",
  212. // Degree: cfd.MaxFloor,
  213. // DifficultyName: gmdata.GetDifficultName(int64(0)),
  214. // //ChallengeCount: int64(v.ClearanceCount + v.FailCount),
  215. // //ClearanceCount: int64(v.ClearanceCount),
  216. // //FailCount: int64(v.FailCount),
  217. // })
  218. // }
  219. //
  220. // models.List = passDatas
  221. //
  222. // return serializer.Suc(models)
  223. //}
  224. func (s *sChapter) doReconnectFromDB(ctx *gin.Context, params forms.ChapterReconnectReq, chapterId, difficulty int32, isNew bool) (count, userCount int64, err error) {
  225. type Result struct {
  226. TotalCount int64 `gorm:"total_count"`
  227. UserCount int64 `gorm:"user_count"`
  228. }
  229. begin, end, _ := utility.GetBeginAndEndOfDay2(params.Day, params.EndDay)
  230. end = end + 1
  231. table := model.GetChapterTable2(chapterId)
  232. var (
  233. q = query.Use(config.DB).ChapterLog.Table(table)
  234. db = q.UnderlyingDB()
  235. )
  236. db = db.Where("event_id = 2 and extra like '%0%' and difficulty = ? and event_at >= ? and event_at <= ?", difficulty, begin, end)
  237. if params.ServerId > 0 {
  238. db = db.Where("server_id = ?", params.ServerId)
  239. }
  240. switch params.ChannelId {
  241. case consts.ChannelIdNone:
  242. // 不选择渠道
  243. case consts.ChannelIdAllAdv, consts.ChannelIdAllWx, consts.ChannelIdAllTT:
  244. // 所有广告渠道
  245. db = db.Where("channel_id in (?)", Channel.GetIdsByType(params.ChannelId))
  246. default:
  247. // 选择指定渠道
  248. db = db.Where("channel_id = ?", params.ChannelId)
  249. }
  250. res := make([]*Result, 0)
  251. if isNew {
  252. err = db.Select("count(*) AS total_count, count( DISTINCT user_id ) AS user_count, DATE (FROM_UNIXTIME( user_created_at )) AS d").Where("user_created_at >= ? and user_created_at <= ?", begin, end).Group("d").Find(&res).Error
  253. } else {
  254. err = db.Select("count(*) AS total_count, count( DISTINCT user_id ) AS user_count").Find(&res).Error
  255. }
  256. if err != nil {
  257. logrus.Errorf("查询出错:%+v", err)
  258. if strings.Contains(err.Error(), "1146") {
  259. createSql := "CREATE TABLE IF NOT EXISTS `%s` (`id` bigint unsigned NOT NULL AUTO_INCREMENT,`channel_id` varchar(128) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '渠道ID',`flag` int DEFAULT '0',`user_id` bigint unsigned NOT NULL COMMENT '用户id',`server_id` int NOT NULL DEFAULT '1' COMMENT '服务器ID',`event_id` tinyint unsigned NOT NULL COMMENT '埋点id',`chapter_id` smallint unsigned NOT NULL COMMENT '关卡id',`difficulty` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '困难度',`room_id` bigint NOT NULL COMMENT '房间id',`user_created_at` int unsigned NOT NULL COMMENT '用户注册时间',`event_at` int unsigned NOT NULL COMMENT '埋点事件触发的时间',`event_at_ns` bigint unsigned NOT NULL,`token` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '登陆的token',`extra` text COLLATE utf8mb4_general_ci COMMENT '其他参数',PRIMARY KEY (`id`),UNIQUE KEY `user_event_ns` (`user_id`,`event_at_ns`),KEY `created_chapter_event_event_at_idx` (`user_created_at`,`chapter_id`,`event_id`,`event_at`),KEY `event_id_event_at_idx` (`event_id`,`event_at`),KEY `server_id` (`server_id`),KEY `channel_id` (`channel_id`),KEY `idx_event_id` (`event_id`) USING BTREE,KEY `server_id_difficulty_user_id_event_id_extra_index` (`server_id`,`difficulty`,`user_id`,`event_id`),KEY `chapter_logs_100_event_at_index` (`event_at`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='关卡信息埋点日志';"
  260. if err := q.UnderlyingDB().Exec(fmt.Sprintf(createSql, table)).Error; err != nil {
  261. logrus.Errorf("创建表%s错误:%+v", table, err)
  262. }
  263. }
  264. return 0, 0, err
  265. }
  266. for _, v := range res {
  267. count += v.TotalCount
  268. userCount += v.UserCount
  269. }
  270. return
  271. }
  272. func (s *sChapter) doReconnect(ctx *gin.Context, params forms.ChapterReconnectReq, chapterId, difficulty int32, isNew bool) (count, userCount int64, err error) {
  273. begin, end, _ := utility.GetBeginAndEndOfDay2(params.Day, params.EndDay)
  274. end = end + 1
  275. index := fmt.Sprintf("%s%s", os.Getenv("ELASTIC_TOPIC"), model.GetChapterTable2(chapterId))
  276. var (
  277. mustNot []eapi.M
  278. where eapi.M
  279. )
  280. must := []eapi.M{
  281. {
  282. "term": eapi.M{
  283. "event_id": "2",
  284. },
  285. },
  286. {
  287. "match": eapi.M{
  288. "extra": "0",
  289. },
  290. },
  291. {
  292. "term": eapi.M{
  293. "chapter_id": chapterId,
  294. },
  295. },
  296. {
  297. "term": eapi.M{
  298. "difficulty": difficulty,
  299. },
  300. },
  301. {
  302. "range": eapi.M{
  303. "event_at": eapi.M{
  304. "gte": begin,
  305. "lte": end,
  306. },
  307. },
  308. },
  309. }
  310. if params.ServerId > 0 {
  311. must = append(must, eapi.M{
  312. "term": eapi.M{
  313. "server_id": params.ServerId,
  314. },
  315. })
  316. }
  317. switch params.ChannelId {
  318. case consts.ChannelIdNone:
  319. // 不选择渠道
  320. case consts.ChannelIdAllAdv, consts.ChannelIdAllWx, consts.ChannelIdAllTT:
  321. // 所有广告渠道
  322. must = append(must, eapi.M{
  323. "terms": eapi.M{
  324. "channel_id": Channel.GetIdsByType(params.ChannelId),
  325. },
  326. })
  327. default:
  328. // 选择指定渠道
  329. must = append(must, eapi.M{
  330. "term": eapi.M{
  331. "channel_id": params.ChannelId,
  332. },
  333. })
  334. }
  335. //if params.ChannelId != "" {
  336. // if params.ChannelId == "1" {
  337. // mustNot = append(mustNot, eapi.M{
  338. // "term": eapi.M{
  339. // "channel_id": "0",
  340. // },
  341. // })
  342. // } else {
  343. // must = append(must, eapi.M{
  344. // "term": eapi.M{
  345. // "channel_id": params.ChannelId,
  346. // },
  347. // })
  348. // }
  349. //
  350. //}
  351. // 新用户
  352. if isNew {
  353. // 同一天
  354. if begin+86400 == end {
  355. must = append(must, eapi.M{
  356. "range": eapi.M{
  357. "user_created_at": eapi.M{
  358. "gte": begin,
  359. "lte": end,
  360. },
  361. },
  362. })
  363. } else {
  364. // 多天的,分析每天的新用户
  365. for i := begin; i < end; i += 86400 {
  366. var newMust = must
  367. newMust = append(newMust, eapi.M{
  368. "range": eapi.M{
  369. "user_created_at": eapi.M{
  370. "gte": i,
  371. "lte": i + 86400,
  372. },
  373. },
  374. })
  375. if len(mustNot) > 0 {
  376. where = eapi.M{
  377. "query": eapi.M{
  378. "bool": eapi.M{
  379. "must": newMust,
  380. "must_not": mustNot,
  381. },
  382. },
  383. }
  384. } else {
  385. where = eapi.M{
  386. "query": eapi.M{
  387. "bool": eapi.M{
  388. "must": newMust,
  389. },
  390. },
  391. }
  392. }
  393. res, err := eapi.Count(ctx, index, where)
  394. if err != nil {
  395. return 0, 0, err
  396. }
  397. if res > 0 {
  398. count += res
  399. }
  400. res2, err := eapi.Cardinality(ctx, index, where, "user_id")
  401. if err != nil {
  402. return 0, 0, err
  403. }
  404. if res2 > 0 {
  405. userCount += res2
  406. }
  407. }
  408. return
  409. }
  410. }
  411. if len(mustNot) > 0 {
  412. where = eapi.M{
  413. "query": eapi.M{
  414. "bool": eapi.M{
  415. "must": must,
  416. "must_not": mustNot,
  417. },
  418. },
  419. }
  420. } else {
  421. where = eapi.M{
  422. "query": eapi.M{
  423. "bool": eapi.M{
  424. "must": must,
  425. },
  426. },
  427. }
  428. }
  429. //logrus.Warnf("doReconnect index:%v\n where:%v\n", index, utility.DumpToJSON(where))
  430. res, err := eapi.Count(ctx, index, where)
  431. if err != nil {
  432. return 0, 0, err
  433. }
  434. res2, err := eapi.Cardinality(ctx, index, where, "user_id")
  435. if err != nil {
  436. return 0, 0, err
  437. }
  438. return res, res2, nil
  439. }
  440. //func (s *sChapter) Reconnect(ctx *gin.Context, params forms.ChapterReconnectReq) (resp serializer.Response) {
  441. // chapterMap := gmdata.GetChaptersMap()
  442. // var (
  443. // wg sync.WaitGroup
  444. // errCh = make(chan error, len(chapterMap))
  445. // )
  446. //
  447. // list := make([]forms.ChapterReconnectItem, 0)
  448. // for _, v := range chapterMap {
  449. // wg.Add(1)
  450. // go func(v *gmdata.Chapter) {
  451. // defer wg.Done()
  452. // //count, users, err := s.doReconnect(ctx, params, int32(v.ID), int32(v.Difficulty), false)
  453. // count, users, err := s.doReconnectFromDB(ctx, params, int32(v.ID), int32(v.Difficulty), false)
  454. // if err != nil {
  455. // errCh <- err
  456. // return
  457. // }
  458. //
  459. // //newCount, newUsers, err := s.doReconnect(ctx, params, int32(v.ID), int32(v.Difficulty), true)
  460. // newCount, newUsers, err := s.doReconnectFromDB(ctx, params, int32(v.ID), int32(v.Difficulty), true)
  461. // if err != nil {
  462. // errCh <- err
  463. // return
  464. // }
  465. //
  466. // list = append(list, forms.ChapterReconnectItem{
  467. // Id: len(list),
  468. // ChapterId: int(v.ID),
  469. // Chapter: Dash.GetChapterName(v.ID, v.Difficulty),
  470. // DifficultyIndex: int(v.Difficulty),
  471. // Difficulty: gmdata.GetDifficultName(v.Difficulty),
  472. // DieCount: count,
  473. // DieUserCount: users,
  474. // NewDieCount: newCount,
  475. // NewDieUserCount: newUsers,
  476. // })
  477. // }(v)
  478. // }
  479. //
  480. // wg.Wait()
  481. //
  482. // select {
  483. // case err1 := <-errCh:
  484. // return serializer.Err(consts.CodeParamErr, err1.Error(), err1)
  485. // default:
  486. //
  487. // }
  488. //
  489. // for k, v := range list {
  490. // list[k].Index = v.ChapterId*1000 + v.DifficultyIndex
  491. // }
  492. // sort.Sort(forms.ChapterReconnectItemSlice(list))
  493. //
  494. // pageCount := int64(len(list))
  495. // models := new(forms.ChapterReconnectRespData)
  496. // models.Page = params.Page
  497. // models.PerPage = params.PerPage
  498. // models.PageCount = int64(math.Ceil(float64(pageCount) / float64(params.PerPage)))
  499. //
  500. // startInx := (models.Page - 1) * models.PerPage
  501. // endInx := (models.Page) * models.PerPage
  502. // if startInx < 0 {
  503. // startInx = 0
  504. // }
  505. // if endInx > pageCount {
  506. // endInx = pageCount
  507. // }
  508. // subList := list[startInx:endInx]
  509. //
  510. // models.List = subList
  511. // return serializer.Suc(models)
  512. //}