player.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. package service
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "fmt"
  6. "gadmin/config"
  7. "gadmin/internal/admin/forms"
  8. "gadmin/internal/gorm/query"
  9. "gadmin/package/gmdata"
  10. "strconv"
  11. "time"
  12. "github.com/gin-gonic/gin"
  13. "github.com/sirupsen/logrus"
  14. "github.com/xuri/excelize/v2"
  15. )
  16. type PlayerService struct {
  17. ctx *gin.Context
  18. }
  19. const (
  20. batchSize = 1000
  21. )
  22. func NewPlayerService(ctx *gin.Context) *PlayerService {
  23. return &PlayerService{
  24. ctx: ctx,
  25. }
  26. }
  27. func (p *PlayerService) ExportStatistics(req forms.StatisticsExportReq) {
  28. // 调用本函数会导致线上服务器重启,暂屏蔽
  29. return
  30. for i := 2; i >= 0; i-- {
  31. searchDay := time.Now().AddDate(0, -i, 0)
  32. //用户属性
  33. playerAttrList, err := p.getUserInfoByMonth(searchDay)
  34. if err != nil {
  35. logrus.WithField("method", "ExportStatistics").Errorf("获取用户属性失败: %v", err)
  36. return
  37. }
  38. if len(playerAttrList) == 0 {
  39. return
  40. }
  41. //用户充值数据
  42. playerRechargeMap, err := p.getUserRechargeData(searchDay)
  43. if err != nil {
  44. logrus.WithField("method", "ExportStatistics").Errorf("获取用户充值数据失败: %v", err)
  45. return
  46. }
  47. //用户属性数据清洗转换
  48. playerInfoList := make([]*forms.PlayerInfo, len(playerAttrList))
  49. // ====定义玩家参数map===
  50. var equipMap map[string]int64 //装备
  51. var roleArr []forms.PlayerRole //角色
  52. var passRoom []forms.PlayerChapter //关卡记录
  53. var bodyArr []forms.PlayerBody //部位
  54. var talentMap map[string]int //天赋
  55. var treasuresArr []forms.PlayerTreasure
  56. // ====定义玩家参数map===
  57. for n, item := range playerAttrList {
  58. //处理数据
  59. playerInfo := &forms.PlayerInfo{
  60. PlayerId: item.PlayerId,
  61. AccID: item.AccID,
  62. NickName: item.NickName,
  63. ServerID: int(item.ServerID),
  64. Level: item.Level,
  65. Exp: item.Exp,
  66. Stamina: item.Stamina,
  67. Coin: item.Coin,
  68. Diamond: int64(item.Diamond),
  69. Lastlogintick: time.Unix(item.Lastlogintick, 0).Format("2006-01-02 15:04:05"),
  70. Lastlogouttick: time.Unix(item.Lastlogouttick, 0).Format("2006-01-02 15:04:05"),
  71. CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
  72. }
  73. //玩家武器
  74. if item.FastEquips != "" {
  75. err = json.Unmarshal([]byte(item.FastEquips), &equipMap)
  76. if err != nil {
  77. logrus.WithField("method", "getUserExtraData").Errorf("玩家武器数据json umarshal错误: %v", err)
  78. return
  79. }
  80. playerWear := make([]gmdata.PlayerWear, 0)
  81. for _, equipId := range equipMap {
  82. equip := gmdata.PlayerWears[equipId]
  83. playerWear = append(playerWear, equip)
  84. }
  85. marshal, err := json.Marshal(playerWear)
  86. if err != nil {
  87. return
  88. }
  89. playerInfo.PlayerWear = string(marshal)
  90. }
  91. //玩家角色
  92. if item.Roles != "" {
  93. err = json.Unmarshal([]byte(item.Roles), &roleArr)
  94. if err != nil {
  95. logrus.WithField("method", "getUserExtraData").Errorf("玩家角色数据json umarshal错误: %v", err)
  96. return
  97. }
  98. playerRole := make([]gmdata.PlayerRole, 0)
  99. for _, role := range roleArr {
  100. playerRole = append(playerRole, gmdata.PlayerRoles[role.Id])
  101. }
  102. marshal, err := json.Marshal(playerRole)
  103. if err != nil {
  104. return
  105. }
  106. playerInfo.PlayerRole = string(marshal)
  107. }
  108. //玩家关卡
  109. if item.PassRoom != "" {
  110. err = json.Unmarshal([]byte(item.PassRoom), &passRoom)
  111. if err != nil {
  112. logrus.WithField("method", "getUserExtraData").Errorf("玩家关卡数据json umarshal错误: %v", err)
  113. return
  114. }
  115. playerChapter := make([]gmdata.PlayerChapter, 0)
  116. for _, chapter := range passRoom {
  117. if chapter.Pass == true {
  118. playerChapter = append(playerChapter, gmdata.PlayerChapters[chapter.ID])
  119. }
  120. }
  121. marshal, err := json.Marshal(playerChapter)
  122. if err != nil {
  123. return
  124. }
  125. playerInfo.PlayerChapter = string(marshal)
  126. }
  127. //玩家部位
  128. if item.Body != "" {
  129. err = json.Unmarshal([]byte(item.Body), &bodyArr)
  130. if err != nil {
  131. logrus.WithField("method", "getUserExtraData").Errorf("玩家部位数据json umarshal错误: %v", err)
  132. return
  133. }
  134. playerBody := make([]gmdata.PlayerBody, 0)
  135. for _, body := range bodyArr {
  136. playerBody = append(playerBody, gmdata.PlayerBody{
  137. Part: body.Part,
  138. Name: gmdata.BodyMap[body.Part],
  139. Level: body.Level,
  140. Step: body.Step,
  141. })
  142. }
  143. marshal, err := json.Marshal(playerBody)
  144. if err != nil {
  145. return
  146. }
  147. playerInfo.PlayerBody = string(marshal)
  148. }
  149. //玩家天赋
  150. if item.Talents != "" {
  151. err = json.Unmarshal([]byte(item.Talents), &talentMap)
  152. if err != nil {
  153. logrus.WithField("method", "getUserExtraData").Errorf("玩家天赋数据json umarshal错误: %v", err)
  154. return
  155. }
  156. playerTalent := make([]gmdata.PlayerTalent, 0)
  157. for tId, num := range talentMap {
  158. atoi, _ := strconv.Atoi(tId)
  159. talent := gmdata.PlayerTalents[atoi]
  160. talent.Number = num
  161. playerTalent = append(playerTalent, talent)
  162. }
  163. marshal, err := json.Marshal(playerTalent)
  164. if err != nil {
  165. return
  166. }
  167. playerInfo.PlayerTalent = string(marshal)
  168. }
  169. //玩家宝物
  170. if item.Treasures != "" {
  171. err = json.Unmarshal([]byte(item.Treasures), &treasuresArr)
  172. if err != nil {
  173. logrus.WithField("method", "getUserExtraData").Errorf("玩家宝物数据json umarshal错误: %v", err)
  174. return
  175. }
  176. playerTreasure := make([]gmdata.PlayerTreasure, 0)
  177. for _, treasure := range treasuresArr {
  178. playerTreasure = append(playerTreasure, gmdata.PlayerTreasures[treasure.TId])
  179. }
  180. marshal, err := json.Marshal(playerTreasure)
  181. if err != nil {
  182. return
  183. }
  184. playerInfo.PlayerTreasure = string(marshal)
  185. }
  186. if m, ok := playerRechargeMap[playerInfo.PlayerId]; ok {
  187. playerInfo.LastChargeAt = m.LastChargeAt
  188. playerInfo.FirstChargeAt = m.FirstChargeAt
  189. playerInfo.RechargeAmount = m.RechargeAmount
  190. playerInfo.RechargeCount = m.RechargeCount
  191. playerInfo.Total = m.Total
  192. playerInfo.Count = m.Count
  193. }
  194. //add
  195. playerInfoList[n] = playerInfo
  196. }
  197. p.exportToExcel(playerInfoList, searchDay, i)
  198. }
  199. }
  200. func (p *PlayerService) getUserInfoByMonth(searchDay time.Time) ([]forms.PlayerAttr, error) {
  201. searchTime := p.getSearchTime(searchDay)
  202. userInfos := make([]forms.PlayerAttr, 0)
  203. userInfoArr := make([]forms.PlayerAttr, 0)
  204. sql := "SELECT `player_attr`.`nickName` as nick_name,`player_attr`.`playerid` as player_id ,`player_attr`.`accId` as acc_id,`player_attr`.`serverId` as server_id,`player_attr`.`level`,`player_attr`.`exp`,`player_attr`.`stamina`,`player_attr`.`lastlogintick`, `player_attr`.`lastlogouttick`, `player_attr`.`create_time`, `player_material`.`coin`, `player_material`.`diamond`, `player_material`.`payCount` as pay_count, `player_material`.`fastEquips` as fast_equips, `player_material`.`roles`, `player_material`.`talents`, `player_material`.`treasures`, `player_material`.`body`, `player_material`.`passRoom` as pass_room FROM `player_attr` LEFT JOIN `player_material` ON `player_material`.`playerid` = `player_attr`.`playerid` WHERE `player_attr`.`create_time` >= ? AND `player_attr`.`create_time` <= ?"
  205. //sql2 := "SELECT `player_attr`.`playerid`,`player_attr`.`accId`,`player_attr`.`nickName`,`player_attr`.`serverId` FROM `player_attr` WHERE `player_attr`.`create_time` >= ?"
  206. var userInfo forms.PlayerAttr
  207. for _, db := range config.GDBGroup {
  208. rows, err := db.Raw(sql, searchTime.FirstDayTimeString, searchTime.LastDayTimesString).Rows()
  209. for rows.Next() {
  210. err = db.ScanRows(rows, &userInfo)
  211. if err != nil {
  212. return nil, err
  213. }
  214. userInfos = append(userInfos, userInfo)
  215. }
  216. if err != nil {
  217. return nil, err
  218. }
  219. userInfoArr = append(userInfoArr, userInfos...)
  220. }
  221. return userInfoArr, nil
  222. }
  223. func (p *PlayerService) getUserRechargeData(searchDay time.Time) (map[int64]*forms.PlayerRecharge, error) {
  224. searchTime := p.getSearchTime(searchDay)
  225. //充值数据 logDB.order 、 消费数据logDB.changedLog
  226. //1. 充值金额与次数
  227. playerRecharge := make([]*forms.PlayerRecharge, 0)
  228. orderQuery := query.Use(config.DB).Order
  229. orderQueryDo := orderQuery.WithContext(p.ctx)
  230. err := orderQueryDo.
  231. Select(orderQuery.Money.Sum().As("total"), orderQuery.ID.Count().As("count"), orderQuery.PlayerID).
  232. Where(orderQuery.CreatedAt.Gte(searchDay)).
  233. Where(orderQuery.Status.Eq(1)).
  234. Where(orderQuery.CreatedAt.Between(time.Unix(searchTime.FirstDayTimestamp, 0), time.Unix(searchTime.LastDayTimestamp, 0))).
  235. Group(orderQuery.PlayerID).
  236. Scan(&playerRecharge)
  237. if err != nil {
  238. logrus.WithField("method", "getUserRechargeData").Errorf("查询玩家充值金额与次数失败: %v", err)
  239. return nil, err
  240. }
  241. //playerRecharge转为map
  242. playerRechargeMap := make(map[int64]*forms.PlayerRecharge)
  243. for _, item := range playerRecharge {
  244. playerRechargeMap[item.PlayerId] = item
  245. }
  246. // 第一次和最后一次充值
  247. var RechargeSql = "SELECT\n first_order.id AS first_id,\n first_order.player_id AS player_id,\n first_order.created_at AS first_created_at,\n last_order.id AS last_id,\n last_order.created_at AS last_created_at\nFROM\n (SELECT\n id,\n player_id,\n created_at\n FROM\n orders\n WHERE\n created_at between @start and @end\n ORDER BY\n created_at\n LIMIT 1) AS first_order\nJOIN\n (SELECT\n id,\n player_id,\n created_at\n FROM\n orders\n WHERE\n created_at between @start and @end\n ORDER BY\n created_at DESC\n LIMIT 1) AS last_order\nON\n first_order.player_id = last_order.player_id"
  248. err = config.DB.Raw(RechargeSql, sql.Named("start", searchTime.FirstDayTimeString), sql.Named("end", searchTime.LastDayTimesString)).Scan(&playerRecharge).Error
  249. if err != nil {
  250. logrus.WithField("method", "getUserRechargeData").Errorf("查询玩家充值时间失败: %v", err)
  251. return nil, err
  252. }
  253. for _, item := range playerRecharge {
  254. if recharge, ok := playerRechargeMap[item.PlayerId]; ok {
  255. recharge.FirstChargeAt = item.FirstChargeAt
  256. recharge.LastChargeAt = item.LastChargeAt
  257. recharge.RechargeCount = item.RechargeCount
  258. recharge.RechargeAmount = item.RechargeAmount
  259. }
  260. }
  261. return playerRechargeMap, nil
  262. }
  263. func (p *PlayerService) getSearchTime(searchDay time.Time) forms.SearchTime {
  264. // 获取月份第一天 00:00:00 时间戳
  265. firstDayOfMonth := time.Date(searchDay.Year(), searchDay.Month(), 1, 0, 0, 0, 0, time.Local)
  266. firstDayTimestamp := firstDayOfMonth.Unix()
  267. firstDayTimeString := firstDayOfMonth.Format("2006-01-02 15:04:05")
  268. fmt.Println("月份第一天 00:00:00 时间戳:", firstDayTimestamp)
  269. // 获取月份最后一天 23:59:59 时间戳
  270. lastDayOfMonth := firstDayOfMonth.AddDate(0, 1, -1)
  271. lastDayTimestamp := lastDayOfMonth.Add(time.Duration(23*time.Hour + 59*time.Minute + 59*time.Second)).Unix()
  272. lastDayTimeString := lastDayOfMonth.Add(time.Duration(23*time.Hour + 59*time.Minute + 59*time.Second)).Format("2006-01-02 15:04:05")
  273. fmt.Println("月份最后一天 23:59:59 时间戳:", lastDayTimestamp)
  274. return forms.SearchTime{
  275. FirstDayTimestamp: firstDayTimestamp,
  276. FirstDayTimeString: firstDayTimeString,
  277. LastDayTimestamp: lastDayTimestamp,
  278. LastDayTimesString: lastDayTimeString,
  279. }
  280. }
  281. func (p *PlayerService) exportToExcel(dataList []*forms.PlayerInfo, searchDay time.Time, n int) {
  282. searchName := searchDay.Format("200601")
  283. f := excelize.NewFile()
  284. sheetName := fmt.Sprintf("sheet_%s_%d", searchName, n)
  285. f.NewSheet(sheetName)
  286. streamWriter, err := f.NewStreamWriter(sheetName)
  287. if err != nil {
  288. logrus.WithField("method", "writeToFile").Errorf("创建流式写入器失败: %v", err)
  289. return
  290. }
  291. // 写入表头
  292. row := []interface{}{"玩家ID", "账号ID", "服务器ID", "昵称", "等级", "经验", "体力", "金币", "钻石", "最后登录", "最后登出", "注册时间", "首充时间", "最后充值时间", "充值总额", "充值次数", "武器", "角色", "部位", "天赋", "宝物", "关卡"}
  293. if err := streamWriter.SetRow("A1", row); err != nil {
  294. logrus.WithField("method", "writeToFile").Errorf("写入表头失败: %v", err)
  295. return
  296. }
  297. // 写入数据
  298. for i, playerInfo := range dataList {
  299. row := []interface{}{
  300. playerInfo.PlayerId, playerInfo.AccID, playerInfo.ServerID, playerInfo.NickName,
  301. playerInfo.Level, playerInfo.Exp, playerInfo.Stamina, playerInfo.Coin, playerInfo.Diamond,
  302. playerInfo.Lastlogintick, playerInfo.Lastlogouttick, playerInfo.CreateTime,
  303. playerInfo.FirstChargeAt, playerInfo.LastChargeAt, playerInfo.RechargeAmount, playerInfo.RechargeCount,
  304. playerInfo.PlayerWear, playerInfo.PlayerRole, playerInfo.PlayerBody, playerInfo.PlayerTalent,
  305. playerInfo.PlayerTreasure, playerInfo.PlayerChapter,
  306. }
  307. if err := streamWriter.SetRow(fmt.Sprintf("A%d", i+2), row); err != nil {
  308. logrus.WithField("method", "writeToFile").Errorf("写入数据失败: %v", err)
  309. return
  310. }
  311. }
  312. if err := streamWriter.Flush(); err != nil {
  313. logrus.WithField("method", "writeToFile").Errorf("刷新流式写入器失败: %v", err)
  314. return
  315. }
  316. if err := f.SaveAs("./player_statistics.xlsx"); err != nil {
  317. logrus.WithField("method", "writeToFile").Errorf("保存文件失败: %v", err)
  318. return
  319. }
  320. }