forked from all_user/stat_center
ChannelStat 按 (channel, sumDate, appId) 三维存储,但子渠道累加到父渠道的
匹配条件只比了 ParentChannel 和 SumDate,漏了 AppId。
当父渠道在多个 app 下都有记录时(如 appId=[2,3,4] 查同一渠道码),一个子渠道
的数据会被同时累加进该 (channel, sumDate) 下所有 app 的父渠道行,选几个 app
就多加几倍。settlement 的三处子查询本身也没有 appId 约束,父列表虽锁定单个
appId,子列表却跨 app,同样会把其他 app 的子渠道数据算进结算金额。
5 处匹配条件统一补上 child.AppId == parent.AppId:
- stat.go:162 SearchChannelStat (列表接口,多值 appId)
- settlement.go:200 ChannelSettlementCalc (结算计算,影响金额)
- settlement.go:327 SearchChannelSettlementStat2 (原本父子共用同一 filter
而侥幸正确,补上以免后续改动破坏该前提)
- settlement.go:857 AddSettlementInfo2 (影响金额)
- settlement.go:1138 DetailSettlementInfo
本次只改匹配条件。settlement 三处 childFilter 缺少 appId 约束的冗余查询,
以及 SearchChannelStat 在无 start/end 时 resp.Total 硬编码为 1000000、
withChild 累加被 canSum 跳过的问题,均未处理。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
322 lines
9.6 KiB
Go
322 lines
9.6 KiB
Go
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 && item.AppId == v.AppId && 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
|
|
}
|