package exportctrl import ( "91porn-server/common/timeutil" "91porn-server/models/v/export_task_mod" "fmt" "net/http" "path/filepath" "strconv" "sync" "time" "91porn-server/common" "91porn-server/common/log" "91porn-server/common/stderr" "91porn-server/common/timeutil/timerange" "91porn-server/models/commod" "91porn-server/models/l/playlgmod" "91porn-server/models/s/statordermod" "91porn-server/models/s/statvidmod" "91porn-server/models/v/exchcodemod" "91porn-server/models/v/rchgordmod" "91porn-server/models/v/txnmod" "91porn-server/models/v/usermod" "91porn-server/models/v/vidmod" "91porn-server/models/v/wdordmod" "91porn-server/web/service/exchcodeser" "91porn-server/web/service/exportser" "91porn-server/web/service/withdrawser" "github.com/gin-gonic/gin" "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) type userListReq struct { usermod.UserListSelector common.StandQuery IsPretendAcc *int `form:"isPretendAcc" json:"isPretendAcc"` //是否马甲账号 } // Export doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/:fileType [get] func Export(c *gin.Context) { typeFile := c.Param("fileType") t, err := strconv.ParseInt(typeFile, 10, 64) if typeFile == "" || err != nil { common.ServeJSON(c, stderr.ErrParamError, err.Error()) return } fpath, err := exportser.Export(t) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(c, stderr.ErrExportFileFail, err.Error()) return } c.Writer.WriteHeader(http.StatusOK) c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) c.Header("Content-Type", "application/octet-stream") c.File(fpath) } func getMuchUser(arg userListReq) []*usermod.User { uInfo := []*usermod.User{} cond, opt := common.StandQueryMap(arg.StandQuery, arg.UserListSelector) //马甲账户 if arg.IsPretendAcc != nil && *arg.IsPretendAcc == 1 { cond["devID"] = bson.M{"$regex": usermod.SystemDevIDPrex, "$options": "i"} } //total, err := usermod.GetTotalCnt(cond, opt) //if err != nil { // return uInfo //} // 每次最多导出1000条 uInfo, _ = usermod.FindMany(cond, opt, int64(arg.PageNumber), int64(1000)) return uInfo //if total < 1000 { // uInfo, err = usermod.FindMany(cond, opt, int64(0), int64(1000)) // return uInfo //} ////创建多个协程去拉取 //const goRoutineCnt int = 60 //var wg sync.WaitGroup //size := total / int64(goRoutineCnt) //wg.Add(goRoutineCnt) //var mData [goRoutineCnt][]*usermod.User //for i := 0; i < goRoutineCnt-1; i++ { // common.GoParam(i, func(i int) { // defer wg.Done() // skip := int64(i) * size // mData[i], err = usermod.FindMany(cond, opt, int64(skip), int64(size)) // }) //} //common.Go(func() { // defer wg.Done() // skip := int64(goRoutineCnt-1) * size // mData[goRoutineCnt-1], err = usermod.FindMany(cond, opt, int64(skip), int64(size+size)) //}) //wg.Wait() //for i := 0; i < goRoutineCnt; i++ { // uInfo = append(uInfo, mData[i]...) //} //return uInfo } func setArray2Map(datas []playlgmod.UIDCount) map[uint64]int { m := make(map[uint64]int) for _, d := range datas { m[d.UID] = d.Count } return m } func calWatchedVideoCnt(uids []uint64) []playlgmod.UIDCount { le := len(uids) if le < 1000 { datas, _ := playlgmod.HasWatchedVideoCnt(uids) return datas } //创建多个协程去拉取 const goRoutineCnt = 10 var wg sync.WaitGroup var mData [goRoutineCnt][]playlgmod.UIDCount size := le / goRoutineCnt wg.Add(goRoutineCnt) for i := 0; i < goRoutineCnt-1; i++ { common.GoParam(i, func(i int) { defer wg.Done() begin := i * size end := begin + size - 1 mData[i], _ = playlgmod.HasWatchedVideoCnt(uids[begin:end]) }) } common.Go(func() { defer wg.Done() begin := (goRoutineCnt - 1) * size end := le - 1 mData[goRoutineCnt-1], _ = playlgmod.HasWatchedVideoCnt(uids[begin:end]) }) wg.Wait() datas := []playlgmod.UIDCount{} for i := 0; i < goRoutineCnt; i++ { datas = append(datas, mData[i]...) } return datas } // //func encodeUsers(users []*usermod.User, videoCntMap map[uint64]int, unDealVideoCntMap map[uint64]int) []usermod.ExportUser { // usersLen := len(users) // uids := make([]uint64, 0, usersLen) // uInfos := make([]usermod.ExportUser, 0, usersLen) // for _, u := range users { // if u != nil { // user := usermod.ExportUser{ // UID: u.UID, // DevID: u.DevID, // DevType: u.DevType, // RegisterIP: u.RegisterIP, // Mobile: u.Mobile, // Gender: u.Gender, // Channel: u.DistrictCode, // Name: u.Name, // PromotionCode: u.PromCode, // Summary: u.Summary, // Region: u.Region, // Birthday: u.Birthday, // VipLevel: u.VipLevel, // VipExpireDate: u.VipExpireDate, // CreatedAt: u.CreatedAt, // MobileBindAt: u.MobileBindAt, // HasLocked: u.HasLocked, // HasBanned: u.HasBanned, // TotalVideoCnt: videoCntMap[u.UID], // UndealVideoCnt: unDealVideoCntMap[u.UID], // LastVisitAt: u.LastVisitAt, // } // uInfos = append(uInfos, user) // uids = append(uids, u.UID) // } // } // wCnt := setArray2Map(calWatchedVideoCnt(uids)) // for i, u := range uInfos { // uInfos[i].WatchCount = wCnt[u.UID] // } // return uInfos //} func getUIDs(users []*usermod.User) []uint64 { uids := make([]uint64, 0, len(users)) for _, v := range users { if v == nil { continue } uids = append(uids, v.UID) } return uids } // Export doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/:fileType [get] func UserList(c *gin.Context) { manager, err := common.GetAdminAct(c) if err != nil { common.ServeJSON(c, stderr.AdminIDErr, err.Error()) return } var arg userListReq if err := c.ShouldBind(&arg); err != nil { common.ServeJSON(c, stderr.ErrParamError, "user List arg error "+err.Error()) return } if code := IsWithinWeek(arg.StartTime, arg.EndTime); code != stderr.Success { common.ServeJSON(c, code, code) return } err = export_task_mod.CreateTask(export_task_mod.ExportUserTask, manager, arg) if err != nil { common.ServeJSON(c, stderr.ErrExportFileFail, "") return } common.ServeJSON(c, stderr.Success, "成功") } // IsWithinWeek 判断是否是一周内 func IsWithinWeek(start *string, end *string) (code stderr.Code) { if start == nil || end == nil { return stderr.ErrTimeIsEmpty } startTime := timeutil.StrTimeToTime(*start) endTime := timeutil.StrTimeToTime(*end) hours := endTime.Sub(startTime).Hours() if hours > 24*7 { return stderr.ErrTimeWithinWeek } return stderr.Success } // RechargeOrder doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/rechargeOrder [get] func RechargeOrder(ctx *gin.Context) { var arg struct { common.StandQuery rchgordmod.RchgQueryReq } if err := ctx.ShouldBind(&arg); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, err) return } //导出条数写死 arg.StandQuery.PageNumber = 1 arg.StandQuery.PageSize = 100000 _, data, err := rchgordmod.FindRechargesOrders(common.StandQueryMap(arg.StandQuery, arg.RchgQueryReq)) if err != nil { common.ServeJSON(ctx, stderr.ErrDbQueryError, "") return } exptData := make([]*rchgordmod.RchargeOrderExport, len(data)) _100 := decimal.NewFromInt(100) for i, v := range data { exptData[i] = &rchgordmod.RchargeOrderExport{ ID: v.ID, UID: v.UID, DevID: v.DevID, OID: v.OID, UserIP: v.UserIP, Name: v.Name, Tel: v.Tel, PayAct: v.PayAct, DevType: v.DevType, Amount: decimal.NewFromInt(v.Amount).Div(_100).StringFixed(4), Money: decimal.NewFromInt(v.Money).Div(_100).StringFixed(4), PayMoney: decimal.NewFromInt(v.PayMoney).Div(_100).StringFixed(4), RechargeType: v.RechargeType, ProductID: v.ProductID, VipID: v.VipID, Channel: v.Channel, Status: v.Status, StatusDesc: v.StatusDesc, ProgressAt: v.ProgressAt, FailureAt: v.FailureAt, SuccessAt: v.SuccessAt, PaymentAt: v.PaymentAt, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, SourcePage: v.SourcePage, SourceRef: v.SourceRef, VideoID: v.VideoID, ActivityID: v.ActivityID, ExperimentID: v.ExperimentID, ExperimentVariant: v.ExperimentVariant, SessionID: v.SessionID, } } fpath, err := exportser.RechargeOrderExport(exptData) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(ctx, stderr.ErrExportFileFail, err.Error()) return } ctx.Writer.WriteHeader(http.StatusOK) ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) ctx.Header("Content-Type", "application/octet-stream") ctx.File(fpath) } // WithdrawOrder doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/withdrawOrder [get] func WithdrawOrder(ctx *gin.Context) { var arg struct { common.StandQuery wdordmod.WithdrawWebQueryReq } if err := ctx.ShouldBind(&arg); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, err) return } //导出条数写死 arg.StandQuery.PageNumber = 1 arg.StandQuery.PageSize = 100000 _, data, err := withdrawser.GetAllOrders(common.StandQueryMap(arg.StandQuery, arg.WithdrawWebQueryReq)) if err != nil { common.ServeJSON(ctx, stderr.ErrDbQueryError, "") return } fpath, err := exportser.WithdrawOrderExport(data) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(ctx, stderr.ErrExportFileFail, err.Error()) return } ctx.Writer.WriteHeader(http.StatusOK) ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) ctx.Header("Content-Type", "application/octet-stream") ctx.File(fpath) } // GoldTurnover doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/withdrawOrder [get] func GoldTurnover(ctx *gin.Context) { req := txnmod.CoinLogReq{} if err := ctx.ShouldBind(&req); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, nil) return } m := make(map[string]interface{}) if req.UID > 0 { m["uid"] = req.UID } if req.Balance == 1 { i := make(map[string]interface{}) i["$gt"] = 0 m["amount"] = i } if req.Balance == 2 { i := make(map[string]interface{}) i["$lt"] = 0 m["amount"] = i } if !req.End.IsZero() { i := make(map[string]interface{}) i["$gte"] = req.Start i["$lt"] = req.End m["createdAt"] = i } if len(req.TranType) != 0 { m["tranType"] = req.TranType } infos, _, _, _ := txnmod.GetCoinLogs(1, 100000, m) fpath, err := exportser.GoldTurnoverExport(infos) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(ctx, stderr.ErrExportFileFail, err.Error()) return } ctx.Writer.WriteHeader(http.StatusOK) ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) ctx.Header("Content-Type", "application/octet-stream") ctx.File(fpath) } // VideoIncome doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/withdrawOrder [get] func VideoIncome(ctx *gin.Context) { type Query struct { StartTime *time.Time `form:"startTime" json:"startTime" binding:""` //开始时间 EndTime *time.Time `form:"endTime" json:"endTime" binding:""` //结束时间 UID *uint64 `form:"uid" json:"uid" binding:""` //uid Vid *string `form:"vid" json:"vid" binding:""` //vid } var arg struct { commod.Page Query } if err := ctx.ShouldBind(&arg); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, "stat VideoEarnStats arg error "+err.Error()) return } endTime := time.Now() if arg.EndTime != nil { endTime = timerange.LocDayRange(*arg.EndTime).Head } startTime := time.Time{} if arg.StartTime != nil { startTime = timerange.LocDayRange(*arg.StartTime).Head } var vid *primitive.ObjectID if arg.Vid != nil { id, _ := primitive.ObjectIDFromHex(*arg.Vid) vid = &id } var vids []primitive.ObjectID if arg.UID != nil { list, _ := vidmod.FindMany((&vidmod.PublisherIDMatch{PublisherID: arg.UID}).New()) vids = list.IDs() } limit := int64(100000) skip := int64(0) page, _ := statvidmod.VideoStatPages(skip, limit, statvidmod.SumDateGTEAndLTMatch{GTE: &startTime, LT: &endTime}, statvidmod.VidMatch{Vid: vid}, statvidmod.VidInMatch{Vids: vids}, ) fpath, err := exportser.VideoIncomeExport(page.List) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(ctx, stderr.ErrExportFileFail, err.Error()) return } ctx.Writer.WriteHeader(http.StatusOK) ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) ctx.Header("Content-Type", "application/octet-stream") ctx.File(fpath) } // OrderStat doc // @Summary 导出文件 // @Description 导出excel文件 // @Tags Web-Export // @Accept mpfd,json // @Produce json,html // @Param fileType query integer true "导出文件的类型" // @Success 200 {string} json "{"msg": "操作成功", "data":[]}" // @Failure 400 {string} json "{"msg": "操作失败"}" // @Router /web/export/withdrawOrder [get] func OrderStat(ctx *gin.Context) { var args struct { Type *int `form:"type" json:"type"` //订单类型 Channel *string `form:"channel" json:"channel"` Start *time.Time `form:"start" json:"start"` End *time.Time `form:"end" json:"end"` } if err := ctx.ShouldBind(&args); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, nil) return } endTime := time.Now() if args.End != nil { endTime = *args.End } var startTime time.Time if args.Start != nil { startTime = *args.Start } skip := int64(0) limit := int64(100000) mats := []statordermod.Matcher{ (&statordermod.ChannelMatch{}).New(), (&statordermod.TypeMatch{}).New(), (&statordermod.SumDateGTEAndLTMatch{GTE: &startTime, LT: &endTime}).New(), } list, err := statordermod.List(nil, &skip, &limit, mats...) if err != nil { common.ServeJSON(ctx, stderr.ErrDbQueryError, "") return } fpath, err := exportser.OrderStatExport(list) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(ctx, stderr.ErrExportFileFail, err.Error()) return } ctx.Writer.WriteHeader(http.StatusOK) ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) ctx.Header("Content-Type", "application/octet-stream") ctx.File(fpath) } // CodeList 导出兑换码 func CodeList(ctx *gin.Context) { var param = exchcodemod.ListReqParam{} if err := ctx.ShouldBind(¶m); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, err) return } data := exchcodeser.ExchangeCodes(param) fpath, err := exportser.ExchangeCodeExport(data) if err != nil { log.Warn("request multipart wrong ", log.E(err)) common.ServeJSON(ctx, stderr.ErrExportFileFail, err.Error()) return } ctx.Writer.WriteHeader(http.StatusOK) ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filepath.Base(fpath))) ctx.Header("Content-Type", "application/octet-stream") ctx.File(fpath) }