123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- package service
- import (
- "database/sql"
- "encoding/json"
- "fmt"
- "gadmin/config"
- "gadmin/internal/admin/forms"
- "gadmin/internal/gorm/query"
- "gadmin/package/gmdata"
- "strconv"
- "time"
- "github.com/gin-gonic/gin"
- "github.com/sirupsen/logrus"
- "github.com/xuri/excelize/v2"
- )
- type PlayerService struct {
- ctx *gin.Context
- }
- const (
- batchSize = 1000
- )
- func NewPlayerService(ctx *gin.Context) *PlayerService {
- return &PlayerService{
- ctx: ctx,
- }
- }
- func (p *PlayerService) ExportStatistics(req forms.StatisticsExportReq) {
- // 调用本函数会导致线上服务器重启,暂屏蔽
- return
- for i := 2; i >= 0; i-- {
- searchDay := time.Now().AddDate(0, -i, 0)
- //用户属性
- playerAttrList, err := p.getUserInfoByMonth(searchDay)
- if err != nil {
- logrus.WithField("method", "ExportStatistics").Errorf("获取用户属性失败: %v", err)
- return
- }
- if len(playerAttrList) == 0 {
- return
- }
- //用户充值数据
- playerRechargeMap, err := p.getUserRechargeData(searchDay)
- if err != nil {
- logrus.WithField("method", "ExportStatistics").Errorf("获取用户充值数据失败: %v", err)
- return
- }
- //用户属性数据清洗转换
- playerInfoList := make([]*forms.PlayerInfo, len(playerAttrList))
- // ====定义玩家参数map===
- var equipMap map[string]int64 //装备
- var roleArr []forms.PlayerRole //角色
- var passRoom []forms.PlayerChapter //关卡记录
- var bodyArr []forms.PlayerBody //部位
- var talentMap map[string]int //天赋
- var treasuresArr []forms.PlayerTreasure
- // ====定义玩家参数map===
- for n, item := range playerAttrList {
- //处理数据
- playerInfo := &forms.PlayerInfo{
- PlayerId: item.PlayerId,
- AccID: item.AccID,
- NickName: item.NickName,
- ServerID: int(item.ServerID),
- Level: item.Level,
- Exp: item.Exp,
- Stamina: item.Stamina,
- Coin: item.Coin,
- Diamond: int64(item.Diamond),
- Lastlogintick: time.Unix(item.Lastlogintick, 0).Format("2006-01-02 15:04:05"),
- Lastlogouttick: time.Unix(item.Lastlogouttick, 0).Format("2006-01-02 15:04:05"),
- CreateTime: item.CreateTime.Format("2006-01-02 15:04:05"),
- }
- //玩家武器
- if item.FastEquips != "" {
- err = json.Unmarshal([]byte(item.FastEquips), &equipMap)
- if err != nil {
- logrus.WithField("method", "getUserExtraData").Errorf("玩家武器数据json umarshal错误: %v", err)
- return
- }
- playerWear := make([]gmdata.PlayerWear, 0)
- for _, equipId := range equipMap {
- equip := gmdata.PlayerWears[equipId]
- playerWear = append(playerWear, equip)
- }
- marshal, err := json.Marshal(playerWear)
- if err != nil {
- return
- }
- playerInfo.PlayerWear = string(marshal)
- }
- //玩家角色
- if item.Roles != "" {
- err = json.Unmarshal([]byte(item.Roles), &roleArr)
- if err != nil {
- logrus.WithField("method", "getUserExtraData").Errorf("玩家角色数据json umarshal错误: %v", err)
- return
- }
- playerRole := make([]gmdata.PlayerRole, 0)
- for _, role := range roleArr {
- playerRole = append(playerRole, gmdata.PlayerRoles[role.Id])
- }
- marshal, err := json.Marshal(playerRole)
- if err != nil {
- return
- }
- playerInfo.PlayerRole = string(marshal)
- }
- //玩家关卡
- if item.PassRoom != "" {
- err = json.Unmarshal([]byte(item.PassRoom), &passRoom)
- if err != nil {
- logrus.WithField("method", "getUserExtraData").Errorf("玩家关卡数据json umarshal错误: %v", err)
- return
- }
- playerChapter := make([]gmdata.PlayerChapter, 0)
- for _, chapter := range passRoom {
- if chapter.Pass == true {
- playerChapter = append(playerChapter, gmdata.PlayerChapters[chapter.ID])
- }
- }
- marshal, err := json.Marshal(playerChapter)
- if err != nil {
- return
- }
- playerInfo.PlayerChapter = string(marshal)
- }
- //玩家部位
- if item.Body != "" {
- err = json.Unmarshal([]byte(item.Body), &bodyArr)
- if err != nil {
- logrus.WithField("method", "getUserExtraData").Errorf("玩家部位数据json umarshal错误: %v", err)
- return
- }
- playerBody := make([]gmdata.PlayerBody, 0)
- for _, body := range bodyArr {
- playerBody = append(playerBody, gmdata.PlayerBody{
- Part: body.Part,
- Name: gmdata.BodyMap[body.Part],
- Level: body.Level,
- Step: body.Step,
- })
- }
- marshal, err := json.Marshal(playerBody)
- if err != nil {
- return
- }
- playerInfo.PlayerBody = string(marshal)
- }
- //玩家天赋
- if item.Talents != "" {
- err = json.Unmarshal([]byte(item.Talents), &talentMap)
- if err != nil {
- logrus.WithField("method", "getUserExtraData").Errorf("玩家天赋数据json umarshal错误: %v", err)
- return
- }
- playerTalent := make([]gmdata.PlayerTalent, 0)
- for tId, num := range talentMap {
- atoi, _ := strconv.Atoi(tId)
- talent := gmdata.PlayerTalents[atoi]
- talent.Number = num
- playerTalent = append(playerTalent, talent)
- }
- marshal, err := json.Marshal(playerTalent)
- if err != nil {
- return
- }
- playerInfo.PlayerTalent = string(marshal)
- }
- //玩家宝物
- if item.Treasures != "" {
- err = json.Unmarshal([]byte(item.Treasures), &treasuresArr)
- if err != nil {
- logrus.WithField("method", "getUserExtraData").Errorf("玩家宝物数据json umarshal错误: %v", err)
- return
- }
- playerTreasure := make([]gmdata.PlayerTreasure, 0)
- for _, treasure := range treasuresArr {
- playerTreasure = append(playerTreasure, gmdata.PlayerTreasures[treasure.TId])
- }
- marshal, err := json.Marshal(playerTreasure)
- if err != nil {
- return
- }
- playerInfo.PlayerTreasure = string(marshal)
- }
- if m, ok := playerRechargeMap[playerInfo.PlayerId]; ok {
- playerInfo.LastChargeAt = m.LastChargeAt
- playerInfo.FirstChargeAt = m.FirstChargeAt
- playerInfo.RechargeAmount = m.RechargeAmount
- playerInfo.RechargeCount = m.RechargeCount
- playerInfo.Total = m.Total
- playerInfo.Count = m.Count
- }
- //add
- playerInfoList[n] = playerInfo
- }
- p.exportToExcel(playerInfoList, searchDay, i)
- }
- }
- func (p *PlayerService) getUserInfoByMonth(searchDay time.Time) ([]forms.PlayerAttr, error) {
- searchTime := p.getSearchTime(searchDay)
- userInfos := make([]forms.PlayerAttr, 0)
- userInfoArr := make([]forms.PlayerAttr, 0)
- 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` <= ?"
- //sql2 := "SELECT `player_attr`.`playerid`,`player_attr`.`accId`,`player_attr`.`nickName`,`player_attr`.`serverId` FROM `player_attr` WHERE `player_attr`.`create_time` >= ?"
- var userInfo forms.PlayerAttr
- for _, db := range config.GDBGroup {
- rows, err := db.Raw(sql, searchTime.FirstDayTimeString, searchTime.LastDayTimesString).Rows()
- for rows.Next() {
- err = db.ScanRows(rows, &userInfo)
- if err != nil {
- return nil, err
- }
- userInfos = append(userInfos, userInfo)
- }
- if err != nil {
- return nil, err
- }
- userInfoArr = append(userInfoArr, userInfos...)
- }
- return userInfoArr, nil
- }
- func (p *PlayerService) getUserRechargeData(searchDay time.Time) (map[int64]*forms.PlayerRecharge, error) {
- searchTime := p.getSearchTime(searchDay)
- //充值数据 logDB.order 、 消费数据logDB.changedLog
- //1. 充值金额与次数
- playerRecharge := make([]*forms.PlayerRecharge, 0)
- orderQuery := query.Use(config.DB).Order
- orderQueryDo := orderQuery.WithContext(p.ctx)
- err := orderQueryDo.
- Select(orderQuery.Money.Sum().As("total"), orderQuery.ID.Count().As("count"), orderQuery.PlayerID).
- Where(orderQuery.CreatedAt.Gte(searchDay)).
- Where(orderQuery.Status.Eq(1)).
- Where(orderQuery.CreatedAt.Between(time.Unix(searchTime.FirstDayTimestamp, 0), time.Unix(searchTime.LastDayTimestamp, 0))).
- Group(orderQuery.PlayerID).
- Scan(&playerRecharge)
- if err != nil {
- logrus.WithField("method", "getUserRechargeData").Errorf("查询玩家充值金额与次数失败: %v", err)
- return nil, err
- }
- //playerRecharge转为map
- playerRechargeMap := make(map[int64]*forms.PlayerRecharge)
- for _, item := range playerRecharge {
- playerRechargeMap[item.PlayerId] = item
- }
- // 第一次和最后一次充值
- 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"
- err = config.DB.Raw(RechargeSql, sql.Named("start", searchTime.FirstDayTimeString), sql.Named("end", searchTime.LastDayTimesString)).Scan(&playerRecharge).Error
- if err != nil {
- logrus.WithField("method", "getUserRechargeData").Errorf("查询玩家充值时间失败: %v", err)
- return nil, err
- }
- for _, item := range playerRecharge {
- if recharge, ok := playerRechargeMap[item.PlayerId]; ok {
- recharge.FirstChargeAt = item.FirstChargeAt
- recharge.LastChargeAt = item.LastChargeAt
- recharge.RechargeCount = item.RechargeCount
- recharge.RechargeAmount = item.RechargeAmount
- }
- }
- return playerRechargeMap, nil
- }
- func (p *PlayerService) getSearchTime(searchDay time.Time) forms.SearchTime {
- // 获取月份第一天 00:00:00 时间戳
- firstDayOfMonth := time.Date(searchDay.Year(), searchDay.Month(), 1, 0, 0, 0, 0, time.Local)
- firstDayTimestamp := firstDayOfMonth.Unix()
- firstDayTimeString := firstDayOfMonth.Format("2006-01-02 15:04:05")
- fmt.Println("月份第一天 00:00:00 时间戳:", firstDayTimestamp)
- // 获取月份最后一天 23:59:59 时间戳
- lastDayOfMonth := firstDayOfMonth.AddDate(0, 1, -1)
- lastDayTimestamp := lastDayOfMonth.Add(time.Duration(23*time.Hour + 59*time.Minute + 59*time.Second)).Unix()
- lastDayTimeString := lastDayOfMonth.Add(time.Duration(23*time.Hour + 59*time.Minute + 59*time.Second)).Format("2006-01-02 15:04:05")
- fmt.Println("月份最后一天 23:59:59 时间戳:", lastDayTimestamp)
- return forms.SearchTime{
- FirstDayTimestamp: firstDayTimestamp,
- FirstDayTimeString: firstDayTimeString,
- LastDayTimestamp: lastDayTimestamp,
- LastDayTimesString: lastDayTimeString,
- }
- }
- func (p *PlayerService) exportToExcel(dataList []*forms.PlayerInfo, searchDay time.Time, n int) {
- searchName := searchDay.Format("200601")
- f := excelize.NewFile()
- sheetName := fmt.Sprintf("sheet_%s_%d", searchName, n)
- f.NewSheet(sheetName)
- streamWriter, err := f.NewStreamWriter(sheetName)
- if err != nil {
- logrus.WithField("method", "writeToFile").Errorf("创建流式写入器失败: %v", err)
- return
- }
- // 写入表头
- row := []interface{}{"玩家ID", "账号ID", "服务器ID", "昵称", "等级", "经验", "体力", "金币", "钻石", "最后登录", "最后登出", "注册时间", "首充时间", "最后充值时间", "充值总额", "充值次数", "武器", "角色", "部位", "天赋", "宝物", "关卡"}
- if err := streamWriter.SetRow("A1", row); err != nil {
- logrus.WithField("method", "writeToFile").Errorf("写入表头失败: %v", err)
- return
- }
- // 写入数据
- for i, playerInfo := range dataList {
- row := []interface{}{
- playerInfo.PlayerId, playerInfo.AccID, playerInfo.ServerID, playerInfo.NickName,
- playerInfo.Level, playerInfo.Exp, playerInfo.Stamina, playerInfo.Coin, playerInfo.Diamond,
- playerInfo.Lastlogintick, playerInfo.Lastlogouttick, playerInfo.CreateTime,
- playerInfo.FirstChargeAt, playerInfo.LastChargeAt, playerInfo.RechargeAmount, playerInfo.RechargeCount,
- playerInfo.PlayerWear, playerInfo.PlayerRole, playerInfo.PlayerBody, playerInfo.PlayerTalent,
- playerInfo.PlayerTreasure, playerInfo.PlayerChapter,
- }
- if err := streamWriter.SetRow(fmt.Sprintf("A%d", i+2), row); err != nil {
- logrus.WithField("method", "writeToFile").Errorf("写入数据失败: %v", err)
- return
- }
- }
- if err := streamWriter.Flush(); err != nil {
- logrus.WithField("method", "writeToFile").Errorf("刷新流式写入器失败: %v", err)
- return
- }
- if err := f.SaveAs("./player_statistics.xlsx"); err != nil {
- logrus.WithField("method", "writeToFile").Errorf("保存文件失败: %v", err)
- return
- }
- }
|