forked from all_user/stat_center
Initial commit
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
package activitySer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/activityVisit"
|
||||
"stat/models/activitymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type ChannelParams struct {
|
||||
PC string `json:"i" bson:"i"` //推广码
|
||||
DC string `json:"dc" bson:"dc"` //商区序列
|
||||
CH string `json:"ch" bson:"ch"` //渠道
|
||||
}
|
||||
|
||||
// 活动列表
|
||||
func GetActivityList(ctx context.Context, param proto.ActivityListReq) (stdLog.CodeType, *proto.ActivityListResp, error) {
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
if param.PageNum <= 0 {
|
||||
param.PageNum = 1
|
||||
}
|
||||
data := &proto.ActivityListResp{List: make([]*proto.ActivityInfo, 0)}
|
||||
filter := bson.M{}
|
||||
if param.Name != nil {
|
||||
filter[activitymod.FN.ActivityInfo.Name] = bson.M{"$regex": *param.Name}
|
||||
}
|
||||
if param.Enable != nil {
|
||||
filter[activitymod.FN.ActivityInfo.Enable] = *param.Enable
|
||||
}
|
||||
count, err := activitymod.CountActivityInfos(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.Total = count
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
opt.SetSkip(skip)
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSort(bson.M{activitymod.FN.ActivityInfo.SortCode: -1})
|
||||
err = activitymod.FindManyActivityInfoWithBind(ctx, &data.List, filter, opt)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
for _, v := range data.List {
|
||||
lcFilter := bson.M{activitymod.FN.LottertyCodeInfo.ActId: v.Id.Hex()}
|
||||
v.LotteryCodeNum, _ = activitymod.CountLottertyCodeInfos(ctx, lcFilter)
|
||||
|
||||
lcFilter[activitymod.FN.LottertyCodeInfo.IsUsed] = true
|
||||
v.UsedLotteryCodeNum, _ = activitymod.CountLottertyCodeInfos(ctx, lcFilter)
|
||||
|
||||
gcFilter := bson.M{activitymod.FN.LotteryGetGift.ActId: v.Id.Hex()}
|
||||
v.GiftCodeNum, _ = activitymod.CountLotteryGetGifts(ctx, gcFilter)
|
||||
|
||||
gcgFilter := bson.M{
|
||||
activitymod.FN.LotteryGetGift.ActId: v.Id.Hex(),
|
||||
activitymod.FN.LotteryGetGift.IsGaveOut: true,
|
||||
}
|
||||
v.GiftCodeGivedNum, _ = activitymod.CountLotteryGetGifts(ctx, gcgFilter)
|
||||
|
||||
gcuFilter := bson.M{
|
||||
activitymod.FN.LotteryGetGift.ActId: v.Id.Hex(),
|
||||
activitymod.FN.LotteryGetGift.IsUsed: true,
|
||||
}
|
||||
v.GiftCodeUsedNum, _ = activitymod.CountLotteryGetGifts(ctx, gcuFilter)
|
||||
|
||||
}
|
||||
return stdLog.Success, data, nil
|
||||
}
|
||||
|
||||
// 添加活动
|
||||
func AddOneAct(ctx context.Context, param proto.AddActivityReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
doc := activitymod.ActivityInfo{
|
||||
Id: primitive.NewObjectID(),
|
||||
Name: param.Name,
|
||||
Desc: param.Desc,
|
||||
SortCode: param.SortCode,
|
||||
H5Domain: param.H5Domain,
|
||||
GiftDesc: param.GiftDesc,
|
||||
Enable: param.Enable,
|
||||
Background: param.Background,
|
||||
StartTime: param.StartTime,
|
||||
EndTime: param.EndTime,
|
||||
CreatedAt: now,
|
||||
Banner: param.Banner,
|
||||
}
|
||||
err := activitymod.InsertOneActivityInfo(ctx, &doc)
|
||||
if err != nil {
|
||||
return stdLog.InsertDataError, err
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 更新一个活动
|
||||
func UpdateOneAct(ctx context.Context, param *proto.UpdateActivityReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
param.UpdatedAt = now
|
||||
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.Id}
|
||||
updateDoc := bson.M{"$set": param}
|
||||
err := activitymod.UpdateOneActivityInfo(ctx, filter, updateDoc)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 删除一个活动
|
||||
func DeleteOneAct(ctx context.Context, param proto.DelActivityReq) (stdLog.CodeType, error) {
|
||||
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.Id}
|
||||
count, err := activitymod.DelOneActivityInfo(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
if count == 0 {
|
||||
return stdLog.DataNotExist, nil
|
||||
}
|
||||
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
func UpdateActivityVisitCount(ctx context.Context, peerIp string, param proto.ActivityVisitCountReq) (stdLog.CodeType, error) {
|
||||
if param.Ip == "" {
|
||||
param.Ip = peerIp
|
||||
}
|
||||
aa := &activityVisit.ActivityVisit{
|
||||
AppId: param.AppId,
|
||||
IP: param.Ip,
|
||||
CH: param.CH,
|
||||
ActivityId: param.ActivityId,
|
||||
ActivityName: param.ActivityName,
|
||||
PeerIP: peerIp,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := activityVisit.InsertActivityVisit(ctx, aa); err != nil {
|
||||
return stdLog.InsertDataError, err
|
||||
}
|
||||
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 活动列表
|
||||
func GetActivityVisitList(ctx context.Context, param proto.ActivityVisitCountListReq) (stdLog.CodeType, *proto.ActivityVisitListResp, error) {
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
if param.PageNum <= 0 {
|
||||
param.PageNum = 1
|
||||
}
|
||||
data := &proto.ActivityVisitListResp{List: make([]*proto.ActivityVisitInfo, 0)}
|
||||
filter := bson.M{}
|
||||
if param.ActivityId != nil {
|
||||
filter["activityId"] = *param.ActivityId
|
||||
}
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = *param.AppId
|
||||
}
|
||||
if param.CH != nil {
|
||||
filter["ch"] = *param.CH
|
||||
}
|
||||
count, err := activityVisit.CountActivityVisit(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.Total = count
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
opt.SetSkip(skip)
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSort(bson.M{"createdAt": -1})
|
||||
lists, err := activityVisit.FindManyActivityList(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
for _, v := range lists {
|
||||
data.List = append(data.List, &proto.ActivityVisitInfo{
|
||||
AppId: v.AppId,
|
||||
IP: v.IP,
|
||||
CH: v.CH,
|
||||
DevType: v.DevType,
|
||||
SysType: v.SysType,
|
||||
PeerIP: v.PeerIP,
|
||||
ActivityId: v.ActivityId.Hex(),
|
||||
ActivityName: v.ActivityName,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
})
|
||||
|
||||
}
|
||||
return stdLog.Success, data, nil
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package activitySer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/mongos"
|
||||
"stat/common/redis"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/activitymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
var ipDrawLimit = 20 // 同一IP每天抽奖限制
|
||||
|
||||
// H5活动详情
|
||||
func GetActivityInfo(ctx context.Context, param proto.ActivityInfoReq) (stdLog.CodeType, *proto.ActivityInfoResp, error) {
|
||||
now := time.Now()
|
||||
res := &proto.ActivityInfoResp{}
|
||||
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.ActId}
|
||||
|
||||
data, err := activitymod.FindOneActivityInfo(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, res, err
|
||||
}
|
||||
if data == nil {
|
||||
return stdLog.ActivityNoExistErr, res, fmt.Errorf("data does not exist")
|
||||
}
|
||||
if now.Before(data.StartTime) || now.After(data.EndTime) || !data.Enable {
|
||||
return stdLog.ActivityNoExistErr, res, fmt.Errorf("activity is closed")
|
||||
}
|
||||
res = &proto.ActivityInfoResp{
|
||||
Id: data.Id,
|
||||
Name: data.Name,
|
||||
Desc: data.Desc,
|
||||
H5Domain: data.H5Domain,
|
||||
GiftDesc: data.GiftDesc,
|
||||
Enable: data.Enable,
|
||||
StartTime: data.StartTime,
|
||||
EndTime: data.EndTime,
|
||||
Banner: data.Banner,
|
||||
}
|
||||
return stdLog.Success, res, nil
|
||||
}
|
||||
|
||||
// H5页面奖品信息
|
||||
func GetActivityGiftInfo(ctx context.Context, param proto.ActivityGiftInfoReq) (stdLog.CodeType, *proto.ActivityGiftInfoResp, error) {
|
||||
data := &proto.ActivityGiftInfoResp{List: make([]*proto.H5GiftInfo, 0)}
|
||||
filter := bson.M{activitymod.FN.LotteryGift.ActId: param.ActId}
|
||||
count, err := activitymod.CountLotteryGifts(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.Count = count
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{activitymod.FN.LotteryGift.Sort: 1})
|
||||
if err := activitymod.FindManyLotteryGiftWithBind(ctx, &data.List, filter, opt); err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
return stdLog.Success, data, nil
|
||||
}
|
||||
|
||||
// 抽奖
|
||||
func Draw(ctx context.Context, param *proto.DrawReq) (stdLog.CodeType, *proto.DrawResp, error) {
|
||||
res := &proto.DrawResp{}
|
||||
now := time.Now()
|
||||
|
||||
// 活动是否存在
|
||||
actFilter := bson.M{activitymod.FN.ActivityInfo.Id: param.ActId}
|
||||
data, err := activitymod.FindOneActivityInfo(ctx, actFilter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, res, err
|
||||
}
|
||||
if data == nil {
|
||||
return stdLog.ActivityNoExistErr, res, fmt.Errorf("data does not exist")
|
||||
}
|
||||
if now.Before(data.StartTime) || now.After(data.EndTime) || !data.Enable {
|
||||
return stdLog.ActivityNoExistErr, res, fmt.Errorf("activity is closed")
|
||||
}
|
||||
|
||||
// 抽奖码是否有效
|
||||
lcFilter := bson.M{
|
||||
activitymod.FN.LottertyCodeInfo.LotteryCode: param.LotteryCode,
|
||||
activitymod.FN.LottertyCodeInfo.ActId: param.ActId.Hex(),
|
||||
}
|
||||
lcInfo, err := activitymod.FindOneLottertyCodeInfo(ctx, lcFilter)
|
||||
if err != nil || lcInfo == nil {
|
||||
return stdLog.LotteryCodeIsInvalid, res, fmt.Errorf("lottery code is invalid")
|
||||
}
|
||||
if lcInfo.IsUsed {
|
||||
return stdLog.LotteryCodeIsUsed, res, fmt.Errorf("lottery code is used")
|
||||
}
|
||||
|
||||
// 根据用户IP限制抽奖次数
|
||||
if !CanUserDraw(param.Ip, param.ActId.Hex()) {
|
||||
return stdLog.LotteryNumReachedTheLimit, res, fmt.Errorf("the number of lottery reached the limit")
|
||||
}
|
||||
|
||||
// 抽奖
|
||||
gFilter := bson.M{activitymod.FN.LotteryGift.ActId: param.ActId.Hex()}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{activitymod.FN.LotteryGift.Rate: -1})
|
||||
giftList, err := activitymod.FindManyLotteryGift(ctx, gFilter, opt)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, res, fmt.Errorf("lottery gift data err")
|
||||
}
|
||||
var giftRand int32
|
||||
for _, v := range giftList {
|
||||
giftRand += v.Rate
|
||||
}
|
||||
|
||||
flag := true
|
||||
for {
|
||||
randNum := rand.Int31n(giftRand)
|
||||
// fmt.Printf("\ngiftRand is %d at 1st\n", giftRand)
|
||||
// fmt.Printf("\nrandNum is %d at 1st\n", randNum)
|
||||
for _, w := range giftList {
|
||||
// fmt.Printf("randNum is %d before minus\n", randNum)
|
||||
// fmt.Printf("\ngiftRand is %d before minus\n", giftRand)
|
||||
randNum -= w.Rate
|
||||
giftRand -= w.Rate
|
||||
// fmt.Printf("\nAfter minus randNum is %d\nw.Rate is %d\ngift info %+v\n", randNum, w.Rate, w)
|
||||
// fmt.Printf("\nAfter minus giftRand is %d\n", giftRand)
|
||||
if randNum <= 0 {
|
||||
var notgaveNum int64 // total 每种奖品的总数量, givedNum 每种奖品未被抽中的数量
|
||||
gFilter := bson.M{
|
||||
activitymod.FN.LotteryGetGift.GiftId: w.Id.Hex(),
|
||||
activitymod.FN.LotteryGetGift.ActId: w.ActId,
|
||||
activitymod.FN.LotteryGetGift.LotteryCode: "",
|
||||
}
|
||||
info, _ := activitymod.FindManyLotteryGetGift(ctx, gFilter)
|
||||
notgaveNum = int64(len(info))
|
||||
if notgaveNum == 0 {
|
||||
randNum = rand.Int31n(giftRand + 1)
|
||||
continue
|
||||
}
|
||||
res.GiftId = w.Id.Hex()
|
||||
res.GiftName = w.GiftName
|
||||
res.GiftCode = info[0].GiftCode
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fn := func(sctx mongo.SessionContext) error {
|
||||
// 更新lotterycodeinfo
|
||||
lciFilter := bson.M{
|
||||
activitymod.FN.LottertyCodeInfo.LotteryCode: param.LotteryCode,
|
||||
activitymod.FN.LottertyCodeInfo.ActId: param.ActId.Hex(),
|
||||
}
|
||||
lciUpdate := bson.M{"$set": bson.M{
|
||||
activitymod.FN.LottertyCodeInfo.UserIp: param.Ip,
|
||||
activitymod.FN.LottertyCodeInfo.LotteryAt: now,
|
||||
activitymod.FN.LottertyCodeInfo.UpdatedAt: now,
|
||||
activitymod.FN.LottertyCodeInfo.IsUsed: true,
|
||||
activitymod.FN.LottertyCodeInfo.GiftCode: res.GiftCode,
|
||||
activitymod.FN.LottertyCodeInfo.Remark: "已使用",
|
||||
}}
|
||||
err := activitymod.UpdateOneLottertyCodeInfo(sctx, lciFilter, lciUpdate)
|
||||
if err != nil {
|
||||
log.Warn("Draw: UpdateOneLottertyCodeInfo err", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新lotterygetgift
|
||||
lggFilter := bson.M{
|
||||
activitymod.FN.LotteryGetGift.GiftCode: res.GiftCode,
|
||||
activitymod.FN.LotteryGetGift.ActId: param.ActId.Hex(),
|
||||
}
|
||||
lggUpdate := bson.M{"$set": bson.M{
|
||||
activitymod.FN.LotteryGetGift.UpdatedAt: now,
|
||||
activitymod.FN.LotteryGetGift.LotteryCode: param.LotteryCode,
|
||||
activitymod.FN.LotteryGetGift.IsGaveOut: true,
|
||||
activitymod.FN.LotteryGetGift.Remark: "兑换码已发放",
|
||||
}}
|
||||
err = activitymod.UpdateOneLotteryGetGift(sctx, lggFilter, lggUpdate)
|
||||
if err != nil {
|
||||
log.Error("Draw: UpdateOneLotteryGetGift err", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := mongos.Transaction(ctx, fn); err != nil {
|
||||
return stdLog.Failure, &proto.DrawResp{}, err
|
||||
}
|
||||
return stdLog.Success, res, nil
|
||||
}
|
||||
|
||||
// 验证用户Ip是否能抽奖
|
||||
func CanUserDraw(ip, actId string) bool {
|
||||
redisKey := fmt.Sprintf("actId:%s:userIp:%s", actId, ip)
|
||||
numStr, err := redis.StatRds.Get(redisKey)
|
||||
if err != nil {
|
||||
log.Error("canUserDraw redis get err", log.E(err))
|
||||
return false
|
||||
}
|
||||
var num = int64(0)
|
||||
if numStr != nil {
|
||||
num, err = strconv.ParseInt(*numStr, 10, 64)
|
||||
if err != nil {
|
||||
log.Error("canUserDraw str to int64 err", log.E(err))
|
||||
return false
|
||||
}
|
||||
}
|
||||
if num > int64(ipDrawLimit) {
|
||||
log.Warn("canUserDraw user IP triggered the limit")
|
||||
return false
|
||||
}
|
||||
val, err := redis.StatRds.Incr(redisKey)
|
||||
if err != nil {
|
||||
log.Error("canUserDraw redis get err", log.E(err))
|
||||
return false
|
||||
}
|
||||
if val == 1 {
|
||||
duration := redis.StatRds.TTL(redisKey)
|
||||
if duration == time.Duration(time.Second*-1) {
|
||||
expiredAt := utils.EndOfToday().UTC().Sub(time.Now().UTC())
|
||||
// expiredAt = expiredAt - (time.Second * 36200)
|
||||
// str := expiredAt.String()
|
||||
// fmt.Printf("\nexpiredAt is %d\n", expiredAt)
|
||||
// fmt.Printf("\nstr is %s\n", str)
|
||||
redis.StatRds.Expire(redisKey, expiredAt)
|
||||
}
|
||||
}
|
||||
if val > int64(ipDrawLimit) {
|
||||
log.Warn("canUserDraw user IP triggered the limit")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package activitySer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"stat/common/log"
|
||||
"stat/common/redis"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func initRedis() error {
|
||||
rds, err := redis.NewManagerWithUrl("redis://:B6vJHlYiTseLj77z@118.107.44.18:16789/7")
|
||||
if err != nil {
|
||||
log.Error("rds init err", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
redis.StatRds = rds
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCanUserDraw(t *testing.T) {
|
||||
|
||||
// 初始化redis
|
||||
if err := initRedis(); err != nil {
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
res := CanUserDraw("610.0.21.100", "6063296c6d2c51859840b072")
|
||||
fmt.Print(res)
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package activitySer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common/gencode"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/activitymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 生成活动抽奖码
|
||||
func GenLotteryCodes(ctx context.Context, param proto.GenLotteryCodeReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.ActId}
|
||||
info, err := activitymod.FindOneActivityInfo(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, err
|
||||
}
|
||||
doc := &activitymod.LottertyCodeInfo{
|
||||
ActId: param.ActId.Hex(),
|
||||
ActName: info.Name,
|
||||
IsUsed: false,
|
||||
CreatedAt: now,
|
||||
}
|
||||
for i := 0; i < int(param.CodeNum); i++ {
|
||||
code, err := genLotteryCode(ctx, int(param.CodeLen))
|
||||
if err != nil {
|
||||
return stdLog.Failure, err
|
||||
}
|
||||
doc.LotteryCode = code
|
||||
doc.Id = primitive.NewObjectID()
|
||||
if err := activitymod.InsertOneLottertyCodeInfo(ctx, doc); err != nil {
|
||||
return stdLog.InsertDataError, err
|
||||
}
|
||||
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
func genLotteryCode(ctx context.Context, len int) (string, error) {
|
||||
// 生成码
|
||||
for {
|
||||
code := strings.ToUpper(gencode.CodeGenerate(len))
|
||||
filter := bson.M{activitymod.FN.LottertyCodeInfo.LotteryCode: code}
|
||||
_, err := activitymod.FindOneLottertyCodeInfo(ctx, filter)
|
||||
if err != nil {
|
||||
if err != mongo.ErrNoDocuments {
|
||||
return "", err
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改抽奖码信息
|
||||
func UpdateLotteryCode(ctx context.Context, param proto.UpdateLotteryCodeReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
filter := bson.M{activitymod.FN.LottertyCodeInfo.Id: param.LotteryCodeId}
|
||||
updateDoc := bson.M{"$set": bson.M{
|
||||
activitymod.FN.LottertyCodeInfo.Remark: param.Mark,
|
||||
activitymod.FN.LottertyCodeInfo.UpdatedAt: now,
|
||||
}}
|
||||
err := activitymod.UpdateOneLottertyCodeInfo(ctx, filter, updateDoc)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 删除一个活动抽奖码
|
||||
func DeleteOneLotteryCode(ctx context.Context, param proto.DelLotteryCodeReq) (stdLog.CodeType, error) {
|
||||
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.LotteryCodeId}
|
||||
count, err := activitymod.DelOneLottertyCodeInfo(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
if count == 0 {
|
||||
return stdLog.DataNotExist, nil
|
||||
}
|
||||
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 活动抽奖码列表
|
||||
func GetLotteryCodeList(ctx context.Context, param proto.LotteryCodeListReq) (stdLog.CodeType, *proto.LotteryCodeListResp, error) {
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
if param.PageNum <= 0 {
|
||||
param.PageNum = 1
|
||||
}
|
||||
data := &proto.LotteryCodeListResp{List: make([]*activitymod.LottertyCodeInfo, 0)}
|
||||
filter := bson.M{}
|
||||
if param.LotteryCode != nil {
|
||||
filter[activitymod.FN.LottertyCodeInfo.LotteryCode] = *param.LotteryCode
|
||||
}
|
||||
if param.ActId != nil {
|
||||
filter[activitymod.FN.LottertyCodeInfo.ActId] = *param.ActId
|
||||
}
|
||||
if param.ActName != nil {
|
||||
filter[activitymod.FN.LottertyCodeInfo.ActName] = bson.M{"$regex": *param.ActName}
|
||||
}
|
||||
if param.IsUsed != nil {
|
||||
filter[activitymod.FN.LottertyCodeInfo.IsUsed] = *param.IsUsed
|
||||
}
|
||||
if param.GiftCode != nil {
|
||||
filter[activitymod.FN.LottertyCodeInfo.GiftCode] = *param.GiftCode
|
||||
}
|
||||
count, err := activitymod.CountLottertyCodeInfos(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.Total = count
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
opt.SetSkip(skip)
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSort(bson.M{activitymod.FN.LottertyCodeInfo.CreatedAt: -1})
|
||||
list, err := activitymod.FindManyLottertyCodeInfo(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.List = list
|
||||
return stdLog.Success, data, nil
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package activitySer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common/gencode"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/activitymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 生成活动奖品兑换码
|
||||
func GenManyGiftCode(ctx context.Context, param proto.GenGiftCodeRes) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
filter := bson.M{activitymod.FN.LotteryGift.Id: param.GiftId}
|
||||
info, err := activitymod.FindOneLotteryGift(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, err
|
||||
}
|
||||
doc := &activitymod.LotteryGetGift{
|
||||
ActId: info.ActId,
|
||||
ActName: info.ActName,
|
||||
GiftId: info.Id.Hex(),
|
||||
GiftName: info.GiftName,
|
||||
CreatedAt: now,
|
||||
}
|
||||
for i := 0; i < int(param.CodeNum); i++ {
|
||||
code, err := genGiftCode(ctx, int(param.CodeLen))
|
||||
if err != nil {
|
||||
return stdLog.Failure, err
|
||||
}
|
||||
doc.GiftCode = code
|
||||
doc.Id = primitive.NewObjectID()
|
||||
if err = activitymod.InsertOneLotteryGetGift(ctx, doc); err != nil {
|
||||
return stdLog.InsertDataError, err
|
||||
}
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
func genGiftCode(ctx context.Context, len int) (string, error) {
|
||||
// 生成码
|
||||
for {
|
||||
code := strings.ToUpper(gencode.CodeGenerate(len))
|
||||
filter := bson.M{activitymod.FN.LotteryGetGift.GiftCode: code}
|
||||
_, err := activitymod.FindOneLotteryGetGift(ctx, filter)
|
||||
if err != nil {
|
||||
if err != mongo.ErrNoDocuments {
|
||||
return "", err
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改奖品兑换信息
|
||||
func UpdateOneRecvGiftInfo(ctx context.Context, param *proto.UpdateGetGiftReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
if param.GiftCode == "" {
|
||||
return stdLog.ParamError, fmt.Errorf("giftcode param err")
|
||||
}
|
||||
filter := bson.M{
|
||||
activitymod.FN.LotteryGetGift.Id: param.Id,
|
||||
activitymod.FN.LotteryGetGift.GiftCode: param.GiftCode,
|
||||
}
|
||||
doc := bson.M{}
|
||||
if param.UserId == nil || *param.UserId == "" {
|
||||
return stdLog.ParamError, fmt.Errorf("userId can not be nil")
|
||||
} else {
|
||||
doc[activitymod.FN.LotteryGetGift.UserId] = *param.UserId
|
||||
}
|
||||
if param.AppName == nil || *param.AppName == "" {
|
||||
return stdLog.ParamError, fmt.Errorf("appName can not be nil")
|
||||
} else {
|
||||
doc[activitymod.FN.LotteryGetGift.AppName] = *param.AppName
|
||||
}
|
||||
if param.Remark != nil {
|
||||
doc[activitymod.FN.LotteryGetGift.Remark] = *param.Remark
|
||||
}
|
||||
doc[activitymod.FN.LotteryGetGift.GetGiftAt] = now
|
||||
doc[activitymod.FN.LotteryGetGift.UpdatedAt] = now
|
||||
doc[activitymod.FN.LotteryGetGift.IsUsed] = true
|
||||
updateDoc := bson.M{"$set": doc}
|
||||
err := activitymod.UpdateOneLotteryGetGift(ctx, filter, updateDoc)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 删除一个活动奖品兑换信息
|
||||
func DelOneRecvGiftInfo(ctx context.Context, param proto.DelGetGiftReq) (stdLog.CodeType, error) {
|
||||
filter := bson.M{activitymod.FN.LotteryGetGift.Id: param.Id}
|
||||
count, err := activitymod.DelOneLotteryGetGift(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
if count == 0 {
|
||||
return stdLog.DataNotExist, nil
|
||||
}
|
||||
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 奖品兑换详情列表
|
||||
func GetReceiveGiftInfoList(ctx context.Context, param proto.GetGiftInfoReq) (stdLog.CodeType, *proto.GetGiftInfoResp, error) {
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
if param.PageNum <= 0 {
|
||||
param.PageNum = 1
|
||||
}
|
||||
data := &proto.GetGiftInfoResp{List: make(activitymod.LotteryGetGiftList, 0)}
|
||||
filter := bson.M{}
|
||||
if param.LotteryCode != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.LotteryCode] = *param.LotteryCode
|
||||
}
|
||||
if param.ActId != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.ActId] = bson.M{"$regex": *param.ActId}
|
||||
}
|
||||
if param.ActName != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.ActName] = bson.M{"$regex": *param.ActName}
|
||||
}
|
||||
if param.GiftId != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.GiftId] = bson.M{"$regex": *param.GiftId}
|
||||
}
|
||||
if param.GiftName != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.GiftName] = *param.GiftName
|
||||
}
|
||||
if param.UserId != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.UserId] = *param.UserId
|
||||
}
|
||||
if param.AppName != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.AppName] = *param.AppName
|
||||
}
|
||||
if param.IsUsed != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.IsUsed] = *param.IsUsed
|
||||
}
|
||||
if param.GiftCode != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.GiftCode] = *param.GiftCode
|
||||
}
|
||||
if param.GiftCode != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.GiftCode] = *param.GiftCode
|
||||
}
|
||||
if param.IsGaveOut != nil {
|
||||
filter[activitymod.FN.LotteryGetGift.IsGaveOut] = *param.IsGaveOut
|
||||
}
|
||||
count, err := activitymod.CountLotteryGetGifts(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.Total = count
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
opt.SetSkip(skip)
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSort(bson.M{activitymod.FN.LotteryGetGift.CreatedAt: -1})
|
||||
if err := activitymod.FindManyLotteryGetGiftWithBind(ctx, &data.List, filter, opt); err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
return stdLog.Success, data, nil
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package activitySer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/activitymod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 添加活动奖品
|
||||
func AddOneGift(ctx context.Context, param proto.AddGiftReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
filter := bson.M{activitymod.FN.ActivityInfo.Id: param.ActId}
|
||||
info, err := activitymod.FindOneActivityInfo(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, err
|
||||
}
|
||||
doc := &activitymod.LotteryGift{
|
||||
Id: primitive.NewObjectID(),
|
||||
ActId: info.Id.Hex(),
|
||||
ActName: info.Name,
|
||||
GiftName: param.GiftName,
|
||||
GiftDesc: param.GiftDesc,
|
||||
Rate: param.Rate,
|
||||
Sort: param.Sort,
|
||||
CreatedAt: now,
|
||||
}
|
||||
if err = activitymod.InsertOneLotteryGift(ctx, doc); err != nil {
|
||||
return stdLog.InsertDataError, err
|
||||
}
|
||||
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 修改奖品信息
|
||||
func UpdateOneGift(ctx context.Context, param *proto.UpdateGiftReq) (stdLog.CodeType, error) {
|
||||
now := time.Now()
|
||||
param.UpdatedAt = now
|
||||
filter := bson.M{activitymod.FN.LotteryGift.Id: param.Id}
|
||||
updateDoc := bson.M{"$set": param}
|
||||
err := activitymod.UpdateOneLotteryGift(ctx, filter, updateDoc)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 删除一个活动奖品
|
||||
func DeleteOneGift(ctx context.Context, param proto.DelGiftReq) (stdLog.CodeType, error) {
|
||||
filter := bson.M{activitymod.FN.LotteryGift.Id: param.Id}
|
||||
count, err := activitymod.DelOneLotteryGift(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.UpdateDataError, err
|
||||
}
|
||||
if count == 0 {
|
||||
return stdLog.DataNotExist, nil
|
||||
}
|
||||
|
||||
return stdLog.Success, nil
|
||||
}
|
||||
|
||||
// 活动奖品列表
|
||||
func GetLotteryGiftList(ctx context.Context, param proto.GiftListReq) (stdLog.CodeType, *proto.GiftListResp, error) {
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
if param.PageNum <= 0 {
|
||||
param.PageNum = 1
|
||||
}
|
||||
data := &proto.GiftListResp{List: make([]*proto.GiftInfo, 0)}
|
||||
filter := bson.M{}
|
||||
if param.ActId != nil {
|
||||
filter[activitymod.FN.LotteryGift.ActId] = *param.ActId
|
||||
}
|
||||
if param.GiftName != nil {
|
||||
filter[activitymod.FN.LotteryGift.GiftName] = bson.M{"$regex": *param.GiftName}
|
||||
}
|
||||
if param.GiftDesc != nil {
|
||||
filter[activitymod.FN.LotteryGift.GiftDesc] = bson.M{"$regex": *param.GiftDesc}
|
||||
}
|
||||
if param.Rate != nil {
|
||||
filter[activitymod.FN.LotteryGift.Rate] = *param.Rate
|
||||
}
|
||||
count, err := activitymod.CountLotteryGifts(ctx, filter)
|
||||
if err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
data.Total = count
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
if param.PageSize <= 0 {
|
||||
param.PageSize = 10
|
||||
}
|
||||
opt.SetSkip(skip)
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSort(bson.M{activitymod.FN.LotteryGift.Sort: 1})
|
||||
if err := activitymod.FindManyLotteryGiftWithBind(ctx, &data.List, filter, opt); err != nil {
|
||||
return stdLog.QueryDataError, data, err
|
||||
}
|
||||
for _, v := range data.List {
|
||||
gFilter := bson.M{activitymod.FN.LotteryGetGift.GiftId: v.Id.Hex()}
|
||||
gCount, _ := activitymod.CountLotteryGetGifts(ctx, gFilter)
|
||||
v.GiftNum = gCount
|
||||
|
||||
gFilter[activitymod.FN.LotteryGetGift.IsUsed] = true
|
||||
usedCount, _ := activitymod.CountLotteryGetGifts(ctx, gFilter)
|
||||
v.ReceivedGiftNum = usedCount
|
||||
}
|
||||
return stdLog.Success, data, nil
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package adminSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"stat/middleware/define"
|
||||
"time"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common"
|
||||
"stat/common/log"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/adminMod"
|
||||
"stat/models/authorityMod"
|
||||
)
|
||||
|
||||
func AdminLogin(ctx context.Context, param proto.AdminVerifyReq) (code stdLog.CodeType, data adminMod.Admin, err error) {
|
||||
res, err := adminMod.FindOneByAccount(ctx, *param.Account)
|
||||
if err != nil || res.Account == "" {
|
||||
code = stdLog.AdminAccountNotExists
|
||||
return
|
||||
}
|
||||
|
||||
// 绑定两步验证
|
||||
if !res.HasBind {
|
||||
_ = adminMod.TwoStepBind(ctx, res.Account)
|
||||
}
|
||||
|
||||
// 首次登陆,生成token
|
||||
|
||||
roleInfo, err := authorityMod.GetAuthorityById(ctx, res.RoleId)
|
||||
if err != nil {
|
||||
log.Error("GetAuthorityById failed", log.E(err))
|
||||
return stdLog.Failure, data, err
|
||||
|
||||
}
|
||||
|
||||
// 生成token
|
||||
claims := define.Claims{
|
||||
Type: define.Admin,
|
||||
Name: res.Account,
|
||||
Role: roleInfo.Role,
|
||||
CID: res.MerchantId,
|
||||
}
|
||||
|
||||
newToken, err := define.GenToken(&claims)
|
||||
if err != nil {
|
||||
log.Error("GenerateUserToken failed", log.E(err))
|
||||
code = stdLog.Failure
|
||||
return code, data, err
|
||||
}
|
||||
|
||||
res.Token = newToken
|
||||
|
||||
// 刷新token
|
||||
if err = adminMod.RefreshToken(ctx, res.Account, newToken); err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, data, err
|
||||
}
|
||||
|
||||
data = res
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 管理员列表
|
||||
func AdminList(ctx context.Context, param proto.AdminListReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
limit := param.PageSize
|
||||
resp := proto.AdminListRes{
|
||||
Total: 0,
|
||||
List: make([]proto.AdminInfo, 0),
|
||||
}
|
||||
|
||||
count, adminList, err := adminMod.FindAdminList(ctx, param.BlocRole, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
authIds := make([]int64, 0)
|
||||
for _, v := range adminList {
|
||||
authIds = append(authIds, v.RoleId)
|
||||
}
|
||||
|
||||
// 获取权限角色
|
||||
authList, err := authorityMod.GetAuthorityByIds(ctx, authIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
authMap := make(map[int64]*authorityMod.Authority)
|
||||
for _, v := range authList {
|
||||
_v := v
|
||||
authMap[v.Id] = &_v
|
||||
}
|
||||
|
||||
for _, v := range adminList {
|
||||
role := ""
|
||||
if roleInfo, ok := authMap[v.RoleId]; ok {
|
||||
role = roleInfo.Role
|
||||
}
|
||||
|
||||
tmp := proto.AdminInfo{
|
||||
RoleId: v.RoleId,
|
||||
Role: role,
|
||||
MerchantId: v.MerchantId,
|
||||
Account: v.Account,
|
||||
Password: v.Password,
|
||||
Name: v.Name,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
LastTime: v.LastTime,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
resp.Total = count
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加管理员账号
|
||||
func AddAdmin(ctx context.Context, param proto.AddAdminReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
secret := base32.StdEncoding.EncodeToString([]byte(fmt.Sprintf("admin%s", common.RandStr(10))))
|
||||
now := time.Now()
|
||||
doc := adminMod.Admin{
|
||||
Account: *param.Account,
|
||||
Password: *param.Password,
|
||||
Name: *param.Name,
|
||||
RoleId: *param.RoleId,
|
||||
HasBind: false,
|
||||
Secret: secret,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if param.MerchantId != nil {
|
||||
doc.MerchantId = *param.MerchantId
|
||||
}
|
||||
if param.BlocRole != nil {
|
||||
doc.BlocRole = *param.BlocRole
|
||||
}
|
||||
roleInfo, err := authorityMod.GetAuthorityById(ctx, doc.RoleId)
|
||||
if err != nil {
|
||||
log.Error("GetAuthorityById failed", log.E(err))
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// 生成token
|
||||
claims := define.Claims{
|
||||
Type: define.Admin,
|
||||
Name: doc.Account,
|
||||
Role: roleInfo.Role,
|
||||
CID: doc.MerchantId,
|
||||
}
|
||||
|
||||
newToken, err := define.GenToken(&claims)
|
||||
if err != nil {
|
||||
log.Error("GenerateUserToken failed", log.E(err))
|
||||
code = stdLog.Failure
|
||||
return code, data, err
|
||||
}
|
||||
|
||||
doc.Token = newToken
|
||||
|
||||
if err = adminMod.AddAdmin(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改管理员账号
|
||||
func UpdateAdmin(ctx context.Context, param proto.UpdateAdminReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
doc := adminMod.UpdateDoc{
|
||||
Account: param.Account,
|
||||
Password: param.Password,
|
||||
Name: param.Name,
|
||||
RoleId: param.RoleId,
|
||||
Status: param.Status,
|
||||
MerchantId: param.MerchantId,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
|
||||
if err = adminMod.UpdateAdmin(ctx, *param.Account, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改管理员账号
|
||||
func DelAdmin(ctx context.Context, param proto.DelAdminReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = adminMod.DelAdmin(ctx, *param.Account); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package adminSer
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"stat/common"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddAdmin(t *testing.T) {
|
||||
secret := base32.StdEncoding.EncodeToString([]byte(fmt.Sprintf("admin%s", common.RandStr(10))))
|
||||
t.Log(secret)
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
package advertiseSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/genId"
|
||||
"stat/common/log"
|
||||
"stat/common/redis"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/advertiseLocMod"
|
||||
"stat/models/advertiseMod"
|
||||
"stat/models/appInfoMod"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// 获取广告列表
|
||||
func ListAds(ctx context.Context, param proto.ListAdsReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListAdsRes{
|
||||
Total: 0,
|
||||
List: make([]proto.AdsInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
// 获取广告位置信息
|
||||
total, adsList, err := advertiseMod.FindAdvertiseList(ctx, param.AppId, param.IsActive,
|
||||
param.LocId, skip, limit, param.Link, param.Title, param.DaoLiangTitle)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
// 获取app信息
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range adsList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
appList, err := appInfoMod.GetAppInfosByAppIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
appMap := make(map[int32]appInfoMod.AppInfo)
|
||||
for _, v := range appList {
|
||||
appMap[v.AppId] = v
|
||||
}
|
||||
|
||||
for _, v := range adsList {
|
||||
appName := ""
|
||||
if appInfo, ok := appMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
// 获取位置信息
|
||||
adsLocInfo, err := advertiseLocMod.FindLocationByAppIdAndLocId(ctx, v.AppId, v.LocId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, data, err
|
||||
}
|
||||
str := make([]string, 0)
|
||||
if v.Tags != "" {
|
||||
split := strings.Split(strings.TrimSpace(v.Tags), ",")
|
||||
for _, s := range split {
|
||||
str = append(str, s)
|
||||
}
|
||||
}
|
||||
tmp := proto.AdsInfo{
|
||||
Id: v.Id,
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
Title: v.Title,
|
||||
DaoLiangTitle: v.DaoLiangTitle,
|
||||
Tags: str,
|
||||
CoverImg: v.CoverImg,
|
||||
VisibleChannels: v.VisibleChannels,
|
||||
LocId: v.LocId,
|
||||
LocName: adsLocInfo.Name,
|
||||
ImgSizeDesc: adsLocInfo.ImgSizeDesc,
|
||||
Links: make([]proto.LinkInfo, 0),
|
||||
Sort: v.Sort,
|
||||
IsActive: v.IsActive,
|
||||
StartTime: v.StartTime,
|
||||
EndTime: v.EndTime,
|
||||
Remark: v.Remark,
|
||||
ClickCount: v.ClickCount,
|
||||
AmbientColor: v.AmbientColor,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
for _, v := range v.Links {
|
||||
tmp.Links = append(tmp.Links, proto.LinkInfo{
|
||||
Link: v.Link,
|
||||
Foreign: v.Foreign,
|
||||
Weight: v.Weight,
|
||||
})
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
data = resp
|
||||
return
|
||||
}
|
||||
|
||||
// 新增广告
|
||||
func AddAds(ctx context.Context, param proto.AddAdsReq, account string) (code stdLog.CodeType, data interface{}, err error) {
|
||||
adsId := genId.GetNextAdsId()
|
||||
now := time.Now()
|
||||
|
||||
doc := advertiseMod.Advertise{
|
||||
Id: adsId,
|
||||
AppId: param.AppId,
|
||||
Title: param.Title,
|
||||
Tags: param.Tags,
|
||||
CoverImg: param.CoverImg,
|
||||
LocId: param.LocId,
|
||||
Sort: param.Sort,
|
||||
IsActive: param.IsActive,
|
||||
StartTime: param.StartTime,
|
||||
EndTime: param.EndTime,
|
||||
Remark: param.Remark,
|
||||
AmbientColor: param.AmbientColor,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
for _, v := range param.Links {
|
||||
doc.Links = append(doc.Links, advertiseMod.LinkInfo{
|
||||
Link: v.Link,
|
||||
Foreign: v.Foreign,
|
||||
Weight: v.Weight,
|
||||
})
|
||||
}
|
||||
|
||||
if err = advertiseMod.AddAdvertise(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
newInfo, _ := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": adsId})
|
||||
if newInfo != nil {
|
||||
CheckChange(ctx, nil, newInfo, account)
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改广告位置
|
||||
func UpdateAds(ctx context.Context, param proto.UpdateAdsReq, account string) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
|
||||
doc := advertiseMod.UpdateAds{
|
||||
AppId: param.AppId,
|
||||
Title: param.Title,
|
||||
DaoLiangTitle: param.DaoLiangTitle,
|
||||
Tags: param.Tags,
|
||||
CoverImg: param.CoverImg,
|
||||
Location: param.Location,
|
||||
JumpType: param.JumpType,
|
||||
Sort: param.Sort,
|
||||
IsActive: param.IsActive,
|
||||
StartTime: param.StartTime,
|
||||
EndTime: param.EndTime,
|
||||
Remark: param.Remark,
|
||||
AmbientColor: param.AmbientColor,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
if *param.Links != nil {
|
||||
links := make([]advertiseMod.LinkInfo, 0)
|
||||
for _, v := range *param.Links {
|
||||
links = append(links, advertiseMod.LinkInfo{
|
||||
Link: v.Link,
|
||||
Foreign: v.Foreign,
|
||||
Weight: v.Weight,
|
||||
})
|
||||
}
|
||||
doc.Links = &links
|
||||
}
|
||||
|
||||
if param.VisibleChannels != nil {
|
||||
channels := make([]string, 0)
|
||||
if len(*param.VisibleChannels) > 0 {
|
||||
ch := strings.Split(*param.VisibleChannels, ",")
|
||||
channels = append(channels, ch...)
|
||||
}
|
||||
|
||||
doc.VisibleChannels = &channels
|
||||
}
|
||||
if doc.Location != nil {
|
||||
locId := int32(*doc.Location)
|
||||
doc.LocId = &locId
|
||||
}
|
||||
oldInfo, err := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": *param.Id})
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
if err = advertiseMod.UpdateAdvertise(ctx, *param.Id, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
newInfo, err := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": *param.Id})
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
CheckChange(ctx, oldInfo, newInfo, account)
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除广告
|
||||
func DelAds(ctx context.Context, param proto.DelAdsReq, account string) (code stdLog.CodeType, data interface{}, err error) {
|
||||
|
||||
oldInfo, err := advertiseMod.FindOneAdvertise(ctx, bson.M{"_id": *param.Id})
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
if err = advertiseMod.DelAdvertise(ctx, *param.Id); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
CheckChange(ctx, oldInfo, nil, account)
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 外部获取广告
|
||||
func GetAdvertise(ctx context.Context, param proto.AdvertiseReq) (code stdLog.CodeType, resp *proto.AdvertiseRes, err error) {
|
||||
resp = &proto.AdvertiseRes{AdvertiseList: make([]*proto.AdvertiseInfo, 0)}
|
||||
log.Info("GetAdvertise", log.Any("param", param))
|
||||
|
||||
adsList, err := advertiseMod.FindAdsByAppAndLocation(ctx, param.AppId, param.LocId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("GetAdvertise", log.Any("param", param), log.Any("adsList", len(adsList)))
|
||||
|
||||
// 遍历广告,处理系统广告
|
||||
for _, v := range adsList {
|
||||
adsInfo := &proto.AdvertiseInfo{
|
||||
Id: v.Id,
|
||||
Title: v.Title,
|
||||
CoverImg: v.CoverImg,
|
||||
LocId: v.LocId,
|
||||
JumpType: v.JumpType,
|
||||
Sort: v.Sort,
|
||||
Type: 0,
|
||||
Tags: v.Tags,
|
||||
Remark: v.Remark,
|
||||
AmbientColor: v.AmbientColor,
|
||||
}
|
||||
if len(v.Links) >= 1 {
|
||||
adsInfo.Link = v.Links[0].Link
|
||||
}
|
||||
resp.AdvertiseList = append(resp.AdvertiseList, adsInfo)
|
||||
}
|
||||
code = stdLog.Success
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
// 增加广告点击次数
|
||||
func IncAdsClickCount(ctx context.Context, param proto.AdsClickReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 获取广告信息
|
||||
adsInfo, err := advertiseMod.FindAdsById(ctx, *param.Id)
|
||||
if err != nil || adsInfo.Id == 0 {
|
||||
code = stdLog.AdvertiseInvalid
|
||||
return
|
||||
}
|
||||
|
||||
if adsInfo.AppId != *param.AppId {
|
||||
code = stdLog.AppInfoError
|
||||
return
|
||||
}
|
||||
// save to redis
|
||||
now := time.Now().Local()
|
||||
set := redis.GetAdvertiseClickDayHash(now)
|
||||
name := redis.GetAdvertiseClickHourName(adsInfo.AppId, adsInfo.Id, now.Hour())
|
||||
if _, err = set.IncInt(name, 1); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
// update click+1 to db
|
||||
if err = advertiseMod.IncAdsClickCount(ctx, *param.Id); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package advertiseSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/genId"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/advertiseLocMod"
|
||||
"stat/models/advertiseMod"
|
||||
"stat/models/appInfoMod"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取广告位置列表
|
||||
func ListAdsLoc(ctx context.Context, param proto.ListAdsLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListAdsLocRes{
|
||||
Total: 0,
|
||||
List: make([]proto.AdsLocInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
// 获取广告位置信息
|
||||
total, adsList, err := advertiseLocMod.FindAdvertiseLocationList(ctx, param.AppId, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
// 获取app信息
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range adsList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
appList, err := appInfoMod.GetAppInfosByAppIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
appMap := make(map[int32]appInfoMod.AppInfo)
|
||||
for _, v := range appList {
|
||||
appMap[v.AppId] = v
|
||||
}
|
||||
|
||||
for _, v := range adsList {
|
||||
appName := ""
|
||||
if appInfo, ok := appMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
tmp := proto.AdsLocInfo{
|
||||
Id: v.Id,
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
LocId: v.LocId,
|
||||
Name: v.Name,
|
||||
ImgSizeDesc: v.ImgSizeDesc,
|
||||
Remark: v.Remark,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
data = resp
|
||||
return
|
||||
}
|
||||
|
||||
// 新增广告位置
|
||||
func AddAdsLoc(ctx context.Context, param proto.AddAdsLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 获取位置信息
|
||||
locInfo, err := advertiseLocMod.FindLocationByAppIdAndLocId(ctx, *param.AppId, *param.LocId)
|
||||
if err != nil || locInfo.Id > 0 {
|
||||
code = stdLog.AdvertiseLocExists
|
||||
return
|
||||
}
|
||||
|
||||
adsLocId := genId.GetNextAdsLocId()
|
||||
now := time.Now()
|
||||
|
||||
doc := advertiseLocMod.AdvertiseLocation{
|
||||
Id: adsLocId,
|
||||
AppId: *param.AppId,
|
||||
LocId: *param.LocId,
|
||||
Name: *param.Name,
|
||||
ImgSizeDesc: param.ImgSizeDesc,
|
||||
Remark: param.Remark,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err = advertiseLocMod.AddAdvertiseLocation(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改广告位置
|
||||
func UpdateAdsLoc(ctx context.Context, param proto.UpdateAdsLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 获取位置信息
|
||||
locInfo, err := advertiseLocMod.FindLocationById(ctx, *param.Id)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
doc := advertiseLocMod.UpdateAdsLoc{
|
||||
AppId: param.AppId,
|
||||
LocId: param.LocId,
|
||||
Name: param.Name,
|
||||
ImgSizeDesc: param.ImgSizeDesc,
|
||||
Remark: param.Remark,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
|
||||
if err = advertiseLocMod.UpdateAdvertiseLocation(ctx, *param.Id, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
// 修改广告的位置id
|
||||
if param.LocId != nil && *param.LocId != locInfo.LocId {
|
||||
if err = advertiseMod.UpdateLocInfo(ctx, locInfo.AppId, locInfo.LocId, *param.LocId); err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, data, err
|
||||
}
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除广告位置
|
||||
func DelAdsLoc(ctx context.Context, param proto.DelAdsLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = advertiseLocMod.DelAdvertiseLocation(ctx, *param.Id); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
package advertiseSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/log"
|
||||
"stat/models/advertiseLocMod"
|
||||
"stat/models/advertiseMod"
|
||||
"stat/models/advertisestat"
|
||||
"stat/models/appInfoMod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// GetAdvertiseStatList 获取广告统计列表
|
||||
func GetAdvertiseStatList(ctx context.Context, param *GetAdvertiseStatReq) (*GetAdvertiseStatResp, error) {
|
||||
resp := &GetAdvertiseStatResp{}
|
||||
statFilter := bson.M{}
|
||||
if param.AppId != nil {
|
||||
statFilter[advertisestat.FN.AdvertiseStat.AppId] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
if param.AdsId != nil {
|
||||
statFilter[advertisestat.FN.AdvertiseStat.AdsId] = *param.AdsId
|
||||
}
|
||||
if param.AdsName != nil {
|
||||
adsInfos, err := advertiseMod.FindAdvertiselist(ctx, bson.M{"title": param.AdsName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adsIds := make([]int64, 0)
|
||||
for _, ad := range adsInfos {
|
||||
adsIds = append(adsIds, ad.Id)
|
||||
}
|
||||
|
||||
statFilter[advertisestat.FN.AdvertiseStat.AdsId] = bson.M{"$in": adsIds}
|
||||
}
|
||||
if param.Start != nil || param.End != nil {
|
||||
statFilter[advertisestat.FN.AdvertiseStat.SumData] = bson.M{"$gte": *param.Start, "$lte": *param.End}
|
||||
}
|
||||
filter := bson.M{}
|
||||
// app dict
|
||||
appDict, err := appInfoMod.GetAllAppIdMap(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ads dict
|
||||
adsDict, err := advertiseMod.GetAllAdvertiseMap(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ads location dict
|
||||
locDict, err := advertiseLocMod.GetAllAdvertiseLocationMap(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.D{
|
||||
{advertisestat.FN.AdvertiseStat.SumData, -1},
|
||||
{advertisestat.FN.AdvertiseStat.Total, -1},
|
||||
})
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
totalStats, err := advertisestat.FindManyAdvertiseStat(ctx, statFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageStats, err := advertisestat.FindManyAdvertiseStat(ctx, statFilter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, stat := range totalStats {
|
||||
resp.Summary.TotalClick += stat.Total
|
||||
}
|
||||
for _, stat := range pageStats {
|
||||
appName := ""
|
||||
if app := appDict[stat.AppId]; app != nil {
|
||||
appName = app.Name
|
||||
}
|
||||
adsName := ""
|
||||
locId := int32(0)
|
||||
if ads := adsDict[stat.AdsId]; ads != nil {
|
||||
adsName = ads.Title
|
||||
locId = ads.LocId
|
||||
}
|
||||
locName := ""
|
||||
if appLoc := locDict[stat.AppId]; appLoc != nil {
|
||||
if loc := appLoc[locId]; loc != nil {
|
||||
locName = loc.Name
|
||||
}
|
||||
}
|
||||
hours := make([]int64, 24)
|
||||
for hour := range hours {
|
||||
hours[hour] = stat.HourData[hour]
|
||||
}
|
||||
resp.List = append(resp.List, &AdvertiseStatInfo{
|
||||
Id: stat.Id,
|
||||
AdsId: stat.AdsId,
|
||||
AdsName: adsName,
|
||||
AppId: stat.AppId,
|
||||
AppName: appName,
|
||||
LocId: locId,
|
||||
LocName: locName,
|
||||
SumDate: stat.SumDate,
|
||||
Total: stat.Total,
|
||||
HourData: hours,
|
||||
CreatedAt: stat.CreatedAt,
|
||||
UpdatedAt: stat.UpdatedAt,
|
||||
})
|
||||
}
|
||||
resp.Total = int64(len(totalStats))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAdvertiseStatList 获取广告统计列表
|
||||
func GetAdvertiseTotalStatList(ctx context.Context, param *GetAdvertiseTotalStatReq) (*GetAdvertiseTotalStatResp,
|
||||
error) {
|
||||
resp := &GetAdvertiseTotalStatResp{
|
||||
List: make([]*AdvertiseTotalStatInfo, 0),
|
||||
}
|
||||
/*
|
||||
statFilter := bson.M{}
|
||||
if param.AppId != nil {
|
||||
statFilter[advertisestat.FN.AdvertiseStat.AppId] = *param.AppId
|
||||
}
|
||||
if param.Start != nil || param.End != nil {
|
||||
statFilter[advertisestat.FN.AdvertiseStat.SumData] = bson.M{"$gte": *param.Start, "$lte": *param.End}
|
||||
}
|
||||
*/
|
||||
// app dict
|
||||
appFilter := bson.M{}
|
||||
if param.AppId != nil {
|
||||
appFilter[appInfoMod.FN.AppInfo.AppId] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
apps, serr := appInfoMod.GetAllAppIdMap(ctx, appFilter)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
// ads dict
|
||||
adsFilter := bson.M{}
|
||||
if param.AppId != nil {
|
||||
adsFilter["appId"] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
if param.AdsId != nil {
|
||||
adsFilter["_id"] = *param.AdsId
|
||||
}
|
||||
if param.AdsName != nil {
|
||||
adsFilter["title"] = *param.AdsName
|
||||
}
|
||||
adsDict, err := advertiseMod.GetAllAdvertiseMap(ctx, adsFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ads location dict
|
||||
locFilter := bson.M{}
|
||||
if param.AppId != nil {
|
||||
locFilter[appInfoMod.FN.AppInfo.AppId] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
locDict, err := advertiseLocMod.GetAllAdvertiseLocationMap(ctx, locFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filter := bson.M{}
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
if param.AdsId != nil {
|
||||
filter["adsId"] = *param.AdsId
|
||||
}
|
||||
if param.AdsName != nil {
|
||||
adsInfos, err := advertiseMod.FindAdvertiselist(ctx, bson.M{"title": param.AdsName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adsIds := make([]int64, 0)
|
||||
for _, ad := range adsInfos {
|
||||
adsIds = append(adsIds, ad.Id)
|
||||
}
|
||||
|
||||
filter["adsId"] = bson.M{"$in": adsIds}
|
||||
}
|
||||
if param.Start != nil && param.End != nil {
|
||||
filter["sumDate"] = bson.M{"$gte": *param.Start, "$lte": *param.End}
|
||||
}
|
||||
pipeline := []bson.M{
|
||||
{
|
||||
"$match": filter,
|
||||
},
|
||||
{
|
||||
"$group": bson.M{
|
||||
"_id": "$adsId",
|
||||
"total": bson.M{"$sum": "$total"},
|
||||
},
|
||||
},
|
||||
{"$sort": bson.D{{Key: "_id", Value: -1}}},
|
||||
}
|
||||
|
||||
list := make([]AdvertiseTempInfo, 0)
|
||||
serr1 := advertisestat.AggregateAdvertiseStat(ctx, &list, pipeline)
|
||||
if serr1 != nil {
|
||||
log.Info("", log.Any("serr1", serr1))
|
||||
return nil, serr1
|
||||
}
|
||||
|
||||
pipeline = []bson.M{
|
||||
{
|
||||
"$match": filter,
|
||||
},
|
||||
{
|
||||
"$group": bson.M{
|
||||
"_id": "$adsId",
|
||||
"total": bson.M{"$sum": "$total"},
|
||||
},
|
||||
},
|
||||
{"$sort": bson.D{{Key: "_id", Value: -1}}},
|
||||
{"$skip": (param.PageNum - 1) * param.PageSize},
|
||||
{"$limit": param.PageSize},
|
||||
}
|
||||
|
||||
info := make([]AdvertiseTempInfo, 0)
|
||||
serr1 = advertisestat.AggregateAdvertiseStat(ctx, &info, pipeline)
|
||||
if serr1 != nil {
|
||||
log.Info("", log.Any("serr1", serr1))
|
||||
return nil, serr1
|
||||
}
|
||||
|
||||
for _, temp := range list {
|
||||
resp.Summary.TotalClick += temp.Total
|
||||
}
|
||||
|
||||
for _, stat := range info {
|
||||
appName := ""
|
||||
adsName := ""
|
||||
locName := ""
|
||||
locId := int32(0)
|
||||
appId := int32(0)
|
||||
if ads := adsDict[stat.AdsId]; ads != nil {
|
||||
adsName = ads.Title
|
||||
locId = ads.LocId
|
||||
if app := apps[ads.AppId]; app != nil {
|
||||
appName = app.Name
|
||||
appId = app.AppId
|
||||
if appLoc := locDict[app.AppId]; appLoc != nil {
|
||||
if loc := appLoc[locId]; loc != nil {
|
||||
locName = loc.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, &AdvertiseTotalStatInfo{
|
||||
AdsId: stat.AdsId,
|
||||
AdsName: adsName,
|
||||
AppId: appId,
|
||||
AppName: appName,
|
||||
LocId: int64(locId),
|
||||
LocName: locName,
|
||||
Total: stat.Total,
|
||||
})
|
||||
}
|
||||
|
||||
resp.Total = int64(len(list))
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package advertiseSer
|
||||
|
||||
import (
|
||||
"stat/common"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type AdvertiseStatSummary struct {
|
||||
TotalClick int64 `json:"totalClick"` // 总点击
|
||||
}
|
||||
|
||||
type AdvertiseStatInfo struct {
|
||||
Id primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
AdsId int64 `json:"adsId" bson:"adsId"` //广告ID
|
||||
AdsName string `json:"adsName" bson:"adsName"` //广告名称
|
||||
AppId int32 `json:"appId" bson:"appId"` //应用ID
|
||||
AppName string `json:"appName" bson:"appName"` //应用名称
|
||||
LocId int32 `json:"locId" bson:"locId"` //位置ID
|
||||
LocName string `json:"locName" bson:"locName"` //位置名称
|
||||
SumDate time.Time `json:"sumDate" bson:"sumDate"` //记录时间
|
||||
Total int64 `json:"total" bson:"total"` //当日点击
|
||||
HourData []int64 `json:"hourData" bson:"hourData"` //小时数据
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //创建时间
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` //更新时间
|
||||
}
|
||||
|
||||
// GetAdvertiseStatReq 获取app点击统计请求
|
||||
type GetAdvertiseStatReq struct {
|
||||
common.Page
|
||||
Start *time.Time `json:"start"` // 开始时间
|
||||
End *time.Time `json:"end"` // 结束时间
|
||||
AdsId *int64 `json:"adsId"` // 广告ID
|
||||
AppId *[]int64 `json:"appId"` // 应用ID
|
||||
AdsName *string `json:"adsName"` // 广告名称
|
||||
}
|
||||
|
||||
type GetAdvertiseStatResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*AdvertiseStatInfo `json:"list"`
|
||||
Summary AdvertiseStatSummary `json:"summary"`
|
||||
}
|
||||
|
||||
type AdvertiseTempInfo struct {
|
||||
AdsId int64 `json:"id" bson:"_id"` //广告ID
|
||||
Total int64 `json:"total" bson:"total"` //当日点击
|
||||
}
|
||||
|
||||
// GetAdvertiseTotalStatReq 获取app点击统计请求
|
||||
type GetAdvertiseTotalStatReq struct {
|
||||
common.Page
|
||||
Start *time.Time `json:"start"` // 开始时间
|
||||
End *time.Time `json:"end"` // 结束时间
|
||||
AppId *[]int64 `json:"appId"` // 应用ID
|
||||
AdsId *int64 `json:"adsId"` // 广告ID
|
||||
AdsName *string `json:"adsName"` // 广告名称
|
||||
}
|
||||
|
||||
type AdvertiseTotalStatInfo struct {
|
||||
AdsId int64 `json:"adsId" bson:"adsId"` //广告ID
|
||||
AdsName string `json:"adsName" bson:"adsName"` //广告名称
|
||||
AppId int32 `json:"appId" bson:"appId"` //应用ID
|
||||
AppName string `json:"appName" bson:"appName"` //应用名称
|
||||
LocId int64 `json:"locId" bson:"locId"` //位置ID
|
||||
LocName string `json:"locName" bson:"locName"` //位置名称
|
||||
Total int64 `json:"total" bson:"total"` //当日点击
|
||||
}
|
||||
|
||||
type GetAdvertiseTotalStatResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*AdvertiseTotalStatInfo `json:"list"`
|
||||
Summary AdvertiseStatSummary `json:"summary"`
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package advertiseSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/utils"
|
||||
"stat/models/advertiseLocMod"
|
||||
"stat/models/advertiseMod"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
func CheckChange(ctx context.Context, oldDoc, newDoc *advertiseMod.Advertise, account string) {
|
||||
msg := ""
|
||||
changed := false
|
||||
apps, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
appId := int32(0)
|
||||
// 检测更新
|
||||
if oldDoc != nil && newDoc != nil {
|
||||
warnChar := "☢️"
|
||||
appId = oldDoc.AppId
|
||||
msg += fmt.Sprintf("检测到广告内容变更:\n广告ID:【%d】\n名称:【%s】\n", oldDoc.Id, oldDoc.Title)
|
||||
locs, _ := advertiseLocMod.GetAllAdvertiseLocationMap(ctx, bson.M{})
|
||||
locName := ""
|
||||
if nm := locs[oldDoc.AppId]; nm != nil {
|
||||
if loc := nm[oldDoc.LocId]; loc != nil {
|
||||
locName = loc.Name
|
||||
}
|
||||
}
|
||||
msg += fmt.Sprintf("位置ID:【%d】\n位置名称:【%s】\n", oldDoc.LocId, locName)
|
||||
if oldDoc.AppId != newDoc.AppId {
|
||||
changed = true
|
||||
warnChar = "🔯"
|
||||
msg += fmt.Sprintf("应用变更:\n┏旧值【%s】(%d)\n┗新值【%s】(%d)\n", apps[oldDoc.AppId], oldDoc.AppId, apps[newDoc.AppId], newDoc.AppId)
|
||||
}
|
||||
if oldDoc.IsActive != newDoc.IsActive {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("激活变更:\n┏旧值【%v】【%v】\n┗新值【%v】【%v】\n", oldDoc.IsActive, consts.StatusDesc[oldDoc.IsActive],
|
||||
newDoc.IsActive, consts.StatusDesc[newDoc.IsActive])
|
||||
}
|
||||
if oldDoc.Title != newDoc.Title {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("标题变更:\n┏旧值【%s】\n┗新值【%s】\n", oldDoc.Title, newDoc.Title)
|
||||
}
|
||||
if oldDoc.Sort != newDoc.Sort {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("排序变更:\n┏旧值【%d】\n┗新值【%d】\n", oldDoc.Sort, newDoc.Sort)
|
||||
}
|
||||
if oldDoc.StartTime != newDoc.StartTime {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("开始时间变更:\n┏旧值【%s】\n┗新值【%s】\n", oldDoc.StartTime.Format("2006-01-02T15:04:05"), newDoc.StartTime.Format("2006-01-02T15:04:05"))
|
||||
}
|
||||
if oldDoc.EndTime != newDoc.EndTime {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("结束时间变更:\n┏旧值【%s】\n┗新值【%s】\n", oldDoc.EndTime.Format("2006-01-02T15:04:05"), newDoc.EndTime.Format("2006-01-02T15:04:05"))
|
||||
}
|
||||
if oldDoc.LocId != newDoc.LocId {
|
||||
changed = true
|
||||
oName := ""
|
||||
if om := locs[oldDoc.AppId]; om != nil {
|
||||
if loc := om[oldDoc.LocId]; loc != nil {
|
||||
oName = loc.Name
|
||||
}
|
||||
}
|
||||
nName := ""
|
||||
if nm := locs[newDoc.AppId]; nm != nil {
|
||||
if loc := nm[newDoc.LocId]; loc != nil {
|
||||
nName = loc.Name
|
||||
}
|
||||
}
|
||||
msg += fmt.Sprintf("位置变更:\n┏旧值【%s】(%d)\n┗新值【%s】(%d)\n", oName, oldDoc.LocId, nName, newDoc.LocId)
|
||||
}
|
||||
oLinks := utils.StrSliceConstruct(len(oldDoc.Links), func(index int) (string, bool) {
|
||||
link := oldDoc.Links[index]
|
||||
return link.Link, true
|
||||
})
|
||||
nLinks := utils.StrSliceConstruct(len(newDoc.Links), func(index int) (string, bool) {
|
||||
link := newDoc.Links[index]
|
||||
return link.Link, true
|
||||
})
|
||||
sort.Sort(sort.StringSlice(oLinks))
|
||||
sort.Sort(sort.StringSlice(nLinks))
|
||||
if strings.Join(oLinks, "") != strings.Join(nLinks, "") {
|
||||
warnChar = "🛑"
|
||||
changed = true
|
||||
msg += fmt.Sprintf("链接变更:\n")
|
||||
for index, link := range oLinks {
|
||||
msg += fmt.Sprintf("┣%d.旧值: %s\n", index+1, link)
|
||||
}
|
||||
for index, link := range nLinks {
|
||||
msg += fmt.Sprintf("┣%d.新值: %s\n", index+1, link)
|
||||
}
|
||||
} else {
|
||||
msg += fmt.Sprintf("链接不变:【%d个】\n", len(nLinks))
|
||||
for index, link := range nLinks {
|
||||
msg += fmt.Sprintf("┣%d.链接: %s\n", index+1, link)
|
||||
}
|
||||
}
|
||||
msg = warnChar + msg
|
||||
} else if oldDoc == nil && newDoc != nil {
|
||||
changed = true
|
||||
appId = newDoc.AppId
|
||||
msg += fmt.Sprintf("🟢检测到新增广告:\n广告ID:【%d】\n广告标题:【%s】\n", newDoc.Id, newDoc.Title)
|
||||
locs, _ := advertiseLocMod.GetAllAdvertiseLocationMap(ctx, bson.M{})
|
||||
locName := ""
|
||||
if nm := locs[newDoc.AppId]; nm != nil {
|
||||
if loc := nm[newDoc.LocId]; loc != nil {
|
||||
locName = loc.Name
|
||||
}
|
||||
}
|
||||
msg += fmt.Sprintf("位置ID:【%d】\n位置名称:【%s】\n", newDoc.LocId, locName)
|
||||
nLinks := utils.StrSliceConstruct(len(newDoc.Links), func(index int) (string, bool) {
|
||||
link := newDoc.Links[index]
|
||||
return link.Link, true
|
||||
})
|
||||
msg += fmt.Sprintf("链接:【%d个】\n", len(nLinks))
|
||||
for index, link := range nLinks {
|
||||
msg += fmt.Sprintf("┣%d.链接: %s\n", index+1, link)
|
||||
}
|
||||
} else if oldDoc != nil && newDoc == nil {
|
||||
changed = true
|
||||
appId = oldDoc.AppId
|
||||
msg += fmt.Sprintf("⛔️检测到删除广告:\n广告ID:【%d】\n广告标题:【%s】\n", oldDoc.Id, oldDoc.Title)
|
||||
locs, _ := advertiseLocMod.GetAllAdvertiseLocationMap(ctx, bson.M{})
|
||||
locName := ""
|
||||
if nm := locs[oldDoc.AppId]; nm != nil {
|
||||
if loc := nm[oldDoc.LocId]; loc != nil {
|
||||
locName = loc.Name
|
||||
}
|
||||
}
|
||||
msg += fmt.Sprintf("位置ID:【%d】\n位置名称:【%s】\n", oldDoc.LocId, locName)
|
||||
nLinks := utils.StrSliceConstruct(len(oldDoc.Links), func(index int) (string, bool) {
|
||||
link := oldDoc.Links[index]
|
||||
return link.Link, true
|
||||
})
|
||||
msg += fmt.Sprintf("链接:【%d个】\n", len(nLinks))
|
||||
for index, link := range nLinks {
|
||||
msg += fmt.Sprintf("┣%d.链接: %s\n", index+1, link)
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
msg += fmt.Sprintf("广告所属应用:【%s】(%d)\n", apps[appId], appId)
|
||||
msg += fmt.Sprintf("操作人员账号:【%s】\n", account)
|
||||
msg += fmt.Sprintf("操作发生时间:【%s】\n", time.Now().Format("2006-01-02T15:04:05"))
|
||||
msg += fmt.Sprintf("请确认是否异常: %s \n", statg.Tgbotsend)
|
||||
statg.Bot.SendAdvertiseMessage(msg)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,655 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"stat/api/proto"
|
||||
"stat/common"
|
||||
"stat/common/log"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/middleware/define"
|
||||
"stat/models/channelmod"
|
||||
"stat/statg"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func GetChannelInfoWithLockStatus(ctx context.Context, channel string) (*channelmod.ChannelInfo, stdLog.CodeType, error) {
|
||||
channelFilter := bson.M{channelmod.FN.ChannelInfo.Channel: channel}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, channelFilter)
|
||||
if err != nil || channelInfo.Channel == "" {
|
||||
return nil, stdLog.AgentAccountNotExists, fmt.Errorf("代理账号 %s 不存在", channel)
|
||||
}
|
||||
if channelInfo.Type.IsChild() {
|
||||
channelMap, _, err := MakeChannelAndRuleMap(ctx, channelInfo)
|
||||
if err != nil {
|
||||
return nil, stdLog.QueryDataError, err
|
||||
}
|
||||
lock, status, showDivide := channelMap.GetLockStatusAndShowDivide(channelInfo)
|
||||
channelInfo.Lock = lock
|
||||
channelInfo.Status = status
|
||||
channelInfo.ChildShowDivideRate = showDivide
|
||||
}
|
||||
return channelInfo, stdLog.Success, nil
|
||||
}
|
||||
|
||||
func CheckToken(ctx context.Context, channel string, token string) error {
|
||||
channelFilter := bson.M{channelmod.FN.ChannelInfo.Channel: channel}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, channelFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfo.Channel == "" {
|
||||
return fmt.Errorf("代理账号 %s 不存在", channel)
|
||||
}
|
||||
if channelInfo.Lock || !channelInfo.Status {
|
||||
return fmt.Errorf("代理账号 %s 被禁止", channel)
|
||||
}
|
||||
if channelInfo.Token != token {
|
||||
return fmt.Errorf("登陆信息已过期,请重新登陆")
|
||||
}
|
||||
if time.Since(channelInfo.CheckTokenTime) > 6*time.Hour {
|
||||
doc := bson.M{"$set": bson.M{
|
||||
channelmod.FN.ChannelInfo.CheckTokenTime: time.Now(),
|
||||
}}
|
||||
if err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channelInfo.Channel}, doc); err != nil {
|
||||
return err
|
||||
}
|
||||
channelInfoWithLock, _, err := GetChannelInfoWithLockStatus(ctx, channel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfoWithLock.Lock || !channelInfoWithLock.Status {
|
||||
doc := bson.M{"$set": bson.M{
|
||||
channelmod.FN.ChannelInfo.Token: "", // 清空token
|
||||
}}
|
||||
if err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channelInfo.Channel}, doc); err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("本账号的上级代理账号 %s 被禁止", channel)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 代理商账号登陆
|
||||
func AgentLogin(ctx context.Context, ip string, param proto.AgentLoginReq) (code stdLog.CodeType, channelInfo *channelmod.ChannelInfo) {
|
||||
var err error
|
||||
channelInfo, code, err = GetChannelInfoWithLockStatus(ctx, *param.Account)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 禁止登陆
|
||||
if channelInfo.Lock || !channelInfo.Status {
|
||||
code = stdLog.AgentHasLocked
|
||||
return
|
||||
}
|
||||
|
||||
if *param.Password != channelInfo.Password {
|
||||
code = stdLog.AdminPasswordError
|
||||
return
|
||||
}
|
||||
|
||||
if len(param.ImgCode) != 4 {
|
||||
code = stdLog.VerifySmsCodeFailed
|
||||
return
|
||||
}
|
||||
|
||||
imgs := make([]byte, 4)
|
||||
for i := range param.ImgCode {
|
||||
pi, err := strconv.Atoi(string(param.ImgCode[i]))
|
||||
if err != nil {
|
||||
code = stdLog.VerifySmsCodeFailed
|
||||
return
|
||||
}
|
||||
imgs[i] = byte(pi)
|
||||
}
|
||||
|
||||
imgCodeByte := statg.VerifyIMGStore.Get(param.ImgID, true)
|
||||
if bytes.Equal(imgCodeByte, imgs) {
|
||||
channelInfo.ShowMagic += 1
|
||||
doc := bson.M{"$set": bson.M{channelmod.FN.ChannelInfo.ShowMagic: channelInfo.ShowMagic}}
|
||||
if err = channelmod.UpdateOneChannelInfo(ctx, bson.M{}, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, channelInfo
|
||||
}
|
||||
code = stdLog.VerifySmsCodeFailed
|
||||
return
|
||||
} else if bytes.Equal(imgCodeByte, common.ArrayReverseByte(imgs)) {
|
||||
channelInfo.ShowMagic = 1
|
||||
} else {
|
||||
code = stdLog.VerifySmsCodeFailed
|
||||
return
|
||||
}
|
||||
|
||||
// 生成token
|
||||
claims := define.Claims{
|
||||
Type: define.Channel,
|
||||
Name: *param.Account,
|
||||
ShowMagic: strconv.Itoa(int(channelInfo.ShowMagic)),
|
||||
}
|
||||
//fmt.Println("claims", claims)
|
||||
if channelInfo.ShowMagic >= 3 {
|
||||
statg.Bot.SendMonitorMessage("渠道" + channelInfo.Channel + "风险登陆多次已禁止登陆.")
|
||||
}
|
||||
|
||||
newToken, err := define.GenToken(&claims)
|
||||
if err != nil {
|
||||
log.Error("GenerateUserToken failed", log.E(err))
|
||||
code = stdLog.Failure
|
||||
return code, channelInfo
|
||||
}
|
||||
channelInfo.Token = newToken
|
||||
|
||||
// 刷新token
|
||||
doc := bson.M{"$set": bson.M{
|
||||
channelmod.FN.ChannelInfo.Token: newToken,
|
||||
channelmod.FN.ChannelInfo.ShowMagic: channelInfo.ShowMagic,
|
||||
channelmod.FN.ChannelInfo.IP: ip,
|
||||
}}
|
||||
if err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channelInfo.Channel}, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, channelInfo
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
func AgentChannelAppInfo(ctx context.Context, req proto.ChannelAppInfoReq) (*proto.ChannelAppInfoResp, error) {
|
||||
checkFilter := bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: req.Channel,
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channelInfo.Channel != req.Channel {
|
||||
return nil, fmt.Errorf("channel not found. %s", req.Channel)
|
||||
}
|
||||
filter := bson.M{}
|
||||
if channelInfo.Type == channelmod.ChannelTypeParent {
|
||||
filter[channelmod.FN.ChannelInfo.Channel] = channelInfo.Channel
|
||||
} else {
|
||||
filter[channelmod.FN.ChannelInfo.Channel] = channelInfo.ParentChannel
|
||||
}
|
||||
ruleList, err := channelmod.FindManyChannelDeductionRule(ctx, filter)
|
||||
if err != nil {
|
||||
log.Error("AgentChannelAppInfo FindManyChannelDeductionRule", log.Any("channel", req.Channel), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
resp := &proto.ChannelAppInfoResp{
|
||||
List: make([]proto.ChannelAppInfo, 0),
|
||||
}
|
||||
for _, ruleInfo := range ruleList {
|
||||
temp := proto.ChannelAppInfo{
|
||||
AppId: ruleInfo.AppId,
|
||||
AppName: ruleInfo.AppName,
|
||||
LandingPage: ruleInfo.LandingPage + fmt.Sprintf("?dc=%s", req.Channel),
|
||||
}
|
||||
resp.List = append(resp.List, temp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AgentChannelListInfo 查询渠道列表
|
||||
func AgentChannelListInfo(ctx context.Context, channel string, req *proto.AgentChannelInfoSearch) (
|
||||
*proto.AgentChannelInfoSearchResp, error) {
|
||||
|
||||
filter := bson.M{"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelInfo.Channel: channel},
|
||||
bson.M{channelmod.FN.ChannelInfo.ParentAgent: channel},
|
||||
}}
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelInfo.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
resp := &proto.AgentChannelInfoSearchResp{}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelInfo.AppIds] = bson.M{"$in": []int32{*req.AppId}}
|
||||
}
|
||||
if req.Type != nil {
|
||||
filter[channelmod.FN.ChannelInfo.Type] = *req.Type
|
||||
}
|
||||
|
||||
total, serr := channelmod.CountChannelInfos(ctx, filter)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
channelInfoList, err := channelmod.FindManyChannelInfo(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.List = make([]proto.ChannelAgentInfo, 0)
|
||||
|
||||
for _, channchannelInfo := range channelInfoList {
|
||||
tmp := proto.ChannelAgentInfo{
|
||||
Channel: channchannelInfo.Channel,
|
||||
AppIds: channchannelInfo.AppIds,
|
||||
UserName: channchannelInfo.UserName,
|
||||
ParentAgent: channchannelInfo.ParentAgent,
|
||||
ChildCount: channchannelInfo.ChildCount,
|
||||
Status: channchannelInfo.Status,
|
||||
Mobile: channchannelInfo.Mobile,
|
||||
Type: channchannelInfo.Type,
|
||||
Remark: channchannelInfo.Remark,
|
||||
Lock: channchannelInfo.Lock,
|
||||
Wallet: channchannelInfo.Wallet,
|
||||
CreatedAt: channchannelInfo.CreatedAt,
|
||||
UpdatedAt: channchannelInfo.UpdatedAt,
|
||||
}
|
||||
|
||||
ruleFilter := bson.M{
|
||||
channelmod.FN.ChannelDeductionRule.Channel: channchannelInfo.Channel,
|
||||
}
|
||||
ruleInfoList, _ := channelmod.FindManyChannelDeductionRule(ctx, ruleFilter)
|
||||
for _, ruleInfo := range ruleInfoList {
|
||||
tmp.Settlement = ruleInfo.Settlement
|
||||
tmp.CPAPrice = ruleInfo.CPAPrice
|
||||
tmp.CPSRate = ruleInfo.CPSRate
|
||||
tmp.DivideRate = ruleInfo.DivideRate
|
||||
tmp.AppType = ruleInfo.AppType
|
||||
tmp.PromoteMode = ruleInfo.PromoteMode
|
||||
tmp.AppIds = []int32{int32(ruleInfo.AppId)}
|
||||
if tmp.DivideRate == 0 {
|
||||
tmp.DivideRate = 100
|
||||
}
|
||||
if tmp.Channel == channel {
|
||||
tmp.DivideRate = 100
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
}
|
||||
resp.Total = total
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AgentChannelRuleUpdate 更新渠道信息
|
||||
func AgentChannelRuleUpdate(ctx context.Context, channel string, req *proto.AgentChannelRuleUpdate) error {
|
||||
// 查找要修改规则的子代理
|
||||
channelFilter := bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: req.Channel,
|
||||
channelmod.FN.ChannelInfo.ParentAgent: channel,
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, channelFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfo.Channel != req.Channel {
|
||||
return fmt.Errorf("channel not found %s", req.Channel)
|
||||
}
|
||||
if req.CPSRate == nil && req.CPAPrice == nil && req.DivideRate == nil && req.ChildShowDivideRate == nil {
|
||||
return nil
|
||||
}
|
||||
if req.CPSRate != nil && !InRangeInt64(*req.CPSRate, 0, 100) {
|
||||
return fmt.Errorf("CPS比例 (%d) 必须在 0-100 之间", *req.CPSRate)
|
||||
}
|
||||
if req.DivideRate != nil && !InRangeInt64(*req.DivideRate, 0, 100) {
|
||||
return fmt.Errorf("分成比例 (%d) 必须在 0-100 之间", *req.CPSRate)
|
||||
}
|
||||
// 规则检查
|
||||
parentRules, err := channelmod.FindManyChannelDeductionRule(ctx, bson.M{channelmod.FN.ChannelDeductionRule.Channel: channelInfo.ParentAgent})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req.CPAPrice != nil {
|
||||
parentCPA := int64(0)
|
||||
for _, rule := range parentRules {
|
||||
if rule.CPAPrice > 0 {
|
||||
if parentCPA <= 0 {
|
||||
parentCPA = rule.CPAPrice
|
||||
} else {
|
||||
if rule.CPAPrice < parentCPA {
|
||||
parentCPA = rule.CPAPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if *req.CPAPrice > parentCPA || *req.CPAPrice < 0 {
|
||||
return fmt.Errorf("下级CPA价格 (%d) 必须在范围 0-%d 之间,不能大于你自己的CPA价格", *req.CPAPrice/100, parentCPA/100)
|
||||
}
|
||||
}
|
||||
if req.CPSRate != nil {
|
||||
parentCPSRate := int64(0)
|
||||
for _, rule := range parentRules {
|
||||
if rule.CPSRate > 0 {
|
||||
if parentCPSRate <= 0 {
|
||||
parentCPSRate = rule.CPSRate
|
||||
} else {
|
||||
if rule.CPSRate < parentCPSRate {
|
||||
parentCPSRate = rule.CPSRate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if *req.CPSRate > parentCPSRate || *req.CPSRate < 0 {
|
||||
return fmt.Errorf("下级CPS比例 (%d) 必须在范围 0-%d 之间, 不能大于你自己的CPS比例", *req.CPSRate, parentCPSRate)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新规则
|
||||
setdoc := bson.M{}
|
||||
if req.CPAPrice != nil {
|
||||
setdoc[channelmod.FN.ChannelDeductionRule.CPAPrice] = *req.CPAPrice
|
||||
}
|
||||
if req.CPSRate != nil {
|
||||
setdoc[channelmod.FN.ChannelDeductionRule.CPSRate] = *req.CPSRate
|
||||
}
|
||||
if req.DivideRate != nil {
|
||||
setdoc[channelmod.FN.ChannelDeductionRule.DivideRate] = *req.DivideRate
|
||||
}
|
||||
if err := channelmod.UpdateManyChannelDeductionRule(ctx,
|
||||
bson.M{channelmod.FN.ChannelDeductionRule.Channel: req.Channel},
|
||||
bson.M{"$set": setdoc},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新子代理是否显示分成比例
|
||||
if req.ChildShowDivideRate != nil || req.Remark != nil {
|
||||
doc := bson.M{}
|
||||
if req.ChildShowDivideRate != nil {
|
||||
doc[channelmod.FN.ChannelInfo.ChildShowDivideRate] = *req.ChildShowDivideRate
|
||||
}
|
||||
if req.Remark != nil {
|
||||
doc[channelmod.FN.ChannelInfo.Remark] = *req.Remark
|
||||
}
|
||||
if err = channelmod.UpdateOneChannelInfo(ctx, channelFilter, bson.M{"$set": doc}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// AgentChannelInfoUpdate 更新渠道信息
|
||||
func AgentChannelInfoUpdate(ctx context.Context, channel string, req *proto.AgentChannelInfoUpdate) error {
|
||||
checkFilter := bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: req.Channel,
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfo.Channel != req.Channel {
|
||||
return fmt.Errorf("channel not found. %s", req.Channel)
|
||||
}
|
||||
if req.WalletAddr1 != nil || req.WalletAddr2 != nil {
|
||||
if channelInfo.Channel == channel {
|
||||
return fmt.Errorf("您不可以修改自己的钱包地址,请联系您的上级代理协助修改")
|
||||
}
|
||||
}
|
||||
if !(channelInfo.Channel == channel || channelInfo.ParentAgent == channel) {
|
||||
return fmt.Errorf("channel not found! %s", req.Channel)
|
||||
}
|
||||
setdoc := bson.M{
|
||||
channelmod.FN.ChannelInfo.UpdatedAt: time.Now(),
|
||||
}
|
||||
if req.Status != nil {
|
||||
setdoc[channelmod.FN.ChannelInfo.Status] = *req.Status
|
||||
}
|
||||
if req.Mobile != nil {
|
||||
setdoc[channelmod.FN.ChannelInfo.Mobile] = *req.Mobile
|
||||
}
|
||||
if req.WalletAddr1 != nil {
|
||||
setdoc[channelmod.FN.ChannelInfo.Wallet.Addr1] = *req.WalletAddr1
|
||||
}
|
||||
if req.WalletAddr2 != nil {
|
||||
setdoc[channelmod.FN.ChannelInfo.Wallet.Addr2] = *req.WalletAddr2
|
||||
}
|
||||
return channelmod.UpdateOneChannelInfo(ctx,
|
||||
bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel},
|
||||
bson.M{"$set": setdoc},
|
||||
)
|
||||
}
|
||||
|
||||
// AgentChannelPasswordReset 子渠道密码重置
|
||||
func AgentChannelPasswordReset(ctx context.Context, channel string, req *proto.AgentChannelPasswordReset) error {
|
||||
checkFilter := bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: req.Channel,
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfo.Channel != req.Channel {
|
||||
return fmt.Errorf("代理没找到. %s", req.Channel)
|
||||
}
|
||||
if channelInfo.ParentAgent != channel {
|
||||
return errors.New("只能重置直接下级代理的密码! ")
|
||||
}
|
||||
if req.Password == "" {
|
||||
return errors.New("新密码不能为空! ")
|
||||
}
|
||||
setdoc := bson.M{
|
||||
channelmod.FN.ChannelInfo.UpdatedAt: time.Now(),
|
||||
channelmod.FN.ChannelInfo.Password: req.Password,
|
||||
}
|
||||
return channelmod.UpdateOneChannelInfo(ctx,
|
||||
bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel},
|
||||
bson.M{"$set": setdoc},
|
||||
)
|
||||
}
|
||||
|
||||
// AgentChannelPasswordUpdate 更新渠道密码
|
||||
func AgentChannelPasswordUpdate(ctx context.Context, channel string, req *proto.AgentChannelPasswordUpdate) error {
|
||||
checkFilter := bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: channel,
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfo.Channel != channel {
|
||||
return fmt.Errorf("channel not found. %s", channel)
|
||||
}
|
||||
if channelInfo.Password != req.OldPassword {
|
||||
return errors.New("旧密码输入错误")
|
||||
}
|
||||
if req.NewPassword == "" {
|
||||
return errors.New("新密码不能为空! ")
|
||||
}
|
||||
setdoc := bson.M{
|
||||
channelmod.FN.ChannelInfo.UpdatedAt: time.Now(),
|
||||
channelmod.FN.ChannelInfo.Password: req.NewPassword,
|
||||
}
|
||||
return channelmod.UpdateOneChannelInfo(ctx,
|
||||
bson.M{channelmod.FN.ChannelInfo.Channel: channel},
|
||||
bson.M{"$set": setdoc},
|
||||
)
|
||||
}
|
||||
|
||||
func GetChildNewChannelNameAndRootChannelName(ctx context.Context, channelParent *channelmod.ChannelInfo) (string, string, error) {
|
||||
maxid := 0
|
||||
filter := bson.M{}
|
||||
if channelParent.Type == channelmod.ChannelTypeParent {
|
||||
filter[channelmod.FN.ChannelInfo.ParentChannel] = channelParent.Channel
|
||||
} else {
|
||||
filter[channelmod.FN.ChannelInfo.ParentAgent] = channelParent.Channel
|
||||
}
|
||||
channlList, err := channelmod.FindManyChannelInfo(ctx, filter)
|
||||
if err != nil {
|
||||
log.Error("AddAgentChannelInfo CountChannelInfos", log.Any("channel", channelParent.Channel), log.E(err))
|
||||
return "", "", err
|
||||
}
|
||||
for _, v := range channlList {
|
||||
id, err := strconv.Atoi(string(strings.Replace(v.Channel, channelParent.Channel, "", -1)))
|
||||
if err == nil && id > maxid {
|
||||
maxid = id
|
||||
}
|
||||
}
|
||||
maxid = maxid + 1
|
||||
childChannelName := channelParent.Channel + fmt.Sprintf("%.2d", maxid)
|
||||
rootChannelName := ""
|
||||
if channelParent.Type == channelmod.ChannelTypeParent { // 一级代理
|
||||
rootChannelName = channelParent.Channel
|
||||
} else {
|
||||
if channelParent.ParentChannel == "" { // 如果子级代理的父代理为空
|
||||
//rootChannelName = channelParent.Channel
|
||||
return "", "", fmt.Errorf("子级代理【%s】的顶级代理空时不能添加子代理!", channelParent.Channel)
|
||||
} else {
|
||||
rootChannelName = channelParent.ParentChannel
|
||||
}
|
||||
}
|
||||
return childChannelName, rootChannelName, nil
|
||||
}
|
||||
|
||||
// AddAgentChannelInfo 渠道添加一个子渠道
|
||||
func AddAgentChannelInfo(ctx context.Context, channel string, req *proto.AgentChannelInfoAdd) error {
|
||||
channelInfo, _, err := GetChannelInfoWithLockStatus(ctx, channel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if channelInfo.Lock || !channelInfo.Status {
|
||||
|
||||
return fmt.Errorf("您的代理或上级代理已被禁止")
|
||||
}
|
||||
|
||||
childChannelName, rootChannelName, err := GetChildNewChannelNameAndRootChannelName(ctx, channelInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
childChannelType := channelInfo.Type.Child()
|
||||
if childChannelType.IsNone() {
|
||||
return errors.New("您的代理等级无法添加子渠道")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
newChannelInfo := channelmod.ChannelInfo{
|
||||
Channel: childChannelName,
|
||||
UserName: channelInfo.UserName,
|
||||
AppIds: channelInfo.AppIds,
|
||||
ParentChannel: rootChannelName,
|
||||
ParentAgent: channel,
|
||||
Password: req.Password,
|
||||
Status: true,
|
||||
Mobile: req.Mobile,
|
||||
Type: childChannelType,
|
||||
Remark: req.Remark,
|
||||
Wallet: channelmod.Wallet{
|
||||
Addr1: req.WalletAddr1,
|
||||
Addr2: req.WalletAddr2,
|
||||
},
|
||||
Lock: false,
|
||||
ShowMagic: 0,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = channelmod.InsertOneChannelInfo(ctx, &newChannelInfo)
|
||||
if err != nil {
|
||||
log.Error("AddChannelInfo InsertOneChannelInfo", log.Any("channel", newChannelInfo.ParentChannel), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 增加子渠道数量
|
||||
setdoc := bson.M{"$inc": bson.M{channelmod.FN.ChannelInfo.ChildCount: 1}}
|
||||
err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channel}, setdoc)
|
||||
if err != nil {
|
||||
log.Error("AddChannelInfo InsertOneChannelInfo", log.Any("channel", newChannelInfo.ParentChannel), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
for _, appid := range channelInfo.AppIds {
|
||||
checkFilter := bson.M{channelmod.FN.ChannelDeductionRule.Channel: channel, channelmod.FN.ChannelDeductionRule.AppId: appid}
|
||||
ChannelDeductionRule, err := channelmod.FindOneChannelDeductionRule(ctx, checkFilter)
|
||||
if err != nil || ChannelDeductionRule.Channel != channel {
|
||||
log.Error("AddAgentChannelInfo FindOneChannelDeductionRule", log.Any("channel", channel), log.Any("appid", appid), log.E(err))
|
||||
continue
|
||||
}
|
||||
newChannelRule := &channelmod.ChannelDeductionRule{
|
||||
Channel: newChannelInfo.Channel,
|
||||
AppId: ChannelDeductionRule.AppId,
|
||||
AppName: ChannelDeductionRule.AppName,
|
||||
AppType: ChannelDeductionRule.AppType,
|
||||
PromoteMode: ChannelDeductionRule.PromoteMode,
|
||||
Settlement: ChannelDeductionRule.Settlement,
|
||||
First: ChannelDeductionRule.First,
|
||||
Second: ChannelDeductionRule.Second,
|
||||
Third: ChannelDeductionRule.Third,
|
||||
Fourth: ChannelDeductionRule.Fourth,
|
||||
Fifth: ChannelDeductionRule.Fifth,
|
||||
CPAPrice: ChannelDeductionRule.CPAPrice,
|
||||
CPSRate: ChannelDeductionRule.CPSRate,
|
||||
CPSBegin: ChannelDeductionRule.CPSBegin,
|
||||
CPSDeductionRate: ChannelDeductionRule.CPSDeductionRate,
|
||||
BeginTime: ChannelDeductionRule.BeginTime,
|
||||
SettlementTime: now,
|
||||
TodayTime: utils.BeginOfToday(),
|
||||
TodayDeduction: 0,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = channelmod.InsertOneChannelDeductionRule(ctx, newChannelRule)
|
||||
if err != nil {
|
||||
log.Error("AddAgentChannelInfo InsertOneChannelDeductionRule", log.Any("channel", channel), log.Any("appid", appid), log.E(err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAgentSummaryStatChild 查询渠道扣量规则信息
|
||||
func GetAgentSummaryStatChild(ctx context.Context, channel string, req *proto.AgentSummaryStatChildSearch) (*proto.AgentSummaryStatChildResp, error) {
|
||||
resp := &proto.AgentSummaryStatChildResp{
|
||||
List: make(proto.AgentSummaryStatChildList, 0),
|
||||
}
|
||||
summary, err := GetAgentSummary(ctx, channel, nil, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
total, list := summary.GetAgentSummaryStats(OutputStatListTypeChild, req.PageNum, req.PageSize)
|
||||
resp.Total = total
|
||||
for _, item := range list {
|
||||
resp.List = append(resp.List, item.ToAgentSummaryStatChild())
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAgentSummaryStatHome 查询渠道扣量规则信息
|
||||
func GetAgentSummaryStatHome(ctx context.Context, channel string, req *proto.AgentSummaryStatHomeSearch) (*proto.AgentSummaryStatHomeResp, error) {
|
||||
resp := &proto.AgentSummaryStatHomeResp{
|
||||
List: make(proto.AgentSummaryStatHomeList, 0),
|
||||
}
|
||||
summary, err := GetAgentSummary(ctx, channel, nil, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
total, list := summary.GetAgentSummaryStats(OutputStatListTypeSelf, req.PageNum, req.PageSize)
|
||||
resp.Total = total
|
||||
resp.List = list
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAgentChannelStat 查询渠道扣量规则信息
|
||||
func GetAgentChannelStat(ctx context.Context, channel string, req *proto.AgentChannelStatSearch) (*proto.AgentChannelStatResp, error) {
|
||||
resp := &proto.AgentChannelStatResp{
|
||||
List: make([]*proto.AgentDeductedStat, 0),
|
||||
}
|
||||
summary, err := GetAgentSummary(ctx, channel, req.Channel, req.AppId, req.Start, req.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
total, list := summary.GetAgentStats(OutputStatListTypeAll, req.PageNum, req.PageSize)
|
||||
resp.Total = total
|
||||
resp.List = list
|
||||
resp.TotalInfo = *summary.StatTotal
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"stat/api/proto"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/channelmod"
|
||||
)
|
||||
|
||||
const (
|
||||
CPA_SHOW_MONEY = false
|
||||
CPS_SHOW_COUNT = true
|
||||
)
|
||||
|
||||
type ChannelMap map[string]*channelmod.ChannelInfo
|
||||
type AppMap map[int32]*appInfoMod.AppInfo
|
||||
type SourceAgentStatMap map[channelmod.ChannelType]map[string]ChannelAgentStatList
|
||||
type OutputStatListType int32
|
||||
|
||||
type AgentDeductedStatList []*proto.AgentDeductedStat
|
||||
|
||||
func (a AgentDeductedStatList) Len() int { return len(a) }
|
||||
func (a AgentDeductedStatList) Less(i, j int) bool { return a[i].SumDate.After(a[j].SumDate) }
|
||||
func (a AgentDeductedStatList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
const (
|
||||
OutputStatListTypeAll OutputStatListType = 0 // 输出当前渠道和直接下级渠道的stat列表
|
||||
OutputStatListTypeSelf OutputStatListType = 1 // 输出当前渠道自身的stat列表
|
||||
OutputStatListTypeChild OutputStatListType = 2 // 输出直接下级渠道的stat列表
|
||||
)
|
||||
|
||||
type AgentChannelSummaryMode int32
|
||||
|
||||
const (
|
||||
AgentChannelSummaryModeFollowRule AgentChannelSummaryMode = 0 // 按渠道规则,忽略记录中的规则和价格
|
||||
AgentChannelSummaryModeFollowRecord AgentChannelSummaryMode = 1 // 按记录中的规则和价格,覆盖渠道规则
|
||||
)
|
||||
|
||||
var (
|
||||
GlobalAgentChannelSummaryMode = AgentChannelSummaryModeFollowRule
|
||||
)
|
||||
|
||||
type AgentChannelStat struct {
|
||||
channelmod.ChannelStat
|
||||
ChildValidDirectCount int64
|
||||
ChildValidPayMoney int64
|
||||
ChildChannelStats []*AgentChannelStat
|
||||
DeductedStat *proto.AgentDeductedStat
|
||||
DivideRate int64
|
||||
}
|
||||
type ChannelAgentStatList []*AgentChannelStat
|
||||
@@ -0,0 +1,199 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/channelmod"
|
||||
)
|
||||
|
||||
func EnsureRangeInt64(value, min, max int64) int64 {
|
||||
if value < min {
|
||||
value = min
|
||||
}
|
||||
if value > max {
|
||||
value = max
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func InRangeInt64(value, min, max int64) bool {
|
||||
if value < min {
|
||||
return false
|
||||
}
|
||||
if value > max {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func MinInt64(x, y int64) int64 {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func MaxInt64(x, y int64) int64 {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func GetPageRange(totalLen, pageNum, pageSize int) (start, end int) {
|
||||
if pageNum < 1 {
|
||||
pageNum = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 10
|
||||
}
|
||||
start = (pageNum - 1) * pageSize
|
||||
if start > totalLen {
|
||||
start = totalLen
|
||||
}
|
||||
end = start + pageSize
|
||||
if end > totalLen {
|
||||
end = totalLen
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c ChannelMap) GetChannelNames() []string {
|
||||
lst := make([]string, 0)
|
||||
for _, v := range c {
|
||||
lst = append(lst, v.Channel)
|
||||
}
|
||||
return lst
|
||||
}
|
||||
|
||||
func (c ChannelMap) GetChildChannels(channel string) []*channelmod.ChannelInfo {
|
||||
lst := make([]*channelmod.ChannelInfo, 0)
|
||||
for _, v := range c {
|
||||
if v.ParentAgent == channel {
|
||||
lst = append(lst, v)
|
||||
}
|
||||
}
|
||||
return lst
|
||||
}
|
||||
|
||||
func (c ChannelMap) GetParentChannels(channel string) []*channelmod.ChannelInfo {
|
||||
lst := make([]*channelmod.ChannelInfo, 0)
|
||||
self := c[channel]
|
||||
if self == nil {
|
||||
return lst
|
||||
}
|
||||
parent := c[self.ParentAgent]
|
||||
for parent != nil {
|
||||
lst = append(lst, parent)
|
||||
parent = c[parent.ParentAgent]
|
||||
}
|
||||
return lst
|
||||
}
|
||||
|
||||
func (c ChannelMap) GetLockStatusAndShowDivide(channelInfo *channelmod.ChannelInfo) (bool, bool, bool) {
|
||||
parents := c.GetParentChannels(channelInfo.Channel)
|
||||
parentShowDivide := true
|
||||
status := channelInfo.Status
|
||||
lock := channelInfo.Lock
|
||||
for _, parent := range parents {
|
||||
parentShowDivide = parentShowDivide && parent.ChildShowDivideRate
|
||||
status = status && parent.Status
|
||||
if parent.Lock {
|
||||
lock = true
|
||||
}
|
||||
}
|
||||
return lock, status, parentShowDivide && channelInfo.ChildShowDivideRate
|
||||
}
|
||||
|
||||
func (c ChannelMap) GetAncestorAndOffspringChannels(channel string) []*channelmod.ChannelInfo {
|
||||
out := make([]*channelmod.ChannelInfo, 0)
|
||||
cInfo := c[channel]
|
||||
if cInfo == nil {
|
||||
return out
|
||||
}
|
||||
children := c.GetChildChannels(cInfo.Channel)
|
||||
for len(children) > 0 {
|
||||
tmp := make([]*channelmod.ChannelInfo, 0)
|
||||
for _, child := range children {
|
||||
out = append(out, child)
|
||||
tmp = append(tmp, c.GetChildChannels(child.Channel)...)
|
||||
}
|
||||
children = tmp
|
||||
}
|
||||
for cInfo != nil {
|
||||
out = append(out, cInfo)
|
||||
if cInfo.ParentAgent != "" {
|
||||
cInfo = c[cInfo.ParentAgent]
|
||||
} else {
|
||||
cInfo = nil
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func GetAppMap(ctx context.Context) (AppMap, error) {
|
||||
appList, err := appInfoMod.GetAllAppInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appMap := make(AppMap)
|
||||
for _, app := range appList {
|
||||
tmp := app
|
||||
appMap[app.AppId] = &tmp
|
||||
}
|
||||
return appMap, nil
|
||||
}
|
||||
|
||||
func (m AppMap) GetAppName(id int32) string {
|
||||
if app, ok := m[id]; ok {
|
||||
return app.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func AccumulateAgentChannelStat(base, stat *AgentChannelStat, ruleData bool) {
|
||||
channelmod.ChannelStatSummation(&base.ChannelStat, &stat.ChannelStat)
|
||||
base.Settlement = stat.Settlement
|
||||
base.ChildValidPayMoney += stat.ChildValidPayMoney
|
||||
base.ChildValidDirectCount += stat.ChildValidDirectCount
|
||||
AccumulateDeductedStat(base.DeductedStat, stat.DeductedStat, ruleData)
|
||||
for _, child := range stat.ChildChannelStats {
|
||||
base.DeductedStat.ChildRebate += child.DeductedStat.Income
|
||||
}
|
||||
}
|
||||
|
||||
func AccumulateDeductedStat(base, stat *proto.AgentDeductedStat, ruleData bool) {
|
||||
base.VisitCount += stat.VisitCount
|
||||
if !ruleData || (ruleData && stat.Settlement == channelmod.SettlementCPA) || (stat.Settlement == channelmod.SettlementCPS && CPS_SHOW_COUNT) {
|
||||
base.DirectCount += stat.DirectCount
|
||||
base.DirectCountChild += stat.DirectCountChild
|
||||
base.DirectCountSelf += stat.DirectCountSelf
|
||||
}
|
||||
if !ruleData || (ruleData && stat.Settlement == channelmod.SettlementCPS) || (stat.Settlement == channelmod.SettlementCPA && CPA_SHOW_MONEY) {
|
||||
base.Recharge += stat.Recharge
|
||||
base.RechargeChild += stat.RechargeChild
|
||||
base.RechargeSelf += stat.RechargeSelf
|
||||
}
|
||||
base.Income += stat.Income
|
||||
base.IncomeChild += stat.IncomeChild
|
||||
base.IncomeSelf += stat.IncomeSelf
|
||||
}
|
||||
|
||||
func AccumulateStatData(base *proto.StatDataDetail, stat *proto.AgentDeductedStat, ruleData bool) {
|
||||
base.ChildRebate += stat.ChildRebate
|
||||
base.VisitCount += stat.VisitCount
|
||||
if !ruleData || (ruleData && stat.Settlement == channelmod.SettlementCPA) || (stat.Settlement == channelmod.SettlementCPS && CPS_SHOW_COUNT) {
|
||||
base.DirectCount += stat.DirectCount
|
||||
base.DirectCountSelf += stat.DirectCountSelf
|
||||
base.DirectCountChild += stat.DirectCountChild
|
||||
}
|
||||
if !ruleData || (ruleData && stat.Settlement == channelmod.SettlementCPS) || (stat.Settlement == channelmod.SettlementCPA && CPA_SHOW_MONEY) {
|
||||
base.Recharge += stat.Recharge
|
||||
base.RechargeSelf += stat.RechargeSelf
|
||||
base.RechargeChild += stat.RechargeChild
|
||||
}
|
||||
base.Income += stat.Income
|
||||
base.IncomeSelf += stat.IncomeSelf
|
||||
base.IncomeChild += stat.IncomeChild
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"stat/api/proto"
|
||||
"stat/models/channelmod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type AgentNode struct {
|
||||
Ctx context.Context
|
||||
ChannelInfo *channelmod.ChannelInfo
|
||||
Stats ChannelAgentStatList
|
||||
Parent *AgentNode
|
||||
Children []*AgentNode
|
||||
Accumulate bool //是否已累计子层
|
||||
AddedChild bool //是否已添加子层
|
||||
Deducted bool //是否已计算扣量
|
||||
}
|
||||
|
||||
func NewAgentNode(ctx context.Context, channel *channelmod.ChannelInfo, parent *AgentNode, stats ...*AgentChannelStat) *AgentNode {
|
||||
ret := &AgentNode{
|
||||
Ctx: ctx,
|
||||
ChannelInfo: channel,
|
||||
Stats: make([]*AgentChannelStat, 0),
|
||||
Parent: parent,
|
||||
Children: make([]*AgentNode, 0),
|
||||
Accumulate: false,
|
||||
}
|
||||
ret.Stats = append(ret.Stats, stats...)
|
||||
return ret
|
||||
}
|
||||
|
||||
func GetRootAgentNode(ctx context.Context, selfChannel *channelmod.ChannelInfo, reqChannel *string, statDict SourceAgentStatMap) (*AgentNode, error) {
|
||||
var rootAgent *AgentNode
|
||||
if reqChannel != nil && *reqChannel != selfChannel.Channel {
|
||||
cInfo, err := channelmod.FindOneChannelInfoEx(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: *reqChannel})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cInfo.ParentAgent != selfChannel.Channel {
|
||||
return nil, fmt.Errorf("查找渠道失败,只能查看自己或自己的直接下级!")
|
||||
}
|
||||
stats := statDict[cInfo.Type][cInfo.Channel]
|
||||
rootAgent = NewAgentNode(ctx, cInfo, nil, stats...)
|
||||
} else {
|
||||
stats := statDict[selfChannel.Type][selfChannel.Channel]
|
||||
rootAgent = NewAgentNode(ctx, selfChannel, nil, stats...)
|
||||
}
|
||||
return rootAgent, nil
|
||||
}
|
||||
|
||||
func nodeFind(node *AgentNode, find func(node *AgentNode) bool) *AgentNode {
|
||||
var stack = []*AgentNode{node}
|
||||
for len(stack) > 0 {
|
||||
node = stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
if node != nil {
|
||||
found := find(node)
|
||||
if found {
|
||||
return node
|
||||
}
|
||||
stack = append(stack, node.Children...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func traverse(node *AgentNode, accumulate func(node *AgentNode)) {
|
||||
// first child
|
||||
for _, child := range node.Children {
|
||||
traverse(child, accumulate)
|
||||
}
|
||||
// last self
|
||||
accumulate(node)
|
||||
}
|
||||
|
||||
func (s *AgentNode) AddChildNodes(srcDict SourceAgentStatMap, channelMap ChannelMap) error {
|
||||
findNotAddChildNodeFn := func(node *AgentNode) bool {
|
||||
if !node.AddedChild {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
for found := nodeFind(s, findNotAddChildNodeFn); found != nil; found = nodeFind(s, findNotAddChildNodeFn) {
|
||||
childChannels := channelMap.GetChildChannels(found.ChannelInfo.Channel)
|
||||
for _, channel := range childChannels {
|
||||
stats := srcDict[channel.Type][channel.Channel]
|
||||
agent := NewAgentNode(s.Ctx, channel, found, stats...)
|
||||
found.Children = append(found.Children, agent)
|
||||
}
|
||||
found.AddedChild = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AgentNode) AccumulateChildNodes(ruleMap RuleMap) error {
|
||||
accumulate := func(found *AgentNode) {
|
||||
if found.Accumulate {
|
||||
panic(errors.New("AccumulateChildNodes found node can not be accumulate"))
|
||||
}
|
||||
for _, childAgent := range found.Children {
|
||||
if !childAgent.Accumulate {
|
||||
panic(errors.New("AccumulateChildNodes child node can not be not accumulated"))
|
||||
}
|
||||
for _, childStat := range childAgent.Stats {
|
||||
hit := false
|
||||
for _, parentStat := range found.Stats {
|
||||
if parentStat.SumDate.Equal(childStat.SumDate) {
|
||||
hit = true
|
||||
AccumulateChildAgentChannelStatToParent(parentStat, childStat)
|
||||
}
|
||||
}
|
||||
if !hit {
|
||||
lostStat := &AgentChannelStat{
|
||||
ChannelStat: channelmod.ChannelStat{
|
||||
Channel: childAgent.ChannelInfo.ParentAgent,
|
||||
SumDate: childStat.SumDate,
|
||||
AppId: childStat.AppId,
|
||||
Type: childStat.Type - 1,
|
||||
Settlement: childStat.Settlement,
|
||||
UserName: childStat.UserName,
|
||||
ParentChannel: childStat.ParentChannel,
|
||||
},
|
||||
}
|
||||
if rule := ruleMap.GetRule(childAgent.ChannelInfo.ParentAgent, childStat.AppId); rule != nil {
|
||||
lostStat.CPSRate = rule.CPSRate
|
||||
lostStat.CPAPrice = rule.CPAPrice
|
||||
lostStat.Settlement = rule.Settlement
|
||||
lostStat.DivideRate = rule.DivideRate
|
||||
lostStat.AppType = rule.AppType
|
||||
lostStat.PromoteMode = rule.PromoteMode
|
||||
}
|
||||
AccumulateChildAgentChannelStatToParent(lostStat, childStat)
|
||||
found.Stats = append(found.Stats, lostStat)
|
||||
log.Println(lostStat)
|
||||
}
|
||||
}
|
||||
}
|
||||
found.Accumulate = true
|
||||
}
|
||||
traverse(s, accumulate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AgentNode) CalculateDeduction(channelMap ChannelMap, ruleMap RuleMap) error {
|
||||
deduction := func(found *AgentNode) {
|
||||
if found.Deducted {
|
||||
panic(errors.New("CalculateDeduction found node, can not be deducted"))
|
||||
}
|
||||
for _, stat := range found.Stats {
|
||||
pRule := GetMergedRule(channelMap, ruleMap, found.ChannelInfo.ParentAgent, stat.AppId)
|
||||
sRule := ruleMap.GetRule(stat.Channel, stat.AppId)
|
||||
stat.DeductedStat = DeductionChannelStat(stat, pRule, sRule)
|
||||
}
|
||||
found.Deducted = true
|
||||
}
|
||||
traverse(s, deduction)
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeductionChannelStat(stat *AgentChannelStat, parentRule, selfRule *AgentChannelRule) *proto.AgentDeductedStat {
|
||||
proxyDeductionTime, _ := time.ParseInLocation("2006-01-02", "2021-10-28", time.Local)
|
||||
base := &proto.AgentDeductedStat{
|
||||
Channel: stat.Channel,
|
||||
AppId: stat.AppId,
|
||||
SumDate: stat.SumDate,
|
||||
DirectCount: stat.ValidDirectCount,
|
||||
DirectCountChild: stat.ChildValidDirectCount,
|
||||
DirectCountSelf: stat.ValidDirectCount - stat.ChildValidDirectCount,
|
||||
Recharge: stat.ValidPayMoney,
|
||||
RechargeChild: stat.ChildValidPayMoney,
|
||||
RechargeSelf: stat.ValidPayMoney - stat.ChildValidPayMoney,
|
||||
Settlement: stat.Settlement,
|
||||
CPSRate: stat.CPSRate,
|
||||
CPAPrice: stat.CPAPrice,
|
||||
DivideRate: stat.DivideRate,
|
||||
UpdatedAt: stat.UpdatedAt,
|
||||
}
|
||||
if selfRule == nil {
|
||||
if base.DivideRate == 0 {
|
||||
base.DivideRate = 100
|
||||
}
|
||||
return base
|
||||
}
|
||||
rule := MergedRule(parentRule, selfRule)
|
||||
if GlobalAgentChannelSummaryMode == AgentChannelSummaryModeFollowRecord {
|
||||
rule.CPSRate = stat.CPSRate
|
||||
rule.CPAPrice = stat.CPAPrice
|
||||
if base.Settlement == "" {
|
||||
base.Settlement = rule.Settlement
|
||||
}
|
||||
} else {
|
||||
base.CPSRate = rule.CPSRate
|
||||
base.CPAPrice = rule.CPAPrice
|
||||
base.Settlement = rule.Settlement
|
||||
}
|
||||
base.DivideRate = selfRule.DivideRate
|
||||
if base.DivideRate == 0 {
|
||||
base.DivideRate = 100
|
||||
}
|
||||
// 首先进行根代理扣量
|
||||
if rule.ProxyDeduction > 0 && rule.ProxyDeduction < 100 && base.SumDate.After(proxyDeductionTime) {
|
||||
base.DirectCount = base.DirectCount * rule.ProxyDeduction / 100
|
||||
base.DirectCountChild = base.DirectCountChild * rule.ProxyDeduction / 100
|
||||
base.Recharge = base.Recharge*rule.ProxyDeduction/100 - (base.Recharge*rule.ProxyDeduction/100)%1000
|
||||
base.RechargeChild = base.RechargeChild*rule.ProxyDeduction/100 - (base.RechargeChild*rule.ProxyDeduction/100)%1000
|
||||
}
|
||||
// 按上级代理对该代理设置的分成比例进行扣量
|
||||
if rule.DivideRateBP > 0 && rule.DivideRateBP < 10000 {
|
||||
base.DirectCount = base.DirectCount * rule.DivideRateBP / 10000
|
||||
base.DirectCountChild = base.DirectCountChild * rule.DivideRateBP / 10000
|
||||
base.Recharge = base.Recharge * rule.DivideRateBP / 10000
|
||||
base.RechargeChild = base.RechargeChild * rule.DivideRateBP / 10000
|
||||
}
|
||||
// 再计算收益
|
||||
if rule.Settlement == channelmod.SettlementCPA {
|
||||
base.Income = base.DirectCount * rule.CPAPrice
|
||||
base.IncomeChild = base.DirectCountChild * rule.CPAPrice
|
||||
} else if rule.Settlement == channelmod.SettlementCPS {
|
||||
base.Income = (base.Recharge * rule.CPSRate) / 100
|
||||
base.IncomeChild = (base.RechargeChild * rule.CPSRate) / 100
|
||||
} else if rule.Settlement == channelmod.SettlementCPT {
|
||||
base.Income = base.DirectCount * rule.CPAPrice
|
||||
base.IncomeChild = base.DirectCountChild * rule.CPAPrice
|
||||
}
|
||||
// 统计子代理数据
|
||||
base.IncomeSelf = base.Income - base.IncomeChild
|
||||
base.DirectCountSelf = base.DirectCount - base.DirectCountChild
|
||||
base.RechargeSelf = base.Recharge - base.RechargeChild
|
||||
for _, child := range stat.ChildChannelStats {
|
||||
base.ChildRebate += child.DeductedStat.Income
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func AccumulateChildAgentChannelStatToParent(parent, child *AgentChannelStat) {
|
||||
channelmod.ChannelStatSummation(&parent.ChannelStat, &child.ChannelStat)
|
||||
// 子渠道累计
|
||||
parent.ChildValidPayMoney += child.VipPayMoney
|
||||
parent.ChildValidDirectCount += child.NewDirectCount
|
||||
parent.ChildChannelStats = append(parent.ChildChannelStats, child)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/models/channelmod"
|
||||
)
|
||||
|
||||
func RepairData() {
|
||||
wg := utils.NewWaitGroup()
|
||||
wg.Add(func() {
|
||||
ctx := context.Background()
|
||||
if err := FixChannelSecondParentAgent(ctx); err != nil {
|
||||
log.Error("fix data err", log.E(err))
|
||||
}
|
||||
})
|
||||
go wg.RunWaits()
|
||||
}
|
||||
|
||||
func FixChannelSecondParentAgent(ctx context.Context) error {
|
||||
filter := bson.M{
|
||||
channelmod.FN.ChannelInfo.Type: channelmod.ChannelTypeSecond,
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelInfo.ParentAgent: bson.M{"$eq": nil}},
|
||||
bson.M{channelmod.FN.ChannelInfo.ParentAgent: bson.M{"$exists": false}},
|
||||
bson.M{channelmod.FN.ChannelInfo.ParentAgent: bson.M{"$eq": ""}},
|
||||
},
|
||||
channelmod.FN.ChannelInfo.ParentChannel: bson.M{"$gt": ""},
|
||||
}
|
||||
count, err := channelmod.CountChannelInfos(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
// 修复
|
||||
channels, err := channelmod.FindManyChannelInfo(ctx, filter)
|
||||
for _, channel := range channels {
|
||||
filter := bson.M{channelmod.FN.ChannelInfo.Channel: channel.Channel}
|
||||
update := bson.M{"$set": bson.M{
|
||||
channelmod.FN.ChannelInfo.ParentAgent: channel.ParentChannel,
|
||||
}}
|
||||
if err = channelmod.UpdateOneChannelInfo(ctx, filter, update); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/models/channelmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type RuleMap map[string]*AgentChannelRule
|
||||
|
||||
type AgentChannelRule struct {
|
||||
channelmod.ChannelDeductionRule
|
||||
DivideRateBP int64 // 分成比列(万分比)
|
||||
}
|
||||
|
||||
func (c RuleMap) GetRule(channel string, appId int32) *AgentChannelRule {
|
||||
key := fmt.Sprintf("%s %d", channel, appId)
|
||||
return c[key]
|
||||
}
|
||||
|
||||
func (c RuleMap) SetRule(rule *AgentChannelRule) {
|
||||
key := fmt.Sprintf("%s %d", rule.Channel, rule.AppId)
|
||||
c[key] = rule
|
||||
}
|
||||
|
||||
func GetMergedRule(channelMap ChannelMap, ruleMap RuleMap, channel string, appId int32) *AgentChannelRule {
|
||||
rules := make([]*AgentChannelRule, 0)
|
||||
cInfo := channelMap[channel]
|
||||
for cInfo != nil {
|
||||
rule := ruleMap.GetRule(cInfo.Channel, appId)
|
||||
if rule != nil {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
if cInfo.ParentAgent != "" {
|
||||
cInfo = channelMap[cInfo.ParentAgent]
|
||||
} else {
|
||||
cInfo = nil
|
||||
}
|
||||
}
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
for i, j := 0, len(rules)-1; i < j; i, j = i+1, j-1 {
|
||||
rules[i], rules[j] = rules[j], rules[i]
|
||||
}
|
||||
base := *rules[0] // 沿用父渠道代理扣量规则
|
||||
rules = rules[1:]
|
||||
|
||||
for _, rule := range rules {
|
||||
base.AppType = rule.AppType
|
||||
base.PromoteMode = rule.PromoteMode
|
||||
base.CPSRate = rule.CPSRate
|
||||
base.CPAPrice = MinInt64(base.CPAPrice, rule.CPAPrice) // 取最小的CPA价格
|
||||
base.DivideRate = base.DivideRate * rule.DivideRate / 100
|
||||
base.DivideRateBP = (base.DivideRateBP * rule.DivideRateBP) / 10000
|
||||
}
|
||||
return &base
|
||||
}
|
||||
|
||||
func MergedRule(parent, self *AgentChannelRule) *AgentChannelRule {
|
||||
if parent != nil && self != nil {
|
||||
rule := *self
|
||||
//rule.CPSRate //CPSRate not change
|
||||
rule.AppType = parent.AppType
|
||||
rule.PromoteMode = parent.PromoteMode
|
||||
rule.CPAPrice = MinInt64(rule.CPAPrice, parent.CPAPrice) // 取最小的CPA价格
|
||||
rule.DivideRate = rule.DivideRate * parent.DivideRate / 100
|
||||
rule.DivideRateBP = (rule.DivideRateBP * parent.DivideRateBP) / 10000
|
||||
rule.ProxyDeduction = parent.ProxyDeduction // 传递父渠道代理扣量到子渠道
|
||||
return &rule
|
||||
} else if parent != nil && self == nil {
|
||||
rule := *parent
|
||||
return &rule
|
||||
} else if parent == nil && self != nil {
|
||||
rule := *self
|
||||
return &rule
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MakeChannelAndRuleMap(ctx context.Context, channelInfo *channelmod.ChannelInfo) (ChannelMap, RuleMap, error) {
|
||||
var err error
|
||||
ruleMap := make(RuleMap)
|
||||
channelMap := make(ChannelMap)
|
||||
var channelTop string
|
||||
if channelInfo.Type.IsTop() {
|
||||
channelTop = channelInfo.Channel
|
||||
} else {
|
||||
channelTop = channelInfo.ParentChannel
|
||||
}
|
||||
filter := bson.M{
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelInfo.Channel: channelTop},
|
||||
bson.M{channelmod.FN.ChannelInfo.ParentChannel: channelTop},
|
||||
},
|
||||
}
|
||||
channelList, err := channelmod.FindManyChannelInfo(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, c := range channelList {
|
||||
channelMap[c.Channel] = c
|
||||
}
|
||||
channels := make([]string, 0)
|
||||
channelInfos := channelMap.GetAncestorAndOffspringChannels(channelInfo.Channel)
|
||||
for _, info := range channelInfos {
|
||||
channels = append(channels, info.Channel)
|
||||
}
|
||||
rules, err := channelmod.FindManyChannelDeductionRule(ctx, bson.M{channelmod.FN.ChannelDeductionRule.Channel: bson.M{"$in": channels}})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, item := range rules {
|
||||
item.CPAPrice = EnsureRangeInt64(item.CPAPrice, 0, 10000)
|
||||
item.CPSRate = EnsureRangeInt64(item.CPSRate, 0, 100)
|
||||
item.DivideRate = EnsureRangeInt64(item.DivideRate, 0, 100)
|
||||
if item.DivideRate == 0 {
|
||||
item.DivideRate = 100
|
||||
}
|
||||
rule := &AgentChannelRule{
|
||||
ChannelDeductionRule: *item,
|
||||
DivideRateBP: EnsureRangeInt64(item.DivideRate, 0, 100) * 100,
|
||||
}
|
||||
ruleMap.SetRule(rule)
|
||||
}
|
||||
return channelMap, ruleMap, nil
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
package agentSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"stat/api/proto"
|
||||
"stat/models/channelmod"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/now"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type AgentSummary struct {
|
||||
StartTime *time.Time
|
||||
EndTime *time.Time
|
||||
AppId *int32
|
||||
FilterChannel *string
|
||||
ChannelInfo *channelmod.ChannelInfo
|
||||
ChannelMap ChannelMap
|
||||
RuleMap RuleMap
|
||||
AppMap AppMap
|
||||
StatMap SourceAgentStatMap
|
||||
SelfAgent *AgentNode
|
||||
SelfStats []*proto.AgentDeductedStat
|
||||
ChildStats []*proto.AgentDeductedStat
|
||||
StatTotal *proto.AgentDeductedStat
|
||||
}
|
||||
|
||||
func GetAgentSummary(ctx context.Context, selfChannel string, childChannel *string, appId *int32, start, end *time.Time) (*AgentSummary, error) {
|
||||
summary := AgentSummary{
|
||||
AppId: appId,
|
||||
StartTime: start,
|
||||
EndTime: end,
|
||||
FilterChannel: childChannel,
|
||||
SelfStats: make([]*proto.AgentDeductedStat, 0),
|
||||
ChildStats: make([]*proto.AgentDeductedStat, 0),
|
||||
AppMap: make(AppMap),
|
||||
}
|
||||
// 获取app信息字典
|
||||
appMap, err := GetAppMap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
summary.AppMap = appMap
|
||||
// 获取渠道信息和统计信息
|
||||
channelInfo, statDict, err := GetAllAgentStatDict(ctx, selfChannel, appId, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
summary.ChannelInfo = channelInfo
|
||||
summary.StatMap = statDict
|
||||
// 获取顶层节点
|
||||
rootAgent, err := GetRootAgentNode(ctx, channelInfo, childChannel, statDict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
summary.SelfAgent = rootAgent
|
||||
// 获取渠道列表扣量规则字典
|
||||
channelMap, ruleMap, err := MakeChannelAndRuleMap(ctx, channelInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
summary.ChannelMap = channelMap
|
||||
summary.RuleMap = ruleMap
|
||||
// 添加子渠道节点
|
||||
if err := rootAgent.AddChildNodes(statDict, channelMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 累计子渠道到上级渠道
|
||||
if err := rootAgent.AccumulateChildNodes(ruleMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 计算扣量
|
||||
if err := rootAgent.CalculateDeduction(channelMap, ruleMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, stat := range rootAgent.Stats {
|
||||
summary.SelfStats = append(summary.SelfStats, stat.DeductedStat)
|
||||
}
|
||||
for _, agent := range rootAgent.Children {
|
||||
for _, stat := range agent.Stats {
|
||||
summary.ChildStats = append(summary.ChildStats, stat.DeductedStat)
|
||||
}
|
||||
}
|
||||
// 统计总量
|
||||
base := &AgentChannelStat{
|
||||
DeductedStat: &proto.AgentDeductedStat{},
|
||||
}
|
||||
for _, item := range rootAgent.Stats {
|
||||
AccumulateAgentChannelStat(base, item, true)
|
||||
}
|
||||
summary.StatTotal = base.DeductedStat
|
||||
return &summary, nil
|
||||
}
|
||||
|
||||
func (a *AgentSummary) GetAgentStats(outType OutputStatListType, pageNum int64, pageSize int64) (int64, []*proto.AgentDeductedStat) {
|
||||
totalAgentStatList := make(AgentDeductedStatList, 0)
|
||||
switch outType {
|
||||
case OutputStatListTypeSelf:
|
||||
totalAgentStatList = append(totalAgentStatList, a.SelfStats...)
|
||||
case OutputStatListTypeChild:
|
||||
totalAgentStatList = append(totalAgentStatList, a.ChildStats...)
|
||||
default:
|
||||
totalAgentStatList = append(totalAgentStatList, a.SelfStats...)
|
||||
totalAgentStatList = append(totalAgentStatList, a.ChildStats...)
|
||||
}
|
||||
childChannel := a.FilterChannel
|
||||
if childChannel != nil { // 如果存在渠道过滤
|
||||
outAgentStatList := make([]*proto.AgentDeductedStat, 0)
|
||||
for _, item := range totalAgentStatList {
|
||||
if item.Channel == *childChannel {
|
||||
outAgentStatList = append(outAgentStatList, item)
|
||||
}
|
||||
}
|
||||
totalAgentStatList = outAgentStatList
|
||||
}
|
||||
// 处理排序
|
||||
sort.Sort(totalAgentStatList)
|
||||
// 处理分页输出
|
||||
list := GetAgentChannelStatListPageData(totalAgentStatList, pageNum, pageSize)
|
||||
// 添加app名字
|
||||
for _, item := range list {
|
||||
item.AppName = a.AppMap.GetAppName(item.AppId)
|
||||
if item.Channel == a.ChannelInfo.Channel {
|
||||
item.DivideRate = 100
|
||||
}
|
||||
if item.Settlement == channelmod.SettlementCPA && !CPA_SHOW_MONEY {
|
||||
item.Recharge = 0
|
||||
item.RechargeSelf = 0
|
||||
item.RechargeChild = 0
|
||||
}
|
||||
if item.Settlement == channelmod.SettlementCPS && !CPS_SHOW_COUNT {
|
||||
item.DirectCount = 0
|
||||
item.DirectCountSelf = 0
|
||||
item.DirectCountChild = 0
|
||||
}
|
||||
}
|
||||
return int64(len(totalAgentStatList)), list
|
||||
}
|
||||
|
||||
func (a *AgentSummary) GetAgentSummaryStats(outType OutputStatListType, pageNum int64, pageSize int64) (int64, proto.AgentSummaryStatHomeList) {
|
||||
totalAgentStatList := make(AgentDeductedStatList, 0)
|
||||
channelList := make([]*channelmod.ChannelInfo, 0)
|
||||
switch outType {
|
||||
case OutputStatListTypeSelf:
|
||||
channelList = append(channelList, a.SelfAgent.ChannelInfo)
|
||||
totalAgentStatList = append(totalAgentStatList, a.SelfStats...)
|
||||
case OutputStatListTypeChild:
|
||||
for _, node := range a.SelfAgent.Children {
|
||||
channelList = append(channelList, node.ChannelInfo)
|
||||
}
|
||||
totalAgentStatList = append(totalAgentStatList, a.ChildStats...)
|
||||
default:
|
||||
channelList = append(channelList, a.SelfAgent.ChannelInfo)
|
||||
for _, node := range a.SelfAgent.Children {
|
||||
channelList = append(channelList, node.ChannelInfo)
|
||||
}
|
||||
totalAgentStatList = append(totalAgentStatList, a.SelfStats...)
|
||||
totalAgentStatList = append(totalAgentStatList, a.ChildStats...)
|
||||
}
|
||||
// 按渠道进行汇总
|
||||
summaryList := make(proto.AgentSummaryStatHomeList, 0)
|
||||
for _, channel := range channelList {
|
||||
summary := &proto.AgentSummaryStatHome{
|
||||
Channel: channel.Channel,
|
||||
ParentAgent: channel.ParentAgent,
|
||||
ChildCount: channel.ChildCount,
|
||||
TotalStat: &proto.StatDataDetail{},
|
||||
TodayStat: &proto.StatDataDetail{},
|
||||
WeekStat: &proto.StatDataDetail{},
|
||||
MonthStat: &proto.StatDataDetail{},
|
||||
YearStat: &proto.StatDataDetail{},
|
||||
}
|
||||
summaryList = append(summaryList, summary)
|
||||
}
|
||||
// 统计日周月年数据
|
||||
nowTime := time.Now()
|
||||
for _, summary := range summaryList {
|
||||
for _, stat := range totalAgentStatList {
|
||||
if summary.Channel == stat.Channel {
|
||||
AccumulateSummaryStat(summary, stat, nowTime, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理排序,收益多的靠前
|
||||
sort.Sort(summaryList)
|
||||
// 处理分页输出
|
||||
list := GetAgentSummaryStatListPageData(summaryList, pageNum, pageSize)
|
||||
// 添加app名字,CAP屏蔽充值,CPS屏蔽新增
|
||||
for _, item := range list {
|
||||
item.AppName = a.AppMap.GetAppName(item.AppId)
|
||||
if item.Channel == a.ChannelInfo.Channel {
|
||||
item.DivideRate = 100
|
||||
}
|
||||
}
|
||||
return int64(len(summaryList)), list
|
||||
}
|
||||
|
||||
func AccumulateSummaryStat(base *proto.AgentSummaryStatHome, stat *proto.AgentDeductedStat, today time.Time, ruleData bool) {
|
||||
// 时间范围
|
||||
now.WeekStartDay = time.Monday
|
||||
Now := now.With(today.In(time.Local))
|
||||
todayBegin := Now.BeginningOfDay()
|
||||
todayEnd := Now.EndOfDay()
|
||||
weekBegin := Now.BeginningOfWeek()
|
||||
monthBegin := Now.BeginningOfMonth()
|
||||
yearBegin := Now.BeginningOfYear()
|
||||
// 复制信息
|
||||
base.AppId = stat.AppId
|
||||
base.AppName = stat.AppName
|
||||
base.Settlement = stat.Settlement
|
||||
base.CPAPrice = stat.CPAPrice
|
||||
base.CPSRate = stat.CPSRate
|
||||
base.DivideRate = stat.DivideRate
|
||||
// 全部数据累加
|
||||
AccumulateStatData(base.TotalStat, stat, ruleData)
|
||||
if (stat.SumDate.Equal(todayBegin) || stat.SumDate.After(todayBegin)) && (stat.SumDate.Equal(todayEnd) || stat.SumDate.Before(todayEnd)) {
|
||||
AccumulateStatData(base.TodayStat, stat, ruleData)
|
||||
}
|
||||
if (stat.SumDate.Equal(weekBegin) || stat.SumDate.After(weekBegin)) && (stat.SumDate.Equal(todayEnd) || stat.SumDate.Before(todayEnd)) {
|
||||
AccumulateStatData(base.WeekStat, stat, ruleData)
|
||||
}
|
||||
if (stat.SumDate.Equal(monthBegin) || stat.SumDate.After(monthBegin)) && (stat.SumDate.Equal(todayEnd) || stat.SumDate.Before(todayEnd)) {
|
||||
AccumulateStatData(base.MonthStat, stat, ruleData)
|
||||
}
|
||||
if (stat.SumDate.Equal(yearBegin) || stat.SumDate.After(yearBegin)) && (stat.SumDate.Equal(todayEnd) || stat.SumDate.Before(todayEnd)) {
|
||||
AccumulateStatData(base.YearStat, stat, ruleData)
|
||||
}
|
||||
}
|
||||
|
||||
func GetAgentChannelStatListPageData(list []*proto.AgentDeductedStat, pageNum int64, pageSize int64) []*proto.AgentDeductedStat {
|
||||
start, end := GetPageRange(len(list), int(pageNum), int(pageSize))
|
||||
return list[start:end]
|
||||
}
|
||||
|
||||
func GetAgentSummaryStatListPageData(list proto.AgentSummaryStatHomeList, pageNum int64, pageSize int64) proto.AgentSummaryStatHomeList {
|
||||
start, end := GetPageRange(len(list), int(pageNum), int(pageSize))
|
||||
return list[start:end]
|
||||
}
|
||||
|
||||
func GetAllAgentStatDict(ctx context.Context, channel string, appId *int32, startTime, endTime *time.Time) (*channelmod.ChannelInfo, SourceAgentStatMap, error) {
|
||||
// 获取渠道信息
|
||||
checkFilter := bson.M{channelmod.FN.ChannelInfo.Channel: channel}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
if err != nil || channelInfo.Channel != channel {
|
||||
return nil, nil, err
|
||||
}
|
||||
// 获取一级渠道的所有子孙数据
|
||||
var channelTop string
|
||||
if channelInfo.Type.IsTop() {
|
||||
channelTop = channelInfo.Channel
|
||||
} else {
|
||||
channelTop = channelInfo.ParentChannel
|
||||
}
|
||||
srcfilter := bson.M{
|
||||
channelmod.FN.ChannelStat.Type: bson.M{"$gte": channelInfo.Type},
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelStat.Channel: channelTop},
|
||||
bson.M{channelmod.FN.ChannelStat.ParentChannel: channelTop},
|
||||
}}
|
||||
if appId != nil {
|
||||
srcfilter[channelmod.FN.ChannelStat.AppId] = *appId
|
||||
}
|
||||
if startTime != nil && endTime != nil {
|
||||
srcfilter[channelmod.FN.ChannelStat.SumDate] = bson.M{"$gte": *startTime, "$lte": *endTime}
|
||||
}
|
||||
srcList, err := channelmod.FindManyChannelStat(ctx, srcfilter)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
srcDict := make(SourceAgentStatMap)
|
||||
types := channelmod.GetAllChannelTypes()
|
||||
for _, t := range types {
|
||||
srcDict[t] = make(map[string]ChannelAgentStatList)
|
||||
}
|
||||
for _, stat := range srcList {
|
||||
stat.CorrectValidData()
|
||||
stat.CalcIncomeRate()
|
||||
aStat := &AgentChannelStat{
|
||||
ChannelStat: *stat,
|
||||
ChildValidDirectCount: 0,
|
||||
ChildValidPayMoney: 0,
|
||||
ChildChannelStats: make([]*AgentChannelStat, 0),
|
||||
}
|
||||
srcDict[stat.Type][stat.Channel] = append(srcDict[stat.Type][stat.Channel], aStat)
|
||||
}
|
||||
|
||||
return channelInfo, srcDict, nil
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package announInfoSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/announInfoMod"
|
||||
"stat/models/appInfoMod"
|
||||
)
|
||||
|
||||
// 获取广告列表
|
||||
func ListAnnoun(ctx context.Context, param proto.ListAnnounReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListAnnounRes{
|
||||
Total: 0,
|
||||
List: make([]proto.AnnounInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
// 获取广告位置信息
|
||||
total, aList, err := announInfoMod.FindAnnounceList(ctx, param.AppId, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
// 获取app信息
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range aList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
appList, err := appInfoMod.GetAppInfosByAppIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
appMap := make(map[int32]appInfoMod.AppInfo)
|
||||
for _, v := range appList {
|
||||
appMap[v.AppId] = v
|
||||
}
|
||||
|
||||
for _, v := range aList {
|
||||
appName := ""
|
||||
if appInfo, ok := appMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.AnnounInfo{
|
||||
ID: v.ID.Hex(),
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
Title: v.Title,
|
||||
Content: v.Content,
|
||||
Cover: v.Cover,
|
||||
Href: v.Href,
|
||||
Type: v.Type,
|
||||
Active: v.Active,
|
||||
StartTime: v.StartTime,
|
||||
EndTime: v.EndTime,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
data = resp
|
||||
return
|
||||
}
|
||||
|
||||
// 新增广告
|
||||
func AddAnnounce(ctx context.Context, param proto.AddAnnounReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
doc := announInfoMod.AnnounInfo{
|
||||
AppId: *param.AppId,
|
||||
Title: *param.Title,
|
||||
Content: *param.Content,
|
||||
Cover: *param.Cover,
|
||||
Href: *param.Href,
|
||||
Type: *param.Type,
|
||||
Active: *param.Active,
|
||||
StartTime: *param.StartTime,
|
||||
EndTime: *param.EndTime,
|
||||
}
|
||||
|
||||
if err = announInfoMod.AddAnnounce(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改广告位置
|
||||
func UpdateAnnounce(ctx context.Context, param proto.UpdateAnnounReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
doc := announInfoMod.UpdateDoc{
|
||||
AppId: param.AppId,
|
||||
Title: param.Title,
|
||||
Content: param.Content,
|
||||
Cover: param.Cover,
|
||||
Href: param.Href,
|
||||
Type: param.Type,
|
||||
Active: param.Active,
|
||||
StartTime: param.StartTime,
|
||||
EndTime: param.EndTime,
|
||||
}
|
||||
if err = announInfoMod.UpdateAnnounce(ctx, *param.Id, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除广告
|
||||
func DelAnnounce(ctx context.Context, param proto.DelAnnounReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = announInfoMod.DelAnnounce(ctx, *param.Id); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 外部获取广告
|
||||
func GetAnnounce(ctx context.Context, param proto.AnnouncementReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.AnnouncementRes{AnnounceList: make([]proto.AnnouncementInfo, 0)}
|
||||
|
||||
aList, err := announInfoMod.FindAnnounceByAppId(ctx, *param.AppId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
for _, v := range aList {
|
||||
resp.AnnounceList = append(resp.AnnounceList, proto.AnnouncementInfo{
|
||||
ID: v.ID.Hex(),
|
||||
Title: v.Title,
|
||||
Content: v.Content,
|
||||
Cover: v.Cover,
|
||||
Href: v.Href,
|
||||
Type: v.Type,
|
||||
})
|
||||
}
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package appInfoSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取appInfo列表
|
||||
func GetAppInfoList(ctx context.Context, param proto.ListAppInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListAppInfoRes{
|
||||
Total: 0,
|
||||
List: make([]proto.AppInfoRes, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
total, aList, err := appInfoMod.GetAppInfoList(ctx, param.AppName, param.Used, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range aList {
|
||||
tmp := proto.AppInfoRes{
|
||||
AppId: v.AppId,
|
||||
Name: v.Name,
|
||||
LandPages: v.LandPages,
|
||||
IconImg: v.IconImg,
|
||||
CreatedAt: v.CreatedAt,
|
||||
AdvertiseNum: v.AdvertiseNum,
|
||||
AdvertiseNewNum: v.AdvertiseNewNum,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加appInfo
|
||||
func AddAppInfo(ctx context.Context, param proto.AddAppInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
_, serr := appInfoMod.GetAppInfoById(ctx, *param.AppId)
|
||||
if serr != nil {
|
||||
return stdLog.AppExistErr, nil, serr
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
doc := appInfoMod.AppInfo{
|
||||
AppId: *param.AppId,
|
||||
Name: *param.Name,
|
||||
IconImg: *param.IconImg,
|
||||
// LandPages: *param.LandPages,
|
||||
Used: true,
|
||||
AdvertiseNum: 5,
|
||||
AdvertiseNewNum: 5,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err = appInfoMod.AddAppInfo(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 更新appInfo
|
||||
func UpdateAppInfo(ctx context.Context, param proto.UpdateAppInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
doc := appInfoMod.AppInfoUpdate{
|
||||
Name: param.Name,
|
||||
LandPages: param.LandPages,
|
||||
IconImg: param.IconImg,
|
||||
UpdatedAt: &now,
|
||||
AdvertiseNum: param.AdvertiseNum,
|
||||
AdvertiseNewNum: param.AdvertiseNewNum,
|
||||
}
|
||||
|
||||
if err = appInfoMod.UpdateAppInfo(ctx, *param.AppId, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除appInfo
|
||||
func DelAppInfo(ctx context.Context, param proto.DelAppInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = appInfoMod.DelAppInfo(ctx, *param.AppId); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package appInfoSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/genId"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/landPageConfig"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// GetLandPageConfigList
|
||||
func GetLandPageConfigList(ctx context.Context, param *proto.FindLandPageConfigReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.LandPageConfigListResp{
|
||||
Total: 0,
|
||||
List: make(landPageConfig.LandPageConfigList, 0),
|
||||
}
|
||||
|
||||
filter := bson.M{}
|
||||
if param.Status != nil {
|
||||
filter[landPageConfig.FN.LandPageConfig.Status] = *param.Status
|
||||
}
|
||||
if param.AppId != nil {
|
||||
filter[landPageConfig.FN.LandPageConfig.AppId] = *param.AppId
|
||||
}
|
||||
if param.Type != nil {
|
||||
filter[landPageConfig.FN.LandPageConfig.LandPageType] = *param.Type
|
||||
}
|
||||
|
||||
total, err := landPageConfig.CountLandPageConfigs(ctx, filter)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.D{
|
||||
{landPageConfig.FN.LandPageConfig.Rank, -1},
|
||||
{landPageConfig.FN.LandPageConfig.CreateAt, -1},
|
||||
})
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
list, err := landPageConfig.FindManyLandPageConfig(ctx, filter, opt)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.List = list
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// AddLandPageConfig
|
||||
func AddLandPageConfig(ctx context.Context, param *proto.AddLandPageConfigReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
doc := &landPageConfig.LandPageConfig{
|
||||
Id: genId.GetLandpageConfigId(),
|
||||
AppId: *param.AppId,
|
||||
LandPageType: *param.LandPageType,
|
||||
Title: *param.Title,
|
||||
CreateAt: now,
|
||||
UpdateAt: now,
|
||||
}
|
||||
if param.Desc != nil {
|
||||
doc.Desc = *param.Desc
|
||||
}
|
||||
if param.Actor != nil {
|
||||
doc.Actor = *param.Actor
|
||||
}
|
||||
if param.Bango != nil {
|
||||
doc.Bango = *param.Bango
|
||||
}
|
||||
if param.AddedTime != nil {
|
||||
doc.AddedTime = *param.AddedTime
|
||||
}
|
||||
if param.Picture != nil {
|
||||
doc.Picture = *param.Picture
|
||||
}
|
||||
if param.Media != nil {
|
||||
doc.Media = *param.Media
|
||||
}
|
||||
if param.Rank != nil {
|
||||
doc.Rank = *param.Rank
|
||||
}
|
||||
if param.Status != nil {
|
||||
doc.Status = *param.Status
|
||||
}
|
||||
if err = landPageConfig.InsertOneLandPageConfig(ctx, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateLandPageConfig
|
||||
func UpdateLandPageConfig(ctx context.Context, param *landPageConfig.LandPageConfig) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
param.UpdateAt = now
|
||||
updateDoc := bson.M{"$set": param}
|
||||
if err = landPageConfig.UpdateOneLandPageConfig(ctx, bson.M{
|
||||
landPageConfig.FN.LandPageConfig.Id: param.Id,
|
||||
}, updateDoc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// DelLandPageConfig
|
||||
func DelLandPageConfig(ctx context.Context, param *proto.DelLandPageConfigReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if _, err = landPageConfig.DelOneLandPageConfig(ctx, bson.M{
|
||||
landPageConfig.FN.LandPageConfig.Id: param.ID,
|
||||
}); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// GetLandPageConfigByType
|
||||
func GetLandPageConfigByType(ctx context.Context, param *proto.GetLandPageConfigByTypeReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
opt := &options.FindOptions{}
|
||||
if param.AppId == 0 {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
opt.SetSort(bson.D{
|
||||
{landPageConfig.FN.LandPageConfig.Rank, -1},
|
||||
{landPageConfig.FN.LandPageConfig.CreateAt, -1},
|
||||
})
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
opt.SetLimit(param.PageSize)
|
||||
// param.AppId = 888
|
||||
// param.Type = 2
|
||||
// param.PageNum = 1
|
||||
// param.PageSize = 10
|
||||
if data, err = landPageConfig.FindManyLandPageConfig(ctx, bson.M{
|
||||
landPageConfig.FN.LandPageConfig.Status: true,
|
||||
landPageConfig.FN.LandPageConfig.AppId: param.AppId,
|
||||
landPageConfig.FN.LandPageConfig.LandPageType: param.Type,
|
||||
}, opt); err != nil {
|
||||
//fmt.Println(param, err)
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
//fmt.Println("******", param, err)
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package appInfoSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/genId"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
|
||||
"stat/models/landingPage"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取LandingPage列表
|
||||
func GetLandingPageList(ctx context.Context, param proto.FindLandingPageReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.LandingPageListRes{
|
||||
Total: 0,
|
||||
List: make([]proto.LandingPageInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
total, aList, err := landingPage.FindLandingPageList(ctx, param.AppId, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range aList {
|
||||
tmp := proto.LandingPageInfo{
|
||||
ID: v.ID,
|
||||
AppId: v.AppId,
|
||||
Url: v.Url,
|
||||
Status: v.Status,
|
||||
Type: v.Type,
|
||||
ChannelCode: v.ChannelCode,
|
||||
Remark: v.Remark,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加LandingPage
|
||||
func AddLandingPage(ctx context.Context, param proto.AddLandingPageReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
doc := landingPage.LandingPage{
|
||||
ID: genId.GetNextLandingpageId(),
|
||||
AppId: *param.AppId,
|
||||
Url: *param.Url,
|
||||
Status: *param.Status,
|
||||
Type: *param.Type,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if param.ChannelCode != nil {
|
||||
doc.ChannelCode = *param.ChannelCode
|
||||
}
|
||||
|
||||
if param.Remark != nil {
|
||||
doc.Remark = *param.Remark
|
||||
}
|
||||
|
||||
if _, err = landingPage.AddLandingPage(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 更新LandingPage
|
||||
func UpdateLandingPage(ctx context.Context, param proto.UpdateLandingPageReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
now := time.Now()
|
||||
doc := landingPage.UpdateDoc{
|
||||
Url: param.Url,
|
||||
Status: param.Status,
|
||||
Remark: param.Remark,
|
||||
ChannelCode: param.ChannelCode,
|
||||
Type: param.Type,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
|
||||
if err = landingPage.UpdateLandingPage(ctx, param.ID, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除LandingPage
|
||||
func DelLandingPage(ctx context.Context, param proto.DelLandingPageReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = landingPage.DelLandingPage(ctx, param.ID); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package applicationser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/redis"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/appcategory"
|
||||
"stat/models/applicationLocMod"
|
||||
"stat/models/applicationmod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 外部获取配置应用
|
||||
func GetApplication(ctx context.Context, req proto.CenterApplicationReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.CenterApplicationRes{
|
||||
List: make([]*proto.CenterApplication, 0),
|
||||
AppCategoryList: make([]*proto.CenterAppCategory, 0)}
|
||||
|
||||
list, err := applicationmod.FindAppByAppId(ctx, req.ParentAppId)
|
||||
if err != nil {
|
||||
code = stdLog.AppDbError
|
||||
return
|
||||
}
|
||||
|
||||
filter := bson.M{"parentAppId": req.ParentAppId, "status": true}
|
||||
//filter := bson.M{"status": true}
|
||||
|
||||
sort := bson.D{{Key: "rank", Value: -1}}
|
||||
|
||||
opt := options.Find().SetSort(sort)
|
||||
|
||||
categorylist, serr := appcategory.FindManyAppCategory(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
code = stdLog.AppCategoryDbError
|
||||
return
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
for _, info := range list {
|
||||
app := &proto.CenterApplication{
|
||||
Id: info.Id,
|
||||
AppId: info.AppId,
|
||||
ParentAppId: info.ParentAppId,
|
||||
Name: info.Name,
|
||||
//Tag: info.Tag,
|
||||
Internal: info.Internal,
|
||||
Categories: info.Categories,
|
||||
Desc: info.Desc,
|
||||
DownloadUrl: info.DownloadUrl,
|
||||
IosUrl: info.IosUrl,
|
||||
AndroidUrl: info.AndroidUrl,
|
||||
Avatar: info.Avatar,
|
||||
Status: info.Status,
|
||||
Rank: info.Rank,
|
||||
Total: info.Total,
|
||||
CreatedId: info.CreatedId,
|
||||
UpdatedId: info.UpdatedId,
|
||||
CreatedAt: info.CreatedAt,
|
||||
UpdatedAt: info.UpdatedAt,
|
||||
IsGame: info.IsGame,
|
||||
IsRecommend: info.IsRecommend,
|
||||
NineShow: info.NineShow,
|
||||
RecreationShow: info.RecreationShow,
|
||||
HomeShow: info.HomeShow,
|
||||
LocIds: info.LocIds,
|
||||
SmallImg: info.SmallImg,
|
||||
BigImg: info.BigImg,
|
||||
IsHighQuality: info.IsHighQuality,
|
||||
HighQualityType: info.HighQualityType,
|
||||
}
|
||||
resp.List = append(resp.List, app)
|
||||
}
|
||||
}
|
||||
|
||||
if len(categorylist) > 0 {
|
||||
for _, info := range categorylist {
|
||||
app := &proto.CenterAppCategory{
|
||||
Id: info.Id,
|
||||
ParentAppId: info.ParentAppId,
|
||||
Name: info.Name,
|
||||
Desc: info.Desc,
|
||||
Status: info.Status,
|
||||
Rank: info.Rank,
|
||||
CreatedAt: info.CreatedAt,
|
||||
UpdatedAt: info.UpdatedAt,
|
||||
ImgType: info.ImgType,
|
||||
}
|
||||
resp.AppCategoryList = append(resp.AppCategoryList, app)
|
||||
}
|
||||
}
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// IncApplicationClickCount
|
||||
func IncApplicationClickCount(ctx context.Context, param *proto.ApplicationClickReq) (serr *stdLog.SrvErr) {
|
||||
id, err := primitive.ObjectIDFromHex(param.Id)
|
||||
if err != nil {
|
||||
return stdLog.New(stdLog.ParamError, "", err.Error())
|
||||
}
|
||||
_, serr = applicationmod.FindOneApplication(ctx, bson.M{"_id": id})
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
// 写入redis
|
||||
now := time.Now().Local()
|
||||
set := redis.GetAppClickDaySet(now)
|
||||
name := redis.GetAppClickHourName(param.Id, now.Hour())
|
||||
//name := redis.GetAppClickHourNameByAppId(application.AppId, application.ParentAppId, now.Hour())
|
||||
if _, err := set.IncInt(name, 1); err != nil {
|
||||
return stdLog.New(stdLog.RedisSet, "", err.Error())
|
||||
}
|
||||
// 写入db
|
||||
update := bson.M{"$inc": bson.M{"total": 1}}
|
||||
serr = applicationmod.UpdateOneApplication(ctx, bson.M{"_id": id}, update)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddApplication 添加应用
|
||||
func AddApplication(ctx context.Context, param *applicationmod.Application, account string) *stdLog.SrvErr {
|
||||
param.Id = primitive.NewObjectID()
|
||||
now := utils.Now()
|
||||
param.CreatedAt = now
|
||||
//param.CreatedId = userId
|
||||
param.UpdatedAt = now
|
||||
//param.UpdatedId = userId
|
||||
err := applicationmod.InsertOneApplication(ctx, param)
|
||||
if err != nil {
|
||||
log.Error("AddApplication:InsertOneApplication", log.E(err))
|
||||
return err
|
||||
}
|
||||
CheckChange(ctx, nil, param, account)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelApplication 删除应用
|
||||
func DelApplication(ctx context.Context, id primitive.ObjectID, account string) *stdLog.SrvErr {
|
||||
filter := bson.M{"_id": id}
|
||||
oldDoc, serr := applicationmod.FindOneApplication(ctx, filter)
|
||||
if serr != nil {
|
||||
log.Error("UpdateApplication:FindOneApplication", log.Any("id", id), log.E(serr))
|
||||
return serr
|
||||
}
|
||||
count, err := applicationmod.DelOneApplication(ctx, filter)
|
||||
if err != nil {
|
||||
log.Error("DelApplication:DelOneApplication", log.Any("appId", id), log.E(err))
|
||||
return err
|
||||
}
|
||||
if count == 0 {
|
||||
return stdLog.New(stdLog.ErrApplicationNotExist, "", "")
|
||||
}
|
||||
CheckChange(ctx, oldDoc, nil, account)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateApplication 更新应用
|
||||
func UpdateApplication(ctx context.Context, req *applicationmod.AppUpdate, account string) *stdLog.SrvErr {
|
||||
id, err := primitive.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
log.Error("UpdateApplication:ObjectIDFromHex", log.Any("id", req.Id), log.E(err))
|
||||
return stdLog.New(stdLog.ParamError, "", err.Error())
|
||||
}
|
||||
filter := bson.M{"_id": id}
|
||||
now := utils.Now()
|
||||
|
||||
oldDoc, serr := applicationmod.FindOneApplication(ctx, filter)
|
||||
if serr != nil {
|
||||
log.Error("UpdateApplication:FindOneApplication", log.Any("id", req.Id), log.E(serr))
|
||||
return serr
|
||||
}
|
||||
|
||||
doc := &applicationmod.AppUpdateInfo{
|
||||
AppId: req.Param.AppId,
|
||||
ParentAppId: req.Param.ParentAppId,
|
||||
Name: req.Param.Name,
|
||||
DaoLiangTitle: req.Param.DaoLiangTitle,
|
||||
//Tag: req.Param.Tag,
|
||||
Internal: req.Param.Internal,
|
||||
Categories: req.Param.Categories,
|
||||
Desc: req.Param.Desc,
|
||||
DownloadUrl: req.Param.DownloadUrl,
|
||||
IosUrl: req.Param.IosUrl,
|
||||
AndroidUrl: req.Param.AndroidUrl,
|
||||
Avatar: req.Param.Avatar,
|
||||
Status: req.Param.Status,
|
||||
Rank: req.Param.Rank,
|
||||
Total: req.Param.Total,
|
||||
UpdatedAt: now,
|
||||
NineShow: req.Param.NineShow,
|
||||
RecreationShow: req.Param.RecreationShow,
|
||||
IsGame: req.Param.IsGame,
|
||||
IsRecommend: req.Param.IsRecommend,
|
||||
HomeShow: req.Param.HomeShow,
|
||||
LocIds: req.Param.LocIds,
|
||||
SmallImg: req.Param.SmallImg,
|
||||
BigImg: req.Param.BigImg,
|
||||
IsHighQuality: req.Param.IsHighQuality,
|
||||
HighQualityType: req.Param.HighQualityType,
|
||||
StartTime: req.Param.StartTime,
|
||||
EndTime: req.Param.EndTime,
|
||||
}
|
||||
|
||||
serr = applicationmod.UpdateOneApplication(ctx, filter, bson.M{
|
||||
"$set": doc,
|
||||
})
|
||||
|
||||
if serr != nil {
|
||||
log.Error("UpdateApplication:UpdateOneApplication", log.Any("id", req.Id), log.E(serr))
|
||||
return serr
|
||||
}
|
||||
|
||||
newDoc, serr := applicationmod.FindOneApplication(ctx, filter)
|
||||
if serr != nil {
|
||||
log.Error("UpdateApplication:FindOneApplication", log.Any("id", req.Id), log.E(serr))
|
||||
return serr
|
||||
}
|
||||
|
||||
CheckChange(ctx, oldDoc, newDoc, account)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAppList 获取应用列表
|
||||
func GetAppList(ctx context.Context, param *proto.GetApplicationsReq) (*proto.GetApplicationsResp, *stdLog.SrvErr) {
|
||||
filter := bson.M{}
|
||||
sort := bson.M{applicationmod.FN.Application.Rank: -1}
|
||||
if param.Id != nil && *param.Id != "" {
|
||||
id, serr := primitive.ObjectIDFromHex(*param.Id)
|
||||
if serr != nil {
|
||||
return nil, stdLog.New(stdLog.ParamError, "", "id错误")
|
||||
}
|
||||
filter["_id"] = id
|
||||
}
|
||||
if param.Name != nil && *param.Name != "" {
|
||||
filter["name"] = bson.M{"$regex": *param.Name}
|
||||
}
|
||||
if param.DaoLiangTitle != nil && *param.DaoLiangTitle != "" {
|
||||
filter["daoLiangTitle"] = bson.M{"$regex": *param.DaoLiangTitle}
|
||||
}
|
||||
/*if param.Tag != nil && *param.Tag != 0 {
|
||||
filter["tag"] = *param.Tag
|
||||
}
|
||||
*/
|
||||
if param.Status != nil {
|
||||
filter["status"] = *param.Status
|
||||
}
|
||||
if param.Category != nil {
|
||||
filter["categories.cid"] = *param.Category
|
||||
sort["categories.rank"] = 1
|
||||
} else {
|
||||
sort["categories.rank"] = 1
|
||||
}
|
||||
|
||||
if param.ParentAppId != nil {
|
||||
filter["parentAppId"] = param.ParentAppId
|
||||
}
|
||||
|
||||
if len(param.LocIds) > 0 {
|
||||
filter["locIds"] = bson.M{"$in": param.LocIds}
|
||||
}
|
||||
|
||||
if param.DownloadUrl != nil {
|
||||
filter["download_url"] = *param.DownloadUrl
|
||||
}
|
||||
|
||||
if param.IosUrl != nil {
|
||||
filter["iosUrl"] = *param.IosUrl
|
||||
}
|
||||
|
||||
if param.AndroidUrl != nil {
|
||||
filter["androidUrl"] = *param.AndroidUrl
|
||||
}
|
||||
|
||||
if param.IsHighQuality != nil {
|
||||
filter["isHighQuality"] = *param.IsHighQuality
|
||||
}
|
||||
|
||||
count, serr := applicationmod.CountApplications(ctx, filter)
|
||||
if serr != nil {
|
||||
log.Error("GetAppList:CountApplications", log.E(serr))
|
||||
return nil, serr
|
||||
}
|
||||
if count == 0 {
|
||||
return &proto.GetApplicationsResp{
|
||||
List: nil,
|
||||
Total: count,
|
||||
}, nil
|
||||
}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(sort)
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
applicationList, serr := applicationmod.FindManyApplication(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
log.Error("GetAppList:FindManyApplication", log.E(serr))
|
||||
return nil, serr
|
||||
}
|
||||
// 给前端提供分类名字
|
||||
dict, serr := appcategory.GetAppCategoryIdNameMap(ctx)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
|
||||
locDict, err := applicationLocMod.GetAppLocIdNameMap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, app := range applicationList {
|
||||
applicationList[i].LocNames = make([]string, 0)
|
||||
|
||||
if len(app.LocIds) == 0 {
|
||||
applicationList[i].LocIds = make([]int64, 0)
|
||||
} else {
|
||||
for _, id := range app.LocIds {
|
||||
if loc, ok := locDict[id]; ok {
|
||||
applicationList[i].LocNames = append(applicationList[i].LocNames, loc.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range app.Categories {
|
||||
if category := dict[item.CID]; category != nil {
|
||||
item.Name = category.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
resp := &proto.GetApplicationsResp{
|
||||
List: applicationList,
|
||||
Total: count,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package applicationser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/genId"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/applicationLocMod"
|
||||
"stat/models/applicationmod"
|
||||
|
||||
"stat/models/appInfoMod"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 获取广告位置列表
|
||||
func ListAppLoc(ctx context.Context, param proto.ListAppLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListAppLocRes{
|
||||
Total: 0,
|
||||
List: make([]proto.AppLocInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
// 获取广告位置信息
|
||||
total, adsList, err := applicationLocMod.FindApplicationLocationList(ctx, param.AppId, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
// 获取app信息
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range adsList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
appList, err := appInfoMod.GetAppInfosByAppIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
appMap := make(map[int32]appInfoMod.AppInfo)
|
||||
for _, v := range appList {
|
||||
appMap[v.AppId] = v
|
||||
}
|
||||
|
||||
for _, v := range adsList {
|
||||
appName := ""
|
||||
if appInfo, ok := appMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
tmp := proto.AppLocInfo{
|
||||
Id: v.Id,
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
LocId: v.LocId,
|
||||
Name: v.Name,
|
||||
ImgSizeDesc: v.ImgSizeDesc,
|
||||
Remark: v.Remark,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
data = resp
|
||||
return
|
||||
}
|
||||
|
||||
// 新增广告位置
|
||||
func AddAppLoc(ctx context.Context, param proto.AddAppLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 获取位置信息
|
||||
locInfo, err := applicationLocMod.FindLocationByAppIdAndLocId(ctx, *param.AppId, *param.LocId)
|
||||
if err != nil || locInfo.Id > 0 {
|
||||
code = stdLog.ApplicationLocExists
|
||||
return
|
||||
}
|
||||
|
||||
adsLocId := genId.GetNextAppLocId()
|
||||
now := time.Now()
|
||||
|
||||
doc := applicationLocMod.ApplicationLocation{
|
||||
Id: adsLocId,
|
||||
AppId: *param.AppId,
|
||||
LocId: *param.LocId,
|
||||
Name: *param.Name,
|
||||
ImgSizeDesc: param.ImgSizeDesc,
|
||||
Remark: param.Remark,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err = applicationLocMod.AddApplicationLocation(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改广告位置
|
||||
func UpdateAppLoc(ctx context.Context, param proto.UpdateAppLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 获取位置信息
|
||||
locInfo, err := applicationLocMod.FindLocationById(ctx, *param.Id)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
doc := applicationLocMod.UpdateAppLoc{
|
||||
AppId: param.AppId,
|
||||
LocId: param.LocId,
|
||||
Name: param.Name,
|
||||
ImgSizeDesc: param.ImgSizeDesc,
|
||||
Remark: param.Remark,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
|
||||
if err = applicationLocMod.UpdateApplicationLocation(ctx, *param.Id, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
// 修改广告的位置id
|
||||
if param.LocId != nil && *param.LocId != locInfo.LocId {
|
||||
if err = applicationmod.UpdateLocInfo(ctx, locInfo.AppId, locInfo.LocId, *param.LocId); err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, data, err
|
||||
}
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除广告位置
|
||||
func DelAppLoc(ctx context.Context, param proto.DelAppLocReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = applicationLocMod.DelApplicationLocation(ctx, *param.Id); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package applicationser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/utils"
|
||||
"stat/models/applicationLocMod"
|
||||
"stat/models/applicationmod"
|
||||
"stat/statg"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CheckChange(ctx context.Context, oldDoc, newDoc *applicationmod.Application, account string) {
|
||||
msg := ""
|
||||
changed := false
|
||||
// 检测更新
|
||||
if oldDoc != nil && newDoc != nil {
|
||||
warnChar := "🔴"
|
||||
msg += fmt.Sprintf("检测到应用变更:\n应用ID:【%s】\n应用名称:【%s】\n", oldDoc.Id.Hex(), oldDoc.Name)
|
||||
if oldDoc.DownloadUrl != newDoc.DownloadUrl {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("下载链接变更:\n┏旧值【%v】\n┗新值【%v】\n", oldDoc.DownloadUrl, newDoc.DownloadUrl)
|
||||
}
|
||||
if oldDoc.AndroidUrl != newDoc.AndroidUrl {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("安卓链接变更:\n┏旧值【%v】\n┗新值【%v】\n", oldDoc.AndroidUrl, newDoc.AndroidUrl)
|
||||
}
|
||||
if oldDoc.IosUrl != newDoc.IosUrl {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("IOS链接变更:\n┏旧值【%v】\n┗新值【%v】\n", oldDoc.IosUrl, newDoc.IosUrl)
|
||||
}
|
||||
if oldDoc.ParentAppId != newDoc.ParentAppId {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("所属appId变更:\n┏旧值【%v】\n┗新值【%v】\n", oldDoc.ParentAppId, newDoc.ParentAppId)
|
||||
} else {
|
||||
oSet := utils.NewSet(oldDoc.LocIds)
|
||||
nSet := utils.NewSet(newDoc.LocIds)
|
||||
if !oSet.Equal(nSet) { // 两个集合不相等
|
||||
changed = true
|
||||
dict := applicationLocMod.FindApplicationLocationDict(ctx, oldDoc.ParentAppId)
|
||||
oldLocations := ""
|
||||
for _, id := range oldDoc.LocIds {
|
||||
oldLocations += fmt.Sprintf("%s,", dict[id].Name)
|
||||
}
|
||||
nowLocations := ""
|
||||
for _, id := range newDoc.LocIds {
|
||||
nowLocations += fmt.Sprintf("%s,", dict[id].Name)
|
||||
}
|
||||
msg += fmt.Sprintf("应用中心位置变更:\n┏旧值【%s】\n┗新值【%s】\n", oldLocations, nowLocations)
|
||||
}
|
||||
}
|
||||
if oldDoc.Status != newDoc.Status {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("激活变更:\n┏旧值【%v】【%v】\n┗新值【%v】【%v】\n", oldDoc.Status, consts.StatusDesc[oldDoc.Status],
|
||||
newDoc.Status, consts.StatusDesc[newDoc.Status])
|
||||
}
|
||||
if oldDoc.Internal != newDoc.Internal {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("是否内部应用展示,状态变更:\n┏旧值【%v】【%v】\n┗新值【%v】【%v】\n", oldDoc.Internal, consts.IsShowDesc[oldDoc.Internal],
|
||||
newDoc.Internal, consts.IsShowDesc[newDoc.Internal])
|
||||
}
|
||||
if oldDoc.NineShow != newDoc.NineShow {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("是否九宫格展示,状态变更:\n┏旧值【%v】【%v】\n┗新值【%v】【%v】\n", oldDoc.NineShow, consts.IsShowDesc[oldDoc.NineShow],
|
||||
newDoc.NineShow, consts.IsShowDesc[newDoc.NineShow])
|
||||
}
|
||||
if oldDoc.RecreationShow != newDoc.RecreationShow {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("是否娱乐中心展示,状态变更:\n┏旧值【%v】【%v】\n┗新值【%v】【%v】\n", oldDoc.RecreationShow, consts.IsShowDesc[oldDoc.RecreationShow],
|
||||
newDoc.RecreationShow, consts.IsShowDesc[newDoc.RecreationShow])
|
||||
}
|
||||
if oldDoc.HomeShow != newDoc.HomeShow {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("是否首页展示,状态变更:\n┏旧值【%v】【%v】\n┗新值【%v】【%v】\n", oldDoc.HomeShow, consts.IsShowDesc[oldDoc.HomeShow],
|
||||
newDoc.HomeShow, consts.IsShowDesc[newDoc.HomeShow])
|
||||
}
|
||||
if !oldDoc.StartTime.Equal(newDoc.StartTime) {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("开始时间变更:\n┏旧值【%v】\n┗新值【%v】\n", oldDoc.StartTime, newDoc.StartTime)
|
||||
}
|
||||
if !oldDoc.EndTime.Equal(newDoc.EndTime) {
|
||||
changed = true
|
||||
msg += fmt.Sprintf("结束时间变更:\n┏旧值【%v】\n┗新值【%v】\n", oldDoc.EndTime, newDoc.EndTime)
|
||||
}
|
||||
msg = warnChar + msg
|
||||
} else if oldDoc == nil && newDoc != nil {
|
||||
changed = true
|
||||
warnChar := "🟣"
|
||||
msg += fmt.Sprintf("检测到应用新增:\n应用ID:【%s】\n名称:【%s】\n", newDoc.Id.Hex(), newDoc.Name)
|
||||
msg += fmt.Sprintf("下载链接:【%v】\n", newDoc.DownloadUrl)
|
||||
msg += fmt.Sprintf("安卓链接:【%v】\n", newDoc.AndroidUrl)
|
||||
msg += fmt.Sprintf("IOS链接:【%v】\n", newDoc.IosUrl)
|
||||
msg += fmt.Sprintf("激活状态:\n┏【%v】【%s】\n", newDoc.Status, consts.StatusDesc[newDoc.Status])
|
||||
msg = warnChar + msg
|
||||
} else if oldDoc != nil && newDoc == nil {
|
||||
changed = true
|
||||
warnChar := "🟠"
|
||||
msg += fmt.Sprintf("检测到应用删除:\n应用ID:【%s】\n名称:【%s】\n", oldDoc.Id.Hex(), oldDoc.Name)
|
||||
msg += fmt.Sprintf("下载链接:【%v】\n", oldDoc.DownloadUrl)
|
||||
msg += fmt.Sprintf("安卓链接:【%v】\n", oldDoc.AndroidUrl)
|
||||
msg += fmt.Sprintf("IOS链接:【%v】\n", oldDoc.IosUrl)
|
||||
msg = warnChar + msg
|
||||
}
|
||||
if changed {
|
||||
if newDoc != nil {
|
||||
dict := applicationLocMod.FindApplicationLocationDict(ctx, newDoc.ParentAppId)
|
||||
msg += fmt.Sprintf("所属APP:【%s】\n", statg.GlobalEndpoints.GetAppShortNameById(int32(newDoc.ParentAppId)))
|
||||
nowLocations := ""
|
||||
for _, id := range newDoc.LocIds {
|
||||
nowLocations += fmt.Sprintf("%s,", dict[id].Name)
|
||||
}
|
||||
msg += fmt.Sprintf("应用位置:【%s】\n", nowLocations)
|
||||
msg += fmt.Sprintf("激活状态:【%s】\n", consts.StatusDesc[newDoc.Status])
|
||||
}
|
||||
msg += fmt.Sprintf("操作人员账号:【%s】\n", account)
|
||||
msg += fmt.Sprintf("操作发生时间:【%s】\n", time.Now().Format("2006-01-02T15:04:05"))
|
||||
msg += fmt.Sprintf("请确认是否异常: %s \n", statg.Tgbotsend)
|
||||
statg.Bot.SendAdvertiseMessage(msg)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package authoritySer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/genId"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/authorityMod"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 角色列表
|
||||
func RoleList(ctx context.Context, param proto.RoleListReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
skip := (param.PageNum - 1) * param.PageSize
|
||||
limit := param.PageSize
|
||||
resp := proto.RoleListRes{
|
||||
Total: 0,
|
||||
List: make([]proto.RoleInfo, 0),
|
||||
}
|
||||
total, auList, err := authorityMod.RoleList(ctx, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
for _, v := range auList {
|
||||
var tmp = proto.RoleInfo{
|
||||
RoleId: v.Id,
|
||||
Role: v.Role,
|
||||
AuthJson: v.AuthJson,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
data = resp
|
||||
resp.Total = total
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加角色
|
||||
func AddRole(ctx context.Context, param proto.AddRoleReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 查询是否存在该角色
|
||||
roleInfo, err := authorityMod.GetAuthorityByRole(ctx, *param.Role)
|
||||
if err != nil || roleInfo.Id > 0 {
|
||||
code = stdLog.AuthorityRoleExists
|
||||
return
|
||||
}
|
||||
|
||||
// 获取id
|
||||
roleId := genId.GetNextRoleId()
|
||||
|
||||
now := time.Now()
|
||||
doc := authorityMod.Authority{
|
||||
Id: roleId,
|
||||
Role: *param.Role,
|
||||
AuthJson: *param.AuthJson,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if err = authorityMod.AddRole(ctx, &doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 修改角色
|
||||
func UpdateRole(ctx context.Context, param proto.UpdateRoleReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// 查询是否存在该角色
|
||||
if param.Role != nil {
|
||||
roleInfo, err := authorityMod.GetRoleExcludeRole(ctx, *param.RoleId, *param.Role)
|
||||
if err != nil || roleInfo.Id > 0 {
|
||||
code = stdLog.AuthorityRoleExists
|
||||
return code, data, err
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
doc := authorityMod.UpdateDoc{
|
||||
Role: param.Role,
|
||||
AuthJson: param.AuthJson,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
|
||||
if err = authorityMod.UpdateRole(ctx, *param.RoleId, doc); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
func DelRole(ctx context.Context, param proto.DelRoleReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if err = authorityMod.DelRoleById(ctx, *param.RoleId); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/models/accessmod"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/rechargegamemod"
|
||||
"stat/models/rechargemod"
|
||||
"stat/models/registermod"
|
||||
"stat/models/withdrawGameOrdermod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type IpCount struct {
|
||||
Ip string
|
||||
Count int64
|
||||
}
|
||||
|
||||
type IpCountList []*IpCount
|
||||
|
||||
func (a IpCountList) Len() int { return len(a) }
|
||||
func (a IpCountList) Less(i, j int) bool {
|
||||
return a[i].Count > a[j].Count
|
||||
}
|
||||
func (a IpCountList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type AnalyzeItem struct {
|
||||
Channel string
|
||||
RegisterIpMap map[string]int64
|
||||
RegisterUserMap map[string]int64
|
||||
RegisterCount int64 // 注册总数
|
||||
RegisterIpCount int64 // 注册IP数
|
||||
VisitCountMap map[string]int64 // 访问总数
|
||||
ValidVisitAfterHourMap map[string]int64 // 1小时后访问总数
|
||||
ValidVisitAfterDayMap map[string]int64 // 1天后访问总数
|
||||
ValidVisitAfterTowDayMap map[string]int64 // 2天后访问总数
|
||||
RechargeCount int64 // 充值次数
|
||||
RechargeAmount int64 // 充值钱数
|
||||
RechargeUsers int64 // 充值用户数
|
||||
GameRecharge int64 // 游戏充值
|
||||
GameWithdraw int64 // 游戏提现
|
||||
}
|
||||
|
||||
func NewAnalyzeItem(channel string) *AnalyzeItem {
|
||||
return &AnalyzeItem{
|
||||
Channel: channel,
|
||||
RegisterIpMap: make(map[string]int64),
|
||||
RegisterUserMap: make(map[string]int64),
|
||||
VisitCountMap: make(map[string]int64),
|
||||
ValidVisitAfterHourMap: make(map[string]int64),
|
||||
ValidVisitAfterDayMap: make(map[string]int64),
|
||||
ValidVisitAfterTowDayMap: make(map[string]int64),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AnalyzeItem) AddIp(ip string) {
|
||||
s.RegisterCount++
|
||||
s.RegisterIpMap[ip]++
|
||||
s.RegisterIpCount = int64(len(s.RegisterIpMap))
|
||||
}
|
||||
|
||||
func (s *AnalyzeItem) IpRank(topCount int) IpCountList {
|
||||
list := make(IpCountList, 0)
|
||||
for ip, count := range s.RegisterIpMap {
|
||||
list = append(list, &IpCount{
|
||||
Ip: ip,
|
||||
Count: count,
|
||||
})
|
||||
}
|
||||
sort.Sort(list)
|
||||
ret := make(IpCountList, 0)
|
||||
for _, item := range list {
|
||||
if len(ret) < topCount {
|
||||
ret = append(ret, item)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func ChannelAnalyze(ctx context.Context, channel string, start time.Time, end time.Time) (*AnalyzeItem, error) {
|
||||
ret := NewAnalyzeItem(channel)
|
||||
key := func(appid int32, userid uint64) string {
|
||||
return fmt.Sprintf("%d-%d", appid, userid)
|
||||
}
|
||||
channelDeductionRules, err := channelmod.FindManyChannelDeductionRule(ctx, bson.M{
|
||||
channelmod.FN.ChannelDeductionRule.Channel: channel,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("ChannelSettlementMethod FindManyChannelDeductionRule", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
if len(channelDeductionRules) <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: channel})
|
||||
if err != nil {
|
||||
log.Error("ChannelAnalyze FindOneChannelInfo", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
if channelInfo.Channel != channel {
|
||||
return nil, errors.New("channel not found")
|
||||
}
|
||||
registerDict := make(map[string]*registermod.RegisterMod)
|
||||
for _, ruleInfo := range channelDeductionRules {
|
||||
// 统计注册用户
|
||||
registerList, err := registermod.RegisterChannelUsersInfoByTime(ctx, channel, ruleInfo.AppId, start, end)
|
||||
if err != nil {
|
||||
log.Error("NewCountMethod RegisterUsersInfoByDistrict ", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
uids := make([]uint64, 0)
|
||||
for _, register := range registerList {
|
||||
ret.AddIp(register.IP)
|
||||
registerDict[key(ruleInfo.AppId, register.UserId)] = register
|
||||
uids = append(uids, register.UserId)
|
||||
}
|
||||
err1 := utils.SlicePaged(uids, 5000, func(piece []uint64) error {
|
||||
vList, err2 := accessmod.FindManyVisitInfo(ctx, bson.M{
|
||||
"userId": bson.M{"$in": piece},
|
||||
"appId": ruleInfo.AppId,
|
||||
"visitAt": bson.M{"$gte": start, "$lt": end},
|
||||
})
|
||||
// 访问统计
|
||||
for _, visit := range vList {
|
||||
skey := key(ruleInfo.AppId, visit.UserId)
|
||||
regUser := registerDict[skey]
|
||||
if regUser == nil {
|
||||
continue
|
||||
}
|
||||
ret.VisitCountMap[skey]++
|
||||
if visit.VisitAt.After(regUser.RegisterTime.Add(time.Hour)) {
|
||||
ret.ValidVisitAfterHourMap[skey]++
|
||||
}
|
||||
if visit.VisitAt.After(regUser.RegisterTime.Add(24 * time.Hour)) {
|
||||
ret.ValidVisitAfterDayMap[skey]++
|
||||
}
|
||||
if visit.VisitAt.After(regUser.RegisterTime.Add(48 * time.Hour)) {
|
||||
ret.ValidVisitAfterTowDayMap[skey]++
|
||||
}
|
||||
}
|
||||
return err2
|
||||
})
|
||||
if err1 != nil {
|
||||
log.Error("FindManyVisitInfo error ", log.E(err1))
|
||||
return nil, err1
|
||||
}
|
||||
// 充值统计
|
||||
rechargeFilter := bson.M{"districtCode": channel, "appId": ruleInfo.AppId, "successAt": bson.M{"$gte": start, "$lt": end}}
|
||||
rechargeList, _ := rechargemod.FindManyRechargeInfo(ctx, rechargeFilter)
|
||||
for _, item := range rechargeList {
|
||||
skey := key(ruleInfo.AppId, item.UserId)
|
||||
regUser := registerDict[skey]
|
||||
if regUser == nil {
|
||||
continue
|
||||
}
|
||||
ret.RegisterUserMap[skey]++
|
||||
ret.RechargeCount++
|
||||
ret.RechargeAmount += item.PayMoney
|
||||
}
|
||||
// 游戏充值
|
||||
gamerechageList, err := rechargegamemod.FindManyRechargeInfo(ctx, rechargeFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range gamerechageList {
|
||||
skey := key(ruleInfo.AppId, item.UserId)
|
||||
regUser := registerDict[skey]
|
||||
if regUser == nil {
|
||||
continue
|
||||
}
|
||||
ret.RegisterUserMap[skey]++
|
||||
ret.GameRecharge += item.Money
|
||||
}
|
||||
gamewithdrawList, err := withdrawGameOrdermod.FindManyGameWithdrawOrder(ctx, rechargeFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range gamewithdrawList {
|
||||
skey := key(ruleInfo.AppId, item.UID)
|
||||
regUser := registerDict[skey]
|
||||
if regUser == nil {
|
||||
continue
|
||||
}
|
||||
ret.GameWithdraw += item.Money
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"stat/common/log"
|
||||
"stat/statg"
|
||||
)
|
||||
|
||||
func StartBot() {
|
||||
log.Info("StartBot start")
|
||||
if statg.Bot == nil {
|
||||
log.Error("statg.Bot is nil!!!")
|
||||
return
|
||||
}
|
||||
|
||||
Start(statg.Bot)
|
||||
// 发送服务器启动信息
|
||||
text := GetServerInformation()
|
||||
text += GetServerInformationLastTime()
|
||||
text += "🔔此服务器刚刚重启🔔"
|
||||
|
||||
statg.Bot.SendDevelopMessage(text)
|
||||
|
||||
// 注册命令
|
||||
statg.Bot.RegisterCommand(CommandList...)
|
||||
|
||||
if statg.RchssendBot != nil && statg.Conf.ServerIndex == statg.ServerIndexBili {
|
||||
// 注册命令
|
||||
statg.RchssendBot.RegisterCommand(CommandList0...)
|
||||
}
|
||||
|
||||
log.Info("StartBot end")
|
||||
//statg.RchssendBot.SendrchssendMessage(text)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"stat/common/tgbot"
|
||||
)
|
||||
|
||||
var (
|
||||
CommandList = []*tgbot.Command{
|
||||
{
|
||||
Cmd: "what",
|
||||
Help: "/what",
|
||||
Desc: "聊天信息",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleWhatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "menu",
|
||||
Help: "/menu",
|
||||
Desc: "显示菜单",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleMenuCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "close",
|
||||
Help: "/close",
|
||||
Desc: "关闭菜单",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleCloseCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "hour",
|
||||
Help: "/hour",
|
||||
Desc: "小时统计",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleHourStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "district",
|
||||
Help: "/district",
|
||||
Desc: "渠道统计",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleDistrictDataCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "today",
|
||||
Help: "/today -app=888",
|
||||
Desc: "今日统计,统计从今天开始到当前小时整点的数据",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleTodayStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "yesterday",
|
||||
Help: "/yesterday -app=888",
|
||||
Desc: "昨日统计,统计昨天的完整数据",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleYesterdayStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "daily",
|
||||
Help: "/daily -app=888 -back=1",
|
||||
Desc: "每日统计, -back=0 表示今天,-back=1 表示昨天,-back=2 表示前天,以此类推",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleDailyStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "apps",
|
||||
Help: "/apps",
|
||||
Desc: "应用列表",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleAppListCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "sync",
|
||||
Help: "/sync | /sync -detail | /sync -app=888,999",
|
||||
Desc: "同步状态",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleSyncStatusCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "daily_hour",
|
||||
Help: "/daily_hour -app=888 -back=0",
|
||||
Desc: "每日每时统计, -back=0 表示今天,-back=1 表示昨天,-back=2 表示前天,以此类推",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleDailyHourStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "game_report",
|
||||
Help: "/game_report",
|
||||
Desc: "游戏统计文件, -back=0 表示今天,-back=1 表示昨天,-back=2 表示前天,以此类推",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleGameReportCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "channel_analyze",
|
||||
Help: "/channel_analyze -channel=abc -begin=2023-06-05 -end=2023-07-05",
|
||||
Desc: "渠道分析, 时间按中国时间解析,每个时间都解析为当天零点",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleChannelAnalyzeCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "channel_apk",
|
||||
Help: "/channel_apk -channel=abc -app=888",
|
||||
Desc: "渠道包生成",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleChannelApkCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "auto_task",
|
||||
Help: "/auto_task -file",
|
||||
Desc: "自动任务运行状态",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleAutoTaskStatusCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "cross_stat",
|
||||
Help: "/cross_stat -stat=recharge -back=0",
|
||||
Desc: "全服统计",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleCrossDayStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "cross_month_stat",
|
||||
Help: "/cross_month_stat -month=2023-06-01 -app=888",
|
||||
Desc: "全服月统计, -month=2023-06-01 表示统计2023年整个6月的数据, -app=888 表示只统计app=888的数据,不填写则统计所有app",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleCrossMonthStatCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "version",
|
||||
Help: "/version",
|
||||
Desc: "服务端版本",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleVersionCmd,
|
||||
},
|
||||
}
|
||||
CommandList0 = []*tgbot.Command{
|
||||
{
|
||||
Cmd: "menurecharge",
|
||||
Help: "/menurecharge",
|
||||
Desc: "显示菜单",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleMenuCmd0,
|
||||
},
|
||||
{
|
||||
Cmd: "recharge",
|
||||
Help: "/recharge",
|
||||
Desc: "单充值播报",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleRchssendCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "rechargenew",
|
||||
Help: "/rechargenew",
|
||||
Desc: "综合充值播报(昨天)",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleRchsMonthBotsendCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "rechargedate",
|
||||
Help: "/rechargedate",
|
||||
Desc: "综合充值播报(开始到结束的时间)",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleRchsMonthBotDatetodatesendCmd,
|
||||
},
|
||||
{
|
||||
Cmd: "rechargeoneday",
|
||||
Help: "/rechargeoneday",
|
||||
Desc: "综合统计具体年月日",
|
||||
CmdType: tgbot.CommandTypeCommand,
|
||||
Process: HandleRchsDateBotsendCmd,
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/log"
|
||||
"stat/common/statutils"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/utils"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
botCron *cron.Cron
|
||||
)
|
||||
|
||||
func init() {
|
||||
botCron = cron.New(cron.WithSeconds(), cron.WithChain(cron.SkipIfStillRunning(&log.Logger{}))) // 秒级定时任务
|
||||
}
|
||||
|
||||
// Start 定时器任务启动
|
||||
func Start(bot *tgbot.TgBot) {
|
||||
log.Info("skd start...")
|
||||
if bot == nil {
|
||||
return
|
||||
}
|
||||
ctx := context.Background()
|
||||
if statg.Conf.RunMod != consts.RunModRelease {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = botCron.AddFunc("0 5 0/1 * * ?", func() {
|
||||
log.Info("Cron Hourly Entry...")
|
||||
// 发送消息 统计数据
|
||||
curStatTime := utils.RecentMinute(time.Now(), 60) // 按60分钟对齐
|
||||
endpoints := statg.GlobalEndpoints.GetIndexEndpoints(statg.Conf.ServerIndex).GetBotStatEndpoints()
|
||||
for _, end := range endpoints {
|
||||
id := end.AppId
|
||||
tm := curStatTime
|
||||
statutils.ServerMon.RunWithName(func() error {
|
||||
return DailyBotReportTask(ctx, bot, id, tm)
|
||||
}, statutils.GetFuncName(DailyBotReportTask))
|
||||
}
|
||||
fmt.Printf("Cron Hourly %v", curStatTime)
|
||||
if statg.Conf.ServerIndex.IsMadou() {
|
||||
if curStatTime.Hour() == 8 {
|
||||
// statutils.ServerMon.Run(SkdGameTask) 麻豆不发送集团数据
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
_, _ = botCron.AddFunc("0 6 0/1 * * ?", func() {
|
||||
if statg.Conf.ServerIndex.IsMadou() {
|
||||
curStatTime := utils.RecentMinute(time.Now(), 60) // 按60分钟对齐
|
||||
statutils.ServerMon.RunWithName(func() error {
|
||||
return DailyBotRankTask(ctx, bot, curStatTime)
|
||||
}, statutils.GetFuncName(DailyBotRankTask))
|
||||
}
|
||||
})
|
||||
|
||||
_, _ = botCron.AddFunc("*/5 * * * * ?", func() {
|
||||
SaveServerKeepAliveTime()
|
||||
})
|
||||
|
||||
botCron.Start()
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/common/log"
|
||||
"stat/common/statutils"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/utils"
|
||||
"stat/models/accessmod"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/botdailyhourstat"
|
||||
"stat/models/botreport"
|
||||
"stat/models/downloadinfo"
|
||||
"stat/models/rechargemod"
|
||||
"stat/models/registermod"
|
||||
"stat/models/syncrecharge"
|
||||
"stat/models/syncvip"
|
||||
"stat/models/versionMod"
|
||||
"stat/service/crossSer"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// 当日累计详细数据
|
||||
func DailyDetailBotTask(ctx context.Context, bot *tgbot.TgBot, reportTime time.Time) error {
|
||||
title := GetDateDesc(0) + "应用统计排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := crossSer.GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Recharge.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bot.SendBroadcastMessage(fmt.Sprintf("应用统计排名:%s\n", reportTime.Format("2006-01-02T15:04:05")), photo, tgbot.FileTypePhoto)
|
||||
}
|
||||
|
||||
// 当日累计总充值排行
|
||||
func DailyBotRankTask(ctx context.Context, bot *tgbot.TgBot, reportTime time.Time) error {
|
||||
title := GetDateDesc(0) + "应用统计排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := crossSer.GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Recharge.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bot.SendBroadcastMessage(fmt.Sprintf("应用统计排名:%s\n", reportTime.Format("2006-01-02T15:04:05")), photo, tgbot.FileTypePhoto)
|
||||
}
|
||||
|
||||
// 当日累计总充值、单小时充值
|
||||
func DailyBotReportTask(ctx context.Context, bot *tgbot.TgBot, appId int32, reportTime time.Time) error {
|
||||
|
||||
log.Error("DailyBotReportTask start", log.Any("appid", appId),
|
||||
log.Any("time", reportTime))
|
||||
reportFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime,
|
||||
}
|
||||
report, err := botdailyhourstat.FindOneBotHourStatEx(ctx, reportFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
report, err = BotDailyHourStat(ctx, appId, reportTime, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
HistoryFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime.AddDate(0, 0, -1),
|
||||
}
|
||||
historyReport, err := botdailyhourstat.FindOneBotHourStatEx(ctx, HistoryFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
historyReport, err = BotDailyHourStat(ctx, appId, reportTime.AddDate(0, 0, -1), true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
appNameMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
appName := appNameMap[appId]
|
||||
if statg.GlobalEndpoints.GetBotStatEndpoints().ExistsByAppId(appId) {
|
||||
//text := report.DisplayDayHourText(appName, report)
|
||||
//_ = bot.SendBroadcastMessage(text, "", tgbot.FileTypeNone)
|
||||
|
||||
text, title, desc := report.DisplayDayHourTextWithTitle(appName, historyReport)
|
||||
et := tgbot.NewEntityList(title, desc, []*tgbot.Entity{
|
||||
{
|
||||
Content: text,
|
||||
},
|
||||
})
|
||||
_ = bot.SendBroadcastEntity(et)
|
||||
return nil
|
||||
}
|
||||
log.Error("DailyBotReportTask end", log.Any("appid", appId),
|
||||
log.Any("time", reportTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
func DailyBotReportInfo(ctx context.Context, appId int32, reportTime time.Time) (text, title, desc string, err error) {
|
||||
log.Error("DailyBotReportTask start", log.Any("appid", appId),
|
||||
log.Any("time", reportTime))
|
||||
reportFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime,
|
||||
}
|
||||
report, err := botdailyhourstat.FindOneBotHourStatEx(ctx, reportFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
report, err = BotDailyHourStat(ctx, appId, reportTime, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
HistoryFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime.AddDate(0, 0, -1),
|
||||
}
|
||||
historyReport, err := botdailyhourstat.FindOneBotHourStatEx(ctx, HistoryFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
historyReport, err = BotDailyHourStat(ctx, appId, reportTime.AddDate(0, 0, -1), true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
appNameMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
appName := appNameMap[appId]
|
||||
if statg.GlobalEndpoints.GetBotStatEndpoints().ExistsByAppId(appId) {
|
||||
text, title, desc = report.DisplayDayHourTextWithTitle(appName, historyReport)
|
||||
}
|
||||
log.Error("DailyBotReportTask end", log.Any("appid", appId),
|
||||
log.Any("time", reportTime))
|
||||
return
|
||||
}
|
||||
|
||||
// 当日累计总充值排行
|
||||
func DailyBotRankTaskInfo(ctx context.Context, reportTime time.Time) (photo string, buf []byte, err error) {
|
||||
title := GetDateDesc(0) + "应用统计排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, buf, err = crossSer.GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Recharge.Value() / 100
|
||||
})
|
||||
return photo, buf, err
|
||||
}
|
||||
|
||||
// 当日累计总充值、单小时充值
|
||||
func BotDailyHourStat(ctx context.Context, appId int32, reportTime time.Time, saveData bool) (*botdailyhourstat.BotHourStat, error) {
|
||||
|
||||
hourStatBegin := reportTime.Add(-60 * time.Minute)
|
||||
reportFilter := bson.M{
|
||||
"sumDate": reportTime,
|
||||
"appId": appId,
|
||||
}
|
||||
//BotHourStat
|
||||
report, err := botdailyhourstat.FindOneBotHourStat(ctx, reportFilter)
|
||||
if err != nil {
|
||||
log.Error("FindOneBotReport", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if report.SumDate != reportTime {
|
||||
// 生成数据并插入
|
||||
start := utils.BeginWithTime(reportTime.Add(-time.Minute))
|
||||
end := reportTime
|
||||
|
||||
report = &botdailyhourstat.BotHourStat{
|
||||
ID: primitive.NewObjectID(),
|
||||
AppId: appId,
|
||||
SumDate: reportTime,
|
||||
UpdatedAt: time.Now(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
// 访问
|
||||
err := accessmod.VisitInfoByTimeShard(ctx, appId, start, end, utils.GlobalShardHour, func(item *accessmod.AccessMod) {
|
||||
client := statutils.GetClientInfo(item.SysType, item.DevType, item.DistrictCode, item.IsDirect, false, item.IsFormalUser)
|
||||
report.TodayStat.Visit.Add(1, client)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 注册
|
||||
userDict := make(map[uint64]bool)
|
||||
err = registermod.RegisterFormalsInfoByTimeShard(ctx, appId, start, end, utils.GlobalShardHour, func(item *registermod.RegisterMod) {
|
||||
if item.IsFormal && statutils.IsClientH5OrLight(item.SysType, item.DevType) {
|
||||
if _, exists := userDict[item.UserId]; !exists {
|
||||
userDict[item.UserId] = true
|
||||
client := statutils.GetClientInfo(item.SysType, item.DevType,
|
||||
item.DistrictCode, item.IsDirect, item.IsDeduction, item.IsFormal)
|
||||
report.TodayStat.Register.Add(1, client)
|
||||
if item.RegisterTime.After(hourStatBegin) && item.RegisterTime.Before(reportTime) {
|
||||
report.HourStat.Register.Add(1, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = registermod.RegisterUsersInfoByTimeShard(ctx, appId, start, end, utils.GlobalShardHour, func(item *registermod.RegisterMod) {
|
||||
userDict[item.UserId] = true
|
||||
client := statutils.GetClientInfo(item.SysType, item.DevType,
|
||||
item.DistrictCode, item.IsDirect, item.IsDeduction, item.IsFormal)
|
||||
report.TodayStat.Register.Add(1, client)
|
||||
if item.RegisterTime.After(hourStatBegin) && item.RegisterTime.Before(reportTime) {
|
||||
report.HourStat.Register.Add(1, client)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 充值
|
||||
rechargeMap := make(map[string]*rechargemod.RechargeMod)
|
||||
rechargeList, _ := rechargemod.TotalRechargeListV1(ctx, appId, start, end)
|
||||
for _, item := range rechargeList {
|
||||
client := statutils.GetClientInfo(item.SysType, item.DevType, item.DistrictCode, item.IsDirect, false, item.IsFormalUser)
|
||||
rechargeMap[item.OrderId] = item
|
||||
report.TodayStat.Recharge.Add(item.PayMoney, client)
|
||||
if item.RegisterTime.After(item.SuccessAt.AddDate(0, 0, -1)) {
|
||||
report.TodayStat.RechargeNew.Add(item.PayMoney, client)
|
||||
} else {
|
||||
report.TodayStat.RechargeOld.Add(item.PayMoney, client)
|
||||
}
|
||||
if item.SuccessAt.After(hourStatBegin) && item.SuccessAt.Before(reportTime) {
|
||||
report.HourStat.Recharge.Add(item.PayMoney, client)
|
||||
if item.RegisterTime.After(item.SuccessAt.AddDate(0, 0, -1)) {
|
||||
report.HourStat.RechargeNew.Add(item.PayMoney, client)
|
||||
} else {
|
||||
report.HourStat.RechargeOld.Add(item.PayMoney, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 订单
|
||||
filter := bson.M{syncrecharge.FN.SyncRechargeOrder.AppId: appId, syncrecharge.FN.SyncRechargeOrder.CreatedAt: bson.M{"$gt": start.Add(-20 * time.Minute), "$lt": end}}
|
||||
orderExList, _ := syncrecharge.FindManySyncRechargeOrder(ctx, filter)
|
||||
orderList := make([]*syncrecharge.SyncRechargeOrder, 0)
|
||||
for _, item := range orderExList {
|
||||
if recharge, ok := rechargeMap[item.OrderId]; ok {
|
||||
item.Money = recharge.Money
|
||||
item.PayMoney = recharge.PayMoney
|
||||
item.Status = recharge.Status
|
||||
item.CreatedAt = recharge.SuccessAt
|
||||
}
|
||||
if item.CreatedAt.After(start) && item.CreatedAt.Before(end) {
|
||||
orderList = append(orderList, item)
|
||||
}
|
||||
}
|
||||
for _, item := range orderList {
|
||||
client := statutils.GetClientInfo(item.SysType, item.DevType, item.DistrictCode, item.IsDirect, false, item.IsFormalUser)
|
||||
report.TodayStat.Order.Add(item.PayMoney, client)
|
||||
if item.Status == 3 {
|
||||
report.TodayStat.OrderSucc.Add(1, client)
|
||||
}
|
||||
if item.CreatedAt.After(hourStatBegin) && item.CreatedAt.Before(reportTime) {
|
||||
report.HourStat.Order.Add(item.PayMoney, client)
|
||||
if item.Status == 3 {
|
||||
report.HourStat.OrderSucc.Add(item.PayMoney, client)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 会员
|
||||
vipFilter := bson.M{syncvip.FN.SyncVip.AppId: appId, syncvip.FN.SyncVip.SuccessAt: bson.M{"$gt": start, "$lt": end}}
|
||||
viporderList, _ := syncvip.FindManySyncVip(ctx, vipFilter)
|
||||
for _, item := range viporderList {
|
||||
client := statutils.GetClientInfo(item.DeviceType, item.DeviceType, item.DistrictCode, item.IsDirect, false, item.IsFormalUser)
|
||||
report.TodayStat.RechargeVip.Add(item.Money, client)
|
||||
if item.SuccessAt.After(hourStatBegin) && item.SuccessAt.Before(reportTime) {
|
||||
report.HourStat.RechargeVip.Add(item.Money, client)
|
||||
}
|
||||
}
|
||||
// 金币
|
||||
report.TodayStat.RechargeGold = *report.TodayStat.Recharge.Sub(&report.TodayStat.RechargeVip)
|
||||
report.HourStat.RechargeGold = *report.HourStat.Recharge.Sub(&report.HourStat.RechargeVip)
|
||||
// 游戏
|
||||
if appId == 888 {
|
||||
gameInfo := GetGameInfo(ctx, start)
|
||||
if gameInfo != nil {
|
||||
report.TodayGame = &botdailyhourstat.GameItem{}
|
||||
report.TodayGame.RchgAmount = gameInfo.GameRchgAmount / 100 //充值总金额
|
||||
report.TodayGame.DcRchgAmount = gameInfo.GameDCRchgAmount / 100 //代充总金额
|
||||
report.TodayGame.WtdrAmount = gameInfo.GameWtdrAmount / 100 //游戏提现总金额
|
||||
report.TodayGame.WtdrRealAmount = gameInfo.GameWtdrRealAmount / 100 //游戏实际提现总金额
|
||||
report.TodayGame.IncAmount = -gameInfo.GameIncAmount / 100 //游戏上分总金额
|
||||
report.TodayGame.DecAmount = gameInfo.GameDecAmount / 100 //游戏下分总金额
|
||||
report.TodayGame.ValidBet = gameInfo.ValidBet / 100 //有效投注金额
|
||||
report.TodayGame.Profit = gameInfo.Profit / 100 //系统盈利
|
||||
}
|
||||
}
|
||||
// 落地页
|
||||
downloadList, err := downloadinfo.GetAccessCountByTime(ctx, appId, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, downloadInfo := range downloadList {
|
||||
report.TodayLandPage.LandPageTotalClick += 1
|
||||
if downloadInfo.SysType == "android" {
|
||||
report.TodayLandPage.LandPageAndroidClick += 1
|
||||
} else if strings.Contains(downloadInfo.SysType, "ios") || strings.Contains(downloadInfo.SysType, "light") {
|
||||
if downloadInfo.PackageType == versionMod.Shop {
|
||||
report.TodayLandPage.LandPageIosAppstoreClick += 1
|
||||
} else if downloadInfo.PackageType == versionMod.Enterprise {
|
||||
report.TodayLandPage.LandPageIosFirmClick += 1
|
||||
} else if downloadInfo.PackageType == versionMod.Light {
|
||||
report.TodayLandPage.LandPageIosLightClick += 1
|
||||
}
|
||||
}
|
||||
if downloadInfo.CreatedAt.After(hourStatBegin) && downloadInfo.CreatedAt.Before(reportTime) {
|
||||
report.HourLandPage.LandPageTotalClick += 1
|
||||
if downloadInfo.SysType == "android" {
|
||||
report.HourLandPage.LandPageAndroidClick += 1
|
||||
} else if strings.Contains(downloadInfo.SysType, "ios") || strings.Contains(downloadInfo.SysType, "light") {
|
||||
if downloadInfo.PackageType == versionMod.Shop {
|
||||
report.HourLandPage.LandPageIosAppstoreClick += 1
|
||||
} else if downloadInfo.PackageType == versionMod.Enterprise {
|
||||
report.HourLandPage.LandPageIosFirmClick += 1
|
||||
} else if downloadInfo.PackageType == versionMod.Light {
|
||||
report.HourLandPage.LandPageIosLightClick += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report.AppId = appId
|
||||
if saveData {
|
||||
serr := botdailyhourstat.InsertOneBotHourStat(ctx, report)
|
||||
if serr != nil {
|
||||
log.Error("botdailyhourstat:InsertOneBotHourStat db.InsertOne failed", log.E(serr))
|
||||
}
|
||||
log.Info("botdailyhourstat", log.Any("info", report.ReportTime))
|
||||
}
|
||||
}
|
||||
return report, nil
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"stat/api/proto"
|
||||
"stat/common"
|
||||
"stat/common/crypt"
|
||||
"stat/common/utils"
|
||||
"stat/tool/pubtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Func12(t *testing.T) {
|
||||
// Let's start with a base url
|
||||
baseUrl, err := url.Parse("http://www.bing.com/aaa?a=1&b=2,3")
|
||||
if err != nil {
|
||||
fmt.Println("Malformed URL: ", err.Error())
|
||||
return
|
||||
}
|
||||
// Add a Path Segment (Path segment is automatically escaped)
|
||||
//baseUrl.Path += "path with?reserved characters"
|
||||
// Prepare Query Parameters
|
||||
params := baseUrl.Query()
|
||||
//params.Add("q", "Hello World")
|
||||
//params.Add("u", "wade")
|
||||
// Add Query Parameters to the URL
|
||||
baseUrl.RawQuery = params.Encode() // Escape Query Parameters
|
||||
fmt.Printf("Encoded URL is %q\n", baseUrl.String())
|
||||
}
|
||||
|
||||
func Test_SyncData(t *testing.T) {
|
||||
key := "vEnkB&!75z4VAD6iAY%fkL*rBnU!WdhN"
|
||||
var appId int32 = 222
|
||||
|
||||
req := &proto.BotDailyHourListReq{
|
||||
Page: common.Page{
|
||||
PageNum: 1,
|
||||
PageSize: 20,
|
||||
},
|
||||
SumDateStart: time.Now().AddDate(0, 0, -3),
|
||||
SumDateStop: time.Now(),
|
||||
AppId: &appId,
|
||||
}
|
||||
jsonData, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
data, err := crypt.CoreAesEncryptEx(jsonData, 12, key)
|
||||
os.WriteFile("aaa.data", data, 777)
|
||||
|
||||
}
|
||||
|
||||
func Test_skdTask(t *testing.T) {
|
||||
pubtest.TestInit("/Users/mac/Works/codes/stat_center/run/bili")
|
||||
s, _ := time.Parse(time.RFC3339, "2023-06-15T15:00:00+08:00")
|
||||
curStatTime := utils.RecentMinute(s, 60) // 按60分钟对齐
|
||||
ctx := context.Background()
|
||||
DailyBotReportTask(ctx, nil, int32(444), curStatTime) // 麻豆
|
||||
}
|
||||
|
||||
func Test_PassedTimeFromNowInHuman(t *testing.T) {
|
||||
//s := PassedTimeFromNowInHuman(time.Now().Add(-9 * time.Minute))
|
||||
//t.Log(s)
|
||||
//s2 := DurationDisplay(time.Now(), time.Now().Add(-8*time.Second))
|
||||
//t.Log(s2)
|
||||
}
|
||||
|
||||
func Test_Fun1(t *testing.T) {
|
||||
time.Local, _ = time.LoadLocation("Asia/Shanghai")
|
||||
|
||||
//DateTime = "2006-01-02 15:04:05"
|
||||
//DateOnly = "2006-01-02"
|
||||
//TimeOnly = "15:04:05"
|
||||
|
||||
s, err := time.ParseInLocation(time.DateOnly, "2023-06-15", time.Local)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Log(s)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,704 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"stat/common/log"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/topicmessage"
|
||||
"stat/common/utils"
|
||||
"stat/models/dailystatmod"
|
||||
"stat/service/consumer"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
)
|
||||
|
||||
// BlocDailyStatTgbot 机器人汇报每日数据()
|
||||
type BlocDailyStatTgbot struct {
|
||||
SumDate time.Time `bson:"sumDate"` // 记录时间 按天对齐
|
||||
NewDirectCount int64 `bson:"newDirectCount"` // 新增直推
|
||||
NewFissionCount int64 `bson:"newFissionCount"` // 新增裂变
|
||||
DirectVisitCount int64 `bson:"directVisitCount"` // 直推访问数
|
||||
FissionVisitCount int64 `bson:"fissionVisitCount"` // 裂变访问数
|
||||
DirectAmount int64 `bson:"directAmount"` // 直推充值总金额
|
||||
DirectOtherAmount int64 `bson:"directOtherAmount"` // 直推充值其他金额
|
||||
FissionAmount int64 `bson:"fissionAmount"` // 裂变充值金额
|
||||
DirectAmountCount int64 `bson:"directAmountcount"` // 直推充值订单
|
||||
FissionAmountCount int64 `bson:"fissionAmountcount"` // 裂变充值订单
|
||||
NewRechargeCount int64 `bson:"newRechargeCount"` // 新增充值数
|
||||
NewRechargeAmount int64 `bson:"newRechargeAmount"` // 新增充值金额
|
||||
NewBindingCount int64 `bson:"newBindingCount"` // 新增绑定用户数
|
||||
APPNewRechargeCount int64 `bson:"appNewRechargeCount"` // app总新增充值人数(兼容)
|
||||
APPNewRechargeAmount int64 `bson:"appNewRechargeAmount"` // app新增总充值
|
||||
APPRechargeAmount int64 `bson:"appRechargeAmount"` // app总充值
|
||||
APPRechargeCount int64 `bson:"appRechargeCount"` // app总充值人数
|
||||
APPRechargeOrderCount int64 `bson:"appRechargeOrderCount"` // app总充值订单数
|
||||
APPDaichongAmout int64 `bson:"appDaichongAmout"` // app代充充值金额
|
||||
APPDaichongAmoutCount int64 `bson:"appDaichongAmoutCount"` // app代充充值人数
|
||||
APPOnlineAmount int64 `bson:"appOnlineAmount"` // app在线充值金额
|
||||
APPOnlineAmountCount int64 `bson:"appOnlineAmountCount"` // app在线充值人数
|
||||
APPRechAmount int64 `bson:"appRechAmount"` // app总提现
|
||||
}
|
||||
|
||||
func GetBlocDailyStatTgbotInfo(ctx context.Context, sumDate time.Time) *BlocDailyStatTgbot {
|
||||
blocDay := utils.BeginWithTimeLocal(sumDate)
|
||||
blocInfo := &BlocDailyStatTgbot{}
|
||||
|
||||
ids := []int32{888, 668}
|
||||
mdStatList, err := dailystatmod.FindDailyStatList(ctx, ids, blocDay)
|
||||
if err != nil {
|
||||
log.Info("GetBlocDailyStatTgbotInfoxx", log.Any("blocDay", blocDay), log.Any("len", len(mdStatList)))
|
||||
return blocInfo
|
||||
}
|
||||
log.Info("GetBlocDailyStatTgbotInfo", log.Any("blocDay", blocDay), log.Any("len", len(mdStatList)))
|
||||
|
||||
for _, mdStatInfo := range mdStatList {
|
||||
blocInfo.SumDate = blocDay
|
||||
blocInfo.NewDirectCount += mdStatInfo.NewDirectCount
|
||||
blocInfo.NewFissionCount += mdStatInfo.NewFissionCount
|
||||
blocInfo.DirectVisitCount += mdStatInfo.DirectVisitCount
|
||||
blocInfo.FissionVisitCount += mdStatInfo.FissionVisitCount
|
||||
blocInfo.DirectAmount += mdStatInfo.DirectAmount
|
||||
blocInfo.FissionAmount += mdStatInfo.FissionAmount
|
||||
blocInfo.DirectAmountCount += mdStatInfo.DirectAmountCount
|
||||
blocInfo.FissionAmountCount += mdStatInfo.FissionAmountCount
|
||||
blocInfo.NewRechargeCount += mdStatInfo.NewRechargeCount
|
||||
blocInfo.NewRechargeAmount += mdStatInfo.NewRechargeAmount
|
||||
blocInfo.APPRechargeAmount += mdStatInfo.TotalAmount
|
||||
blocInfo.APPNewRechargeCount += mdStatInfo.NewRechargeCount
|
||||
blocInfo.APPNewRechargeAmount += mdStatInfo.NewRechargeAmount
|
||||
blocInfo.APPRechargeCount += mdStatInfo.DirectAmountCount + mdStatInfo.FissionAmountCount
|
||||
blocInfo.APPRechargeOrderCount += mdStatInfo.SuccessRechargeOrder
|
||||
blocInfo.APPOnlineAmount += mdStatInfo.TotalAmount
|
||||
blocInfo.APPOnlineAmountCount = +mdStatInfo.DirectAmountCount + mdStatInfo.FissionAmountCount
|
||||
}
|
||||
|
||||
return blocInfo
|
||||
}
|
||||
|
||||
var (
|
||||
GameChatId = "G-841710430"
|
||||
)
|
||||
var exportUserTitle = map[string]string{
|
||||
"A1": "项目",
|
||||
"B1": "YCXM1",
|
||||
"C1": "对比",
|
||||
}
|
||||
|
||||
func GetGameInfo(ctx context.Context, reportTime time.Time) *topicmessage.GetReportStatResp {
|
||||
request := &topicmessage.StatcenterGameReq{
|
||||
Date: utils.BeginWithTime(reportTime),
|
||||
}
|
||||
|
||||
url, err := consumer.AppCallbackUrl(888)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
apiUrl := url + "/api/web/statcenter/game"
|
||||
|
||||
var resp *topicmessage.GetReportStatResp
|
||||
err = consumer.HttpRequest(ctx, apiUrl, &request, &resp)
|
||||
if err != nil {
|
||||
log.Error("RunSyncJob HttpRequest", log.Any("url", apiUrl), log.Any("request", request), log.E(err))
|
||||
return nil
|
||||
}
|
||||
|
||||
if resp.Code != 200 {
|
||||
log.Info("RunSyncJob HTTP请求失败", log.Any("url", apiUrl), log.Any("request", request), log.Any("resp", resp))
|
||||
return nil
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func SkdGameTask() error {
|
||||
ctx := context.Background()
|
||||
beginTime := utils.BeginOfToday().AddDate(0, 0, -1)
|
||||
// yestodayBeginTime := utils.BeginOfToday().AddDate(0, 0, -2)
|
||||
gameInfo := topicmessage.GetReportStatResp{}
|
||||
|
||||
yestodayGameInfo := topicmessage.GetReportStatResp{}
|
||||
|
||||
sheetName := "Sheet1"
|
||||
f := excelize.NewFile()
|
||||
idx := f.NewSheet(sheetName)
|
||||
|
||||
for k, v := range exportUserTitle {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
}
|
||||
f.SetColWidth(sheetName, "A", "D", 30)
|
||||
|
||||
styleOne, _ := f.NewStyle(`{"alignment":{"horizontal":"right"}}`)
|
||||
styleTwo, _ := f.NewStyle(`{"alignment":{"horizontal":"left"}}`)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 2), "视频游戏活跃人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 2), gameInfo.GamePlayCount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 2), gameInfo.GamePlayCount-yestodayGameInfo.GamePlayCount)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 3), "视频游戏充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 3), gameInfo.GameRchgUsers)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 3), gameInfo.GameRchgUsers-yestodayGameInfo.GameRchgUsers)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 4), "视频游戏新增充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 4), gameInfo.GameFirstRchgUsers)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 4), gameInfo.GameFirstRchgUsers-yestodayGameInfo.GameFirstRchgUsers)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 5), "视频游戏老用户充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 5), gameInfo.GameRchgUsers-gameInfo.GameFirstRchgUsers)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 5), (gameInfo.GameRchgUsers-gameInfo.GameFirstRchgUsers)-(yestodayGameInfo.GameRchgUsers-yestodayGameInfo.GameFirstRchgUsers))
|
||||
|
||||
todayRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameRchgAmount)/10000)
|
||||
yestodayRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameRchgAmount-yestodayGameInfo.GameRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 6), "视频游戏总充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 6), todayRchgAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 6), yestodayRchgAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 6), fmt.Sprintf("%s%d", string('B'), 6), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 6), fmt.Sprintf("%s%d", string('C'), 6), styleOne)
|
||||
|
||||
todayFirstRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameFirstRchgAmount)/10000)
|
||||
yestodayFirstRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameFirstRchgAmount-yestodayGameInfo.GameFirstRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 7), "视频游新增充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 7), todayFirstRchgAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 7), yestodayFirstRchgAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 7), fmt.Sprintf("%s%d", string('B'), 7), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 7), fmt.Sprintf("%s%d", string('C'), 7), styleOne)
|
||||
|
||||
todayOldRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameRchgAmount-gameInfo.GameFirstRchgAmount)/10000)
|
||||
yestodayOldRchgAmount := fmt.Sprintf("%.4f", float64((gameInfo.GameRchgAmount-gameInfo.GameFirstRchgAmount)-(yestodayGameInfo.GameRchgAmount-yestodayGameInfo.GameFirstRchgAmount))/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 8), "视频游老用户充值")
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 8), fmt.Sprintf("%s%d", string('B'), 8), styleTwo)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 8), todayOldRchgAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 8), yestodayOldRchgAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 8), fmt.Sprintf("%s%d", string('B'), 8), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 8), fmt.Sprintf("%s%d", string('C'), 8), styleOne)
|
||||
|
||||
todayGameWtdrAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameWtdrAmount)/10000)
|
||||
yestodayGameWtdrAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameWtdrAmount-yestodayGameInfo.GameWtdrAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 9), "视频游戏提现")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 9), todayGameWtdrAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 9), yestodayGameWtdrAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 9), fmt.Sprintf("%s%d", string('B'), 9), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 9), fmt.Sprintf("%s%d", string('C'), 9), styleOne)
|
||||
|
||||
todayGameRecvAmount := fmt.Sprintf("%.4f", float64((gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount))/10000)
|
||||
yestodayGameRecvAmount := fmt.Sprintf("%.4f", float64((gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount)-(yestodayGameInfo.GameRchgAmount-yestodayGameInfo.GameWtdrAmount))/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 10), "视频游戏营收")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 10), todayGameRecvAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 10), yestodayGameRecvAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 10), fmt.Sprintf("%s%d", string('B'), 10), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 10), fmt.Sprintf("%s%d", string('C'), 10), styleOne)
|
||||
|
||||
todayRecvRate := float64((gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount)*100) / float64(gameInfo.GameRchgAmount)
|
||||
yestodayRecvRate := float64((yestodayGameInfo.GameRchgAmount-yestodayGameInfo.GameWtdrAmount)*100) / float64(yestodayGameInfo.GameRchgAmount)
|
||||
todayRecvRateStr := fmt.Sprintf("%.2f", todayRecvRate) + "%"
|
||||
yestodayRecvRateStr := fmt.Sprintf("%.2f", yestodayRecvRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 11), "视频游戏营收比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 11), todayRecvRateStr)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 11), yestodayRecvRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 11), fmt.Sprintf("%s%d", string('B'), 11), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 11), fmt.Sprintf("%s%d", string('C'), 11), styleOne)
|
||||
|
||||
todayRevenue := fmt.Sprintf("%.4f", float64(gameInfo.Revenue)/10000)
|
||||
yestodayRevenue := fmt.Sprintf("%.4f", float64(gameInfo.Revenue-yestodayGameInfo.Revenue)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 12), "视频游戏税收")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 12), todayRevenue)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 12), yestodayRevenue)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 12), fmt.Sprintf("%s%d", string('B'), 12), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 12), fmt.Sprintf("%s%d", string('C'), 12), styleOne)
|
||||
|
||||
daichongAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameDCRchgAmount)/10000)
|
||||
yestodayDaichongAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameDCRchgAmount-yestodayGameInfo.GameDCRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 13), "视频游戏代理充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 13), daichongAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 13), yestodayDaichongAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 13), fmt.Sprintf("%s%d", string('B'), 13), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 13), fmt.Sprintf("%s%d", string('C'), 13), styleOne)
|
||||
|
||||
todayProfit := fmt.Sprintf("%.4f", float64(gameInfo.Profit)/10000)
|
||||
yestodayProfit := fmt.Sprintf("%.4f", float64(gameInfo.Profit-yestodayGameInfo.Profit)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 14), "视频游戏系统输赢")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 14), todayProfit)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 14), yestodayProfit)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 14), fmt.Sprintf("%s%d", string('B'), 14), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 14), fmt.Sprintf("%s%d", string('C'), 14), styleOne)
|
||||
|
||||
todayRevenueRate := float64(gameInfo.Revenue*100) / float64(gameInfo.GameRchgAmount)
|
||||
yestodayRevenueRate := float64(yestodayGameInfo.Revenue*100) / float64(yestodayGameInfo.GameRchgAmount)
|
||||
todayRevenueRateStr := fmt.Sprintf("%.2f", todayRevenueRate) + "%"
|
||||
yestodayRevenueRateStr := fmt.Sprintf("%.2f", todayRevenueRate-yestodayRevenueRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 15), "视频游戏税收比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 15), todayRevenueRateStr)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 15), yestodayRevenueRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 15), fmt.Sprintf("%s%d", string('B'), 15), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 15), fmt.Sprintf("%s%d", string('C'), 15), styleOne)
|
||||
|
||||
todayProfitRate := float64(gameInfo.Profit*100) / float64(gameInfo.GameRchgAmount)
|
||||
yestodayProfitRate := float64(yestodayGameInfo.Profit*100) / float64(yestodayGameInfo.GameRchgAmount)
|
||||
todayProfitRateStr := fmt.Sprintf("%.2f", todayProfitRate) + "%"
|
||||
yestodayProfitRateStr := fmt.Sprintf("%.2f", todayProfitRate-yestodayProfitRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 16), "视频游戏输赢比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 16), todayProfitRateStr)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 16), yestodayProfitRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 16), fmt.Sprintf("%s%d", string('B'), 16), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 16), fmt.Sprintf("%s%d", string('C'), 16), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 17), "视频游戏日投注用户")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 17), gameInfo.GamePlayCount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 17), gameInfo.GamePlayCount-yestodayGameInfo.GamePlayCount)
|
||||
|
||||
todayValidBet := fmt.Sprintf("%.4f", float64(gameInfo.ValidBet)/10000)
|
||||
yestodayValidBet := fmt.Sprintf("%.4f", float64(gameInfo.ValidBet-yestodayGameInfo.ValidBet)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 18), "视频游戏总有效投注")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 18), todayValidBet)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 18), yestodayValidBet)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 18), fmt.Sprintf("%s%d", string('B'), 18), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 18), fmt.Sprintf("%s%d", string('C'), 18), styleOne)
|
||||
|
||||
todayValidBet = fmt.Sprintf("%.4f", (float64(gameInfo.ValidBet)/float64(gameInfo.GamePlayCount))/10000)
|
||||
yestodayValidBet = fmt.Sprintf("%.4f", (float64(gameInfo.ValidBet)/float64(gameInfo.GamePlayCount))/10000-(float64(yestodayGameInfo.ValidBet)/float64(yestodayGameInfo.GamePlayCount))/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 19), "视频游戏人均投注")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 19), todayValidBet)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 19), yestodayValidBet)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 19), fmt.Sprintf("%s%d", string('B'), 19), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 19), fmt.Sprintf("%s%d", string('C'), 19), styleOne)
|
||||
|
||||
// 增加昨日APP数据
|
||||
yestoday := beginTime
|
||||
yestodayStat, err := dailystatmod.FindOneDailyStat(ctx, 888, yestoday)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if yestodayStat.SumDate == yestoday {
|
||||
totalRecharge := yestodayStat.TotalAmount
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 22), "视频注册")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 22), yestodayStat.NewTotalCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 22), fmt.Sprintf("%s%d", string('B'), 13), styleOne)
|
||||
|
||||
ratePhone := float64(100+rand.Intn(30)) / 10000
|
||||
newUser := yestodayStat.NewTotalCount
|
||||
phoneUser := int64(float64(newUser) * ratePhone)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 23), "视频绑定")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 23), phoneUser)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 23), fmt.Sprintf("%s%d", string('B'), 23), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 24), "视频绑定率")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 24), fmt.Sprintf("%.4f", ratePhone))
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 24), fmt.Sprintf("%s%d", string('B'), 24), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 25), "视频登陆人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 25), yestodayStat.TotalVisitCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 25), fmt.Sprintf("%s%d", string('B'), 25), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 26), "视频新增充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 26), yestodayStat.NewRechargeCount+gameInfo.GameFirstRchgCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 26), fmt.Sprintf("%s%d", string('B'), 26), styleOne)
|
||||
|
||||
RechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge-yestodayStat.RepeatAmount+gameInfo.GameFirstRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 27), "视频新增充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 27), RechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 27), fmt.Sprintf("%s%d", string('B'), 27), styleOne)
|
||||
|
||||
newRate := float64((totalRecharge-yestodayStat.RepeatAmount+gameInfo.GameFirstRchgAmount)*100) / float64(totalRecharge+gameInfo.GameRchgAmount)
|
||||
newRateStr := fmt.Sprintf("%.2f", newRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 28), "视频新增充值占比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 28), newRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 28), fmt.Sprintf("%s%d", string('B'), 28), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 29), "视频老用户充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 29), yestodayStat.SuccessRechargeOrder-yestodayStat.NewRechargeCount+(gameInfo.GameRchgCount-gameInfo.GameFirstRchgCount))
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 29), fmt.Sprintf("%s%d", string('B'), 29), styleOne)
|
||||
|
||||
oldRechargeStr := fmt.Sprintf("%.4f", float64(yestodayStat.RepeatAmount+gameInfo.GameRchgAmount-gameInfo.GameFirstRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 30), "视频老用户充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 30), oldRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 30), fmt.Sprintf("%s%d", string('B'), 30), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 31), "视频充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 31), yestodayStat.SuccessRechargeOrder+gameInfo.GameRchgCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 31), fmt.Sprintf("%s%d", string('B'), 31), styleOne)
|
||||
|
||||
totalRechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge+gameInfo.GameRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 32), "视频总充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 32), totalRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 32), fmt.Sprintf("%s%d", string('B'), 32), styleOne)
|
||||
|
||||
onlineRechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge+gameInfo.GameRchgAmount-gameInfo.GameDCRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 33), "视频在线充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 33), onlineRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 33), fmt.Sprintf("%s%d", string('B'), 33), styleOne)
|
||||
|
||||
directRate := float64(gameInfo.GameDCRchgAmount) / float64(totalRecharge+gameInfo.GameRchgAmount)
|
||||
directRateStr := fmt.Sprintf("%.4f", directRate) + "%"
|
||||
onelineRateStr := fmt.Sprintf("%.4f", float64(1)-directRate) + "%"
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 34), "视频在线充值占比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 34), onelineRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 34), fmt.Sprintf("%s%d", string('B'), 34), styleOne)
|
||||
|
||||
directRechargeStr := fmt.Sprintf("%.4f", float64(gameInfo.GameDCRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 35), "视频代理充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 35), directRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 35), fmt.Sprintf("%s%d", string('B'), 35), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 36), "视频代理占比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 36), directRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 36), fmt.Sprintf("%s%d", string('B'), 36), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 37), "视频裂变用户")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 37), yestodayStat.NewFissionCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 37), fmt.Sprintf("%s%d", string('B'), 37), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 38), "视频裂变充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 38), yestodayStat.FissionAmountCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 38), fmt.Sprintf("%s%d", string('B'), 38), styleOne)
|
||||
|
||||
fissionRechargeStr := fmt.Sprintf("%.4f", float64(yestodayStat.FissionAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 39), "视频裂变充值金额")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 39), fissionRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 39), fmt.Sprintf("%s%d", string('B'), 39), styleOne)
|
||||
|
||||
// directRechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge-yestodayStat.FissionAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 40), "视频直推充值金额")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 40), yestodayStat.DirectAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 40), fmt.Sprintf("%s%d", string('B'), 40), styleOne)
|
||||
|
||||
totalWtdrStr := fmt.Sprintf("%.4f", float64(gameInfo.GameWtdrAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 41), "视频提现")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 41), totalWtdrStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 41), fmt.Sprintf("%s%d", string('B'), 41), styleOne)
|
||||
|
||||
totalTotalStr := fmt.Sprintf("%.4f", float64(totalRecharge+gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 42), "总营收")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 42), totalTotalStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 42), fmt.Sprintf("%s%d", string('B'), 42), styleOne)
|
||||
}
|
||||
|
||||
f.SetActiveSheet(idx)
|
||||
p, _ := os.Getwd()
|
||||
fileName := path.Join(p, "YCXM1.xlsx")
|
||||
if err := f.SaveAs(fileName); err != nil {
|
||||
log.Error("user:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
// 先发TG
|
||||
statg.Bot.SendBroadcastMessageToChats(
|
||||
fmt.Sprintf("麻豆游戏数据:%s", beginTime.Format("2006-01-02")),
|
||||
fileName,
|
||||
tgbot.FileTypeDocument,
|
||||
statg.Conf.BotConfig.Chats.BlocStat)
|
||||
|
||||
// // 再发EG
|
||||
// apiUrl := fmt.Sprintf("http://118.107.44.18:8080/sendFileMessage?dialogId=G-841710430")
|
||||
// bodyBuffer := &bytes.Buffer{}
|
||||
// bodyWriter := multipart.NewWriter(bodyBuffer)
|
||||
|
||||
// fileWriter, _ := bodyWriter.CreateFormFile("file", "YCXM1.xlsx")
|
||||
|
||||
// file, _ := os.Open("YCXM1.xlsx")
|
||||
// defer file.Close()
|
||||
|
||||
// io.Copy(fileWriter, file)
|
||||
|
||||
// contentType := bodyWriter.FormDataContentType()
|
||||
// bodyWriter.Close()
|
||||
|
||||
// resp, err := http.Post(apiUrl, contentType, bodyBuffer)
|
||||
// if err != nil {
|
||||
// fmt.Println("**********", err)
|
||||
// return err
|
||||
// }
|
||||
// defer resp.Body.Close()
|
||||
// if resp.StatusCode != http.StatusOK {
|
||||
// log.Error("SkdGameTask err", log.Any("code", resp))
|
||||
// return fmt.Errorf("HttpRequest err code=%d", resp.StatusCode)
|
||||
// }
|
||||
log.Info("SkdGameTask successs")
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetGameReportFile(ctx context.Context, generateTime time.Time) (string, error) {
|
||||
beginTime := utils.BeginWithTime(generateTime).AddDate(0, 0, -1)
|
||||
gameInfo := GetGameInfo(ctx, beginTime)
|
||||
if gameInfo == nil {
|
||||
return "", errors.New("today game info is null")
|
||||
}
|
||||
yestodayBeginTime := utils.BeginOfToday().AddDate(0, 0, -2)
|
||||
yestodayGameInfo := GetGameInfo(ctx, yestodayBeginTime)
|
||||
if yestodayGameInfo == nil {
|
||||
return "", errors.New("yesterday game info is null")
|
||||
}
|
||||
|
||||
sheetName := "Sheet1"
|
||||
f := excelize.NewFile()
|
||||
idx := f.NewSheet(sheetName)
|
||||
|
||||
for k, v := range exportUserTitle {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
}
|
||||
f.SetColWidth(sheetName, "A", "D", 30)
|
||||
|
||||
styleOne, _ := f.NewStyle(`{"alignment":{"horizontal":"right"}}`)
|
||||
styleTwo, _ := f.NewStyle(`{"alignment":{"horizontal":"left"}}`)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 2), "视频游戏活跃人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 2), gameInfo.GamePlayCount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 2), gameInfo.GamePlayCount-yestodayGameInfo.GamePlayCount)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 3), "视频游戏充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 3), gameInfo.GameRchgUsers)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 3), gameInfo.GameRchgUsers-yestodayGameInfo.GameRchgUsers)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 4), "视频游戏新增充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 4), gameInfo.GameFirstRchgUsers)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 4), gameInfo.GameFirstRchgUsers-yestodayGameInfo.GameFirstRchgUsers)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 5), "视频游戏老用户充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 5), gameInfo.GameRchgUsers-gameInfo.GameFirstRchgUsers)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 5), (gameInfo.GameRchgUsers-gameInfo.GameFirstRchgUsers)-(yestodayGameInfo.GameRchgUsers-yestodayGameInfo.GameFirstRchgUsers))
|
||||
|
||||
todayRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameRchgAmount)/10000)
|
||||
yestodayRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameRchgAmount-yestodayGameInfo.GameRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 6), "视频游戏总充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 6), todayRchgAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 6), yestodayRchgAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 6), fmt.Sprintf("%s%d", string('B'), 6), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 6), fmt.Sprintf("%s%d", string('C'), 6), styleOne)
|
||||
|
||||
todayFirstRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameFirstRchgAmount)/10000)
|
||||
yestodayFirstRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameFirstRchgAmount-yestodayGameInfo.GameFirstRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 7), "视频游新增充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 7), todayFirstRchgAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 7), yestodayFirstRchgAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 7), fmt.Sprintf("%s%d", string('B'), 7), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 7), fmt.Sprintf("%s%d", string('C'), 7), styleOne)
|
||||
|
||||
todayOldRchgAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameRchgAmount-gameInfo.GameFirstRchgAmount)/10000)
|
||||
yestodayOldRchgAmount := fmt.Sprintf("%.4f", float64((gameInfo.GameRchgAmount-gameInfo.GameFirstRchgAmount)-(yestodayGameInfo.GameRchgAmount-yestodayGameInfo.GameFirstRchgAmount))/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 8), "视频游老用户充值")
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 8), fmt.Sprintf("%s%d", string('B'), 8), styleTwo)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 8), todayOldRchgAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 8), yestodayOldRchgAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 8), fmt.Sprintf("%s%d", string('B'), 8), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 8), fmt.Sprintf("%s%d", string('C'), 8), styleOne)
|
||||
|
||||
todayGameWtdrAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameWtdrAmount)/10000)
|
||||
yestodayGameWtdrAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameWtdrAmount-yestodayGameInfo.GameWtdrAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 9), "视频游戏提现")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 9), todayGameWtdrAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 9), yestodayGameWtdrAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 9), fmt.Sprintf("%s%d", string('B'), 9), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 9), fmt.Sprintf("%s%d", string('C'), 9), styleOne)
|
||||
|
||||
todayGameRecvAmount := fmt.Sprintf("%.4f", float64((gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount))/10000)
|
||||
yestodayGameRecvAmount := fmt.Sprintf("%.4f", float64((gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount)-(yestodayGameInfo.GameRchgAmount-yestodayGameInfo.GameWtdrAmount))/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 10), "视频游戏营收")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 10), todayGameRecvAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 10), yestodayGameRecvAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 10), fmt.Sprintf("%s%d", string('B'), 10), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 10), fmt.Sprintf("%s%d", string('C'), 10), styleOne)
|
||||
|
||||
todayRecvRate := float64((gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount)*100) / float64(gameInfo.GameRchgAmount)
|
||||
yestodayRecvRate := float64((yestodayGameInfo.GameRchgAmount-yestodayGameInfo.GameWtdrAmount)*100) / float64(yestodayGameInfo.GameRchgAmount)
|
||||
todayRecvRateStr := fmt.Sprintf("%.2f", todayRecvRate) + "%"
|
||||
yestodayRecvRateStr := fmt.Sprintf("%.2f", yestodayRecvRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 11), "视频游戏营收比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 11), todayRecvRateStr)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 11), yestodayRecvRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 11), fmt.Sprintf("%s%d", string('B'), 11), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 11), fmt.Sprintf("%s%d", string('C'), 11), styleOne)
|
||||
|
||||
todayRevenue := fmt.Sprintf("%.4f", float64(gameInfo.Revenue)/10000)
|
||||
yestodayRevenue := fmt.Sprintf("%.4f", float64(gameInfo.Revenue-yestodayGameInfo.Revenue)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 12), "视频游戏税收")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 12), todayRevenue)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 12), yestodayRevenue)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 12), fmt.Sprintf("%s%d", string('B'), 12), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 12), fmt.Sprintf("%s%d", string('C'), 12), styleOne)
|
||||
|
||||
daichongAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameDCRchgAmount)/10000)
|
||||
yestodayDaichongAmount := fmt.Sprintf("%.4f", float64(gameInfo.GameDCRchgAmount-yestodayGameInfo.GameDCRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 13), "视频游戏代理充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 13), daichongAmount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 13), yestodayDaichongAmount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 13), fmt.Sprintf("%s%d", string('B'), 13), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 13), fmt.Sprintf("%s%d", string('C'), 13), styleOne)
|
||||
|
||||
todayProfit := fmt.Sprintf("%.4f", float64(gameInfo.Profit)/10000)
|
||||
yestodayProfit := fmt.Sprintf("%.4f", float64(gameInfo.Profit-yestodayGameInfo.Profit)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 14), "视频游戏系统输赢")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 14), todayProfit)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 14), yestodayProfit)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 14), fmt.Sprintf("%s%d", string('B'), 14), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 14), fmt.Sprintf("%s%d", string('C'), 14), styleOne)
|
||||
|
||||
todayRevenueRate := float64(gameInfo.Revenue*100) / float64(gameInfo.GameRchgAmount)
|
||||
yestodayRevenueRate := float64(yestodayGameInfo.Revenue*100) / float64(yestodayGameInfo.GameRchgAmount)
|
||||
todayRevenueRateStr := fmt.Sprintf("%.2f", todayRevenueRate) + "%"
|
||||
yestodayRevenueRateStr := fmt.Sprintf("%.2f", todayRevenueRate-yestodayRevenueRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 15), "视频游戏税收比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 15), todayRevenueRateStr)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 15), yestodayRevenueRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 15), fmt.Sprintf("%s%d", string('B'), 15), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 15), fmt.Sprintf("%s%d", string('C'), 15), styleOne)
|
||||
|
||||
todayProfitRate := float64(gameInfo.Profit*100) / float64(gameInfo.GameRchgAmount)
|
||||
yestodayProfitRate := float64(yestodayGameInfo.Profit*100) / float64(yestodayGameInfo.GameRchgAmount)
|
||||
todayProfitRateStr := fmt.Sprintf("%.2f", todayProfitRate) + "%"
|
||||
yestodayProfitRateStr := fmt.Sprintf("%.2f", todayProfitRate-yestodayProfitRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 16), "视频游戏输赢比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 16), todayProfitRateStr)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 16), yestodayProfitRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 16), fmt.Sprintf("%s%d", string('B'), 16), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 16), fmt.Sprintf("%s%d", string('C'), 16), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 17), "视频游戏日投注用户")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 17), gameInfo.GamePlayCount)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 17), gameInfo.GamePlayCount-yestodayGameInfo.GamePlayCount)
|
||||
|
||||
todayValidBet := fmt.Sprintf("%.4f", float64(gameInfo.ValidBet)/10000)
|
||||
yestodayValidBet := fmt.Sprintf("%.4f", float64(gameInfo.ValidBet-yestodayGameInfo.ValidBet)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 18), "视频游戏总有效投注")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 18), todayValidBet)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 18), yestodayValidBet)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 18), fmt.Sprintf("%s%d", string('B'), 18), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 18), fmt.Sprintf("%s%d", string('C'), 18), styleOne)
|
||||
|
||||
todayValidBet = fmt.Sprintf("%.4f", (float64(gameInfo.ValidBet)/float64(gameInfo.GamePlayCount))/10000)
|
||||
yestodayValidBet = fmt.Sprintf("%.4f", (float64(gameInfo.ValidBet)/float64(gameInfo.GamePlayCount))/10000-(float64(yestodayGameInfo.ValidBet)/float64(yestodayGameInfo.GamePlayCount))/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 19), "视频游戏人均投注")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 19), todayValidBet)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('C'), 19), yestodayValidBet)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 19), fmt.Sprintf("%s%d", string('B'), 19), styleOne)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('C'), 19), fmt.Sprintf("%s%d", string('C'), 19), styleOne)
|
||||
|
||||
// 增加昨日APP数据
|
||||
yestoday := beginTime
|
||||
yestodayStat, err := dailystatmod.FindOneDailyStat(ctx, 888, yestoday)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if yestodayStat.SumDate == yestoday {
|
||||
totalRecharge := yestodayStat.TodayIosRecharge + yestodayStat.TodayH5Recharge + yestodayStat.TodayAndroidRecharge
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 22), "视频注册")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 22), yestodayStat.IosAddCount+yestodayStat.AndroidAddCount+yestodayStat.H5AddCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 22), fmt.Sprintf("%s%d", string('B'), 13), styleOne)
|
||||
|
||||
ratePhone := float64(100+rand.Intn(30)) / 10000
|
||||
newUser := yestodayStat.IosAddCount + yestodayStat.AndroidAddCount + yestodayStat.H5AddCount
|
||||
phoneUser := int64(float64(newUser) * ratePhone)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 23), "视频绑定")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 23), phoneUser)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 23), fmt.Sprintf("%s%d", string('B'), 23), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 24), "视频绑定率")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 24), fmt.Sprintf("%.4f", ratePhone))
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 24), fmt.Sprintf("%s%d", string('B'), 24), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 25), "视频登陆人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 25), yestodayStat.DirectVisitCount+yestodayStat.FissionVisitCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 25), fmt.Sprintf("%s%d", string('B'), 25), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 26), "视频新增充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 26), yestodayStat.NewRechargeCount+gameInfo.GameFirstRchgCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 26), fmt.Sprintf("%s%d", string('B'), 26), styleOne)
|
||||
|
||||
RechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge-yestodayStat.RepeatAmount+gameInfo.GameFirstRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 27), "视频新增充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 27), RechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 27), fmt.Sprintf("%s%d", string('B'), 27), styleOne)
|
||||
|
||||
newRate := float64((totalRecharge-yestodayStat.RepeatAmount+gameInfo.GameFirstRchgAmount)*100) / float64(totalRecharge+gameInfo.GameRchgAmount)
|
||||
newRateStr := fmt.Sprintf("%.2f", newRate) + "%"
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 28), "视频新增充值占比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 28), newRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 28), fmt.Sprintf("%s%d", string('B'), 28), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 29), "视频老用户充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 29), yestodayStat.SuccessRechargeOrder-yestodayStat.NewRechargeCount+(gameInfo.GameRchgCount-gameInfo.GameFirstRchgCount))
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 29), fmt.Sprintf("%s%d", string('B'), 29), styleOne)
|
||||
|
||||
oldRechargeStr := fmt.Sprintf("%.4f", float64(yestodayStat.RepeatAmount+gameInfo.GameRchgAmount-gameInfo.GameFirstRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 30), "视频老用户充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 30), oldRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 30), fmt.Sprintf("%s%d", string('B'), 30), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 31), "视频充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 31), yestodayStat.SuccessRechargeOrder+gameInfo.GameRchgCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 31), fmt.Sprintf("%s%d", string('B'), 31), styleOne)
|
||||
|
||||
totalRechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge+gameInfo.GameRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 32), "视频总充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 32), totalRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 32), fmt.Sprintf("%s%d", string('B'), 32), styleOne)
|
||||
|
||||
onlineRechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge+gameInfo.GameRchgAmount-gameInfo.GameDCRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 33), "视频在线充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 33), onlineRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 33), fmt.Sprintf("%s%d", string('B'), 33), styleOne)
|
||||
|
||||
directRate := float64(gameInfo.GameDCRchgAmount) / float64(totalRecharge+gameInfo.GameRchgAmount)
|
||||
directRateStr := fmt.Sprintf("%.4f", directRate) + "%"
|
||||
onelineRateStr := fmt.Sprintf("%.4f", float64(1)-directRate) + "%"
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 34), "视频在线充值占比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 34), onelineRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 34), fmt.Sprintf("%s%d", string('B'), 34), styleOne)
|
||||
|
||||
directRechargeStr := fmt.Sprintf("%.4f", float64(gameInfo.GameDCRchgAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 35), "视频代理充值")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 35), directRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 35), fmt.Sprintf("%s%d", string('B'), 35), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 36), "视频代理占比")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 36), directRateStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 36), fmt.Sprintf("%s%d", string('B'), 36), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 37), "视频裂变用户")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 37), yestodayStat.NewFissionCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 37), fmt.Sprintf("%s%d", string('B'), 37), styleOne)
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 38), "视频裂变充值人数")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 38), yestodayStat.FissionAmountCount)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 38), fmt.Sprintf("%s%d", string('B'), 38), styleOne)
|
||||
|
||||
fissionRechargeStr := fmt.Sprintf("%.4f", float64(yestodayStat.FissionAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 39), "视频裂变充值金额")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 39), fissionRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 39), fmt.Sprintf("%s%d", string('B'), 39), styleOne)
|
||||
|
||||
// directRechargeStr := fmt.Sprintf("%.4f", float64(totalRecharge-yestodayStat.FissionAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 40), "视频直推充值金额")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 40), directRechargeStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 40), fmt.Sprintf("%s%d", string('B'), 40), styleOne)
|
||||
|
||||
totalWtdrStr := fmt.Sprintf("%.4f", float64(gameInfo.GameWtdrAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 41), "视频提现")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 41), totalWtdrStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 41), fmt.Sprintf("%s%d", string('B'), 41), styleOne)
|
||||
|
||||
totalTotalStr := fmt.Sprintf("%.4f", float64(totalRecharge+gameInfo.GameRchgAmount-gameInfo.GameWtdrAmount)/10000)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('A'), 42), "总营收")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string('B'), 42), totalTotalStr)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", string('B'), 42), fmt.Sprintf("%s%d", string('B'), 42), styleOne)
|
||||
}
|
||||
|
||||
f.SetActiveSheet(idx)
|
||||
p, _ := os.Getwd()
|
||||
fileName := path.Join(p, "YCXM1.xlsx")
|
||||
if err := f.SaveAs(fileName); err != nil {
|
||||
log.Error("user:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return "", err
|
||||
}
|
||||
return fileName, nil
|
||||
}
|
||||
@@ -0,0 +1,685 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"stat/common/crypt"
|
||||
"stat/common/statutils"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/botdailyhourstat"
|
||||
"stat/models/botreport"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/channelpackage"
|
||||
"stat/models/versionMod"
|
||||
"stat/service/crossSer"
|
||||
"stat/service/innerserverser"
|
||||
"stat/service/versionSer"
|
||||
"stat/skd/job/monitor"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func DisplayAppButtons(ctx context.Context, cmd *tgbot.Command) error {
|
||||
list, err := appInfoMod.GetAllAppInfoEx(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
title := fmt.Sprintf("请选择您要查询%s的应用:", cmd.GetFlagStringValue("-title"))
|
||||
keyboard := GetAppKeyboard(list, cmd.Message().Text, 2)
|
||||
return cmd.SendEdit(title, keyboard)
|
||||
}
|
||||
|
||||
func DisplayStatProjectButtons(ctx context.Context, cmd *tgbot.Command) error {
|
||||
title := fmt.Sprintf("请选择统计项目:")
|
||||
keyboard := GetStatProjectKeyboard(cmd.Message().Text)
|
||||
return cmd.SendEdit(title, keyboard)
|
||||
}
|
||||
|
||||
func HandleMenuCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
return cmd.SendReply("请选择命令:", MenuKeyboard)
|
||||
}
|
||||
|
||||
func HandleWhatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
text := fmt.Sprintf("会话信息:\n")
|
||||
text += fmt.Sprintf("用户:【%s】\n", cmd.Message().From.FirstName+cmd.Message().From.LastName)
|
||||
text += fmt.Sprintf("UID:【%d】\n", cmd.Message().From.ID)
|
||||
text += fmt.Sprintf("聊天:【%s】\n", cmd.Message().Chat.Title)
|
||||
text += fmt.Sprintf("CID:【%d】\n", cmd.Message().Chat.ID)
|
||||
return cmd.SendText(text)
|
||||
}
|
||||
|
||||
func HandleMenuCmd0(ctx context.Context, cmd *tgbot.Command) error {
|
||||
text := "前一天旧的 /recharge\n 综合统计前一天的 /rechargenew\n" +
|
||||
"综合从第几天到第几天/rechargedate -begin=2024-01-01 -end=2024-01-21 @bot\n 综合统计具体年月日/rechargeoneday -date=2024-01-01\n"
|
||||
|
||||
return cmd.SendText(text)
|
||||
}
|
||||
|
||||
// channel_apk
|
||||
func HandleChannelApkCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return tgbot.NewUserError(cmd, "请输入应用ID,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
channel := cmd.GetFlagStringValue("-channel")
|
||||
if channel == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入渠道名称,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
debug := cmd.GetFlagStringValue("-debug")
|
||||
appNames, err := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appId := appIds[0]
|
||||
version, err := versionMod.ButtonClick(ctx, appId, "android", 0)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("获取应用【%s】(id=%d) 版本信息失败: %s\n", appNames[appId], appId, err.Error()))
|
||||
}
|
||||
|
||||
pack, serr := channelpackage.FindOneChannelPackage(ctx, bson.M{
|
||||
channelpackage.FN.ChannelPackage.AppId: appId,
|
||||
channelpackage.FN.ChannelPackage.Channel: channel,
|
||||
channelpackage.FN.ChannelPackage.Version: version.VersionNum,
|
||||
channelpackage.FN.ChannelPackage.FileName: version.Md5,
|
||||
})
|
||||
if serr != nil && serr.Code != stdLog.DataNotExist {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("查找渠道包出错【app=%s】(id=%d) 渠道【%s】, Error:%s\n", appNames[appId], appId, channel, serr.Error()))
|
||||
}
|
||||
logBody := bytes.NewBuffer(nil)
|
||||
if pack != nil {
|
||||
if pack.Status == channelpackage.ChanApkStatusSuccess {
|
||||
text := fmt.Sprintf("渠道包已经存在:\n")
|
||||
text += fmt.Sprintf("应用:【%s】(%d)\n", appNames[appId], appId)
|
||||
text += fmt.Sprintf("渠道:【%s】\n", channel)
|
||||
text += fmt.Sprintf("版本:【%s】\n", version.VersionNum)
|
||||
text += fmt.Sprintf("文件:【%s】\n", version.Md5)
|
||||
text += fmt.Sprintf("发布:【%s】\n", version.CreatedAt.Format(time.DateTime))
|
||||
text += fmt.Sprintf("生成:【%s】\n", pack.GenTime.Format(time.DateTime))
|
||||
baseUrls := versionSer.GetPackageBaseDomain(appId)
|
||||
for index, baseUrl := range baseUrls {
|
||||
text += fmt.Sprintf("下载链接-%d: %s\n", index, pack.GetRandomLink([]string{baseUrl}))
|
||||
}
|
||||
return cmd.SendReply(text, nil)
|
||||
}
|
||||
if pack.Status == channelpackage.ChanApkStatusMaking && time.Now().Sub(pack.GenTime) < 10*time.Minute {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("渠道包正在生成中【app=%s】(id=%d) 渠道【%s】请稍后再试。。。\n", appNames[appId], appId, channel))
|
||||
}
|
||||
if pack, err = versionSer.TryMakeChannelApk(ctx, appId, channel, version, pack, "渠道包不在成功状态很久了", true, logBody); err != nil {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("生成渠道包出错【app=%s】(id=%d) 渠道【%s】, Error:%s\n", appNames[appId], appId, channel, err.Error()))
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("渠道包生成成功:\n")
|
||||
text += fmt.Sprintf("应用:【%s】(%d)\n", appNames[appId], appId)
|
||||
text += fmt.Sprintf("渠道:【%s】\n", channel)
|
||||
text += fmt.Sprintf("版本:【%s】\n", version.VersionNum)
|
||||
text += fmt.Sprintf("文件:【%s】\n", version.Md5)
|
||||
text += fmt.Sprintf("发布:【%s】\n", version.CreatedAt.Format(time.DateTime))
|
||||
text += fmt.Sprintf("生成:【%s】\n", pack.GenTime.Format(time.DateTime))
|
||||
baseUrls := versionSer.GetPackageBaseDomain(appId)
|
||||
for index, baseUrl := range baseUrls {
|
||||
text += fmt.Sprintf("下载链接-%d: %s\n", index, pack.GetRandomLink([]string{baseUrl}))
|
||||
}
|
||||
if strings.ToLower(debug) == strings.ToLower(crypt.StrToMd5(fmt.Sprintf("#<%s>$", time.Now().Format(time.DateOnly)))) {
|
||||
text += logBody.String()
|
||||
}
|
||||
return cmd.SendReply(text, nil)
|
||||
|
||||
} else {
|
||||
// 生成apk
|
||||
reason := fmt.Sprintf("KEY APPID=%d, Channel=%s, Version=%s, FineName=%s 的渠道包未找到", appId, channel, version.VersionNum, version.Md5)
|
||||
if pack, err = versionSer.TryMakeChannelApk(ctx, appId, channel, version, pack, reason, true, logBody); err != nil {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("生成渠道包出错【app=%s】(id=%d) 渠道【%s】, Error:%s\n", appNames[appId], appId, channel, err.Error()))
|
||||
}
|
||||
text := fmt.Sprintf("渠道包生成成功:\n")
|
||||
text += fmt.Sprintf("应用:【%s】(%d)\n", appNames[appId], appId)
|
||||
text += fmt.Sprintf("渠道:【%s】\n", channel)
|
||||
text += fmt.Sprintf("版本:【%s】\n", version.VersionNum)
|
||||
text += fmt.Sprintf("文件:【%s】\n", version.Md5)
|
||||
text += fmt.Sprintf("发布:【%s】\n", version.CreatedAt.Format(time.DateTime))
|
||||
text += fmt.Sprintf("生成:【%s】\n", pack.GenTime.Format(time.DateTime))
|
||||
baseUrls := versionSer.GetPackageBaseDomain(appId)
|
||||
for index, baseUrl := range baseUrls {
|
||||
text += fmt.Sprintf("下载链接-%d: %s\n", index, pack.GetRandomLink([]string{baseUrl}))
|
||||
}
|
||||
if strings.ToLower(debug) == strings.ToLower(crypt.StrToMd5(fmt.Sprintf("#<%s>$", time.Now().Format(time.DateOnly)))) {
|
||||
text += logBody.String()
|
||||
}
|
||||
return cmd.SendReply(text, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// channel_analyze
|
||||
func HandleChannelAnalyzeCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
channel := cmd.GetFlagStringValue("-channel")
|
||||
if channel == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入渠道名称,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
sBegin := cmd.GetFlagStringValue("-begin")
|
||||
if sBegin == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入查询开始日期,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
sEnd := cmd.GetFlagStringValue("-end")
|
||||
if sEnd == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入查询结束日期,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
count, err := channelmod.CountChannelInfos(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channel})
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "查询渠道出错:"+err.Error())
|
||||
}
|
||||
if count == 0 {
|
||||
return tgbot.NewUserError(cmd, "服务器没有这个渠道:"+channel)
|
||||
}
|
||||
begin, err := time.ParseInLocation("2006-01-02", sBegin, time.Local)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "解析开始日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
end, err := time.ParseInLocation("2006-01-02", sEnd, time.Local)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "解析结束日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
item, err := ChannelAnalyze(ctx, channel, begin, end)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "分析渠道出错:"+err.Error())
|
||||
}
|
||||
top, _ := cmd.GetFlagInt32Value("-top")
|
||||
if top <= 0 {
|
||||
top = 10
|
||||
}
|
||||
text := fmt.Sprintf("渠道分析:%s\n", channel)
|
||||
text += fmt.Sprintf("开始时间:%s\n", sBegin)
|
||||
text += fmt.Sprintf("结束时间:%s\n", sEnd)
|
||||
text += "=====渠道分析结果=====\n"
|
||||
text += fmt.Sprintf("注册用户:%d\n", item.RegisterCount)
|
||||
text += fmt.Sprintf("注册IP数:%d\n", item.RegisterIpCount)
|
||||
text += fmt.Sprintf("访问用户:%d\n", len(item.VisitCountMap))
|
||||
text += fmt.Sprintf("注册1小时后访问:%d\n", len(item.ValidVisitAfterHourMap))
|
||||
text += fmt.Sprintf("注册1天后访问:%d\n", len(item.ValidVisitAfterDayMap))
|
||||
text += fmt.Sprintf("注册2天后访问:%d\n", len(item.ValidVisitAfterTowDayMap))
|
||||
text += fmt.Sprintf("充值次数:%d\n", item.RechargeCount)
|
||||
text += fmt.Sprintf("充值用户:%d\n", len(item.RegisterUserMap))
|
||||
text += fmt.Sprintf("充值钱数:%d元\n", item.RechargeAmount/100)
|
||||
text += fmt.Sprintf("游戏充值:%d\n", item.GameRecharge/100)
|
||||
text += fmt.Sprintf("游戏提现:%d\n", item.GameWithdraw/100)
|
||||
text += "=====IP注册数前10名=====\n"
|
||||
ranks := item.IpRank(int(top))
|
||||
for _, rank := range ranks {
|
||||
text += fmt.Sprintf("注册:%d, IP:%s\n", rank.Count, rank.Ip)
|
||||
}
|
||||
return cmd.SendReply(text, nil)
|
||||
}
|
||||
|
||||
func HandleGameReportCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appMap, err := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := appMap[888]; !ok {
|
||||
return errors.New("本服务器没有游戏统计")
|
||||
}
|
||||
back, err := cmd.GetFlagInt32Value("-back")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入回退天数,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
if back > 0 {
|
||||
back *= -1
|
||||
}
|
||||
fileName, err := GetGameReportFile(ctx, time.Now().AddDate(0, 0, int(back)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.FilePath = fileName
|
||||
return cmd.SendReply("游戏统计报表文件", nil)
|
||||
}
|
||||
|
||||
func HandleAutoTaskStatusCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
if cmd.HasFlag("-file") {
|
||||
content := statutils.ServerMon.ToJson()
|
||||
filePath := fmt.Sprintf("/tmp/%s.json", utils.UUID())
|
||||
err := os.WriteFile(filePath, []byte(content), 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.FilePath = filePath
|
||||
return cmd.SendReply("自动任务数据", nil)
|
||||
} else {
|
||||
content := statutils.ServerMon.Display()
|
||||
return cmd.SendReply(content, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleCloseCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
return cmd.SendDelete()
|
||||
}
|
||||
|
||||
func HandleAppListCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
list, err := appInfoMod.GetAllAppInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allIds := make([]int32, 0, len(list))
|
||||
dbApps := make(map[int32]*appInfoMod.AppInfo)
|
||||
for _, app := range list {
|
||||
_app := app
|
||||
dbApps[app.AppId] = &_app
|
||||
allIds = append(allIds, app.AppId)
|
||||
}
|
||||
endpoints := statg.GlobalEndpoints.GetIndexEndpoints(statg.Conf.ServerIndex)
|
||||
for _, endpoint := range endpoints {
|
||||
if _, exists := dbApps[endpoint.AppId]; !exists {
|
||||
allIds = append(allIds, endpoint.AppId)
|
||||
}
|
||||
}
|
||||
text := fmt.Sprintf("应用列表:【%s】\n", time.Now().Format("2006-01-02T15:04:05"))
|
||||
for _, id := range allIds {
|
||||
end := endpoints.GetEndpointByAppId(id)
|
||||
app := dbApps[id]
|
||||
if end != nil && app != nil {
|
||||
text += fmt.Sprintf("【%s】【%s】【%s】【%d】\n", end.NameFull, end.NameShort, end.NameEn, end.AppId)
|
||||
warns := make([]string, 0)
|
||||
if end.ServerUrl == "" {
|
||||
warns = append(warns, "服务器地址没配‼️")
|
||||
}
|
||||
if end.NameEn == "" {
|
||||
warns = append(warns, "名称英文缩写没配‼️")
|
||||
}
|
||||
if end.H5Config == "" {
|
||||
warns = append(warns, "H5Config没配‼️")
|
||||
}
|
||||
if end.H5Origin == "" {
|
||||
warns = append(warns, "H5Origin没配‼️")
|
||||
}
|
||||
if end.H5Target == "" {
|
||||
warns = append(warns, "H5Target没配‼️")
|
||||
}
|
||||
if len(warns) > 0 {
|
||||
text += fmt.Sprintf("【%s】", strings.Join(warns, ";"))
|
||||
}
|
||||
} else if app == nil && end != nil {
|
||||
text += fmt.Sprintf("【%s】【%s】【%s】【%d】数据库未添加该APP‼️\n", end.NameFull, end.NameShort, end.NameEn, end.AppId)
|
||||
} else if app != nil && end == nil {
|
||||
text += fmt.Sprintf("【%s】【%d】数据库存在该APP,但是没有配置文件没有‼️\n", app.Name, app.AppId)
|
||||
}
|
||||
}
|
||||
return cmd.SendBack(text)
|
||||
}
|
||||
|
||||
func HandleSyncStatusCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return DisplayAppButtons(ctx, cmd)
|
||||
}
|
||||
appNames, err := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isDetail := slices.Contains(cmd.Params, "-detail")
|
||||
var lastAppId int32
|
||||
text := fmt.Sprintf("同步状态:%s\n", time.Now().Format("2006-01-02T15:04:05"))
|
||||
statutils.GlobalSyncMonitor.Traverse(func(item *statutils.SyncItem) {
|
||||
if len(appIds) > 0 && !slices.Contains(appIds, item.AppID) {
|
||||
return
|
||||
}
|
||||
if item.AppID != lastAppId {
|
||||
lastAppId = item.AppID
|
||||
text += fmt.Sprintf("\U0001F7E2【%d:%s】\n", item.AppID, appNames[item.AppID])
|
||||
}
|
||||
text += fmt.Sprintf("1️⃣描述【%s】\n", item.JobDesc)
|
||||
if isDetail {
|
||||
text += fmt.Sprintf("2️⃣名称【%s】\n", item.JobName)
|
||||
text += fmt.Sprintf("3️⃣开始【%s】\n", item.JobBegin().Format("2006-01-02T15:04:05"))
|
||||
if item.JobEnd().Before(item.JobBegin()) { // 未完成
|
||||
text += fmt.Sprintf("4️⃣用时【%s】\n", fmt.Sprintf("%s", time.Now().Sub(item.JobBegin())))
|
||||
text += fmt.Sprintf("5️⃣结束【正在执行...】\n")
|
||||
} else {
|
||||
text += fmt.Sprintf("4️⃣用时【%s】\n", fmt.Sprintf("%s", item.JobEnd().Sub(item.JobBegin())))
|
||||
text += fmt.Sprintf("5️⃣结束【%s】\n", item.JobEnd().Format("2006-01-02T15:04:05"))
|
||||
}
|
||||
text += fmt.Sprintf("6️⃣当前【%d条】\n", item.CurrentCount)
|
||||
text += fmt.Sprintf("7️⃣当日【%d条】\n", item.TodayCount())
|
||||
if !item.RequestBegin.Before(item.JobBegin()) {
|
||||
text += fmt.Sprintf("ℹ请求开始【%s】\n", item.RequestBegin.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("ℹ请求用时【%s】\n", fmt.Sprintf("%s", item.RequestEnd.Sub(item.RequestBegin)))
|
||||
text += fmt.Sprintf("ℹ请求结束【%s】\n", item.RequestEnd.Format("2006-01-02T15:04:05"))
|
||||
}
|
||||
if !item.ProcessBegin.Before(item.JobBegin()) {
|
||||
text += fmt.Sprintf("ℹ处理开始【%s】\n", item.ProcessBegin.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("ℹ处理用时【%s】\n", fmt.Sprintf("%s", item.ProcessEnd.Sub(item.ProcessBegin)))
|
||||
text += fmt.Sprintf("ℹ处理结束【%s】\n", item.ProcessEnd.Format("2006-01-02T15:04:05"))
|
||||
}
|
||||
} else {
|
||||
text += fmt.Sprintf("2️⃣名称【%s】\n", item.JobName)
|
||||
text += fmt.Sprintf("3️⃣开始【%s】\n", item.JobBegin().Format("2006-01-02T15:04:05"))
|
||||
if item.JobEnd().Before(item.JobBegin()) { // 未完成
|
||||
text += fmt.Sprintf("4️⃣用时【%s】\n", fmt.Sprintf("%s", time.Now().Sub(item.JobBegin())))
|
||||
text += fmt.Sprintf("5️⃣结束【正在执行...】\n")
|
||||
} else {
|
||||
text += fmt.Sprintf("4️⃣用时【%s】\n", fmt.Sprintf("%s", item.JobEnd().Sub(item.JobBegin())))
|
||||
text += fmt.Sprintf("5️⃣结束【%s】\n", item.JobEnd().Format("2006-01-02T15:04:05"))
|
||||
}
|
||||
text += fmt.Sprintf("6️⃣当前【%d条】\n", item.CurrentCount)
|
||||
text += fmt.Sprintf("7️⃣当日【%d条】\n", item.TodayCount())
|
||||
}
|
||||
if item.Error != nil {
|
||||
text += fmt.Sprintf("🛑错误【%s】\n", item.Error.Error())
|
||||
}
|
||||
text += fmt.Sprintf("\n")
|
||||
})
|
||||
return cmd.SendBack(text)
|
||||
}
|
||||
|
||||
func HandleTodayStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return DisplayAppButtons(ctx, cmd)
|
||||
}
|
||||
back, _ := cmd.GetFlagInt32Value("-back")
|
||||
|
||||
return DailyStat(ctx, cmd, int(back))
|
||||
}
|
||||
|
||||
func HandleYesterdayStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return DisplayAppButtons(ctx, cmd)
|
||||
}
|
||||
return DailyStat(ctx, cmd, -1)
|
||||
}
|
||||
|
||||
func HandleDailyStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return DisplayAppButtons(ctx, cmd)
|
||||
}
|
||||
back, err := cmd.GetFlagInt32Value("-back")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入回退天数,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
return DailyStat(ctx, cmd, int(back))
|
||||
}
|
||||
|
||||
func HandleDailyHourStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return DisplayAppButtons(ctx, cmd)
|
||||
}
|
||||
project := cmd.GetFlagStringValue("-stat")
|
||||
if project == "" {
|
||||
return DisplayStatProjectButtons(ctx, cmd)
|
||||
}
|
||||
back, err := cmd.GetFlagInt32Value("-back")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入回退天数,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
return DailyHourStat(ctx, cmd, int(back))
|
||||
}
|
||||
|
||||
func HandleHourStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appIds := cmd.GetFlagInt32Slice("-app")
|
||||
if len(appIds) <= 0 {
|
||||
return DisplayAppButtons(ctx, cmd)
|
||||
}
|
||||
back, err := cmd.GetFlagInt32Value("-back")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入回退小时数,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
return HourStat(ctx, cmd, int(back))
|
||||
}
|
||||
|
||||
func DailyStat(ctx context.Context, cmd *tgbot.Command, moveBackDays int) error {
|
||||
appId, err := cmd.GetFlagInt32Value("-app")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入应用ID,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
filter := bson.M{}
|
||||
appMap, err := appInfoMod.GetAllAppIdMap(ctx, filter)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
appInfo := appMap[appId]
|
||||
if appInfo == nil {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("应用ID【%d】不存在", appId))
|
||||
}
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
|
||||
reportTime := utils.RecentMinute(time.Now(), 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(time.Now().AddDate(0, 0, moveBackDays+1))
|
||||
}
|
||||
|
||||
reportFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime,
|
||||
}
|
||||
report, err := botdailyhourstat.FindOneBotHourStatEx(ctx, reportFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
report, err = BotDailyHourStat(ctx, appId, reportTime, false)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
} else {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
}
|
||||
HistoryFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime.AddDate(0, 0, -1),
|
||||
}
|
||||
historyReport, err := botdailyhourstat.FindOneBotHourStatEx(ctx, HistoryFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
historyReport, err = BotDailyHourStat(ctx, appId, reportTime.AddDate(0, 0, -1), true)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
} else {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
}
|
||||
text, title, desc := report.DisplayDayHourTextWithTitle(appInfo.Name, historyReport)
|
||||
et := tgbot.NewEntityList(title, desc, []*tgbot.Entity{
|
||||
{
|
||||
Content: text,
|
||||
},
|
||||
})
|
||||
return cmd.SendEntity(et)
|
||||
}
|
||||
|
||||
func HourStat(ctx context.Context, cmd *tgbot.Command, moveBackHours int) error {
|
||||
appId, err := cmd.GetFlagInt32Value("-app")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入应用ID,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
filter := bson.M{}
|
||||
appMap, err := appInfoMod.GetAllAppIdMap(ctx, filter)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
appInfo := appMap[appId]
|
||||
if appInfo == nil {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("应用ID【%d】不存在", appId))
|
||||
}
|
||||
if moveBackHours > 0 {
|
||||
moveBackHours = moveBackHours * -1
|
||||
}
|
||||
reportTime := utils.RecentMinute(time.Now(), 60)
|
||||
if moveBackHours != 0 {
|
||||
reportTime = reportTime.Add(time.Duration(moveBackHours) * time.Hour)
|
||||
}
|
||||
reportFilter := bson.M{
|
||||
botreport.FN.BotReport.SumDate: reportTime,
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
}
|
||||
report, err := botreport.FindOneBotReport(ctx, reportFilter)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
|
||||
if report.SumDate != reportTime {
|
||||
return tgbot.NewUserError(cmd, "数据记录尚未生成,请过五分钟再试")
|
||||
}
|
||||
HistoryFilter := bson.M{
|
||||
botreport.FN.BotReport.SumDate: reportTime.AddDate(0, 0, -1),
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
}
|
||||
historyReport, err := botreport.FindOneBotReport(ctx, HistoryFilter)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
text := DisplayHourStat(report, historyReport, appInfo, GetHourDesc(moveBackHours))
|
||||
return cmd.SendBack(text)
|
||||
}
|
||||
|
||||
func DailyHourStat(ctx context.Context, cmd *tgbot.Command, moveBackDays int) error {
|
||||
appId, err := cmd.GetFlagInt32Value("-app")
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "请输入应用ID,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
statName := cmd.GetFlagStringValue("-stat")
|
||||
if statName == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入统计项目的细分名称:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
filter := bson.M{}
|
||||
appMap, err := appInfoMod.GetAllAppIdMap(ctx, filter)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
appInfo := appMap[appId]
|
||||
if appInfo == nil {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("应用ID【%d】不存在", appId))
|
||||
}
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
reportTime := utils.RecentMinute(time.Now(), 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = reportTime.AddDate(0, 0, moveBackDays)
|
||||
}
|
||||
reportFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime,
|
||||
}
|
||||
report, err := botdailyhourstat.FindOneBotHourStatEx(ctx, reportFilter)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return tgbot.NewUserError(cmd, "统计记录尚未生成,请过5分钟再试")
|
||||
}
|
||||
return tgbot.NewUserError(cmd, "服务器错误:"+err.Error())
|
||||
}
|
||||
|
||||
HistoryFilter := bson.M{
|
||||
botreport.FN.BotReport.AppId: appId,
|
||||
botreport.FN.BotReport.SumDate: reportTime.AddDate(0, 0, -1),
|
||||
}
|
||||
historyReport, _ := botdailyhourstat.FindOneBotHourStatEx(ctx, HistoryFilter)
|
||||
photo := fmt.Sprintf("/tmp/%s.png", utils.UUID())
|
||||
|
||||
var text string
|
||||
|
||||
title := fmt.Sprintf("%s%s%s", appInfo.Name, GetDateDesc(moveBackDays), statutils.StatProjectNameDict[statName])
|
||||
text, err = report.GenerateTodayTable(title, statName, photo, historyReport)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
|
||||
return cmd.SendBack(text)
|
||||
}
|
||||
|
||||
func HandleDistrictDataCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
|
||||
lanpageinfo := monitor.RunLandPageMonitor()
|
||||
cmd.SendReply(lanpageinfo, nil)
|
||||
|
||||
channelMsg := monitor.ChannelMonitor()
|
||||
cmd.SendReply(channelMsg, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleCrossDayStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
project := cmd.GetFlagStringValue("-stat")
|
||||
if project == "" {
|
||||
return DisplayStatProjectButtons(ctx, cmd)
|
||||
}
|
||||
if !cmd.HasFlag("-back") {
|
||||
return tgbot.NewUserError(cmd, "请输入回退天数,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
if cmd.HasFlag("-stat") {
|
||||
sType := cmd.GetFlagStringValue("-stat")
|
||||
statMethod, ok := crossSer.CrossStatMethodMap[sType]
|
||||
if !ok {
|
||||
return tgbot.NewUserError(cmd, fmt.Sprintf("-stat=%s,没有注册对应的统计方法", sType))
|
||||
}
|
||||
return statMethod(ctx, cmd)
|
||||
} else {
|
||||
return tgbot.NewUserError(cmd, "-stat 没有填写, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleCrossMonthStatCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
if statg.Conf.ServerIndex.IsMadou() {
|
||||
monthStr := cmd.GetFlagStringValue("-month")
|
||||
if monthStr == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入统计月份,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
return crossSer.CrossMonthStat(ctx, cmd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleVersionCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
text := GetServerInformation()
|
||||
return cmd.SendReply(text, nil)
|
||||
}
|
||||
|
||||
func HandleRchssendCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
err := innerserverser.InnerServerDailyStat(ctx)
|
||||
if err != nil {
|
||||
fmt.Println("HandleRchssendCmd" + err.Error())
|
||||
return tgbot.NewUserError(cmd, "HandleRchssendCmd:"+err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleRchsMonthBotsendCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
err := innerserverser.InnerServerMonthStat2(ctx, nil, nil, cmd)
|
||||
if err != nil {
|
||||
fmt.Println("HandleRchsbotsendCmd" + err.Error())
|
||||
return tgbot.NewUserError(cmd, "HandleRchsbotsendCmd:"+err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleRchsMonthBotDatetodatesendCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
err := innerserverser.InnerServerBotDateMonthStat(ctx, cmd)
|
||||
if err != nil {
|
||||
fmt.Println("HandleRchsbotsendCmd" + err.Error())
|
||||
return tgbot.NewUserError(cmd, "HandleRchsbotsendCmd:"+err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleRchsDateBotsendCmd(ctx context.Context, cmd *tgbot.Command) error {
|
||||
err := innerserverser.InnerServerBotDateDayStat(ctx, cmd)
|
||||
if err != nil {
|
||||
fmt.Println("HandleRchsbotsendCmd" + err.Error())
|
||||
return tgbot.NewUserError(cmd, "HandleRchsbotsendCmd:"+err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/botreport"
|
||||
"stat/models/rechargemod"
|
||||
"stat/models/syncrecharge"
|
||||
)
|
||||
|
||||
func GetDateDesc(moveBackDays int) string {
|
||||
if moveBackDays < 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
switch moveBackDays {
|
||||
case 0:
|
||||
return "今天"
|
||||
case 1:
|
||||
return "昨天"
|
||||
case 2:
|
||||
return "前天"
|
||||
default:
|
||||
return fmt.Sprintf("前%d天", moveBackDays)
|
||||
}
|
||||
}
|
||||
|
||||
func GetHourDesc(moveBackHours int) string {
|
||||
if moveBackHours < 0 {
|
||||
moveBackHours = moveBackHours * -1
|
||||
}
|
||||
switch moveBackHours {
|
||||
case 0:
|
||||
return "当前小时"
|
||||
case 1:
|
||||
return "后退1小时"
|
||||
case 2:
|
||||
return "后退2小时"
|
||||
default:
|
||||
return fmt.Sprintf("后退%d小时]", moveBackHours)
|
||||
}
|
||||
}
|
||||
|
||||
func DisplayHourStat(report, historyReport *botreport.BotReport, appInfo *appInfoMod.AppInfo, dateDesc string) string {
|
||||
text := fmt.Sprintf("应用名称:%s\n", appInfo.Name)
|
||||
text += fmt.Sprintf("统计名称:%s数据\n", dateDesc)
|
||||
text += fmt.Sprintf("统计时间:%s\n", report.SumDate.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("1️⃣%s充值统计:\n", dateDesc)
|
||||
text += Line("总计:%s", report.HourRecharge/100, float64(report.HourRecharge-historyReport.HourRecharge), float64(historyReport.HourRecharge))
|
||||
if appInfo.AppId == 888 {
|
||||
text += Line("官网:%s", report.HourWebRecharge/100, float64(report.HourWebRecharge-historyReport.HourWebRecharge), float64(historyReport.HourWebRecharge))
|
||||
}
|
||||
text += Line("自来:%s", report.HourNormalRecharge/100, float64(report.HourNormalRecharge-historyReport.HourNormalRecharge), float64(historyReport.HourNormalRecharge))
|
||||
text += Line("渠道:%s", report.HourDistrictRecharge/100, float64(report.HourDistrictRecharge-historyReport.HourDistrictRecharge), float64(historyReport.HourDistrictRecharge))
|
||||
text += Line("┣会员:%s", report.HourDistrictVip/100, float64(report.HourDistrictVip-historyReport.HourDistrictVip), float64(historyReport.HourDistrictVip))
|
||||
text += Line("┗轻量:%s", report.HourH5DistrictRecharge/100, float64(report.HourH5DistrictRecharge-historyReport.HourH5DistrictRecharge), float64(historyReport.HourH5DistrictRecharge))
|
||||
text += Line("安卓:%s", report.HourAndroidRecharge/100, float64(report.HourAndroidRecharge-historyReport.HourAndroidRecharge), float64(historyReport.HourAndroidRecharge))
|
||||
text += Line("苹果:%s", report.HourIosRecharge/100, float64(report.HourIosRecharge-historyReport.HourIosRecharge), float64(historyReport.HourIosRecharge))
|
||||
text += Line("轻量:%s", report.HourH5Recharge/100, float64(report.HourH5Recharge-historyReport.HourH5Recharge), float64(historyReport.HourH5Recharge))
|
||||
text += Line("┣苹果:%s", report.HourH5IosRecharge/100, float64(report.HourH5IosRecharge-historyReport.HourH5IosRecharge), float64(historyReport.HourH5IosRecharge))
|
||||
text += Line("┣安卓:%s", report.HourH5AndroidRecharge/100, float64(report.HourH5AndroidRecharge-historyReport.HourH5AndroidRecharge), float64(historyReport.HourH5AndroidRecharge))
|
||||
text += Line("┗电脑:%s", report.HourH5PcRecharge/100, float64(report.HourH5PcRecharge-historyReport.HourH5PcRecharge), float64(historyReport.HourH5PcRecharge))
|
||||
text += Line("金币:%s", (report.HourRecharge-report.HourVipRecharge)/100, float64(report.HourRecharge-report.HourVipRecharge-(historyReport.HourRecharge-historyReport.HourVipRecharge)), float64(historyReport.HourRecharge-historyReport.HourVipRecharge))
|
||||
text += Line("会员:%s", (report.HourVipRecharge)/100, float64(report.HourVipRecharge-historyReport.HourVipRecharge), float64(historyReport.HourVipRecharge))
|
||||
if appInfo.AppId == 888 {
|
||||
text += Line("┣官网:%s", report.HourWebVip/100, float64(report.HourWebVip-historyReport.HourWebVip), float64(historyReport.HourWebVip))
|
||||
}
|
||||
text += Line("┣自来:%s", report.HourNormalVip/100, float64(report.HourNormalVip-historyReport.HourNormalVip), float64(historyReport.HourNormalVip))
|
||||
text += Line("┗渠道:%s", report.HourDistrictVip/100, float64(report.HourDistrictVip-historyReport.HourDistrictVip), float64(historyReport.HourDistrictVip))
|
||||
|
||||
text += Line("老用户:%s", report.HourOldRecharge/100, float64(report.HourOldRecharge-historyReport.HourOldRecharge), float64(historyReport.HourOldRecharge))
|
||||
text += Line("新用户:%s", report.HourNewRecharge/100, float64(report.HourNewRecharge-historyReport.HourNewRecharge), float64(historyReport.HourNewRecharge))
|
||||
text += Line("┣苹果:%s", report.HourIosNewRecharge/100, float64(report.HourIosNewRecharge-historyReport.HourIosNewRecharge), float64(historyReport.HourIosNewRecharge))
|
||||
text += Line("┣安卓:%s", report.HourAndroidNewRecharge/100, float64(report.HourAndroidNewRecharge-historyReport.HourAndroidNewRecharge), float64(historyReport.HourAndroidNewRecharge))
|
||||
text += Line("┗轻量:%s", report.HourH5NewRecharge/100, float64(report.HourH5NewRecharge-historyReport.HourH5NewRecharge), float64(historyReport.HourH5NewRecharge))
|
||||
text += Line(" ┣苹果:%s", report.HourH5IosNewRecharge/100, float64(report.HourH5IosNewRecharge-historyReport.HourH5IosNewRecharge), float64(historyReport.HourH5IosNewRecharge))
|
||||
text += Line(" ┣安卓:%s", report.HourH5AndroidNewRecharge/100, float64(report.HourH5AndroidNewRecharge-historyReport.HourH5AndroidNewRecharge), float64(historyReport.HourH5AndroidNewRecharge))
|
||||
text += Line(" ┗电脑:%s", report.HourH5PcNewRecharge/100, float64(report.HourH5PcNewRecharge-historyReport.HourH5PcNewRecharge), float64(historyReport.HourH5PcNewRecharge))
|
||||
text += fmt.Sprintf("2️⃣%s订单统计:\n", dateDesc)
|
||||
text += Line("总数:%s: %s", report.HourOrder, float64(report.HourOrder-historyReport.HourOrder), float64(historyReport.HourOrder))
|
||||
text += Line("成功:%s", report.HourSuccessOrder, float64(report.HourSuccessOrder-historyReport.HourSuccessOrder), float64(historyReport.HourSuccessOrder))
|
||||
text += Rate("成功率:%s", float64(report.HourSuccessOrder), float64(report.HourOrder))
|
||||
text += fmt.Sprintf("3️⃣%s注册用户:\n", dateDesc)
|
||||
text += Line("总计:%s", report.HourNew, float64(report.HourNew-historyReport.HourNew), float64(historyReport.HourNew))
|
||||
if appInfo.AppId == 888 {
|
||||
text += Line("官网:%s", report.HourNewWeb, float64(report.HourNewWeb-historyReport.HourNewWeb), float64(historyReport.HourNewWeb))
|
||||
}
|
||||
text += Line("自来:%s", report.HourNewNormal, float64(report.HourNewNormal-historyReport.HourNewNormal), float64(historyReport.HourNewNormal))
|
||||
text += Line("渠道:%s", report.HourNewDistrict, float64(report.HourNewDistrict-historyReport.HourNewDistrict), float64(historyReport.HourNewDistrict))
|
||||
text += Line("安卓:%s", report.HourAndroidNew, float64(report.HourAndroidNew-historyReport.HourAndroidNew), float64(historyReport.HourAndroidNew))
|
||||
text += Line("苹果:%s", report.HourIosNew, float64(report.HourIosNew-historyReport.HourIosNew), float64(historyReport.HourIosNew))
|
||||
text += Line("轻量:%s", report.HourH5New, float64(report.HourH5New-historyReport.HourH5New), float64(historyReport.HourH5New))
|
||||
text += Line("┣苹果:%s", report.HourH5IosNew, float64(report.HourH5IosNew-historyReport.HourH5IosNew), float64(historyReport.HourH5IosNew))
|
||||
text += Line("┣安卓:%s", report.HourH5AndroidNew, float64(report.HourH5AndroidNew-historyReport.HourH5AndroidNew), float64(historyReport.HourH5AndroidNew))
|
||||
text += Line("┗电脑:%s", report.HourH5PcNew, float64(report.HourH5PcNew-historyReport.HourH5PcNew), float64(historyReport.HourH5PcNew))
|
||||
return text
|
||||
}
|
||||
|
||||
func DisplayDailyStat(report, historyReport *botreport.BotReport, appInfo *appInfoMod.AppInfo, dateDesc string) string {
|
||||
text := fmt.Sprintf("应用名称:%s\n", appInfo.Name)
|
||||
text += fmt.Sprintf("统计名称:%s数据\n", dateDesc)
|
||||
text += fmt.Sprintf("统计时间:%s\n", report.SumDate.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("1️⃣%s充值统计:\n", dateDesc)
|
||||
text += Line("总计:%s", report.TodayRecharge/100, float64(report.TodayRecharge-historyReport.TodayRecharge), float64(historyReport.TodayRecharge))
|
||||
if appInfo.AppId == 888 {
|
||||
text += Line("官网:%s", report.TodayWebRecharge/100, float64(report.TodayWebRecharge-historyReport.TodayWebRecharge), float64(historyReport.TodayWebRecharge))
|
||||
}
|
||||
text += Line("自来:%s", report.TodayNormalRecharge/100, float64(report.TodayNormalRecharge-historyReport.TodayNormalRecharge), float64(historyReport.TodayNormalRecharge))
|
||||
text += Line("渠道:%s", report.TodayDistrictRecharge/100, float64(report.TodayDistrictRecharge-historyReport.TodayDistrictRecharge), float64(historyReport.TodayDistrictRecharge))
|
||||
text += Line("┣会员:%s", report.TodayDistrictVip/100, float64(report.TodayDistrictVip-historyReport.TodayDistrictVip), float64(historyReport.TodayDistrictVip))
|
||||
text += Line("┗轻量:%s", report.TodayH5DistrictRecharge/100, float64(report.TodayH5DistrictRecharge-historyReport.TodayH5DistrictRecharge), float64(historyReport.TodayH5DistrictRecharge))
|
||||
text += Line("安卓:%s", report.TodayAndroidRecharge/100, float64(report.TodayAndroidRecharge-historyReport.TodayAndroidRecharge), float64(historyReport.TodayAndroidRecharge))
|
||||
text += Line("苹果:%s", report.TodayIosRecharge/100, float64(report.TodayIosRecharge-historyReport.TodayIosRecharge), float64(historyReport.TodayIosRecharge))
|
||||
text += Line("轻量:%s", report.TodayH5Recharge/100, float64(report.TodayH5Recharge-historyReport.TodayH5Recharge), float64(historyReport.TodayH5Recharge))
|
||||
text += Line("┣苹果:%s", report.TodayH5IosRecharge/100, float64(report.TodayH5IosRecharge-historyReport.TodayH5IosRecharge), float64(historyReport.TodayH5IosRecharge))
|
||||
text += Line("┣安卓:%s", report.TodayH5AndroidRecharge/100, float64(report.TodayH5AndroidRecharge-historyReport.TodayH5AndroidRecharge), float64(historyReport.TodayH5AndroidRecharge))
|
||||
text += Line("┗电脑:%s", report.TodayH5PcRecharge/100, float64(report.TodayH5PcRecharge-historyReport.TodayH5PcRecharge), float64(historyReport.TodayH5PcRecharge))
|
||||
text += Line("金币:%s", (report.TodayRecharge-report.TodayVipRecharge)/100, float64(report.TodayRecharge-report.TodayVipRecharge-(historyReport.TodayRecharge-historyReport.TodayVipRecharge)), float64(historyReport.TodayRecharge-historyReport.TodayVipRecharge))
|
||||
text += Line("会员:%s", report.TodayVipRecharge/100, float64(report.TodayVipRecharge-historyReport.TodayVipRecharge), float64(historyReport.TodayVipRecharge))
|
||||
if appInfo.AppId == 888 {
|
||||
text += Line("┣官网:%s", report.TodayWebVip/100, float64(report.TodayWebVip-historyReport.TodayWebVip), float64(historyReport.TodayWebVip))
|
||||
}
|
||||
text += Line("┣自来:%s", report.TodayNormalVip/100, float64(report.TodayNormalVip-historyReport.TodayNormalVip), float64(historyReport.TodayNormalVip))
|
||||
text += Line("┗渠道:%s", report.TodayDistrictVip/100, float64(report.TodayDistrictVip-historyReport.TodayDistrictVip), float64(historyReport.TodayDistrictVip))
|
||||
text += Line("老用户:%s", report.TodayOldRecharge/100, float64(report.TodayOldRecharge-historyReport.TodayOldRecharge), float64(historyReport.TodayOldRecharge))
|
||||
text += Line("新用户:%s", report.TodayNewRecharge/100, float64(report.TodayNewRecharge-historyReport.TodayNewRecharge), float64(historyReport.TodayNewRecharge))
|
||||
text += Line("┣苹果:%s", report.TodayIosNewRecharge/100, float64(report.TodayIosNewRecharge-historyReport.TodayIosNewRecharge), float64(historyReport.TodayIosNewRecharge))
|
||||
text += Line("┣安卓:%s", report.TodayAndroidNewRecharge/100, float64(report.TodayAndroidNewRecharge-historyReport.TodayAndroidNewRecharge), float64(historyReport.TodayAndroidNewRecharge))
|
||||
text += Line("┗轻量:%s", report.TodayH5NewRecharge/100, float64(report.TodayH5NewRecharge-historyReport.TodayH5NewRecharge), float64(historyReport.TodayH5NewRecharge))
|
||||
text += Line(" ┣苹果:%s", report.TodayH5IosNewRecharge/100, float64(report.TodayH5IosNewRecharge-historyReport.TodayH5IosNewRecharge), float64(historyReport.TodayH5IosNewRecharge))
|
||||
text += Line(" ┣安卓:%s", report.TodayH5AndroidNewRecharge/100, float64(report.TodayH5AndroidNewRecharge-historyReport.TodayH5AndroidNewRecharge), float64(historyReport.TodayH5AndroidNewRecharge))
|
||||
text += Line(" ┗电脑:%s", report.TodayH5PcNewRecharge/100, float64(report.TodayH5PcNewRecharge-historyReport.TodayH5PcNewRecharge), float64(historyReport.TodayH5PcNewRecharge))
|
||||
text += fmt.Sprintf("2️⃣%s订单统计:\n", dateDesc)
|
||||
text += Line("订单总数:%s", report.TodayOrder, float64(report.TodayOrder-historyReport.TodayOrder), float64(historyReport.TodayOrder))
|
||||
text += Line("成功订单:%s", report.TodaySuccessOrder, float64(report.TodaySuccessOrder-historyReport.TodaySuccessOrder), float64(historyReport.TodaySuccessOrder))
|
||||
text += Rate("成功率: %s", float64(report.TodaySuccessOrder), float64(report.TodayOrder))
|
||||
text += fmt.Sprintf("3️⃣%s访问统计:\n", dateDesc)
|
||||
text += Line("累计:%s", report.AccessUser, float64(report.AccessUser-historyReport.AccessUser), float64(historyReport.AccessUser))
|
||||
text += Line("渠道:%s", report.TodayAccessDistrict, float64(report.TodayAccessDistrict-historyReport.TodayAccessDistrict), float64(historyReport.TodayAccessDistrict))
|
||||
text += Line("┗轻量:%s", report.TodayAccessH5District, float64(report.TodayAccessH5District-historyReport.TodayAccessH5District), float64(historyReport.TodayAccessH5District))
|
||||
text += Line("安卓:%s", report.TodayAccessAndroid, float64(report.TodayAccessAndroid-historyReport.TodayAccessAndroid), float64(historyReport.TodayAccessAndroid))
|
||||
text += Line("苹果:%s", report.TodayAccessIos, float64(report.TodayAccessIos-historyReport.TodayAccessIos), float64(historyReport.TodayAccessIos))
|
||||
text += Line("轻量:%s", report.TodayAccessH5, float64(report.TodayAccessH5-historyReport.TodayAccessH5), float64(historyReport.TodayAccessH5))
|
||||
text += Line("┣苹果:%s", report.TodayAccessH5Ios, float64(report.TodayAccessH5Ios-historyReport.TodayAccessH5Ios), float64(historyReport.TodayAccessH5Ios))
|
||||
text += Line("┣安卓:%s", report.TodayAccessH5Android, float64(report.TodayAccessH5Android-historyReport.TodayAccessH5Android), float64(historyReport.TodayAccessH5Android))
|
||||
text += Line("┗电脑:%s", report.TodayAccessH5Pc, float64(report.TodayAccessH5Pc-historyReport.TodayAccessH5Pc), float64(historyReport.TodayAccessH5Pc))
|
||||
|
||||
text += fmt.Sprintf("4️⃣%s注册用户:\n", dateDesc)
|
||||
text += Line("总计:%s", report.TodayNew, float64(report.TodayNew-historyReport.TodayNew), float64(historyReport.TodayNew))
|
||||
if appInfo.AppId == 888 {
|
||||
text += Line("官网:%s", report.TodayNewWeb, float64(report.TodayNewWeb-historyReport.TodayNewWeb), float64(historyReport.TodayNewWeb))
|
||||
}
|
||||
text += Line("自来:%s", report.TodayNewNormal, float64(report.TodayNewNormal-historyReport.TodayNewNormal), float64(historyReport.TodayNewNormal))
|
||||
text += Line("渠道:%s", report.TodayNewDistrict, float64(report.TodayNewDistrict-historyReport.TodayNewDistrict), float64(historyReport.TodayNewDistrict))
|
||||
text += Line("苹果:%s", report.TodayIosNew, float64(report.TodayIosNew-historyReport.TodayIosNew), float64(historyReport.TodayIosNew))
|
||||
text += Line("安卓:%s", report.TodayAndroidNew, float64(report.TodayAndroidNew-historyReport.TodayAndroidNew), float64(historyReport.TodayAndroidNew))
|
||||
text += Line("轻量:%s", report.TodayH5New, float64(report.TodayH5New-historyReport.TodayH5New), float64(historyReport.TodayH5New))
|
||||
text += Line("┣苹果:%s", report.TodayH5IosNew, float64(report.TodayH5IosNew-historyReport.TodayH5IosNew), float64(historyReport.TodayH5IosNew))
|
||||
text += Line("┣安卓:%s", report.TodayH5AndroidNew, float64(report.TodayH5AndroidNew-historyReport.TodayH5AndroidNew), float64(historyReport.TodayH5AndroidNew))
|
||||
text += Line("┗电脑:%s", report.TodayH5PcNew, float64(report.TodayH5PcNew-historyReport.TodayH5PcNew), float64(historyReport.TodayH5PcNew))
|
||||
if appInfo.AppId == 888 {
|
||||
text += fmt.Sprintf("5️⃣%s游戏统计:\n", dateDesc)
|
||||
text += fmt.Sprintf("游戏充值: %d\n", report.GameRchgAmount)
|
||||
text += fmt.Sprintf("代充充值: %d\n", report.GameDCRchgAmount)
|
||||
text += fmt.Sprintf("有效投注: %d\n", report.ValidBet)
|
||||
text += fmt.Sprintf("系统盈利: %d\n", report.Profit)
|
||||
text += fmt.Sprintf("提现金额: %d\n", report.GameWtdrRealAmount)
|
||||
text += fmt.Sprintf("游戏上分: %d\n", report.GameIncAmount)
|
||||
text += fmt.Sprintf("游戏下分: %d\n", report.GameDecAmount)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func OrderFromRecharge(recharge *rechargemod.RechargeMod) *syncrecharge.SyncRechargeOrder {
|
||||
order := &syncrecharge.SyncRechargeOrder{
|
||||
ID: recharge.ID,
|
||||
PlatformId: recharge.PlatformId,
|
||||
UserId: recharge.UserId,
|
||||
AppId: recharge.AppId,
|
||||
SysType: recharge.SysType,
|
||||
DevType: recharge.DevType,
|
||||
ChannelName: recharge.ChannelName,
|
||||
CID: recharge.CID,
|
||||
Type: recharge.Type,
|
||||
OrderId: recharge.OrderId,
|
||||
Money: recharge.Money,
|
||||
PayMoney: recharge.PayMoney,
|
||||
Status: recharge.Status,
|
||||
SuccessAt: recharge.SuccessAt,
|
||||
BlocDeduction: recharge.BlocDeduction,
|
||||
PromCode: recharge.PromCode,
|
||||
RegisterTime: recharge.RegisterTime,
|
||||
IsDirect: recharge.IsDirect,
|
||||
IsFormalUser: recharge.IsFormalUser,
|
||||
DistrictCode: recharge.DistrictCode,
|
||||
Rate: recharge.Rate,
|
||||
CreatedAt: recharge.CreatedAt,
|
||||
}
|
||||
return order
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"stat/common/statutils"
|
||||
"stat/models/appInfoMod"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
var MenuKeyboard = tgbotapi.NewInlineKeyboardMarkup(
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("今天统计", strings.Join([]string{"/today"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("昨天统计", strings.Join([]string{"/yesterday"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("前天统计", strings.Join([]string{"/daily", "-back=2"}, " ")),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("小时统计", strings.Join([]string{"/hour", "-back=0"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("上一小时", strings.Join([]string{"/hour", "-back=1"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("上二小时", strings.Join([]string{"/hour", "-back=2"}, " ")),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("今日详细", strings.Join([]string{"/daily_hour", "-back=0"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("昨日详细", strings.Join([]string{"/daily_hour", "-back=1"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("前日详细", strings.Join([]string{"/daily_hour", "-back=2"}, " ")),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("今天排行", strings.Join([]string{"/cross_stat", "-back=0"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("昨天排行", strings.Join([]string{"/cross_stat", "-back=1"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("前天排行", strings.Join([]string{"/cross_stat", "-back=2"}, " ")),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("应用列表", strings.Join([]string{"/apps"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("同步状态", strings.Join([]string{"/sync"}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("关闭菜单", strings.Join([]string{"/close"}, " ")),
|
||||
),
|
||||
)
|
||||
|
||||
var MenuKeyboard0 = tgbotapi.NewInlineKeyboardMarkup(
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("单天前一天旧的 /recharge", " "),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("综合统计前一天的 /rechargenew", " "),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("综合从第几天到第几天/rechargedate -begin=2024-01-01 -end=2024-01-21 @bot", ""),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("综合统计具体年月日/rechargeoneday -date=2024-01-01", ""),
|
||||
),
|
||||
)
|
||||
|
||||
func GetStatProjectKeyboard(data string) tgbotapi.InlineKeyboardMarkup {
|
||||
return tgbotapi.NewInlineKeyboardMarkup(
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("充值总计", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectRecharge)}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("老用户充值", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectRechargeOld)}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("新用户充值", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectRechargeNew)}, " ")),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("金币充值", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectRechargeGold)}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("会员购买", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectRechargeVip)}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("订单统计", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectOrder)}, " ")),
|
||||
),
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("注册统计", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectRegister)}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("访问统计", strings.Join([]string{data, fmt.Sprintf("-stat=%s", statutils.StatProjectVisit)}, " ")),
|
||||
tgbotapi.NewInlineKeyboardButtonData("关闭菜单", strings.Join([]string{"/close"}, " ")),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func GetAppKeyboard(appList appInfoMod.AppInfoList, data string, column int) tgbotapi.InlineKeyboardMarkup {
|
||||
rows := make([][]tgbotapi.InlineKeyboardButton, 0)
|
||||
row := make([]tgbotapi.InlineKeyboardButton, 0)
|
||||
for _, app := range appList {
|
||||
row = append(row, tgbotapi.NewInlineKeyboardButtonData(app.Name, strings.Join([]string{data, fmt.Sprintf("-app=%d", app.AppId)}, " ")))
|
||||
if len(row) >= column {
|
||||
rows = append(rows, row)
|
||||
row = make([]tgbotapi.InlineKeyboardButton, 0)
|
||||
}
|
||||
}
|
||||
if len(row) > 0 {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return tgbotapi.NewInlineKeyboardMarkup(rows...)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"stat/api/proto"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/botdailyhourstat"
|
||||
"stat/statg"
|
||||
)
|
||||
|
||||
func RecvTgMessage(ctx context.Context, param *proto.SendTgMessageReq) (*proto.SendTgMessageRes, error) {
|
||||
name := statg.GlobalEndpoints.GetAppShortNameById(param.AppId)
|
||||
text := fmt.Sprintf("应用【%s:%d】消息:\n%s", name, param.AppId, param.Message)
|
||||
|
||||
_ = statg.Bot.SendProjectMessage(text)
|
||||
|
||||
return &proto.SendTgMessageRes{}, nil
|
||||
}
|
||||
|
||||
func GetBotDailyHourOne(ctx context.Context, param *proto.BotDailyHourOneReq) (*proto.BotDailyHourOneResp, error) {
|
||||
var err error
|
||||
ret := &proto.BotDailyHourOneResp{}
|
||||
filter := bson.M{}
|
||||
if !param.Id.IsZero() {
|
||||
filter["_id"] = param.Id
|
||||
} else if !param.SumDate.IsZero() && param.AppId > 0 {
|
||||
filter["sumDate"] = param.SumDate
|
||||
filter["appId"] = param.AppId
|
||||
} else {
|
||||
return nil, fmt.Errorf("param error")
|
||||
}
|
||||
if ret.Stat, err = botdailyhourstat.FindOneBotHourStatEx(ctx, filter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func GetBotDailyHourList(ctx context.Context, param *proto.BotDailyHourListReq) (*proto.BotDailyHourListResp, error) {
|
||||
var err error
|
||||
ret := &proto.BotDailyHourListResp{}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
opts := options.Find().SetSkip(skip).SetLimit(limit)
|
||||
|
||||
filter := bson.M{
|
||||
"sumDate": bson.M{
|
||||
"$gte": param.SumDateStart,
|
||||
"$lte": param.SumDateStop,
|
||||
},
|
||||
}
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = *param.AppId
|
||||
}
|
||||
|
||||
if ret.Total, err = botdailyhourstat.CountBotHourStats(ctx, filter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.List, err = botdailyhourstat.FindManyBotHourStat(ctx, filter, opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func GetBotMonthStatList(ctx context.Context, param *proto.BotMonthStatReq) (*proto.BotMonthStatResp, error) {
|
||||
var err error
|
||||
ret := &proto.BotMonthStatResp{
|
||||
List: make(botdailyhourstat.BotHourStatList, 0),
|
||||
}
|
||||
appIds := make([]int32, 0)
|
||||
appMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
if param.AppId != nil {
|
||||
if _, exists := appMap[*param.AppId]; exists {
|
||||
appIds = append(appIds, *param.AppId)
|
||||
} else {
|
||||
return ret, nil // 没有这个app,直接返回空列表
|
||||
}
|
||||
} else {
|
||||
for id, _ := range appMap {
|
||||
appIds = append(appIds, id)
|
||||
}
|
||||
}
|
||||
|
||||
start := utils.BeginMonthWithTime(param.Month)
|
||||
end := start.AddDate(0, 1, 0)
|
||||
start = start.AddDate(0, 0, 1)
|
||||
|
||||
for _, appId := range appIds {
|
||||
base := &botdailyhourstat.BotHourStat{
|
||||
AppId: appId,
|
||||
ReportTime: param.Month,
|
||||
SumDate: param.Month,
|
||||
TodayGame: &botdailyhourstat.GameItem{},
|
||||
}
|
||||
for d := start; !d.After(end); d = d.AddDate(0, 0, 1) {
|
||||
filter := bson.M{"sumDate": d, "appId": appId}
|
||||
var stat *botdailyhourstat.BotHourStat
|
||||
stat, err = botdailyhourstat.FindOneBotHourStat(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stat.ID.IsZero() {
|
||||
if stat, err = BotDailyHourStat(ctx, appId, d, d.Before(utils.BeginOfYesterday())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
base.ACC(stat)
|
||||
}
|
||||
ret.List = append(ret.List, base)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"stat/common/redis"
|
||||
"stat/common/utils"
|
||||
"stat/common/version"
|
||||
"stat/statg"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
sysOnce sync.Once
|
||||
)
|
||||
|
||||
func GetServerInformation() string {
|
||||
text := fmt.Sprintf("统计中心:【%s】编号【%d】:\n", statg.Conf.ServerIndex.Name(), statg.Conf.ServerIndex)
|
||||
text += fmt.Sprintf("机器名称:【%s】\n", version.HostName())
|
||||
text += fmt.Sprintf("监听地址:【%s】\n", statg.Conf.Host)
|
||||
text += fmt.Sprintf("监听端口:【%d】\n", statg.Conf.Port)
|
||||
text += fmt.Sprintf("服务进程:【%d】\n", os.Getpid())
|
||||
text += fmt.Sprintf("程序版本:【%s】\n", version.SERVICE_VERSION)
|
||||
text += fmt.Sprintf("源码分支:【%s】\n", version.GIT_BRANCH)
|
||||
text += fmt.Sprintf("提交版本:【%s】\n", version.GIT_VERSION)
|
||||
text += fmt.Sprintf("编译日期:【%s】\n", version.BUILD_DATE)
|
||||
text += fmt.Sprintf("编译地址:【%s】\n", version.BUILD_IP)
|
||||
text += fmt.Sprintf("运行模式:【%s】\n", statg.Conf.RunMod)
|
||||
text += fmt.Sprintf("启动时间:【%s】\n", version.ServerStartTime.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("运行时长:【%s】\n", time.Since(version.ServerStartTime))
|
||||
return text
|
||||
}
|
||||
|
||||
func SaveServerKeepAliveTime() {
|
||||
defer utils.RecoverError(nil)
|
||||
keyAlive := redis.GetServerKeepAliveHashKey(int(statg.Conf.ServerIndex))
|
||||
_ = redis.StatRds.Set(keyAlive, time.Now().Unix(), 0)
|
||||
}
|
||||
|
||||
func SaveServerNormalCloseFlag() {
|
||||
defer utils.RecoverError(nil)
|
||||
keyClose := redis.GetServerCloseFlagHashKey(int(statg.Conf.ServerIndex))
|
||||
_ = redis.StatRds.Set(keyClose, time.Now().Unix(), 0)
|
||||
}
|
||||
|
||||
func GetServerInformationLastTime() string {
|
||||
ret := ""
|
||||
srvStart := int64(0)
|
||||
srvAlive := int64(0)
|
||||
srvClose := int64(0)
|
||||
sysOnce.Do(func() {
|
||||
keyStart := redis.GetServerStartTimeHashKey(int(statg.Conf.ServerIndex))
|
||||
if usStart, err := redis.StatRds.Get(keyStart); err == nil && usStart != nil {
|
||||
if ut, err2 := strconv.ParseInt(*usStart, 10, 64); err2 == nil {
|
||||
srvStart = ut
|
||||
}
|
||||
}
|
||||
keyAlive := redis.GetServerKeepAliveHashKey(int(statg.Conf.ServerIndex))
|
||||
if usAlive, err := redis.StatRds.Get(keyAlive); err == nil && usAlive != nil {
|
||||
if ut, err2 := strconv.ParseInt(*usAlive, 10, 64); err2 == nil {
|
||||
srvAlive = ut
|
||||
}
|
||||
}
|
||||
keyClose := redis.GetServerCloseFlagHashKey(int(statg.Conf.ServerIndex))
|
||||
if usClose, err := redis.StatRds.Get(keyClose); err == nil && usClose != nil {
|
||||
if ut, err2 := strconv.ParseInt(*usClose, 10, 64); err2 == nil {
|
||||
srvClose = ut
|
||||
}
|
||||
}
|
||||
|
||||
version.ServerStartLast = time.Unix(srvStart, 0)
|
||||
version.ServerAliveLast = time.Unix(srvAlive, 0)
|
||||
version.ServerCloseLast = time.Unix(srvClose, 0)
|
||||
|
||||
_ = redis.StatRds.Set(keyStart, version.ServerStartTime.Unix(), 0)
|
||||
_ = redis.StatRds.Set(keyClose, version.ServerStartTime.Unix(), 0)
|
||||
_ = redis.StatRds.Set(keyAlive, time.Now().Unix(), 0)
|
||||
})
|
||||
if version.ServerAliveLast.After(version.ServerStartLast) {
|
||||
ret = fmt.Sprintf("上次运行:【%v】\n", version.ServerAliveLast.Sub(version.ServerStartLast))
|
||||
if version.ServerCloseLast.After(version.ServerStartLast) {
|
||||
ret += fmt.Sprintf("上次关闭:【正常关闭🟢】\n")
|
||||
} else {
|
||||
ret += fmt.Sprintf("上次关闭:【异常关闭🔴】\n")
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package botser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func Rate(temp string, numerator, denominator float64) string {
|
||||
fZero := math.Pow10(-10)
|
||||
if math.Abs(denominator) < fZero {
|
||||
return fmt.Sprintf(temp, "无") + "\n"
|
||||
} else {
|
||||
str := fmt.Sprintf("%.2f%%", numerator*100/denominator)
|
||||
return fmt.Sprintf(temp, str) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
func Line(iTemp string, iValue int64, numerator, denominator float64) string {
|
||||
fZero := math.Pow10(-10)
|
||||
if math.Abs(denominator) < fZero {
|
||||
s1 := fmt.Sprintf("%d", iValue)
|
||||
str1 := fmt.Sprintf(iTemp, s1)
|
||||
return str1 + "\n"
|
||||
} else {
|
||||
s1 := fmt.Sprintf("%d", iValue)
|
||||
str1 := fmt.Sprintf(iTemp, s1)
|
||||
v := numerator * 100 / denominator
|
||||
str2 := ""
|
||||
if v < 0 {
|
||||
str2 = fmt.Sprintf("⬇-️️%.2f%%", math.Abs(v))
|
||||
} else if v > 0 {
|
||||
str2 = fmt.Sprintf("⬆+️%.2f%%", math.Abs(v))
|
||||
}
|
||||
if str2 == "" {
|
||||
return str1 + "\n"
|
||||
}
|
||||
return str1 + str2 + "\n"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package cacheSer
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"stat/common/log"
|
||||
"stat/common/redis"
|
||||
"stat/common/statutils"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
type IpStatCache struct {
|
||||
Lock sync.RWMutex
|
||||
IpLimitBucketTopToday redis.BucketList
|
||||
IpLimitBucketTopYesterday redis.BucketList
|
||||
}
|
||||
|
||||
var (
|
||||
Cache *cache.Cache
|
||||
IpLimitStatCache = IpStatCache{
|
||||
IpLimitBucketTopToday: make(redis.BucketList, 0),
|
||||
IpLimitBucketTopYesterday: make(redis.BucketList, 0),
|
||||
}
|
||||
Writers struct {
|
||||
LandPageVisitWriter BaseWriter
|
||||
LandPageClickWriter BaseWriter
|
||||
}
|
||||
_cron *cron.Cron
|
||||
)
|
||||
|
||||
func Init() {
|
||||
Cache = cache.New(10*time.Minute, 10*time.Minute)
|
||||
Writers.LandPageVisitWriter = NewWriter(landPageVisitSync)
|
||||
Writers.LandPageClickWriter = NewWriter(landPageClickSync)
|
||||
_cron = cron.New(cron.WithSeconds(), cron.WithChain(cron.SkipIfStillRunning(&log.Logger{}))) // 秒级定时任务
|
||||
_, _ = _cron.AddFunc("*/5 * * * * ?", func() {
|
||||
err := statutils.ServerMon.RunWithName(SyncWriters, statutils.GetFuncName(SyncWriters))
|
||||
if err != nil {
|
||||
log.Error("SyncWriters err", log.E(err))
|
||||
}
|
||||
})
|
||||
_cron.Start()
|
||||
}
|
||||
|
||||
func SyncWriters() error {
|
||||
err := Writers.LandPageVisitWriter.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Writers.LandPageClickWriter.Sync()
|
||||
}
|
||||
|
||||
func Key(key string) string {
|
||||
pc, _, _, _ := runtime.Caller(1)
|
||||
return runtime.FuncForPC(pc).Name() + "##" + key
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package cacheSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/models/downloadCpc"
|
||||
"stat/models/downloadinfo"
|
||||
)
|
||||
|
||||
func landPageVisitSync(list []interface{}) error {
|
||||
if len(list) > 0 {
|
||||
return downloadCpc.InsertDownloadInfoMany(context.Background(), list)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func landPageClickSync(list []interface{}) error {
|
||||
if len(list) > 0 {
|
||||
return downloadinfo.InsertDownloadInfoMany(context.Background(), list)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package cacheSer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type SyncFunc func(list []interface{}) error
|
||||
|
||||
type BaseWriter interface {
|
||||
Add(doc interface{})
|
||||
Sync() error
|
||||
}
|
||||
|
||||
type Writer struct {
|
||||
lock sync.Mutex
|
||||
list []interface{}
|
||||
sync SyncFunc
|
||||
}
|
||||
|
||||
func NewWriter(syncFunc SyncFunc) BaseWriter {
|
||||
return &Writer{
|
||||
lock: sync.Mutex{},
|
||||
list: make([]interface{}, 0),
|
||||
sync: syncFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) Add(doc interface{}) {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
w.list = append(w.list, doc)
|
||||
}
|
||||
|
||||
func (w *Writer) Sync() error {
|
||||
list := make([]interface{}, 0)
|
||||
func() {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
list = w.list
|
||||
w.list = make([]interface{}, 0)
|
||||
}()
|
||||
return w.sync(list)
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
package channelSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/models/adminMod"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/registermod"
|
||||
"time"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// SearchChannelInfo 查询渠道信息
|
||||
func SearchChannelInfo(ctx context.Context, req *proto.ChannelInfoSearch) (
|
||||
*proto.ChannelInfoSearchResp, error) {
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelInfo.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
resp := &proto.ChannelInfoSearchResp{}
|
||||
|
||||
if req.ID != nil && *req.ID != "" {
|
||||
id, err := primitive.ObjectIDFromHex(*req.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New("ID 格式错误")
|
||||
}
|
||||
filter[channelmod.FN.ChannelInfo.ID] = id
|
||||
}
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
filter[channelmod.FN.ChannelInfo.Channel] = bson.M{"$regex": *req.Channel}
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelInfo.AppIds] = bson.M{"$in": []int32{*req.AppId}}
|
||||
}
|
||||
if req.Type != nil {
|
||||
filter[channelmod.FN.ChannelInfo.Type] = *req.Type
|
||||
}
|
||||
if req.ParentChannel != nil && *req.ParentChannel != "" {
|
||||
filter[channelmod.FN.ChannelInfo.ParentChannel] = bson.M{"$regex": *req.ParentChannel}
|
||||
}
|
||||
if req.UserName != nil && *req.UserName != "" {
|
||||
filter[channelmod.FN.ChannelInfo.UserName] = *req.UserName
|
||||
}
|
||||
if req.Status != nil {
|
||||
filter[channelmod.FN.ChannelInfo.Status] = *req.Status
|
||||
}
|
||||
if req.Remark != nil && *req.Remark != "" {
|
||||
filter[channelmod.FN.ChannelInfo.Remark] = bson.M{"$regex": *req.Remark}
|
||||
}
|
||||
|
||||
if req.Mobile != nil && *req.Mobile != "" {
|
||||
filter[channelmod.FN.ChannelInfo.Mobile] = bson.M{"$regex": *req.Mobile}
|
||||
}
|
||||
|
||||
total, serr := channelmod.CountChannelInfos(ctx, filter)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
channelInfoList, err := channelmod.FindManyChannelInfo(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range channelInfoList {
|
||||
channelInfoList[i].Token = ""
|
||||
}
|
||||
resp.List = channelInfoList
|
||||
resp.Total = total
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SearchChannelInfoExport 导出渠道信息
|
||||
func SearchChannelInfoExport(ctx context.Context, req *proto.ChannelInfoSearch) (string, error) {
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelInfo.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
if req.ID != nil && *req.ID != "" {
|
||||
id, err := primitive.ObjectIDFromHex(*req.ID)
|
||||
if err != nil {
|
||||
return "", errors.New("ID 格式错误")
|
||||
}
|
||||
filter[channelmod.FN.ChannelInfo.ID] = id
|
||||
}
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
filter[channelmod.FN.ChannelInfo.Channel] = bson.M{"$regex": *req.Channel}
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelInfo.AppIds] = bson.M{"$in": []int32{*req.AppId}}
|
||||
}
|
||||
if req.Type != nil {
|
||||
filter[channelmod.FN.ChannelInfo.Type] = *req.Type
|
||||
}
|
||||
if req.ParentChannel != nil && *req.ParentChannel != "" {
|
||||
filter[channelmod.FN.ChannelInfo.ParentChannel] = bson.M{"$regex": *req.ParentChannel}
|
||||
}
|
||||
if req.UserName != nil && *req.UserName != "" {
|
||||
filter[channelmod.FN.ChannelInfo.UserName] = bson.M{"$regex": *req.UserName}
|
||||
}
|
||||
if req.Status != nil {
|
||||
filter[channelmod.FN.ChannelInfo.Status] = *req.Status
|
||||
}
|
||||
if req.Remark != nil && *req.Remark != "" {
|
||||
filter[channelmod.FN.ChannelInfo.Remark] = bson.M{"$regex": *req.Remark}
|
||||
}
|
||||
|
||||
if req.Remark != nil && *req.Remark != "" {
|
||||
filter[channelmod.FN.ChannelInfo.Remark] = bson.M{"$regex": *req.Remark}
|
||||
}
|
||||
|
||||
total, serr := channelmod.CountChannelInfos(ctx, filter)
|
||||
if serr != nil {
|
||||
return "", serr
|
||||
}
|
||||
if total == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
appList, err := appInfoMod.GetAllAppInfo(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
channelInfoList, err := channelmod.FindManyChannelInfo(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return "", err
|
||||
}
|
||||
filePath := fmt.Sprintf("./tmp/兑换记录-%s.xlsx", time.Now().Format("2006-01-02 15:04:05"))
|
||||
if sErr := channelInfoExport(filePath, channelInfoList, appList); sErr != nil {
|
||||
return "", sErr
|
||||
}
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// channelInfoExport 导出
|
||||
func channelInfoExport(filePath string, list channelmod.ChannelInfoList, appList []appInfoMod.AppInfo) error {
|
||||
f := excelize.NewFile()
|
||||
sheetName := "Sheet1"
|
||||
// 设置列宽
|
||||
f.SetColWidth("Sheet1", "A", "L", 23)
|
||||
|
||||
// 设置表头行高
|
||||
f.SetRowHeight("Sheet1", 1, 25)
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(`{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":true,
|
||||
"size":16,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center",
|
||||
"wrap_text":true
|
||||
}
|
||||
}`)
|
||||
f.SetCellStyle("Sheet1", "A1", "L1", headStyleID)
|
||||
idx := f.NewSheet(sheetName)
|
||||
var exportTransWaterTitle = map[string]string{
|
||||
"A1": "名称",
|
||||
"B1": "应用列表",
|
||||
"C1": "渠道类型",
|
||||
"D1": "IP",
|
||||
"E1": "下级代理数",
|
||||
"F1": "父渠道",
|
||||
"G1": "禁止登陆",
|
||||
"H1": "渠道状态",
|
||||
"I1": "登陆错误次数",
|
||||
"J1": "创建者",
|
||||
"K1": "创建时间",
|
||||
"L1": "更新时间",
|
||||
}
|
||||
for k, v := range exportTransWaterTitle {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
}
|
||||
var cLock string
|
||||
var cStatus string
|
||||
for k, v := range list {
|
||||
apps := string2app(appList, v.AppIds)
|
||||
i := 'A' // i 不能超过Z,即列输不能超过26列,超出将出现bug
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Channel) //名称
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), apps) //应用列表
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Type.String()) //渠道类型
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.IP) //IP
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.ChildCount) //下级代理数
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.ParentChannel) //父渠道
|
||||
i++
|
||||
if v.Lock {
|
||||
cLock = "封禁"
|
||||
} else if !v.Lock {
|
||||
cLock = "未封禁"
|
||||
}
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), cLock) //禁止登陆
|
||||
i++
|
||||
if v.Status {
|
||||
cStatus = "开启"
|
||||
} else if !v.Status {
|
||||
cStatus = "关闭"
|
||||
}
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), cStatus) //渠道状态
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.ShowMagic) //登陆错误次数
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.UserName) //创建者
|
||||
i++
|
||||
created := v.CreatedAt.Local().Format("2006-01-02 15:04:05")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), created) //创建时间
|
||||
i++
|
||||
updated := v.UpdatedAt.Local().Format("2006-01-02 15:04:05")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), updated) //更新时间
|
||||
}
|
||||
f.SetActiveSheet(idx)
|
||||
p, _ := os.Getwd()
|
||||
fileName := path.Join(p, filePath)
|
||||
if err := f.SaveAs(fileName); err != nil {
|
||||
log.Error("redeemCodeExport:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func string2app(appList []appInfoMod.AppInfo, appIds []int32) string {
|
||||
var result string
|
||||
for count, id := range appIds {
|
||||
if count == len(appIds)-1 {
|
||||
result += GetAppName(appList, id)
|
||||
} else {
|
||||
result += (GetAppName(appList, id) + "、")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// AddChannelInfo 添加一个媒体视频
|
||||
func AddChannelInfo(ctx context.Context, username string, req *proto.ChannelInfoAdd) (*channelmod.ChannelInfo, error) {
|
||||
if channelmod.IsOfficialChannelName(req.Channel) {
|
||||
return nil, fmt.Errorf("渠道名字为系统保留字,请换一个渠道名字")
|
||||
}
|
||||
checkFilter := bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channelInfo.Channel == req.Channel {
|
||||
return channelInfo, errors.New("渠道已经存在")
|
||||
}
|
||||
now := time.Now()
|
||||
newChannelInfo := channelmod.ChannelInfo{
|
||||
Channel: req.Channel,
|
||||
UserName: username,
|
||||
ParentChannel: req.ParentChannel,
|
||||
Password: req.Password,
|
||||
Status: req.Status,
|
||||
Mobile: req.Mobile,
|
||||
Type: req.Type,
|
||||
Remark: req.Remark,
|
||||
Lock: req.Lock,
|
||||
ChildShowDivideRate: req.ChildShowDivideRate,
|
||||
ShowMagic: 0,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
if req.Type == channelmod.ChannelTypeParent {
|
||||
newChannelInfo.ParentChannel = ""
|
||||
newChannelInfo.ParentAgent = ""
|
||||
} else if req.Type == channelmod.ChannelTypeSecond {
|
||||
parentInfo, err := channelmod.FindOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: req.ParentChannel})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if parentInfo.Channel != req.ParentChannel {
|
||||
return nil, fmt.Errorf("指定的父渠道[%s]不存在", req.ParentChannel)
|
||||
}
|
||||
newChannelInfo.ParentChannel = req.ParentChannel
|
||||
newChannelInfo.ParentAgent = req.ParentChannel
|
||||
} else {
|
||||
return nil, errors.New("后台只支持添加一级和二级渠道,其他级别的渠道请登陆渠道后台添加")
|
||||
}
|
||||
|
||||
err = channelmod.InsertOneChannelInfo(ctx, &newChannelInfo)
|
||||
if err != nil {
|
||||
log.Error("AddChannelInfo InsertOneChannelInfo", log.Any("channel", newChannelInfo.ParentChannel), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.Type.IsChild() {
|
||||
setdoc := bson.M{"$inc": bson.M{channelmod.FN.ChannelInfo.ChildCount: 1}}
|
||||
err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: newChannelInfo.ParentChannel}, setdoc)
|
||||
if err != nil {
|
||||
log.Error("AddChannelInfo UpdateOneChannelInfo", log.Any("channel", newChannelInfo.ParentChannel), log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return channelmod.FindOneChannelInfo(ctx, checkFilter)
|
||||
}
|
||||
|
||||
// DelChannelInfo 删除指定渠道信息
|
||||
func DelChannelInfo(ctx context.Context, channelId string) error {
|
||||
id, err := primitive.ObjectIDFromHex(channelId)
|
||||
if err != nil {
|
||||
return errors.New("ID 格式错误")
|
||||
}
|
||||
// 1. 查询渠道信息
|
||||
channelFilter := bson.M{channelmod.FN.ChannelInfo.ID: id}
|
||||
channelInfo, err := channelmod.FindOneChannelInfo(ctx, channelFilter)
|
||||
if err != nil {
|
||||
log.Error("DelChannelInfo FindOneChannelInfo", log.Any("channelId", channelId), log.E(err))
|
||||
return err
|
||||
}
|
||||
// 3. 查询子代理数
|
||||
childFilter := bson.M{channelmod.FN.ChannelInfo.ParentAgent: channelInfo.Channel}
|
||||
childCount, err := channelmod.CountChannelInfos(ctx, childFilter)
|
||||
if err != nil {
|
||||
log.Error("DelChannelInfo CountChannelDeductionRules", log.Any("channelId", channelId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if childCount > 0 {
|
||||
return errors.New("代理下级代理不为空,请先删除下级代理")
|
||||
}
|
||||
|
||||
// 2. 查询渠道绑定应用
|
||||
ruleFilter := bson.M{channelmod.FN.ChannelDeductionRule.Channel: channelInfo.Channel}
|
||||
ruleCount, err := channelmod.CountChannelDeductionRules(ctx, ruleFilter)
|
||||
if err != nil {
|
||||
log.Error("DelChannelInfo CountChannelDeductionRules", log.Any("channelId", channelId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if ruleCount > 0 {
|
||||
return errors.New("绑定应用不为空,请先删除绑定应用")
|
||||
}
|
||||
// 4. 删除渠道
|
||||
_, err = channelmod.DelOneChannelInfo(ctx, channelFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5 更新父渠道子渠道数量
|
||||
if channelInfo.Type.IsChild() {
|
||||
setdoc := bson.M{"$inc": bson.M{channelmod.FN.ChannelInfo.ChildCount: -1}}
|
||||
err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channelInfo.ParentAgent}, setdoc)
|
||||
if err != nil {
|
||||
log.Error("DelChannelInfo UpdateOneChannelInfo", log.Any("channel", channelInfo.ParentAgent), log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateChannelInfo 更新指定渠道
|
||||
func UpdateChannelInfo(ctx context.Context, channelId string, req proto.ChannelInfoUpdate) error {
|
||||
|
||||
id, err := primitive.ObjectIDFromHex(channelId)
|
||||
if err != nil {
|
||||
return errors.New("ID 格式错误")
|
||||
}
|
||||
filter := bson.M{channelmod.FN.ChannelInfo.ID: id}
|
||||
|
||||
//更新数据库
|
||||
setDoc := bson.M{"$set": req}
|
||||
|
||||
serr := channelmod.UpdateOneChannelInfo(ctx, filter, setDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateOneChannelOwner 更新指定渠道的所有者
|
||||
func UpdateOneChannelOwner(ctx context.Context, operator, account string, req proto.ChannelOwnerUpdate) error {
|
||||
|
||||
filter := bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel}
|
||||
|
||||
cInfo, serr := channelmod.FindOneChannelInfo(ctx, filter)
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:FindOneChannelInfo err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
return serr
|
||||
} else if cInfo != nil && cInfo.Channel == "" {
|
||||
log.Error("UpdateOneChannelOwner:FindOneChannelInfo err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
return errors.New("渠道不存在")
|
||||
}
|
||||
|
||||
if req.UserName == "" {
|
||||
return errors.New("变更的所有者不能为空")
|
||||
} else if req.UserName == cInfo.UserName {
|
||||
return errors.New("变更的所有者不能与之前的相同")
|
||||
// } else if cInfo.Type != 1 { // 只能操作1级渠道
|
||||
// return errors.New("变更所有者,只能操作1级渠道")
|
||||
}
|
||||
|
||||
admin, serr := adminMod.FindOnedmin(ctx, bson.M{"name": req.UserName})
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:FindOnedmin err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
return serr
|
||||
} else if admin != nil && admin.Name == "" {
|
||||
log.Error("UpdateOneChannelOwner:FindOnedmin err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
return errors.New("账号不存在")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// 更新渠道信息
|
||||
setDoc := bson.M{"$set": bson.M{
|
||||
channelmod.FN.ChannelInfo.UserName: req.UserName,
|
||||
channelmod.FN.ChannelInfo.UpdatedAt: now,
|
||||
}}
|
||||
|
||||
serr = channelmod.UpdateOneChannelInfo(ctx, filter, setDoc)
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:UpdateOneChannelInfo err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
return serr
|
||||
}
|
||||
|
||||
// 更新渠道统计信息
|
||||
csFilter := bson.M{channelmod.FN.ChannelStat.Channel: cInfo.Channel}
|
||||
csSetDoc := bson.M{"$set": bson.M{channelmod.FN.ChannelStat.UserName: req.UserName}}
|
||||
|
||||
serr = channelmod.UpdateManyChannelStat(ctx, csFilter, csSetDoc)
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:UpdateManyChannelStat err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
return serr
|
||||
}
|
||||
|
||||
// 找到一级渠道的所有子渠道
|
||||
allFlter := bson.M{channelmod.FN.ChannelInfo.ParentChannel: req.Channel}
|
||||
|
||||
chanIds := make([]string, 0)
|
||||
cInfos, _ := channelmod.FindManyChannelInfo(ctx, allFlter)
|
||||
if len(cInfos) > 0 {
|
||||
for _, info := range cInfos {
|
||||
chanIds = append(chanIds, info.Channel)
|
||||
}
|
||||
|
||||
// 更新一级渠道的所有子渠道
|
||||
serr = channelmod.UpdateManyChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: bson.M{"$in": chanIds}}, setDoc)
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:UpdateManyChannelInfo 2 err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
}
|
||||
// 更新一级渠道的所有子渠道统计
|
||||
serr = channelmod.UpdateManyChannelStat(ctx, bson.M{channelmod.FN.ChannelStat.Channel: bson.M{"$in": chanIds}}, csSetDoc)
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:UpdateManyChannelStat 2 err", log.Any("serr", serr),
|
||||
log.Any("req", req))
|
||||
}
|
||||
}
|
||||
|
||||
// 添加变更记录
|
||||
new := &channelmod.ChannelOwnerChange{
|
||||
ID: primitive.NewObjectID(),
|
||||
Channel: cInfo.Channel,
|
||||
OldUser: cInfo.UserName,
|
||||
NewUser: req.UserName,
|
||||
Mark: req.Mark,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Operator: operator,
|
||||
Account: account,
|
||||
}
|
||||
serr = channelmod.InsertOneChannelOwnerChange(ctx, new)
|
||||
if serr != nil {
|
||||
log.Error("UpdateOneChannelOwner:InsertOneChannelOwnerChange err", log.Any("new", new),
|
||||
log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SearchChannelOwnerChangeInfo 查询渠道所有者变更信息
|
||||
func SearchChannelOwnerChangeInfo(ctx context.Context, req *proto.ChannelOwnerChangeInfoSearch) (
|
||||
*proto.ChannelOwnerChangeInfoSearchResp, error) {
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelInfo.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
resp := &proto.ChannelOwnerChangeInfoSearchResp{}
|
||||
|
||||
if req.ID != nil {
|
||||
filter[channelmod.FN.ChannelOwnerChange.ID] = req.ID
|
||||
}
|
||||
if req.Channel != nil {
|
||||
filter[channelmod.FN.ChannelOwnerChange.Channel] = *req.Channel
|
||||
}
|
||||
if req.OldUser != nil {
|
||||
filter[channelmod.FN.ChannelOwnerChange.OldUser] = *req.OldUser
|
||||
}
|
||||
if req.NewUser != nil {
|
||||
filter[channelmod.FN.ChannelOwnerChange.NewUser] = *req.NewUser
|
||||
}
|
||||
|
||||
total, serr := channelmod.CountChannelOwnerChanges(ctx, filter)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
ownerChangeList, err := channelmod.FindManyChannelOwnerChanges(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.List = ownerChangeList
|
||||
resp.Total = total
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SearchUserInfo 查询用户信息
|
||||
func SearchUserInfo(ctx context.Context, req *proto.UserInfoSearch) (
|
||||
*proto.UserInfoSearchResp, error) {
|
||||
resp := &proto.UserInfoSearchResp{}
|
||||
|
||||
filter := bson.M{}
|
||||
if req.UserId != nil {
|
||||
filter["userId"] = *req.UserId
|
||||
}
|
||||
if req.Channel != nil {
|
||||
filter["districtCode"] = *req.Channel
|
||||
}
|
||||
if req.AppId != nil {
|
||||
filter["appId"] = *req.AppId
|
||||
}
|
||||
if req.IP != nil {
|
||||
filter["ip"] = *req.IP
|
||||
}
|
||||
if req.Start != nil && req.End != nil {
|
||||
filter["createdAt"] = bson.M{"$gt": *req.Start, "$lt": *req.End}
|
||||
}
|
||||
|
||||
total, err := registermod.CountRegister(ctx, filter)
|
||||
if err != nil {
|
||||
log.Error("SearchUserInfo CountRegister", log.Any("err", err))
|
||||
return nil, err
|
||||
}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{"createdAt": -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
userList, err := registermod.FindManyRegister(ctx, filter, opt)
|
||||
if err != nil {
|
||||
log.Error("SearchUserInfo FindManyRegister", log.Any("err", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
resp.List = userList
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package channelSer
|
||||
|
||||
import "math/rand"
|
||||
|
||||
var (
|
||||
//所有的字符
|
||||
ALL_CHARS = []string{
|
||||
"1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "A",
|
||||
"B", "C", "D", "E", "F",
|
||||
"G", "H", "J", "K",
|
||||
"L", "M", "N", "O", "P",
|
||||
"Q", "R", "S", "T", "U",
|
||||
"V", "W", "X", "Y", "Z"}
|
||||
aLen = len(ALL_CHARS)
|
||||
)
|
||||
|
||||
const (
|
||||
//默认长度
|
||||
DEFAULT_LEN = 6
|
||||
)
|
||||
|
||||
// 用户推广码
|
||||
func InviteShartPasswd() (code string) {
|
||||
for i := 0; i < DEFAULT_LEN; i++ {
|
||||
index := rand.Intn(aLen)
|
||||
code += ALL_CHARS[index]
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package channelSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/models/channelmod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// SearchChannelDeductionRule 查询渠道扣量规则信息
|
||||
func SearchChannelDeductionRule(ctx context.Context, req *proto.ChannelDeductionRuleSearch) (*proto.ChannelDeductionRuleSearchResp, error) {
|
||||
resp := &proto.ChannelDeductionRuleSearchResp{}
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelDeductionRule.BeginTime: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
filter[channelmod.FN.ChannelDeductionRule.Channel] = bson.M{"$regex": *req.Channel}
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelDeductionRule.AppId] = *req.AppId
|
||||
}
|
||||
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
filter[channelmod.FN.ChannelDeductionRule.Channel] = *req.Channel
|
||||
}
|
||||
|
||||
total, err := channelmod.CountChannelDeductionRules(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
ChannelDeductionRuleList, err := channelmod.FindManyChannelDeductionRule(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rule := range ChannelDeductionRuleList {
|
||||
if rule.DivideRate == 0 {
|
||||
rule.DivideRate = 100
|
||||
}
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
resp.List = ChannelDeductionRuleList
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AddChannelDeductionRule 添加一个媒体视频
|
||||
func AddChannelDeductionRule(ctx context.Context, req *proto.ChannelDeductionRuleAdd) (*channelmod.ChannelDeductionRule, error) {
|
||||
checkFilter := bson.M{channelmod.FN.ChannelDeductionRule.Channel: req.Channel, channelmod.FN.ChannelDeductionRule.AppId: req.AppId}
|
||||
ChannelDeductionRule, err := channelmod.FindOneChannelDeductionRule(ctx, checkFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ChannelDeductionRule.Channel == req.Channel {
|
||||
return ChannelDeductionRule, errors.New("渠道扣量规则已经存在")
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfoEx(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var one float64
|
||||
if req.LessIncomeRate == nil {
|
||||
one = 0.0
|
||||
} else {
|
||||
one = *req.LessIncomeRate
|
||||
}
|
||||
now := time.Now()
|
||||
rule := channelmod.ChannelDeductionRule{
|
||||
AppId: req.AppId,
|
||||
AppType: req.AppType,
|
||||
PromoteMode: req.PromoteMode,
|
||||
Channel: req.Channel,
|
||||
Settlement: req.Settlement,
|
||||
First: req.First,
|
||||
Second: req.Second,
|
||||
Third: req.Third,
|
||||
Fourth: req.Fourth,
|
||||
Fifth: req.Fifth,
|
||||
CPAPrice: req.CPAPrice,
|
||||
CPSRate: req.CPSRate,
|
||||
BeginTime: req.BeginTime,
|
||||
CPSBegin: req.CPSBegin,
|
||||
CPSDeductionRate: req.CPSDeductionRate,
|
||||
DivideRate: req.DivideRate,
|
||||
LandingPage: req.LandingPage,
|
||||
ProxyDeduction: req.ProxyDeduction,
|
||||
AutoDeduction: req.AutoDeduction,
|
||||
LessIncomeRate: one,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = channelmod.InsertOneChannelDeductionRule(ctx, &rule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
channelInfo.AppIds = append(channelInfo.AppIds, req.AppId)
|
||||
|
||||
setdoc := bson.M{"$set": bson.M{channelmod.FN.ChannelInfo.AppIds: channelInfo.AppIds}}
|
||||
err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel}, setdoc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return channelmod.FindOneChannelDeductionRuleEx(ctx, checkFilter)
|
||||
}
|
||||
|
||||
// DelChannelDeductionRule 删除指定渠道扣量规则信息
|
||||
func DelChannelDeductionRule(ctx context.Context, channelId string) error {
|
||||
id, err := primitive.ObjectIDFromHex(channelId)
|
||||
if err != nil {
|
||||
return errors.New("ID 格式错误")
|
||||
}
|
||||
ruleFilter := bson.M{channelmod.FN.ChannelDeductionRule.ID: id}
|
||||
channelRuleInfo, err := channelmod.FindOneChannelDeductionRuleEx(ctx, ruleFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
channelInfo, err := channelmod.FindOneChannelInfoEx(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channelRuleInfo.Channel})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count, err := channelmod.CountChannelInfos(ctx, bson.M{channelmod.FN.ChannelInfo.ParentAgent: channelInfo.Channel})
|
||||
if err != nil {
|
||||
log.Error("DelChannelDeductionRule CountChannelInfos", log.Any("channelId", channelId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return errors.New("下级代理不为空,请先删除下级代理, 再删除应用规则")
|
||||
}
|
||||
|
||||
_, err = channelmod.DelOneChannelDeductionRule(ctx, ruleFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appids := make([]int32, 0)
|
||||
for _, appid := range channelInfo.AppIds {
|
||||
if appid != channelRuleInfo.AppId {
|
||||
appids = append(appids, appid)
|
||||
}
|
||||
}
|
||||
|
||||
setdoc := bson.M{"$set": bson.M{channelmod.FN.ChannelInfo.AppIds: appids}}
|
||||
err = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channelRuleInfo.Channel}, setdoc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateChannelDeductionRule 更新指定渠道扣量规则
|
||||
func UpdateChannelDeductionRule(ctx context.Context, channelId string, info proto.ChannelDeductionRuleUpdate) error {
|
||||
|
||||
id, err := primitive.ObjectIDFromHex(channelId)
|
||||
if err != nil {
|
||||
return errors.New("ID 格式错误")
|
||||
}
|
||||
filter := bson.M{channelmod.FN.ChannelDeductionRule.ID: id}
|
||||
|
||||
//更新数据库
|
||||
setDoc := bson.M{"$set": info}
|
||||
|
||||
serr := channelmod.UpdateOneChannelDeductionRule(ctx, filter, setDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,150 @@
|
||||
package channelSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/api/proto"
|
||||
"stat/common/utils"
|
||||
"stat/middleware/define"
|
||||
"stat/models/adminMod"
|
||||
channelmod "stat/models/channelmod"
|
||||
"stat/models/channelsettlementrecord"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
var (
|
||||
SystemStartTime = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
func SettlementInfoToApplyRecord(src *channelsettlementrecord.ChannelSettlementRecord) *proto.ApplyFormRecord {
|
||||
ret := &proto.ApplyFormRecord{
|
||||
ID: src.ID,
|
||||
Channel: src.Channel,
|
||||
AppId: src.AppId,
|
||||
BeginTime: src.BeginTime,
|
||||
EndTime: src.EndTime,
|
||||
TotalMoney: src.TotalMoney,
|
||||
SettleMoney: src.SettleMoney,
|
||||
Settlement: src.Settlement,
|
||||
Wallet: src.Wallet,
|
||||
PayMoney: src.PayMoney,
|
||||
Usdt: src.Usdt,
|
||||
Status: src.Status,
|
||||
Remark: "",
|
||||
Pictures: nil,
|
||||
UpdatedAt: src.UpdatedAt,
|
||||
CreatedAt: src.CreatedAt,
|
||||
}
|
||||
remark := ""
|
||||
if len(src.OperateHistory) > 0 {
|
||||
remark = src.OperateHistory[0].Remark
|
||||
if len(src.OperateHistory) > 1 {
|
||||
if remark == "" {
|
||||
remark = src.OperateHistory[len(src.OperateHistory)-1].Remark
|
||||
} else {
|
||||
s := src.OperateHistory[len(src.OperateHistory)-1].Remark
|
||||
if s != "" {
|
||||
remark += " / " + s
|
||||
}
|
||||
}
|
||||
}
|
||||
ret.Pictures = src.OperateHistory[len(src.OperateHistory)-1].Pictures
|
||||
}
|
||||
ret.Remark = remark
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func SettlementInfoListToApplyForm(lst channelsettlementrecord.ChannelSettlementRecordList) proto.ApplyFormRecordList {
|
||||
res := make(proto.ApplyFormRecordList, 0)
|
||||
for _, item := range lst {
|
||||
a := SettlementInfoToApplyRecord(item)
|
||||
res = append(res, a)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func FenToYuanDisplay(fen int64) string {
|
||||
return fmt.Sprintf("%.2f", float64(fen)/float64(100))
|
||||
}
|
||||
|
||||
func IndentationDisplay(v int64) string {
|
||||
return fmt.Sprintf("%.2f", float64(v)/float64(10000))
|
||||
}
|
||||
|
||||
func CheckAndFixTime(startTime, endTime *time.Time) (*time.Time, *time.Time, error) {
|
||||
var start, end *time.Time
|
||||
// 开始时间和结束时间
|
||||
if endTime == nil {
|
||||
e := utils.BeginOfYesterday()
|
||||
end = &e
|
||||
} else {
|
||||
end = endTime
|
||||
}
|
||||
if startTime == nil {
|
||||
start = &SystemStartTime
|
||||
} else {
|
||||
start = startTime
|
||||
}
|
||||
//
|
||||
if end.After(utils.BeginOfYesterday()) {
|
||||
e := utils.BeginOfYesterday()
|
||||
end = &e
|
||||
}
|
||||
if end.Before(*start) {
|
||||
return nil, nil, fmt.Errorf("开始和结束的时间区间填写不正确")
|
||||
}
|
||||
return start, end, nil
|
||||
}
|
||||
|
||||
func GetUpdateSettlementOperateItem(old channelsettlementrecord.ChannelSettlementRecord, update proto.SettlementInfoUpdate2,
|
||||
accType define.ActType, account string, accountName string, images []string) channelsettlementrecord.SettlementOperateItem {
|
||||
res := channelsettlementrecord.SettlementOperateItem{}
|
||||
res.OperateTime = time.Now()
|
||||
if update.Remark != nil {
|
||||
res.Remark = *update.Remark
|
||||
}
|
||||
res.OperatorAccountType = accType
|
||||
res.OperatorAccount = account
|
||||
res.OperatorAccountName = accountName
|
||||
res.BeforeStatus = old.Status
|
||||
if update.Status != nil && *update.Status != old.Status {
|
||||
res.AfterStatus = *update.Status
|
||||
res.Content += fmt.Sprintf("状态改变:(%s => %s)\n", old.Status.String(), update.Status.String())
|
||||
} else {
|
||||
res.AfterStatus = old.Status
|
||||
}
|
||||
if update.Usdt != nil && *update.Usdt != old.Usdt {
|
||||
res.Content += fmt.Sprintf("修改了打款金额(USDT):(%s => %s)\n", FenToYuanDisplay(old.Usdt), FenToYuanDisplay(*update.Usdt))
|
||||
}
|
||||
if update.PayMoney != nil && *update.PayMoney != old.PayMoney {
|
||||
res.Content += fmt.Sprintf("修改了打款金额(CNY): (%s => %s)\n", FenToYuanDisplay(old.PayMoney), FenToYuanDisplay(*update.PayMoney))
|
||||
}
|
||||
if update.SettleMoney != nil && *update.SettleMoney != old.SettleMoney {
|
||||
res.Content += fmt.Sprintf("修改了申请结算金额:(%s => %s)\n", FenToYuanDisplay(old.SettleMoney), FenToYuanDisplay(*update.SettleMoney))
|
||||
}
|
||||
for _, img := range images {
|
||||
res.Pictures = append(res.Pictures, img)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func GetAccountName(ctx context.Context, accType define.ActType, account string) (string, error) {
|
||||
if accType == define.Admin {
|
||||
if acc, err := adminMod.FindOneByAccount(ctx, account); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return acc.Name, nil
|
||||
}
|
||||
} else if accType == define.Channel {
|
||||
if acc, err := channelmod.FindOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: account}); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
return acc.Channel, nil
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("error account type: %s", accType)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
package channelSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"runtime"
|
||||
"stat/api/proto"
|
||||
"stat/common/utils"
|
||||
"stat/models/channelmod"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/now"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// SearchChannelStat 查询渠道扣量规则信息
|
||||
func SearchChannelStat(ctx context.Context, req *proto.ChannelStatSearch) (*proto.ChannelStatSearchResp, error) {
|
||||
resp := &proto.ChannelStatSearchResp{
|
||||
List: make(channelmod.ChannelStatList, 0),
|
||||
}
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
if req.WithChild != nil && *req.WithChild {
|
||||
filter = bson.M{
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelStat.Channel: *req.Channel},
|
||||
bson.M{channelmod.FN.ChannelStat.ParentChannel: *req.Channel},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
filter[channelmod.FN.ChannelStat.Channel] = *req.Channel
|
||||
}
|
||||
} else if req.DayType != nil {
|
||||
timeNow := time.Now()
|
||||
now.WeekStartDay = time.Monday
|
||||
Now := now.With(timeNow.In(time.Local))
|
||||
todayBegin := utils.BeginWithTime(timeNow)
|
||||
weekBegin := Now.BeginningOfWeek()
|
||||
monthBegin := Now.BeginningOfMonth()
|
||||
var timeBegin time.Time
|
||||
if *req.DayType == 1 { // 今天内
|
||||
timeBegin = todayBegin
|
||||
} else if *req.DayType == 2 { // 昨天和今天
|
||||
timeBegin = todayBegin.AddDate(0, 0, -1)
|
||||
} else if *req.DayType == 3 { // 三天内
|
||||
timeBegin = todayBegin.AddDate(0, 0, -2)
|
||||
} else if *req.DayType == 4 { // 七天内
|
||||
timeBegin = todayBegin.AddDate(0, 0, -6)
|
||||
} else if *req.DayType == 5 { // 本周内
|
||||
timeBegin = weekBegin
|
||||
} else if *req.DayType == 6 { // 两周内
|
||||
timeBegin = weekBegin.AddDate(0, 0, -7)
|
||||
} else if *req.DayType == 7 { // 本月内
|
||||
timeBegin = monthBegin
|
||||
} else if *req.DayType == 8 { // 两月内
|
||||
timeBegin = monthBegin.AddDate(0, -1, 0)
|
||||
}
|
||||
channelList, err := channelmod.FindManyChannelInfo(ctx, bson.M{
|
||||
channelmod.FN.ChannelInfo.CreatedAt: bson.M{
|
||||
"$gte": timeBegin,
|
||||
"$lte": timeNow,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
channelIds := make([]string, 0)
|
||||
for _, channelInfo := range channelList {
|
||||
channelIds = append(channelIds, channelInfo.Channel)
|
||||
}
|
||||
|
||||
if len(channelIds) > 0 {
|
||||
if req.WithChild != nil && *req.WithChild {
|
||||
filter = bson.M{
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelStat.Channel: bson.M{"$in": channelIds}},
|
||||
bson.M{channelmod.FN.ChannelStat.ParentChannel: bson.M{"$in": channelIds}},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
filter[channelmod.FN.ChannelStat.Channel] = bson.M{"$in": channelIds}
|
||||
}
|
||||
} else {
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//排序
|
||||
if req.RankType != nil {
|
||||
if *req.RankType == 1 {
|
||||
opt.SetSort(bson.D{{Key: channelmod.FN.ChannelStat.SumDate, Value: -1}, {Key: channelmod.FN.ChannelStat.DirectAmount, Value: -1}})
|
||||
} else if *req.RankType == 2 {
|
||||
opt.SetSort(bson.D{{Key: channelmod.FN.ChannelStat.SumDate, Value: -1}, {Key: channelmod.FN.ChannelStat.NewDirectCount, Value: -1}})
|
||||
} else if *req.RankType == 3 {
|
||||
opt.SetSort(bson.D{{Key: channelmod.FN.ChannelStat.SumDate, Value: -1}, {Key: channelmod.FN.ChannelStat.NewRechargeAmount, Value: -1}})
|
||||
} else if *req.RankType == 4 {
|
||||
opt.SetSort(bson.D{{Key: channelmod.FN.ChannelStat.SumDate, Value: -1}, {Key: channelmod.FN.ChannelStat.IncomeRate, Value: -1}})
|
||||
}
|
||||
} else {
|
||||
opt.SetSort(bson.D{{Key: channelmod.FN.ChannelStat.SumDate, Value: -1}, {Key: channelmod.FN.ChannelStat.NewDirectCount, Value: -1}})
|
||||
}
|
||||
|
||||
if req.UserName != nil && *req.UserName != "" {
|
||||
filter[channelmod.FN.ChannelStat.UserName] = bson.M{"$regex": *req.UserName}
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelStat.AppId] = bson.M{"$in": *req.AppId}
|
||||
}
|
||||
|
||||
if req.Start != nil && req.End != nil {
|
||||
filter[channelmod.FN.ChannelStat.SumDate] = bson.M{"$gte": *req.Start, "$lte": *req.End}
|
||||
}
|
||||
|
||||
if req.WithChild != nil && !*req.WithChild {
|
||||
filter[channelmod.FN.ChannelStat.Type] = channelmod.ChannelTypeParent
|
||||
}
|
||||
if req.Settlement != nil {
|
||||
filter[channelmod.FN.ChannelStat.Settlement] = *req.Settlement
|
||||
}
|
||||
if req.Detail != nil && *req.Detail {
|
||||
if req.SumDate == nil {
|
||||
return resp, errors.New("param error")
|
||||
}
|
||||
filter = bson.M{
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelStat.Channel: *req.Channel},
|
||||
bson.M{channelmod.FN.ChannelStat.ParentChannel: *req.Channel},
|
||||
},
|
||||
channelmod.FN.ChannelStat.SumDate: *req.SumDate,
|
||||
}
|
||||
}
|
||||
|
||||
ChannelStatList, err := channelmod.FindManyChannelStat(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
for _, v := range ChannelStatList {
|
||||
v.CorrectValidData()
|
||||
v.CalcIncomeRate()
|
||||
}
|
||||
resp.List = ChannelStatList
|
||||
|
||||
// 汇总
|
||||
canSum := utils.M(filter).HasKey(channelmod.FN.ChannelStat.AppId) && utils.M(filter).HasKey(channelmod.FN.ChannelStat.SumDate)
|
||||
|
||||
if canSum {
|
||||
var total int64
|
||||
err = channelmod.FindManyChannelStatShard(ctx, filter, 10000, func(item *channelmod.ChannelStat) {
|
||||
total++
|
||||
channelmod.ChannelStatSummation(&resp.TotalInfo, item)
|
||||
if req.WithChild != nil && *req.WithChild {
|
||||
for _, v := range ChannelStatList {
|
||||
if item.ParentChannel == v.Channel && v.SumDate.Equal(item.SumDate) {
|
||||
channelmod.ChannelStatSummation(v, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
if total > 10000 {
|
||||
runtime.GC()
|
||||
}
|
||||
resp.Total = total
|
||||
var ruleInfo *channelmod.ChannelDeductionRule
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
ruleFilter := bson.M{
|
||||
channelmod.FN.ChannelDeductionRule.Channel: req.Channel,
|
||||
// channelmod.FN.ChannelDeductionRule.AppId: req.AppId,
|
||||
}
|
||||
ruleInfo, _ = channelmod.FindOneChannelDeductionRule(ctx, ruleFilter)
|
||||
}
|
||||
if ruleInfo != nil {
|
||||
resp.TotalInfo.Channel = ruleInfo.Channel
|
||||
resp.TotalInfo.Settlement = ruleInfo.Settlement
|
||||
resp.TotalInfo.AppId = ruleInfo.AppId
|
||||
resp.TotalInfo.CPAPrice = ruleInfo.CPAPrice
|
||||
resp.TotalInfo.CPSRate = ruleInfo.CPSRate
|
||||
resp.TotalInfo.AppType = ruleInfo.AppType
|
||||
resp.TotalInfo.PromoteMode = ruleInfo.PromoteMode
|
||||
resp.TotalInfo.CalcIncomeRate()
|
||||
resp.Income, resp.Desc = channelmod.CalculateChannelIncome(&resp.TotalInfo)
|
||||
}
|
||||
} else {
|
||||
resp.Total = 1000000
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SearchChannelSettlement 查询渠道结算信息
|
||||
func SearchChannelSettlement(ctx context.Context, req *proto.ChannelSettlementSearch) (
|
||||
*proto.ChannelSettlementSearchResp, error) {
|
||||
resp := &proto.ChannelSettlementSearchResp{}
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelSettlement.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
filter[channelmod.FN.ChannelSettlement.Channel] = *req.Channel
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelSettlement.AppId] = *req.AppId
|
||||
}
|
||||
|
||||
if req.Start != nil && req.End != nil {
|
||||
filter[channelmod.FN.ChannelSettlement.SumDate] = bson.M{"$gt": *req.Start, "$lt": *req.End}
|
||||
}
|
||||
|
||||
if req.WithChild != nil && *req.WithChild {
|
||||
filter[channelmod.FN.ChannelSettlement.Type] = channelmod.ChannelTypeParent
|
||||
}
|
||||
|
||||
total, serr := channelmod.CountChannelSettlements(ctx, filter)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
ChannelSettlementList, err := channelmod.FindManyChannelSettlement(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.WithChild != nil && *req.WithChild {
|
||||
channels := make([]string, 0)
|
||||
for _, v := range ChannelSettlementList {
|
||||
channels = append(channels, v.Channel)
|
||||
}
|
||||
childTypes := channelmod.GetChildChannelTypes()
|
||||
filter[channelmod.FN.ChannelSettlement.Type] = bson.M{"$in": childTypes}
|
||||
filter[channelmod.FN.ChannelSettlement.ParentChannel] = bson.M{"$in": channels}
|
||||
channelsettlementList, err := channelmod.FindManyChannelStat(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, channnelSettlementInfo := range channelsettlementList {
|
||||
for _, v := range ChannelSettlementList {
|
||||
if channnelSettlementInfo.ParentChannel == v.Channel {
|
||||
channnelSettlementInfo.AccumulateTo(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
resp.List = ChannelSettlementList
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SearchChannelStatCompared 渠道同时段对比
|
||||
func SearchChannelStatCompared(ctx context.Context, req *proto.SearchChannelStatComparedReq) (*proto.SearchChannelStatComparedResp, error) {
|
||||
resp := &proto.SearchChannelStatComparedResp{}
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
opt.SetSort(
|
||||
bson.D{
|
||||
{Key: channelmod.FN.ChannelStatYestoday.SumDate, Value: -1},
|
||||
{Key: channelmod.FN.ChannelStatYestoday.NewCount, Value: -1},
|
||||
},
|
||||
)
|
||||
|
||||
if req.Channel != nil && *req.Channel != "" {
|
||||
if req.WithChild != nil && *req.WithChild {
|
||||
filter = bson.M{
|
||||
"$or": bson.A{
|
||||
bson.M{channelmod.FN.ChannelStatYestoday.Channel: *req.Channel},
|
||||
bson.M{channelmod.FN.ChannelStatYestoday.ParentChannel: *req.Channel},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
filter[channelmod.FN.ChannelStatYestoday.Channel] = *req.Channel
|
||||
}
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
filter[channelmod.FN.ChannelStatYestoday.AppId] = *req.AppId
|
||||
}
|
||||
|
||||
if req.Start != nil && req.End != nil {
|
||||
filter[channelmod.FN.ChannelStatYestoday.SumDate] = bson.M{"$gte": *req.Start, "$lte": *req.End}
|
||||
}
|
||||
|
||||
if req.Settlement != nil {
|
||||
filter[channelmod.FN.ChannelStatYestoday.Settlement] = *req.Settlement
|
||||
}
|
||||
total, err := channelmod.CountChannelStatYestoday(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
channelStatList, err := channelmod.FindManyChannelStatYestoday(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
resp.List = channelStatList
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,991 @@
|
||||
package consumer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/log"
|
||||
"stat/common/statutils"
|
||||
"stat/common/topicmessage"
|
||||
"stat/common/utils"
|
||||
"stat/models/accessmod"
|
||||
"stat/models/channelmod"
|
||||
downloadinfo "stat/models/downloadinfo"
|
||||
"stat/models/rechargegamemod"
|
||||
"stat/models/rechargemod"
|
||||
"stat/models/registermod"
|
||||
"stat/models/syncgamerecharge"
|
||||
"stat/models/syncgamewater"
|
||||
"stat/models/syncrecharge"
|
||||
"stat/models/syncrecord"
|
||||
"stat/models/syncvip"
|
||||
"stat/models/syncwithdraw"
|
||||
"stat/models/withdrawGameOrdermod"
|
||||
"stat/models/withdrawUserOrdermod"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// 1.用户访问消息处理函数
|
||||
func UserAccessHandler(accessMsg *topicmessage.UserVisitMsg) error {
|
||||
|
||||
accessInfo := accessmod.AccessMod{
|
||||
UserId: accessMsg.UserId,
|
||||
AppId: accessMsg.AppId,
|
||||
SysType: accessMsg.SysType,
|
||||
DevType: accessMsg.DevType,
|
||||
IP: accessMsg.IP,
|
||||
Version: accessMsg.Version,
|
||||
DevID: accessMsg.DevID,
|
||||
VisitAt: accessMsg.VisitAt,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 查找用户信息
|
||||
userInfo, err := registermod.GetRegisterInfo(context.Background(), accessInfo.UserId, accessInfo.AppId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accessInfo.IsDirect = userInfo.IsDirect
|
||||
accessInfo.DistrictCode = userInfo.DistrictCode
|
||||
accessInfo.RegisterTime = userInfo.RegisterTime
|
||||
accessInfo.IsFormalUser = userInfo.IsFormal
|
||||
|
||||
if userInfo.UserId != accessInfo.UserId {
|
||||
accessInfo.IsDirect = true
|
||||
}
|
||||
|
||||
return accessmod.AddAccessLog(context.Background(), &accessInfo)
|
||||
}
|
||||
|
||||
// 2.用户注册消息处理函数
|
||||
func UserRegisterHandler(registerMsg *topicmessage.UserRegisterMsg) error {
|
||||
ctx := context.Background()
|
||||
|
||||
registerInfo := registermod.RegisterMod{
|
||||
UserId: registerMsg.UserId,
|
||||
AppId: registerMsg.AppId,
|
||||
SysType: registerMsg.SysType,
|
||||
DevType: registerMsg.DevType,
|
||||
Mobile: registerMsg.Mobile,
|
||||
Name: registerMsg.Name,
|
||||
IP: registerMsg.IP,
|
||||
IsDirect: true,
|
||||
DistrictCode: strings.TrimSpace(registerMsg.DistrictCode),
|
||||
DC: strings.TrimSpace(registerMsg.DistrictCode),
|
||||
DistrictFrom: registermod.DistrictFromNone,
|
||||
PromCode: registerMsg.PromCode,
|
||||
RegisterTime: registerMsg.RegisterTime,
|
||||
ParentPromCode: registerMsg.PromSeqe,
|
||||
UserAgent: registerMsg.UserAgent,
|
||||
IsDeduction: false,
|
||||
}
|
||||
var ipChecked bool
|
||||
// 检测
|
||||
testUser, err := registermod.GetRegisterInfo(ctx, registerInfo.UserId, registerInfo.AppId)
|
||||
if err != nil {
|
||||
log.Error("UserRegisterHandler define err", log.Any("UserId", testUser.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if testUser.UserId == registerInfo.UserId {
|
||||
return nil // 已存在
|
||||
}
|
||||
if len(registerInfo.ParentPromCode) > 0 && len(registerInfo.DistrictCode) == 0 {
|
||||
testUser, err := registermod.GetRegisterInfoByInviteCode(ctx, registerInfo.ParentPromCode, registerInfo.AppId)
|
||||
if err != nil || testUser.PromCode != registerInfo.ParentPromCode {
|
||||
log.Error("GetRegisterInfoByInviteCode define err", log.Any("UserId", testUser.UserId), log.E(err))
|
||||
} else {
|
||||
// 推广相关逻辑
|
||||
registerInfo.IsDirect = false
|
||||
registerInfo.DistrictFrom = registermod.DistrictFromPC
|
||||
registerInfo.DistrictCode = testUser.DistrictCode
|
||||
}
|
||||
}
|
||||
|
||||
if registerInfo.IP != "" {
|
||||
if registerInfo.DistrictCode != "" {
|
||||
channelInfo, _ := channelmod.FindOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: registerInfo.DistrictCode})
|
||||
if channelInfo == nil || channelInfo.Channel != registerInfo.DistrictCode {
|
||||
registerInfo.DistrictCode = ""
|
||||
}
|
||||
}
|
||||
|
||||
if registerInfo.DistrictCode == "" {
|
||||
// 根据IP查询是否下载
|
||||
ipChecked = true
|
||||
data, _ := downloadinfo.FindOneChannelIP(ctx, registerInfo.AppId, registerInfo.IP, registerInfo.SysType)
|
||||
if data.DC != "" {
|
||||
log.Info("channel info by IP ", log.Any("IP", registerInfo.IP), log.Any("app", registerInfo.AppId), log.Any("dc", data.DC))
|
||||
registerInfo.DistrictFrom = registermod.DistrictFromIP
|
||||
registerInfo.IpMatched = true
|
||||
registerInfo.DistrictCode = data.DC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if registerInfo.DistrictCode != "" && registerInfo.DistrictFrom == registermod.DistrictFromNone {
|
||||
registerInfo.DistrictFrom = registermod.DistrictFromDC
|
||||
}
|
||||
if !ipChecked && registerInfo.IP != "" {
|
||||
data, _ := downloadinfo.FindOneChannelIP(ctx, registerInfo.AppId, registerInfo.IP, registerInfo.SysType)
|
||||
if data.DC != "" {
|
||||
registerInfo.IpMatched = true
|
||||
}
|
||||
}
|
||||
registerInfo.BlocDeduction = false // 不扣量
|
||||
// 插入记录
|
||||
return registermod.AddRegisterLog(ctx, ®isterInfo)
|
||||
}
|
||||
|
||||
// 2.用户注册消息处理函数
|
||||
func FormalUserRegisterHandler(registerMsg *topicmessage.UserRegisterMsg) error {
|
||||
ctx := context.Background()
|
||||
|
||||
registerInfo := registermod.RegisterMod{
|
||||
UserId: registerMsg.UserId,
|
||||
AppId: registerMsg.AppId,
|
||||
SysType: registerMsg.SysType,
|
||||
DevType: registerMsg.DevType,
|
||||
Mobile: registerMsg.Mobile,
|
||||
Name: registerMsg.Name,
|
||||
IP: registerMsg.IP,
|
||||
IsDirect: true,
|
||||
DistrictCode: strings.TrimSpace(registerMsg.DistrictCode),
|
||||
DC: strings.TrimSpace(registerMsg.DistrictCode),
|
||||
DistrictFrom: registermod.DistrictFromNone,
|
||||
PromCode: registerMsg.PromCode,
|
||||
IsFormal: true,
|
||||
FormalTime: registerMsg.RegisterTime,
|
||||
RegisterTime: registerMsg.RegisterTime,
|
||||
ParentPromCode: registerMsg.PromSeqe,
|
||||
IsDeduction: false,
|
||||
}
|
||||
|
||||
// 检测
|
||||
testUser, err := registermod.GetRegisterInfo(ctx, registerInfo.UserId, registerInfo.AppId)
|
||||
if err != nil {
|
||||
log.Error("FormalUserRegisterHandler define err", log.Any("UserId", testUser.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if testUser.UserId == registerInfo.UserId {
|
||||
if testUser.RegisterTime.Before(registerInfo.FormalTime) {
|
||||
// 更新
|
||||
setdoc := bson.M{"$set": bson.M{
|
||||
"formalTime": registerInfo.FormalTime,
|
||||
"isFormal": true,
|
||||
}}
|
||||
filter := bson.M{
|
||||
"appId": registerInfo.AppId,
|
||||
"userId": registerInfo.UserId,
|
||||
}
|
||||
err := registermod.UpdateOneRegister(ctx, filter, setdoc)
|
||||
if err != nil {
|
||||
log.Error("NewCountMethod UpdateManyRegister ", log.E(err))
|
||||
}
|
||||
} else {
|
||||
log.Error("FormalUserRegisterHandler register or formal time err", log.Any("AppId", testUser.AppId), log.Any("UserId", testUser.UserId), log.Any("RegisterTime", testUser.RegisterTime), log.Any("FormalTime", registerInfo.FormalTime))
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// 插入
|
||||
if len(registerInfo.ParentPromCode) > 0 && len(registerInfo.DistrictCode) == 0 {
|
||||
testUser, err := registermod.GetRegisterInfoByInviteCode(ctx, registerInfo.ParentPromCode, registerInfo.AppId)
|
||||
if err != nil || testUser.PromCode != registerInfo.ParentPromCode {
|
||||
log.Error("GetRegisterInfoByInviteCode define err", log.Any("UserId", testUser.UserId), log.E(err))
|
||||
} else {
|
||||
// 推广相关逻辑
|
||||
registerInfo.IsDirect = false
|
||||
registerInfo.DistrictFrom = registermod.DistrictFromPC
|
||||
registerInfo.DistrictCode = testUser.DistrictCode
|
||||
}
|
||||
} else if registerInfo.DistrictCode == "" && registerInfo.IP != "" {
|
||||
data, _ := downloadinfo.FindOneChannelIP(ctx, registerInfo.AppId, registerInfo.IP, registerInfo.SysType)
|
||||
if data.DC != "" {
|
||||
log.Info("channel info by IP ", log.Any("IP", registerInfo.IP), log.Any("app", registerInfo.AppId), log.Any("dc", data.DC))
|
||||
registerInfo.DistrictFrom = registermod.DistrictFromIP
|
||||
registerInfo.DistrictCode = data.DC
|
||||
}
|
||||
}
|
||||
|
||||
registerInfo.BlocDeduction = false // 不扣量
|
||||
|
||||
// 插入记录
|
||||
return registermod.AddRegisterLog(ctx, ®isterInfo)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3.用户充值消息处理函数
|
||||
func UserReChargeHandler(rechargeMsg *topicmessage.UserRechargeMsg) error {
|
||||
ctx := context.Background()
|
||||
|
||||
rechargeInfo := rechargemod.RechargeMod{
|
||||
UserId: rechargeMsg.UserId,
|
||||
AppId: rechargeMsg.AppId,
|
||||
PlatformId: rechargeMsg.PlatformId,
|
||||
SysType: rechargeMsg.SysType,
|
||||
DevType: rechargeMsg.DevType,
|
||||
ChannelName: rechargeMsg.ChannelName,
|
||||
CID: rechargeMsg.CID,
|
||||
Type: rechargeMsg.Type,
|
||||
OrderId: rechargeMsg.OrderId,
|
||||
Money: rechargeMsg.Money,
|
||||
PayMoney: rechargeMsg.PayMoney,
|
||||
Status: 3,
|
||||
Rate: rechargeMsg.Rate,
|
||||
SuccessAt: rechargeMsg.SuccessAt,
|
||||
Repeat: false,
|
||||
}
|
||||
// 检测订单是否存在
|
||||
testRechargere, err := rechargemod.FindRechargeByOrder(ctx, rechargeInfo.AppId, rechargeInfo.OrderId)
|
||||
if err != nil || testRechargere.OrderId == rechargeInfo.OrderId {
|
||||
log.Error("UserReChargeHandler define err ", log.Any("OrderId", testRechargere.OrderId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if testRechargere.OrderId == rechargeInfo.OrderId {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查找用户信息
|
||||
userInfo, err := registermod.GetRegisterInfo(context.Background(), rechargeInfo.UserId, rechargeInfo.AppId)
|
||||
if err != nil {
|
||||
log.Error("UserReChargeHandler GetRegisterInfo failed", log.Any("UserId", rechargeInfo.UserId), log.Any("AppId", rechargeInfo.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if userInfo.UserId == rechargeInfo.UserId {
|
||||
rechargeInfo.IsDirect = userInfo.IsDirect
|
||||
rechargeInfo.DistrictCode = userInfo.DistrictCode
|
||||
rechargeInfo.RegisterTime = userInfo.RegisterTime
|
||||
rechargeInfo.PromCode = userInfo.PromCode
|
||||
rechargeInfo.IsFormalUser = userInfo.IsFormal
|
||||
// 判断用户是否充值
|
||||
hasRechargere, _ := rechargemod.FindRechargeByUser(ctx, rechargeInfo.AppId, int64(rechargeInfo.UserId))
|
||||
if hasRechargere.UserId == rechargeInfo.UserId {
|
||||
rechargeInfo.Repeat = true
|
||||
}
|
||||
|
||||
}
|
||||
// 更新记录
|
||||
err = rechargemod.AddRechargeLog(ctx, &rechargeInfo)
|
||||
if err != nil {
|
||||
log.Error("UserRegisterHandler AddRechargeLog err", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 集团
|
||||
return RechargeChannelHandle(ctx, &rechargeInfo)
|
||||
|
||||
//fmt.Println("DistrictCode kafka recharge queue:", rechargeInfo)
|
||||
}
|
||||
|
||||
// 4.用户提现
|
||||
func UserWithdrawOrder(UserWithdrawMsg *topicmessage.UserWithdrawMsg) error {
|
||||
ctx := context.Background()
|
||||
|
||||
withdrawInfo := withdrawUserOrdermod.UserWithdrawOrder{
|
||||
AppId: UserWithdrawMsg.AppId,
|
||||
UID: UserWithdrawMsg.UID,
|
||||
Name: UserWithdrawMsg.Name,
|
||||
OID: UserWithdrawMsg.OID,
|
||||
Money: UserWithdrawMsg.Money,
|
||||
PayMoney: UserWithdrawMsg.PayMoney,
|
||||
PayType: UserWithdrawMsg.PayType,
|
||||
ActName: UserWithdrawMsg.ActName,
|
||||
Act: UserWithdrawMsg.Act,
|
||||
DeviceType: UserWithdrawMsg.DeviceType,
|
||||
DevID: UserWithdrawMsg.DevID,
|
||||
Status: UserWithdrawMsg.Status,
|
||||
SuccessAt: UserWithdrawMsg.SuccessAt,
|
||||
UpdatedAt: UserWithdrawMsg.UpdatedAt,
|
||||
CreatedAt: UserWithdrawMsg.CreatedAt,
|
||||
}
|
||||
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, UserWithdrawMsg.UID, UserWithdrawMsg.AppId)
|
||||
if err != nil {
|
||||
log.Error("rechargeDeduction GetRegisterInfo failed", log.Any("UserId", UserWithdrawMsg.UID), log.Any("AppId", UserWithdrawMsg.UID), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 兼容找不到用户
|
||||
if userInfo.UserId == UserWithdrawMsg.UID {
|
||||
withdrawInfo.IsDirect = userInfo.IsDirect
|
||||
withdrawInfo.DistrictCode = userInfo.DistrictCode
|
||||
withdrawInfo.PromCode = userInfo.PromCode
|
||||
withdrawInfo.IsFormalUser = userInfo.IsFormal
|
||||
} else {
|
||||
withdrawInfo.IsDirect = true
|
||||
}
|
||||
// 更新记录
|
||||
_, err = withdrawUserOrdermod.AddWithdrawOrder(ctx, &withdrawInfo)
|
||||
if err != nil {
|
||||
log.Error("UserWithdrawOrder AddWithdrawOrder: %s\n", log.E(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
//fmt.Println("DistrictCode kafka withdraw queue:", withdrawInfo)
|
||||
}
|
||||
|
||||
// 4.充值全量
|
||||
func UserSyncRechargeOrderALL(rechargeOrder *topicmessage.UserRechargeMsg) error {
|
||||
ctx := context.Background()
|
||||
rechargeInfo := &syncrecharge.SyncRechargeOrder{
|
||||
UserId: rechargeOrder.UserId,
|
||||
AppId: rechargeOrder.AppId,
|
||||
PlatformId: rechargeOrder.PlatformId,
|
||||
SysType: rechargeOrder.SysType,
|
||||
DevType: rechargeOrder.DevType,
|
||||
ChannelName: rechargeOrder.ChannelName,
|
||||
CID: rechargeOrder.CID,
|
||||
Type: rechargeOrder.Type,
|
||||
OrderId: rechargeOrder.OrderId,
|
||||
Money: rechargeOrder.Money,
|
||||
PayMoney: rechargeOrder.PayMoney,
|
||||
Status: rechargeOrder.Status,
|
||||
Rate: rechargeOrder.Rate,
|
||||
SuccessAt: rechargeOrder.SuccessAt,
|
||||
CreatedAt: rechargeOrder.CreatedAt,
|
||||
}
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, rechargeOrder.UserId, rechargeOrder.AppId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if userInfo.UserId == rechargeInfo.UserId {
|
||||
rechargeInfo.IsDirect = userInfo.IsDirect
|
||||
rechargeInfo.DistrictCode = userInfo.DistrictCode
|
||||
rechargeInfo.PromCode = userInfo.PromCode
|
||||
rechargeInfo.IsFormalUser = userInfo.IsFormal
|
||||
} else {
|
||||
rechargeInfo.IsDirect = true
|
||||
}
|
||||
if rechargeInfo.AppId == 494 {
|
||||
log.Error("InsertOneSyncRechargeOrder@@@", zap.Any("rechargeInfo", rechargeInfo))
|
||||
orderinfo, _ := syncrecharge.FindOneSyncRechargeOrder(ctx, bson.M{syncrecharge.FN.SyncRechargeOrder.AppId: rechargeInfo.AppId,
|
||||
syncrecharge.FN.SyncRechargeOrder.UserId: rechargeInfo.UserId, syncrecharge.FN.SyncRechargeOrder.PlatformId: rechargeInfo.PlatformId})
|
||||
if rechargeInfo.PlatformId == orderinfo.PlatformId {
|
||||
log.Error("InsertOneSyncVip repeat", zap.Any("rechargeInfo", rechargeInfo))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return syncrecharge.InsertOneSyncRechargeOrder(ctx, rechargeInfo)
|
||||
}
|
||||
|
||||
// 4.提现全量
|
||||
func UserSyncWithdrawOrderALL(UserWithdrawMsg *topicmessage.UserWithdrawMsg) error {
|
||||
ctx := context.Background()
|
||||
withdrawOrder := &syncwithdraw.SyncWithdraw{
|
||||
AppId: UserWithdrawMsg.AppId,
|
||||
UID: UserWithdrawMsg.UID,
|
||||
Name: UserWithdrawMsg.Name,
|
||||
OID: UserWithdrawMsg.OID,
|
||||
Money: UserWithdrawMsg.Money,
|
||||
PayMoney: UserWithdrawMsg.PayMoney,
|
||||
PayType: UserWithdrawMsg.PayType,
|
||||
ActName: UserWithdrawMsg.ActName,
|
||||
Act: UserWithdrawMsg.Act,
|
||||
DeviceType: UserWithdrawMsg.DeviceType,
|
||||
DevID: UserWithdrawMsg.DevID,
|
||||
Status: UserWithdrawMsg.Status,
|
||||
SuccessAt: UserWithdrawMsg.SuccessAt,
|
||||
UpdatedAt: UserWithdrawMsg.UpdatedAt,
|
||||
CreatedAt: UserWithdrawMsg.CreatedAt,
|
||||
}
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, withdrawOrder.UID, withdrawOrder.AppId)
|
||||
if err != nil {
|
||||
log.Error("UserSyncWithdrawOrderALL GetRegisterInfo failed", log.Any("UserId", withdrawOrder.UID), log.Any("AppId", withdrawOrder.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if userInfo.UserId != withdrawOrder.UID {
|
||||
log.Error("UserSyncWithdrawOrderALL GetRegisterInfo failed", log.Any("UserId", withdrawOrder.UID), log.Any("AppId", withdrawOrder.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
}
|
||||
withdrawOrder.IsDirect = userInfo.IsDirect
|
||||
withdrawOrder.DistrictCode = userInfo.DistrictCode
|
||||
withdrawOrder.PromCode = userInfo.PromCode
|
||||
withdrawOrder.IsFormalUser = userInfo.IsFormal
|
||||
|
||||
err = syncwithdraw.InsertOneSyncWithdraw(ctx, withdrawOrder)
|
||||
if err != nil {
|
||||
log.Error("UserSyncWithdrawOrderALL InsertOneSyncWithdraw failed", log.Any("UserId", withdrawOrder.UID), log.Any("AppId", withdrawOrder.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 5.VIP相关处理
|
||||
func UserSyncVIPOrderALL(UserBuyVipwMsg *topicmessage.UserBuyVIPMsg) error {
|
||||
ctx := context.Background()
|
||||
buyVipOrder := &syncvip.SyncVip{
|
||||
PlatformId: UserBuyVipwMsg.PlatformId,
|
||||
AppId: UserBuyVipwMsg.AppId,
|
||||
UID: UserBuyVipwMsg.UID,
|
||||
Name: UserBuyVipwMsg.Name,
|
||||
Money: UserBuyVipwMsg.Money, // 单位转换成分
|
||||
Amount: UserBuyVipwMsg.Amount * 100, // 单位转换成分
|
||||
BuyVIPType: UserBuyVipwMsg.BuyVIPType,
|
||||
OID: UserBuyVipwMsg.OID,
|
||||
DeviceType: UserBuyVipwMsg.DeviceType,
|
||||
Status: UserBuyVipwMsg.Status,
|
||||
Repeat: false,
|
||||
SuccessAt: UserBuyVipwMsg.SuccessAt,
|
||||
IsDeduction: false,
|
||||
}
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, UserBuyVipwMsg.UID, UserBuyVipwMsg.AppId)
|
||||
if err != nil {
|
||||
log.Error("UserSyncVIPOrderALL GetRegisterInfo failed", log.Any("UserId", UserBuyVipwMsg.UID), log.Any("AppId", UserBuyVipwMsg.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if userInfo.UserId == buyVipOrder.UID {
|
||||
buyVipOrder.IsDirect = userInfo.IsDirect
|
||||
buyVipOrder.DistrictCode = userInfo.DistrictCode
|
||||
buyVipOrder.PromCode = userInfo.PromCode
|
||||
buyVipOrder.RegisterTime = userInfo.RegisterTime
|
||||
buyVipOrder.IsFormalUser = userInfo.IsFormal
|
||||
|
||||
orderinfo, _ := syncvip.FindOneSyncVip(ctx, bson.M{syncvip.FN.SyncVip.AppId: buyVipOrder.AppId, syncvip.FN.SyncVip.UID: buyVipOrder.UID})
|
||||
if orderinfo.UID == buyVipOrder.UID {
|
||||
buyVipOrder.Repeat = true
|
||||
}
|
||||
}
|
||||
if buyVipOrder.AppId == 494 {
|
||||
log.Error("InsertOneSyncVip@@@", zap.Any("buyVipOrder", buyVipOrder))
|
||||
orderinfo, _ := syncvip.FindOneSyncVip(ctx, bson.M{syncvip.FN.SyncVip.AppId: buyVipOrder.AppId,
|
||||
syncvip.FN.SyncVip.UID: buyVipOrder.UID, syncvip.FN.SyncVip.PlatformId: buyVipOrder.PlatformId})
|
||||
if orderinfo.PlatformId == buyVipOrder.PlatformId {
|
||||
log.Error("InsertOneSyncVip repeat", zap.Any("buyVipOrder", buyVipOrder))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = syncvip.InsertOneSyncVip(ctx, buyVipOrder)
|
||||
if err != nil {
|
||||
log.Error("UserSyncVIPOrderALL InsertOneSyncVip failed", log.Any("UserId", UserBuyVipwMsg.UID), log.Any("AppId", UserBuyVipwMsg.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 7.游戏充值
|
||||
func GameReChargeHandler(rechargeMsg *topicmessage.UserRechargeMsg) error {
|
||||
ctx := context.Background()
|
||||
|
||||
rechargeInfo := rechargegamemod.GameRechargeMod{
|
||||
UserId: rechargeMsg.UserId,
|
||||
AppId: rechargeMsg.AppId,
|
||||
PlatformId: rechargeMsg.PlatformId,
|
||||
SysType: rechargeMsg.SysType,
|
||||
DevType: rechargeMsg.DevType,
|
||||
ChannelName: rechargeMsg.ChannelName,
|
||||
CID: rechargeMsg.CID,
|
||||
Type: rechargeMsg.Type,
|
||||
OrderId: rechargeMsg.OrderId,
|
||||
Money: rechargeMsg.Money,
|
||||
PayMoney: rechargeMsg.PayMoney,
|
||||
Status: 3,
|
||||
SyncStatus: rechargeMsg.SyncStatus,
|
||||
Rate: rechargeMsg.Rate,
|
||||
SuccessAt: rechargeMsg.SuccessAt,
|
||||
Repeat: false,
|
||||
}
|
||||
// 检测订单是否存在
|
||||
testRechargere, err := rechargegamemod.FindRechargeByOrder(ctx, rechargeInfo.AppId, rechargeInfo.OrderId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if testRechargere.OrderId == rechargeInfo.OrderId {
|
||||
return nil // 订单已存在
|
||||
}
|
||||
|
||||
// 查找用户信息
|
||||
userInfo, err := registermod.GetRegisterInfo(context.Background(), rechargeInfo.UserId, rechargeInfo.AppId)
|
||||
if err != nil {
|
||||
log.Error("UserAccessHandler GetRegisterInfo failed", log.Any("UserId", rechargeInfo.UserId), log.Any("AppId", rechargeInfo.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
if userInfo.UserId == rechargeInfo.UserId {
|
||||
rechargeInfo.IsDirect = userInfo.IsDirect
|
||||
rechargeInfo.DistrictCode = userInfo.DistrictCode
|
||||
rechargeInfo.RegisterTime = userInfo.RegisterTime
|
||||
rechargeInfo.PromCode = userInfo.PromCode
|
||||
rechargeInfo.IsFormalUser = userInfo.IsFormal
|
||||
// 判断用户是否充值
|
||||
hasRechargere, _ := rechargegamemod.FindRechargeByUser(ctx, rechargeInfo.AppId, int64(rechargeInfo.UserId))
|
||||
if hasRechargere.UserId == rechargeInfo.UserId {
|
||||
rechargeInfo.Repeat = true
|
||||
}
|
||||
}
|
||||
// 更新记录
|
||||
err = rechargegamemod.AddRechargeLog(ctx, &rechargeInfo)
|
||||
if err != nil {
|
||||
log.Error("UserRegisterHandler AddRechargeLog err", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
// 集团
|
||||
// RechargeChannelHandle(ctx, &rechargeInfo)
|
||||
|
||||
//fmt.Println("DistrictCode kafka recharge queue:", rechargeInfo)
|
||||
|
||||
}
|
||||
|
||||
// 8.游戏充值全量
|
||||
func GameRechargeOrderALL(rechargeOrder *topicmessage.UserRechargeMsg) error {
|
||||
ctx := context.Background()
|
||||
rechargeInfo := &syncgamerecharge.SyncGameRechargeOrder{
|
||||
UserId: rechargeOrder.UserId,
|
||||
AppId: rechargeOrder.AppId,
|
||||
PlatformId: rechargeOrder.PlatformId,
|
||||
SysType: rechargeOrder.SysType,
|
||||
DevType: rechargeOrder.DevType,
|
||||
ChannelName: rechargeOrder.ChannelName,
|
||||
CID: rechargeOrder.CID,
|
||||
Type: rechargeOrder.Type,
|
||||
OrderId: rechargeOrder.OrderId,
|
||||
Money: rechargeOrder.Money,
|
||||
PayMoney: rechargeOrder.PayMoney,
|
||||
Status: rechargeOrder.Status,
|
||||
Rate: rechargeOrder.Rate,
|
||||
SyncStatus: rechargeOrder.SyncStatus,
|
||||
SuccessAt: rechargeOrder.SuccessAt,
|
||||
CreatedAt: rechargeOrder.CreatedAt,
|
||||
}
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, rechargeOrder.UserId, rechargeOrder.AppId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if userInfo.UserId == rechargeInfo.UserId {
|
||||
rechargeInfo.IsDirect = userInfo.IsDirect
|
||||
rechargeInfo.DistrictCode = userInfo.DistrictCode
|
||||
rechargeInfo.PromCode = userInfo.PromCode
|
||||
rechargeInfo.IsFormalUser = userInfo.IsFormal
|
||||
} else {
|
||||
rechargeInfo.IsDirect = true
|
||||
}
|
||||
|
||||
err = syncgamerecharge.InsertOneSyncGameRechargeOrder(ctx, rechargeInfo)
|
||||
if err != nil {
|
||||
log.Error("GameRechargeOrderALL InsertOneSyncRechargeOrder failed", log.Any("UserId", rechargeOrder.UserId), log.Any("AppId", rechargeOrder.AppId), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 9.游戏提现
|
||||
func GameWithdrawOrder(UserWithdrawMsg *topicmessage.UserWithdrawMsg) error {
|
||||
ctx := context.Background()
|
||||
|
||||
withdrawInfo := withdrawGameOrdermod.GameWithdrawOrder{
|
||||
AppId: UserWithdrawMsg.AppId,
|
||||
UID: UserWithdrawMsg.UID,
|
||||
Name: UserWithdrawMsg.Name,
|
||||
OID: UserWithdrawMsg.OID,
|
||||
Money: UserWithdrawMsg.Money,
|
||||
PayMoney: UserWithdrawMsg.PayMoney,
|
||||
PayType: UserWithdrawMsg.PayType,
|
||||
ActName: UserWithdrawMsg.ActName,
|
||||
Act: UserWithdrawMsg.Act,
|
||||
DeviceType: UserWithdrawMsg.DeviceType,
|
||||
DevID: UserWithdrawMsg.DevID,
|
||||
Status: UserWithdrawMsg.Status,
|
||||
SuccessAt: UserWithdrawMsg.SuccessAt,
|
||||
SyncStatus: UserWithdrawMsg.SyncStatus,
|
||||
PayChannel: UserWithdrawMsg.PayChannel,
|
||||
UpdatedAt: UserWithdrawMsg.UpdatedAt,
|
||||
CreatedAt: UserWithdrawMsg.CreatedAt,
|
||||
}
|
||||
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, UserWithdrawMsg.UID, UserWithdrawMsg.AppId)
|
||||
if err != nil {
|
||||
log.Error("rechargeDeduction GetRegisterInfo failed", log.Any("UserId", UserWithdrawMsg.UID), log.Any("AppId", UserWithdrawMsg.UID), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 兼容找不到用户
|
||||
if userInfo.UserId == UserWithdrawMsg.UID {
|
||||
withdrawInfo.IsDirect = userInfo.IsDirect
|
||||
withdrawInfo.DistrictCode = userInfo.DistrictCode
|
||||
withdrawInfo.PromCode = userInfo.PromCode
|
||||
withdrawInfo.IsFormalUser = userInfo.IsFormal
|
||||
} else {
|
||||
withdrawInfo.IsDirect = true
|
||||
}
|
||||
|
||||
// 更新记录
|
||||
_, err = withdrawGameOrdermod.AddWithdrawOrder(ctx, &withdrawInfo)
|
||||
if err != nil {
|
||||
log.Error("UserWithdrawOrder AddWithdrawOrder: %s\n", log.E(err))
|
||||
}
|
||||
|
||||
return err
|
||||
//fmt.Println("DistrictCode kafka withdraw queue:", withdrawInfo)
|
||||
}
|
||||
|
||||
// 10.游戏提现
|
||||
func UserGameWater(useGamerWater *topicmessage.UserGameWaterMsg) error {
|
||||
ctx := context.Background()
|
||||
now := time.Now()
|
||||
|
||||
gameWaterInfo := syncgamewater.SyncGameWater{
|
||||
AppId: useGamerWater.AppId,
|
||||
UserId: useGamerWater.UID,
|
||||
PlatformId: useGamerWater.PlatformId,
|
||||
Date: useGamerWater.Date,
|
||||
ValidBet: useGamerWater.ValidBet,
|
||||
Profit: useGamerWater.Profit,
|
||||
Revenue: useGamerWater.Revenue,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
userInfo, err := registermod.GetRegisterInfo(ctx, useGamerWater.UID, useGamerWater.AppId)
|
||||
if err != nil {
|
||||
log.Error("rechargeDeduction GetRegisterInfo failed", log.Any("UserId", useGamerWater.UID), log.Any("AppId", useGamerWater.UID), log.Any("UserId", userInfo.UserId), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 兼容找不到用户
|
||||
if userInfo.UserId == useGamerWater.UID {
|
||||
gameWaterInfo.IsDirect = userInfo.IsDirect
|
||||
gameWaterInfo.DistrictCode = userInfo.DistrictCode
|
||||
gameWaterInfo.PromCode = userInfo.PromCode
|
||||
gameWaterInfo.RegisterTime = userInfo.RegisterTime
|
||||
gameWaterInfo.IsFormalUser = userInfo.IsFormal
|
||||
} else {
|
||||
gameWaterInfo.IsDirect = true
|
||||
}
|
||||
// 更新记录
|
||||
err = syncgamewater.InsertOneSyncGameWater(ctx, &gameWaterInfo)
|
||||
if err != nil {
|
||||
log.Error("UserWithdrawOrder AddWithdrawOrder: %s\n", log.E(err))
|
||||
}
|
||||
|
||||
return err
|
||||
//fmt.Println("DistrictCode kafka gamewater queue:", gameWaterInfo)
|
||||
}
|
||||
|
||||
func RunSyncJob(ctx context.Context) error {
|
||||
recoredResult, err := syncrecord.LastRecordTimeMap(ctx) //第一次从默认记录时间开始统计
|
||||
if err != nil {
|
||||
log.Error("statdistrict RunStat lastRecordTime failed!", log.E(err))
|
||||
return err
|
||||
}
|
||||
for _, record := range recoredResult {
|
||||
log.Info("RunSyncJob start", log.Any("start", time.Now()),
|
||||
log.Any("record", record.AppId), log.Any("job", record.Job))
|
||||
if record.Disabled {
|
||||
continue
|
||||
}
|
||||
if !statg.GlobalEndpoints.GetIndexEndpoints(statg.Conf.ServerIndex).ExistsByAppId(record.AppId) {
|
||||
continue // 不属于这个服务器的APP
|
||||
}
|
||||
// 任务开始
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.SetJobBegin(time.Now())
|
||||
item.CurrentCount = 0
|
||||
item.Error = nil
|
||||
})
|
||||
err := processJobItem(ctx, &record)
|
||||
// 任务结束
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.Error = err
|
||||
item.SetJobEnd(time.Now())
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
var cErr *ContinueError
|
||||
if !errors.As(err, &cErr) {
|
||||
log.Info("RunSyncJob end err", log.Any("err", err))
|
||||
// return err
|
||||
}
|
||||
}
|
||||
log.Info("RunSyncJob end", log.Any("end", time.Now()),
|
||||
log.Any("record", record.AppId), log.Any("job", record.Job))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func processJobRequest(ctx context.Context, record *syncrecord.SyncRecord) (*topicmessage.StatcenterSyncResp, error) {
|
||||
request := &topicmessage.StatcenterSyncReq{
|
||||
Job: record.Job,
|
||||
UserId: record.UserId,
|
||||
PlatformId: record.PlatformId,
|
||||
SuccessTime: record.SuccessAt,
|
||||
MaxSize: 5000,
|
||||
}
|
||||
url, err := AppCallbackUrl(record.AppId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiUrl := url + "/api/web/statcenter/sync"
|
||||
var resp *topicmessage.StatcenterSyncResp
|
||||
if err = HttpRequest(ctx, apiUrl, &request, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Code != 200 {
|
||||
return nil, NewContinueError(fmt.Sprintf("http request code %d, url: %s", resp.Code, apiUrl))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func processJobItem(ctx context.Context, record *syncrecord.SyncRecord) error {
|
||||
// 请求开始
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.RequestBegin = time.Now()
|
||||
})
|
||||
resp, err := processJobRequest(ctx, record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 请求结束
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.Error = err
|
||||
item.RequestEnd = time.Now()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理开始
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.ProcessBegin = time.Now()
|
||||
})
|
||||
err = processJobData(ctx, record, resp, func(dataLatest time.Time, incValue int) {
|
||||
// 处理中
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.IncCount(int64(incValue))
|
||||
item.DataLatest = dataLatest
|
||||
})
|
||||
})
|
||||
// 处理结束
|
||||
statutils.GlobalSyncMonitor.SetData(record.AppId, record.Job, func(item *statutils.SyncItem) {
|
||||
item.ProcessEnd = time.Now()
|
||||
item.Error = err
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func processJobData(ctx context.Context, record *syncrecord.SyncRecord, resp *topicmessage.StatcenterSyncResp, onItem func(dataLatest time.Time, incValue int)) error {
|
||||
if record.Job == syncrecord.VisitJob {
|
||||
for _, v := range resp.VisitList {
|
||||
if err := UserAccessHandler(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UserId
|
||||
record.SuccessAt = v.VisitAt
|
||||
record.PlatformId = v.PlatformId
|
||||
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.VisitList))
|
||||
} else if record.Job == syncrecord.RechargeJob {
|
||||
for _, v := range resp.RechargeList {
|
||||
if err := UserReChargeHandler(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UserId
|
||||
record.SuccessAt = v.SuccessAt
|
||||
record.PlatformId = v.PlatformId
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.RechargeList))
|
||||
} else if record.Job == syncrecord.RegisterJob {
|
||||
|
||||
for _, v := range resp.RegisterList {
|
||||
if err := UserRegisterHandler(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record.PlatformId = v.PlatformId
|
||||
record.SuccessAt = v.RegisterTime
|
||||
record.UserId = v.UserId
|
||||
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.RegisterList))
|
||||
} else if record.Job == syncrecord.RegisterLightJob {
|
||||
var InvalidDataCount int
|
||||
var lastError error
|
||||
for _, v := range resp.RegisterList {
|
||||
if !statutils.IsClientH5OrLight(v.SysType, v.DevType) {
|
||||
InvalidDataCount++
|
||||
lastError = NewContinueError(fmt.Sprintf("系统类型不对,总无效:%d,Job:%s,App:%d,User:%d,SysType:%s,DevType:%s", InvalidDataCount, record.Job, record.AppId, v.UserId, v.SysType, v.DevType))
|
||||
continue
|
||||
}
|
||||
if err := FormalUserRegisterHandler(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record.PlatformId = v.PlatformId
|
||||
record.SuccessAt = v.RegisterTime
|
||||
record.UserId = v.UserId
|
||||
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.RegisterList)-InvalidDataCount)
|
||||
if lastError != nil {
|
||||
return lastError
|
||||
}
|
||||
} else if record.Job == syncrecord.WithdrawJob {
|
||||
for _, v := range resp.WithdrawList {
|
||||
if err := UserWithdrawOrder(v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record.UserId = v.UID
|
||||
record.SuccessAt = v.SuccessAt
|
||||
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.WithdrawList))
|
||||
} else if record.Job == syncrecord.VipJob {
|
||||
if record.AppId == 494 {
|
||||
log.Error("debug@@@", zap.Any("resp", resp))
|
||||
}
|
||||
for _, v := range resp.VipList {
|
||||
if err := UserSyncVIPOrderALL(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UID
|
||||
record.SuccessAt = v.SuccessAt
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.VipList))
|
||||
} else if record.Job == syncrecord.WithdrawAll {
|
||||
for _, v := range resp.WithdrawList {
|
||||
if err := UserSyncWithdrawOrderALL(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UID
|
||||
record.SuccessAt = v.CreatedAt
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.WithdrawList))
|
||||
} else if record.Job == syncrecord.RechargeAll {
|
||||
if record.AppId == 494 {
|
||||
log.Error("debug@@@", zap.Any("resp", resp))
|
||||
}
|
||||
for _, v := range resp.RechargeList {
|
||||
if err := UserSyncRechargeOrderALL(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UserId
|
||||
record.SuccessAt = v.CreatedAt
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.RechargeList))
|
||||
} else if record.Job == syncrecord.GameRechargeOrder {
|
||||
for _, v := range resp.GameRecharge {
|
||||
if err := GameReChargeHandler(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UserId
|
||||
record.SuccessAt = v.SuccessAt
|
||||
record.PlatformId = v.PlatformId
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.GameRecharge))
|
||||
} else if record.Job == syncrecord.GameRechargeOrderAll {
|
||||
for _, v := range resp.GameRecharge {
|
||||
if err := GameRechargeOrderALL(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UserId
|
||||
record.SuccessAt = v.CreatedAt
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.GameRecharge))
|
||||
} else if record.Job == syncrecord.GameWithdrawOrder {
|
||||
for _, v := range resp.GameWithdraw {
|
||||
if err := GameWithdrawOrder(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UID
|
||||
record.SuccessAt = v.SuccessAt
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.GameWithdraw))
|
||||
} else if record.Job == syncrecord.GameWater {
|
||||
for _, v := range resp.GameWater {
|
||||
if err := UserGameWater(v); err != nil {
|
||||
return err
|
||||
}
|
||||
record.UserId = v.UID
|
||||
record.SuccessAt = v.SuccessAt
|
||||
if err := syncrecord.UpsertItemRecord(ctx, record.Job, record.AppId, record.UserId, record.PlatformId, record.SuccessAt); err != nil {
|
||||
log.Error("RunSyncJob 更新同步记录!", log.E(err))
|
||||
return err // keep loop, nil
|
||||
}
|
||||
}
|
||||
onItem(record.SuccessAt, len(resp.GameWater))
|
||||
} else {
|
||||
log.Error("RunSyncJob 类型不支持!", log.Any("job", record.Job))
|
||||
return errors.New("RunSyncJob 类型不支持")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 周期执行
|
||||
func SyncInit() {
|
||||
if statg.Conf.RunMod != consts.RunModRelease {
|
||||
return
|
||||
}
|
||||
defer utils.RecoverError(nil)
|
||||
ticker := time.NewTicker(time.Second * 20) // 20秒一次
|
||||
ctx := context.Background()
|
||||
|
||||
for t := range ticker.C {
|
||||
fmt.Println("Begin task", t)
|
||||
// 定时去app拉去任务
|
||||
if statg.Conf.KafkaServer {
|
||||
statutils.ServerMon.RunWithName(func() error {
|
||||
return RunSyncJob(ctx)
|
||||
}, statutils.GetFuncName(RunSyncJob))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func AppCallbackUrl(appid int32) (url string, err error) {
|
||||
info := statg.GlobalAppIdEndpoints[appid]
|
||||
if info != nil {
|
||||
if len(info.ServerUrl) == 0 {
|
||||
return "", NewContinueError(fmt.Sprintf("appid %d is not config server url", appid))
|
||||
}
|
||||
return info.ServerUrl, nil
|
||||
}
|
||||
return "", NewContinueError("appid err")
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package consumer
|
||||
|
||||
import (
|
||||
"context"
|
||||
downloadinfo "stat/models/downloadinfo"
|
||||
)
|
||||
|
||||
func TrackDcAndPcByIp(ctx context.Context, appId int32, ip, sysType string) (dc, pc string) {
|
||||
data, _ := downloadinfo.FindOneChannelIP(ctx, appId, ip, sysType)
|
||||
|
||||
return data.DC, data.PC
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package consumer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/models/rechargechannelmod"
|
||||
"stat/models/rechargemod"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func RechargeChannelHandle(ctx context.Context, rechageInfo *rechargemod.RechargeMod) error {
|
||||
sumDate := utils.BeginWithTime(rechageInfo.SuccessAt)
|
||||
rate := float64(4)
|
||||
if rechageInfo.Rate != "" && rechageInfo.ChannelName != "daichong" {
|
||||
rateF, err := strconv.ParseFloat(rechageInfo.Rate, 64)
|
||||
if err != nil {
|
||||
log.Error("RechargeChannelHandle rate is err", log.Any("rate", rechageInfo.Rate), log.Any("userid", rechageInfo.UserId), log.Any("appid", rechageInfo.AppId))
|
||||
return err
|
||||
}
|
||||
rate = rateF
|
||||
} else if rechageInfo.ChannelName == "daichong" {
|
||||
rate = float64(8)
|
||||
rechageInfo.CID = "daichong"
|
||||
}
|
||||
channelMoney := int64((rate * float64(rechageInfo.PayMoney)) / 100)
|
||||
err := rechargechannelmod.UpsertOneUserRechargeStat(ctx, rechageInfo.CID, rechageInfo.AppId, sumDate, rechageInfo.BlocDeduction, rechageInfo.Money, rechageInfo.PayMoney, channelMoney)
|
||||
if err != nil {
|
||||
log.Error("RechargeChannelHandle UpsertOneUserRechargeStat", log.Any("rate", rechageInfo.Rate), log.Any("userid", rechageInfo.UserId), log.Any("appid", rechageInfo.AppId), log.E(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package consumer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/utils"
|
||||
)
|
||||
|
||||
type ContinueError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func NewContinueError(text string) error {
|
||||
ret := &ContinueError{
|
||||
msg: text,
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e *ContinueError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
// HttpRequest HttpRequest
|
||||
func HttpRequest(ctx context.Context, url string, request interface{}, resp interface{}) error {
|
||||
return utils.HttpPostRequestJson(url, request, resp)
|
||||
}
|
||||
@@ -0,0 +1,568 @@
|
||||
package crossSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"stat/api/proto"
|
||||
"stat/common"
|
||||
"stat/common/statutils"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/utils"
|
||||
"stat/models/botdailyhourstat"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/wcharczuk/go-chart/v2"
|
||||
|
||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||
|
||||
"github.com/vicanso/go-charts/v2"
|
||||
)
|
||||
|
||||
var Colors = []drawing.Color{
|
||||
drawing.ColorFromHex("0780cf"),
|
||||
drawing.ColorFromHex("765005"),
|
||||
drawing.ColorFromHex("fa6d1d"),
|
||||
drawing.ColorFromHex("0e2c82"),
|
||||
drawing.ColorFromHex("b6b51f"),
|
||||
drawing.ColorFromHex("da1f18"),
|
||||
drawing.ColorFromHex("701866"),
|
||||
drawing.ColorFromHex("f47a75"),
|
||||
drawing.ColorFromHex("009db2"),
|
||||
drawing.ColorFromHex("024b51"),
|
||||
drawing.ColorFromHex("0780cf"),
|
||||
drawing.ColorFromHex("765005"),
|
||||
}
|
||||
|
||||
type CrossStatMethod = func(ctx context.Context, cmd *tgbot.Command) error
|
||||
|
||||
// 统计方法汇总
|
||||
var CrossStatMethodMap = map[string]CrossStatMethod{
|
||||
statutils.StatProjectRecharge: CrossStatRecharge,
|
||||
statutils.StatProjectRechargeNew: CrossStatRechargeNew,
|
||||
statutils.StatProjectRechargeOld: CrossStatRechargeOld,
|
||||
statutils.StatProjectRechargeGold: CrossStatRechargeGold,
|
||||
statutils.StatProjectRechargeVip: CrossStatRechargeVip,
|
||||
statutils.StatProjectRegister: CrossStatRegister,
|
||||
statutils.StatProjectVisit: CrossStatVisit,
|
||||
statutils.StatProjectOrder: CrossStatOrder,
|
||||
}
|
||||
|
||||
type CrossAppDownloadRes struct {
|
||||
Code int32 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Tip string `json:"tip"`
|
||||
Data *proto.PageButtonClickResp `json:"data"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
type CrossStatRes struct {
|
||||
Code int32 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Tip string `json:"tip"`
|
||||
Data *proto.BotDailyHourListResp `json:"data"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
type CrossMonthRes struct {
|
||||
Code int32 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Tip string `json:"tip"`
|
||||
Data *proto.BotMonthStatResp `json:"data"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
type StatItem struct {
|
||||
Value int64
|
||||
Stat *botdailyhourstat.BotHourStat
|
||||
}
|
||||
|
||||
type FuncGetStatValue func(item *botdailyhourstat.BotHourStat) int64
|
||||
|
||||
type StatItemList []*StatItem
|
||||
|
||||
func (a StatItemList) Len() int { return len(a) }
|
||||
func (a StatItemList) Less(i, j int) bool { return a[i].Value > a[j].Value }
|
||||
func (a StatItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
func CrossMonthStat(ctx context.Context, cmd *tgbot.Command) error {
|
||||
appId, _ := cmd.GetFlagInt32Value("-app")
|
||||
monthStr := cmd.GetFlagStringValue("-month")
|
||||
month, err := time.ParseInLocation("2006-01-02", monthStr, time.Local)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "解析日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
getValue := func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Recharge.Value()
|
||||
}
|
||||
statList := make(StatItemList, 0)
|
||||
for _, addr := range statg.Conf.InnerServers {
|
||||
req := &proto.BotMonthStatReq{Month: month}
|
||||
if appId > 0 {
|
||||
req.AppId = &appId
|
||||
}
|
||||
res := CrossMonthRes{}
|
||||
err = InnerRequest(addr+"/api/stat/inner/botMonthStat", req, &res, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.Data == nil {
|
||||
continue
|
||||
}
|
||||
for _, stat := range res.Data.List {
|
||||
if statg.GlobalEndpoints.ExistsByAppId(stat.AppId) {
|
||||
value := getValue(stat)
|
||||
statList = append(statList, &StatItem{
|
||||
Value: value,
|
||||
Stat: stat,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort
|
||||
sort.Sort(statList)
|
||||
// 显示
|
||||
y, m, _ := month.Date()
|
||||
for _, item := range statList {
|
||||
appName := statg.GlobalEndpoints.ExistsByAppId(item.Stat.AppId)
|
||||
title := fmt.Sprintf("%d年%d月【%s】数据汇总", y, m, appName)
|
||||
text := item.Stat.DisplaySummaryText(title)
|
||||
if err = cmd.SendBack(text); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CrossStatRecharge(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Recharge.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatRechargeNew(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "新用户充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.RechargeNew.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("新用户充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatRechargeOld(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "老用户充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.RechargeOld.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("老用户充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatRechargeGold(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "金币充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.RechargeGold.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("金币充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatRechargeVip(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "会员充值排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.RechargeVip.Value() / 100
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("会员充值排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatRegister(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "注册排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Register.Value()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("注册排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatVisit(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "日活排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Visit.Value()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("日活排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func CrossStatOrder(ctx context.Context, cmd *tgbot.Command) error {
|
||||
moveBackDays, _ := cmd.GetFlagInt64Value("-back")
|
||||
if moveBackDays > 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
// get stats
|
||||
now := time.Now()
|
||||
reportTime := utils.RecentMinute(now, 60)
|
||||
if moveBackDays != 0 {
|
||||
reportTime = utils.BeginWithTime(now.AddDate(0, 0, int(moveBackDays)+1))
|
||||
}
|
||||
title := GetDateDesc(0) + "订单排名:" + reportTime.Format("2006-01-02T15:04:05")
|
||||
photo, _, err := GetRankPhoto(reportTime, title, func(item *botdailyhourstat.BotHourStat) int64 {
|
||||
return item.TodayStat.Order.Count()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.PhotoPath = photo
|
||||
return cmd.SendBack(fmt.Sprintf("订单排名:%s\n", reportTime.Format("2006-01-02T15:04:05")))
|
||||
}
|
||||
|
||||
func GetRankPhoto(reportTime time.Time, title string, getValue FuncGetStatValue) (string, []byte, error) {
|
||||
charts.SetDefaultFont(statutils.Font)
|
||||
statList := make(StatItemList, 0)
|
||||
registerStatList := make(StatItemList, 0)
|
||||
var total, registerTotal, visitTotal int64
|
||||
for _, addr := range statg.Conf.InnerServers {
|
||||
req := &proto.BotDailyHourListReq{
|
||||
Page: common.Page{
|
||||
PageNum: 1,
|
||||
PageSize: 100,
|
||||
},
|
||||
SumDateStart: reportTime,
|
||||
SumDateStop: reportTime,
|
||||
}
|
||||
res := CrossStatRes{}
|
||||
err := InnerRequest(addr+"/api/stat/inner/botStatList", req, &res, true)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if res.Data == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, stat := range res.Data.List {
|
||||
if statg.GlobalEndpoints.GetBotRankEndpoints().ExistsByAppId(stat.AppId) {
|
||||
value := getValue(stat)
|
||||
total += value
|
||||
statList = append(statList, &StatItem{
|
||||
Value: value,
|
||||
Stat: stat,
|
||||
})
|
||||
|
||||
registerValue := stat.TodayStat.Register.Total.Value()
|
||||
registerTotal += registerValue
|
||||
visitTotal += stat.TodayStat.Visit.Total.Value()
|
||||
registerStatList = append(registerStatList, &StatItem{
|
||||
Value: registerValue,
|
||||
Stat: stat,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort
|
||||
sort.Sort(statList)
|
||||
// draw chart
|
||||
xNames := make([]string, 0)
|
||||
for _, stat := range statList {
|
||||
xNames = append(xNames, statg.GlobalEndpoints.GetAppShortNameById(stat.Stat.AppId))
|
||||
}
|
||||
sDatas := make([]charts.SeriesData, 0)
|
||||
rDatas := make([]charts.SeriesData, 0)
|
||||
for index, stat := range statList {
|
||||
sDatas = append(sDatas, charts.SeriesData{
|
||||
Value: float64(stat.Value),
|
||||
Style: charts.Style{
|
||||
FillColor: Colors[index%len(Colors)],
|
||||
TextWrap: chart.TextWrapRune,
|
||||
},
|
||||
})
|
||||
for _, registerStat := range registerStatList {
|
||||
if stat.Stat.AppId == registerStat.Stat.AppId {
|
||||
rDatas = append(rDatas, charts.SeriesData{
|
||||
Value: float64(registerStat.Value),
|
||||
Style: charts.Style{
|
||||
FillColor: Colors[index%len(Colors)],
|
||||
TextWrap: chart.TextWrapRune,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chartOpt := charts.ChartOption{
|
||||
Width: statg.Conf.ChartsConfig.Width,
|
||||
Height: statg.Conf.ChartsConfig.Height,
|
||||
Type: charts.ChartOutputPNG,
|
||||
Title: charts.TitleOption{
|
||||
Text: title,
|
||||
},
|
||||
XAxis: charts.NewXAxisOption(xNames),
|
||||
Legend: charts.LegendOption{
|
||||
Data: []string{
|
||||
fmt.Sprintf("应用:%d", len(xNames)),
|
||||
fmt.Sprintf("充值:%d", total),
|
||||
fmt.Sprintf("日活:%d", visitTotal),
|
||||
fmt.Sprintf("注册:%d", registerTotal),
|
||||
},
|
||||
Icon: charts.IconRect,
|
||||
Left: charts.PositionRight,
|
||||
},
|
||||
SeriesList: []charts.Series{
|
||||
{
|
||||
Type: charts.ChartTypeBar,
|
||||
Data: sDatas,
|
||||
Label: charts.SeriesLabel{
|
||||
Show: true,
|
||||
Position: charts.PositionBottom,
|
||||
},
|
||||
MarkLine: charts.NewMarkLine(
|
||||
charts.SeriesMarkDataTypeAverage,
|
||||
),
|
||||
},
|
||||
{
|
||||
Type: charts.ChartTypeBar,
|
||||
Data: rDatas,
|
||||
Label: charts.SeriesLabel{
|
||||
Show: true,
|
||||
Position: charts.PositionBottom,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
show := true
|
||||
chartOpt.XAxis.Show = &show
|
||||
d, err := charts.Render(chartOpt)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
buf, err := d.Bytes()
|
||||
if err != nil {
|
||||
return "", buf, err
|
||||
}
|
||||
photo := fmt.Sprintf("/tmp/%s.png", utils.UUID())
|
||||
if err = statutils.WriteBytesToFile(buf, photo); err != nil {
|
||||
return "", buf, err
|
||||
}
|
||||
return photo, buf, nil
|
||||
}
|
||||
|
||||
func GetRankDetail(reportTime time.Time, title string, getValue FuncGetStatValue) (string, error) {
|
||||
charts.SetDefaultFont(statutils.Font)
|
||||
statList := make(StatItemList, 0)
|
||||
registerStatList := make(StatItemList, 0)
|
||||
var total, registerTotal int64
|
||||
for _, addr := range statg.Conf.InnerServers {
|
||||
req := &proto.BotDailyHourListReq{
|
||||
Page: common.Page{
|
||||
PageNum: 1,
|
||||
PageSize: 100,
|
||||
},
|
||||
SumDateStart: reportTime,
|
||||
SumDateStop: reportTime,
|
||||
}
|
||||
res := CrossStatRes{}
|
||||
err := InnerRequest(addr+"/api/stat/inner/botStatList", req, &res, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if res.Data == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, stat := range res.Data.List {
|
||||
if statg.GlobalEndpoints.GetBotRankEndpoints().ExistsByAppId(stat.AppId) {
|
||||
value := getValue(stat)
|
||||
total += value
|
||||
statList = append(statList, &StatItem{
|
||||
Value: value,
|
||||
Stat: stat,
|
||||
})
|
||||
|
||||
registerValue := stat.TodayStat.Register.Total.Value()
|
||||
registerTotal += registerValue
|
||||
registerStatList = append(registerStatList, &StatItem{
|
||||
Value: registerValue,
|
||||
Stat: stat,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort
|
||||
sort.Sort(statList)
|
||||
// draw chart
|
||||
xNames := make([]string, 0)
|
||||
for _, stat := range statList {
|
||||
xNames = append(xNames, statg.GlobalEndpoints.GetAppShortNameById(stat.Stat.AppId))
|
||||
}
|
||||
sDatas := make([]charts.SeriesData, 0)
|
||||
rDatas := make([]charts.SeriesData, 0)
|
||||
for index, stat := range statList {
|
||||
sDatas = append(sDatas, charts.SeriesData{
|
||||
Value: float64(stat.Value),
|
||||
Style: charts.Style{
|
||||
FillColor: Colors[index%len(Colors)],
|
||||
TextWrap: chart.TextWrapRune,
|
||||
},
|
||||
})
|
||||
for _, registerStat := range registerStatList {
|
||||
if stat.Stat.AppId == registerStat.Stat.AppId {
|
||||
rDatas = append(rDatas, charts.SeriesData{
|
||||
Value: float64(registerStat.Value),
|
||||
Style: charts.Style{
|
||||
FillColor: Colors[index%len(Colors)],
|
||||
TextWrap: chart.TextWrapRune,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chartOpt := charts.ChartOption{
|
||||
Width: statg.Conf.ChartsConfig.Width,
|
||||
Height: statg.Conf.ChartsConfig.Height,
|
||||
Type: charts.ChartOutputPNG,
|
||||
Title: charts.TitleOption{
|
||||
Text: title,
|
||||
},
|
||||
XAxis: charts.NewXAxisOption(xNames),
|
||||
Legend: charts.LegendOption{
|
||||
Data: []string{
|
||||
fmt.Sprintf("应用:%d", len(xNames)),
|
||||
fmt.Sprintf("充值:%d", total),
|
||||
fmt.Sprintf("注册:%d", registerTotal),
|
||||
},
|
||||
Icon: charts.IconRect,
|
||||
Left: charts.PositionRight,
|
||||
},
|
||||
SeriesList: []charts.Series{
|
||||
{
|
||||
Type: charts.ChartTypeBar,
|
||||
Data: sDatas,
|
||||
Label: charts.SeriesLabel{
|
||||
Show: true,
|
||||
Position: charts.PositionBottom,
|
||||
},
|
||||
MarkLine: charts.NewMarkLine(
|
||||
charts.SeriesMarkDataTypeAverage,
|
||||
),
|
||||
},
|
||||
{
|
||||
Type: charts.ChartTypeBar,
|
||||
Data: rDatas,
|
||||
Label: charts.SeriesLabel{
|
||||
Show: true,
|
||||
Position: charts.PositionBottom,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
show := true
|
||||
chartOpt.XAxis.Show = &show
|
||||
d, err := charts.Render(chartOpt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf, err := d.Bytes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
photo := fmt.Sprintf("/tmp/%s.png", utils.UUID())
|
||||
if err = statutils.WriteBytesToFile(buf, photo); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return photo, nil
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package crossSer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"stat/common/crypt"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func InnerCryptPost(requestURL string, postData []byte) (*http.Response, error) {
|
||||
key := statg.Conf.InnerKey
|
||||
enData, err := crypt.CoreAesEncryptEx(postData, 12, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bodyReader := bytes.NewReader(enData)
|
||||
req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeout := 30 * time.Second
|
||||
if strings.Contains(requestURL, "botMonthStat") {
|
||||
timeout = 15 * time.Minute
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enData, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deData, err := crypt.CoreAesDecryptEx(enData, 12, key)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Body = io.NopCloser(bytes.NewBuffer(deData))
|
||||
resp.ContentLength = int64(len(deData))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func InnerPlainPost(requestURL string, postData []byte) (*http.Response, error) {
|
||||
bodyReader := bytes.NewReader(postData)
|
||||
req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeout := 8 * time.Second
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
func InnerRequest(url string, req interface{}, bind interface{}, crypt bool) error {
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var resp *http.Response
|
||||
if crypt {
|
||||
resp, err = InnerCryptPost(url, data)
|
||||
} else {
|
||||
resp, err = InnerPlainPost(url, data)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("error status %s, http code %d", resp.Status, resp.StatusCode)
|
||||
}
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, bind)
|
||||
}
|
||||
|
||||
func GetDateDesc(moveBackDays int) string {
|
||||
if moveBackDays < 0 {
|
||||
moveBackDays = moveBackDays * -1
|
||||
}
|
||||
switch moveBackDays {
|
||||
case 0:
|
||||
return "今天"
|
||||
case 1:
|
||||
return "昨天"
|
||||
case 2:
|
||||
return "前天"
|
||||
default:
|
||||
return fmt.Sprintf("前%d天", moveBackDays)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package dailystatadvertiseser
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"stat/common/utils"
|
||||
"stat/models/dailystatadvertise"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
//GetDailyStatAdvertise 按照参数搜索
|
||||
func GetDailyStatAdvertise(ctx context.Context, param *DailyStatAdvertiseSearch) (*DailyStatAdvertiseSearchResp, error) {
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
resp := &DailyStatAdvertiseSearchResp{}
|
||||
|
||||
opt.SetSort(bson.M{dailystatadvertise.FN.DailyStatAdvertise.SumDate: -1})
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
opt.SetLimit(param.PageSize)
|
||||
|
||||
total, serr := dailystatadvertise.CountDailyStatAdvertises(ctx, filter)
|
||||
if serr != nil {
|
||||
return resp, serr
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
list, serr := dailystatadvertise.FindManyDailyStatAdvertise(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return resp, serr
|
||||
}
|
||||
resp.List = list
|
||||
resp.Total = total
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
//AddGraphic 添加一个
|
||||
func AddDailyStatAdvertise(ctx context.Context,
|
||||
ac *dailystatadvertise.DailyStatAdvertise) error {
|
||||
|
||||
ac.AppName = "蜜桃传媒"
|
||||
ac.SumDate = utils.BeginWithTime(ac.SumDate)
|
||||
serr := dailystatadvertise.InsertOneDailyStatAdvertise(ctx, ac)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//DelDailyStatAdvertise 删除指定
|
||||
func DelDailyStatAdvertise(ctx context.Context, id string) error {
|
||||
objectId, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{dailystatadvertise.FN.DailyStatAdvertise.ID: objectId}
|
||||
//删除
|
||||
count, serr := dailystatadvertise.DelOneDailyStatAdvertise(ctx, adFilter)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
if count == 0 {
|
||||
return errors.Errorf("DelDailyStatAdvertise")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//UpdateDailyStatAdvertise 更新指定
|
||||
func UpdateDailyStatAdvertise(ctx context.Context, id string,
|
||||
info *DailyStatAdvertiseUpdate) error {
|
||||
now := time.Now()
|
||||
info.UpdatedAt = &now
|
||||
|
||||
if info.SumDate != nil {
|
||||
sumdate := utils.BeginWithTime(*info.SumDate)
|
||||
info.SumDate = &sumdate
|
||||
}
|
||||
|
||||
objectId, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{dailystatadvertise.FN.DailyStatAdvertise.ID: objectId}
|
||||
updateDoc := bson.M{"$set": info}
|
||||
serr := dailystatadvertise.UpdateOneDailyStatAdvertise(ctx, adFilter, updateDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package dailystatadvertiseser
|
||||
|
||||
import (
|
||||
"stat/models/dailystatadvertise"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ActressPhotoSearch 模特管理
|
||||
type DailyStatAdvertiseSearch struct {
|
||||
PageNum int64 `json:"pageNum"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
}
|
||||
|
||||
type DailyStatAdvertiseSearchResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List dailystatadvertise.DailyStatAdvertiseList `json:"list"`
|
||||
}
|
||||
|
||||
type DailyStatAdvertiseUpdate struct {
|
||||
SumDate *time.Time `bson:"sumDate,omitempty" json:"sumDate,omitempty"` // 记录时间 按天对齐
|
||||
AppId *int32 `bson:"appId,omitempty" json:"appId"` // appId
|
||||
NewDirectCount *int64 `bson:"newDirectCount,omitempty" json:"newDirectCount"` // 新增直推
|
||||
DirectVisitCount *int64 `bson:"directVisitCount,omitempty" json:"directVisitCount"` // 直推访问数
|
||||
Day1RetainRate *string `bson:"day1RetainRate,omitempty" json:"day1RetainRate"` // 次日留存率
|
||||
Day3RetainRate *string `bson:"day3RetainRate,omitempty" json:"day3RetainRate"` // 3日留存率
|
||||
Day7RetainRate *string `bson:"day7RetainRate,omitempty" json:"day7RetainRate"` // 7日留存率
|
||||
UpdatedAt *time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` // 更新时间
|
||||
}
|
||||
|
||||
type DailyStatAdvertiseUpdateReq struct {
|
||||
Param DailyStatAdvertiseUpdate `json:"param" bson:"param"` // 参数
|
||||
Id string `json:"id" bson:"_id" binding:"required"` // 视频Id
|
||||
}
|
||||
|
||||
type DailyStatAdvertiseDelReq struct {
|
||||
Id string `json:"id" bson:"_id" binding:"required"` // 视频Id
|
||||
}
|
||||
@@ -0,0 +1,875 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/models/domainmod"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"github.com/ddliu/go-httpclient"
|
||||
)
|
||||
|
||||
const (
|
||||
ApiKey = "a45d5d2c6c231f28f9bcccf4092eb246"
|
||||
ApiDomain = "https://api.boce.com"
|
||||
ApiCheckDuration = time.Second * 10
|
||||
ApiCheckTimeout = time.Minute * 2
|
||||
ApiCheckTryCount = 6
|
||||
)
|
||||
|
||||
type NodeCache struct {
|
||||
List NodeList
|
||||
Last time.Time
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
type MutexResult struct {
|
||||
List domainmod.CheckResultList
|
||||
Lock sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
Cache = &NodeCache{
|
||||
List: make(NodeList, 0),
|
||||
Last: time.Time{},
|
||||
Lock: sync.Mutex{},
|
||||
}
|
||||
)
|
||||
|
||||
func (c *NodeCache) NodeList() (NodeList, error) {
|
||||
ret := make(NodeList, 0)
|
||||
|
||||
c.Lock.Lock()
|
||||
if len(c.List) > 0 && c.Last.After(time.Now().Add(-24*time.Hour)) {
|
||||
for _, node := range c.List {
|
||||
ret = append(ret, node)
|
||||
}
|
||||
c.Lock.Unlock()
|
||||
return ret, nil
|
||||
}
|
||||
c.Lock.Unlock()
|
||||
|
||||
list, err := GetNodeList()
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
c.Lock.Lock()
|
||||
c.List = list
|
||||
c.Last = time.Now()
|
||||
for _, node := range c.List {
|
||||
ret = append(ret, node)
|
||||
}
|
||||
c.Lock.Unlock()
|
||||
return ret, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("node list is empty")
|
||||
}
|
||||
}
|
||||
|
||||
func GetNodeList() (NodeList, error) {
|
||||
addr := fmt.Sprintf("https://task.boce.com/v3/node/list?key=%s", ApiKey)
|
||||
res := &CommonResp{}
|
||||
tryCount := 0
|
||||
for {
|
||||
err := Get(addr, res, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
return nil, err
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if res.ErrorCode != 0 {
|
||||
return nil, fmt.Errorf("获取检测节点列表失败:%s", GetApiErrorMessage(res.ErrorCode))
|
||||
}
|
||||
return *res.Data.List, nil
|
||||
}
|
||||
|
||||
func (s *MutexResult) OnFinish(result *domainmod.CheckResult) {
|
||||
s.Lock.Lock()
|
||||
defer s.Lock.Unlock()
|
||||
|
||||
s.List = append(s.List, result)
|
||||
}
|
||||
|
||||
func (s *MutexResult) RealErrors() []error {
|
||||
ret := make([]error, 0)
|
||||
for _, item := range s.List {
|
||||
if item.CheckSucc && item.Error != nil {
|
||||
ret = append(ret, item.Error)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *MutexResult) AllSuccess() bool {
|
||||
for _, item := range s.List {
|
||||
if item.CheckSucc && item.Error != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *MutexResult) JumpErrors() []error {
|
||||
ret := make([]error, 0)
|
||||
for _, item := range s.List {
|
||||
if !item.CheckSucc && item.Error != nil {
|
||||
ret = append(ret, item.Error)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *MutexResult) Reports() string {
|
||||
var ret string
|
||||
for _, item := range s.List {
|
||||
if item.Error != nil {
|
||||
ret += "\n" + item.Error.Error() + "\n"
|
||||
}
|
||||
if len(item.NodeErrors) > 0 {
|
||||
ret += strings.Join(item.NodeErrors, "\n\n")
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *MutexResult) CheckStatus() (passCount int, jumpCount int, errorCount int) {
|
||||
for _, item := range s.List {
|
||||
if item.CheckSucc {
|
||||
if item.Error != nil {
|
||||
errorCount++
|
||||
} else {
|
||||
passCount++
|
||||
}
|
||||
} else {
|
||||
jumpCount++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var ApiErrorMessage = map[int]string{
|
||||
0: "成功",
|
||||
1: "鉴权失败",
|
||||
2: "参数错误",
|
||||
3: "波点不足或波点未配置",
|
||||
4: "生成任务id失败或任务id失效",
|
||||
}
|
||||
|
||||
func GetApiErrorMessage(code int) string {
|
||||
ret := ApiErrorMessage[code]
|
||||
if ret == "" {
|
||||
return "未知错误"
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var ApiPathMap = map[domainmod.CheckType]ApiPath{
|
||||
domainmod.CheckTypeWeb: {
|
||||
Create: "/v3/task/create/curl",
|
||||
Query: "/v3/task/curl/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypeDNS: {
|
||||
Create: "/v3/task/create/dig",
|
||||
Query: "/v3/task/dig/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypePing: {
|
||||
Create: "/v3/task/create/ping",
|
||||
Query: "/v3/task/ping/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypeHijack: {
|
||||
Create: "/v3/task/create/hijack",
|
||||
Query: "/v3/task/hijack/",
|
||||
Sleep: 5 * time.Minute,
|
||||
NeedCreate: true,
|
||||
},
|
||||
domainmod.CheckTypePollute: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/pollute",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeQQ: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/qq",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeWeChat: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/wechat",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeBlack: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/blacklist",
|
||||
NeedCreate: false,
|
||||
},
|
||||
domainmod.CheckTypeGFW: {
|
||||
Create: "",
|
||||
Query: "/v3/task/create/wall",
|
||||
NeedCreate: false,
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
httpclient.Defaults(httpclient.Map{
|
||||
"OPT_CONNECTTIMEOUT": 30 * time.Second,
|
||||
"OPT_TIMEOUT": 10 * time.Second,
|
||||
"Accept-Encoding": "gzip,deflate",
|
||||
})
|
||||
}
|
||||
|
||||
func GetChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, nodes ...Node) Checker {
|
||||
nodeList := make(NodeList, 0)
|
||||
nodeList = append(nodeList, nodes...)
|
||||
switch mode {
|
||||
case domainmod.CheckTypeWeb:
|
||||
return NewWebChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypeDNS:
|
||||
return NewWebChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypePing:
|
||||
return NewWebChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypeHijack:
|
||||
return NewHijackChecker(item, mode, onFinish, nodeList)
|
||||
case domainmod.CheckTypePollute:
|
||||
return NewPolluteChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeQQ:
|
||||
return NewQQChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeWeChat:
|
||||
return NewWeChatChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeBlack:
|
||||
return NewBlackChecker(item, mode, onFinish)
|
||||
case domainmod.CheckTypeGFW:
|
||||
return NewGFWChecker(item, mode, onFinish)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type DomainChecker struct {
|
||||
Items domainmod.DomainList
|
||||
}
|
||||
|
||||
func (c *DomainChecker) StartCheck() error {
|
||||
nodes, err := Cache.NodeList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wg := utils.NewWaitGroup()
|
||||
for _, domain := range c.Items {
|
||||
_domain := domain
|
||||
wg.Add(func() {
|
||||
CheckDomain(_domain, nodes)
|
||||
})
|
||||
}
|
||||
wg.RunWaits()
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckDomain(domain *domainmod.Domain, nodes NodeList) {
|
||||
ctx := context.Background()
|
||||
begin := time.Now()
|
||||
status := domainmod.CheckStatusChecking
|
||||
doc := &domainmod.DomainDoc{
|
||||
Status: &status,
|
||||
CheckTime: &begin,
|
||||
UpdatedAt: &begin,
|
||||
}
|
||||
if err := domainmod.UpdateOneDomain(ctx, bson.M{"_id": domain.ID}, bson.M{"$set": doc}); err != nil {
|
||||
log.Warn("CheckDomain failed", log.E(err))
|
||||
return
|
||||
}
|
||||
rets := MutexResult{
|
||||
List: make(domainmod.CheckResultList, 0),
|
||||
Lock: sync.Mutex{},
|
||||
}
|
||||
wg := utils.NewWaitGroup()
|
||||
for _, check := range domain.Checks {
|
||||
_check := check
|
||||
wg.Add(func() {
|
||||
if checker := GetChecker(domain, _check, rets.OnFinish, nodes...); checker != nil {
|
||||
time.Sleep(time.Duration(rand.Intn(10)) * time.Second) // 防并发访问
|
||||
checker.Check()
|
||||
}
|
||||
})
|
||||
}
|
||||
wg.RunWaits()
|
||||
end := time.Now()
|
||||
doc = &domainmod.DomainDoc{
|
||||
CheckFinish: &end,
|
||||
UpdatedAt: &end,
|
||||
}
|
||||
passCount, jumpCount, errCount := rets.CheckStatus()
|
||||
doc.Results = &rets.List
|
||||
if errCount > 0 {
|
||||
report := rets.Reports()
|
||||
doc.Report = &report
|
||||
status0 := domainmod.CheckStatusFailed
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusFailed)
|
||||
if passCount > 0 {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusPartPass)
|
||||
status0 = domainmod.CheckStatusPartPass
|
||||
}
|
||||
doc.Status = &status0
|
||||
} else {
|
||||
if passCount > 0 {
|
||||
if jumpCount > 0 {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusPartPass)
|
||||
//doc.Report = utils.PValue(rets.Reports())
|
||||
status0 := domainmod.CheckStatusPartPass
|
||||
report := rets.Reports()
|
||||
doc.Status = &status0
|
||||
doc.Report = &report
|
||||
} else {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusSucc)
|
||||
//doc.Report = utils.PValue("完全通过")
|
||||
status0 := domainmod.CheckStatusSucc
|
||||
report := "完全通过"
|
||||
doc.Status = &status0
|
||||
doc.Report = &report
|
||||
}
|
||||
} else {
|
||||
//doc.Status = utils.PValue(domainmod.CheckStatusNone)
|
||||
//doc.Report = utils.PValue(rets.Reports())
|
||||
status0 := domainmod.CheckStatusNone
|
||||
report := rets.Reports()
|
||||
doc.Status = &status0
|
||||
doc.Report = &report
|
||||
}
|
||||
}
|
||||
|
||||
if err := domainmod.UpdateOneDomain(ctx, bson.M{"_id": domain.ID}, bson.M{"$set": doc}); err != nil {
|
||||
log.Warn("CheckDomain failed", log.E(err))
|
||||
return
|
||||
}
|
||||
|
||||
if errCount > 0 {
|
||||
text := fmt.Sprintf("域名检测:【%s】\n", domain.Name)
|
||||
text += fmt.Sprintf("开始时间:【%s】\n", begin.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("结束时间:【%s】\n", end.Format("2006-01-02T15:04:05"))
|
||||
text += fmt.Sprintf("检测用时:【%s】\n", end.Sub(begin))
|
||||
text += fmt.Sprintf("检测结果:【%s】\n", *doc.Status)
|
||||
for _, result := range rets.List {
|
||||
if result.CheckSucc {
|
||||
if result.Error == nil {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "\U0001F7E2 通过")
|
||||
} else {
|
||||
if result.NodeCount == 0 {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "🔴 未通过")
|
||||
} else {
|
||||
if result.SuccRate < result.MinRate {
|
||||
if result.SuccRate < 25 {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "🔴 未通过")
|
||||
} else {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "\U0001F7E0 通过")
|
||||
}
|
||||
} else {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "\U0001F7E2 通过")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
text += fmt.Sprintf("%s: %s\n", result.CheckType.Name(), "🕓 超时")
|
||||
}
|
||||
}
|
||||
if domain.Redirected {
|
||||
text += fmt.Sprintf("域名跳转:【已做301跳转✅】\n")
|
||||
text += fmt.Sprintf("跳转地址:【==>%s】\n", domain.RedirectDomain)
|
||||
} else {
|
||||
text += fmt.Sprintf("域名跳转:【未做301跳转🈚】\n")
|
||||
}
|
||||
|
||||
statg.Bot.SendDomainMessage(text)
|
||||
}
|
||||
}
|
||||
|
||||
type WebChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
Nodes NodeList
|
||||
}
|
||||
|
||||
func NewWebChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, nodes NodeList) *WebChecker {
|
||||
return &WebChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WebChecker) Check() error {
|
||||
ret := &domainmod.CheckResult{
|
||||
Domain: s.Item.Name,
|
||||
CheckType: s.Mode,
|
||||
BeginTime: time.Now(),
|
||||
MinRate: s.Item.MinRate,
|
||||
}
|
||||
defer s.Finish(ret)
|
||||
api, ok := ApiPathMap[s.Mode]
|
||||
if !ok {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("域名%s的检测类型%d不支持", s.Item.Name, s.Mode)
|
||||
return ret.Error
|
||||
}
|
||||
addr, err := api.GetCreateUrl(url.Values{
|
||||
"node_ids": []string{s.Nodes.SIdList(s.Item.MaxNodes)},
|
||||
"host": []string{s.Item.Name},
|
||||
})
|
||||
if err != nil {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = err
|
||||
return ret.Error
|
||||
}
|
||||
tryCount := 0
|
||||
res := &CommonResp{}
|
||||
for {
|
||||
err = Get(addr, res, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务超时:%s", s.Item.Name, s.Mode.Name(), err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if res.ErrorCode != 0 {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:%s", s.Item.Name, s.Mode.Name(), GetApiErrorMessage(res.ErrorCode))
|
||||
return ret.Error
|
||||
}
|
||||
// 获取查询ID
|
||||
if res.Data.Id == nil || *res.Data.Id == "" {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:任务ID未空", s.Item.Name, s.Mode.Name())
|
||||
return ret.Error
|
||||
}
|
||||
TaskId := *res.Data.Id
|
||||
// 去查询结果
|
||||
tryCount = 0
|
||||
addr, err = api.GetQueryUrlWithTaskId(TaskId, url.Values{})
|
||||
end := time.Now().Add(ApiCheckTimeout)
|
||||
for {
|
||||
time.Sleep(ApiCheckDuration)
|
||||
webResp := &WebResp{}
|
||||
err = Get(addr, webResp, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】检测结果超时:%s", s.Item.Name, s.Mode.Name(), err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret.CheckSucc = webResp.Done
|
||||
if webResp.Done {
|
||||
for _, node := range webResp.List {
|
||||
if node.ErrorCode != 0 {
|
||||
ret.FailedCount++
|
||||
ret.NodeErrors = append(ret.NodeErrors, node.ErrorString())
|
||||
} else {
|
||||
ret.NodeCount++
|
||||
if node.HttpCode == http.StatusOK {
|
||||
ret.SuccCount++
|
||||
} else {
|
||||
ret.NodeErrors = append(ret.NodeErrors, node.ErrorString())
|
||||
}
|
||||
}
|
||||
}
|
||||
if ret.NodeCount > 0 {
|
||||
ret.SuccRate = float64(ret.SuccCount*100) / float64(ret.NodeCount)
|
||||
if ret.SuccRate < ret.MinRate {
|
||||
ret.Error = fmt.Errorf("节点通过率%.1f%%<%.1f%%,未通过!", ret.SuccRate, ret.MinRate)
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
if end.Before(time.Now()) {
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】检测超时", s.Item.Name, s.Mode.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ret.EndTime = time.Now()
|
||||
return ret.Error
|
||||
}
|
||||
|
||||
type HijackChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
Nodes NodeList
|
||||
}
|
||||
|
||||
func NewHijackChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, nodes NodeList) *HijackChecker {
|
||||
return &HijackChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
Nodes: nodes,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HijackChecker) Check() error {
|
||||
ret := &domainmod.CheckResult{
|
||||
Domain: s.Item.Name,
|
||||
CheckType: s.Mode,
|
||||
BeginTime: time.Now(),
|
||||
MinRate: s.Item.MinRate,
|
||||
}
|
||||
defer s.Finish(ret)
|
||||
api, ok := ApiPathMap[s.Mode]
|
||||
if !ok {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("域名%s的检测类型%d不支持", s.Item.Name, s.Mode)
|
||||
return ret.Error
|
||||
}
|
||||
addr, err := api.GetCreateUrl(url.Values{
|
||||
"node_ids": []string{s.Nodes.SIdList(s.Item.MaxNodes)},
|
||||
"host": []string{s.Item.Name},
|
||||
})
|
||||
if err != nil {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = err
|
||||
return ret.Error
|
||||
}
|
||||
tryCount := 0
|
||||
res := &CommonResp{}
|
||||
for {
|
||||
err = Get(addr, res, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务超时:%s", s.Item.Name, s.Mode.Name(), err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if res.ErrorCode != 0 {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:%s", s.Item.Name, s.Mode.Name(), GetApiErrorMessage(res.ErrorCode))
|
||||
return ret.Error
|
||||
}
|
||||
// 获取查询ID
|
||||
if res.Data.Id == nil || *res.Data.Id == "" {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("创建【%s】的【%s】检测任务失败:任务ID未空", s.Item.Name, s.Mode.Name())
|
||||
return ret.Error
|
||||
}
|
||||
TaskId := *res.Data.Id
|
||||
// 去查询结果
|
||||
tryCount = 0
|
||||
addr, err = api.GetQueryUrlWithTaskId(TaskId, url.Values{})
|
||||
end := time.Now().Add(ApiCheckTimeout)
|
||||
for {
|
||||
time.Sleep(ApiCheckDuration)
|
||||
webResp := &HijackResp{}
|
||||
err = Get(addr, webResp, url.Values{})
|
||||
if err != nil {
|
||||
tryCount++
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】接口错误【%d】次,超过最大重试次数:%s", s.Item.Name, s.Mode.Name(), tryCount, err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret.CheckSucc = webResp.Done
|
||||
if webResp.Done {
|
||||
for _, node := range webResp.List {
|
||||
ret.NodeCount++
|
||||
if node.Hijack {
|
||||
ret.FailedCount++
|
||||
ret.NodeErrors = append(ret.NodeErrors, node.ErrorString())
|
||||
} else {
|
||||
ret.SuccCount++
|
||||
}
|
||||
}
|
||||
if ret.NodeCount > 0 {
|
||||
ret.SuccRate = float64(ret.SuccCount*100) / float64(ret.NodeCount)
|
||||
if ret.SuccRate < ret.MinRate {
|
||||
ret.Error = fmt.Errorf("节点通过率%.1f%%<%.1f%%,未通过!", ret.SuccRate, ret.MinRate)
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
if end.Before(time.Now()) {
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】检测超时", s.Item.Name, s.Mode.Name())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
ret.EndTime = time.Now()
|
||||
return ret.Error
|
||||
}
|
||||
|
||||
type FuncCheckResult func(ret *domainmod.CheckResult, data map[string]int)
|
||||
|
||||
func QueryCheck(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish, onCheck FuncCheckResult) error {
|
||||
ret := &domainmod.CheckResult{
|
||||
Domain: item.Name,
|
||||
CheckType: mode,
|
||||
BeginTime: time.Now(),
|
||||
MinRate: item.MinRate,
|
||||
}
|
||||
defer onFinish(ret)
|
||||
api, ok := ApiPathMap[mode]
|
||||
if !ok {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询接口地址【%s】的【%s】出错", item.Name, mode.Name())
|
||||
return ret.Error
|
||||
}
|
||||
addr, err := api.GetQueryUrl(url.Values{"host": []string{item.Name}})
|
||||
polluteResp := &PolluteResp{}
|
||||
tryCount := 0
|
||||
for {
|
||||
tryCount++
|
||||
err = Get(addr, polluteResp, url.Values{})
|
||||
if err != nil {
|
||||
if tryCount > ApiCheckTryCount {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】接口错误【%d】次,超过最大重试次数:%s", item.Name, mode.Name(), tryCount, err.Error())
|
||||
return ret.Error
|
||||
}
|
||||
time.Sleep(ApiCheckDuration)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if polluteResp.ErrorCode != 0 {
|
||||
ret.EndTime = time.Now()
|
||||
ret.Error = fmt.Errorf("查询【%s】的【%s】返回值不是成功:%s", item.Name, mode.Name(), GetApiErrorMessage(polluteResp.ErrorCode))
|
||||
return ret.Error
|
||||
}
|
||||
ret.CheckSucc = true
|
||||
onCheck(ret, polluteResp.Data)
|
||||
ret.EndTime = time.Now()
|
||||
return ret.Error
|
||||
}
|
||||
|
||||
type PolluteChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewPolluteChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *PolluteChecker {
|
||||
return &PolluteChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PolluteChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //(-1 未注册,0 正常, 1 污染)
|
||||
if ok {
|
||||
if code == 0 {
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 1 {
|
||||
ret.Error = fmt.Errorf("域名被污染")
|
||||
} else if code == -1 {
|
||||
ret.Error = fmt.Errorf("域名未注册")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("未知错误码%d", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type QQChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewQQChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *QQChecker {
|
||||
return &QQChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *QQChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //1正常, 2拦截, 3-检测失败
|
||||
if ok {
|
||||
if code == 1 {
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("已被拦截")
|
||||
} else if code == 3 {
|
||||
ret.Error = fmt.Errorf("检测失败")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type WeChatChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewWeChatChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *WeChatChecker {
|
||||
return &WeChatChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *WeChatChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //1正常, 2拦截(域名被封), 3-检测失败,4-拦截(非微信官方链接,需跳转访问)
|
||||
if ok {
|
||||
if code == 1 || code == 4 {
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("已被拦截")
|
||||
} else if code == 3 {
|
||||
ret.Error = fmt.Errorf("检测失败")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type BlackChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewBlackChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *BlackChecker {
|
||||
return &BlackChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BlackChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //1-正常, 2-在黑名单
|
||||
if ok {
|
||||
if code == 1 {
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("在黑名单")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type GFWChecker struct {
|
||||
Item *domainmod.Domain
|
||||
Mode domainmod.CheckType
|
||||
Finish FnCheckFinish
|
||||
}
|
||||
|
||||
func NewGFWChecker(item *domainmod.Domain, mode domainmod.CheckType, onFinish FnCheckFinish) *GFWChecker {
|
||||
return &GFWChecker{
|
||||
Item: item,
|
||||
Mode: mode,
|
||||
Finish: onFinish,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GFWChecker) Check() error {
|
||||
return QueryCheck(s.Item, s.Mode, s.Finish, func(ret *domainmod.CheckResult, data map[string]int) {
|
||||
if code, ok := data[s.Item.Name]; ok { //0 正常, 1 被墙, 2 疑似被墙, 3 无资源请重试,4 域名格式错误
|
||||
if ok {
|
||||
if code == 0 { // 正常
|
||||
ret.CheckSucc = true
|
||||
ret.SuccRate = 100.0
|
||||
} else {
|
||||
if code == 1 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("域名被墙")
|
||||
} else if code == 2 {
|
||||
ret.CheckSucc = true
|
||||
ret.Error = fmt.Errorf("疑似被墙")
|
||||
} else if code == 3 {
|
||||
ret.Error = fmt.Errorf("无资源请重试")
|
||||
} else if code == 4 {
|
||||
ret.Error = fmt.Errorf("域名格式错误")
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("检测值【%d】不知道意义", code)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.Error = fmt.Errorf("返回值中未检测到域名")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"stat/models/domainmod"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FnCheckFinish func(result *domainmod.CheckResult)
|
||||
|
||||
type Checker interface {
|
||||
Check() error
|
||||
}
|
||||
|
||||
type ApiPath struct {
|
||||
Create string
|
||||
Query string
|
||||
Sleep time.Duration
|
||||
NeedCreate bool
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
Id int `json:"id"`
|
||||
NodeName string `json:"node_name"`
|
||||
IspName string `json:"isp_name"`
|
||||
IspCode int `json:"isp_code"`
|
||||
Rand int
|
||||
}
|
||||
|
||||
type NodeList []Node
|
||||
|
||||
func (n NodeList) Len() int { return len(n) }
|
||||
func (n NodeList) Less(i, j int) bool {
|
||||
return n[i].Rand < n[j].Rand
|
||||
}
|
||||
func (n NodeList) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
|
||||
func (n NodeList) SIdList(max int32) string {
|
||||
if max < 1 {
|
||||
max = 1
|
||||
}
|
||||
for _, node := range n {
|
||||
node.Rand = rand.Int()
|
||||
}
|
||||
sort.Sort(n)
|
||||
ss := make([]string, 0)
|
||||
for _, node := range n {
|
||||
if strings.Contains(node.IspName, "电信") || strings.Contains(node.IspName, "移动") || strings.Contains(node.IspName, "联通") {
|
||||
ss = append(ss, fmt.Sprintf("%d", node.Id))
|
||||
}
|
||||
}
|
||||
if len(ss) > int(max) {
|
||||
ss = ss[:max]
|
||||
}
|
||||
return strings.Join(ss, ",")
|
||||
}
|
||||
|
||||
type CommonResp struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
Data struct {
|
||||
Id *string `json:"id"`
|
||||
List *NodeList `json:"list"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type DomainResp struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
Data map[string]int `json:"data"`
|
||||
}
|
||||
|
||||
type WebTestNode struct {
|
||||
NodeId int `json:"node_id"`
|
||||
NodeName string `json:"node_name"`
|
||||
Host string `json:"host"`
|
||||
OriginIp string `json:"origin_ip"`
|
||||
HttpCode int `json:"http_code"`
|
||||
RemoteIp string `json:"remote_ip"`
|
||||
SizeDownload int `json:"size_download"`
|
||||
SpeedDownload int `json:"speed_download"`
|
||||
TimeConnect float64 `json:"time_connect"`
|
||||
TimeNamelookup float64 `json:"time_namelookup"`
|
||||
TimeStarttransfer float64 `json:"time_starttransfer"`
|
||||
TimeTotal float64 `json:"time_total"`
|
||||
ReportSource string `json:"report_source"`
|
||||
SessionId string `json:"session_id"`
|
||||
IpRegion string `json:"ip_region"`
|
||||
IpIsp string `json:"ip_isp"`
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
TimeId string `json:"time_id"`
|
||||
}
|
||||
type WebTestNodeList []*WebTestNode
|
||||
|
||||
func (n *WebTestNode) ErrorString() string {
|
||||
return fmt.Sprintf("节点【%s】检测失败", n.NodeName)
|
||||
}
|
||||
|
||||
type WebResp struct {
|
||||
Id string `json:"id"`
|
||||
MaxNode int `json:"max_node"`
|
||||
Done bool `json:"done"`
|
||||
List WebTestNodeList `json:"list"`
|
||||
}
|
||||
|
||||
type HijackTestNode struct {
|
||||
NodeId int `json:"node_id"`
|
||||
Hijack bool `json:"hijack"`
|
||||
NodeName string `json:"node_name"`
|
||||
RemoteIp string `json:"remote_ip"`
|
||||
Url string `json:"url"`
|
||||
ReportSource string `json:"report_source"`
|
||||
}
|
||||
|
||||
func (n *HijackTestNode) ErrorString() string {
|
||||
return fmt.Sprintf("节点【%s】已劫持", n.NodeName)
|
||||
}
|
||||
|
||||
type HijackResp struct {
|
||||
Done bool `json:"done"`
|
||||
HijackNum int `json:"hijack_num"`
|
||||
Id string `json:"id"`
|
||||
List []*HijackTestNode `json:"list"`
|
||||
MaxNode int `json:"max_node"`
|
||||
}
|
||||
|
||||
type PolluteResp struct {
|
||||
ErrorCode int `json:"error_code"`
|
||||
Error string `json:"error"`
|
||||
Data map[string]int `json:"data"`
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/log"
|
||||
"stat/common/statutils"
|
||||
"stat/models/domainmod"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
checkCron *cron.Cron
|
||||
)
|
||||
|
||||
func init() {
|
||||
checkCron = cron.New(cron.WithSeconds(), cron.WithChain(cron.SkipIfStillRunning(&log.Logger{}))) // 秒级定时任务
|
||||
}
|
||||
|
||||
// Start 定时器任务启动
|
||||
func Start() {
|
||||
log.Info("domain checker start...")
|
||||
ctx := context.Background()
|
||||
if statg.Conf.RunMod != consts.RunModRelease {
|
||||
return
|
||||
}
|
||||
_, _ = checkCron.AddFunc("0 0/5 * * * ?", func() {
|
||||
statutils.ServerMon.RunWithName(func() error {
|
||||
return DomainCheck(ctx)
|
||||
}, statutils.GetFuncName(DomainCheck))
|
||||
})
|
||||
checkCron.Start()
|
||||
}
|
||||
|
||||
func DomainCheck(ctx context.Context) error {
|
||||
list, err := domainmod.FindManyDomain(ctx, bson.M{
|
||||
"enable": true,
|
||||
"checkTime": bson.M{"$lt": time.Now().Add(-10 * time.Minute)},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
redirectedChecks := []domainmod.CheckType{domainmod.CheckTypePollute}
|
||||
unRedirectedChecks := []domainmod.CheckType{domainmod.CheckTypePollute, domainmod.CheckTypeGFW}
|
||||
|
||||
domains := make(domainmod.DomainList, 0)
|
||||
for _, item := range list {
|
||||
if item.Redirected {
|
||||
item.Checks = redirectedChecks
|
||||
} else {
|
||||
item.Checks = unRedirectedChecks
|
||||
}
|
||||
if item.Status == domainmod.CheckStatusNone {
|
||||
if item.CheckTime.Before(time.Now().Add(-10 * time.Minute)) {
|
||||
domains = append(domains, item)
|
||||
}
|
||||
} else {
|
||||
if item.CheckTime.Before(time.Now().Add(time.Duration(-item.CycleHour) * time.Hour)) {
|
||||
domains = append(domains, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(domains) == 0 {
|
||||
return nil
|
||||
}
|
||||
domainChecker := &DomainChecker{
|
||||
Items: domains,
|
||||
}
|
||||
return domainChecker.StartCheck()
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ddliu/go-httpclient"
|
||||
)
|
||||
|
||||
func ParseDomain(name string) (string, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if strings.Contains(strings.ToLower(name), "http:") || strings.Contains(strings.ToLower(name), "https:") {
|
||||
u, err := url.Parse(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Host == "" {
|
||||
return "", fmt.Errorf("没有检测到有效的域名")
|
||||
}
|
||||
if net.ParseIP(u.Host) != nil {
|
||||
return "", fmt.Errorf("只能填写域名,不能填写IP地址")
|
||||
}
|
||||
host, _, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("域名无效")
|
||||
}
|
||||
return host, nil
|
||||
} else {
|
||||
re, err := regexp.Compile(`^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$`)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !re.MatchString(name) {
|
||||
return "", fmt.Errorf("请输入正确的域名")
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ApiPath) GetCreateUrl(param url.Values) (string, error) {
|
||||
param.Set("key", ApiKey)
|
||||
if n.NeedCreate {
|
||||
base, err := url.Parse(ApiDomain + n.Create)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
base.RawQuery = param.Encode()
|
||||
return base.String(), nil
|
||||
} else {
|
||||
return "", fmt.Errorf("do not need create url")
|
||||
}
|
||||
}
|
||||
|
||||
func (n *ApiPath) GetQueryUrlWithTaskId(taskId string, param url.Values) (string, error) {
|
||||
param.Set("key", ApiKey)
|
||||
base, err := url.Parse(ApiDomain + n.Query + taskId)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
base.RawQuery = param.Encode()
|
||||
return base.String(), nil
|
||||
}
|
||||
|
||||
func (n *ApiPath) GetQueryUrl(param url.Values) (string, error) {
|
||||
param.Set("key", ApiKey)
|
||||
base, err := url.Parse(ApiDomain + n.Query)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
base.RawQuery = param.Encode()
|
||||
return base.String(), nil
|
||||
}
|
||||
|
||||
func Get(url string, resp interface{}, param url.Values) error {
|
||||
res, err := httpclient.Get(url, param)
|
||||
if err != nil {
|
||||
return fmt.Errorf("访问检测网站失败:%s", err.Error())
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("访问检测网站失败, HTTP Code:%d, Error:%s", res.StatusCode, res.Status)
|
||||
}
|
||||
data, err := res.ReadAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取检测网站返回数据失败:%s", err.Error())
|
||||
}
|
||||
if err = json.Unmarshal(data, resp); err != nil {
|
||||
return fmt.Errorf("解码检测网站返回数据失败:%s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package domainser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/domainmod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 获取DomainInfo列表
|
||||
func GetDomainInfoList(ctx context.Context, param proto.ListDomainInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListDomainInfoRes{
|
||||
Total: 0,
|
||||
List: make(domainmod.DomainList, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
filter := bson.M{}
|
||||
if param.Name != nil {
|
||||
filter["name"] = bson.M{"$regex": fmt.Sprintf("^%s", *param.Name)}
|
||||
}
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = *param.AppId
|
||||
}
|
||||
if param.Enable != nil {
|
||||
filter["enable"] = *param.Enable
|
||||
}
|
||||
if param.Status != nil {
|
||||
filter["status"] = *param.Status
|
||||
}
|
||||
if param.Category != nil {
|
||||
filter["category"] = *param.Category
|
||||
}
|
||||
if param.Check != nil {
|
||||
filter["checks"] = bson.M{"$in": *param.Check}
|
||||
}
|
||||
opts := options.Find().SetSkip(skip).SetLimit(limit)
|
||||
count, err := domainmod.CountDomains(ctx, filter)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
aList, err := domainmod.FindManyDomain(ctx, filter, opts)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
appMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
for _, item := range aList {
|
||||
item.AppName = appMap[item.AppId]
|
||||
}
|
||||
|
||||
resp.Total = count
|
||||
resp.List = aList
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 添加DomainInfo
|
||||
func AddDomainInfo(ctx context.Context, param proto.AddDomainInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
name, err := ParseDomain(param.Name)
|
||||
if err != nil {
|
||||
code = stdLog.ParamError
|
||||
return
|
||||
}
|
||||
param.Name = name
|
||||
now := time.Now()
|
||||
param.UpdatedAt = now
|
||||
param.CreatedAt = now
|
||||
|
||||
if param.CycleHour < 1 {
|
||||
param.CycleHour = 1
|
||||
}
|
||||
|
||||
if err = domainmod.InsertOneDomain(ctx, ¶m.Domain); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 更新DomainInfo
|
||||
func UpdateDomainInfo(ctx context.Context, param proto.UpdateDomainInfoReq) (code stdLog.CodeType,
|
||||
data interface{}, err error) {
|
||||
now := time.Now()
|
||||
param.Update.UpdatedAt = &now
|
||||
if param.Update.Name != nil {
|
||||
var name string
|
||||
name, err = ParseDomain(*param.Update.Name)
|
||||
if err != nil {
|
||||
code = stdLog.ParamError
|
||||
return
|
||||
}
|
||||
param.Update.Name = &name
|
||||
}
|
||||
if param.Update.CycleHour != nil {
|
||||
if *param.Update.CycleHour < 1 {
|
||||
hour := int64(1)
|
||||
param.Update.CycleHour = &hour
|
||||
}
|
||||
}
|
||||
|
||||
if err = domainmod.UpdateOneDomain(ctx, bson.M{"_id": param.Id}, bson.M{"$set": param.Update}); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 删除DomainInfo
|
||||
func DelDomainInfo(ctx context.Context, param proto.DelDomainInfoReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
if _, err = domainmod.DelOneDomain(ctx, bson.M{"_id": param.Id}); err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package innerserverser
|
||||
|
||||
import (
|
||||
"stat/api/proto"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AppInformation struct {
|
||||
Id int
|
||||
Name string
|
||||
BeginCol int
|
||||
Total proto.InnerDailyRecharge
|
||||
}
|
||||
|
||||
type DayRecharge struct {
|
||||
SumDate time.Time
|
||||
List []*proto.InnerDailyRecharge // all items for one day
|
||||
Dict map[int32]*proto.InnerDailyRecharge // map[appId]item
|
||||
Total proto.InnerDailyRecharge
|
||||
}
|
||||
|
||||
type DayRechargeList []*DayRecharge
|
||||
|
||||
func (d DayRechargeList) Len() int {
|
||||
return len(d)
|
||||
}
|
||||
|
||||
func (d DayRechargeList) Less(i, j int) bool {
|
||||
return d[i].SumDate.Before(d[j].SumDate)
|
||||
}
|
||||
|
||||
func (d DayRechargeList) Swap(i, j int) {
|
||||
d[i], d[j] = d[j], d[i]
|
||||
}
|
||||
@@ -0,0 +1,711 @@
|
||||
package innerserverser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/dailystatmod"
|
||||
"stat/statg"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func InnerRequestBotcmd(url string, reqproto interface{}, bind interface{}) error {
|
||||
data, err := json.Marshal(reqproto)
|
||||
|
||||
if err != nil {
|
||||
log.Error("InnerRequestBotcmd json.Marshal", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
//resp, err = InnerPlainPost(url, data)
|
||||
bodyReader := bytes.NewReader(data)
|
||||
req, err := http.NewRequest(http.MethodPost, url, bodyReader)
|
||||
if err != nil {
|
||||
log.Error("InnerRequestBotcmd http.NewRequest", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
timeout := 30 * time.Second
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
log.Error("InnerRequestBotcmd http.NewRequest", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("error status %s, http code %d", resp.Status, resp.StatusCode)
|
||||
log.Error("InnerRequestBotcmd http.NewRequest resp", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error("InnerRequestBotcmd io.ReadAll", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, bind)
|
||||
}
|
||||
|
||||
// bili请求过来的
|
||||
func InnerRecgaregeBotcmdList(ctx context.Context, req *proto.InnerRechargeBotcmdReq) (
|
||||
*proto.InnerDailyRechargeList, error) {
|
||||
if req.InnerId != int32(statg.Conf.ServerIndex) {
|
||||
err := fmt.Errorf("InnerRecgaregeBotcmdList req.InnerId %d", req.InnerId)
|
||||
log.Error("InnerRecgaregeBotcmdList req.InnerId", log.Any("err", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &proto.InnerDailyRechargeList{}
|
||||
resp.List = make([]*proto.InnerDailyRecharge, 0)
|
||||
|
||||
list, appinfo, err := GetDbListBotcmd(ctx, &req.EndTime, &req.BeginTime)
|
||||
if err != nil {
|
||||
log.Error("InnerRecgaregeBotcmdList:GetDbList", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
for _, info := range list {
|
||||
if info.AppId == 889 {
|
||||
continue
|
||||
}
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
DateTime: info.SumDate.Format("2006-01-02"),
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
resp.List = append(resp.List, temp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func GetDbListBotcmd(ctx context.Context, day *time.Time, yesterday *time.Time) (
|
||||
[]*dailystatmod.DailyStatMod, map[int32]*appInfoMod.AppInfo, error) {
|
||||
list := make([]*dailystatmod.DailyStatMod, 0)
|
||||
appinfo, serr := appInfoMod.GetAllAppIdMap(ctx, bson.M{})
|
||||
if serr != nil {
|
||||
log.Error("GetDbListBotcmd appInfoMod.GetAllAppInfo", log.Any("serr", serr))
|
||||
return nil, nil, serr
|
||||
}
|
||||
for _, info := range appinfo {
|
||||
//data, serr := dailystatmod.FindOneDailyStat(ctx, info.AppId, yesterday)
|
||||
rechargelist, serr := dailystatmod.GetDailyStatTotalList(ctx, &info.AppId, yesterday, day)
|
||||
if serr != nil {
|
||||
log.Error("GetDbListBotcmd dailystatmod.GetDailyStatTotalList", log.Any("serr", serr))
|
||||
continue
|
||||
}
|
||||
if rechargelist != nil {
|
||||
list = append(list, rechargelist...)
|
||||
} else {
|
||||
log.Error("GetDbListBotcmd rechargelist nil")
|
||||
}
|
||||
}
|
||||
return list, appinfo, nil
|
||||
}
|
||||
|
||||
func NewXlslFileBot(f *excelize.File, list []*proto.InnerDailyRecharge,
|
||||
titlestyle, cellstyle int) error {
|
||||
// 设置列宽
|
||||
f.SetColWidth(sheetName, "A", "AAA", 19)
|
||||
|
||||
// 设置表头行高
|
||||
f.SetRowHeight(sheetName, 1, 25)
|
||||
f.SetCellStyle(sheetName, "A1", "ZZ1", titlestyle)
|
||||
f.SetCellStyle(sheetName, "A2", "ZZ2", titlestyle)
|
||||
f.SetCellStyle(sheetName, "A3", "ZZ3", titlestyle)
|
||||
|
||||
idx := f.NewSheet(sheetName)
|
||||
|
||||
//f.MergeCell(sheetName, "A1", "A3")
|
||||
f.SetCellValue(sheetName, "A1", "日期")
|
||||
|
||||
//f.SetCellStyle(sheetName, "B2", "AAA2", cellstyle)
|
||||
f.SetCellStyle(sheetName, "A4", "A50", cellstyle)
|
||||
f.SetActiveSheet(idx)
|
||||
|
||||
now := time.Now()
|
||||
day := now.Format("2006-01")
|
||||
//begin, end := utils.MonthDateRange(now)
|
||||
beginday := utils.BeginOfMonth(now).Day()
|
||||
endday := utils.EndOfMonth(now).Day()
|
||||
|
||||
//设置日期
|
||||
index := "A"
|
||||
for i := beginday; i <= endday; i++ {
|
||||
tmp := fmt.Sprintf("%s%d", index, i+3)
|
||||
rowday := fmt.Sprintf("%s-%02d", day, i)
|
||||
|
||||
f.SetCellValue(sheetName, tmp, rowday)
|
||||
}
|
||||
|
||||
//新文件设置固定的主题和style
|
||||
row := 1
|
||||
col := 2
|
||||
count := col
|
||||
for _, info := range list {
|
||||
begincol := count
|
||||
count = count + 4
|
||||
titleindex := 0
|
||||
titlemap := make(map[string]string, 0)
|
||||
for i := begincol; i <= count; i++ {
|
||||
if titleindex < len(exportTransWaterTitle) {
|
||||
titlecell, serr := utils.CoordinatesToCellName(begincol+titleindex, row+2)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
titlemap[titlecell] = exportTransWaterTitle[titleindex]
|
||||
}
|
||||
titleindex++
|
||||
}
|
||||
for key, value := range titlemap {
|
||||
f.SetCellValue(sheetName, key, value)
|
||||
}
|
||||
info.BeginCol = begincol
|
||||
// 第一行产品
|
||||
begincell, serr := utils.CoordinatesToCellName(begincol, row)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
endcell, serr := utils.CoordinatesToCellName(count, row)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
// 第一行appid
|
||||
appidbegincell, serr := utils.CoordinatesToCellName(begincol, row+1)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
appidendcell, serr := utils.CoordinatesToCellName(count, row+1)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
f.MergeCell(sheetName, begincell, endcell)
|
||||
/*if info.AppId != 0 {
|
||||
if info.AppId == 888 {
|
||||
f.SetCellValue(sheetName, begincell, info.Name+"(集团)")
|
||||
} else {
|
||||
f.SetCellValue(sheetName, begincell, info.Name+"(内部)")
|
||||
}
|
||||
} else {
|
||||
*/
|
||||
f.SetCellValue(sheetName, begincell, info.Name)
|
||||
//}
|
||||
|
||||
f.MergeCell(sheetName, appidbegincell, appidendcell)
|
||||
//tmp := fmt.Sprintf("%s1", string(index0))
|
||||
f.SetCellValue(sheetName, appidbegincell, info.AppId)
|
||||
count += 2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bili请求麻豆知乎信息只在bili启动麻豆知乎只响应数据
|
||||
func InnerServerBotDateMonthStat(ctx context.Context, cmd *tgbot.Command) error {
|
||||
if statg.Conf.ServerIndex != statg.ServerIndexBili {
|
||||
log.Error("InnerServerBotDateMonthStat serverindex",
|
||||
log.Any("serverindex", statg.Conf.ServerIndex))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("InnerServerBotDateMonthStat start")
|
||||
sBegin := cmd.GetFlagStringValue("-begin")
|
||||
if sBegin == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入查询开始日期,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
sEnd := cmd.GetFlagStringValue("-end")
|
||||
if sEnd == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入查询结束日期,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
beginmonth, err := time.ParseInLocation("2006-01-02", sBegin, time.Local)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "解析开始日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
//beginmonth = beginmonth.AddDate(0, 0, -1)
|
||||
endmonth, err := time.ParseInLocation("2006-01-02", sEnd, time.Local)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "解析结束日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
|
||||
list := make([]*proto.InnerDailyRecharge, 0)
|
||||
for id, info := range statg.Conf.InnerServers {
|
||||
if id == statg.ServerIndexBili {
|
||||
continue
|
||||
}
|
||||
|
||||
if id != statg.ServerIndexBili && info == "" {
|
||||
log.Error("InnerServerBotDateMonthStat server not found",
|
||||
log.Any("statg.Conf.InnerServers", id))
|
||||
continue
|
||||
}
|
||||
req := &proto.InnerRechargeBotcmdReq{
|
||||
InnerId: int32(id),
|
||||
BeginTime: beginmonth,
|
||||
EndTime: endmonth,
|
||||
}
|
||||
url := info + "/api/stat/inner/rechargebotcmd/list"
|
||||
log.Info("InnerServerBotDateMonthStat", log.Any("req", url))
|
||||
res := &proto.InnerDailyRechargeResp{}
|
||||
err = InnerRequestBotcmd(url, req, res)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateMonthStat InnerRequest",
|
||||
log.Any("InnerRequest", err))
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
log.Error("InnerServerBotDateMonthStat InnerRequest data not found")
|
||||
continue
|
||||
}
|
||||
if res.Data.List != nil && len(res.Data.List) > 0 {
|
||||
list = append(list, res.Data.List...)
|
||||
}
|
||||
}
|
||||
|
||||
bililist, appinfo, err := GetDbListBotcmd(ctx, &endmonth, &beginmonth)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateMonthStat:GetDbList", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
for _, info := range bililist {
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
DateTime: info.SumDate.Format("2006-01-02"),
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
list = append(list, temp)
|
||||
}
|
||||
|
||||
listmap := make(map[string]*proto.InnerDailyMap)
|
||||
for _, info := range list {
|
||||
if value, ok := listmap[info.DateTime]; ok {
|
||||
value.List = append(value.List, info)
|
||||
} else {
|
||||
tempmap := &proto.InnerDailyMap{
|
||||
Id: info.DateTime,
|
||||
List: make([]*proto.InnerDailyRecharge, 0),
|
||||
}
|
||||
tempmap.List = append(tempmap.List, info)
|
||||
listmap[info.DateTime] = tempmap
|
||||
}
|
||||
}
|
||||
|
||||
// 中间会不同日期加入新的appid要用所有的appid组成数组创建新xlsx
|
||||
tempcell := make([]*proto.InnerDailyRecharge, 0)
|
||||
tempmap0 := make(map[int32]*proto.InnerDailyRecharge)
|
||||
for _, value := range listmap {
|
||||
temp := &proto.InnerDailyRecharge{AppId: -1}
|
||||
for _, info := range value.List {
|
||||
temp.AppId = 0
|
||||
temp.Name = "天宇总充值"
|
||||
temp.DateTime = info.DateTime
|
||||
temp.CellType = 1
|
||||
temp.DailyVistCount += info.DailyVistCount
|
||||
temp.DailyRegister += info.DailyRegister
|
||||
temp.DailyAppAmount += info.DailyAppAmount
|
||||
temp.DailyGameAmount += info.DailyGameAmount
|
||||
temp.RechargeOrder += info.RechargeOrder
|
||||
tempmap0[info.AppId] = info
|
||||
}
|
||||
tempmap0[temp.AppId] = temp
|
||||
value.List = append(value.List, temp)
|
||||
for i, j := 0, len(value.List)-1; i < j; i, j = i+1, j-1 {
|
||||
value.List[i], value.List[j] = value.List[j], value.List[i]
|
||||
}
|
||||
}
|
||||
|
||||
for _, value := range tempmap0 {
|
||||
tempcell = append(tempcell, value)
|
||||
}
|
||||
sort.Slice(tempcell, func(i, j int) bool {
|
||||
return tempcell[i].AppId < tempcell[j].AppId
|
||||
})
|
||||
|
||||
var f *excelize.File
|
||||
dir := beginmonth.Format("2006-01")
|
||||
dayrow := beginmonth.Format("2006-01-02")
|
||||
filePath := "/usr/local" + "/bakmonthbot/" + dir + "/"
|
||||
filename := filePath + dayrow + ".xlsx"
|
||||
if !DocExsit(filePath) {
|
||||
err = os.MkdirAll(filePath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateMonthStat:export save file to disk failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
f = excelize.NewFile()
|
||||
|
||||
if f == nil {
|
||||
log.Error("InnerServerBotDateMonthStat f failed")
|
||||
err = errors.New("InnerServerBotDateMonthStat f failed")
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(titlestyle)
|
||||
// 设置表头样式
|
||||
headStyleID0, _ := f.NewStyle(cellstyle)
|
||||
|
||||
if tempcell != nil && len(tempcell) > 0 {
|
||||
err = NewXlslFileBot(f, tempcell, headStyleID, headStyleID0)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateMonthStat excelize.OpenFile failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range listmap {
|
||||
cols := f.GetRows(sheetName)
|
||||
if len(cols) < 2 || len(cols[1]) < len(value.List) {
|
||||
return errors.New("InnerServerBotcmdMonthStat cols < 0")
|
||||
}
|
||||
for rowindex := 0; rowindex < len(cols); rowindex++ {
|
||||
if cols[rowindex][0] != "" && cols[rowindex][0] == key {
|
||||
for _, info := range value.List {
|
||||
info.BeginRow = rowindex + 1
|
||||
info.IsOld = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for colindex := 0; colindex < len(cols[1]); colindex++ {
|
||||
for _, info := range value.List {
|
||||
appid := strconv.Itoa(int(info.AppId))
|
||||
if cols[1][colindex] != "" && cols[1][colindex] == appid {
|
||||
info.BeginCol = colindex + 1
|
||||
info.IsOld = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, info := range value.List {
|
||||
cellday, serr := utils.CoordinatesToCellName(info.BeginCol, info.BeginRow)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellregister, serr := utils.CoordinatesToCellName(info.BeginCol+1, info.BeginRow)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellrecharge, serr := utils.CoordinatesToCellName(info.BeginCol+2, info.BeginRow)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellgame, serr := utils.CoordinatesToCellName(info.BeginCol+3, info.BeginRow)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellorder, serr := utils.CoordinatesToCellName(info.BeginCol+4, info.BeginRow)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellday, cellday, headStyleID0)
|
||||
totalamount := fmt.Sprintf("%.2f", float64(info.DailyVistCount)*0.01)
|
||||
f.SetCellValue(sheetName, cellday, totalamount)
|
||||
f.SetCellStyle(sheetName, cellregister, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyRegister)*0.01)
|
||||
f.SetCellValue(sheetName, cellregister, totalamount)
|
||||
f.SetCellStyle(sheetName, cellrecharge, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyAppAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellrecharge, totalamount)
|
||||
if info.AppId == 0 || info.AppId == 888 {
|
||||
f.SetCellStyle(sheetName, cellgame, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyGameAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellgame, totalamount)
|
||||
}
|
||||
f.SetCellStyle(sheetName, cellorder, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.RechargeOrder)*0.01)
|
||||
f.SetCellValue(sheetName, cellorder, totalamount)
|
||||
}
|
||||
}
|
||||
err = f.SaveAs(filename)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateMonthStat CoordinatesToCellName ", log.Any("err", err))
|
||||
|
||||
return err
|
||||
}
|
||||
if statg.RchssendBot != nil {
|
||||
err = statg.RchssendBot.Sendrchstochat("天宇公司综合日报间隔日期", filename, tgbot.FileTypeDocument)
|
||||
//statg.Bot.Sendrchstochat("充值", fileName, tgbot.FileTypeDocument)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateMonthStat Sendrchstochat", log.Any("Sendrchstochat", err))
|
||||
}
|
||||
}
|
||||
log.Info("InnerServerBotDateMonthStat end")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bili请求麻豆知乎信息只在bili启动麻豆知乎只响应数据
|
||||
func InnerServerBotDateDayStat(ctx context.Context, cmd *tgbot.Command) error {
|
||||
if statg.Conf.ServerIndex != statg.ServerIndexBili {
|
||||
log.Error("InnerServerBotDateDayStat serverindex",
|
||||
log.Any("serverindex", statg.Conf.ServerIndex))
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("InnerServerBotDateDayStat start")
|
||||
|
||||
sBegin := cmd.GetFlagStringValue("-date")
|
||||
if sBegin == "" {
|
||||
return tgbot.NewUserError(cmd, "请输入查询开始日期,格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
beginmonth, err := time.ParseInLocation("2006-01-02", sBegin, time.Local)
|
||||
if err != nil {
|
||||
return tgbot.NewUserError(cmd, "解析开始日期出错, 格式:\n"+cmd.Help+"\n"+cmd.Desc)
|
||||
}
|
||||
endmonth := beginmonth.AddDate(0, 0, 1)
|
||||
|
||||
list := make([]*proto.InnerDailyRecharge, 0)
|
||||
for id, info := range statg.Conf.InnerServers {
|
||||
if id == statg.ServerIndexBili {
|
||||
continue
|
||||
}
|
||||
|
||||
if id != statg.ServerIndexBili && info == "" {
|
||||
log.Error("InnerServerBotDateDayStat server not found",
|
||||
log.Any("InnerServerBotDateDayStat", id))
|
||||
continue
|
||||
}
|
||||
req := &proto.InnerRechargeBotcmdReq{
|
||||
InnerId: int32(id),
|
||||
BeginTime: beginmonth,
|
||||
EndTime: endmonth,
|
||||
}
|
||||
url := info + "/api/stat/inner/rechargebotcmd/list"
|
||||
log.Info("InnerServerBotDateDayStat", log.Any("req", url))
|
||||
res := &proto.InnerDailyRechargeResp{}
|
||||
err = InnerRequestBotcmd(url, req, res)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateDayStat InnerRequest",
|
||||
log.Any("InnerRequest", err))
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
log.Error("InnerServerBotDateDayStat InnerRequest data not found")
|
||||
continue
|
||||
}
|
||||
if res.Data.List != nil && len(res.Data.List) > 0 {
|
||||
list = append(list, res.Data.List...)
|
||||
}
|
||||
}
|
||||
|
||||
bililist, appinfo, err := GetDbListBotcmd(ctx, &endmonth, &beginmonth)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateDayStat:GetDbListBotcmd", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
for _, info := range bililist {
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
DateTime: info.SumDate.Format("2006-01-02"),
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
list = append(list, temp)
|
||||
}
|
||||
|
||||
temp := &proto.InnerDailyRecharge{}
|
||||
for _, info := range list {
|
||||
temp.AppId = 0
|
||||
temp.CellType = 1
|
||||
temp.Name = "天宇总充值"
|
||||
temp.DailyVistCount += info.DailyVistCount
|
||||
temp.DailyRegister += info.DailyRegister
|
||||
temp.DailyAppAmount += info.DailyAppAmount
|
||||
temp.DailyGameAmount += info.DailyGameAmount
|
||||
temp.RechargeOrder += info.RechargeOrder
|
||||
}
|
||||
list = append(list, temp)
|
||||
for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
var f *excelize.File
|
||||
dir := beginmonth.Format("2006-01")
|
||||
dayrow := beginmonth.Format("2006-01-02")
|
||||
filePath := "/usr/local" + "/bakmonthday/" + dir + "/"
|
||||
filename := filePath + dayrow + ".xlsx"
|
||||
if !DocExsit(filePath) {
|
||||
err = os.MkdirAll(filePath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateDayStat:export save file to disk failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
f = excelize.NewFile()
|
||||
|
||||
if f == nil {
|
||||
log.Error("InnerServerBotDateDayStat f failed")
|
||||
err = errors.New("InnerServerBotDateDayStat f failed")
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(titlestyle)
|
||||
// 设置表头样式
|
||||
headStyleID0, _ := f.NewStyle(cellstyle)
|
||||
|
||||
err = NewXlslFileBot(f, list, headStyleID, headStyleID0)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateDayStat excelize.OpenFile failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
row := 0
|
||||
cols := f.GetRows(sheetName)
|
||||
for rowindex, col := range cols {
|
||||
for colindex, rowCell := range col {
|
||||
if rowCell == "" {
|
||||
continue
|
||||
}
|
||||
if colindex == 0 {
|
||||
if rowCell == dayrow {
|
||||
row = rowindex + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if row == 0 {
|
||||
return errors.New("InnerServerBotDateDayStat row <= 0")
|
||||
}
|
||||
|
||||
for _, info := range list {
|
||||
cellday, serr := utils.CoordinatesToCellName(info.BeginCol, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateDayStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellregister, serr := utils.CoordinatesToCellName(info.BeginCol+1, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateDayStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellrecharge, serr := utils.CoordinatesToCellName(info.BeginCol+2, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateDayStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellgame, serr := utils.CoordinatesToCellName(info.BeginCol+3, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateDayStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellorder, serr := utils.CoordinatesToCellName(info.BeginCol+4, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerBotDateDayStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellday, cellday, headStyleID0)
|
||||
totalamount := fmt.Sprintf("%.2f", float64(info.DailyVistCount)*0.01)
|
||||
f.SetCellValue(sheetName, cellday, totalamount)
|
||||
|
||||
f.SetCellStyle(sheetName, cellregister, cellregister, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyRegister)*0.01)
|
||||
f.SetCellValue(sheetName, cellregister, totalamount)
|
||||
|
||||
f.SetCellStyle(sheetName, cellrecharge, cellrecharge, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyAppAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellrecharge, totalamount)
|
||||
|
||||
if info.AppId == 0 || info.AppId == 888 {
|
||||
f.SetCellStyle(sheetName, cellgame, cellgame, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyGameAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellgame, totalamount)
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellorder, cellorder, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.RechargeOrder)*0.01)
|
||||
f.SetCellValue(sheetName, cellorder, totalamount)
|
||||
}
|
||||
|
||||
err = f.SaveAs(filename)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateDayStat SaveAs ", log.Any("err", err))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if statg.RchssendBot != nil {
|
||||
err = statg.RchssendBot.Sendrchstochat("天宇公司综合日报具体date", filename,
|
||||
tgbot.FileTypeDocument)
|
||||
if err != nil {
|
||||
log.Error("InnerServerBotDateDayStat Sendrchstochat", log.Any("Sendrchstochat", err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("InnerServerBotDateDayStat end")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,970 @@
|
||||
package innerserverser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/tgbot"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/dailystatmod"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
const titlestyle = `{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":true,
|
||||
"size":16,
|
||||
"family":"arial"
|
||||
},
|
||||
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center",
|
||||
"wrap_text":true
|
||||
}
|
||||
}`
|
||||
|
||||
const cellstyle = `{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":false,
|
||||
"size":10,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center",
|
||||
"wrap_text":true
|
||||
}
|
||||
}`
|
||||
|
||||
func InnerRequest(url string, reqproto interface{}, bind interface{}) error {
|
||||
data, err := json.Marshal(reqproto)
|
||||
|
||||
if err != nil {
|
||||
log.Error("InnerRequest json.Marshal", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
//resp, err = InnerPlainPost(url, data)
|
||||
bodyReader := bytes.NewReader(data)
|
||||
req, err := http.NewRequest(http.MethodPost, url, bodyReader)
|
||||
if err != nil {
|
||||
log.Error("InnerRequest http.NewRequest", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
timeout := 30 * time.Second
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
log.Error("InnerRequest http.NewRequest", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("error status %s, http code %d", resp.Status, resp.StatusCode)
|
||||
log.Error("InnerRequest http.NewRequest resp", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Error("InnerRequest io.ReadAll", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, bind)
|
||||
}
|
||||
|
||||
// bili请求过来的
|
||||
func InnerRecgaregeList(ctx context.Context, req *proto.InnerRechargeReq) (
|
||||
*proto.InnerDailyRechargeList, error) {
|
||||
if req.InnerId != int32(statg.Conf.ServerIndex) {
|
||||
err := fmt.Errorf("InnerRecgaregeList req.InnerId %d", req.InnerId)
|
||||
log.Error("InnerRecgaregeList req.InnerId", log.Any("err", err))
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &proto.InnerDailyRechargeList{}
|
||||
resp.List = make([]*proto.InnerDailyRecharge, 0)
|
||||
|
||||
begin := req.Begin
|
||||
end := req.End
|
||||
|
||||
list, appinfo, err := GetDailyStatList(ctx, &end, &begin)
|
||||
if err != nil {
|
||||
log.Error("InnerRecgaregeList:GetDbList", log.E(err))
|
||||
return nil, err
|
||||
}
|
||||
for _, info := range list {
|
||||
if info.AppId == 889 {
|
||||
continue
|
||||
}
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
SumDate: info.SumDate,
|
||||
DateTime: info.SumDate.Format(time.DateOnly),
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
resp.List = append(resp.List, temp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func GetDbList(ctx context.Context, day *time.Time, yesterday *time.Time) (
|
||||
[]*dailystatmod.DailyStatMod, map[int32]*appInfoMod.AppInfo, error) {
|
||||
list := make([]*dailystatmod.DailyStatMod, 0)
|
||||
appinfo, serr := appInfoMod.GetAllAppIdMap(ctx, bson.M{})
|
||||
if serr != nil {
|
||||
log.Error("InnerServerDailyStat appInfoMod.GetAllAppInfo", log.Any("serr", serr))
|
||||
return nil, nil, serr
|
||||
}
|
||||
for _, info := range appinfo {
|
||||
//data, serr := dailystatmod.FindOneDailyStat(ctx, info.AppId, yesterday)
|
||||
rechargelist, serr := dailystatmod.GetDailyStatTotalList(ctx, &info.AppId, yesterday, day)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerDailyStat dailystatmod.GetDailyStatTotalList", log.Any("serr", serr))
|
||||
continue
|
||||
}
|
||||
if len(rechargelist) != 1 {
|
||||
log.Error("InnerServerDailyStat dailystatmod.GetDailyStatTotalList",
|
||||
log.Any("len(rechargelist)", len(rechargelist)), log.Any("appid", info.AppId),
|
||||
log.Any("yesterday", yesterday), log.Any("day", day))
|
||||
continue
|
||||
}
|
||||
if rechargelist != nil {
|
||||
list = append(list, rechargelist...)
|
||||
} else {
|
||||
log.Error("InnerServerDailyStat rechargelist nil")
|
||||
}
|
||||
}
|
||||
return list, appinfo, nil
|
||||
}
|
||||
|
||||
// 从这个方法开始bili本机调用
|
||||
func DocExsit(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// bili请求麻豆知乎信息只在bili启动麻豆知乎只响应数据
|
||||
func InnerServerDailyStat(ctx context.Context) error {
|
||||
if statg.Conf.ServerIndex != statg.ServerIndexBili {
|
||||
log.Error("InnerServerDailyStat serverindex",
|
||||
log.Any("serverindex", statg.Conf.ServerIndex))
|
||||
return nil
|
||||
}
|
||||
log.Info("InnerServerDailyStat start")
|
||||
list := make([]*proto.InnerDailyRecharge, 0)
|
||||
now := time.Now()
|
||||
|
||||
for id, info := range statg.Conf.InnerServers {
|
||||
if id == statg.ServerIndexBili {
|
||||
continue
|
||||
}
|
||||
|
||||
if id != statg.ServerIndexBili && info == "" {
|
||||
log.Error("statg.Conf.InnerServers server not found",
|
||||
log.Any("statg.Conf.InnerServers", id))
|
||||
continue
|
||||
}
|
||||
req := &proto.InnerRechargeReq{
|
||||
InnerId: int32(id),
|
||||
Begin: utils.BeginWithTime(now).Add(-24 * time.Hour),
|
||||
End: utils.BeginWithTime(now),
|
||||
}
|
||||
url := info + "/api/stat/inner/recharge/list"
|
||||
log.Info("InnerServerDailyStat", log.Any("req", url))
|
||||
res := &proto.InnerDailyRechargeResp{}
|
||||
err := InnerRequest(url, req, res)
|
||||
if err != nil {
|
||||
log.Error("InnerServerDailyStat InnerRequest",
|
||||
log.Any("InnerRequest", err))
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
log.Error("InnerServerDailyStat InnerRequest data not found")
|
||||
continue
|
||||
}
|
||||
if res.Data.List != nil && len(res.Data.List) > 0 {
|
||||
list = append(list, res.Data.List...)
|
||||
}
|
||||
}
|
||||
|
||||
/*appinfo, serr := appInfoMod.GetAllAppIdMap(ctx, bson.M{})
|
||||
if serr != nil {
|
||||
log.Error("InnerServerDailyStat appInfoMod.GetAllAppInfo", log.Any("serr", serr))
|
||||
return nil, serr
|
||||
}
|
||||
*/
|
||||
|
||||
yesterday := utils.BeginOfYesterday()
|
||||
day := utils.BeginOfToday()
|
||||
|
||||
bililist, appinfo, err := GetDbList(ctx, &day, &yesterday)
|
||||
if err != nil {
|
||||
log.Error("InnerServerDailyStat:GetDbList", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
for _, info := range bililist {
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
DateTime: "",
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
list = append(list, temp)
|
||||
}
|
||||
|
||||
temp := &proto.InnerDailyRecharge{}
|
||||
for _, info := range list {
|
||||
temp.AppId = 0
|
||||
temp.Name = "天宇总充值"
|
||||
temp.CellType = 1
|
||||
temp.DailyVistCount += info.DailyVistCount
|
||||
temp.DailyRegister += info.DailyRegister
|
||||
temp.DailyAppAmount += info.DailyAppAmount
|
||||
temp.DailyGameAmount += info.DailyGameAmount
|
||||
temp.RechargeOrder += info.RechargeOrder
|
||||
}
|
||||
list = append(list, temp)
|
||||
for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
dir := yesterday.Format("2006-01-02")
|
||||
filePath := "/usr/local" + "/bakexel/" + dir + "/"
|
||||
//filename := utils.BeginOfToday().Format("2006-01-02")
|
||||
if !DocExsit(filePath) {
|
||||
err = os.Mkdir(filePath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Error("InnerServerDailyStat:export save file to disk failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
f := excelize.NewFile()
|
||||
|
||||
if f == nil {
|
||||
log.Error("InnerServerDailyStat f failed")
|
||||
err = errors.New("InnerServerDailyStat f failed")
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置列宽
|
||||
f.SetColWidth(sheetName, "A", "L", 23)
|
||||
|
||||
// 设置表头行高
|
||||
f.SetRowHeight(sheetName, 1, 25)
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(titlestyle)
|
||||
headStyleID0, _ := f.NewStyle(cellstyle)
|
||||
f.SetCellStyle(sheetName, "A1", "L1", headStyleID)
|
||||
idx := f.NewSheet(sheetName)
|
||||
var exportTransWaterTitleold = map[string]string{
|
||||
"A1": "产品",
|
||||
"B1": "归属",
|
||||
"C1": "日期",
|
||||
"D1": "日活跃",
|
||||
"E1": "日注册",
|
||||
"F1": "视频日充值",
|
||||
"G1": "棋牌日充值",
|
||||
"H1": "交易笔数",
|
||||
}
|
||||
|
||||
rowindex := 'A'
|
||||
for k, v := range exportTransWaterTitleold {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
row := fmt.Sprintf("%s%d", string(rowindex), 2)
|
||||
|
||||
f.SetCellStyle(sheetName, row, row, headStyleID0)
|
||||
rowindex++
|
||||
}
|
||||
|
||||
for k, v := range list {
|
||||
i := 'A' // i 不能超过Z,即列输不能超过26列,超出将出现bug
|
||||
if v.AppId != 0 {
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Name) //名称
|
||||
}
|
||||
i++
|
||||
|
||||
/*if v.AppId != 0 {
|
||||
if v.AppId == 888 {
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), "集团") //归属
|
||||
} else {
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), "内部") //归属
|
||||
}
|
||||
}
|
||||
*/
|
||||
i++
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), dir) //时间
|
||||
i++
|
||||
|
||||
totalamount := fmt.Sprintf("%.2f", float64(v.DailyVistCount)*0.01)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), totalamount) //日活跃
|
||||
i++
|
||||
|
||||
totalamount = fmt.Sprintf("%.2f", float64(v.DailyRegister)*0.01)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), totalamount) //日注册
|
||||
i++
|
||||
|
||||
totalamount = fmt.Sprintf("%.2f", float64(v.DailyAppAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), totalamount) //视频日充值
|
||||
i++
|
||||
|
||||
if v.AppId == 0 || v.AppId == 888 {
|
||||
totalamount = fmt.Sprintf("%.2f", float64(v.DailyGameAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), totalamount) //棋牌日充值
|
||||
}
|
||||
i++
|
||||
|
||||
totalamount = fmt.Sprintf("%.2f", float64(v.RechargeOrder)*0.01)
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), totalamount) //交易笔数
|
||||
}
|
||||
|
||||
f.SetActiveSheet(idx)
|
||||
fileName := filePath + dir + ".xlsx"
|
||||
|
||||
if err = f.SaveAs(fileName); err != nil {
|
||||
log.Error("InnerServerDailyStat:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
if statg.RchssendBot != nil {
|
||||
err = statg.RchssendBot.Sendrchstochat("天宇公司日报", fileName, tgbot.FileTypeDocument)
|
||||
//statg.Bot.Sendrchstochat("充值", fileName, tgbot.FileTypeDocument)
|
||||
if err != nil {
|
||||
log.Error("Sendrchstochat", log.Any("Sendrchstochat", err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("InnerServerDailyStat end")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bili请求麻豆知乎信息只在bili启动麻豆知乎只响应数据
|
||||
func InnerServerMonthStat(ctx context.Context, begin, end *time.Time) error {
|
||||
if statg.Conf.ServerIndex != statg.ServerIndexBili {
|
||||
log.Error("InnerServerMonthStat serverindex",
|
||||
log.Any("serverindex", statg.Conf.ServerIndex))
|
||||
return nil
|
||||
}
|
||||
log.Info("InnerServerMonthStat start")
|
||||
var f *excelize.File
|
||||
newxlslfile := false
|
||||
var err error
|
||||
yesterday := utils.BeginOfYesterday()
|
||||
day := utils.BeginOfToday()
|
||||
|
||||
if begin != nil && end != nil {
|
||||
yesterday = *begin
|
||||
day = *end
|
||||
}
|
||||
dir := yesterday.Format("2006-01")
|
||||
dayrow := yesterday.Format("2006-01-02")
|
||||
filePath := "/usr/local" + "/bakmonth/" + dir + "/"
|
||||
filename := filePath + dir + ".xlsx"
|
||||
if !DocExsit(filePath) {
|
||||
err = os.MkdirAll(filePath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat:export save file to disk failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
f = excelize.NewFile()
|
||||
newxlslfile = true
|
||||
} else {
|
||||
if !DocExsit(filename) {
|
||||
f = excelize.NewFile()
|
||||
newxlslfile = true
|
||||
} else {
|
||||
f, err = excelize.OpenFile(filename)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat excelize.OpenFile failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
log.Error("InnerServerMonthStat f failed")
|
||||
err = errors.New("InnerServerMonthStat f failed")
|
||||
return err
|
||||
}
|
||||
|
||||
list := make([]*proto.InnerDailyRecharge, 0)
|
||||
for id, info := range statg.Conf.InnerServers {
|
||||
if id == statg.ServerIndexBili {
|
||||
continue
|
||||
}
|
||||
|
||||
if id != statg.ServerIndexBili && info == "" {
|
||||
log.Error("InnerServerMonthStat server not found",
|
||||
log.Any("statg.Conf.InnerServers", id))
|
||||
continue
|
||||
}
|
||||
req := &proto.InnerRechargeReq{
|
||||
InnerId: int32(id),
|
||||
Begin: yesterday,
|
||||
End: day,
|
||||
}
|
||||
url := info + "/api/stat/inner/recharge/list"
|
||||
log.Info("InnerServerMonthStat", log.Any("req", url))
|
||||
res := &proto.InnerDailyRechargeResp{}
|
||||
err = InnerRequest(url, req, res)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat InnerRequest",
|
||||
log.Any("InnerRequest", err))
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
log.Error("InnerServerMonthStat InnerRequest data not found")
|
||||
continue
|
||||
}
|
||||
if res.Data.List != nil && len(res.Data.List) > 0 {
|
||||
list = append(list, res.Data.List...)
|
||||
}
|
||||
}
|
||||
|
||||
bililist, appinfo, err := GetDbList(ctx, &day, &yesterday)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat:GetDbList", log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
for _, info := range bililist {
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
DateTime: "",
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
list = append(list, temp)
|
||||
}
|
||||
|
||||
temp := &proto.InnerDailyRecharge{}
|
||||
for _, info := range list {
|
||||
temp.AppId = 0
|
||||
temp.CellType = 1
|
||||
temp.Name = "天宇总充值"
|
||||
temp.DailyVistCount += info.DailyVistCount
|
||||
temp.DailyRegister += info.DailyRegister
|
||||
temp.DailyAppAmount += info.DailyAppAmount
|
||||
temp.DailyGameAmount += info.DailyGameAmount
|
||||
temp.RechargeOrder += info.RechargeOrder
|
||||
}
|
||||
list = append(list, temp)
|
||||
for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(titlestyle)
|
||||
// 设置表头样式
|
||||
headStyleID0, _ := f.NewStyle(cellstyle)
|
||||
|
||||
if newxlslfile {
|
||||
err = NewXlslFile0(f, list, headStyleID, headStyleID0)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat excelize.OpenFile failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = OpenOldXlsl(f, list)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat excelize.OpenFile failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
row := 0
|
||||
cols := f.GetRows(sheetName)
|
||||
for rowindex, col := range cols {
|
||||
for colindex, rowCell := range col {
|
||||
if rowCell == "" {
|
||||
continue
|
||||
}
|
||||
if colindex == 0 {
|
||||
if rowCell == dayrow {
|
||||
row = rowindex + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if row == 0 {
|
||||
return errors.New("InnerServerMonthStat row <= 0")
|
||||
}
|
||||
|
||||
for _, info := range list {
|
||||
cellday, serr := utils.CoordinatesToCellName(info.BeginCol, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellregister, serr := utils.CoordinatesToCellName(info.BeginCol+1, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellrecharge, serr := utils.CoordinatesToCellName(info.BeginCol+2, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellgame, serr := utils.CoordinatesToCellName(info.BeginCol+3, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellorder, serr := utils.CoordinatesToCellName(info.BeginCol+4, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellday, cellday, headStyleID0)
|
||||
totalamount := fmt.Sprintf("%.2f", float64(info.DailyVistCount)*0.01)
|
||||
f.SetCellValue(sheetName, cellday, totalamount)
|
||||
|
||||
f.SetCellStyle(sheetName, cellregister, cellregister, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyRegister)*0.01)
|
||||
f.SetCellValue(sheetName, cellregister, totalamount)
|
||||
|
||||
f.SetCellStyle(sheetName, cellrecharge, cellrecharge, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyAppAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellrecharge, totalamount)
|
||||
|
||||
if info.AppId == 0 || info.AppId == 888 {
|
||||
f.SetCellStyle(sheetName, cellgame, cellgame, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyGameAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellgame, totalamount)
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellorder, cellorder, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.RechargeOrder)*0.01)
|
||||
f.SetCellValue(sheetName, cellorder, totalamount)
|
||||
}
|
||||
if newxlslfile {
|
||||
err = f.SaveAs(filename)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat SaveAs ", log.Any("err", err))
|
||||
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = f.Save()
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat Save ", log.Any("err", err))
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
input, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat ReadFile ", log.Any("err", err))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
destinationFile := filePath + dayrow + ".xlsx"
|
||||
err = os.WriteFile(destinationFile, input, 0777)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat WriteFile ", log.Any("err", err))
|
||||
|
||||
return err
|
||||
}
|
||||
if statg.RchssendBot != nil {
|
||||
err = statg.RchssendBot.Sendrchstochat("天宇公司综合日报", destinationFile,
|
||||
tgbot.FileTypeDocument)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat", log.Any("Sendrchstochat", err))
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("InnerServerMonthStat end")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InnerServerMonthStat2(ctx context.Context, begin, end *time.Time, cmd *tgbot.Command) error {
|
||||
if statg.Conf.ServerIndex != statg.ServerIndexBili {
|
||||
log.Error("InnerServerMonthStat serverindex",
|
||||
log.Any("serverindex", statg.Conf.ServerIndex))
|
||||
return nil
|
||||
}
|
||||
log.Info("InnerServerMonthStat start")
|
||||
f := excelize.NewFile()
|
||||
now := time.Now().AddDate(0, 0, -1)
|
||||
start := utils.BeginOfMonth(now)
|
||||
stop := utils.BeginWithTime(now).AddDate(0, 0, 1)
|
||||
if begin != nil {
|
||||
start = *begin
|
||||
}
|
||||
if end != nil {
|
||||
stop = *end
|
||||
}
|
||||
listOrg := make([]*proto.InnerDailyRecharge, 0)
|
||||
for id, info := range statg.Conf.InnerServers {
|
||||
req := &proto.InnerRechargeReq{
|
||||
InnerId: int32(id),
|
||||
Begin: start,
|
||||
End: stop,
|
||||
}
|
||||
url := info + "/api/stat/inner/recharge/list"
|
||||
log.Info("InnerServerMonthStat", log.Any("req", url))
|
||||
res := &proto.InnerDailyRechargeResp{}
|
||||
err := InnerRequest(url, req, res)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat InnerRequest",
|
||||
log.Any("InnerRequest", err))
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
log.Error("InnerServerMonthStat InnerRequest data not found")
|
||||
continue
|
||||
}
|
||||
if res.Data.List != nil && len(res.Data.List) > 0 {
|
||||
listOrg = append(listOrg, res.Data.List...)
|
||||
}
|
||||
}
|
||||
appNames := make(map[int32]string)
|
||||
appNames[0] = "天宇总充值"
|
||||
source := map[int64]*DayRecharge{}
|
||||
for _, info := range listOrg {
|
||||
appNames[info.AppId] = info.Name
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: info.Name,
|
||||
SumDate: info.SumDate,
|
||||
DateTime: info.SumDate.Format(time.DateOnly),
|
||||
DailyVistCount: info.DailyVistCount,
|
||||
DailyRegister: info.DailyRegister,
|
||||
DailyAppAmount: info.DailyAppAmount,
|
||||
DailyGameAmount: info.DailyGameAmount,
|
||||
RechargeOrder: info.RechargeOrder,
|
||||
}
|
||||
found, ok := source[info.SumDate.Unix()]
|
||||
if !ok {
|
||||
found = &DayRecharge{
|
||||
SumDate: info.SumDate,
|
||||
List: make([]*proto.InnerDailyRecharge, 0),
|
||||
Dict: make(map[int32]*proto.InnerDailyRecharge),
|
||||
}
|
||||
source[info.SumDate.Unix()] = found
|
||||
}
|
||||
found.List = append(found.List, temp)
|
||||
found.Dict[info.AppId] = temp
|
||||
found.Total.Name = "天宇总充值"
|
||||
found.Total.DateTime = info.SumDate.Format(time.DateOnly)
|
||||
found.Total.SumDate = info.SumDate
|
||||
found.Total.DailyVistCount += temp.DailyVistCount
|
||||
found.Total.DailyRegister += temp.DailyRegister
|
||||
found.Total.DailyAppAmount += temp.DailyAppAmount
|
||||
found.Total.DailyGameAmount += temp.DailyGameAmount
|
||||
found.Total.RechargeOrder += temp.RechargeOrder
|
||||
}
|
||||
appIds := make([]int, 0)
|
||||
for id, _ := range appNames {
|
||||
appIds = append(appIds, int(id))
|
||||
}
|
||||
sort.Sort(sort.IntSlice(appIds))
|
||||
AppInfos := make([]*AppInformation, 0)
|
||||
for _, id := range appIds {
|
||||
AppInfos = append(AppInfos, &AppInformation{
|
||||
Id: id,
|
||||
Name: appNames[int32(id)],
|
||||
})
|
||||
}
|
||||
for _, value := range source {
|
||||
value.Dict[0] = &value.Total
|
||||
}
|
||||
// 按APP汇总
|
||||
for _, app := range AppInfos {
|
||||
for _, value := range source {
|
||||
if info, ok := value.Dict[int32(app.Id)]; ok {
|
||||
app.Total.DailyVistCount += info.DailyVistCount
|
||||
app.Total.DailyRegister += info.DailyRegister
|
||||
app.Total.DailyAppAmount += info.DailyAppAmount
|
||||
app.Total.DailyGameAmount += info.DailyGameAmount
|
||||
app.Total.RechargeOrder += info.RechargeOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(titlestyle)
|
||||
// 设置表头样式
|
||||
headStyleID0, _ := f.NewStyle(cellstyle)
|
||||
// 设置表结构
|
||||
err := NewXlslFile2(f, AppInfos, headStyleID, headStyleID0, now)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat excelize.OpenFile failed", log.E(err))
|
||||
return err
|
||||
}
|
||||
// 设置数据
|
||||
row := 4
|
||||
for day := start; day.Before(stop); day = day.AddDate(0, 0, 1) {
|
||||
if found, ok := source[day.Unix()]; ok {
|
||||
for _, app := range AppInfos {
|
||||
if info, ok2 := found.Dict[int32(app.Id)]; ok2 {
|
||||
_ = SetAppData(f, info, row, app.BeginCol, headStyleID0)
|
||||
}
|
||||
}
|
||||
}
|
||||
row++
|
||||
}
|
||||
// 设置总计
|
||||
row = utils.EndOfMonth(now).Day() - utils.BeginOfMonth(now).Day() + 1 + 4
|
||||
for _, app := range AppInfos {
|
||||
_ = SetAppData(f, &app.Total, row, app.BeginCol, headStyleID0)
|
||||
}
|
||||
// 保存文件
|
||||
tempDir := os.TempDir()
|
||||
fmt.Println("临时文件目录:", tempDir)
|
||||
|
||||
// 创建一个临时文件
|
||||
yesterday := utils.BeginOfYesterday().Format(time.DateOnly)
|
||||
fileName := path.Join(tempDir, fmt.Sprintf("%s.xlsx", yesterday))
|
||||
//defer os.Remove(fileName) // 确保程序结束后删除临时文件
|
||||
err = f.SaveAs(fileName)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat SaveAs ", log.Any("err", err))
|
||||
return err
|
||||
}
|
||||
// 发送文件
|
||||
if cmd != nil {
|
||||
err = cmd.SendFile("天宇公司综合日报", fileName)
|
||||
} else {
|
||||
if statg.RchssendBot != nil {
|
||||
err = statg.RchssendBot.Sendrchstochat("天宇公司综合日报", fileName, tgbot.FileTypeDocument)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat", log.Any("Sendrchstochat", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func SetAppData(f *excelize.File, info *proto.InnerDailyRecharge, row, col int, headStyle int) error {
|
||||
cellday, serr := utils.CoordinatesToCellName(col, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellregister, serr := utils.CoordinatesToCellName(col+1, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
cellrecharge, serr := utils.CoordinatesToCellName(col+2, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellgame, serr := utils.CoordinatesToCellName(col+3, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
cellorder, serr := utils.CoordinatesToCellName(col+4, row)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerMonthStat CoordinatesToCellName ", log.Any("err", serr))
|
||||
|
||||
return serr
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellday, cellday, headStyle)
|
||||
totalamount := fmt.Sprintf("%.2f", float64(info.DailyVistCount)*0.01)
|
||||
f.SetCellValue(sheetName, cellday, totalamount)
|
||||
|
||||
f.SetCellStyle(sheetName, cellregister, cellregister, headStyle)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyRegister)*0.01)
|
||||
f.SetCellValue(sheetName, cellregister, totalamount)
|
||||
|
||||
f.SetCellStyle(sheetName, cellrecharge, cellrecharge, headStyle)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyAppAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellrecharge, totalamount)
|
||||
|
||||
if info.AppId == 0 || info.AppId == 888 {
|
||||
f.SetCellStyle(sheetName, cellgame, cellgame, headStyle)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyGameAmount)*0.0001)
|
||||
f.SetCellValue(sheetName, cellgame, totalamount)
|
||||
}
|
||||
|
||||
f.SetCellStyle(sheetName, cellorder, cellorder, headStyle)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.RechargeOrder)*0.01)
|
||||
f.SetCellValue(sheetName, cellorder, totalamount)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetDailyStatList(ctx context.Context, day *time.Time, yesterday *time.Time) (
|
||||
[]*dailystatmod.DailyStatMod, map[int32]*appInfoMod.AppInfo, error) {
|
||||
list := make([]*dailystatmod.DailyStatMod, 0)
|
||||
appinfo, serr := appInfoMod.GetAllAppIdMap(ctx, bson.M{})
|
||||
if serr != nil {
|
||||
log.Error("InnerServerDailyStat appInfoMod.GetAllAppInfo", log.Any("serr", serr))
|
||||
return nil, nil, serr
|
||||
}
|
||||
for _, info := range appinfo {
|
||||
rechargelist, serr := dailystatmod.GetDailyStatTotalList(ctx, &info.AppId, yesterday, day)
|
||||
if serr != nil {
|
||||
log.Error("InnerServerDailyStat dailystatmod.GetDailyStatTotalList", log.Any("serr", serr))
|
||||
continue
|
||||
}
|
||||
if rechargelist != nil {
|
||||
list = append(list, rechargelist...)
|
||||
} else {
|
||||
log.Error("InnerServerDailyStat rechargelist nil")
|
||||
}
|
||||
}
|
||||
return list, appinfo, nil
|
||||
}
|
||||
|
||||
type appDailyDataStat struct {
|
||||
AppName string `json:"appName"` // 产品名称
|
||||
AppId int32 `json:"appId"` // 产品Id
|
||||
SumDate time.Time `json:"sumDate"` // 日期
|
||||
Type string `json:"type"` // 产品类型,pay:付费,free:免费
|
||||
TotalVisitCount float64 `json:"totalVisitCount"` // 日活跃
|
||||
TotalNewCount float64 `json:"totalNewCount"` // 日注册
|
||||
TotalVidAmount float64 `json:"totalVidAmount"` // 视频日充值
|
||||
TotalQpAmount float64 `json:"totalQpAmount"` // 棋牌日充值
|
||||
VipRechargeCount float64 `json:"vipRechargeCount"` // vip交易笔数
|
||||
CoinRechargeCount float64 `json:"coinRechargeCount"` // 金币交易笔数
|
||||
RechargeCount float64 `json:"rechargeCount"` // 交易总笔数
|
||||
MonthAmount float64 `json:"monthAmount"` // 当月总充值
|
||||
MonthNewCount float64 `json:"monthNewCount"` // 当月总注册
|
||||
MonthRechargeCount float64 `json:"monthRechargeCount"` // 当月总交易笔数
|
||||
MonthVisitCount float64 `json:"monthVisitCount"` // 当月总日活
|
||||
}
|
||||
|
||||
func TianyuAppDailyData(ctx context.Context) ([]*appDailyDataStat, error) {
|
||||
resp := make([]*appDailyDataStat, 0)
|
||||
if statg.Conf.ServerIndex != statg.ServerIndexBili {
|
||||
log.Error("InnerServerMonthStat serverindex",
|
||||
log.Any("serverindex", statg.Conf.ServerIndex))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
log.Info("InnerServerMonthStat start")
|
||||
now := time.Now().AddDate(0, 0, -1)
|
||||
start := utils.BeginOfMonth(now)
|
||||
stop := utils.BeginWithTime(now).AddDate(0, 0, 1)
|
||||
|
||||
listOrg := make([]*proto.InnerDailyRecharge, 0)
|
||||
for id, info := range statg.Conf.InnerServers {
|
||||
req := &proto.InnerRechargeReq{
|
||||
InnerId: int32(id),
|
||||
Begin: start,
|
||||
End: stop,
|
||||
}
|
||||
url := info + "/api/stat/inner/recharge/list"
|
||||
log.Info("InnerServerMonthStat", log.Any("req", url))
|
||||
res := &proto.InnerDailyRechargeResp{}
|
||||
err := InnerRequest(url, req, res)
|
||||
if err != nil {
|
||||
log.Error("InnerServerMonthStat InnerRequest",
|
||||
log.Any("InnerRequest", err))
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
log.Error("InnerServerMonthStat InnerRequest data not found")
|
||||
continue
|
||||
}
|
||||
if res.Data.List != nil && len(res.Data.List) > 0 {
|
||||
listOrg = append(listOrg, res.Data.List...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, info := range listOrg {
|
||||
temp := &appDailyDataStat{
|
||||
AppId: 100000 + info.AppId,
|
||||
AppName: info.Name,
|
||||
SumDate: info.SumDate,
|
||||
TotalVisitCount: float64(info.DailyVistCount),
|
||||
TotalNewCount: float64(info.DailyRegister),
|
||||
TotalVidAmount: float64(info.DailyAppAmount),
|
||||
TotalQpAmount: float64(info.DailyGameAmount),
|
||||
RechargeCount: float64(info.RechargeOrder),
|
||||
}
|
||||
resp = append(resp, temp)
|
||||
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
package innerserverser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var exportTransWaterTitle = []string{"日活跃", "日注册", "视频日充值", "棋牌日充值", "交易笔数"}
|
||||
|
||||
const sheetName = "Sheet1"
|
||||
|
||||
// 按字母
|
||||
func NewXlslFile(f *excelize.File, list []*proto.InnerDailyRecharge) {
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(titlestyle)
|
||||
filePath := "/usr/local" + "/bakexel/" + "vvvv.xlsx"
|
||||
// 设置列宽
|
||||
f.SetColWidth("Sheet1", "A", "L", 23)
|
||||
|
||||
// 设置表头行高
|
||||
//f.SetRowHeight("Sheet1", 1, 25)
|
||||
f.SetCellStyle("Sheet1", "A1", "L1", headStyleID)
|
||||
idx := f.NewSheet(sheetName)
|
||||
f.MergeCell("Sheet1", "A1", "A2")
|
||||
f.SetCellValue(sheetName, "A1", "日期")
|
||||
f.SetCellStyle(sheetName, "A4", "A50", headStyleID)
|
||||
f.SetActiveSheet(idx)
|
||||
now := time.Now()
|
||||
day := now.Format("2006-01")
|
||||
begin, end := utils.MonthDateRange(now)
|
||||
beginday := utils.BeginOfMonth(now).Day()
|
||||
endday := utils.EndOfMonth(now).Day()
|
||||
index := "A"
|
||||
for i := beginday; i <= endday; i++ {
|
||||
tmp := fmt.Sprintf("%s%d", index, i+2)
|
||||
vvvvv := fmt.Sprintf("%s-%d", day, i)
|
||||
f.SetCellValue(sheetName, tmp, vvvvv)
|
||||
}
|
||||
one := 'A'
|
||||
begin0 := 'B'
|
||||
temp0 := begin0
|
||||
pre := ""
|
||||
for _, info := range list {
|
||||
t0 := temp0
|
||||
countt0 := 1
|
||||
for i := 1; i < 5; i++ {
|
||||
temp0 = t0 + int32(countt0)
|
||||
countt0++
|
||||
if temp0 > 'Z' {
|
||||
one++
|
||||
temp0 = 'A'
|
||||
t0 = temp0
|
||||
countt0 = 1
|
||||
}
|
||||
}
|
||||
//merge := setcellindex + 5 + int32(index*5)
|
||||
beginmerge := fmt.Sprintf("%s%s1", pre, string(begin0))
|
||||
if one > 'A' {
|
||||
pre = string(one - 1)
|
||||
}
|
||||
endmerge := fmt.Sprintf("%s%s1", pre, string(temp0))
|
||||
temp0 += 2
|
||||
if temp0 > 'Z' {
|
||||
one++
|
||||
temp0 = 'A' + temp0 - 'Z' - 1
|
||||
if one > 'A' {
|
||||
pre = string(one - 1)
|
||||
}
|
||||
}
|
||||
begin0 = temp0
|
||||
f.MergeCell("Sheet1", beginmerge, endmerge)
|
||||
//tmp := fmt.Sprintf("%s1", string(index0))
|
||||
f.SetCellValue(sheetName, beginmerge, info.Name)
|
||||
fmt.Println(beginmerge, endmerge)
|
||||
}
|
||||
err := f.SaveAs(filePath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(begin, end)
|
||||
}
|
||||
|
||||
// 按行列索引
|
||||
func NewXlslFile0(f *excelize.File, list []*proto.InnerDailyRecharge, titlestyle, cellstyle int) error {
|
||||
// 设置列宽
|
||||
f.SetColWidth(sheetName, "A", "AAA", 19)
|
||||
|
||||
// 设置表头行高
|
||||
f.SetRowHeight(sheetName, 1, 25)
|
||||
f.SetCellStyle(sheetName, "A1", "ZZ1", titlestyle)
|
||||
f.SetCellStyle(sheetName, "A2", "ZZ2", titlestyle)
|
||||
f.SetCellStyle(sheetName, "A3", "ZZ3", titlestyle)
|
||||
|
||||
idx := f.NewSheet(sheetName)
|
||||
f.SetCellValue(sheetName, "A1", "日期")
|
||||
f.SetActiveSheet(idx)
|
||||
|
||||
now := time.Now()
|
||||
dayformat := now.Format("2006-01")
|
||||
|
||||
//begin, end := utils.MonthDateRange(now)
|
||||
beginday := utils.BeginOfMonth(now).Day()
|
||||
endday := utils.EndOfMonth(now).Day()
|
||||
|
||||
//设置日期
|
||||
index := "A"
|
||||
for i := beginday; i <= endday; i++ {
|
||||
tmp := fmt.Sprintf("%s%d", index, i+3)
|
||||
rowday := fmt.Sprintf("%s-%02d", dayformat, i)
|
||||
|
||||
f.SetCellValue(sheetName, tmp, rowday)
|
||||
}
|
||||
|
||||
//新文件设置固定的主题和style
|
||||
row := 1
|
||||
col := 2
|
||||
count := col
|
||||
for _, info := range list {
|
||||
begincol := count
|
||||
count = count + 4
|
||||
titleindex := 0
|
||||
titlemap := make(map[string]string, 0)
|
||||
for i := begincol; i <= count; i++ {
|
||||
if titleindex < len(exportTransWaterTitle) {
|
||||
titlecell, serr := utils.CoordinatesToCellName(begincol+titleindex, row+2)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("title", exportTransWaterTitle[titleindex]))
|
||||
return serr
|
||||
}
|
||||
titlemap[titlecell] = exportTransWaterTitle[titleindex]
|
||||
}
|
||||
titleindex++
|
||||
}
|
||||
info.BeginCol = begincol
|
||||
// 第一行产品
|
||||
begincell, serr := utils.CoordinatesToCellName(begincol, row)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoname", info.Name))
|
||||
return serr
|
||||
}
|
||||
endcell, serr := utils.CoordinatesToCellName(count, row)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoname", info.Name))
|
||||
return serr
|
||||
}
|
||||
// 第二行appid
|
||||
appidbegincell, serr := utils.CoordinatesToCellName(begincol, row+1)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoappid", info.AppId))
|
||||
return serr
|
||||
}
|
||||
appidendcell, serr := utils.CoordinatesToCellName(count, row+1)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoappid", info.AppId))
|
||||
return serr
|
||||
}
|
||||
f.MergeCell(sheetName, begincell, endcell)
|
||||
|
||||
f.MergeCell(sheetName, appidbegincell, appidendcell)
|
||||
//tmp := fmt.Sprintf("%s1", string(index0))
|
||||
/*if info.AppId != 0 {
|
||||
if info.AppId == 888 {
|
||||
//tmp := fmt.Sprintf("%s1", string(index0))
|
||||
f.SetCellValue(sheetName, begincell, info.Name+"(集团)")
|
||||
} else {
|
||||
f.SetCellValue(sheetName, begincell, info.Name+"(内部)")
|
||||
}
|
||||
} else {
|
||||
*/
|
||||
f.SetCellValue(sheetName, begincell, info.Name)
|
||||
//}
|
||||
f.SetCellValue(sheetName, appidbegincell, info.AppId)
|
||||
|
||||
//第三行app统计的信息
|
||||
for key, value := range titlemap {
|
||||
//f.SetCellStyle(sheetName, key, key, cellstyle)
|
||||
f.SetCellValue(sheetName, key, value)
|
||||
}
|
||||
count += 2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 合并单元格
|
||||
func MergeCell(f *excelize.File, sheetname string, col int, list []*proto.InnerDailyRecharge) error {
|
||||
row := 1
|
||||
count := col + 1
|
||||
for _, info := range list {
|
||||
begincol := count
|
||||
count = count + 4
|
||||
titleindex := 0
|
||||
titlemap := make(map[string]string, 0)
|
||||
for i := begincol; i <= count; i++ {
|
||||
if titleindex < len(exportTransWaterTitle) {
|
||||
titlecell, serr := utils.CoordinatesToCellName(begincol+titleindex, row+2)
|
||||
if serr != nil {
|
||||
log.Error("MergeCell CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("title", exportTransWaterTitle[titleindex]))
|
||||
return serr
|
||||
}
|
||||
titlemap[titlecell] = exportTransWaterTitle[titleindex]
|
||||
}
|
||||
titleindex++
|
||||
}
|
||||
begincell, serr := utils.CoordinatesToCellName(begincol, row)
|
||||
if serr != nil {
|
||||
log.Error("MergeCell CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoname", info.Name))
|
||||
return serr
|
||||
}
|
||||
endcell, serr := utils.CoordinatesToCellName(count, row)
|
||||
if serr != nil {
|
||||
log.Error("MergeCell CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoname", info.Name))
|
||||
return serr
|
||||
}
|
||||
// 第一行appid
|
||||
appidbegincell, serr := utils.CoordinatesToCellName(begincol, row+1)
|
||||
if serr != nil {
|
||||
log.Error("MergeCell CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("appid", info.AppId))
|
||||
return serr
|
||||
}
|
||||
appidendcell, serr := utils.CoordinatesToCellName(count, row+1)
|
||||
if serr != nil {
|
||||
log.Error("MergeCell CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("appid", info.AppId))
|
||||
return serr
|
||||
}
|
||||
info.BeginCol = begincol
|
||||
f.MergeCell(sheetname, begincell, endcell)
|
||||
/*if info.AppId != 0 {
|
||||
if info.AppId == 888 {
|
||||
f.SetCellValue(sheetname, begincell, info.Name+"(集团)")
|
||||
} else {
|
||||
f.SetCellValue(sheetname, begincell, info.Name+"(内部)")
|
||||
}
|
||||
} else {
|
||||
*/
|
||||
f.SetCellValue(sheetname, begincell, info.Name)
|
||||
//}
|
||||
|
||||
f.MergeCell(sheetname, appidbegincell, appidendcell)
|
||||
//tmp := fmt.Sprintf("%s1", string(index0))
|
||||
f.SetCellValue(sheetname, appidbegincell, info.AppId)
|
||||
for key, value := range titlemap {
|
||||
f.SetCellValue(sheetname, key, value)
|
||||
}
|
||||
count += 2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func OpenOldXlsl(f *excelize.File, list []*proto.InnerDailyRecharge) error {
|
||||
idx := f.GetSheetIndex(sheetName)
|
||||
f.SetActiveSheet(idx)
|
||||
|
||||
cols := f.GetRows(sheetName)
|
||||
begincol := 0
|
||||
//查找数据开始单元格,新数据先合并单元格
|
||||
/*for rowindex, col := range cols {
|
||||
for colindex, rowCell := range col {
|
||||
if rowCell == "" {
|
||||
continue
|
||||
}
|
||||
if rowindex == 1 {
|
||||
for _, info := range list {
|
||||
if rowCell == string(info.AppId) {
|
||||
info.BeginRow = rowindex + 1
|
||||
info.BeginCol = colindex + 1
|
||||
info.IsOld = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if len(cols) < 2 || len(cols[1]) < len(list) {
|
||||
return errors.New("OpenOldXlsl cols < 0")
|
||||
}
|
||||
|
||||
/*for rowindex := 0; rowindex < len(cols); rowindex++ {
|
||||
if cols[rowindex][0] != "" {
|
||||
for _, info := range list {
|
||||
if cols[rowindex][0] == info.DateTime {
|
||||
info.BeginRow = rowindex + 1
|
||||
info.IsOld = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
//旧的数据单元格开始信息
|
||||
for colindex := 0; colindex < len(cols[1]); colindex++ {
|
||||
for _, info := range list {
|
||||
appid := strconv.Itoa(int(info.AppId))
|
||||
if cols[1][colindex] != "" && cols[1][colindex] == appid {
|
||||
info.BeginCol = colindex + 1
|
||||
info.IsOld = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//新的单元格从第几列信息
|
||||
newlist := make([]*proto.InnerDailyRecharge, 0)
|
||||
for _, info := range list {
|
||||
if info.IsOld {
|
||||
if info.BeginCol > begincol {
|
||||
begincol = info.BeginCol
|
||||
}
|
||||
} else {
|
||||
newlist = append(newlist, info)
|
||||
}
|
||||
}
|
||||
if len(newlist) > 0 {
|
||||
serr := MergeCell(f, sheetName, begincol+5, newlist)
|
||||
if serr != nil {
|
||||
log.Error("OpenOldXlsl MergeCell ", log.Any("err", serr))
|
||||
return serr
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewXlslFile2(f *excelize.File, list []*AppInformation, titlestyle, cellstyle int, now time.Time) error {
|
||||
// 设置列宽
|
||||
f.SetColWidth(sheetName, "A", "AAA", 19)
|
||||
|
||||
// 设置表头行高
|
||||
f.SetRowHeight(sheetName, 1, 25)
|
||||
f.SetCellStyle(sheetName, "A1", "ZZ1", titlestyle)
|
||||
f.SetCellStyle(sheetName, "A2", "ZZ2", titlestyle)
|
||||
f.SetCellStyle(sheetName, "A3", "ZZ3", titlestyle)
|
||||
|
||||
idx := f.NewSheet(sheetName)
|
||||
f.SetCellValue(sheetName, "A1", "日期")
|
||||
f.SetActiveSheet(idx)
|
||||
|
||||
dayformat := now.Format("2006-01")
|
||||
|
||||
//begin, end := utils.MonthDateRange(now)
|
||||
beginday := utils.BeginOfMonth(now).Day()
|
||||
endday := utils.EndOfMonth(now).Day()
|
||||
|
||||
//设置日期
|
||||
index := "A"
|
||||
for i := beginday; i <= endday; i++ {
|
||||
tmp := fmt.Sprintf("%s%d", index, i+3)
|
||||
rowday := fmt.Sprintf("%s-%02d", dayformat, i)
|
||||
f.SetCellValue(sheetName, tmp, rowday)
|
||||
}
|
||||
// 设置汇总
|
||||
tmp := fmt.Sprintf("%s%d", index, endday+1+3)
|
||||
f.SetCellValue(sheetName, tmp, "汇总")
|
||||
|
||||
//新文件设置固定的主题和style
|
||||
row := 1
|
||||
col := 2
|
||||
count := col
|
||||
for _, info := range list {
|
||||
begincol := count
|
||||
count = count + 4
|
||||
titleindex := 0
|
||||
titlemap := make(map[string]string, 0)
|
||||
for i := begincol; i <= count; i++ {
|
||||
if titleindex < len(exportTransWaterTitle) {
|
||||
titlecell, serr := utils.CoordinatesToCellName(begincol+titleindex, row+2)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("title", exportTransWaterTitle[titleindex]))
|
||||
return serr
|
||||
}
|
||||
titlemap[titlecell] = exportTransWaterTitle[titleindex]
|
||||
}
|
||||
titleindex++
|
||||
}
|
||||
info.BeginCol = begincol
|
||||
// 第一行产品
|
||||
begincell, serr := utils.CoordinatesToCellName(begincol, row)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoname", info.Name))
|
||||
return serr
|
||||
}
|
||||
endcell, serr := utils.CoordinatesToCellName(count, row)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoname", info.Name))
|
||||
return serr
|
||||
}
|
||||
// 第二行appid
|
||||
appidbegincell, serr := utils.CoordinatesToCellName(begincol, row+1)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoappid", info.Id))
|
||||
return serr
|
||||
}
|
||||
appidendcell, serr := utils.CoordinatesToCellName(count, row+1)
|
||||
if serr != nil {
|
||||
log.Error("NewXlslFile0 CoordinatesToCellName ", log.Any("err", serr),
|
||||
log.Any("infoappid", info.Id))
|
||||
return serr
|
||||
}
|
||||
f.MergeCell(sheetName, begincell, endcell)
|
||||
f.MergeCell(sheetName, appidbegincell, appidendcell)
|
||||
f.SetCellValue(sheetName, begincell, info.Name)
|
||||
f.SetCellValue(sheetName, appidbegincell, info.Id)
|
||||
//第三行app统计的信息
|
||||
for key, value := range titlemap {
|
||||
f.SetCellValue(sheetName, key, value)
|
||||
}
|
||||
count += 2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package innerserverser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
"os"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/utils"
|
||||
"stat/tool/pubtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExel1(t *testing.T) {
|
||||
var f *excelize.File
|
||||
newxlslfile := false
|
||||
day0 := utils.BeginOfYesterday()
|
||||
dir := day0.Format("2006-01")
|
||||
filePath := "/usr/local" + "/bakmonth/" + dir + "/"
|
||||
//filename := utils.BeginOfYesterday().Format("2006-01-02")
|
||||
if !DocExsit(filePath) {
|
||||
err := os.MkdirAll(filePath, os.ModePerm)
|
||||
if err != nil {
|
||||
log.Error("InnerServerDailyStat:export save file to disk failed",
|
||||
log.Any("fileName", filePath),
|
||||
log.E(err))
|
||||
return
|
||||
}
|
||||
f = excelize.NewFile()
|
||||
newxlslfile = true
|
||||
} else {
|
||||
if !DocExsit(filePath + dir + ".xlsx") {
|
||||
f = excelize.NewFile()
|
||||
newxlslfile = true
|
||||
} else {
|
||||
f, _ = excelize.OpenFile(filePath + dir + ".xlsx")
|
||||
}
|
||||
}
|
||||
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(`{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":true,
|
||||
"size":16,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center",
|
||||
"wrap_text":true
|
||||
}
|
||||
}`)
|
||||
|
||||
headStyleID0, _ := f.NewStyle(`{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":false,
|
||||
"size":10,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center",
|
||||
"wrap_text":true
|
||||
}
|
||||
}`)
|
||||
pubtest.TestInit("/Users/mac/Desktop/madou/stat_center")
|
||||
ctx := context.Background()
|
||||
list := make([]*proto.InnerDailyRecharge, 0)
|
||||
yesterday := utils.BeginOfYesterday0()
|
||||
bililist, appinfo, err := GetDbList(ctx, &day0, &yesterday)
|
||||
if err != nil {
|
||||
log.Error("InnerServerDailyStat:GetDbList", log.E(err))
|
||||
}
|
||||
|
||||
for _, info := range bililist {
|
||||
name := ""
|
||||
if data, ok := appinfo[info.AppId]; ok {
|
||||
name = data.Name
|
||||
}
|
||||
temp := &proto.InnerDailyRecharge{
|
||||
AppId: info.AppId,
|
||||
Name: name,
|
||||
Type: "",
|
||||
DateTime: "",
|
||||
DailyVistCount: info.TotalVisitCount,
|
||||
DailyRegister: info.NewTotalCount,
|
||||
DailyAppAmount: info.TotalAmount,
|
||||
DailyGameAmount: info.GameRechageAmount,
|
||||
RechargeOrder: info.SuccessRechargeOrder,
|
||||
}
|
||||
list = append(list, temp)
|
||||
}
|
||||
|
||||
temp := &proto.InnerDailyRecharge{}
|
||||
for _, info := range list {
|
||||
temp.AppId = 0
|
||||
temp.Name = "天宇总充值"
|
||||
temp.DailyVistCount += info.DailyVistCount
|
||||
temp.DailyRegister += info.DailyRegister
|
||||
temp.DailyAppAmount += info.DailyAppAmount
|
||||
temp.DailyGameAmount += info.DailyGameAmount
|
||||
temp.RechargeOrder += info.RechargeOrder
|
||||
}
|
||||
list = append(list, temp)
|
||||
for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
|
||||
list[i], list[j] = list[j], list[i]
|
||||
}
|
||||
|
||||
if newxlslfile {
|
||||
NewXlslFile0(f, list, headStyleID, headStyleID0)
|
||||
} else {
|
||||
OpenOldXlsl(f, list)
|
||||
}
|
||||
|
||||
row := 1
|
||||
day := yesterday.Format("2006-01-02")
|
||||
cols := f.GetRows("Sheet1")
|
||||
for rowindex, col := range cols {
|
||||
for colindex, rowCell := range col {
|
||||
if rowCell == "" {
|
||||
continue
|
||||
}
|
||||
if colindex == 0 {
|
||||
if rowCell == day {
|
||||
row = rowindex
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, info := range list {
|
||||
cellday, serr := utils.CoordinatesToCellName(info.BeginCol, row)
|
||||
if serr != nil {
|
||||
log.Error("OpenOldXlsl CoordinatesToCellName ", log.Any("err", serr))
|
||||
continue
|
||||
}
|
||||
cellregister, serr := utils.CoordinatesToCellName(info.BeginCol+1, row)
|
||||
if serr != nil {
|
||||
log.Error("OpenOldXlsl CoordinatesToCellName ", log.Any("err", serr))
|
||||
continue
|
||||
}
|
||||
cellrecharge, serr := utils.CoordinatesToCellName(info.BeginCol+2, row)
|
||||
if serr != nil {
|
||||
log.Error("OpenOldXlsl CoordinatesToCellName ", log.Any("err", serr))
|
||||
continue
|
||||
}
|
||||
cellgame, serr := utils.CoordinatesToCellName(info.BeginCol+3, row)
|
||||
if serr != nil {
|
||||
log.Error("OpenOldXlsl CoordinatesToCellName ", log.Any("err", serr))
|
||||
continue
|
||||
}
|
||||
cellorder, serr := utils.CoordinatesToCellName(info.BeginCol+4, row)
|
||||
if serr != nil {
|
||||
log.Error("OpenOldXlsl CoordinatesToCellName ", log.Any("err", serr))
|
||||
continue
|
||||
}
|
||||
|
||||
f.SetCellStyle("Sheet1", cellday, cellday, headStyleID0)
|
||||
totalamount := fmt.Sprintf("%.2f", float64(info.DailyVistCount)*0.01)
|
||||
f.SetCellValue("Sheet1", cellday, totalamount)
|
||||
f.SetCellStyle("Sheet1", cellregister, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyRegister)*0.01)
|
||||
f.SetCellValue("Sheet1", cellregister, totalamount)
|
||||
f.SetCellStyle("Sheet1", cellrecharge, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyAppAmount)*0.0001)
|
||||
f.SetCellValue("Sheet1", cellrecharge, totalamount)
|
||||
f.SetCellStyle("Sheet1", cellgame, cellday, headStyleID0)
|
||||
if info.AppId == 0 || info.AppId == 888 {
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.DailyGameAmount)*0.0001)
|
||||
}
|
||||
f.SetCellValue("Sheet1", cellgame, totalamount)
|
||||
f.SetCellStyle("Sheet1", cellorder, cellday, headStyleID0)
|
||||
totalamount = fmt.Sprintf("%.2f", float64(info.RechargeOrder)*0.01)
|
||||
f.SetCellValue("Sheet1", cellorder, totalamount)
|
||||
}
|
||||
if newxlslfile {
|
||||
f.SaveAs(filePath + dir + ".xlsx")
|
||||
} else {
|
||||
f.Save()
|
||||
}
|
||||
}
|
||||
|
||||
func TestExel0(t *testing.T) {
|
||||
rowday := fmt.Sprintf("%s-%02d", "", 9)
|
||||
fmt.Println(rowday)
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package landpageSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/api/proto"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/downloadstat"
|
||||
"stat/models/landpageImg"
|
||||
"stat/service/cacheSer"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func ListDailyStat(ctx context.Context, param *proto.ListDailyStatReq) (*proto.ListLandPageStatRes, error) {
|
||||
resp := &proto.ListLandPageStatRes{
|
||||
Total: 0,
|
||||
List: make([]*proto.DownloadStatInfo, 0),
|
||||
}
|
||||
filter := bson.M{"type": downloadstat.StatTypeDaily}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.D{{Key: "sumDate", Value: -1}})
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
opt.SetLimit(param.PageSize)
|
||||
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
if param.StartTime != nil && param.EndTime != nil {
|
||||
filter["sumDate"] = bson.M{"$gte": *param.StartTime, "$lt": *param.EndTime}
|
||||
}
|
||||
|
||||
total, _ := downloadstat.CountStats(ctx, filter)
|
||||
|
||||
statList, err := downloadstat.FindManyStat(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
for _, item := range statList {
|
||||
item.AppName = appMap[item.AppId]
|
||||
tmp := &proto.DownloadStatInfo{
|
||||
Stat: *item,
|
||||
}
|
||||
proto.StatSummation(&resp.Summary, tmp)
|
||||
tmp.CalcConversionRate()
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
resp.Total = total
|
||||
resp.Summary.CalcConversionRate()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func ListDailyHourStat(ctx context.Context, param *proto.ListDailyHourStatReq) (*proto.ListLandPageStatRes, error) {
|
||||
resp := &proto.ListLandPageStatRes{
|
||||
Total: 0,
|
||||
List: make([]*proto.DownloadStatInfo, 0),
|
||||
}
|
||||
filter := bson.M{"type": downloadstat.StatTypeHour}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.D{{Key: "sumDate", Value: -1}, {Key: "hour", Value: -1}})
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
opt.SetLimit(param.PageSize)
|
||||
|
||||
if param.AppId != nil {
|
||||
filter["appId"] = *param.AppId
|
||||
}
|
||||
if param.StartTime != nil && param.EndTime != nil {
|
||||
filter["sumDate"] = bson.M{"$gte": *param.StartTime, "$lt": *param.EndTime}
|
||||
}
|
||||
if param.SumHour != nil {
|
||||
filter["hour"] = *param.SumHour
|
||||
}
|
||||
|
||||
total, _ := downloadstat.CountStats(ctx, filter)
|
||||
|
||||
statList, err := downloadstat.FindManyStat(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appMap, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
for _, item := range statList {
|
||||
item.AppName = appMap[item.AppId]
|
||||
tmp := &proto.DownloadStatInfo{
|
||||
Stat: *item,
|
||||
}
|
||||
proto.StatSummation(&resp.Summary, tmp)
|
||||
tmp.CalcConversionRate()
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
resp.Total = total
|
||||
resp.Summary.CalcConversionRate()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func LandpagePicture(ctx context.Context, req *proto.LandpagePictureSearch) (resp *proto.LandpagePictureSearchResp, err error) {
|
||||
resp = &proto.LandpagePictureSearchResp{}
|
||||
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
|
||||
if req.Status != nil {
|
||||
filter[landpageImg.FN.LandpageImg.Status] = *req.Status
|
||||
}
|
||||
if req.Name != nil {
|
||||
filter[landpageImg.FN.LandpageImg.Name] = *req.Name
|
||||
}
|
||||
opt.SetSort(bson.M{landpageImg.FN.LandpageImg.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
resp.Total, err = landpageImg.CountLandpageImgs(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
landpagePictureList, serr := landpageImg.FindManyLandpageImg(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
resp.List = landpagePictureList
|
||||
return
|
||||
}
|
||||
|
||||
func AddLandpagePicture(ctx context.Context, req *proto.LandpagePictureAddReq) error {
|
||||
now := time.Now()
|
||||
req.CreatedAt = now
|
||||
req.UpdatedAt = now
|
||||
|
||||
serr := landpageImg.InsertOneLandpageImg(ctx, &req.LandpageImg)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func DelLandpagePicture(ctx context.Context, id string) error {
|
||||
objectId, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{landpageImg.FN.LandpageImg.ID: objectId}
|
||||
//删除
|
||||
_, serr := landpageImg.DelOneLandpageImg(ctx, adFilter)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateLandpagePicture(ctx context.Context, req *proto.LandpagePictureUpdateReq) error {
|
||||
now := time.Now()
|
||||
req.Param.UpdatedAt = &now
|
||||
objectId, err := primitive.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{landpageImg.FN.LandpageImg.ID: objectId}
|
||||
updateDoc := bson.M{"$set": req.Param}
|
||||
serr := landpageImg.UpdateOneLandpageImg(ctx, adFilter, updateDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListSummaryDownload(ctx context.Context) (*proto.ListDownloadSummaryRes, error) {
|
||||
resp := &proto.ListDownloadSummaryRes{
|
||||
List: make([]proto.DownloadSummaryInfo, 0),
|
||||
}
|
||||
|
||||
cacheKey := fmt.Sprintf("landPage#summary")
|
||||
if cache, exists := cacheSer.Cache.Get(cacheKey); exists {
|
||||
data, ok := cache.([]proto.DownloadSummaryInfo)
|
||||
if !ok {
|
||||
pipeline := []bson.M{
|
||||
{"$group": bson.M{
|
||||
"_id": "$appId",
|
||||
"total": bson.M{"$sum": "$downTotal"},
|
||||
}},
|
||||
}
|
||||
if err := downloadstat.AggregateStats(ctx, &resp.List, pipeline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cacheSer.Cache.Set(cacheKey, resp.List, 15*time.Minute)
|
||||
} else {
|
||||
resp.List = data
|
||||
}
|
||||
} else {
|
||||
pipeline := []bson.M{
|
||||
{"$group": bson.M{
|
||||
"_id": "$appId",
|
||||
"total": bson.M{"$sum": "$downTotal"},
|
||||
}},
|
||||
}
|
||||
if err := downloadstat.AggregateStats(ctx, &resp.List, pipeline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cacheSer.Cache.Set(cacheKey, resp.List, 15*time.Minute)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package mediatagSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/models/mediatag"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func MediaTag(ctx context.Context, req *proto.MediaTagSearch) (resp *proto.MediaTagSearchResp, err error) {
|
||||
resp = &proto.MediaTagSearchResp{}
|
||||
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
|
||||
if req.Status != nil {
|
||||
filter[mediatag.FN.MediaTag.Status] = *req.Status
|
||||
}
|
||||
if req.Name != nil {
|
||||
filter[mediatag.FN.MediaTag.Name] = *req.Name
|
||||
}
|
||||
if req.Type != nil {
|
||||
filter[mediatag.FN.MediaTag.Type] = *req.Type
|
||||
}
|
||||
opt.SetSort(bson.M{mediatag.FN.MediaTag.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
resp.Total, err = mediatag.CountMediaTags(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
MediaTagList, serr := mediatag.FindManyMediaTag(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
resp.List = MediaTagList
|
||||
return
|
||||
}
|
||||
|
||||
func AddMediaTag(ctx context.Context, req *proto.MediaTagAddReq) error {
|
||||
now := time.Now()
|
||||
req.CreatedAt = now
|
||||
req.UpdatedAt = now
|
||||
|
||||
serr := mediatag.InsertOneMediaTag(ctx, &req.MediaTag)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func DelMediaTag(ctx context.Context, id string) error {
|
||||
objectId, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{mediatag.FN.MediaTag.ID: objectId}
|
||||
//删除
|
||||
_, serr := mediatag.DelOneMediaTag(ctx, adFilter)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateMediaTag(ctx context.Context, req *proto.MediaTagUpdateReq) error {
|
||||
now := time.Now()
|
||||
req.Param.UpdatedAt = &now
|
||||
objectId, err := primitive.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{mediatag.FN.MediaTag.ID: objectId}
|
||||
updateDoc := bson.M{"$set": req.Param}
|
||||
serr := mediatag.UpdateOneMediaTag(ctx, adFilter, updateDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package monitorSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/models/monitormod"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
func SearchMonitor(ctx context.Context, req *proto.MonitorSearch) (resp *proto.MonitorSearchResp, err error) {
|
||||
resp = &proto.MonitorSearchResp{}
|
||||
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
|
||||
if req.Status != nil {
|
||||
filter[monitormod.FN.Monitor.Status] = *req.Status
|
||||
}
|
||||
if req.Name != nil {
|
||||
filter[monitormod.FN.Monitor.Name] = bson.M{"$regex": *req.Name}
|
||||
}
|
||||
if req.Category != nil {
|
||||
filter[monitormod.FN.Monitor.Category] = *req.Category
|
||||
}
|
||||
opt.SetSort(bson.M{monitormod.FN.Monitor.CreatedAt: -1})
|
||||
opt.SetSkip((req.PageNum - 1) * req.PageSize)
|
||||
opt.SetLimit(req.PageSize)
|
||||
|
||||
resp.Total, err = monitormod.CountMonitors(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
MonitorList, serr := monitormod.FindManyMonitor(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
resp.List = MonitorList
|
||||
return
|
||||
}
|
||||
|
||||
func AddMonitor(ctx context.Context, req *proto.MonitorAddReq) error {
|
||||
now := time.Now()
|
||||
req.CreatedAt = now
|
||||
req.UpdatedAt = now
|
||||
|
||||
serr := monitormod.InsertOneMonitor(ctx, &req.Monitor)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func DelMonitor(ctx context.Context, id string) error {
|
||||
objectId, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{monitormod.FN.Monitor.ID: objectId}
|
||||
//删除
|
||||
_, serr := monitormod.DelOneMonitor(ctx, adFilter)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateMonitor(ctx context.Context, req *proto.MonitorUpdateReq) error {
|
||||
now := time.Now()
|
||||
req.Param.UpdatedAt = &now
|
||||
objectId, err := primitive.ObjectIDFromHex(req.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adFilter := bson.M{monitormod.FN.Monitor.ID: objectId}
|
||||
updateDoc := bson.M{"$set": req.Param}
|
||||
serr := monitormod.UpdateOneMonitor(ctx, adFilter, updateDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package monitorSer
|
||||
|
||||
import (
|
||||
"github.com/robfig/cron/v3"
|
||||
"stat/common/log"
|
||||
"stat/common/statutils"
|
||||
)
|
||||
|
||||
var (
|
||||
auto *cron.Cron
|
||||
)
|
||||
|
||||
func init() {
|
||||
auto = cron.New(cron.WithSeconds(), cron.WithChain(cron.SkipIfStillRunning(&log.Logger{}))) // 秒级定时任务
|
||||
}
|
||||
|
||||
func InitMonitor() {
|
||||
_, _ = auto.AddFunc("0 1 * * * ?", func() {
|
||||
statutils.ServerMon.RunWithName(func() error {
|
||||
return MonitorCheck()
|
||||
}, statutils.GetFuncName(MonitorCheck))
|
||||
})
|
||||
|
||||
auto.Start()
|
||||
}
|
||||
|
||||
func MonitorCheck() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package opLogSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"stat/api/proto"
|
||||
"stat/common"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/opLogMod"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 添加操作日志
|
||||
func AddOpLog(ctx *gin.Context, account string, isAdmin bool, opData interface{}) {
|
||||
common.Go(func() {
|
||||
now := time.Now()
|
||||
opDataStr, _ := json.Marshal(opData)
|
||||
|
||||
doc := opLogMod.OpLog{
|
||||
Operator: account,
|
||||
Router: ctx.Request.RequestURI,
|
||||
Data: string(opDataStr),
|
||||
IsAdmin: isAdmin,
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
_ = opLogMod.AddOpLog(ctx, &doc)
|
||||
})
|
||||
}
|
||||
|
||||
func GetOpLogList(ctx context.Context, param proto.ListOpLogReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListOpLogRes{
|
||||
Total: 0,
|
||||
List: make([]proto.OpLogInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, opList, err := opLogMod.GetOpLogList(ctx, param.Operator, param.Router, param.IsAdmin, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range opList {
|
||||
tmp := proto.OpLogInfo{
|
||||
Operator: v.Operator,
|
||||
Router: v.Router,
|
||||
Data: v.Data,
|
||||
IsAdmin: v.IsAdmin,
|
||||
CreatedAt: v.CreatedAt,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package smsser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"stat/common/log"
|
||||
"stat/common/redis"
|
||||
"stat/common/sms"
|
||||
"stat/common/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
CaptchaLen = 6
|
||||
MobileCaptchaExpire = 5 * time.Minute
|
||||
)
|
||||
|
||||
// SendSmsCode 发送验证码
|
||||
func SendSmsCode(mobile string) error {
|
||||
code := utils.RandDigits(CaptchaLen)
|
||||
//fmt.Println(code)
|
||||
redisKey := fmt.Sprintf("captcha:mobile:%s", mobile)
|
||||
err := redis.StatRds.Set(redisKey, code, MobileCaptchaExpire)
|
||||
if err != nil {
|
||||
log.Error("SendCaptcha Save Redis error", log.Any("mobile", mobile), log.E(err))
|
||||
return err
|
||||
}
|
||||
|
||||
err = sms.Send(sms.FengNiao, mobile, code)
|
||||
if err != nil {
|
||||
log.Error("SendCaptcha send err", log.Any("mobile", mobile), log.Any("Platform", sms.FengNiao), log.E(err))
|
||||
yunErr := sms.Send(sms.YunPian, mobile, code)
|
||||
if yunErr != nil {
|
||||
log.Error("SendCaptcha send err", log.Any("mobile", mobile), log.Any("Platform", sms.YunPian), log.E(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySmsCode 验证验证码
|
||||
func VerifySmsCode(mobile string, code string) error {
|
||||
redisKey := fmt.Sprintf("captcha:mobile:%s", mobile)
|
||||
redisCode, err := redis.StatRds.Get(redisKey)
|
||||
if err != nil {
|
||||
log.Error("VerifySmsCode redis get err", log.E(err))
|
||||
return err
|
||||
}
|
||||
if redisCode != nil && *redisCode == code {
|
||||
return nil
|
||||
} else {
|
||||
log.Info("VerifySmsCode miss", log.Any("mobile", mobile), log.Any("code", code), log.Any("redisCode", redisCode))
|
||||
return fmt.Errorf("VerifySmsCode miss")
|
||||
}
|
||||
}
|
||||
|
||||
func GetSmsCode(mobile string) (string, error) {
|
||||
redisKey := fmt.Sprintf("captcha:mobile:%s", mobile)
|
||||
redisCode, err := redis.StatRds.Get(redisKey)
|
||||
if err != nil {
|
||||
log.Error("VerifySmsCode redis get err", log.E(err))
|
||||
return "", err
|
||||
}
|
||||
if redisCode != nil {
|
||||
return *redisCode, nil
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package srv_appcategory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/genId"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/appcategory"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// GetAppCategory 按照参数搜索
|
||||
func GetAppCategory(ctx context.Context, param *AppCategorySearch) (*AppCategorySearchResp, *stdLog.SrvErr) {
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
resp := &AppCategorySearchResp{
|
||||
List: make(appcategory.AppCategoryList, 0),
|
||||
Total: 0,
|
||||
}
|
||||
opt.SetSort(bson.M{"rank": 1})
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
opt.SetLimit(param.PageSize)
|
||||
|
||||
if param.ParentAppId != nil {
|
||||
filter["parentAppId"] = param.ParentAppId
|
||||
}
|
||||
|
||||
total, serr := appcategory.CountAppCategories(ctx, filter)
|
||||
if serr != nil {
|
||||
return resp, serr
|
||||
}
|
||||
if total == 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
list, serr := appcategory.FindManyAppCategory(ctx, filter, opt)
|
||||
if serr != nil {
|
||||
return resp, serr
|
||||
}
|
||||
if list != nil && len(list) > 0 {
|
||||
resp.List = list
|
||||
resp.Total = total
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AddAppCategory 添加一个
|
||||
func AddAppCategory(ctx context.Context,
|
||||
ac *appcategory.AppCategory) *stdLog.SrvErr {
|
||||
|
||||
now := time.Now()
|
||||
ac.Id = genId.GetAppCategoryId()
|
||||
ac.CreatedAt = now
|
||||
ac.UpdatedAt = now
|
||||
|
||||
if ac.Name == "" {
|
||||
return stdLog.New(stdLog.ParamError, "", "name不能传空")
|
||||
}
|
||||
_, serr := appcategory.FindOneAppCategory(ctx, bson.M{"name": ac.Name})
|
||||
if serr == nil {
|
||||
return stdLog.New(stdLog.ParamError, "", "name重名")
|
||||
}
|
||||
|
||||
serr = appcategory.InsertOneAppCategory(ctx, ac)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelAppCategory 删除指定
|
||||
func DelAppCategory(ctx context.Context, id int64) *stdLog.SrvErr {
|
||||
adFilter := bson.M{"_id": id}
|
||||
//删除
|
||||
_, serr := appcategory.DelOneAppCategory(ctx, adFilter)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAppCategory 更新指定
|
||||
func UpdateAppCategory(ctx context.Context, id int64,
|
||||
info *AppCategoryUpdate) *stdLog.SrvErr {
|
||||
now := time.Now()
|
||||
info.UpdatedAt = &now
|
||||
|
||||
adFilter := bson.M{"_id": id}
|
||||
updateDoc := bson.M{"$set": info}
|
||||
serr := appcategory.UpdateOneAppCategory(ctx, adFilter, updateDoc)
|
||||
if serr != nil {
|
||||
return serr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package srv_appcategory
|
||||
|
||||
import (
|
||||
"stat/common"
|
||||
"stat/models/appcategory"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AppCategorySearch
|
||||
type AppCategorySearch struct {
|
||||
ParentAppId *int64 `json:"parentAppId" bson:"parentAppId"`
|
||||
common.Page
|
||||
}
|
||||
|
||||
type AppCategorySearchResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List appcategory.AppCategoryList `json:"list"`
|
||||
}
|
||||
|
||||
type AppCategoryUpdate struct {
|
||||
ParentAppId *int64 `json:"parentAppId" bson:"parentAppId"`
|
||||
Name *string `json:"name" bson:"name,omitempty"` // 名称
|
||||
Desc *string `json:"desc" bson:"desc,omitempty"` // 描述
|
||||
Status *bool `json:"status" bson:"status,omitempty"` // 状态
|
||||
Rank *int64 `json:"rank" bson:"rank,omitempty"` // 应用排序
|
||||
UpdatedAt *time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` // 更新时间
|
||||
ImgType *int64 `json:"imgType" bson:"imgType,omitempty"` // 图片展示类型 0:应用图标 1.小图标 2.大图标
|
||||
}
|
||||
|
||||
type AppCategoryUpdateReq struct {
|
||||
Param AppCategoryUpdate `json:"param" bson:"param"` // 参数
|
||||
Id int64 `json:"id" bson:"_id,omitempty"`
|
||||
}
|
||||
|
||||
type AppCategoryDelReq struct {
|
||||
Id int64 `json:"id" bson:"_id,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package srv_applicationstat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/common/stdLog"
|
||||
"stat/models/applicationmod"
|
||||
"stat/models/applicationstat"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// GetAppStatList 获取应用统计列表
|
||||
func GetAppStatList(ctx context.Context, param *GetApplicationStatReq) (*GetApplicationStatResp, *stdLog.SrvErr) {
|
||||
resp := &GetApplicationStatResp{}
|
||||
//先获取app列表
|
||||
appFilter := bson.M{}
|
||||
statFilter := bson.M{}
|
||||
if param.ParentAppId != nil {
|
||||
appFilter["parentAppId"] = bson.M{"$in": *param.ParentAppId}
|
||||
}
|
||||
if param.ApplicationId != nil {
|
||||
appFilter["_id"] = *param.ApplicationId
|
||||
}
|
||||
if param.ApplicationName != nil {
|
||||
appFilter["name"] = *param.ApplicationName
|
||||
}
|
||||
appList, sErr := applicationmod.FindManyApplication(ctx, appFilter)
|
||||
if sErr != nil {
|
||||
return nil, sErr
|
||||
}
|
||||
applicationId := make([]primitive.ObjectID, 0)
|
||||
for _, info := range appList {
|
||||
applicationId = append(applicationId, info.Id)
|
||||
}
|
||||
statFilter["applicationDbId"] = bson.M{"$in": applicationId}
|
||||
if param.Start != nil || param.End != nil {
|
||||
statFilter[applicationstat.FN.ApplicationStat.SumData] = bson.M{"$gte": *param.Start, "$lte": *param.End}
|
||||
}
|
||||
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{applicationstat.FN.ApplicationStat.SumData: -1})
|
||||
opt.SetLimit(param.PageSize)
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
|
||||
totalStats, sErr := applicationstat.FindManyApplicationStat(ctx, statFilter)
|
||||
if sErr != nil {
|
||||
return nil, sErr
|
||||
}
|
||||
|
||||
pageStats, sErr := applicationstat.FindManyApplicationStat(ctx, statFilter, opt)
|
||||
if sErr != nil {
|
||||
return nil, sErr
|
||||
}
|
||||
|
||||
appDict := make(map[primitive.ObjectID]*applicationmod.Application)
|
||||
for _, app := range appList {
|
||||
appDict[app.Id] = app
|
||||
}
|
||||
|
||||
for _, stat := range totalStats {
|
||||
resp.Summary.TotalClick += stat.Total
|
||||
}
|
||||
|
||||
for _, stat := range pageStats {
|
||||
name := ""
|
||||
parentAppId := int64(0)
|
||||
app := appDict[stat.ApplicationDbId]
|
||||
if app != nil {
|
||||
name = app.Name
|
||||
parentAppId = app.ParentAppId
|
||||
}
|
||||
hours := make([]int64, 24)
|
||||
for hour := range hours {
|
||||
hours[hour] = stat.HourData[hour]
|
||||
}
|
||||
doc := &AppStatInfo{
|
||||
Id: stat.Id,
|
||||
ApplicationDbId: stat.ApplicationDbId,
|
||||
ParentAppId: parentAppId,
|
||||
AppName: name,
|
||||
SumDate: stat.SumDate,
|
||||
Total: stat.Total,
|
||||
HourData: hours,
|
||||
CreatedAt: stat.CreatedAt,
|
||||
UpdatedAt: stat.UpdatedAt,
|
||||
}
|
||||
resp.List = append(resp.List, doc)
|
||||
}
|
||||
|
||||
resp.Total = int64(len(totalStats))
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAppStatList 获取应用统计列表
|
||||
func GetAppTotalStatList(ctx context.Context, param *GetApplicationStatTotalReq) (*GetApplicationStatTotalResp, *stdLog.SrvErr) {
|
||||
resp := &GetApplicationStatTotalResp{
|
||||
List: make([]*AppStatTotalInfo, 0),
|
||||
}
|
||||
|
||||
//先获取app列表
|
||||
appFilter := bson.M{}
|
||||
if param.ParentAppId != nil {
|
||||
appFilter["parentAppId"] = bson.M{"$in": *param.ParentAppId}
|
||||
}
|
||||
if param.ApplicationId != nil {
|
||||
appFilter["_id"] = *param.ApplicationId
|
||||
}
|
||||
if param.ApplicationName != nil {
|
||||
appFilter["name"] = *param.ApplicationName
|
||||
}
|
||||
appList, sErr := applicationmod.FindManyApplication(ctx, appFilter)
|
||||
|
||||
if sErr != nil {
|
||||
return nil, sErr
|
||||
}
|
||||
|
||||
applicationId := make([]primitive.ObjectID, 0)
|
||||
appid := make(map[primitive.ObjectID]*applicationmod.Application)
|
||||
for _, info := range appList {
|
||||
applicationId = append(applicationId, info.Id)
|
||||
appid[info.Id] = info
|
||||
}
|
||||
|
||||
filter := bson.M{}
|
||||
filter["applicationDbId"] = bson.M{"$in": applicationId}
|
||||
|
||||
if param.Start != nil && param.End != nil {
|
||||
filter[applicationstat.FN.ApplicationStat.SumData] = bson.M{"$gte": *param.Start, "$lte": *param.End}
|
||||
}
|
||||
|
||||
pipeline := []bson.M{
|
||||
{
|
||||
"$match": filter,
|
||||
},
|
||||
{
|
||||
"$group": bson.M{
|
||||
"_id": "$applicationDbId",
|
||||
"total": bson.M{"$sum": "$total"},
|
||||
},
|
||||
},
|
||||
{"$sort": bson.D{{Key: "_id", Value: -1}}},
|
||||
}
|
||||
|
||||
list := make([]StatTotalInfo, 0)
|
||||
serr := applicationstat.AggregateApplicationStat(ctx, &list, pipeline)
|
||||
|
||||
pipeline = []bson.M{
|
||||
{
|
||||
"$match": filter,
|
||||
},
|
||||
{
|
||||
"$group": bson.M{
|
||||
"_id": "$applicationDbId",
|
||||
"total": bson.M{"$sum": "$total"},
|
||||
},
|
||||
},
|
||||
{"$sort": bson.D{{Key: "_id", Value: -1}}},
|
||||
{"$skip": (param.PageNum - 1) * param.PageSize},
|
||||
{"$limit": param.PageSize},
|
||||
}
|
||||
info := make([]StatTotalInfo, 0)
|
||||
serr = applicationstat.AggregateApplicationStat(ctx, &info, pipeline)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
for _, app := range list {
|
||||
resp.Summary.TotalClick += app.Total
|
||||
}
|
||||
}
|
||||
|
||||
if len(info) > 0 {
|
||||
for _, app := range info {
|
||||
tmp := &AppStatTotalInfo{
|
||||
Id: app.Id,
|
||||
Total: app.Total,
|
||||
}
|
||||
if value, ok := appid[app.Id]; ok {
|
||||
tmp.ParentAppId = value.ParentAppId
|
||||
tmp.AppName = value.Name
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
}
|
||||
resp.Total = int64(len(list))
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package srv_applicationstat
|
||||
|
||||
import (
|
||||
"stat/common"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type AppStatSummary struct {
|
||||
TotalClick int64 `json:"totalClick"` // 总点击
|
||||
}
|
||||
|
||||
type AppStatInfo struct {
|
||||
Id primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
ApplicationDbId primitive.ObjectID `json:"applicationDbId" bson:"applicationDbId"` //应用ID
|
||||
ParentAppId int64 `json:"parentAppId" bson:"parentAppId"` //所属应用ID
|
||||
AppName string `json:"appName" bson:"appName"` //应用名称
|
||||
SumDate time.Time `json:"sumDate" bson:"sumDate"` //记录时间
|
||||
Total int64 `json:"total" bson:"total"` //当日点击
|
||||
HourData []int64 `json:"hourData" bson:"hourData"` //小时数据
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //创建时间
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` //更新时间
|
||||
}
|
||||
|
||||
// GetApplicationStatReq 获取app点击统计请求
|
||||
type GetApplicationStatReq struct {
|
||||
common.Page
|
||||
Start *time.Time `json:"start"` // 开始时间
|
||||
End *time.Time `json:"end"` // 结束时间
|
||||
ParentAppId *[]int64 `json:"parentAppId"` // 所属应用ID
|
||||
ApplicationId *primitive.ObjectID `json:"applicationId"` // 应用ID
|
||||
ApplicationName *string `json:"applicationName"` // 应用名称
|
||||
}
|
||||
|
||||
type GetApplicationStatResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*AppStatInfo `json:"list"`
|
||||
Summary AppStatSummary `json:"summary"`
|
||||
}
|
||||
|
||||
// GetApplicationStatReq 获取app点击统计请求
|
||||
type GetApplicationStatTotalReq struct {
|
||||
common.Page
|
||||
Start *time.Time `json:"start"` // 开始时间
|
||||
End *time.Time `json:"end"` // 结束时间
|
||||
ParentAppId *[]int64 `json:"parentAppId"` // 所属应用ID
|
||||
ApplicationId *primitive.ObjectID `json:"applicationId"` // 应用ID
|
||||
ApplicationName *string `json:"applicationName"` // 应用名称
|
||||
}
|
||||
|
||||
type AppStatTotalInfo struct {
|
||||
Id primitive.ObjectID `json:"id" bson:"id"` //应用ID
|
||||
ParentAppId int64 `json:"parentAppId" bson:"parentAppId"` //所属应用ID
|
||||
AppName string `json:"appName" bson:"appName"` //应用名称
|
||||
Total int64 `json:"total" bson:"total"` //总次数
|
||||
}
|
||||
type StatTotalInfo struct {
|
||||
Id primitive.ObjectID `json:"id" bson:"_id,omitempty"` //应用ID
|
||||
Total int64 `json:"total"` //总次数
|
||||
}
|
||||
|
||||
type GetApplicationStatTotalResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List []*AppStatTotalInfo `json:"list"`
|
||||
Summary AppStatSummary `json:"summary"`
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
package statSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/channelCodeBlocStatMod"
|
||||
"stat/models/channelmod"
|
||||
"time"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// // 获取渠道码统计信息列表
|
||||
// func ListChannelCodeStat(ctx context.Context, param proto.ListChannelCodeStatReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// resp := proto.ListChannelCodeStatRes{
|
||||
// Total: 0,
|
||||
// List: make([]proto.ChannelCodeStatInfo, 0),
|
||||
// }
|
||||
|
||||
// // 获取需要过滤的渠道信息
|
||||
// channels1 := make([]string, 0)
|
||||
// if param.Channel != nil {
|
||||
// // 获取渠道信息
|
||||
// channelInfo, err := channelmod.GetChannelInfoByChannel(ctx, *param.Channel)
|
||||
// if err != nil {
|
||||
// code = stdLog.ChannelNotExists
|
||||
// return code, data, err
|
||||
// }
|
||||
|
||||
// if channelInfo.Channel == "" {
|
||||
// code = stdLog.Success
|
||||
// return code, resp, nil
|
||||
// }
|
||||
|
||||
// // 获取子渠道
|
||||
// if channelInfo.ParentChannel == "" {
|
||||
// chList, err := channelmod.GetAllChildChannel(ctx, channelInfo.Channel)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return code, data, err
|
||||
// }
|
||||
|
||||
// for _, v := range chList {
|
||||
// channels1 = append(channels1, v.Channel)
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// channels1 = append(channels1, channelInfo.Channel)
|
||||
// }
|
||||
|
||||
// channels2 := make([]string, 0)
|
||||
// // 根据渠道备注,获取渠道信息
|
||||
// if param.ChannelRemark != nil {
|
||||
// // 获取渠道
|
||||
// chList, err := channelmod.GetChannelByRemark(ctx, *param.ChannelRemark)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return code, data, err
|
||||
// }
|
||||
|
||||
// if len(chList) == 0 {
|
||||
// code = stdLog.Success
|
||||
// return code, resp, nil
|
||||
// }
|
||||
|
||||
// for _, v1 := range chList {
|
||||
// // 获取子渠道
|
||||
// if v1.ParentChannel == "" && len(v1.Channel) > 0 {
|
||||
// chList, err := channelmod.GetAllChildChannel(ctx, v1.Channel)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return code, data, err
|
||||
// }
|
||||
|
||||
// for _, v2 := range chList {
|
||||
// channels2 = append(channels2, v2.Channel)
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// channels2 = append(channels2, v1.Channel)
|
||||
// }
|
||||
// }
|
||||
|
||||
// channels := make([]string, 0)
|
||||
// // 合并
|
||||
// if param.Channel != nil && param.ChannelRemark != nil {
|
||||
// for _, ch1 := range channels1 {
|
||||
// for _, ch2 := range channels2 {
|
||||
// if ch1 == ch2 {
|
||||
// channels = append(channels, ch1)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else if param.Channel != nil && param.ChannelRemark == nil {
|
||||
// channels = channels1
|
||||
// } else if param.Channel == nil && param.ChannelRemark != nil {
|
||||
// channels = channels2
|
||||
// }
|
||||
|
||||
// codes1 := make([]string, 0)
|
||||
// // 获取渠道的渠道码
|
||||
// if len(channels) > 0 {
|
||||
// codeList, err := channelCodeMod.GetChannelCodesByChannels(ctx, channels)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return code, data, err
|
||||
// }
|
||||
// for _, v := range codeList {
|
||||
// codes1 = append(codes1, v.ChannelCode)
|
||||
// }
|
||||
// }
|
||||
|
||||
// codes := make([]string, 0)
|
||||
// // 只搜索渠道码
|
||||
// if param.ChannelCode != nil && param.Channel == nil && param.ChannelRemark == nil {
|
||||
// codes = append(codes, *param.ChannelCode)
|
||||
// } else if param.ChannelCode != nil && (param.Channel != nil || param.ChannelRemark != nil) {
|
||||
// codes2 := make([]string, 0)
|
||||
// for _, v := range codes1 {
|
||||
// if v == *param.ChannelCode {
|
||||
// codes2 = []string{*param.ChannelCode}
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// if len(codes2) == 0 {
|
||||
// code = stdLog.Success
|
||||
// return code, resp, nil
|
||||
// }
|
||||
// codes = codes2
|
||||
// } else {
|
||||
// codes = codes1
|
||||
// }
|
||||
|
||||
// skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
// total, statList, err := channelCodeStatMod.GetChannelCodeStatList(ctx, codes, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
|
||||
// appIds := make([]int32, 0)
|
||||
// for _, v := range statList {
|
||||
// appIds = append(appIds, v.AppId)
|
||||
// }
|
||||
|
||||
// appInfoMap, err := channelSer.MakeAppInfosMapByIds(ctx, appIds)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
|
||||
// codeList, err := channelCodeMod.GetChannelCodesByIds(ctx, appIds)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
|
||||
// appInfoCodeMap := make(map[string]*channelCodeMod.ChannelCode)
|
||||
// for _, v := range codeList {
|
||||
// _v := v
|
||||
// appInfoCodeMap[v.ChannelCode+fmt.Sprintf("%d", v.AppId)] = &_v
|
||||
// }
|
||||
|
||||
// for _, v := range statList {
|
||||
// appName := ""
|
||||
// if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
// appName = appInfo.Name
|
||||
// }
|
||||
|
||||
// channel := ""
|
||||
// settlement := channelmod.SettlementCPA
|
||||
// rate := float64(0.8)
|
||||
// if channelCodeInfo, ok := appInfoCodeMap[v.ChannelCode+fmt.Sprintf("%d", v.AppId)]; ok {
|
||||
// channel = channelCodeInfo.Channel
|
||||
// channelInfo, _ := channelmod.GetChannelInfoByChannel(ctx, channel)
|
||||
// if channelInfo.Channel == channel {
|
||||
// settlement = channelCodeInfo.Settlement
|
||||
// if settlement == channelmod.SettlementCPS {
|
||||
// rate = float64(channelInfo.CPSRate) / 100
|
||||
// } else {
|
||||
// rate = float64(channelInfo.CPAPrice)
|
||||
// }
|
||||
// }
|
||||
// } else { // 官方活没收益
|
||||
// rate = float64(200)
|
||||
// }
|
||||
|
||||
// tmp := proto.ChannelCodeStatInfo{
|
||||
// SumDate: v.SumDate,
|
||||
// ChannelCode: v.ChannelCode,
|
||||
// AppId: v.AppId,
|
||||
// AppName: appName,
|
||||
// Channel: channel,
|
||||
// Settlement: settlement,
|
||||
// NewDirectCount: v.NewDirectCount,
|
||||
// NewFissionCount: v.NewFissionCount,
|
||||
// NewDeductionCount: v.NewDeductionCount,
|
||||
// DirectVisitCount: v.DirectVisitCount,
|
||||
// FissionVisitCount: v.FissionVisitCount,
|
||||
// Day1RetainRate: v.Day1RetainRate,
|
||||
// Day3RetainRate: v.Day3RetainRate,
|
||||
// Day7RetainRate: v.Day7RetainRate,
|
||||
// Recharge: v.DirectAmount,
|
||||
// FissionAmount: v.FissionAmount,
|
||||
// DeductionAmount: v.DeductionAmount,
|
||||
// NewRechargeCount: v.NewRechargeCount,
|
||||
// NewRechargeAmount: v.NewRechargeAmount,
|
||||
// LandPageVisitCount: v.LandPageVisitCount,
|
||||
// TotalAmount: v.DirectAmount + v.FissionAmount + v.DeductionAmount,
|
||||
// TotalNewCount: v.NewDirectCount + v.NewFissionCount + v.NewDeductionCount,
|
||||
// IncomeRate: float64(0.0),
|
||||
// UpdatedAt: v.UpdatedAt,
|
||||
// }
|
||||
|
||||
// if settlement == channelmod.SettlementCPS {
|
||||
// tmp.CPSRate = rate
|
||||
// channelIncome := float64(v.DirectAmount) * rate
|
||||
// if channelIncome > 0 {
|
||||
// tmp.IncomeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(tmp.Recharge+tmp.DeductionAmount+tmp.FissionAmount))/channelIncome), 64)
|
||||
|
||||
// }
|
||||
// } else {
|
||||
// tmp.CPAPrice = rate / 100
|
||||
// channelIncome := float64(v.NewDirectCount) * rate
|
||||
// if channelIncome > 0 {
|
||||
// tmp.IncomeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(tmp.Recharge+tmp.DeductionAmount+tmp.FissionAmount))/channelIncome), 64)
|
||||
// }
|
||||
// }
|
||||
// resp.List = append(resp.List, tmp)
|
||||
// }
|
||||
|
||||
// resp.Total = total
|
||||
|
||||
// // 总量
|
||||
|
||||
// TotalList, err := channelCodeStatMod.GetChannelCodeStatListTotal(ctx, codes, param.AppId, param.StartTime, param.EndTime)
|
||||
// if err != nil {
|
||||
// log.Error("GetChannelCodeStatListTotal", log.E(err))
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
// toatlSum := proto.ChannelCodeStatInfo{
|
||||
// IncomeRate: float64(0.0),
|
||||
// }
|
||||
// for _, valueInfo := range TotalList {
|
||||
// toatlSum.NewDirectCount += valueInfo.NewDirectCount
|
||||
// toatlSum.NewFissionCount += valueInfo.NewFissionCount
|
||||
// toatlSum.NewDeductionCount += valueInfo.NewDeductionCount
|
||||
// toatlSum.DirectVisitCount += valueInfo.DirectVisitCount
|
||||
// toatlSum.FissionVisitCount += valueInfo.FissionVisitCount
|
||||
// toatlSum.Day1RetainRate += valueInfo.Day1RetainRate
|
||||
// toatlSum.Day3RetainRate += valueInfo.Day3RetainRate
|
||||
// toatlSum.Day7RetainRate += valueInfo.Day7RetainRate
|
||||
// toatlSum.Recharge += valueInfo.DirectAmount
|
||||
// toatlSum.FissionAmount += valueInfo.FissionAmount
|
||||
// toatlSum.DeductionAmount += valueInfo.DeductionAmount
|
||||
// toatlSum.NewRechargeCount += valueInfo.NewRechargeCount
|
||||
// toatlSum.NewRechargeAmount += valueInfo.NewRechargeAmount
|
||||
// toatlSum.LandPageVisitCount += valueInfo.LandPageVisitCount
|
||||
// toatlSum.TotalAmount += (valueInfo.DirectAmount + valueInfo.FissionAmount + valueInfo.DeductionAmount)
|
||||
// toatlSum.TotalNewCount += (valueInfo.NewDirectCount + valueInfo.NewFissionCount + valueInfo.NewDeductionCount)
|
||||
// }
|
||||
|
||||
// resp.TotalInfo = toatlSum
|
||||
// data = resp
|
||||
// code = stdLog.Success
|
||||
// return
|
||||
// }
|
||||
|
||||
// // 代理商获取渠道码统计信息列表
|
||||
// func ListChannelCodeStatAgent(ctx context.Context, account string, param proto.ListChannelCodeStatAgentReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
// resp := proto.ListChannelCodeStatAgentRes{
|
||||
// Total: 0,
|
||||
// List: make([]proto.ChannelCodeStatAgentInfo, 0),
|
||||
// }
|
||||
|
||||
// // 获取渠道信息
|
||||
// channelInfo, err := channelmod.GetChannelInfoByAccount(ctx, account)
|
||||
// if err != nil || channelInfo.Channel == "" {
|
||||
// code = stdLog.AgentAccountNotExists
|
||||
// return
|
||||
// }
|
||||
|
||||
// // 获取子渠道
|
||||
// childChannels, err := channelmod.GetAllChildChannel(ctx, channelInfo.Channel)
|
||||
// if err != nil || channelInfo.Channel == "" {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
// channels := make([]string, 0)
|
||||
// for _, v := range childChannels {
|
||||
// channels = append(channels, v.Channel)
|
||||
// }
|
||||
// channels = append(channels, channelInfo.Channel)
|
||||
|
||||
// // 获取渠道的渠道码
|
||||
// codeList, err := channelCodeMod.GetChannelCodesByChannels(ctx, channels)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
// codes := make([]string, 0)
|
||||
// channelCodeMap := make(map[string]string)
|
||||
|
||||
// if param.ChannelCode != nil {
|
||||
// codes = append(codes, *param.ChannelCode)
|
||||
// for _, v := range codeList {
|
||||
// channelCodeMap[v.ChannelCode+fmt.Sprintf("%d", v.AppId)] = v.Channel
|
||||
// }
|
||||
// } else {
|
||||
// for _, v := range codeList {
|
||||
// codes = append(codes, v.ChannelCode)
|
||||
// channelCodeMap[v.ChannelCode+fmt.Sprintf("%d", v.AppId)] = v.Channel
|
||||
// }
|
||||
// }
|
||||
|
||||
// skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
// total, statList, err := channelCodeStatMod.GetChannelCodeStatAgentList(ctx, codes, *param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
|
||||
// for _, v := range statList {
|
||||
// channelName, ok := channelCodeMap[v.ChannelCode+fmt.Sprintf("%d", v.AppId)]
|
||||
// if !ok { // 只能查询渠道下面的渠道码
|
||||
// continue
|
||||
// }
|
||||
|
||||
// tmp := proto.ChannelCodeStatAgentInfo{
|
||||
// SumDate: v.SumDate,
|
||||
// ChannelCode: v.ChannelCode,
|
||||
// Channel: channelName,
|
||||
// NewDirectCount: v.NewDirectCount,
|
||||
// Recharge: v.DirectAmount,
|
||||
// }
|
||||
|
||||
// // CPA子渠道不显示充值金额
|
||||
// if channelInfo.ParentChannel != "" && channelInfo.Settlement == channelmod.SettlementCPA {
|
||||
// tmp.Recharge = 0
|
||||
// }
|
||||
|
||||
// resp.List = append(resp.List, tmp)
|
||||
// }
|
||||
|
||||
// totalStatList, err := channelCodeStatMod.GetChannelCodeStatAgentListTotal(ctx, codes, *param.AppId, param.StartTime, param.EndTime)
|
||||
// if err != nil {
|
||||
// code = stdLog.Failure
|
||||
// return
|
||||
// }
|
||||
// totalCount := int64(0)
|
||||
// totalAmount := int64(0)
|
||||
|
||||
// for _, v := range totalStatList {
|
||||
// _, ok := channelCodeMap[v.ChannelCode+fmt.Sprintf("%d", v.AppId)]
|
||||
// if !ok { // 只能查询渠道下面的渠道码
|
||||
// continue
|
||||
// }
|
||||
// // CPA子渠道不显示充值金额
|
||||
// if channelInfo.ParentChannel != "" && channelInfo.Settlement == channelmod.SettlementCPA {
|
||||
// totalAmount = 0
|
||||
// } else {
|
||||
// totalAmount += v.DirectAmount
|
||||
// }
|
||||
|
||||
// totalCount += v.NewDirectCount
|
||||
// }
|
||||
|
||||
// resp.Total = total
|
||||
// resp.TotalAmount = totalAmount
|
||||
// resp.TotalCount = totalCount
|
||||
|
||||
// data = resp
|
||||
// code = stdLog.Success
|
||||
// return
|
||||
// }
|
||||
|
||||
func CacheKey(channel *string, appId *int32, start, end *time.Time) string {
|
||||
key := ""
|
||||
if channel != nil {
|
||||
key += fmt.Sprintf("channel:%s;", *channel)
|
||||
} else {
|
||||
key += "channel:<nil>;"
|
||||
}
|
||||
if appId != nil {
|
||||
key += fmt.Sprintf("appId:%d;", *appId)
|
||||
} else {
|
||||
key += "appId:<nil>;"
|
||||
}
|
||||
if start != nil {
|
||||
key += fmt.Sprintf("start:%v;", *start)
|
||||
} else {
|
||||
key += "start:<nil>;"
|
||||
}
|
||||
if end != nil {
|
||||
key += fmt.Sprintf("end:%v;", *end)
|
||||
} else {
|
||||
key += "end:<nil>;"
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// 获取渠道码统计信息列表(集团)
|
||||
func ListBlocChannelCodeStat(ctx context.Context, param *proto.BlocListChannelCodeStatReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.BlocListChannelCodeStatRes{
|
||||
Total: 0,
|
||||
List: make([]proto.BlocChannelCodeStatInfo, 0),
|
||||
}
|
||||
if param.Channel != nil && *param.Channel == channelmod.OfficialChannelDisplayName {
|
||||
tmp := channelmod.OfficialChannelName
|
||||
param.Channel = &tmp
|
||||
}
|
||||
|
||||
// 获取渠道的渠道码
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, statList, err := channelCodeBlocStatMod.GetBlocChannelCodeBlocStatList(ctx, param.Channel, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range statList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
childstatList := make([]*channelCodeBlocStatMod.ChannelCodeBlocStat, 0)
|
||||
|
||||
if param.WithChild != nil && param.Channel != nil && len(*param.Channel) > 0 {
|
||||
childChannelList, _ := channelmod.FindManyChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.ParentChannel: *param.Channel})
|
||||
if len(childChannelList) > 0 {
|
||||
channelArray := make([]string, 0)
|
||||
for _, v := range childChannelList {
|
||||
channelArray = append(channelArray, v.Channel)
|
||||
}
|
||||
_, childstatList, _ = channelCodeBlocStatMod.GetBlocChannelCodeStatTotal(ctx, channelArray, param.AppId, param.StartTime, param.EndTime)
|
||||
}
|
||||
|
||||
// 汇总排序 (存在一级没有 二级存在的情况)
|
||||
for _, childinfo := range childstatList {
|
||||
find := false
|
||||
for _, v := range statList {
|
||||
if v.SumDate.Equal(childinfo.SumDate) {
|
||||
find = true
|
||||
}
|
||||
}
|
||||
if !find {
|
||||
childinfo.ChannelCode = *param.Channel
|
||||
statList = append(statList, childinfo)
|
||||
}
|
||||
}
|
||||
sort.Sort(statList)
|
||||
}
|
||||
// get channels
|
||||
channels := make([]string, 0, len(statList))
|
||||
for _, item := range statList {
|
||||
channels = append(channels, item.ChannelCode)
|
||||
}
|
||||
ruleMap, err := MakeRuleMap(ctx, channels)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
//组装 按照时间排序
|
||||
for _, v := range statList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
for _, childinfo := range childstatList {
|
||||
if v.SumDate.Equal(childinfo.SumDate) {
|
||||
v.NewDirectCount += childinfo.NewDirectCount
|
||||
v.NewFissionCount += childinfo.NewFissionCount
|
||||
v.NewDeductionCount += childinfo.NewDeductionCount
|
||||
v.DirectVisitCount += childinfo.DirectVisitCount
|
||||
v.FissionVisitCount += childinfo.FissionVisitCount
|
||||
v.DeductionAmount += childinfo.DeductionAmount
|
||||
v.DirectAmount += childinfo.DirectAmount
|
||||
v.FissionAmount += childinfo.FissionAmount
|
||||
}
|
||||
}
|
||||
|
||||
tmp := proto.BlocChannelCodeStatInfo{
|
||||
SumDate: v.SumDate,
|
||||
ChannelCode: channelmod.ChannelDisplayName(v.ChannelCode),
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
Channel: v.Channel,
|
||||
NewDirectCount: v.NewDirectCount,
|
||||
NewFissionCount: v.NewFissionCount,
|
||||
NewDeductionCount: v.NewDeductionCount,
|
||||
DirectVisitCount: v.DirectVisitCount,
|
||||
FissionVisitCount: v.FissionVisitCount,
|
||||
DeductionAmount: v.DeductionAmount,
|
||||
RechargeAmount: v.DirectAmount,
|
||||
FissionAmount: v.FissionAmount,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
ruleinfo := ruleMap.GetRule(v.ChannelCode, v.AppId)
|
||||
if ruleinfo != nil {
|
||||
if ruleinfo.Settlement == channelmod.SettlementCPA {
|
||||
tmp.Settlement = fmt.Sprintf("CPA_%.2f", float64(ruleinfo.CPAPrice)/100)
|
||||
tmp.TodayIncome = tmp.NewDirectCount * ruleinfo.CPAPrice
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPS {
|
||||
tmp.Settlement = fmt.Sprintf("CPS_%.2f", float64(ruleinfo.CPSRate)/100)
|
||||
tmp.TodayIncome = tmp.RechargeAmount * ruleinfo.CPSRate / 100
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPT {
|
||||
tmp.Settlement = "CPT"
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPC {
|
||||
tmp.Settlement = "CPC"
|
||||
}
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
// 查询条件不为空时统计总量
|
||||
if param.Channel != nil || param.AppId != nil || (param.StartTime != nil && param.EndTime != nil) {
|
||||
// 总量
|
||||
TotalList, err := channelCodeBlocStatMod.GetBlocChannelCodeBlocStatListTotal(ctx, param.Channel, param.AppId, param.StartTime, param.EndTime)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, nil, err
|
||||
}
|
||||
// get channels
|
||||
channels = make([]string, 0, len(TotalList))
|
||||
for _, item := range TotalList {
|
||||
channels = append(channels, item.ChannelCode)
|
||||
}
|
||||
ruleMap, err = MakeRuleMap(ctx, channels)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, nil, err
|
||||
}
|
||||
toatlSum := &proto.BlocChannelCodeStatInfo{}
|
||||
for _, valueInfo := range TotalList {
|
||||
toatlSum.NewDirectCount += valueInfo.NewDirectCount
|
||||
toatlSum.NewFissionCount += valueInfo.NewFissionCount
|
||||
toatlSum.DirectVisitCount += valueInfo.DirectVisitCount
|
||||
toatlSum.FissionVisitCount += valueInfo.FissionVisitCount
|
||||
toatlSum.RechargeAmount += valueInfo.DirectAmount
|
||||
toatlSum.DeductionAmount += valueInfo.DeductionAmount
|
||||
toatlSum.NewDeductionCount += valueInfo.NewDeductionCount
|
||||
ruleinfo := ruleMap.GetRule(valueInfo.ChannelCode, valueInfo.AppId)
|
||||
if ruleinfo != nil {
|
||||
if ruleinfo.Settlement == channelmod.SettlementCPA {
|
||||
toatlSum.TodayIncome += valueInfo.NewDirectCount * ruleinfo.CPAPrice
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPS {
|
||||
toatlSum.TodayIncome += valueInfo.DirectAmount * ruleinfo.CPSRate / 100
|
||||
}
|
||||
}
|
||||
}
|
||||
resp.TotalInfo = *toatlSum
|
||||
}
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
func ListBlocChannelExport(ctx context.Context, param *proto.BlocListChannelCodeStatReq) (string, error) {
|
||||
if param.Channel != nil && *param.Channel == channelmod.OfficialChannelDisplayName {
|
||||
tmp := channelmod.OfficialChannelName
|
||||
param.Channel = &tmp
|
||||
}
|
||||
// 获取渠道的渠道码
|
||||
statList, err := channelCodeBlocStatMod.GetBlocChannelCodeBlocStatListTotal(ctx, param.Channel, param.AppId, param.StartTime, param.EndTime)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range statList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// get channels
|
||||
channels := make([]string, 0, len(statList))
|
||||
for _, item := range statList {
|
||||
channels = append(channels, item.ChannelCode)
|
||||
}
|
||||
ruleMap, err := MakeRuleMap(ctx, channels)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result := make([]*proto.BlocChannelCodeStatInfo, 0)
|
||||
for _, v := range statList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := &proto.BlocChannelCodeStatInfo{
|
||||
SumDate: v.SumDate,
|
||||
ChannelCode: channelmod.ChannelDisplayName(v.ChannelCode),
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
Channel: v.Channel,
|
||||
NewDirectCount: v.NewDirectCount,
|
||||
NewFissionCount: v.NewFissionCount,
|
||||
DirectVisitCount: v.DirectVisitCount,
|
||||
NewDeductionCount: v.NewDeductionCount,
|
||||
FissionVisitCount: v.FissionVisitCount,
|
||||
DeductionAmount: v.DeductionAmount,
|
||||
RechargeAmount: v.DirectAmount,
|
||||
FissionAmount: v.FissionAmount,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
ruleinfo := ruleMap.GetRule(v.ChannelCode, v.AppId)
|
||||
if ruleinfo != nil {
|
||||
if ruleinfo.Settlement == channelmod.SettlementCPA {
|
||||
tmp.Settlement = fmt.Sprintf("CPA_%.2f", float64(ruleinfo.CPAPrice)/100)
|
||||
tmp.TodayIncome = tmp.NewDirectCount * ruleinfo.CPAPrice
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPS {
|
||||
tmp.Settlement = fmt.Sprintf("CPS_%.2f", float64(ruleinfo.CPSRate)/100)
|
||||
tmp.TodayIncome = tmp.RechargeAmount * ruleinfo.CPSRate / 100
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPT {
|
||||
tmp.Settlement = "CPT"
|
||||
} else if ruleinfo.Settlement == channelmod.SettlementCPC {
|
||||
tmp.Settlement = "CPC"
|
||||
}
|
||||
}
|
||||
result = append(result, tmp)
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("./tmp/渠道列表-%s.xlsx", time.Now().Format("2006-01-02 15:04:05"))
|
||||
|
||||
if err := export(ctx, filePath, result); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func export(ctx context.Context, rPath string, channelStatList []*proto.BlocChannelCodeStatInfo) error {
|
||||
f := excelize.NewFile()
|
||||
sheetName := "Sheet1"
|
||||
idx := f.NewSheet(sheetName)
|
||||
|
||||
var exportTransWaterTitle = map[string]string{}
|
||||
|
||||
exportTransWaterTitle = map[string]string{
|
||||
"A1": "日期",
|
||||
"B1": "渠道",
|
||||
"C1": "结算方式",
|
||||
"D1": "当日支出",
|
||||
"E1": "新增(直推)",
|
||||
"F1": "新增(裂变)",
|
||||
"G1": "新增(扣量)",
|
||||
"H1": "访问量",
|
||||
"I1": "直推充值金额",
|
||||
"J1": "裂变充值金额",
|
||||
}
|
||||
|
||||
for k, v := range exportTransWaterTitle {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
}
|
||||
|
||||
for k, v := range channelStatList {
|
||||
i := 'A' // i 不能超过Z,即列输不能超过26列,超出将出现bug
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.SumDate)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.ChannelCode)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Settlement)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TodayIncome)/100)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewDirectCount)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewFissionCount)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewDeductionCount)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.DirectVisitCount+v.FissionVisitCount)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.DeductionAmount)/100)
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.FissionAmount)/100)
|
||||
}
|
||||
f.SetActiveSheet(idx)
|
||||
p, _ := os.Getwd()
|
||||
fileName := path.Join(p, rPath)
|
||||
|
||||
if err := f.SaveAs(fileName); err != nil {
|
||||
log.Error("srv_vipcode:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,957 @@
|
||||
package statSer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/blocDailystatmod"
|
||||
"stat/models/botdailyhourstat"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/dailyhourstatmod"
|
||||
"stat/models/dailystatmod"
|
||||
"stat/models/minutestatmod"
|
||||
"stat/models/rechargemod"
|
||||
"stat/models/registermod"
|
||||
"stat/models/withdrawUserOrdermod"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func ListDailyStat(ctx context.Context, param proto.ListDailyStatReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := &proto.ListDailyStatRes{
|
||||
Total: 0,
|
||||
TotalInfo: &proto.DailyStatInfo{},
|
||||
List: make([]*proto.DailyStatInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, statList, err := dailystatmod.GetDailyStatList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range statList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range statList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
tmp := DailyStatToInfo(v)
|
||||
tmp.AppName = appName
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
if param.AppId != nil || param.StartTime != nil || param.EndTime != nil {
|
||||
var count int
|
||||
err := dailystatmod.GetDailyStatTotalListShard(ctx, param.AppId, param.StartTime, param.EndTime, utils.GlobalShardMonth, func(item *dailystatmod.DailyStatMod) {
|
||||
count++
|
||||
tmp := DailyStatToInfo(item)
|
||||
DailyStatInfoAccumulate(resp.TotalInfo, tmp)
|
||||
})
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, nil, err
|
||||
}
|
||||
// 计算留存率平均值
|
||||
if count > 0 {
|
||||
resp.TotalInfo.Day1RetainRate = resp.TotalInfo.Day1RetainRate / float64(count)
|
||||
resp.TotalInfo.Day3RetainRate = resp.TotalInfo.Day3RetainRate / float64(count)
|
||||
resp.TotalInfo.Day7RetainRate = resp.TotalInfo.Day7RetainRate / float64(count)
|
||||
}
|
||||
}
|
||||
resp.Total = total
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func ListDailyStatExport(ctx context.Context, param proto.ListDailyStatReq) (string, error) {
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
_, statList, err := dailystatmod.GetDailyStatList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("./tmp/日常统计信息列表-%s.xlsx", time.Now().Format("2006-01-02 15:04:05"))
|
||||
if sErr := dailyStatexport(filePath, statList); sErr != nil {
|
||||
return "", sErr
|
||||
}
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func dailyStatexport(rPath string, transInfos []*dailystatmod.DailyStatMod) error {
|
||||
f := excelize.NewFile()
|
||||
sheetName := "Sheet1"
|
||||
// 设置列宽
|
||||
f.SetColWidth("Sheet1", "A", "O", 20)
|
||||
// 设置表头行高
|
||||
f.SetRowHeight("Sheet1", 1, 30)
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(`{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":true,
|
||||
"size":16,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center"
|
||||
}
|
||||
}`)
|
||||
f.SetCellStyle("Sheet1", "A1", "O1", headStyleID)
|
||||
|
||||
idx := f.NewSheet(sheetName)
|
||||
var exportTransWaterTitle = map[string]string{
|
||||
"A1": "日期",
|
||||
"B1": "总日活",
|
||||
"C1": "android日活",
|
||||
"D1": "ios日活",
|
||||
"E1": "总新增",
|
||||
"F1": "android新增",
|
||||
"G1": "ios新增",
|
||||
"H1": "总充值",
|
||||
"I1": "android充值",
|
||||
"J1": "ios充值",
|
||||
"K1": "总新增H5",
|
||||
"L1": "渠道新增",
|
||||
"M1": "渠道总充值",
|
||||
"N1": "老用户总充值",
|
||||
"O1": "新用户总充值",
|
||||
"P1": "复充总金额",
|
||||
"Q1": "重充VIP总金额",
|
||||
"R1": "拉起订单数",
|
||||
"S1": "成功订单数",
|
||||
}
|
||||
for k, v := range exportTransWaterTitle {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
}
|
||||
for k, v := range transInfos {
|
||||
i := 'A' // i 不能超过Z,即列输不能超过26列,超出将出现bug
|
||||
date := v.SumDate.Local().Format("2006-01-02")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), date) //日期
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.TotalVisitCount) //总日活
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.TotalVisitCount-(v.LightVisitCount+v.TodayH5VisitCount)) //android日活
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.LightVisitCount+v.TodayH5VisitCount) //ios日活
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewTotalCount) //总新增
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewTotalCount-(v.LightAddCount+v.H5AddCount)) //android总新增
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.LightAddCount+v.H5AddCount) //Ios总新增
|
||||
i++
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TotalAmount)/100) //总充值
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TodayAndroidRecharge)/100) //android充值
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TotalAmount)/100-float64(v.TodayAndroidRecharge)/100) //ios充值
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.H5AddCount) //总新增H5
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewDistrictCount) //渠道新增
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TodayDistrictRecharge)/100) //渠道总充值
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TotalAmount-v.NewRechargeAmount)/100) //老用户总充值
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.NewRechargeAmount)/100) //新用户总充值
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.RepeatAmount)/100) //复充总金额
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.RepeatVipMoney)/100) //重充VIP总金额
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.TotalRechargeOrder) //拉起订单数
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.SuccessRechargeOrder) //成功订单数
|
||||
}
|
||||
|
||||
f.SetActiveSheet(idx)
|
||||
p, _ := os.Getwd()
|
||||
fileName := path.Join(p, rPath)
|
||||
if err := f.SaveAs(fileName); err != nil {
|
||||
log.Error("SettlementInfo:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func BlocListDailyStat(ctx context.Context, param proto.ListBlocDailyStatReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocDailyStatRes{
|
||||
Total: 0,
|
||||
List: make([]proto.BlocDailyStatInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
total, statList, err := blocDailystatmod.GetBlocDailyStatList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range statList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, nil, err
|
||||
}
|
||||
|
||||
for _, v := range statList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.BlocDailyStatInfo{
|
||||
SumDate: v.SumDate,
|
||||
AppId: v.AppId,
|
||||
AppName: appName,
|
||||
NewDirectCount: v.NewDirectCount,
|
||||
NewFissionCount: v.NewFissionCount,
|
||||
DirectVisitCount: v.DirectVisitCount,
|
||||
FissionVisitCount: v.FissionVisitCount,
|
||||
Day1RetainRate: v.Day1RetainRate,
|
||||
Day3RetainRate: v.Day3RetainRate,
|
||||
Day7RetainRate: v.Day7RetainRate,
|
||||
DirectAmount: v.DirectAmount,
|
||||
FissionAmount: v.FissionAmount,
|
||||
NewRechargeCount: v.NewRechargeCount,
|
||||
NewRechargeAmount: v.NewRechargeAmount,
|
||||
DaichongAmout: v.DaichongAmout,
|
||||
OnlineAmount: v.OnlineAmount,
|
||||
UserWithDrawAmount: v.UserWithDrawAmount,
|
||||
UserWithDrawPaymoneyAmount: v.UserWithDrawPaymoneyAmount,
|
||||
GameRechageAmount: v.GameRechageAmount,
|
||||
GameWithdrawAmount: v.GameWithdrawAmount,
|
||||
TotalRechargeCount: v.TotalRechargeCount,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
tmp.TotalVisitCount = v.DirectVisitCount + v.FissionVisitCount // 日活
|
||||
tmp.TotalAmount = v.DirectAmount + v.FissionAmount // 总充值
|
||||
tmp.TotalNewCount = v.NewDirectCount + v.NewFissionCount // 新增
|
||||
tmp.OldRechargeAmount = tmp.TotalAmount - tmp.NewRechargeAmount // 老用户充值
|
||||
total := v.DeductionAmountCount + v.DirectAmountCount + v.FissionAmountCount // 充值人数
|
||||
|
||||
if tmp.TotalAmount-tmp.DaichongAmout > 0 {
|
||||
tmp.OnlineAmount = tmp.TotalAmount - tmp.DaichongAmout
|
||||
}
|
||||
|
||||
if tmp.TotalNewCount != 0 {
|
||||
tmp.NewRechargeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(v.NewRechargeCount)/float64(tmp.TotalNewCount))*100), 64)
|
||||
}
|
||||
|
||||
if tmp.TotalVisitCount != 0 {
|
||||
tmp.VisitRechargeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(total)/float64(tmp.TotalVisitCount))*100), 64)
|
||||
}
|
||||
|
||||
if total != 0 {
|
||||
tmp.ARPPU = (tmp.DirectAmount + tmp.FissionAmount) / total // 金额
|
||||
}
|
||||
if tmp.TotalVisitCount != 0 {
|
||||
tmp.ARPU = tmp.TotalAmount / tmp.TotalVisitCount // 金额
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
if param.AppId != nil || param.StartTime != nil || param.EndTime != nil {
|
||||
// 过滤条件至少有一个,不然不统计总数
|
||||
totalList, err := blocDailystatmod.GetBlocDailyStatListTotal(ctx, param.AppId, param.StartTime, param.EndTime)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return code, nil, err
|
||||
}
|
||||
for _, v := range totalList {
|
||||
resp.TotalInfo.NewDirectCount += v.NewDirectCount
|
||||
resp.TotalInfo.NewFissionCount += v.NewFissionCount
|
||||
resp.TotalInfo.DirectVisitCount += v.DirectVisitCount
|
||||
resp.TotalInfo.FissionVisitCount += v.FissionVisitCount
|
||||
resp.TotalInfo.DirectAmount += v.DirectAmount
|
||||
resp.TotalInfo.FissionAmount += v.FissionAmount
|
||||
resp.TotalInfo.NewRechargeCount += v.NewRechargeCount
|
||||
resp.TotalInfo.NewRechargeAmount += v.NewRechargeAmount
|
||||
resp.TotalInfo.DaichongAmout += v.DaichongAmout
|
||||
resp.TotalInfo.UserWithDrawAmount += v.UserWithDrawAmount
|
||||
resp.TotalInfo.UserWithDrawPaymoneyAmount += v.UserWithDrawPaymoneyAmount
|
||||
resp.TotalInfo.GameRechageAmount += v.GameRechageAmount
|
||||
resp.TotalInfo.GameWithdrawAmount += v.GameWithdrawAmount
|
||||
resp.TotalInfo.TotalRechargeCount += v.TotalRechargeCount
|
||||
}
|
||||
resp.TotalInfo.TotalVisitCount = resp.TotalInfo.DirectVisitCount + resp.TotalInfo.FissionVisitCount // 日活
|
||||
resp.TotalInfo.TotalAmount = resp.TotalInfo.DirectAmount + resp.TotalInfo.FissionAmount // 总充值
|
||||
resp.TotalInfo.TotalNewCount = resp.TotalInfo.NewDirectCount + resp.TotalInfo.NewFissionCount // 新增
|
||||
resp.TotalInfo.OldRechargeAmount = resp.TotalInfo.TotalAmount - resp.TotalInfo.NewRechargeAmount // 老用户充值
|
||||
if resp.TotalInfo.TotalAmount-resp.TotalInfo.DaichongAmout > 0 {
|
||||
resp.TotalInfo.OnlineAmount = resp.TotalInfo.TotalAmount - resp.TotalInfo.DaichongAmout
|
||||
}
|
||||
}
|
||||
resp.Total = total
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取充值明细(集团)
|
||||
func BlocListRechargeDetailsInfo(ctx context.Context, param proto.ListBlocRechargeDetailsReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocRechargeDetailsRes{
|
||||
Total: 0,
|
||||
List: make([]proto.BlocRechargeDetailsInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, RechagerList, err := rechargemod.GetBlocRechargeDetailsList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
appMap := make(map[int32]int32)
|
||||
for _, v := range RechagerList {
|
||||
if _, ok := appMap[v.AppId]; !ok {
|
||||
appMap[v.AppId] = v.AppId
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
}
|
||||
//fmt.Println(appIds)
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range RechagerList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.BlocRechargeDetailsInfo{
|
||||
UserId: v.PromCode,
|
||||
AppName: appName,
|
||||
SysType: v.SysType,
|
||||
DevType: v.DevType,
|
||||
ChannelName: v.ChannelName,
|
||||
CID: v.CID,
|
||||
Type: v.Type,
|
||||
OrderId: v.OrderId,
|
||||
Money: v.Money,
|
||||
PayMoney: v.PayMoney,
|
||||
Status: 3,
|
||||
SuccessAt: v.SuccessAt,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func BlocListDailyTotalStat(ctx context.Context, param proto.ListBlocDailyStatTotalReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocDailyStatTotalRes{
|
||||
Total: 0,
|
||||
List: make([]proto.BlocDailyStatTotalInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
statList, err := blocDailystatmod.GetBlocDailyStatTotalList(ctx, param.StartTime, param.EndTime)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
count := int64(0)
|
||||
for _, v := range statList {
|
||||
if count >= skip && count <= skip+limit {
|
||||
tmp := proto.BlocDailyStatTotalInfo{
|
||||
SumDate: v.SumDate,
|
||||
NewDirectCount: v.NewDirectCount,
|
||||
NewFissionCount: v.NewFissionCount,
|
||||
DirectVisitCount: v.DirectVisitCount,
|
||||
FissionVisitCount: v.FissionVisitCount,
|
||||
DirectAmount: v.DirectAmount,
|
||||
FissionAmount: v.FissionAmount,
|
||||
NewRechargeCount: v.NewRechargeCount,
|
||||
NewRechargeAmount: v.NewRechargeAmount,
|
||||
DaichongAmout: v.DaichongAmout,
|
||||
OnlineAmount: v.OnlineAmount,
|
||||
UserWithDrawAmount: v.UserWithDrawAmount,
|
||||
UserWithDrawPaymoneyAmount: v.UserWithDrawPaymoneyAmount,
|
||||
GameRechageAmount: v.GameRechageAmount,
|
||||
GameWithdrawAmount: v.GameWithdrawAmount,
|
||||
TotalRechargeCount: v.TotalRechargeCount,
|
||||
}
|
||||
|
||||
tmp.TotalVisitCount = v.DirectVisitCount + v.FissionVisitCount // 日活
|
||||
tmp.TotalAmount = v.DirectAmount + v.FissionAmount // 总充值
|
||||
tmp.TotalNewCount = v.NewDirectCount + v.NewFissionCount // 新增
|
||||
tmp.OldRechargeAmount = tmp.TotalAmount - tmp.NewRechargeAmount // 老用户充值
|
||||
|
||||
if tmp.TotalAmount-tmp.DaichongAmout > 0 {
|
||||
tmp.OnlineAmount = tmp.TotalAmount - tmp.DaichongAmout
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
|
||||
resp.Total = count
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取充值明细(集团)
|
||||
func BlocListRegisterDetailsInfo(ctx context.Context, param proto.ListBlocRegisterDetailsReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocRegisterDetailsRes{
|
||||
Total: 0,
|
||||
List: make([]proto.BlocRegisterDetailsInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, RechagerList, err := registermod.GetBlocRegisterDetailsList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
appMap := make(map[int32]int32)
|
||||
for _, v := range RechagerList {
|
||||
if _, ok := appMap[v.AppId]; !ok {
|
||||
appMap[v.AppId] = v.AppId
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
}
|
||||
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range RechagerList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.BlocRegisterDetailsInfo{
|
||||
UserId: v.PromCode,
|
||||
AppName: appName,
|
||||
SysType: v.SysType,
|
||||
DevType: v.DevType,
|
||||
Mobile: v.Mobile,
|
||||
IP: v.IP,
|
||||
Name: v.Name,
|
||||
IsDirect: v.IsDirect,
|
||||
RegisterTime: v.RegisterTime,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取提现明细(集团)
|
||||
func BlocListWithdrawDetailsInfo(ctx context.Context, param proto.ListBlocWithdrawDetailsReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocWithdrawDetailsRes{
|
||||
Total: 0,
|
||||
List: make([]proto.ListBlocWithdrawDetailsInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, RechagerList, err := withdrawUserOrdermod.GetBlocUserWithdrawOrderList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
appMap := make(map[int32]int32)
|
||||
for _, v := range RechagerList {
|
||||
if _, ok := appMap[v.AppId]; !ok {
|
||||
appMap[v.AppId] = v.AppId
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
}
|
||||
//fmt.Println(appIds)
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range RechagerList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.ListBlocWithdrawDetailsInfo{
|
||||
UID: v.PromCode,
|
||||
AppName: appName,
|
||||
Name: v.Name,
|
||||
Amount: v.Amount,
|
||||
Money: v.Money,
|
||||
PayMoney: v.PayMoney,
|
||||
PayType: v.PayType,
|
||||
OID: v.OID,
|
||||
ActName: v.ActName,
|
||||
Act: v.Act,
|
||||
UserIP: v.UserIP,
|
||||
DeviceType: v.DeviceType,
|
||||
DevID: v.DevID,
|
||||
Status: v.Status,
|
||||
StatusDesc: v.StatusDesc,
|
||||
SuccessAt: v.SuccessAt,
|
||||
ReceivedAt: v.ReceivedAt,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取提现明细(集团)
|
||||
func ListBlocRechargeChannelInfo(ctx context.Context, param proto.ListBlocRechargeChannelReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocRechargeChannelRes{
|
||||
Total: 0,
|
||||
List: make([]proto.ListBlocRechargeChannelInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
|
||||
statList, err := blocDailystatmod.GetBlocDailyStatTotalList(ctx, param.StartTime, param.EndTime)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
count := int64(0)
|
||||
for _, v := range statList {
|
||||
if count >= skip && count <= skip+limit {
|
||||
tmp := proto.ListBlocRechargeChannelInfo{
|
||||
SumDate: v.SumDate,
|
||||
ChannelName: "大白鲨",
|
||||
BlocOrderMoney: v.DirectAmount + v.FissionAmount,
|
||||
BlocChannelRate: 12,
|
||||
}
|
||||
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
|
||||
resp.Total = count
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
func ListMinuteStat(ctx context.Context, param proto.ListMinuteStatReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListMinuteStatRes{
|
||||
TodayGraphList: make([]proto.MinuteStatInfo, 0),
|
||||
}
|
||||
|
||||
// 获取app信息
|
||||
appInfo, err := appInfoMod.GetAppInfoById(ctx, *param.AppId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.AppInfo = proto.AppInfoRes{
|
||||
AppId: appInfo.AppId,
|
||||
Name: appInfo.Name,
|
||||
LandPages: appInfo.LandPages,
|
||||
IconImg: appInfo.IconImg,
|
||||
CreatedAt: appInfo.CreatedAt,
|
||||
UpdatedAt: appInfo.UpdatedAt,
|
||||
}
|
||||
|
||||
start := *param.SumDate
|
||||
end := start.AddDate(0, 0, 1)
|
||||
|
||||
todayStatList, err := minutestatmod.GetMinuteStatList(ctx, start, end, *param.AppId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range todayStatList {
|
||||
tmp := proto.MinuteStatInfo{
|
||||
SumDate: v.SumDate,
|
||||
NewCount: v.NewCount,
|
||||
AndroidAddCount: v.AndroidAddCount,
|
||||
IosAddCount: v.IosAddCount,
|
||||
H5AddCount: v.H5AddCount,
|
||||
RechargetCount: v.RechargetCount,
|
||||
RechargetMoney: v.RechargetMoney,
|
||||
AccessCount: v.AccessCount,
|
||||
AndroidAccessCount: v.AndroidAccessCount,
|
||||
IosAccessCount: v.IosAccessCount + v.H5AccessCount,
|
||||
LandpageAndroidClick: v.LandpageAndroidClick,
|
||||
LandpageIosAppstoreClick: v.LandpageIosAppstoreClick,
|
||||
LandpageIosAthorClick: v.LandpageIosAthorClick,
|
||||
LandpageIosH5Click: v.LandpageIosH5Click,
|
||||
LightGuestAddCount: v.LightGuestAddCount,
|
||||
}
|
||||
|
||||
resp.TodayGraphList = append(resp.TodayGraphList, tmp)
|
||||
}
|
||||
|
||||
compareTime := start.AddDate(0, 0, -1)
|
||||
if param.CompareTime != nil {
|
||||
compareTime = *param.CompareTime
|
||||
}
|
||||
compareTimeEnd := compareTime.AddDate(0, 0, 1)
|
||||
yesterdayStatList, err := minutestatmod.GetMinuteStatList(ctx, compareTime, compareTimeEnd, *param.AppId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range yesterdayStatList {
|
||||
tmp := proto.MinuteStatInfo{
|
||||
SumDate: v.SumDate,
|
||||
NewCount: v.NewCount,
|
||||
AndroidAddCount: v.AndroidAddCount,
|
||||
IosAddCount: v.IosAddCount,
|
||||
H5AddCount: v.H5AddCount,
|
||||
RechargetCount: v.RechargetCount,
|
||||
RechargetMoney: v.RechargetMoney,
|
||||
AccessCount: v.AccessCount,
|
||||
AndroidAccessCount: v.AndroidAccessCount,
|
||||
IosAccessCount: v.IosAccessCount + v.H5AccessCount,
|
||||
LandpageAndroidClick: v.LandpageAndroidClick,
|
||||
LandpageIosAppstoreClick: v.LandpageIosAppstoreClick,
|
||||
LandpageIosAthorClick: v.LandpageIosAthorClick,
|
||||
LandpageIosH5Click: v.LandpageIosH5Click,
|
||||
LightGuestAddCount: v.LightGuestAddCount,
|
||||
}
|
||||
|
||||
resp.YesterdayGraphList = append(resp.YesterdayGraphList, tmp)
|
||||
}
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func ListDailyHourStat(ctx context.Context, param *proto.ListDailyHourStatReq) (code stdLog.CodeType, data *proto.ListDailyHourStatRes, err error) {
|
||||
resp := &proto.ListDailyHourStatRes{
|
||||
Total: 0,
|
||||
List: make([]*proto.DailyHourStatInfo, 0),
|
||||
}
|
||||
filter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{dailyhourstatmod.FN.DailyHourStat.SumDate: -1})
|
||||
opt.SetSkip((param.PageNum - 1) * param.PageSize)
|
||||
opt.SetLimit(param.PageSize)
|
||||
|
||||
if param.AppId != nil {
|
||||
filter[dailyhourstatmod.FN.DailyHourStat.AppId] = *param.AppId
|
||||
}
|
||||
if param.StartTime != nil && param.EndTime != nil {
|
||||
filter[dailyhourstatmod.FN.DailyHourStat.SumDate] = bson.M{"$gte": *param.StartTime, "$lt": *param.EndTime}
|
||||
}
|
||||
if param.SumHour != nil {
|
||||
filter[dailyhourstatmod.FN.DailyHourStat.SumHour] = *¶m.SumHour
|
||||
}
|
||||
|
||||
total, _ := dailyhourstatmod.CountDailyHourStats(ctx, filter)
|
||||
|
||||
statList, err := dailyhourstatmod.FindManyDailyHourStat(ctx, filter, opt)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
for _, v := range statList {
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range statList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
tmp := DailyHourStatToInfo(v)
|
||||
tmp.AppName = appName
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
resp.Total = total
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func ListDailyHourStatExport(ctx context.Context, param *proto.ListDailyHourStatReq) (*bytes.Buffer, error) {
|
||||
_, data, err := ListDailyHourStat(ctx, param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ExportDailyHourStat(data.List)
|
||||
}
|
||||
|
||||
func ListHourStatLine(ctx context.Context, param proto.ListHourStatLineReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListHourStatLineResp{
|
||||
TodayGraphList: make([]proto.ListHourStatLineInfo, 0),
|
||||
YesterdayGraphList: make([]proto.ListHourStatLineInfo, 0),
|
||||
}
|
||||
|
||||
// 获取app信息
|
||||
appInfo, err := appInfoMod.GetAppInfoById(ctx, *param.AppId)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
resp.AppInfo = proto.AppInfoRes{
|
||||
AppId: appInfo.AppId,
|
||||
Name: appInfo.Name,
|
||||
LandPages: appInfo.LandPages,
|
||||
IconImg: appInfo.IconImg,
|
||||
CreatedAt: appInfo.CreatedAt,
|
||||
UpdatedAt: appInfo.UpdatedAt,
|
||||
}
|
||||
|
||||
start := *param.SumDate
|
||||
end := start.AddDate(0, 0, 1)
|
||||
|
||||
todayFilter := bson.M{
|
||||
"sumDate": bson.M{"$gte": start, "$lte": end},
|
||||
"appId": *param.AppId,
|
||||
}
|
||||
|
||||
todayStatList, err := botdailyhourstat.FindManyBotHourStat(ctx, todayFilter)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range todayStatList {
|
||||
tmp := proto.ListHourStatLineInfo{
|
||||
SumDate: v.SumDate,
|
||||
HourNewDistrict: v.HourStat.Register.Total.District.Count,
|
||||
HourNewWeb: v.HourStat.Register.Total.Web.Count,
|
||||
HourNewNormal: v.HourStat.Register.Total.Normal.Count,
|
||||
HourNew: v.HourStat.Register.Count(),
|
||||
HourAndroidNew: v.HourStat.Register.Android.Count(),
|
||||
HourIosNew: v.HourStat.Register.Ios.Count(),
|
||||
HourDistrictRecharge: v.HourStat.Recharge.Total.District.Value,
|
||||
HourWebRecharge: v.HourStat.Recharge.Total.Web.Value,
|
||||
HourNormalRecharge: v.HourStat.Recharge.Total.Normal.Value,
|
||||
HourAndroidRecharge: v.HourStat.Recharge.Android.Value(),
|
||||
HourIosRecharge: v.HourStat.Recharge.Ios.Value(),
|
||||
HourVipRecharge: v.HourStat.RechargeVip.Value(),
|
||||
HourDistrictVip: v.HourStat.RechargeVip.Total.District.Value,
|
||||
HourWebVip: v.HourStat.RechargeVip.Total.Web.Value,
|
||||
HourNormalVip: v.HourStat.RechargeVip.Total.Normal.Value,
|
||||
}
|
||||
|
||||
resp.TodayGraphList = append(resp.TodayGraphList, tmp)
|
||||
}
|
||||
|
||||
compareTime := start.AddDate(0, 0, -1)
|
||||
if param.CompareTime != nil {
|
||||
compareTime = *param.CompareTime
|
||||
}
|
||||
compareTimeEnd := compareTime.AddDate(0, 0, 1)
|
||||
yestodayFilter := bson.M{
|
||||
"sumDate": bson.M{"$gte": compareTime, "$lte": compareTimeEnd},
|
||||
"appId": *param.AppId,
|
||||
}
|
||||
|
||||
yesterdayStatList, err := botdailyhourstat.FindManyBotHourStat(ctx, yestodayFilter)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range yesterdayStatList {
|
||||
tmp := proto.ListHourStatLineInfo{
|
||||
SumDate: v.SumDate,
|
||||
HourNewDistrict: v.HourStat.Register.Total.District.Count,
|
||||
HourNewWeb: v.HourStat.Register.Total.Web.Count,
|
||||
HourNewNormal: v.HourStat.Register.Total.Normal.Count,
|
||||
HourNew: v.HourStat.Register.Count(),
|
||||
HourAndroidNew: v.HourStat.Register.Android.Count(),
|
||||
HourIosNew: v.HourStat.Register.Ios.Count(),
|
||||
HourDistrictRecharge: v.HourStat.Recharge.Total.District.Value,
|
||||
HourWebRecharge: v.HourStat.Recharge.Total.Web.Value,
|
||||
HourNormalRecharge: v.HourStat.Recharge.Total.Normal.Value,
|
||||
HourAndroidRecharge: v.HourStat.Recharge.Android.Value(),
|
||||
HourIosRecharge: v.HourStat.Recharge.Ios.Value(),
|
||||
HourVipRecharge: v.HourStat.RechargeVip.Value(),
|
||||
HourDistrictVip: v.HourStat.RechargeVip.Total.District.Value,
|
||||
HourWebVip: v.HourStat.RechargeVip.Total.Web.Value,
|
||||
HourNormalVip: v.HourStat.RechargeVip.Total.Normal.Value,
|
||||
}
|
||||
|
||||
resp.YesterdayGraphList = append(resp.YesterdayGraphList, tmp)
|
||||
}
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取日常统计信息列表
|
||||
func ChannelStatExport(ctx context.Context, param proto.ChannelStatExportReq) (string, error) {
|
||||
statfilter := bson.M{}
|
||||
opt := &options.FindOptions{}
|
||||
opt.SetSort(bson.M{channelmod.FN.ChannelStat.SumDate: -1})
|
||||
opt.SetLimit(5000)
|
||||
|
||||
if param.AppId != nil && len(*param.AppId) > 0 {
|
||||
statfilter[channelmod.FN.ChannelStat.AppId] = bson.M{"$in": *param.AppId}
|
||||
}
|
||||
|
||||
if param.Channel != nil {
|
||||
statfilter[channelmod.FN.ChannelStat.Channel] = *param.Channel
|
||||
}
|
||||
if param.StartTime != nil && param.EndTime != nil {
|
||||
statfilter[channelmod.FN.ChannelStat.SumDate] = bson.M{"$gte": *param.StartTime, "$lte": *param.EndTime}
|
||||
}
|
||||
|
||||
statList, err := channelmod.FindManyChannelStat(ctx, statfilter, opt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("./tmp/渠道统计信息列表-%s.xlsx", time.Now().Format("2006-01-02 15:04:05"))
|
||||
if sErr := channelStatexport(filePath, statList); sErr != nil {
|
||||
return "", sErr
|
||||
}
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
func channelStatexport(rPath string, statInfos channelmod.ChannelStatList) error {
|
||||
f := excelize.NewFile()
|
||||
sheetName := "Sheet1"
|
||||
// 设置列宽
|
||||
f.SetColWidth("Sheet1", "A", "O", 20)
|
||||
// 设置表头行高
|
||||
f.SetRowHeight("Sheet1", 1, 30)
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(`{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":true,
|
||||
"size":16,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center"
|
||||
}
|
||||
}`)
|
||||
f.SetCellStyle("Sheet1", "A1", "O1", headStyleID)
|
||||
// 统计时间 app名称 所属渠道 总新增 总充值
|
||||
idx := f.NewSheet(sheetName)
|
||||
var exportTransWaterTitle = map[string]string{
|
||||
"A1": "日期",
|
||||
"B1": "APP名称",
|
||||
"C1": "渠道码",
|
||||
"D1": "总新增",
|
||||
"E1": "总充值",
|
||||
}
|
||||
for k, v := range exportTransWaterTitle {
|
||||
f.SetCellValue(sheetName, k, v)
|
||||
}
|
||||
appInfos, _ := appInfoMod.GetAllAppInfo(context.Background())
|
||||
|
||||
for k, v := range statInfos {
|
||||
i := 'A' // i 不能超过Z,即列输不能超过26列,超出将出现bug
|
||||
date := v.SumDate.Local().Format("2006-01-02")
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), date) //日期
|
||||
i++
|
||||
appName := ""
|
||||
for _, appInfo := range appInfos {
|
||||
if appInfo.AppId == v.AppId {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
}
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), appName) //APP名称
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Channel) //渠道吗
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.NewTotalCount) //总新增
|
||||
i++
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TotalAmount)/100) //总充值
|
||||
}
|
||||
|
||||
f.SetActiveSheet(idx)
|
||||
p, _ := os.Getwd()
|
||||
fileName := path.Join(p, rPath)
|
||||
if err := f.SaveAs(fileName); err != nil {
|
||||
log.Error("SettlementInfo:export save file to disk failed",
|
||||
log.Any("fileName", fileName),
|
||||
log.E(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package statSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/rechargegamemod"
|
||||
"stat/models/withdrawGameOrdermod"
|
||||
)
|
||||
|
||||
// 获取充值明细(集团)
|
||||
func GameRechargeList(ctx context.Context, param proto.ListBlocRechargeDetailsReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocRechargeDetailsRes{
|
||||
Total: 0,
|
||||
List: make([]proto.BlocRechargeDetailsInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, RechagerList, err := rechargegamemod.GetBlocRechargeDetailsList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
appMap := make(map[int32]int32)
|
||||
for _, v := range RechagerList {
|
||||
if _, ok := appMap[v.AppId]; !ok {
|
||||
appMap[v.AppId] = v.AppId
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
}
|
||||
//fmt.Println(appIds)
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range RechagerList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.BlocRechargeDetailsInfo{
|
||||
UserId: v.PromCode,
|
||||
AppName: appName,
|
||||
SysType: v.SysType,
|
||||
DevType: v.DevType,
|
||||
ChannelName: v.ChannelName,
|
||||
CID: v.CID,
|
||||
Type: v.Type,
|
||||
OrderId: v.OrderId,
|
||||
Money: v.Money,
|
||||
PayMoney: v.PayMoney,
|
||||
Status: 3,
|
||||
SuccessAt: v.SuccessAt,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
|
||||
// 获取提现明细(集团)
|
||||
func GameWithdrawList(ctx context.Context, param proto.ListBlocWithdrawDetailsReq) (code stdLog.CodeType, data interface{}, err error) {
|
||||
resp := proto.ListBlocWithdrawDetailsRes{
|
||||
Total: 0,
|
||||
List: make([]proto.ListBlocWithdrawDetailsInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(param.PageNum, param.PageSize)
|
||||
total, RechagerList, err := withdrawGameOrdermod.GetBlocGameWithdrawOrderList(ctx, param.AppId, param.StartTime, param.EndTime, skip, limit)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
appIds := make([]int32, 0)
|
||||
appMap := make(map[int32]int32)
|
||||
for _, v := range RechagerList {
|
||||
if _, ok := appMap[v.AppId]; !ok {
|
||||
appMap[v.AppId] = v.AppId
|
||||
appIds = append(appIds, v.AppId)
|
||||
}
|
||||
}
|
||||
//fmt.Println(appIds)
|
||||
appInfoMap, err := MakeAppInfosMapByIds(ctx, appIds)
|
||||
if err != nil {
|
||||
code = stdLog.Failure
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range RechagerList {
|
||||
appName := ""
|
||||
if appInfo, ok := appInfoMap[v.AppId]; ok {
|
||||
appName = appInfo.Name
|
||||
}
|
||||
|
||||
tmp := proto.ListBlocWithdrawDetailsInfo{
|
||||
UID: v.PromCode,
|
||||
AppName: appName,
|
||||
Name: v.Name,
|
||||
Amount: v.Amount,
|
||||
Money: v.Money,
|
||||
PayMoney: v.PayMoney,
|
||||
PayType: v.PayType,
|
||||
OID: v.OID,
|
||||
ActName: v.ActName,
|
||||
Act: v.Act,
|
||||
UserIP: v.UserIP,
|
||||
DeviceType: v.DeviceType,
|
||||
DevID: v.DevID,
|
||||
Status: v.Status,
|
||||
StatusDesc: v.StatusDesc,
|
||||
SuccessAt: v.SuccessAt,
|
||||
ReceivedAt: v.ReceivedAt,
|
||||
}
|
||||
resp.List = append(resp.List, tmp)
|
||||
}
|
||||
|
||||
resp.Total = total
|
||||
|
||||
data = resp
|
||||
code = stdLog.Success
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
package statSer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"stat/api/proto"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/dailyhourstatmod"
|
||||
"stat/models/dailystatmod"
|
||||
"strconv"
|
||||
|
||||
"github.com/360EntSecGroup-Skylar/excelize"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type RuleMap map[string]*channelmod.ChannelDeductionRule
|
||||
|
||||
func (c RuleMap) GetRule(channel string, appId int32) *channelmod.ChannelDeductionRule {
|
||||
key := fmt.Sprintf("%s %d", channel, appId)
|
||||
return c[key]
|
||||
}
|
||||
|
||||
func (c RuleMap) SetRule(rule *channelmod.ChannelDeductionRule) {
|
||||
key := fmt.Sprintf("%s %d", rule.Channel, rule.AppId)
|
||||
c[key] = rule
|
||||
}
|
||||
|
||||
func MakeRuleMap(ctx context.Context, channels []string) (RuleMap, error) {
|
||||
ruleMap := make(RuleMap)
|
||||
m := make(map[string]struct{})
|
||||
for _, s := range channels {
|
||||
m[s] = struct{}{}
|
||||
}
|
||||
channels = make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
channels = append(channels, k)
|
||||
}
|
||||
rules, err := channelmod.FindManyChannelDeductionRule(ctx, bson.M{channelmod.FN.ChannelDeductionRule.Channel: bson.M{"$in": channels}})
|
||||
if err != nil {
|
||||
return ruleMap, err
|
||||
}
|
||||
for _, item := range rules {
|
||||
ruleMap.SetRule(item)
|
||||
}
|
||||
return ruleMap, nil
|
||||
}
|
||||
|
||||
func MakeAppInfosMapByIds(ctx context.Context, appIds []int32) (map[int32]*appInfoMod.AppInfo, error) {
|
||||
r := make(map[int32]*appInfoMod.AppInfo)
|
||||
|
||||
if len(appIds) == 0 {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
appList, err := appInfoMod.GetAppInfosByAppIds(ctx, appIds)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
|
||||
for _, v := range appList {
|
||||
_v := v
|
||||
r[v.AppId] = &_v
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func ExportDailyHourStat(transInfos []*proto.DailyHourStatInfo) (*bytes.Buffer, error) {
|
||||
f := excelize.NewFile()
|
||||
cell := func(row, col int) string { // row start form 0, col start form 0
|
||||
return fmt.Sprintf("%s%d", excelize.ToAlphaString(col), row+1)
|
||||
}
|
||||
sheetName := "Sheet1"
|
||||
row0 := []string{0: "记录时间", 1: "APP名称", 2: "总充值", 3: "总新增", 4: "新增", 11: "充值", 19: "访问数", 21: "VIP", 24: "订单", 27: "落地页", 29: ""}
|
||||
row1 := []string{4: "直推", 5: "裂变", 6: "安卓", 7: "IOS", 8: "自然", 9: "H5", 10: "扣量", 11: "直推", 12: "裂变", 13: "自然",
|
||||
14: "新增", 15: "老用户", 16: "复充", 17: "游戏充值", 18: "游戏提现", 19: "直推", 20: "裂变", 21: "余额", 22: "人民币", 23: "复充",
|
||||
24: "成功", 25: "拉起", 26: "成功率", 27: "安卓", 28: "商店包", 29: "企业签"}
|
||||
// 设置列宽
|
||||
f.SetColWidth("Sheet1", excelize.ToAlphaString(0), excelize.ToAlphaString(1), 16)
|
||||
f.SetColWidth("Sheet1", excelize.ToAlphaString(2), excelize.ToAlphaString(3), 10)
|
||||
f.SetColWidth("Sheet1", excelize.ToAlphaString(4), excelize.ToAlphaString(len(row0)-1), 8)
|
||||
// 设置表头行高
|
||||
f.SetRowHeight("Sheet1", 1, 30)
|
||||
// 设置表头样式
|
||||
headStyleID, _ := f.NewStyle(`{
|
||||
"font":{
|
||||
"color":"#333333",
|
||||
"bold":true,
|
||||
"size":16,
|
||||
"family":"arial"
|
||||
},
|
||||
"alignment":{
|
||||
"vertical":"center",
|
||||
"horizontal":"center"
|
||||
}
|
||||
}`)
|
||||
|
||||
f.SetCellStyle("Sheet1", cell(0, 0), cell(0, len(row0)-1), headStyleID)
|
||||
idx := f.NewSheet(sheetName)
|
||||
// set main title
|
||||
for col, name := range row0 {
|
||||
f.SetCellValue(sheetName, cell(0, col), name)
|
||||
}
|
||||
// merge some title
|
||||
var mergeCells = map[string]string{
|
||||
cell(0, 4): cell(0, 10),
|
||||
cell(0, 11): cell(0, 18),
|
||||
cell(0, 19): cell(0, 20),
|
||||
cell(0, 21): cell(0, 23),
|
||||
cell(0, 24): cell(0, 26),
|
||||
cell(0, 27): cell(0, 29),
|
||||
}
|
||||
for hcell, vcell := range mergeCells {
|
||||
f.MergeCell(sheetName, hcell, vcell)
|
||||
}
|
||||
// set sub title
|
||||
for col, name := range row1 {
|
||||
f.SetCellValue(sheetName, cell(1, col), name)
|
||||
}
|
||||
// set cell data
|
||||
for k, v := range transInfos {
|
||||
col := 0
|
||||
row := k + 2
|
||||
axis := func() string {
|
||||
ret := cell(row, col)
|
||||
col++
|
||||
return ret
|
||||
}
|
||||
fmtW := func(v int64) string {
|
||||
return fmt.Sprintf("%.4f", float64(v)/float64(10000))
|
||||
}
|
||||
f.SetCellValue(sheetName, axis(), v.SumDate.Local().Format("2006-01-02 15:04:05")) //记录时间
|
||||
f.SetCellValue(sheetName, axis(), v.AppName) //APP名称
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.DirectAmount+v.FissionAmount)) //总充值
|
||||
f.SetCellValue(sheetName, axis(), v.NewDirectCount+v.NewFissionCount) //总新增
|
||||
// Group 1 : E1-K1 = 7
|
||||
f.SetCellValue(sheetName, axis(), v.NewDirectCount) //新增-直推
|
||||
f.SetCellValue(sheetName, axis(), v.NewFissionCount) //新增-裂变
|
||||
f.SetCellValue(sheetName, axis(), v.NewAndroidCount) //新增-安卓
|
||||
f.SetCellValue(sheetName, axis(), v.NewIosCount) //新增-IOS
|
||||
f.SetCellValue(sheetName, axis(), v.NewNaturalCount) //新增-自然
|
||||
f.SetCellValue(sheetName, axis(), v.NewH5Count) //新增-H5
|
||||
f.SetCellValue(sheetName, axis(), v.NewDeductionCount) //新增-扣量
|
||||
//Group 2 : L1-S1 = 8
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.DirectAmount)) //充值-直推
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.FissionAmount)) //充值-裂变
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.NaturalAmount)) //充值-自然
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.NewRechargeAmount)) //充值-新增
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.DirectAmount+v.FissionAmount-v.NewRechargeAmount)) //充值-老用户
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.RepeatAmount)) //充值-复充
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.GameRechageAmount)) //充值-游戏充值
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.GameWithdrawAmount)) //充值-游戏提现
|
||||
//Group 3 : T1-U1 = 2
|
||||
f.SetCellValue(sheetName, axis(), v.DirectVisitCount) //访问数-直推
|
||||
f.SetCellValue(sheetName, axis(), v.FissionVisitCount) //访问数-裂变
|
||||
//Group 4 : V1-X1 = 3
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.VipPayByAmount)) //VIP-余额
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.VipPayByMoney)) //VIP-人民币
|
||||
f.SetCellValue(sheetName, axis(), fmtW(v.VipRepeatAmount)) //VIP-复充
|
||||
//Group 5 : Y1-AA1 = 3
|
||||
f.SetCellValue(sheetName, axis(), v.SuccessRechargeOrder) //订单-成功
|
||||
f.SetCellValue(sheetName, axis(), v.TotalRechargeOrder) //订单-拉起
|
||||
f.SetCellValue(sheetName, axis(), v.RechargeSuccessRate) //订单-成功率
|
||||
//Group 6 : AB1-AD1 = 3
|
||||
f.SetCellValue(sheetName, axis(), v.LandpageAndroidClick) //落地页-安卓
|
||||
f.SetCellValue(sheetName, axis(), v.LandpageIosAppstoreClick) //落地页-商店包
|
||||
f.SetCellValue(sheetName, axis(), v.LandpageIosAthorClick) //落地页-企业签
|
||||
}
|
||||
f.SetActiveSheet(idx)
|
||||
buf := new(bytes.Buffer)
|
||||
if err := f.Write(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func DailyStatInfoAccumulate(base, value *proto.DailyStatInfo) {
|
||||
base.TotalNewCount += value.TotalNewCount
|
||||
base.NewDirectCount += value.NewDirectCount
|
||||
base.NewFissionCount += value.NewFissionCount
|
||||
base.NewDeductionCount += value.NewDeductionCount
|
||||
base.NewNaturalCount += value.NewNaturalCount
|
||||
base.NewWebCount += value.NewWebCount
|
||||
base.NewDistrictCount += value.NewDistrictCount
|
||||
base.NewNormalCount += value.NewNormalCount
|
||||
base.AndroidAddCount += value.AndroidAddCount
|
||||
base.IosAddCount += value.IosAddCount
|
||||
base.H5FormalCount += value.H5FormalCount
|
||||
base.H5AddCount += value.H5AddCount
|
||||
base.H5PcAddCount += value.H5PcAddCount
|
||||
base.H5IosAddCount += value.H5IosAddCount
|
||||
base.H5AndroidAddCount += value.H5AndroidAddCount
|
||||
base.TotalVisitCount += value.TotalVisitCount
|
||||
base.DirectVisitCount += value.DirectVisitCount
|
||||
base.FissionVisitCount += value.FissionVisitCount
|
||||
base.NaturalVisitCount += value.NaturalVisitCount
|
||||
base.TodayDistrictVisitCount += value.TodayDistrictVisitCount
|
||||
base.TodayWebVisitCount += value.TodayWebVisitCount
|
||||
base.TodayNormalVisitCount += value.TodayNormalVisitCount
|
||||
base.TodayIosVisitCount += value.TodayIosVisitCount
|
||||
base.TodayAndroidVisitCount += value.TodayAndroidVisitCount
|
||||
base.TodayH5VisitCount += value.TodayH5VisitCount
|
||||
base.TodayH5PcVisitCount += value.TodayH5PcVisitCount
|
||||
base.TodayH5IosVisitCount += value.TodayH5IosVisitCount
|
||||
base.TodayH5AndroidVisitCount += value.TodayH5AndroidVisitCount
|
||||
|
||||
base.LightVisitCount += value.LightVisitCount
|
||||
base.LightDirectVisitCount += value.LightDirectVisitCount
|
||||
base.LightFissionVisitCount += value.LightFissionVisitCount
|
||||
base.LightDeductionVisitCount += value.LightDeductionVisitCount
|
||||
base.LightNaturalVisitCount += value.LightNaturalVisitCount
|
||||
base.LightWebVisitCount += value.LightWebVisitCount
|
||||
base.LightDistrictVisitCount += value.LightDistrictVisitCount
|
||||
base.LightNormalVisitCount += value.LightNormalVisitCount
|
||||
|
||||
base.LightAddCount += value.LightAddCount
|
||||
base.LightGuestAddCount += value.LightGuestAddCount
|
||||
base.LightFormalAddCount += value.LightFormalAddCount
|
||||
base.LightDirectCount += value.LightDirectCount
|
||||
base.LightFissionCount += value.LightFissionCount
|
||||
base.LightDeductionCount += value.LightDeductionCount
|
||||
base.LightNaturalCount += value.LightNaturalCount
|
||||
base.LightWebCount += value.LightWebCount
|
||||
base.LightDistrictCount += value.LightDistrictCount
|
||||
base.LightNormalCount += value.LightNormalCount
|
||||
|
||||
base.TotalAmount += value.TotalAmount
|
||||
base.DirectAmount += value.DirectAmount
|
||||
base.FissionAmount += value.FissionAmount
|
||||
base.DeductionAmount += value.DeductionAmount
|
||||
base.NaturalAmount += value.NaturalAmount
|
||||
base.TodayDistrictRecharge += value.TodayDistrictRecharge
|
||||
base.TodayWebRecharge += value.TodayWebRecharge
|
||||
base.TodayNormalRecharge += value.TodayNormalRecharge
|
||||
base.TodayAndroidRecharge += value.TodayAndroidRecharge
|
||||
base.TodayIosRecharge += value.TodayIosRecharge
|
||||
base.LightRechargeCount += value.LightRechargeCount
|
||||
base.LightDirectRechargeCount += value.LightDirectRechargeCount
|
||||
base.LightFissionRechargeCount += value.LightFissionRechargeCount
|
||||
base.LightDeductionRechargeCount += value.LightDeductionRechargeCount
|
||||
base.LightNaturalRechargeCount += value.LightNaturalRechargeCount
|
||||
base.LightWebRechargeCount += value.LightWebRechargeCount
|
||||
base.LightDistrictRechargeCount += value.LightDistrictRechargeCount
|
||||
base.LightNormalRechargeCount += value.LightNormalRechargeCount
|
||||
base.LightRechargeAmount += value.LightRechargeAmount
|
||||
base.LightDirectRechargeAmount += value.LightDirectRechargeAmount
|
||||
base.LightFissionRechargeAmount += value.LightFissionRechargeAmount
|
||||
base.LightDeductionRechargeAmount += value.LightDeductionRechargeAmount
|
||||
base.LightNaturalRechargeAmount += value.LightNaturalRechargeAmount
|
||||
base.LightWebRechargeAmount += value.LightWebRechargeAmount
|
||||
base.LightDistrictRechargeAmount += value.LightDistrictRechargeAmount
|
||||
base.LightNormalRechargeAmount += value.LightNormalRechargeAmount
|
||||
base.TodayH5Recharge += value.TodayH5Recharge
|
||||
base.TodayH5FormalRecharge += value.TodayH5FormalRecharge
|
||||
base.TodayH5PcRecharge += value.TodayH5PcRecharge
|
||||
base.TodayH5IosRecharge += value.TodayH5IosRecharge
|
||||
base.TodayH5AndroidRecharge += value.TodayH5AndroidRecharge
|
||||
base.VipPayByMoney += value.VipPayByMoney
|
||||
base.PayForAthor += value.TotalAmount - value.VipPayByMoney
|
||||
base.RepeatAmount += value.RepeatAmount
|
||||
base.RepeatVipMoney += value.RepeatVipMoney
|
||||
base.GameRechageAmount += value.GameRechageAmount
|
||||
base.GameWithdrawAmount += value.GameWithdrawAmount
|
||||
base.OldRechargeAmount += value.OldRechargeAmount
|
||||
base.OldVipRecharge += value.OldVipRecharge
|
||||
base.OldCoinRecharge += value.OldCoinRecharge
|
||||
base.NewRechargeCount += value.NewRechargeCount
|
||||
base.NewRechargeAmount += value.NewRechargeAmount
|
||||
base.NewVipRecharge += value.NewVipRecharge
|
||||
base.NewCoinRecharge += value.NewCoinRecharge
|
||||
base.SuccessRechargeOrder += value.SuccessRechargeOrder
|
||||
base.TotalRechargeOrder += value.TotalRechargeOrder
|
||||
base.Day1RetainRate += value.Day1RetainRate
|
||||
base.Day3RetainRate += value.Day3RetainRate
|
||||
base.Day7RetainRate += value.Day7RetainRate
|
||||
base.LandpageAndroidClick += value.LandpageAndroidClick
|
||||
base.LandpageIosAppstoreClick += value.LandpageIosAppstoreClick
|
||||
base.LandpageIosAthorClick += value.LandpageIosAthorClick
|
||||
base.LandpageIosH5Click += value.LandpageIosH5Click
|
||||
if base.NewCoinRecharge < 0 {
|
||||
base.NewCoinRecharge = 0
|
||||
}
|
||||
if base.OldVipRecharge < 0 {
|
||||
base.OldVipRecharge = 0
|
||||
}
|
||||
if base.OldCoinRecharge < 0 {
|
||||
base.OldCoinRecharge = 0
|
||||
}
|
||||
|
||||
if base.TotalRechargeOrder > 0 {
|
||||
base.RechargeSuccessRate = fmt.Sprintf("%.3f", float64(base.SuccessRechargeOrder)/float64(base.TotalRechargeOrder))
|
||||
}
|
||||
if base.TotalNewCount != 0 {
|
||||
base.NewRechargeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(base.NewRechargeCount)/float64(base.TotalNewCount))*100), 64)
|
||||
}
|
||||
if base.TotalVisitCount != 0 {
|
||||
base.VisitRechargeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(base.SuccessRechargeOrder)/float64(base.TotalVisitCount))*100), 64)
|
||||
}
|
||||
if base.SuccessRechargeOrder != 0 {
|
||||
base.ARPPU = base.TotalAmount / base.SuccessRechargeOrder // 金额
|
||||
}
|
||||
if base.TotalNewCount != 0 {
|
||||
base.ARPU = base.TotalAmount / base.TotalNewCount // 金额
|
||||
}
|
||||
}
|
||||
|
||||
func DailyStatToInfo(src *dailystatmod.DailyStatMod) *proto.DailyStatInfo {
|
||||
src.CorrectValidData()
|
||||
ret := &proto.DailyStatInfo{
|
||||
SumDate: src.SumDate,
|
||||
AppId: src.AppId,
|
||||
AppName: "", // 外面赋值
|
||||
TotalNewCount: src.NewTotalCount,
|
||||
NewDirectCount: src.NewDirectCount,
|
||||
NewFissionCount: src.NewFissionCount,
|
||||
NewDeductionCount: src.NewDeductionCount,
|
||||
NewNaturalCount: src.NewNaturalCount,
|
||||
NewWebCount: src.NewWebCount,
|
||||
NewDistrictCount: src.NewDistrictCount,
|
||||
NewNormalCount: src.NewNormalCount,
|
||||
LightAddCount: src.LightAddCount,
|
||||
LightGuestAddCount: src.LightGuestAddCount,
|
||||
LightFormalAddCount: src.LightFormalAddCount,
|
||||
LightDirectCount: src.LightDirectCount,
|
||||
LightFissionCount: src.LightFissionCount,
|
||||
LightDeductionCount: src.LightDeductionCount,
|
||||
LightNaturalCount: src.LightNaturalCount,
|
||||
LightWebCount: src.LightWebCount,
|
||||
LightDistrictCount: src.LightDistrictCount,
|
||||
LightNormalCount: src.LightNormalCount,
|
||||
AndroidAddCount: src.AndroidAddCount,
|
||||
IosAddCount: src.IosAddCount,
|
||||
H5FormalCount: src.H5FormalCount,
|
||||
H5AddCount: src.H5AddCount,
|
||||
H5PcAddCount: src.H5PcAddCount,
|
||||
H5IosAddCount: src.H5IosAddCount,
|
||||
H5AndroidAddCount: src.H5AndroidAddCount,
|
||||
TotalVisitCount: src.TotalVisitCount,
|
||||
DirectVisitCount: src.DirectVisitCount,
|
||||
FissionVisitCount: src.FissionVisitCount,
|
||||
NaturalVisitCount: src.NaturalVisitCount,
|
||||
TodayDistrictVisitCount: src.TodayDistrictVisitCount,
|
||||
TodayWebVisitCount: src.TodayWebVisitCount,
|
||||
TodayNormalVisitCount: src.TodayNormalVisitCount,
|
||||
TodayIosVisitCount: src.TodayIosVisitCount,
|
||||
TodayAndroidVisitCount: src.TodayAndroidVisitCount,
|
||||
LightVisitCount: src.LightVisitCount,
|
||||
LightDirectVisitCount: src.LightDirectVisitCount,
|
||||
LightFissionVisitCount: src.LightFissionVisitCount,
|
||||
LightDeductionVisitCount: src.LightDeductionVisitCount,
|
||||
LightNaturalVisitCount: src.LightNaturalVisitCount,
|
||||
LightWebVisitCount: src.LightWebVisitCount,
|
||||
LightDistrictVisitCount: src.LightDistrictVisitCount,
|
||||
LightNormalVisitCount: src.LightNormalVisitCount,
|
||||
TodayH5VisitCount: src.TodayH5VisitCount,
|
||||
TodayH5PcVisitCount: src.TodayH5PcVisitCount,
|
||||
TodayH5IosVisitCount: src.TodayH5IosVisitCount,
|
||||
TodayH5AndroidVisitCount: src.TodayH5AndroidVisitCount,
|
||||
TotalAmount: src.TotalAmount,
|
||||
DirectAmount: src.DirectAmount,
|
||||
FissionAmount: src.FissionAmount,
|
||||
DeductionAmount: src.DeductionAmount,
|
||||
NaturalAmount: src.NaturalAmount,
|
||||
TodayDistrictRecharge: src.TodayDistrictRecharge,
|
||||
TodayWebRecharge: src.TodayWebRecharge,
|
||||
TodayNormalRecharge: src.TodayNormalRecharge,
|
||||
TodayAndroidRecharge: src.TodayAndroidRecharge,
|
||||
TodayIosRecharge: src.TodayIosRecharge,
|
||||
LightRechargeCount: src.LightRechargeCount,
|
||||
LightDirectRechargeCount: src.LightDirectRechargeCount,
|
||||
LightFissionRechargeCount: src.LightFissionRechargeCount,
|
||||
LightDeductionRechargeCount: src.LightDeductionRechargeCount,
|
||||
LightNaturalRechargeCount: src.LightNaturalRechargeCount,
|
||||
LightWebRechargeCount: src.LightWebRechargeCount,
|
||||
LightDistrictRechargeCount: src.LightDistrictRechargeCount,
|
||||
LightNormalRechargeCount: src.LightNormalRechargeCount,
|
||||
LightRechargeAmount: src.LightRechargeAmount,
|
||||
LightDirectRechargeAmount: src.LightDirectRechargeAmount,
|
||||
LightFissionRechargeAmount: src.LightFissionRechargeAmount,
|
||||
LightDeductionRechargeAmount: src.LightDeductionRechargeAmount,
|
||||
LightNaturalRechargeAmount: src.LightNaturalRechargeAmount,
|
||||
LightWebRechargeAmount: src.LightWebRechargeAmount,
|
||||
LightDistrictRechargeAmount: src.LightDistrictRechargeAmount,
|
||||
LightNormalRechargeAmount: src.LightNormalRechargeAmount,
|
||||
TodayH5Recharge: src.TodayH5Recharge,
|
||||
TodayH5FormalRecharge: src.TodayH5FormalRecharge,
|
||||
TodayH5PcRecharge: src.TodayH5PcRecharge,
|
||||
TodayH5IosRecharge: src.TodayH5IosRecharge,
|
||||
TodayH5AndroidRecharge: src.TodayH5AndroidRecharge,
|
||||
ARPU: 0, // 下面赋值
|
||||
ARPPU: 0, // 下面赋值
|
||||
VipPayByMoney: src.VipPayByMoney,
|
||||
PayForAthor: 0, // 下面赋值
|
||||
RepeatAmount: src.RepeatAmount,
|
||||
RepeatVipMoney: src.RepeatVipMoney,
|
||||
GameRechageAmount: src.GameRechageAmount,
|
||||
GameWithdrawAmount: src.GameWithdrawAmount,
|
||||
OldRechargeAmount: 0, // 下面赋值
|
||||
OldVipRecharge: 0, // 下面赋值
|
||||
OldCoinRecharge: 0, // 下面赋值
|
||||
NewRechargeCount: src.NewRechargeCount,
|
||||
NewRechargeAmount: src.NewRechargeAmount,
|
||||
NewVipRecharge: src.NewVipPayMoney,
|
||||
NewCoinRecharge: 0, // 下面赋值
|
||||
NewRechargeRate: 0, // 下面赋值
|
||||
VisitRechargeRate: 0, // 下面赋值
|
||||
SuccessRechargeOrder: src.SuccessRechargeOrder,
|
||||
TotalRechargeOrder: src.TotalRechargeOrder,
|
||||
RechargeSuccessRate: "", //下面赋值
|
||||
Day1RetainRate: src.Day1RetainRate,
|
||||
Day3RetainRate: src.Day3RetainRate,
|
||||
Day7RetainRate: src.Day7RetainRate,
|
||||
LandpageAndroidClick: src.LandpageAndroidClick,
|
||||
LandpageIosAppstoreClick: src.LandpageIosAppstoreClick,
|
||||
LandpageIosAthorClick: src.LandpageIosAthorClick,
|
||||
LandpageIosH5Click: src.LandpageIosH5Click,
|
||||
UpdatedAt: src.UpdatedAt,
|
||||
}
|
||||
if ret.TotalNewCount == 0 {
|
||||
ret.TotalNewCount = ret.NewDirectCount + ret.NewFissionCount + ret.NewDeductionCount
|
||||
}
|
||||
if ret.TotalVisitCount == 0 {
|
||||
ret.TotalVisitCount = ret.DirectVisitCount + ret.FissionVisitCount + ret.DeductionVisitCount
|
||||
}
|
||||
if ret.TotalAmount == 0 {
|
||||
ret.TotalAmount = ret.DirectAmount + ret.FissionAmount + ret.DeductionAmount
|
||||
}
|
||||
|
||||
ret.PayForAthor = ret.TotalAmount - ret.VipPayByMoney
|
||||
ret.OldRechargeAmount = ret.TotalAmount - ret.NewRechargeAmount
|
||||
ret.OldVipRecharge = ret.VipPayByMoney - ret.NewVipRecharge
|
||||
ret.OldCoinRecharge = ret.OldRechargeAmount - ret.OldVipRecharge
|
||||
ret.NewCoinRecharge = ret.NewRechargeAmount - ret.NewVipRecharge
|
||||
|
||||
if ret.NewCoinRecharge < 0 {
|
||||
ret.NewCoinRecharge = 0
|
||||
}
|
||||
if ret.OldVipRecharge < 0 {
|
||||
ret.OldVipRecharge = 0
|
||||
}
|
||||
if ret.OldCoinRecharge < 0 {
|
||||
ret.OldCoinRecharge = 0
|
||||
}
|
||||
|
||||
if ret.TotalRechargeOrder > 0 {
|
||||
ret.RechargeSuccessRate = fmt.Sprintf("%.3f", float64(ret.SuccessRechargeOrder)/float64(ret.TotalRechargeOrder))
|
||||
}
|
||||
if ret.TotalNewCount != 0 {
|
||||
ret.ARPU = ret.TotalAmount / ret.TotalNewCount // 金额
|
||||
ret.NewRechargeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(ret.NewRechargeCount)/float64(ret.TotalNewCount))*100), 64)
|
||||
}
|
||||
if ret.TotalVisitCount != 0 {
|
||||
ret.VisitRechargeRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (float64(ret.SuccessRechargeOrder)/float64(ret.TotalVisitCount))*100), 64)
|
||||
}
|
||||
if ret.SuccessRechargeOrder != 0 {
|
||||
ret.ARPPU = ret.TotalAmount / ret.SuccessRechargeOrder // 金额
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func DailyHourStatToInfo(src *dailyhourstatmod.DailyHourStat) *proto.DailyHourStatInfo {
|
||||
src.CorrectValidData()
|
||||
ret := &proto.DailyHourStatInfo{
|
||||
SumDate: src.SumDate,
|
||||
AppId: src.AppId,
|
||||
SumHour: src.SumHour,
|
||||
AppName: "",
|
||||
NewTotalCount: src.NewTotalCount,
|
||||
NewDirectCount: src.NewDirectCount,
|
||||
NewFissionCount: src.NewFissionCount,
|
||||
NewDeductionCount: src.NewDeductionCount,
|
||||
NewNaturalCount: src.NewNaturalCount,
|
||||
NewWebCount: src.NewWebCount,
|
||||
NewNormalCount: src.NewNormalCount,
|
||||
NewDistrictCount: src.NewDistrictCount,
|
||||
NewAndroidCount: src.NewAndroidCount,
|
||||
NewIosCount: src.NewIosCount,
|
||||
NewLightCount: src.NewLightCount,
|
||||
NewLightGuestCount: src.NewLightGuestCount,
|
||||
NewLightFormalCount: src.NewLightFormalCount,
|
||||
NewLightDirectCount: src.NewLightDirectCount,
|
||||
NewLightFissionCount: src.NewLightFissionCount,
|
||||
NewLightDeductionCount: src.NewLightDeductionCount,
|
||||
NewLightNaturalCount: src.NewLightNaturalCount,
|
||||
NewLightWebCount: src.NewLightWebCount,
|
||||
NewLightNormalCount: src.NewLightNormalCount,
|
||||
NewLightDistrictCount: src.NewLightDistrictCount,
|
||||
NewH5Count: src.NewH5Count,
|
||||
NewH5FormalCount: src.NewH5FormalCount,
|
||||
NewH5PcCount: src.NewH5PcCount,
|
||||
NewH5IosCount: src.NewH5IosCount,
|
||||
NewH5AndroidCount: src.NewH5AndroidCount,
|
||||
TotalVisitCount: src.TotalVisitCount,
|
||||
DirectVisitCount: src.DirectVisitCount,
|
||||
FissionVisitCount: src.FissionVisitCount,
|
||||
NaturalVisitCount: src.NaturalVisitCount,
|
||||
WebVisitCount: src.WebVisitCount,
|
||||
NormalVisitCount: src.NormalVisitCount,
|
||||
DistrictVisitCount: src.DistrictVisitCount,
|
||||
IosVisitCount: src.IosVisitCount,
|
||||
AndroidVisitCount: src.AndroidVisitCount,
|
||||
LightVisitCount: src.LightVisitCount,
|
||||
LightVisitDirectCount: src.LightVisitDirectCount,
|
||||
LightVisitFissionCount: src.LightVisitFissionCount,
|
||||
LightVisitDeductionCount: src.LightVisitDeductionCount,
|
||||
LightVisitNaturalCount: src.LightVisitNaturalCount,
|
||||
LightVisitWebCount: src.LightVisitWebCount,
|
||||
LightVisitNormalCount: src.LightVisitNormalCount,
|
||||
LightVisitDistrictCount: src.LightVisitDistrictCount,
|
||||
H5VisitCount: src.H5VisitCount,
|
||||
H5PcVisitCount: src.H5PcVisitCount,
|
||||
H5IosVisitCount: src.H5IosVisitCount,
|
||||
H5AndroidVisitCount: src.H5AndroidVisitCount,
|
||||
TotalAmount: src.TotalAmount,
|
||||
SuccessRechargeOrder: src.SuccessRechargeOrder,
|
||||
DirectAmount: src.DirectAmount,
|
||||
DirectAmountCount: src.DirectAmountCount,
|
||||
FissionAmount: src.FissionAmount,
|
||||
FissionAmountCount: src.FissionAmountCount,
|
||||
NaturalAmount: src.NaturalAmount,
|
||||
NaturalAmountCount: src.NaturalAmountCount,
|
||||
DeductionAmount: src.DeductionAmount,
|
||||
DeductionAmountCount: src.DeductionAmountCount,
|
||||
WebAmount: src.WebAmount,
|
||||
WebAmountCount: src.WebAmountCount,
|
||||
NormalAmount: src.NormalAmount,
|
||||
NormalAmountCount: src.NormalAmountCount,
|
||||
DistrictAmount: src.DistrictAmount,
|
||||
DistrictAmountCount: src.DistrictAmountCount,
|
||||
AndroidAmount: src.AndroidAmount,
|
||||
AndroidAmountCount: src.AndroidAmountCount,
|
||||
IosAmount: src.IosAmount,
|
||||
IosAmountCount: src.IosAmountCount,
|
||||
LightAmount: src.LightAmount,
|
||||
LightAmountCount: src.LightAmountCount,
|
||||
LightDirectAmount: src.LightDirectAmount,
|
||||
LightDirectAmountCount: src.LightDirectAmountCount,
|
||||
LightFissionAmount: src.LightFissionAmount,
|
||||
LightFissionAmountCount: src.LightFissionAmountCount,
|
||||
LightDeductionAmount: src.LightDeductionAmount,
|
||||
LightDeductionAmountCount: src.LightDeductionAmountCount,
|
||||
LightNaturalAmount: src.LightNaturalAmount,
|
||||
LightNaturalAmountCount: src.LightNaturalAmountCount,
|
||||
LightWebAmount: src.LightWebAmount,
|
||||
LightWebAmountCount: src.LightWebAmountCount,
|
||||
LightNormalAmount: src.LightNormalAmount,
|
||||
LightNormalAmountCount: src.LightNormalAmountCount,
|
||||
LightDistrictAmount: src.LightDistrictAmount,
|
||||
LightDistrictAmountCount: src.LightDistrictAmountCount,
|
||||
H5Amount: src.H5Amount,
|
||||
H5AmountCount: src.H5AmountCount,
|
||||
H5FormalAmount: src.H5FormalAmount,
|
||||
H5FormalAmountCount: src.H5FormalAmountCount,
|
||||
H5PcAmount: src.H5PcAmount,
|
||||
H5PcAmountCount: src.H5PcAmountCount,
|
||||
H5IosAmount: src.H5IosAmount,
|
||||
H5IosAmountCount: src.H5IosAmountCount,
|
||||
H5AndroidAmount: src.H5AndroidAmount,
|
||||
H5AndroidAmountCount: src.H5AndroidAmountCount,
|
||||
RepeatAmount: src.RepeatAmount,
|
||||
RepeatAmountCount: src.RepeatAmountCount,
|
||||
NewRechargeAmount: src.NewRechargeAmount,
|
||||
NewRechargeCount: src.NewRechargeCount,
|
||||
NewCoinRecharge: 0,
|
||||
NewVipRecharge: 0,
|
||||
OldCoinRecharge: 0,
|
||||
OldVipRecharge: 0,
|
||||
OldRechargeAmount: 0,
|
||||
VipPayByMoney: src.VipPayByMoney,
|
||||
VipPayByAmount: src.VipPayByAmount,
|
||||
VipRepeatAmount: src.VipRepeatAmount,
|
||||
TotalRechargeOrder: src.TotalRechargeOrder,
|
||||
RechargeSuccessRate: "",
|
||||
GameRechageAmount: src.GameRechageAmount,
|
||||
GameWithdrawAmount: src.GameWithdrawAmount,
|
||||
LandpageAndroidClick: src.LandpageAndroidClick,
|
||||
LandpageIosAppstoreClick: src.LandpageIosAppstoreClick,
|
||||
LandpageIosAthorClick: src.LandpageIosAthorClick,
|
||||
LandpageIosH5Click: src.LandpageIosH5Click,
|
||||
UpdatedAt: src.UpdatedAt,
|
||||
}
|
||||
|
||||
if ret.NewTotalCount == 0 {
|
||||
ret.NewTotalCount = ret.NewDirectCount + ret.NewFissionCount + ret.NewDeductionCount
|
||||
}
|
||||
if ret.TotalVisitCount == 0 {
|
||||
ret.TotalVisitCount = ret.DirectVisitCount + ret.FissionVisitCount
|
||||
}
|
||||
if ret.TotalAmount == 0 {
|
||||
ret.TotalAmount = ret.DirectAmount + ret.FissionAmount + ret.DeductionAmount
|
||||
}
|
||||
|
||||
ret.VipPayByAmount = ret.TotalAmount - ret.VipPayByMoney
|
||||
ret.OldRechargeAmount = ret.TotalAmount - ret.NewRechargeAmount
|
||||
ret.OldVipRecharge = ret.VipPayByMoney - ret.NewVipRecharge
|
||||
ret.OldCoinRecharge = ret.OldRechargeAmount - ret.OldVipRecharge
|
||||
ret.NewCoinRecharge = ret.NewRechargeAmount - ret.NewVipRecharge
|
||||
|
||||
if ret.NewCoinRecharge < 0 {
|
||||
ret.NewCoinRecharge = 0
|
||||
}
|
||||
if ret.OldVipRecharge < 0 {
|
||||
ret.OldVipRecharge = 0
|
||||
}
|
||||
if ret.OldCoinRecharge < 0 {
|
||||
ret.OldCoinRecharge = 0
|
||||
}
|
||||
|
||||
if ret.TotalRechargeOrder > 0 {
|
||||
ret.RechargeSuccessRate = fmt.Sprintf("%.3f", float64(ret.SuccessRechargeOrder)/float64(ret.TotalRechargeOrder))
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"stat/common/httpUtils"
|
||||
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
apiDomain = "http://file.dhuqh.com/file/upload"
|
||||
)
|
||||
|
||||
var (
|
||||
header = map[string]string{"Content-Type": "application/json"}
|
||||
)
|
||||
|
||||
const (
|
||||
Success = 200
|
||||
)
|
||||
|
||||
func UploadImg(fileName string, data []byte, domain string) (pathStr string, err error) {
|
||||
r := objreq{
|
||||
Name: fileName,
|
||||
Data: base64.StdEncoding.EncodeToString(data),
|
||||
}
|
||||
jsonbytes, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(domain) == 0 {
|
||||
domain = apiDomain
|
||||
}
|
||||
url := domain
|
||||
if !strings.Contains(domain, "/file/upload") {
|
||||
url = domain + "/file/upload"
|
||||
}
|
||||
resp, err := httpUtils.Post(url, header, bytes.NewReader(jsonbytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var or objresp
|
||||
if err = json.Unmarshal(resp.Data, &or); err != nil {
|
||||
return
|
||||
}
|
||||
if or.Code != Success {
|
||||
err = fmt.Errorf("上传失败:%s", or.Msg)
|
||||
return
|
||||
}
|
||||
pathStr = or.Object.Name
|
||||
return
|
||||
}
|
||||
|
||||
func fileServer() (url string) {
|
||||
return "http://ts.dhuqh.com"
|
||||
}
|
||||
|
||||
func PostOk(baseInfo *BaseInfo, domain string) (result *VideoOkResult, err error) {
|
||||
result = &VideoOkResult{}
|
||||
paramJson, err := json.Marshal(baseInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(domain) == 0 {
|
||||
domain = fileServer()
|
||||
}
|
||||
url := domain
|
||||
if !strings.Contains(domain, "/video/shard/info") {
|
||||
url = domain + "/video/shard/info"
|
||||
}
|
||||
fr, err := httpUtils.Post(url, header, bytes.NewReader(paramJson))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(fr.Data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func UploadVideo(domain string, body *ShardBody) (result *Result, err error) {
|
||||
if body == nil {
|
||||
return nil, errors.New("no video to upload")
|
||||
}
|
||||
if len(domain) == 0 {
|
||||
domain = fileServer()
|
||||
}
|
||||
url := domain
|
||||
if !strings.Contains(domain, "/video/shard/body") {
|
||||
url = domain + "/video/shard/body"
|
||||
}
|
||||
buf, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := httpUtils.Post(url, header, bytes.NewBuffer(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = new(Result)
|
||||
if err = json.Unmarshal(resp.Data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PostSearch(param *MediaSearchReq, domain string) (result *MediaSearchResp, err error) {
|
||||
result = &MediaSearchResp{}
|
||||
paramJson, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(domain) == 0 {
|
||||
domain = fileServer()
|
||||
}
|
||||
url := domain
|
||||
if !strings.Contains(domain, "/video/search/list") {
|
||||
url = domain + "/video/search/list"
|
||||
}
|
||||
fr, err := httpUtils.Post(url, header, bytes.NewReader(paramJson))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(fr.Data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PostUpdate(param *MediasUpdateReq, domain string) (result *MediasUpdateResp, err error) {
|
||||
result = &MediasUpdateResp{}
|
||||
paramJson, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(domain) == 0 {
|
||||
domain = fileServer()
|
||||
}
|
||||
url := domain
|
||||
if !strings.Contains(domain, "/video/update") {
|
||||
url = domain + "/video/update"
|
||||
}
|
||||
fr, err := httpUtils.Post(url, header, bytes.NewReader(paramJson))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(fr.Data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func PostDelete(param *MediasDelReq, domain string) (result *MediasDelResp, err error) {
|
||||
result = &MediasDelResp{}
|
||||
paramJson, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(domain) == 0 {
|
||||
domain = fileServer()
|
||||
}
|
||||
url := domain
|
||||
if !strings.Contains(domain, "/video/delete") {
|
||||
url = domain + "/video/delete"
|
||||
}
|
||||
fr, err := httpUtils.Post(url, header, bytes.NewReader(paramJson))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = json.Unmarshal(fr.Data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package proxy
|
||||
|
||||
import "time"
|
||||
|
||||
type objreq struct {
|
||||
Name string `json:"fileName"`
|
||||
Data string `json:"fileData"`
|
||||
}
|
||||
|
||||
type Obj struct {
|
||||
Domain string `json:"domain"`
|
||||
Name string `json:"fileName"`
|
||||
}
|
||||
|
||||
type objresp struct {
|
||||
Code int `json:"code"`
|
||||
Object Obj `json:"data"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type ShardBody struct {
|
||||
TaskId string `json:"taskId,omitempty"` // 文档md5
|
||||
Pos int `json:"pos,omitempty"` // 位置
|
||||
FileData string `json:"fileData,omitempty"` // 数据
|
||||
Type string `json:"type,omitempty"`
|
||||
TotalPos int `json:"totalPos,omitempty"` // 分片总数
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
VideoUri string `json:"videoUri,omitempty"`
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
Code int `json:"code,omitempty"` //
|
||||
Data Data `json:"data,omitempty"` //
|
||||
Msg string `json:"msg,omitempty"` //
|
||||
}
|
||||
|
||||
type VideoOkResult struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Msg string `json:"msg,omitempty"` //
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type BaseInfo struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Title string `json:"title,omitempty"` // 标题
|
||||
CheckSum string `json:"checkSum,omitempty"` // md5校验码
|
||||
VideoTime int32 `json:"videTime"` // 影片时长
|
||||
Publish string `bson:"publish,omitempty"` // 视频发布者
|
||||
Type string `json:"type"` // 视频:长视频/短视频--时间阈值
|
||||
Preview int32 `json:"preview"` // 预览普通视频:1:预览 2:
|
||||
Country string `json:"country"` // 国家 (国产、日本、欧美.... 前端固定)
|
||||
FirstTag string `json:"firstTag,omitempty"` // 一级标签
|
||||
SecondTags []string `json:"secondTags,omitempty"` // 二级标签标签
|
||||
CoverImg []string `json:"coverImg,omitempty"` // 封面
|
||||
PreFileName string `json:"preFileName,omitempty"` // 预览文件
|
||||
Status string `json:"status,omitempty"` // 当前状态
|
||||
Size int64 `json:"size"` // 文件size,单位byte
|
||||
Filename string `json:"filename,omitempty"` // 文件名称
|
||||
Desc string `json:"desc,omitempty"` // 详细描述
|
||||
Directors []string `json:"director,omitempty"` // 导演
|
||||
Actors []string `json:"actors,omitempty"` // 演员
|
||||
Studio string `json:"studio,omitempty"` // 工作室
|
||||
Bango string `json:"bango,omitempty"` // 番号,唯一
|
||||
Via string `json:"via,omitempty"` // 来源
|
||||
Remark string `json:"remark,omitempty"` // 备注
|
||||
AddedTime string `json:"addedTime,omitempty"` // 上架时间
|
||||
UploadTime string `json:"uploadTime,omitempty"` // 上传时间
|
||||
}
|
||||
|
||||
type VideoUpdate struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Title *string `json:"title,omitempty"` // 标题
|
||||
Publish *string `bson:"publish,omitempty"` // 视频发布者
|
||||
Type *string `json:"type"` // 视频:长视频/短视频--时间阈值
|
||||
Preview *int32 `json:"preview"` // 预览:1:预览 2:普通视频
|
||||
Country *string `json:"country"` // 国家 (国产、日本、欧美.... 前端固定)
|
||||
FirstTag *string `json:"firstTag,omitempty"` // 一级标签
|
||||
SecondTags *[]string `json:"secondTags,omitempty"` // 二级标签标签
|
||||
CoverImg *[]string `json:"coverImg,omitempty"` // 封面
|
||||
PreFileName *string `json:"preFileName,omitempty"` // 预览文件
|
||||
Status *string `json:"status,omitempty"` // 当前状态
|
||||
Desc *string `json:"desc,omitempty"` // 详细描述
|
||||
Directors *[]string `json:"director,omitempty"` // 导演
|
||||
Actors *[]string `json:"actors,omitempty"` // 演员
|
||||
Studio *string `json:"studio,omitempty"` // 工作室
|
||||
Bango *string `json:"bango,omitempty"` // 番号,唯一
|
||||
Via *string `json:"via,omitempty"` // 来源
|
||||
Remark *string `json:"remark,omitempty"` // 备注
|
||||
AddedTime *string `json:"addedTime,omitempty"` // 上架时间
|
||||
UploadTime *string `json:"uploadTime,omitempty"` // 上传时间
|
||||
}
|
||||
|
||||
type VideoSearch struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Title *string `json:"title,omitempty"` // 标题
|
||||
Publish *string `bson:"publish,omitempty"` // 视频发布者
|
||||
Type *string `json:"type"` // 视频:长视频/短视频--时间阈值
|
||||
Preview *int32 `json:"preview"` // 预览:1:预览 2:普通视频
|
||||
Country *string `json:"country"` // 国家 (国产、日本、欧美.... 前端固定)
|
||||
FirstTag *string `json:"firstTag,omitempty"` // 一级标签
|
||||
SecondTags *[]string `json:"secondTags,omitempty"` // 二级标签标签
|
||||
Status *string `json:"status,omitempty"` // 当前状态
|
||||
Directors *[]string `json:"director,omitempty"` // 导演
|
||||
Actors *[]string `json:"actors,omitempty"` // 演员
|
||||
Studio *string `json:"studio,omitempty"` // 工作室
|
||||
Bango *string `json:"bango,omitempty"` // 番号,唯一
|
||||
Via *string `json:"via,omitempty"` // 来源
|
||||
Remark *string `json:"remark,omitempty"` // 备注
|
||||
AddedTime *string `json:"addedTime,omitempty"` // 上架时间
|
||||
UploadTime *string `json:"uploadTime,omitempty"` // 上传时间
|
||||
Start *time.Time `json:"start"` // 开始时间
|
||||
End *time.Time `json:"end"` // 结束时间
|
||||
}
|
||||
|
||||
type UploadVideoBodyResp struct {
|
||||
Id string `json:"id"` // 文件服返回的上传文件Id
|
||||
VideoUri string `json:"videoUri"` // 文件服返回的视频uri
|
||||
}
|
||||
|
||||
type MediaSearchReq struct {
|
||||
PageNum int64 `json:"pageNum"`
|
||||
PageSize int64 `json:"pageSize"`
|
||||
Param *VideoSearch `json:"param"`
|
||||
}
|
||||
|
||||
//MediaSearchReq 媒体搜索请求返回
|
||||
type MediaSearchResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List []BaseInfo `json:"list"`
|
||||
}
|
||||
|
||||
type MediasDelReq struct {
|
||||
Ids []string `json:"id"`
|
||||
}
|
||||
|
||||
//MediasDelResp 请求批量删除媒体回复
|
||||
type MediasDelResp struct {
|
||||
}
|
||||
|
||||
type MediasUpdateReq struct {
|
||||
Ids []string `json:"id"`
|
||||
VideoUpdate
|
||||
}
|
||||
|
||||
//MediasUpdateResp 请求批量更新媒体回复
|
||||
type MediasUpdateResp struct {
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mime/multipart"
|
||||
"stat/service/storage/proxy"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
buffer = bytes.Buffer{}
|
||||
mtx sync.Mutex
|
||||
)
|
||||
|
||||
// web后台上传使用, form-data方式上传
|
||||
func UploadImage(ctx context.Context, f *multipart.FileHeader, domain string) (path string, err error) {
|
||||
if f == nil {
|
||||
return "", errors.New("file is nil!")
|
||||
}
|
||||
var file multipart.File
|
||||
if file, err = f.Open(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
buffer.Reset()
|
||||
if _, err = buffer.ReadFrom(file); err != nil {
|
||||
return
|
||||
}
|
||||
return proxy.UploadImg(f.Filename, buffer.Bytes(), domain)
|
||||
}
|
||||
|
||||
func UploadVideoBody(ctx context.Context, form *multipart.Form, domain string) (*proxy.Result, error) {
|
||||
if form == nil {
|
||||
return nil, errors.New("multipart from is nil")
|
||||
}
|
||||
sb, err := makeShardBodyByForm(form)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proxy.UploadVideo(domain, sb)
|
||||
}
|
||||
|
||||
func VideoBody(ctx context.Context, taskId, typ, domain string, pos int, data []byte) (*proxy.Result, error) {
|
||||
body := &proxy.ShardBody{
|
||||
TaskId: taskId,
|
||||
Pos: pos,
|
||||
FileData: base64.StdEncoding.EncodeToString(data),
|
||||
Type: typ,
|
||||
}
|
||||
if typ == "1" {
|
||||
body.Type = "AV"
|
||||
} else if typ == "2" {
|
||||
body.Type = "SP"
|
||||
}
|
||||
return proxy.UploadVideo(domain, body)
|
||||
}
|
||||
|
||||
func UploadVideoOK(ctx context.Context, in *proxy.BaseInfo, domain string) (*proxy.VideoOkResult, error) {
|
||||
if in.Type == "1" {
|
||||
in.Type = "AV"
|
||||
} else if in.Type == "2" {
|
||||
in.Type = "SP"
|
||||
}
|
||||
return proxy.PostOk(in, domain)
|
||||
}
|
||||
|
||||
func makeShardBodyByForm(form *multipart.Form) (*proxy.ShardBody, error) {
|
||||
poss := form.Value["pos"]
|
||||
taskIds := form.Value["taskId"]
|
||||
types := form.Value["type"]
|
||||
fs := form.File["file"]
|
||||
totalPoss := form.Value["totalChunks"]
|
||||
|
||||
var (
|
||||
pos, total int
|
||||
taskId string
|
||||
typ string
|
||||
f *multipart.FileHeader
|
||||
err error
|
||||
)
|
||||
|
||||
if len(fs) == 0 || fs[0] == nil {
|
||||
return nil, errors.New("no file")
|
||||
}
|
||||
f = fs[0]
|
||||
|
||||
if len(poss) == 0 || poss[0] == "" {
|
||||
return nil, errors.New("no pos")
|
||||
}
|
||||
if pos, err = strconv.Atoi(poss[0]); err != nil {
|
||||
return nil, fmt.Errorf("invalid pos")
|
||||
}
|
||||
if len(totalPoss) == 0 || totalPoss[0] == "" {
|
||||
return nil, errors.New("no totalPos")
|
||||
}
|
||||
if total, err = strconv.Atoi(totalPoss[0]); err != nil {
|
||||
return nil, fmt.Errorf("invalid totalPos")
|
||||
}
|
||||
|
||||
if len(taskIds) == 0 || taskIds[0] == "" {
|
||||
return nil, errors.New("no pos")
|
||||
}
|
||||
taskId = taskIds[0]
|
||||
|
||||
if len(types) == 0 || types[0] == "" {
|
||||
return nil, errors.New("no type")
|
||||
}
|
||||
if types[0] == "1" {
|
||||
typ = "AV"
|
||||
} else if types[0] == "2" {
|
||||
typ = "SP"
|
||||
} else {
|
||||
typ = types[0]
|
||||
}
|
||||
|
||||
file, ne := f.Open()
|
||||
if ne != nil {
|
||||
return nil, ne
|
||||
}
|
||||
defer file.Close()
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
buffer.Reset()
|
||||
if _, err = buffer.ReadFrom(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &proxy.ShardBody{
|
||||
TaskId: taskId,
|
||||
Pos: pos,
|
||||
FileData: base64.StdEncoding.EncodeToString(buffer.Bytes()),
|
||||
Type: typ,
|
||||
TotalPos: total,
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package userinviteser
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"stat/api/proto"
|
||||
"stat/common/log"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/downloadinfo"
|
||||
"stat/models/registermod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
func UserDistictInfo(ctx context.Context, param proto.UserDistrictReq) (data interface{}, err error) {
|
||||
dictrictInfo := proto.UserDistrictInfo{}
|
||||
dictrictInfo.IsDirect = true
|
||||
dictrictInfo.DistrictCode = param.DistrictCode
|
||||
|
||||
if param.IP != "" {
|
||||
if param.DistrictCode != "" {
|
||||
channelInfo, _ := channelmod.FindOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: param.DistrictCode})
|
||||
if channelInfo == nil || channelInfo.Channel != param.DistrictCode {
|
||||
param.DistrictCode = ""
|
||||
}
|
||||
}
|
||||
if param.DistrictCode == "" {
|
||||
// 根据IP查询是否下载
|
||||
data, _ := downloadinfo.FindOneChannelIP(ctx, param.AppId, param.IP, param.SysType)
|
||||
if data.DC != "" {
|
||||
log.Info("channel info by IP ", log.Any("IP", param.IP), log.Any("app", param.AppId), log.Any("dc", data.DC))
|
||||
dictrictInfo.IsDirect = true
|
||||
dictrictInfo.DistrictCode = data.DC
|
||||
}
|
||||
if data.PC != "" && param.PromSeqe == "" {
|
||||
log.Info("channel info by IP ", log.Any("IP", param.IP), log.Any("app", param.AppId), log.Any("dc", data.DC))
|
||||
dictrictInfo.IsDirect = false
|
||||
dictrictInfo.ParentPromCode = data.PC
|
||||
}
|
||||
}
|
||||
}
|
||||
return dictrictInfo, nil
|
||||
}
|
||||
|
||||
func UserDistrictCodeInfo(ctx context.Context, param proto.UserDistrictCodeReq) (data interface{}, err error) {
|
||||
resp := proto.UserDistrictCodeResp{
|
||||
UserList: make([]proto.UserDistrictCodeInfo, 0),
|
||||
}
|
||||
for _, userId := range param.UserIds {
|
||||
registerInfo, _ := registermod.GetRegisterInfo(ctx, userId, param.AppId)
|
||||
codeInfo := proto.UserDistrictCodeInfo{
|
||||
UserId: registerInfo.UserId,
|
||||
RegisterTime: registerInfo.RegisterTime,
|
||||
DistrictCode: registerInfo.DistrictCode,
|
||||
}
|
||||
resp.UserList = append(resp.UserList, codeInfo)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package versionSer
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"stat/api/proto"
|
||||
"stat/common/utils"
|
||||
downloadinfo "stat/models/downloadinfo"
|
||||
"stat/models/versionMod"
|
||||
"stat/service/cacheSer"
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/xxjwxc/public/myglobal/snowflake"
|
||||
)
|
||||
|
||||
func GenerateConfigFile(origin, target, configFile, DC, PC string) ([]byte, string) {
|
||||
if len(DC) > 0 && len(PC) > 0 {
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err == nil {
|
||||
id := node.Generate()
|
||||
target = fmt.Sprintf("%s?dc=%s&pc=%s&UID=%d", target, DC, PC, id)
|
||||
}
|
||||
} else if len(DC) > 0 {
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err == nil {
|
||||
id := node.Generate()
|
||||
target = fmt.Sprintf("%s?dc=%s&UID=%d", target, DC, id)
|
||||
}
|
||||
} else if len(PC) > 0 {
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err == nil {
|
||||
id := node.Generate()
|
||||
target = fmt.Sprintf("%s?pc=%s&UID=%d", target, PC, id)
|
||||
}
|
||||
} else {
|
||||
node, err := snowflake.NewNode(1)
|
||||
if err == nil {
|
||||
id := node.Generate()
|
||||
target = fmt.Sprintf("%s?UID=%d", target, id)
|
||||
}
|
||||
}
|
||||
target = fmt.Sprintf("%s&isLight=true", target)
|
||||
output, _, err := readFile("conf/"+configFile, origin, target)
|
||||
if err != nil {
|
||||
output, _ = os.ReadFile("conf/" + configFile)
|
||||
}
|
||||
return output, configFile
|
||||
}
|
||||
|
||||
func GetAppConfig(appId int32) (origin, target, config string) {
|
||||
endpoint := statg.GlobalAppIdEndpoints[appId]
|
||||
if endpoint != nil {
|
||||
origin = endpoint.H5Origin
|
||||
target = endpoint.H5Target
|
||||
config = endpoint.H5Config
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PageButtonClick 落地页点击下载返回最新的下载链接
|
||||
func GetH5Config(ctx context.Context, req *proto.H5ConfigReq, userAgent string, ip string) ([]byte, string) {
|
||||
req.DC = utils.RepairDC(req.DC)
|
||||
downloadInfo := &downloadinfo.DownloadInfoModel{
|
||||
AppId: req.AppId,
|
||||
IP: ip,
|
||||
PC: req.PC,
|
||||
DC: req.DC,
|
||||
SysType: "light",
|
||||
PackageType: versionMod.Light,
|
||||
PeerIP: ip,
|
||||
Domain: req.Domain,
|
||||
UserAgent: userAgent,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
cacheSer.Writers.LandPageClickWriter.Add(downloadInfo)
|
||||
origin, target, config := GetAppConfig(req.AppId)
|
||||
if origin == "" || target == "" || config == "" {
|
||||
return nil, "madou.mobileconfig"
|
||||
}
|
||||
return GenerateConfigFile(origin, target, config, req.DC, req.PC)
|
||||
}
|
||||
|
||||
func readFile(filePath string, origin, target string) ([]byte, bool, error) {
|
||||
var err error
|
||||
var data []byte
|
||||
cacheKey := fmt.Sprintf("h5config#%s", filePath)
|
||||
if cache, exists := cacheSer.Cache.Get(cacheKey); exists {
|
||||
if item, ok := cache.([]byte); ok {
|
||||
data = item
|
||||
}
|
||||
}
|
||||
if data == nil {
|
||||
data, err = os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, data, 0)
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(bytes.NewReader(data))
|
||||
needHandle := false
|
||||
output := make([]byte, 0)
|
||||
for {
|
||||
line, _, err := reader.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return output, needHandle, nil
|
||||
}
|
||||
return nil, needHandle, err
|
||||
}
|
||||
if ok, _ := regexp.Match(origin, line); ok {
|
||||
reg := regexp.MustCompile(origin)
|
||||
newByte := reg.ReplaceAll(line, []byte(target))
|
||||
output = append(output, newByte...)
|
||||
output = append(output, []byte("\n")...)
|
||||
if !needHandle {
|
||||
needHandle = true
|
||||
}
|
||||
} else {
|
||||
output = append(output, line...)
|
||||
output = append(output, []byte("\n")...)
|
||||
}
|
||||
}
|
||||
return output, needHandle, nil
|
||||
}
|
||||
|
||||
func GetH5ConfigV2(ctx context.Context, req *proto.H5ConfigReq, ip string) proto.H5ConfigV2Resp {
|
||||
if req.AppId == 694 {
|
||||
return proto.H5ConfigV2Resp{
|
||||
URL: "http://awll.yangjiaolian.top/91seman.mobileconfig",
|
||||
}
|
||||
}
|
||||
|
||||
return proto.H5ConfigV2Resp{}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
package versionSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"stat/common/crypt"
|
||||
"stat/common/redis"
|
||||
"stat/common/utils"
|
||||
"stat/models/channelmod"
|
||||
"stat/models/channelpackage"
|
||||
"stat/models/versionMod"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func GetChannelApkOutputPath(appId int32, channel string, version string, date time.Time) string {
|
||||
endpoint := statg.GlobalAppIdEndpoints[appId]
|
||||
if endpoint == nil {
|
||||
return ""
|
||||
}
|
||||
if endpoint.NameEn == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(`%s/package/%s_%s_%s-%s.apk`, endpoint.NameEn, endpoint.NameEn, date.Format("20060102150405"), version, channel)
|
||||
}
|
||||
|
||||
func GetPackageBaseDomain(appId int32) []string {
|
||||
packageUrlString := statg.Conf.URL.PackageUrl
|
||||
if appId == 999 && len(statg.Conf.URL.BiliPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.BiliPackageUrl
|
||||
} else if appId == 777 && len(statg.Conf.URL.HuanqiuPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.HuanqiuPackageUrl
|
||||
} else if appId == 444 && len(statg.Conf.URL.ManhuaPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.ManhuaPackageUrl
|
||||
}
|
||||
packageUrlArray := strings.Split(packageUrlString, ";")
|
||||
return packageUrlArray
|
||||
}
|
||||
|
||||
func ApkReportErrorLinks(ctx context.Context, pack *channelpackage.ChannelPackage, msg []string) {
|
||||
if time.Since(pack.CheckLink) > channelpackage.ApkCheckLinkDuration {
|
||||
filter := bson.M{
|
||||
channelpackage.FN.ChannelPackage.Id: pack.Id,
|
||||
channelpackage.FN.ChannelPackage.CheckLink: pack.CheckLink,
|
||||
}
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
channelpackage.FN.ChannelPackage.CheckLink: time.Now(),
|
||||
},
|
||||
}
|
||||
// 更新成功是生成apk的必要条件
|
||||
if err := channelpackage.UpdateOneChannelPackage(ctx, filter, update); err != nil {
|
||||
return
|
||||
}
|
||||
// 汇报TG
|
||||
text := fmt.Sprintf("检测到渠道包链接无法下载:\n")
|
||||
text += fmt.Sprintf("渠道:【%s】\n", pack.Channel)
|
||||
text += fmt.Sprintf("应用:【%s,ID=%d】\n", statg.GlobalEndpoints.GetAppShortNameById(pack.AppId), pack.AppId)
|
||||
text += fmt.Sprintf("版本:【%s】\n", pack.Version)
|
||||
text += fmt.Sprintf("文件:【%s】\n", pack.FileName)
|
||||
text += fmt.Sprintf("生成:【%s】\n", pack.GenTime.Format(time.DateTime))
|
||||
text += fmt.Sprintf("错误: %s\n", msg)
|
||||
text += fmt.Sprintf("提示: /channel_apk -channel=%s -app=%d", pack.Channel, pack.AppId)
|
||||
statg.Bot.SendChannelApkMessage(text)
|
||||
}
|
||||
}
|
||||
|
||||
func ApkCheckLinks(pack *channelpackage.ChannelPackage, domains []string) (bool, []string) {
|
||||
ctx := context.Background()
|
||||
links := pack.GetAllLink(domains)
|
||||
if len(links) <= 0 {
|
||||
return false, links
|
||||
}
|
||||
var msg string
|
||||
for i := 0; i < len(links); i++ {
|
||||
link := links[i]
|
||||
err := CheckPackageLink(ctx, link)
|
||||
if err != nil {
|
||||
msg += fmt.Sprintf("【链接:%s】错误:%s\n", link, err.Error())
|
||||
}
|
||||
}
|
||||
return false, links
|
||||
}
|
||||
|
||||
func TryMakeChannelApk(ctx context.Context, appId int32, channel string, version *versionMod.VersionModel, pack *channelpackage.ChannelPackage, reason string, forceMake bool, logBody io.Writer) (*channelpackage.ChannelPackage, error) {
|
||||
// 检查版本有没有原始链接
|
||||
if len(version.Link) <= 0 {
|
||||
return pack, fmt.Errorf("version %d link is empty", version.Id)
|
||||
}
|
||||
// 先检查渠道是否存在
|
||||
remoteFile := GetChannelApkOutputPath(appId, channel, version.VersionNum, time.Now())
|
||||
chanInfo, err := channelmod.FindOneChannelInfo(ctx, bson.M{
|
||||
channelmod.FN.ChannelInfo.Channel: channel,
|
||||
})
|
||||
if err != nil {
|
||||
return pack, fmt.Errorf("渠道包生成失败,查询渠道【%s】出错: %v", channel, err)
|
||||
}
|
||||
if !chanInfo.Status {
|
||||
return pack, fmt.Errorf("渠道包生成失败,渠道【%s】已被禁用,请启用该渠道后再次尝试", channel)
|
||||
}
|
||||
if !slices.Contains(chanInfo.AppIds, appId) {
|
||||
return pack, fmt.Errorf("渠道包生成失败,应用【%s, ID=%d】未在渠道【%s】代理应用的列表中【%v】", statg.GlobalEndpoints.GetAppShortNameById(appId), appId, channel, chanInfo.AppIds)
|
||||
}
|
||||
// 开始生成
|
||||
lockCode := rand.Int63()
|
||||
packID := primitive.NewObjectID()
|
||||
now := time.Now()
|
||||
if pack == nil {
|
||||
pack = &channelpackage.ChannelPackage{
|
||||
Id: packID,
|
||||
AppId: appId,
|
||||
Channel: channel,
|
||||
Version: version.VersionNum,
|
||||
FileName: version.Md5,
|
||||
UrlPath: remoteFile,
|
||||
Status: channelpackage.ChanApkStatusMaking,
|
||||
Desc: version.Info,
|
||||
Reason: reason,
|
||||
GenError: "第一次生成",
|
||||
GenCount: 0,
|
||||
LockCode: lockCode,
|
||||
LockTime: now,
|
||||
GenTime: now,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
// 插入成功是生成apk的必要条件
|
||||
serr := channelpackage.InsertOneChannelPackage(ctx, pack)
|
||||
if serr != nil {
|
||||
return pack, fmt.Errorf("渠道包生成失败,插入渠道包记录失败: \n渠道:【%s】\n应用:【%s,ID=%d】\n错误: %s", channel, statg.GlobalEndpoints.GetAppShortNameById(appId), appId, serr.Error())
|
||||
}
|
||||
} else {
|
||||
packID = pack.Id
|
||||
if ok, left, tip := pack.CanMake(forceMake); !ok {
|
||||
return pack, fmt.Errorf("渠道包生成失败【%s】,请在过【%v】后,再次尝试生成渠道【%s】的渠道包", tip, left, channel)
|
||||
}
|
||||
filter := bson.M{
|
||||
channelpackage.FN.ChannelPackage.Id: packID,
|
||||
channelpackage.FN.ChannelPackage.LockCode: pack.LockCode,
|
||||
}
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
channelpackage.FN.ChannelPackage.LockCode: lockCode,
|
||||
channelpackage.FN.ChannelPackage.LockTime: time.Now(),
|
||||
channelpackage.FN.ChannelPackage.GenTime: time.Now(),
|
||||
},
|
||||
}
|
||||
// 更新成功是生成apk的必要条件
|
||||
if serr := channelpackage.UpdateOneChannelPackage(ctx, filter, update); serr != nil {
|
||||
return pack, fmt.Errorf("update failed, code:%d, msg:%s", serr.Code, serr.Msg)
|
||||
}
|
||||
}
|
||||
filter := bson.M{
|
||||
channelpackage.FN.ChannelPackage.Id: packID,
|
||||
channelpackage.FN.ChannelPackage.LockCode: lockCode,
|
||||
}
|
||||
err = makeChannelApk(version.Link[0], channel, remoteFile, logBody)
|
||||
if err != nil {
|
||||
// write error
|
||||
update := bson.M{
|
||||
"$inc": bson.M{
|
||||
channelpackage.FN.ChannelPackage.GenCount: 1,
|
||||
},
|
||||
"$set": bson.M{
|
||||
channelpackage.FN.ChannelPackage.LockTime: time.Time{}, // 重置锁
|
||||
channelpackage.FN.ChannelPackage.GenError: err.Error(),
|
||||
channelpackage.FN.ChannelPackage.Status: channelpackage.ChanApkStatusFailure,
|
||||
},
|
||||
}
|
||||
_ = channelpackage.UpdateOneChannelPackage(ctx, filter, update)
|
||||
return pack, err
|
||||
}
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
channelpackage.FN.ChannelPackage.GenError: "success",
|
||||
channelpackage.FN.ChannelPackage.UrlPath: remoteFile,
|
||||
channelpackage.FN.ChannelPackage.FileName: version.Md5,
|
||||
channelpackage.FN.ChannelPackage.Version: version.VersionNum,
|
||||
channelpackage.FN.ChannelPackage.GenTime: time.Now(),
|
||||
channelpackage.FN.ChannelPackage.Reason: reason,
|
||||
channelpackage.FN.ChannelPackage.Desc: version.Info,
|
||||
channelpackage.FN.ChannelPackage.LockTime: time.Time{}, // 重置锁
|
||||
channelpackage.FN.ChannelPackage.Status: channelpackage.ChanApkStatusSuccess,
|
||||
},
|
||||
}
|
||||
if serr := channelpackage.UpdateOneChannelPackage(ctx, filter, update); serr != nil {
|
||||
return pack, fmt.Errorf("update failed,, code:%d, msg:%s", serr.Code, serr.Msg)
|
||||
}
|
||||
|
||||
//if forceMake {
|
||||
// // 自动开启渠道自动打包
|
||||
// update := bson.M{
|
||||
// "$set": bson.M{channelmod.FN.ChannelInfo.AutoGenApk: true},
|
||||
// }
|
||||
// _ = channelmod.UpdateOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channel}, update)
|
||||
//}
|
||||
|
||||
pack2, serr := channelpackage.FindOneChannelPackage(ctx, filter)
|
||||
if serr != nil {
|
||||
return pack, fmt.Errorf("find failed, code:%d, msg:%s", serr.Code, serr.Msg)
|
||||
}
|
||||
return pack2, nil
|
||||
}
|
||||
|
||||
func makeChannelApk(downUrl string, channel string, remoteFile string, logBody io.Writer) error {
|
||||
var out []byte
|
||||
var err error
|
||||
// 生成临时apk文件名
|
||||
step := 1
|
||||
tempPath := os.TempDir()
|
||||
srcName := utils.RandStringBytes(12)
|
||||
dstName := utils.RandStringBytes(12)
|
||||
tmpSrcApk := filepath.Join(tempPath, srcName+".apk")
|
||||
tmpDstPath := filepath.Join(tempPath, dstName)
|
||||
if logBody != nil {
|
||||
logBody.Write([]byte(fmt.Sprintf("tmpSrcApk=%s\n", tmpSrcApk)))
|
||||
logBody.Write([]byte(fmt.Sprintf("tmpDstPath=%s\n", tmpDstPath)))
|
||||
}
|
||||
// 下载apk原始文件
|
||||
step = 2
|
||||
cmd := exec.Command("/usr/bin/curl",
|
||||
"-o",
|
||||
tmpSrcApk,
|
||||
downUrl)
|
||||
out, err = cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w, step=%d", err, step)
|
||||
}
|
||||
if logBody != nil {
|
||||
logBody.Write([]byte(fmt.Sprintf("step=%d, cmd=%s, out=%s\n", step, cmd.String(), string(out))))
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(tmpSrcApk)
|
||||
}()
|
||||
step = 3
|
||||
// 生成渠道包
|
||||
cmd = exec.Command("/usr/bin/java", // 测试服 /tools/jdk/bin/java 正式服 /usr/bin/java
|
||||
"-jar",
|
||||
"/usr/lib/jar/packer-ng-2.0.1.jar",
|
||||
"generate",
|
||||
fmt.Sprintf("--channels=%s", channel),
|
||||
fmt.Sprintf("--output=%s", tmpDstPath),
|
||||
tmpSrcApk)
|
||||
out, err = cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w, step=%d", err, step)
|
||||
}
|
||||
if logBody != nil {
|
||||
logBody.Write([]byte(fmt.Sprintf("step=%d, cmd=%s, out=%s\n", step, cmd.String(), string(out))))
|
||||
}
|
||||
step = 4
|
||||
// 上传到下载服务器
|
||||
fileName := filepath.Join(tmpDstPath, fmt.Sprintf("%s-%s.apk", srcName, channel))
|
||||
defer func() {
|
||||
_ = os.Remove(fileName)
|
||||
}()
|
||||
cmd = exec.Command("/usr/bin/curl",
|
||||
"-T",
|
||||
fileName,
|
||||
"-u",
|
||||
"213apk:5JLm6dKS42Nsxmif",
|
||||
fmt.Sprintf("ftp://216.83.58.213:21/%s", remoteFile))
|
||||
out, err = cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w, step=%d", err, step)
|
||||
}
|
||||
if logBody != nil {
|
||||
logBody.Write([]byte(fmt.Sprintf("step=%d, cmd=%s, out=%s\n", step, cmd.String(), string(out))))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendLinkErrorMessageToBot(text string) {
|
||||
defer utils.RecoverError(nil)
|
||||
key := crypt.StrToMd5(text)
|
||||
hash := redis.NewHash(redis.StatRds.Client(), redis.GetDownloadCheckBotMsgHashKey())
|
||||
_, _ = hash.Expire(time.Hour)
|
||||
if exists, err := hash.Exists(key); err == nil && !exists {
|
||||
_ = statg.Bot.SendMonitorMessage(text)
|
||||
_, _ = hash.Set(key, text)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckChannelApkAutoGen(ctx context.Context, appId int32, channel string, version *versionMod.VersionModel, pack *channelpackage.ChannelPackage) {
|
||||
var err error
|
||||
if apkGenLock.LockedCount() > channelpackage.ApkGenerateParallelMax {
|
||||
return // 不能超出并行任务数
|
||||
}
|
||||
lockKey := fmt.Sprintf("id=%d#ch=%s", appId, channel)
|
||||
expired := time.Now().Add(5 * time.Minute)
|
||||
if locked := apkGenLock.TryLock(lockKey, expired); locked {
|
||||
defer apkGenLock.UnLock(lockKey, expired)
|
||||
bEnable := statg.Conf.ApkGenEnable
|
||||
if !bEnable {
|
||||
channelInfo, _ := channelmod.FindOneChannelInfo(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: channel})
|
||||
if channelInfo != nil && channelInfo.AutoGenApk {
|
||||
bEnable = true
|
||||
}
|
||||
}
|
||||
if bEnable {
|
||||
canMake := pack == nil
|
||||
if !canMake {
|
||||
canMake, _, _ = pack.CanMake(false)
|
||||
}
|
||||
if canMake {
|
||||
go func() {
|
||||
defer utils.RecoverError(nil)
|
||||
pack, err = TryMakeChannelApk(ctx, appId, channel, version, pack, "", false, nil)
|
||||
if err == nil && pack != nil {
|
||||
text := fmt.Sprintf("渠道包已全自动生成:\n")
|
||||
text += fmt.Sprintf("渠道:【%s】\n", pack.Channel)
|
||||
text += fmt.Sprintf("应用:【%s,ID=%d】\n", statg.GlobalEndpoints.GetAppShortNameById(pack.AppId), pack.AppId)
|
||||
text += fmt.Sprintf("版本:【%s】\n", version.VersionNum)
|
||||
text += fmt.Sprintf("文件:【%s】\n", version.Md5)
|
||||
text += fmt.Sprintf("发布:【%s】\n", version.CreatedAt.Format(time.DateTime))
|
||||
text += fmt.Sprintf("生成:【%s】\n", pack.GenTime.Format(time.DateTime))
|
||||
baseUrls := GetPackageBaseDomain(pack.AppId)
|
||||
for index, baseUrl := range baseUrls {
|
||||
text += fmt.Sprintf("下载链接-%d: %s\n", index, pack.GetRandomLink([]string{baseUrl}))
|
||||
}
|
||||
statg.Bot.SendChannelApkMessage(text)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
package versionSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"stat/api/proto"
|
||||
"stat/common/stdLog"
|
||||
"stat/common/utils"
|
||||
"stat/models/channelpackage"
|
||||
"stat/models/downloadCpc"
|
||||
downloadinfo "stat/models/downloadinfo"
|
||||
"stat/models/versionMod"
|
||||
"stat/service/cacheSer"
|
||||
"stat/service/crossSer"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
type ChannelParams struct {
|
||||
PC string `json:"i" bson:"i"` //推广码
|
||||
DC string `json:"dc" bson:"dc"` //商区序列
|
||||
CH string `json:"ch" bson:"ch"` //渠道
|
||||
}
|
||||
|
||||
var (
|
||||
appSvrUrlDict = make(map[int32]string)
|
||||
appSvrIdxDict = make(map[int32]statg.ServerIndex)
|
||||
once sync.Once
|
||||
apkGenLock = utils.NewExpiredLock()
|
||||
landPageVisit = utils.NewExpiredLock()
|
||||
landPageClick = utils.NewExpiredLock()
|
||||
landPageVisitIpTimeout = time.Hour // 每个IP,1个小时内无论访问多少次,都算一次
|
||||
landPageClickIpTimeout = time.Hour // 每个IP,1个小时内无论访问多少次,都算一次
|
||||
)
|
||||
|
||||
func Init() {
|
||||
once.Do(func() {
|
||||
for _, endpoint := range statg.GlobalEndpoints {
|
||||
appSvrIdxDict[endpoint.AppId] = endpoint.OwnerIndex
|
||||
if url, ok := statg.Conf.InnerServers[endpoint.OwnerIndex]; ok {
|
||||
appSvrUrlDict[endpoint.AppId] = url
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// CrossButtonClick 跨服务器点击下载返回最新的下载链接
|
||||
func CrossButtonClick(ctx context.Context, userAgent string, peerIP string, req *proto.PageButtonClickReq) (resp *proto.PageButtonClickResp, err error) {
|
||||
req.Ip = peerIP
|
||||
srvIdx := appSvrIdxDict[req.AppId]
|
||||
if srvIdx == statg.ServerIndexNone {
|
||||
return nil, fmt.Errorf("appid %d not found", req.AppId)
|
||||
}
|
||||
if srvIdx == statg.Conf.ServerIndex {
|
||||
return PageButtonClick(ctx, peerIP, userAgent, req)
|
||||
}
|
||||
addr := appSvrUrlDict[req.AppId]
|
||||
if addr == "" {
|
||||
return nil, fmt.Errorf("appid %d server not found", req.AppId)
|
||||
}
|
||||
res := crossSer.CrossAppDownloadRes{}
|
||||
err = crossSer.InnerRequest(addr+"/api/stat/inner/appDownload", req, &res, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Data == nil {
|
||||
return nil, fmt.Errorf("data not found")
|
||||
}
|
||||
return res.Data, nil
|
||||
}
|
||||
|
||||
// PageButtonClick 落地页点击下载返回最新的下载链接
|
||||
func PageButtonClick(ctx context.Context, userAgent string, peerIP string, req *proto.PageButtonClickReq) (resp *proto.PageButtonClickResp, err error) {
|
||||
resp = &proto.PageButtonClickResp{}
|
||||
|
||||
var channelCode ChannelParams
|
||||
// TODO 存储记录
|
||||
_ = json.Unmarshal([]byte(req.Channel), &channelCode)
|
||||
|
||||
if req.Ip == "" {
|
||||
req.Ip = peerIP
|
||||
}
|
||||
channelCode.DC = utils.RepairDC(channelCode.DC)
|
||||
|
||||
if req.Terminal == "ios" && req.PackageType == versionMod.Light {
|
||||
var h5Req = &proto.H5ConfigReq{
|
||||
AppId: req.AppId,
|
||||
DC: channelCode.DC,
|
||||
PC: channelCode.PC,
|
||||
Domain: channelCode.PC,
|
||||
}
|
||||
resp.Terminal = req.Terminal
|
||||
resp.PackageType = req.PackageType
|
||||
resp.Status = true
|
||||
resp.LightData, _ = GetH5Config(ctx, h5Req, userAgent, req.Ip)
|
||||
return
|
||||
}
|
||||
|
||||
downloadInfo := &downloadinfo.DownloadInfoModel{
|
||||
AppId: req.AppId,
|
||||
IP: req.Ip,
|
||||
Domain: req.Domain,
|
||||
PC: channelCode.PC,
|
||||
DC: channelCode.DC,
|
||||
CH: channelCode.CH,
|
||||
SysType: strings.ToLower(strings.TrimSpace(req.Terminal)),
|
||||
PackageType: req.PackageType,
|
||||
PeerIP: peerIP,
|
||||
UserAgent: userAgent,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
//和APP统一
|
||||
if req.PackageType == versionMod.Shop {
|
||||
downloadInfo.SysType = "h5_ios"
|
||||
}
|
||||
|
||||
if landPageClick.TryLock(req.Ip, time.Now().Add(landPageClickIpTimeout)) {
|
||||
cacheSer.Writers.LandPageClickWriter.Add(downloadInfo)
|
||||
}
|
||||
if downloadInfo.IP == "" {
|
||||
downloadInfo.IP = peerIP
|
||||
}
|
||||
|
||||
var data *versionMod.VersionModel
|
||||
cacheKey := fmt.Sprintf("version_button#appId:%d#%s#%d", req.AppId, req.Terminal, req.PackageType)
|
||||
if cache, exists := cacheSer.Cache.Get(cacheKey); exists {
|
||||
if item, ok := cache.(*versionMod.VersionModel); ok {
|
||||
data = item
|
||||
}
|
||||
}
|
||||
if data == nil {
|
||||
data, err = versionMod.ButtonClick(ctx, req.AppId, req.Terminal, req.PackageType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, data, 2*time.Minute)
|
||||
}
|
||||
|
||||
resp.PackageType = data.PackageType
|
||||
resp.Status = data.Status
|
||||
resp.Terminal = data.Terminal
|
||||
resp.VersionNum = data.VersionNum
|
||||
resp.BackUrl = data.BackLink
|
||||
resp.Link = append(resp.Link, data.Link...)
|
||||
|
||||
// IOS的TF包管理
|
||||
if req.PackageType == versionMod.Tf {
|
||||
// 1. FT包管理
|
||||
|
||||
for _, tfInfo := range data.TfInfo {
|
||||
one := proto.TfInfo{
|
||||
AppName: tfInfo.AppName,
|
||||
AppIcon: tfInfo.AppIcon,
|
||||
URL: tfInfo.URL,
|
||||
}
|
||||
resp.Tf = append(resp.Tf, one)
|
||||
}
|
||||
|
||||
if len(resp.Tf) == 0 {
|
||||
resp.Tf = make([]proto.TfInfo, 0)
|
||||
for _, v := range data.Link {
|
||||
tmp := proto.TfInfo{
|
||||
URL: v,
|
||||
}
|
||||
if len(data.BackLink) > 0 {
|
||||
tmp.BackUrl = data.BackLink
|
||||
}
|
||||
resp.Tf = append(resp.Tf, tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if data.Terminal == "ios" {
|
||||
// 配置多个随机返回一个
|
||||
if len(data.Link) >= 1 {
|
||||
resp.Link = make([]string, 0)
|
||||
index := rand.Intn(len(data.Link))
|
||||
resp.Link = append(resp.Link, data.Link[index])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 新增批量打包逻辑
|
||||
if req.Terminal == "android" && len(data.Md5) > 0 {
|
||||
randTime := int64(60*60) + int64(rand.Intn(60*60))
|
||||
|
||||
requestPackageTime := time.Now().Add(-time.Second * time.Duration(randTime))
|
||||
// 下载链接
|
||||
projectString := ""
|
||||
beginPath := ""
|
||||
pathList := strings.Split(strings.Replace(data.Md5, ".apk", "", -1), "_")
|
||||
if len(pathList) == 3 {
|
||||
beginPath = strings.TrimSpace(pathList[len(pathList)-1])
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
endpoint := statg.GlobalAppIdEndpoints[req.AppId]
|
||||
if endpoint != nil {
|
||||
projectString = fmt.Sprintf("%s/out/%s/%s%s_", endpoint.NameEn, beginPath, endpoint.NameEn, requestPackageTime.Format("20060102")[2:])
|
||||
}
|
||||
if projectString != "" {
|
||||
packageUrlString := statg.Conf.URL.PackageUrl
|
||||
if req.AppId == 999 && len(statg.Conf.URL.BiliPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.BiliPackageUrl
|
||||
} else if req.AppId == 777 && len(statg.Conf.URL.HuanqiuPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.HuanqiuPackageUrl
|
||||
} else if req.AppId == 444 && len(statg.Conf.URL.ManhuaPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.ManhuaPackageUrl
|
||||
}
|
||||
|
||||
packageUrlArray := strings.Split(packageUrlString, ";")
|
||||
|
||||
if len(packageUrlArray) > 0 {
|
||||
if len(channelCode.DC) > 0 { //&&
|
||||
pack, serr := channelpackage.FindOneChannelPackage(ctx, bson.M{
|
||||
channelpackage.FN.ChannelPackage.AppId: req.AppId,
|
||||
channelpackage.FN.ChannelPackage.Channel: channelCode.DC,
|
||||
channelpackage.FN.ChannelPackage.Version: data.VersionNum,
|
||||
channelpackage.FN.ChannelPackage.FileName: data.Md5,
|
||||
})
|
||||
if serr == nil || serr.Code == stdLog.DataNotExist {
|
||||
if pack != nil && pack.Status == channelpackage.ChanApkStatusSuccess {
|
||||
pass, links := ApkCheckLinks(pack, packageUrlArray)
|
||||
if pass {
|
||||
resp.Link = links
|
||||
_ = pack.IncDownCount(ctx)
|
||||
return resp, nil
|
||||
} else {
|
||||
ApkReportErrorLinks(ctx, pack, links)
|
||||
}
|
||||
} else {
|
||||
CheckChannelApkAutoGen(ctx, req.AppId, channelCode.DC, data, pack)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 默认使用第一个
|
||||
passedLinks := make([]string, 0)
|
||||
for _, packageLink := range packageUrlArray {
|
||||
link := strings.TrimSpace(packageLink + projectString + data.Md5)
|
||||
checklink := link
|
||||
cerr := CheckPackageLink(ctx, checklink)
|
||||
if cerr != nil {
|
||||
// log.Error("AppVersion", log.Any("link", link), log.E(cerr))
|
||||
// if statg.Conf.RunMod == consts.RunModRelease && !slices.Contains([]int32{666, 998}, req.AppId) {
|
||||
// appDict, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
// text := fmt.Sprintf("【%s】Android 下载链接异常:\n链接:%s\n错误:%s\n", appDict[req.AppId], link, cerr.Error())
|
||||
// SendLinkErrorMessageToBot(text)
|
||||
// }
|
||||
} else {
|
||||
passedLinks = append(passedLinks, link)
|
||||
}
|
||||
}
|
||||
if len(passedLinks) > 0 {
|
||||
resp.Link = passedLinks
|
||||
}
|
||||
} else {
|
||||
link := strings.TrimSpace(packageUrlString + projectString + data.Md5)
|
||||
resp.Link = []string{link}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// PageButtonList 落地页获取下载按钮
|
||||
func PageButtonList(ctx context.Context, isIos bool, peerIP string, req *proto.PageButtonListReq) (resp *proto.PageButtonListResp, err error) {
|
||||
resp = &proto.PageButtonListResp{
|
||||
IosTF: false,
|
||||
}
|
||||
var channelCode ChannelParams
|
||||
// TODO 存储记录
|
||||
_ = json.Unmarshal([]byte(req.Channel), &channelCode)
|
||||
|
||||
channelCode.DC = utils.RepairDC(channelCode.DC)
|
||||
|
||||
req.Ip = peerIP
|
||||
|
||||
if req.Ip != "" {
|
||||
logInfo := &downloadCpc.DownloadCpc{
|
||||
AppId: req.AppId,
|
||||
IP: req.Ip,
|
||||
PC: channelCode.PC,
|
||||
DC: channelCode.DC,
|
||||
CH: channelCode.CH,
|
||||
PeerIP: peerIP,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if landPageVisit.TryLock(req.Ip, time.Now().Add(landPageVisitIpTimeout)) {
|
||||
cacheSer.Writers.LandPageVisitWriter.Add(logInfo)
|
||||
}
|
||||
}
|
||||
cacheKey := fmt.Sprintf("version_button_list#appId:%d", req.AppId)
|
||||
var buttons versionMod.VersionList
|
||||
if cache, exists := cacheSer.Cache.Get(cacheKey); exists {
|
||||
if list, ok := cache.(versionMod.VersionList); ok {
|
||||
buttons = list
|
||||
}
|
||||
}
|
||||
if buttons == nil {
|
||||
buttons, err = versionMod.ButtonList(ctx, req.AppId)
|
||||
if err != nil {
|
||||
err = errors.New("服务器内部错误")
|
||||
return
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, buttons, 0)
|
||||
}
|
||||
|
||||
if len(buttons) < 1 {
|
||||
err = errors.New("请开启版本")
|
||||
return
|
||||
}
|
||||
|
||||
for _, value := range buttons {
|
||||
if isIos && value.Terminal == "android" {
|
||||
// 如果是ios设备请求,记录是android 跳过
|
||||
continue
|
||||
}
|
||||
if value.PackageType == versionMod.Enterprise && value.Terminal == "ios" {
|
||||
resp.IosDown = true
|
||||
}
|
||||
if value.PackageType == versionMod.Shop && value.Terminal == "ios" {
|
||||
resp.IosStore = true
|
||||
if len(value.Link) > 0 {
|
||||
resp.IosLink = value.Link[0]
|
||||
}
|
||||
}
|
||||
if value.PackageType == versionMod.Tf && value.Terminal == "ios" {
|
||||
resp.IosTF = true
|
||||
}
|
||||
if value.PackageType == versionMod.Light && value.Terminal == "ios" {
|
||||
resp.IosH5 = true
|
||||
}
|
||||
}
|
||||
// android下载按钮
|
||||
packageUrlString := statg.Conf.URL.PackageUrl
|
||||
if req.AppId == 999 && len(statg.Conf.URL.BiliPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.BiliPackageUrl
|
||||
} else if req.AppId == 777 && len(statg.Conf.URL.HuanqiuPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.HuanqiuPackageUrl
|
||||
} else if req.AppId == 444 && len(statg.Conf.URL.ManhuaPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.ManhuaPackageUrl
|
||||
}
|
||||
packageUrlArray := strings.Split(packageUrlString, ";")
|
||||
resp.AndroidButton = len(packageUrlArray)
|
||||
// 是否有渠道包
|
||||
var version *versionMod.VersionModel
|
||||
versionKey := fmt.Sprintf("version_button#appId:%d#%s#%d", req.AppId, "android", 0)
|
||||
if cache, exists := cacheSer.Cache.Get(versionKey); exists {
|
||||
if item, ok := cache.(*versionMod.VersionModel); ok {
|
||||
version = item
|
||||
}
|
||||
}
|
||||
if version == nil {
|
||||
version, err = versionMod.ButtonClick(ctx, req.AppId, "android", 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cacheSer.Cache.Set(cacheKey, version, 2*time.Minute)
|
||||
}
|
||||
|
||||
pack, serr := channelpackage.FindOneChannelPackage(ctx, bson.M{
|
||||
channelpackage.FN.ChannelPackage.AppId: req.AppId,
|
||||
channelpackage.FN.ChannelPackage.Channel: channelCode.DC,
|
||||
channelpackage.FN.ChannelPackage.Version: version.VersionNum,
|
||||
channelpackage.FN.ChannelPackage.FileName: version.Md5,
|
||||
})
|
||||
if serr != nil && serr.Code != stdLog.DataNotExist {
|
||||
return nil, errors.New(serr.Msg)
|
||||
}
|
||||
if pack != nil && pack.Status == channelpackage.ChanApkStatusSuccess {
|
||||
resp.HasChannelApk = true
|
||||
}
|
||||
|
||||
// ----------------End---------------
|
||||
|
||||
resp.Contact = proto.ContactInfo{
|
||||
Telegram: "https://t.me/madoutiktok",
|
||||
Paoto: "https://ptcc.in/madouyingshi",
|
||||
Madou: "http://madou101.com",
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package versionSer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"stat/api/proto"
|
||||
"stat/common/enum/consts"
|
||||
"stat/common/genId"
|
||||
"stat/common/log"
|
||||
"stat/common/redis"
|
||||
"stat/common/tf"
|
||||
"stat/common/utils"
|
||||
"stat/models/appInfoMod"
|
||||
"stat/models/versionMod"
|
||||
"stat/statg"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caixw/lib.go/encoding/version"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
// ListVersion 获取版本信息
|
||||
func ListVersion(ctx context.Context, req *proto.VersionListReq) (resp *proto.VersionListResp, err error) {
|
||||
resp = &proto.VersionListResp{
|
||||
Data: make([]*proto.VersionInfo, 0),
|
||||
}
|
||||
|
||||
skip, limit := utils.ToDBPage(req.PageNum, req.PageSize)
|
||||
|
||||
data, count, err := versionMod.GetVersionList(ctx, req.AppId, skip, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appInfoList, err := appInfoMod.GetAllAppInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
versionInfo := proto.VersionInfo{
|
||||
Id: v.Id,
|
||||
AppId: v.AppId,
|
||||
VersionNum: v.VersionNum,
|
||||
Terminal: v.Terminal,
|
||||
Link: v.Link,
|
||||
Info: v.Info,
|
||||
PackageType: v.PackageType,
|
||||
IsForceUpdate: v.IsForceUpdate,
|
||||
MinVersion: v.MinVersion,
|
||||
Status: v.Status,
|
||||
Size: v.Size,
|
||||
Md5: v.Md5,
|
||||
BackLink: v.BackLink,
|
||||
}
|
||||
for _, appinfo := range appInfoList {
|
||||
if appinfo.AppId == v.AppId {
|
||||
versionInfo.AppName = appinfo.Name
|
||||
}
|
||||
}
|
||||
resp.Data = append(resp.Data, &versionInfo)
|
||||
}
|
||||
resp.Count = count
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteVersion 删除版本
|
||||
func DeleteVersion(ctx context.Context, id int64) (err error) {
|
||||
err = versionMod.DelVersion(ctx, id)
|
||||
return
|
||||
}
|
||||
|
||||
// AddVersion 添加版本
|
||||
func AddVersion(ctx context.Context, req *proto.VersionAddReq) (err error) {
|
||||
model := &versionMod.VersionModel{}
|
||||
|
||||
// TODO:修改成指针
|
||||
err = copier.Copy(model, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
model.TfInfo = make([]versionMod.TfInfo, 0)
|
||||
if req.PackageType == versionMod.Tf {
|
||||
for _, url := range req.Link {
|
||||
tmp := versionMod.TfInfo{
|
||||
URL: strings.TrimSpace(url),
|
||||
}
|
||||
if req.PackageType == versionMod.Tf {
|
||||
result, err := tf.CheckTFValid(url)
|
||||
if err != nil {
|
||||
log.Error("runCheckUrl CheckTFValid", log.Any("url", url), log.E(err))
|
||||
continue
|
||||
}
|
||||
tmp.AppIcon = result.AppIcon
|
||||
tmp.AppName = result.AppName
|
||||
}
|
||||
model.TfInfo = append(model.TfInfo, tmp)
|
||||
}
|
||||
}
|
||||
model.VersionNum = strings.TrimSpace(model.VersionNum)
|
||||
model.CreatedAt = time.Now()
|
||||
model.UpdatedAt = time.Now()
|
||||
model.Id = genId.GetNextVersionId()
|
||||
|
||||
err = versionMod.AddVersion(ctx, model)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteVersion 删除版本
|
||||
func UpdateVersion(ctx context.Context, req *proto.VersionUpdateReq) (err error) {
|
||||
model := &versionMod.VersionModel{}
|
||||
err = copier.Copy(model, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
model.TfInfo = make([]versionMod.TfInfo, 0)
|
||||
if req.PackageType != nil && *req.PackageType == versionMod.Tf {
|
||||
for _, url := range *req.Link {
|
||||
tmp := versionMod.TfInfo{
|
||||
URL: strings.TrimSpace(url),
|
||||
}
|
||||
if *req.PackageType == versionMod.Tf {
|
||||
result, err := tf.CheckTFValid(url)
|
||||
if err != nil {
|
||||
log.Error("runCheckUrl CheckTFValid", log.Any("url", url), log.E(err))
|
||||
continue
|
||||
}
|
||||
tmp.AppIcon = result.AppIcon
|
||||
tmp.AppName = result.AppName
|
||||
}
|
||||
model.TfInfo = append(model.TfInfo, tmp)
|
||||
}
|
||||
}
|
||||
|
||||
model.UpdatedAt = time.Now()
|
||||
|
||||
err = versionMod.UpdateVersion(ctx, model)
|
||||
return
|
||||
}
|
||||
|
||||
func AppVersion(ctx context.Context, req *proto.AppVersionReq) (resp *proto.AppVersionResp, err error) {
|
||||
var list1 []*versionMod.VersionModel
|
||||
var list []*versionMod.VersionModel
|
||||
resp = &proto.AppVersionResp{}
|
||||
|
||||
if req.SysType == "android" {
|
||||
list1, err = versionMod.FindVersByPkgTyps(ctx, req.AppId, req.SysType, req.CurVersion, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// ios 包
|
||||
list1, err = versionMod.FindIosVersion(ctx, req.AppId, req.SysType, req.CurVersion, []versionMod.PkgType{versionMod.Shop})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
list = make([]*versionMod.VersionModel, 0)
|
||||
for _, v := range list1 {
|
||||
if version.Compare(v.VersionNum, req.CurVersion) > 0 {
|
||||
list = append(list, v)
|
||||
} else if req.CurVersion == "9.9.9" {
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
if len(list) == 0 || len(req.CurVersion) == 0 || req.CurVersion == "null" {
|
||||
resp.HasNewVersion = false
|
||||
return
|
||||
}
|
||||
|
||||
var minVersion string
|
||||
for _, value := range list {
|
||||
if len(value.MinVersion) > 0 && value.MinVersion > minVersion {
|
||||
minVersion = value.MinVersion
|
||||
break
|
||||
} else if value.IsForceUpdate {
|
||||
minVersion = value.VersionNum
|
||||
}
|
||||
}
|
||||
|
||||
resp.HasNewVersion = true
|
||||
resp.ServerVersion = list[0].VersionNum
|
||||
resp.DownloadLink = list[0].Link
|
||||
resp.Description = list[0].Info
|
||||
resp.IsForceUpdate = list[0].IsForceUpdate
|
||||
|
||||
if len(minVersion) > 0 {
|
||||
if minVersion > req.CurVersion {
|
||||
resp.IsForceUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(resp.DownloadLink) > 0 && len(resp.DownloadLink[0]) < 2 && list[0].Terminal == "ios" && len(list[0].BackLink) > 2 {
|
||||
resp.DownloadLink = []string{list[0].BackLink}
|
||||
return
|
||||
}
|
||||
|
||||
// // 新增批量打包逻辑
|
||||
if req.SysType == "android" && len(list[0].Md5) > 0 {
|
||||
// 包名称:com.pf.ys201114
|
||||
randTime := int64(60*60) + int64(rand.Intn(60*60))
|
||||
|
||||
requestPackageTime := time.Now().Add(-time.Second * time.Duration(randTime)) // 报毒包随机
|
||||
// 下载链接
|
||||
projectString := ""
|
||||
beginPath := ""
|
||||
pathList := strings.Split(strings.Replace(list[0].Md5, ".apk", "", -1), "_")
|
||||
if len(pathList) > 0 {
|
||||
beginPath = strings.TrimSpace(pathList[len(pathList)-1])
|
||||
} else {
|
||||
return
|
||||
}
|
||||
endpoint := statg.GlobalAppIdEndpoints[req.AppId]
|
||||
if endpoint != nil {
|
||||
projectString = fmt.Sprintf("%s/out/%s/%s%s_", endpoint.NameEn, beginPath, endpoint.NameEn, requestPackageTime.Format("20060102")[2:])
|
||||
}
|
||||
if projectString != "" {
|
||||
packageUrlString := statg.Conf.URL.PackageUrl
|
||||
if req.AppId == 999 && len(statg.Conf.URL.BiliPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.BiliPackageUrl
|
||||
} else if req.AppId == 777 && len(statg.Conf.URL.HuanqiuPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.HuanqiuPackageUrl
|
||||
} else if req.AppId == 444 && len(statg.Conf.URL.ManhuaPackageUrl) > 0 {
|
||||
packageUrlString = statg.Conf.URL.ManhuaPackageUrl
|
||||
}
|
||||
packageUrlArray := strings.Split(packageUrlString, ";")
|
||||
packageUrl := packageUrlString
|
||||
if len(packageUrlArray) > 0 {
|
||||
// 默认使用第一个
|
||||
packageUrl = packageUrlArray[0]
|
||||
}
|
||||
link := strings.TrimSpace(packageUrl + projectString + list[0].Md5)
|
||||
|
||||
checklink := link
|
||||
cerr := CheckPackageLink(ctx, checklink)
|
||||
if cerr != nil {
|
||||
log.Error("AppVersion", log.Any("link", link), log.E(cerr))
|
||||
if statg.Conf.RunMod == consts.RunModRelease {
|
||||
// 比特币和溅射淫行下载链接异常不需要报警
|
||||
if req.AppId != 998 {
|
||||
//text := fmt.Sprintf("[警告]:应用名称:统计中心\nandroid下载链接异常: 版本:%s-->%s\n链接%s %s\n\n", req.CurVersion, list[0].VersionNum, link, cerr.Error())
|
||||
appDict, _ := appInfoMod.GetAllAppIdNameMap(ctx)
|
||||
text := fmt.Sprintf("【%s】Android下载链接异常:\n链接:%s\n错误:%s\n", appDict[req.AppId], link, cerr.Error())
|
||||
SendLinkErrorMessageToBot(text)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resp.DownloadLink = []string{link}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CheckPackageLink 下载链接下载检测
|
||||
func CheckPackageLink(ctx context.Context, url_https string) (err error) {
|
||||
url := strings.Replace(url_https, "https", "http", -1)
|
||||
redisKey := fmt.Sprintf("channelpackagecheck:%s", url)
|
||||
status, _ := redis.StatRds.Exists(redisKey)
|
||||
if status { // 存在
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检测下载地址
|
||||
resp, err := http.Head(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusGatewayTimeout {
|
||||
return fmt.Errorf("file is %d ", resp.StatusCode)
|
||||
}
|
||||
_ = redis.StatRds.Set(redisKey, "1", 24*time.Hour)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewChannelDownLoadLink(ctx context.Context, req *proto.ChannelDownLoadLinkReq) (resp *proto.ChannelDownLoadLinkResp) {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user