package domain import ( "admin/apps/game/domain/projects" "admin/apps/game/domain/repo" "admin/internal/consts" "admin/internal/errcode" "admin/internal/model/dto" "admin/lib/xlog" "context" "github.com/jmoiron/sqlx" "github.com/xuri/excelize/v2" "gorm.io/gorm" "strconv" "time" ) type GameLogService struct { projectRepo repo.IProjectRepo repo repo.IGameLogRepo } func NewGameLogSvc(db *gorm.DB, metaDb *gorm.DB, clickHouseSqlx *sqlx.DB) *GameLogService { return &GameLogService{projectRepo: repo.NewProjectRepo(db), repo: repo.NewGameLogRepo(metaDb, clickHouseSqlx)} } func (svc *GameLogService) QueryEventList(projectId int, eventName []string, serverId int, account, roleId string, pageNo, pageLen int, dateStart, dateEnd time.Time) (totalCount int, fieldsDescInfo []*dto.GameLogFieldInfo, rows [][]any, err error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return 0, nil, nil, err } if !find { return 0, nil, nil, errcode.New(errcode.ServerError, "not found project %v db data", projectId) } appId := projectEt.Po.BiAdminAppId if appId == 0 { return 0, make([]*dto.GameLogFieldInfo, 0), make([][]any, 0), nil } var attrList []*repo.AttrInfo attrList, err = svc.repo.QueryAttrListByEvent(appId, eventName) if err != nil { return } querier := svc.repo.NewEventListQuerier("xwl_event"+strconv.Itoa(appId), eventName, attrList) totalCount, fieldsDescInfo, rows, err = querier.CondRoleId(serverId, roleId).CondAccount(serverId, account). Go(context.Background(), pageNo, pageLen, dateStart, dateEnd) if err != nil { xlog.Warnf("event list error:%v", err) return } hook := projects.GetProjectResourceHook(projectEt, consts.ResourcesName_GameLog) if h, ok := hook.(projects.IGameLogEventListHook); ok { totalCount, fieldsDescInfo, rows = h.Trim(projectEt, eventName, totalCount, fieldsDescInfo, rows) } if rows == nil { rows = make([][]any, 0) } return } func (svc *GameLogService) QueryEventListExport(projectId int, eventName []string, serverId int, account, roleId string, dateStart, dateEnd time.Time) (filePath string, err error) { _, projectEt, find, err := svc.projectRepo.GetById(projectId) if err != nil { return "", err } if !find { return "", errcode.New(errcode.ServerError, "not found project %v db data", projectId) } appId := projectEt.Po.BiAdminAppId if appId == 0 { return "", nil } var attrList []*repo.AttrInfo attrList, err = svc.repo.QueryAttrListByEvent(appId, eventName) if err != nil { return } pageNo := 1 pageLen := 1000 querier := svc.repo.NewEventListQuerier("xwl_event"+strconv.Itoa(appId), eventName, attrList) querier.CondRoleId(serverId, roleId).CondAccount(serverId, account) queryEventLogFun := func(curPageNo, curPageLen int) (int, []*dto.GameLogFieldInfo, [][]any, error) { a, b, c, err := querier.Go(context.Background(), curPageNo, curPageLen, dateStart, dateEnd) if err != nil { return 0, nil, nil, err } hook := projects.GetProjectResourceHook(projectEt, consts.ResourcesName_GameLog) if h, ok := hook.(projects.IGameLogEventListHook); ok { a, b, c = h.Trim(projectEt, eventName, a, b, c) } return a, b, c, nil } // 先读第一页看看总数 totalCount, fieldsDescInfo, tmpRows, err := queryEventLogFun(pageNo, pageLen) if err != nil { xlog.Warnf("event list error:%v", err) return } filePath = "/tmp/" + "gamelog-" + time.Now().Format("20060102150405") + ".xlsx" f := excelize.NewFile() defer f.Close() f.SetDefaultFont("Arial") index, err := f.NewSheet("Sheet1") if err != nil { return "", errcode.New(errcode.ServerError, "new excel sheet error:%v", err) } f.SetActiveSheet(index) sheetWriter, err := f.NewStreamWriter("Sheet1") if err != nil { return "", errcode.New(errcode.ServerError, "new excel sheet writer error:%v", err) } // 写入excel文件头 writeHeaders := make([]any, 0, len(fieldsDescInfo)) for _, field := range fieldsDescInfo { writeHeaders = append(writeHeaders, field.Alias) } headerCell, _ := excelize.CoordinatesToCellName(1, 1) err = sheetWriter.SetRow(headerCell, writeHeaders) if err != nil { xlog.Warnf("set row error:%v", err) return "", errcode.New(errcode.ServerError, "set row error:%v", err) } xlog.Tracef("set headers %+v ok", writeHeaders) //err = sheetWriter.Flush() //if err != nil { // return "", errcode.New(errcode.ServerError, "flush error:%v", err) //} writeQueue := make(chan [][]any, 20) resultQueue := make(chan error, 1) // 启动另一个协程来写文件 go func() { curRowNum := 2 timeout := time.NewTicker(time.Second * 120) for { select { case recvRows, ok := <-writeQueue: if !ok { return } for _, row := range recvRows { rowCell, _ := excelize.CoordinatesToCellName(1, curRowNum) err = sheetWriter.SetRow(rowCell, row) if err != nil { resultQueue <- errcode.New(errcode.ServerError, "set row error:%v", err) return } xlog.Tracef("set row %+v ok", row) curRowNum++ } if curRowNum >= totalCount { err = sheetWriter.Flush() if err != nil { resultQueue <- errcode.New(errcode.ServerError, "flush error:%v", err) return } saveErr := f.SaveAs(filePath) if saveErr != nil { err = errcode.New(errcode.ServerError, "save as error:%v", err) return } xlog.Infof("finish write total count %v to file:%v", curRowNum, filePath) // 搞完了 resultQueue <- nil return } case <-timeout.C: xlog.Warnf("timeout!!") } } }() writeFileHandler := func(fields []*dto.GameLogFieldInfo, rows [][]any) { writeQueue <- rows } // 写第一页数据 writeFileHandler(fieldsDescInfo, tmpRows) if totalCount >= pageLen { // 总数还有,分页读了 totalPageNum := totalCount/pageLen + 1 for i := 1; i <= totalPageNum; i++ { if i == 1 { // 第一页读过了,跳过 continue } totalCount, fieldsDescInfo, tmpRows, err = queryEventLogFun(pageNo, i*pageLen) if err != nil { xlog.Warnf("event list error:%v", err) return } writeFileHandler(fieldsDescInfo, tmpRows) } } err = <-resultQueue if err != nil { xlog.Warnf("event list write excel file:%v", err) } else { xlog.Infof("export file %v successfully!", filePath) } return }