package channelSer import ( "context" "errors" "fmt" "os" "path" "stat/api/proto" "stat/common/log" "stat/common/mongos" "stat/common/utils" "stat/middleware/define" "stat/models/appInfoMod" "stat/models/channelmod" "stat/models/channelsettlementrecord" "strings" "time" "go.mongodb.org/mongo-driver/mongo" "github.com/360EntSecGroup-Skylar/excelize" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) // SearchChannelSettlementStat 查询渠道结算纤细 func SearchChannelSettlementStat(ctx context.Context, req *proto.ChannelStatSettlementSearch) (*proto.ChannelStatSettlementSearchResp, error) { resp := &proto.ChannelStatSettlementSearchResp{} 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 = bson.M{channelmod.FN.ChannelStat.Channel: *req.Channel} } } if req.AppId != nil { filter[channelmod.FN.ChannelStat.AppId] = *req.AppId } if req.UserName != nil && *req.UserName != "" { filter[channelmod.FN.ChannelStat.UserName] = *req.UserName } if req.Settlement != nil && *req.Settlement != "" { filter[channelmod.FN.ChannelStat.Settlement] = *req.Settlement } // 开始时间和结束时间 endTime := utils.BeginOfToday().AddDate(0, 0, -1) if req.Start != nil { if req.End != nil { if req.End.Before(utils.BeginOfToday()) { endTime = *req.End } } filter[channelmod.FN.ChannelStat.SumDate] = bson.M{"$gte": *req.Start, "$lte": endTime} } else { if req.End != nil { if req.End.Before(utils.BeginOfToday()) { endTime = *req.End } } filter[channelmod.FN.ChannelStat.SumDate] = bson.M{"$lte": endTime} } filter[channelmod.FN.ChannelStat.IsPay] = bson.M{"$ne": true} totalParentChannelStatInfo, err := channelmod.FindManyChannelStat(ctx, filter) if err != nil { return nil, err } if len(totalParentChannelStatInfo) == 0 { return resp, nil } ChannelStatList, err := channelmod.FindManyChannelStat(ctx, filter, opt) if err != nil { return nil, err } 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 } // 计算合计数据 for _, channelStatInfo := range totalParentChannelStatInfo { channelmod.ChannelStatSummation(&resp.TotalInfo, channelStatInfo) } resp.Total = int64(len(totalParentChannelStatInfo)) resp.List = ChannelStatList if ruleInfo != nil { resp.Income, resp.Desc = channelmod.CalculateChannelIncome(resp.TotalInfo.CorrectValidData()) } return resp, nil } // ChannelSettlementCalc 渠道结算项实时计算 func ChannelSettlementCalc(ctx context.Context, req *proto.SettlementCalcReq) (*proto.SettlementCalcResp, error) { // 参数检查 if len(req.SettleIds) <= 0 { return nil, errors.New("没有提交要结算的统计id数组") } // 在已结算表中检查是否有重复结算 settlementInfoFilter := bson.M{ channelsettlementrecord.FN.ChannelSettlementRecord.SettleIds: bson.M{"$in": req.SettleIds}, } settlementInfoList, err := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, settlementInfoFilter) if err != nil { return nil, err } if len(settlementInfoList) > 0 { return nil, errors.New("提交的统计记录列表中含有重复结算项") } // 获取扣量规则 ruleFilter := bson.M{ channelmod.FN.ChannelDeductionRule.ID: req.RuleId, channelmod.FN.ChannelDeductionRule.AppId: req.AppId, channelmod.FN.ChannelDeductionRule.Channel: req.Channel, } channelRule, err1 := channelmod.FindOneChannelDeductionRuleEx(ctx, ruleFilter) // 检查扣量规则 if err1 != nil { return nil, fmt.Errorf("查找渠道结算规则失败:%s", err1.Error()) } // 开始时间和结束时间 req.BeginTime, req.EndTime, err = CheckAndFixTime(req.BeginTime, req.EndTime) if err != nil { return nil, err } // 一级记录查找 opt := &options.FindOptions{} opt.SetSort(bson.M{channelmod.FN.ChannelStat.SumDate: 1}) parentFilter := bson.M{ channelmod.FN.ChannelStat.AppId: req.AppId, // 应用限定 channelmod.FN.ChannelStat.Channel: req.Channel, // 渠道限定 channelmod.FN.ChannelStat.Type: channelmod.ChannelTypeParent, // 类型限定 channelmod.FN.ChannelStat.IsPay: bson.M{"$ne": true}, // 未结算过 channelmod.FN.ChannelStat.SumDate: bson.M{"$gte": req.BeginTime, "$lte": req.EndTime}, // 时间限定 channelmod.FN.ChannelStat.ID: bson.M{"$in": req.SettleIds}, // 提交的IDs } ParentList, err := channelmod.FindManyChannelStat(ctx, parentFilter, opt) if err != nil { return nil, err } if len(ParentList) != len(req.SettleIds) { return nil, fmt.Errorf("提交的统计记录条数[%d]和查找到的[%d]不一致", len(req.SettleIds), len(ParentList)) } // 结算项检查 for _, stat := range ParentList { if stat.Type != channelmod.ChannelTypeParent { return nil, fmt.Errorf("子渠道[%s]不能单独参与结算,只需提交其父渠道即可", stat.Channel) } } if len(ParentList) == 0 { return nil, fmt.Errorf("没有找到渠道结算数据") } // 列表的起止时间 listBeginTime := ParentList[0].SumDate listEndTime := ParentList[len(ParentList)-1].SumDate // 查找二级渠道的数据 childTypes := channelmod.GetChildChannelTypes() childFilter := bson.M{ channelmod.FN.ChannelStat.ParentChannel: req.Channel, // 限定父渠道 channelmod.FN.ChannelStat.Type: bson.M{"$in": childTypes}, // 限定子渠道 channelmod.FN.ChannelStat.IsPay: bson.M{"$ne": true}, // 未结算过 channelmod.FN.ChannelStat.SumDate: bson.M{"$gte": listBeginTime, "$lte": listEndTime}, // 时间限定 } ChildList, err := channelmod.FindManyChannelStat(ctx, childFilter) if err != nil { return nil, err } // 把二级记录汇总到一级记录上面 for _, parent := range ParentList { parent.CorrectValidData() for _, child := range ChildList { if child.ParentChannel == parent.Channel && child.SumDate.Equal(parent.SumDate) { channelmod.ChannelStatSummation(parent, child.CorrectValidData()) // 把子渠道的数据累加到父渠道上面 } } } // 对一级记录进行统计汇总 resp := &proto.SettlementCalcResp{} resp.TotalCount = int64(len(ParentList)) for _, statInfo := range ParentList { channelmod.ChannelStatSummation(&(resp.TotalInfo), statInfo) } resp.TotalInfo.Channel = req.Channel resp.TotalInfo.AppId = req.AppId resp.TotalInfo.Settlement = channelRule.Settlement resp.TotalInfo.CPAPrice = channelRule.CPAPrice resp.TotalInfo.CPSRate = channelRule.CPSRate resp.TotalInfo.AppType = channelRule.AppType resp.TotalInfo.PromoteMode = channelRule.PromoteMode // 根据扣量规则计算结算金额 resp.TotalInfo.CalcIncomeRate() resp.TotalIncome, resp.TotalDesc = channelmod.CalculateChannelIncome(resp.TotalInfo.CorrectValidData()) return resp, nil } // SearchChannelSettlementStat2 查询渠道结算纤细 func SearchChannelSettlementStat2(ctx context.Context, req *proto.ChannelStatSettlementSearch2) (*proto.ChannelStatSettlementSearch2Resp, error) { resp := &proto.ChannelStatSettlementSearch2Resp{} if req.Channel == nil || *req.Channel == "" { return nil, fmt.Errorf("渠道必须填写") } if req.AppId == nil { return nil, fmt.Errorf("应用ID必须填写") } appInfo, err := appInfoMod.GetAppInfoByIdEx(ctx, *req.AppId) if err != nil { return nil, fmt.Errorf("应用不存在或查找失败:%s", err.Error()) } channelInfo, err2 := channelmod.FindOneChannelInfoEx(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: *req.Channel}) if err2 != nil || channelInfo.Channel == "" { return nil, fmt.Errorf("渠道[%s]不存在,或查找失败", *req.Channel) } if channelInfo.Type != channelmod.ChannelTypeParent { return nil, fmt.Errorf("渠道[%s]不是一级渠道,只有一级渠道才能结算", *req.Channel) } ruleFilter := bson.M{ channelmod.FN.ChannelDeductionRule.Channel: req.Channel, channelmod.FN.ChannelDeductionRule.AppId: req.AppId, } ruleInfo, err1 := channelmod.FindOneChannelDeductionRuleEx(ctx, ruleFilter) if err1 != nil { return nil, fmt.Errorf("渠道[%s]的应用[%s]结算规则不存在或查找失败:%s", *req.Channel, appInfo.Name, err1.Error()) } resp.RuleId = ruleInfo.ID tmpStat := channelmod.ChannelStat{ Channel: ruleInfo.Channel, AppId: ruleInfo.AppId, Settlement: ruleInfo.Settlement, CPAPrice: ruleInfo.CPAPrice, CPSRate: ruleInfo.CPSRate, AppType: ruleInfo.AppType, PromoteMode: ruleInfo.PromoteMode, } resp.TotalInfo = tmpStat resp.ValidInfo = tmpStat resp.ValidInfo = tmpStat opt := &options.FindOptions{} opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) opt.SetSort(bson.D{ {Key: channelmod.FN.ChannelStat.SumDate, Value: -1}, {Key: channelmod.FN.ChannelStat.CreatedAt, Value: -1}, }) filter := bson.M{} // 渠道结算一定包含子渠道 filter[channelmod.FN.ChannelStat.AppId] = *req.AppId // 返回渠道的钱包信息 resp.Wallet = channelInfo.Wallet if req.UserName != nil && *req.UserName != "" { filter[channelmod.FN.ChannelStat.UserName] = *req.UserName } // 开始时间和结束时间 req.Start, req.End, err = CheckAndFixTime(req.Start, req.End) if err != nil { return nil, err } filter[channelmod.FN.ChannelStat.SumDate] = bson.M{"$gte": *req.Start, "$lte": *req.End} // 已结算的是否显示 if req.IsPayed != nil { if *req.IsPayed { filter[channelmod.FN.ChannelStat.IsPay] = true } else { filter[channelmod.FN.ChannelStat.IsPay] = bson.M{"$ne": true} } } // 找到所有一级记录 filter[channelmod.FN.ChannelStat.Channel] = *req.Channel filter[channelmod.FN.ChannelStat.Type] = channelmod.ChannelTypeParent ParentsChannelStatList, err := channelmod.FindManyChannelStat(ctx, filter) if err != nil { return nil, err } if len(ParentsChannelStatList) == 0 { return resp, nil } // 找到分页一级记录 PagedChannelStatList, err := channelmod.FindManyChannelStat(ctx, filter, opt) if err != nil { return nil, err } // 找到所有子级记录 delete(filter, channelmod.FN.ChannelStat.Channel) filter[channelmod.FN.ChannelStat.ParentChannel] = *req.Channel childTypes := channelmod.GetChildChannelTypes() filter[channelmod.FN.ChannelStat.Type] = bson.M{"$in": childTypes} ChildChannelStatList, err := channelmod.FindManyChannelStat(ctx, filter) if err != nil { return nil, err } // 把子级记录汇总到一级记录上面 for _, parent := range ParentsChannelStatList { parent.CorrectValidData() for _, child := range ChildChannelStatList { if child.ParentChannel == parent.Channel && child.SumDate.Equal(parent.SumDate) { channelmod.ChannelStatSummation(parent, child) // 把子渠道的数据累加到父渠道上面 } } } // 对一级记录进行统计汇总 for _, channelStatInfo := range ParentsChannelStatList { resp.TotalCount++ channelmod.ChannelStatSummation(&(resp.TotalInfo), channelStatInfo) if channelStatInfo.IsPay { resp.InvalidCount++ channelmod.ChannelStatSummation(&(resp.InvalidInfo), channelStatInfo) } else { resp.ValidCount++ channelmod.ChannelStatSummation(&(resp.ValidInfo), channelStatInfo) } } // 把Page列表项替换为带子渠道数据的项 for i, pageItem := range PagedChannelStatList { for j, parentItem := range ParentsChannelStatList { if pageItem.ID.String() == parentItem.ID.String() { PagedChannelStatList[i] = ParentsChannelStatList[j] break } } } // 计算金币的数值:金币等于渠道直推充值金额-购买VIP金额-扣量 for _, v := range PagedChannelStatList { v.CorrectValidData() v.CalcIncomeRate() } // 修正数据 resp.TotalInfo.CorrectValidData() resp.ValidInfo.CorrectValidData() resp.InvalidInfo.CorrectValidData() // 收益率 resp.TotalInfo.CalcIncomeRate() resp.ValidInfo.CalcIncomeRate() resp.InvalidInfo.CalcIncomeRate() // 结算数值及提示信息 resp.TotalIncome, resp.TotalDesc = channelmod.CalculateChannelIncome(&resp.TotalInfo) resp.ValidIncome, resp.ValidDesc = channelmod.CalculateChannelIncome(&resp.ValidInfo) resp.InvalidIncome, resp.InvalidDesc = channelmod.CalculateChannelIncome(&resp.InvalidInfo) // 输出列表 resp.List = PagedChannelStatList resp.Channel = channelInfo.Channel resp.AppId = *req.AppId return resp, nil } // SearchSettlementInfo 查询渠道信息 func GetSettlementApplicantList(ctx context.Context, req *proto.GetSettlementApplicantListReq) (*proto.GetSettlementApplicantListResp, error) { resp := proto.GetSettlementApplicantListResp{} match := bson.M{} pipelines := []bson.M{ {"$match": match}, {"$group": bson.M{ "_id": bson.M{"applicant": "$applicant"}, "count": bson.M{"$sum": 1}, }}, {"$project": bson.M{ "applicant": "$_id.applicant", "count": "$count", "_id": 0, }}, {"$sort": bson.D{{Key: "count", Value: -1}}}, } type Data struct { Applicant string `bson:"applicant" json:"applicant"` Count int64 `bson:"count" json:"count"` } List := make([]Data, 0) if err := channelsettlementrecord.AggregateChannelSettlementRecords(ctx, &List, pipelines); err != nil { return nil, err } for _, item := range List { resp.List = append(resp.List, item.Applicant) } return &resp, nil } // SearchApplyForm 查询渠道信息 func SearchApplyForm(ctx context.Context, req *proto.GetApplyFormReq, channel string) ( *proto.GetApplyFormResp, error) { filter := bson.M{} opt := &options.FindOptions{} opt.SetSort(bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt: -1}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) resp := &proto.GetApplyFormResp{} filter[channelsettlementrecord.FN.ChannelSettlementRecord.Channel] = channel if req.AppId != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.AppId] = *req.AppId } if req.Start != nil && req.End != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt] = bson.M{"$gte": *req.Start, "$lt": *req.End} } if req.Status != nil { if *req.Status == channelsettlementrecord.SettlementInit || *req.Status == channelsettlementrecord.SettlementSuccess { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = *req.Status } else { return nil, fmt.Errorf("未知状态") } } else { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = bson.M{"$in": []channelsettlementrecord.SettlementStatus{ channelsettlementrecord.SettlementInit, channelsettlementrecord.SettlementSuccess, }} } total, err := channelsettlementrecord.CountChannelSettlementRecords(ctx, filter) if err != nil { return nil, err } if total == 0 { return resp, nil } SettlementInfoList, err := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, filter, opt) if err != nil { return nil, err } resp.List = SettlementInfoListToApplyForm(SettlementInfoList) resp.Total = total return resp, nil } // SearchSettlementInfo 查询渠道信息 func SearchSettlementInfo(ctx context.Context, req *proto.SettlementInfoSearch) ( *proto.SettlementInfoSearchResp, error) { filter := bson.M{} opt := &options.FindOptions{} opt.SetSort(bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt: -1}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) resp := &proto.SettlementInfoSearchResp{} if req.Channel != nil && *req.Channel != "" { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Channel] = *req.Channel } if req.AppId != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.AppId] = *req.AppId } if req.Start != nil && req.End != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt] = bson.M{"$gte": *req.Start, "$lt": *req.End} } if req.Remark != nil && *req.Remark != "" { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Remark] = bson.M{"$regex": *req.Remark} } if len(req.Applicants) > 0 { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Applicant] = bson.M{"$in": req.Applicants} } if len(req.Status) > 0 { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = bson.M{"$in": req.Status} } else { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = bson.M{"$in": []channelsettlementrecord.SettlementStatus{}} } total, serr := channelsettlementrecord.CountChannelSettlementRecords(ctx, filter) if serr != nil { return nil, serr } if total == 0 { return resp, nil } SettlementInfoList, err := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, filter, opt) if err != nil { return nil, err } resp.List = SettlementInfoList resp.Total = total return resp, nil } // SearchDeletedSettlementInfo 查询渠道信息 func SearchDeletedSettlementInfo(ctx context.Context, req *proto.SettlementInfoSearch) ( *proto.SettlementInfoSearchResp, error) { filter := bson.M{} opt := &options.FindOptions{} opt.SetSort(bson.M{channelsettlementrecord.FN.ChannelSettlementDeleted.CreatedAt: -1}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) resp := &proto.SettlementInfoSearchResp{} if req.Channel != nil && *req.Channel != "" { filter[channelsettlementrecord.FN.ChannelSettlementDeleted.Channel] = *req.Channel } if req.AppId != nil { filter[channelsettlementrecord.FN.ChannelSettlementDeleted.AppId] = *req.AppId } if req.Start != nil && req.End != nil { filter[channelsettlementrecord.FN.ChannelSettlementDeleted.CreatedAt] = bson.M{"$gte": *req.Start, "$lt": *req.End} } if req.Remark != nil && *req.Remark != "" { filter[channelsettlementrecord.FN.ChannelSettlementDeleted.Remark] = bson.M{"$regex": *req.Remark} } //if req.PayType != nil { // switch *req.PayType { // case 1: // filter[channelsettlementrecord.FN.ChannelSettlementDeleted.ERC] = bson.M{"$gt": ""} // case 2: // filter[channelsettlementrecord.FN.ChannelSettlementDeleted.TRC] = bson.M{"$gt": ""} // case 3: // filter[channelsettlementrecord.FN.ChannelSettlementDeleted.BankCard] = bson.M{"$gt": ""} // case 4: // filter[channelsettlementrecord.FN.ChannelSettlementDeleted.Alipay] = bson.M{"$gt": ""} // } //} total, serr := channelsettlementrecord.CountChannelSettlementDeletes(ctx, filter) if serr != nil { return nil, serr } if total == 0 { return resp, nil } SettlementInfoList, err := channelsettlementrecord.FindManyChannelSettlementDeleted(ctx, filter, opt) if serr != nil { return nil, err } resp.List = SettlementInfoList resp.Total = total return resp, nil } // SearchSettlementInfoExport 导出渠道信息 func SearchSettlementInfoExport(ctx context.Context, req *proto.SettlementInfoSearch) (string, error) { filter := bson.M{} opt := &options.FindOptions{} opt.SetSort(bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt: -1}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) if req.Channel != nil && *req.Channel != "" { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Channel] = *req.Channel } if req.AppId != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.AppId] = *req.AppId } if req.Start != nil && req.End != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt] = bson.M{"$gte": *req.Start, "$lt": *req.End} } if req.Remark != nil && *req.Remark != "" { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Remark] = bson.M{"$regex": *req.Remark} } if len(req.Applicants) > 0 { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Applicant] = bson.M{"$in": req.Applicants} } if len(req.Status) > 0 { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = bson.M{"$in": req.Status} } appList, err := appInfoMod.GetAllAppInfo(ctx) if err != nil { return "", err } total, err1 := channelsettlementrecord.CountChannelSettlementRecords(ctx, filter) if err1 != nil { return "", err1 } if total == 0 { return "", nil } SettlementInfoList, err2 := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, filter, opt) if err2 != nil { return "", err2 } filePath := fmt.Sprintf("./tmp/渠道结算列表-%s.xlsx", time.Now().Format("2006-01-02 15:04:05")) if sErr := export(filePath, SettlementInfoList, appList); sErr != nil { return "", sErr } return filePath, nil } // SearchSettlementInfoApplyExport 导出结算申请信息 func SearchSettlementInfoApplyExport(ctx context.Context, req *proto.SettlementInfoSearch) (string, error) { filter := bson.M{} opt := &options.FindOptions{} opt.SetSort(bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt: -1}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) if req.Channel != nil && *req.Channel != "" { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Channel] = *req.Channel } if req.AppId != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.AppId] = *req.AppId } if req.Start != nil && req.End != nil { filter[channelsettlementrecord.FN.ChannelSettlementRecord.CreatedAt] = bson.M{"$gte": *req.Start, "$lt": *req.End} } if req.Remark != nil && *req.Remark != "" { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Remark] = bson.M{"$regex": *req.Remark} } if len(req.Applicants) > 0 { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Applicant] = bson.M{"$in": req.Applicants} } if len(req.Status) > 0 { filter[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = bson.M{"$in": req.Status} } total, err1 := channelsettlementrecord.CountChannelSettlementRecords(ctx, filter) if err1 != nil { return "", err1 } if total == 0 { return "", nil } SettlementInfoList, err2 := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, filter, opt) if err2 != nil { return "", err2 } if len(SettlementInfoList) <= 0 { return "", nil } appList, err := appInfoMod.GetAllAppInfo(ctx) if err != nil { return "", err } filePath := fmt.Sprintf("./tmp/渠道结算申请列表-%s.xlsx", time.Now().Format("2006-01-02 15:04:05")) if sErr := ExportSettleApplyForm(filePath, SettlementInfoList, appList); sErr != nil { return "", sErr } return filePath, nil } // AddSettlementInfo 添加一个媒体视频 func AddSettlementInfo(ctx context.Context, req *proto.SettlementInfoAdd) error { settlementInfoFilter := bson.M{ channelsettlementrecord.FN.ChannelSettlementRecord.Channel: req.Channel, channelsettlementrecord.FN.ChannelSettlementRecord.AppId: req.AppId, } settlementInfoList, serr := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, settlementInfoFilter) for _, valueInfo := range settlementInfoList { if req.BeginTime.After(valueInfo.BeginTime) && req.BeginTime.Before(valueInfo.EndTime) { return errors.New("重复结算") } if req.EndTime.After(valueInfo.BeginTime) && req.EndTime.Before(valueInfo.EndTime) { return errors.New("重复结算") } } request := &proto.ChannelStatSettlementSearch{ Channel: &req.Channel, AppId: &req.AppId, Start: &req.BeginTime, End: &req.EndTime, } resp, serr := SearchChannelSettlementStat(ctx, request) if serr != nil { return serr } if resp.Income != req.TotalMoney { return errors.New("金额不一致") } // 更新是否已经结算 channelStatFilter := bson.M{ "$or": bson.A{ bson.M{channelmod.FN.ChannelStat.Channel: req.Channel}, bson.M{channelmod.FN.ChannelStat.ParentChannel: req.Channel}, }, channelmod.FN.ChannelStat.AppId: req.AppId, channelmod.FN.ChannelStat.SumDate: bson.M{"$gte": req.BeginTime, "$lte": req.EndTime}, channelmod.FN.ChannelStat.IsPay: bson.M{"$ne": true}, } set := bson.M{ "$set": bson.M{ channelmod.FN.ChannelStat.IsPay: true, }, } err := channelmod.UpdateManyChannelStat(ctx, channelStatFilter, set) if err != nil { return err } filter := bson.M{ channelmod.FN.ChannelDeductionRule.Channel: req.Channel, channelmod.FN.ChannelDeductionRule.AppId: req.AppId, } newRecord := &channelsettlementrecord.ChannelSettlementRecord{ Channel: req.Channel, AppId: req.AppId, BeginTime: req.BeginTime, EndTime: req.EndTime, TotalMoney: resp.Income, NewDirectCount: resp.TotalInfo.NewDirectCount, NewFissionCount: resp.TotalInfo.NewFissionCount, NewDeductionCount: resp.TotalInfo.NewDeductionCount, RechargeAmount: resp.TotalInfo.DirectAmount + resp.TotalInfo.FissionAmount + resp.TotalInfo.DeductionAmount, VipPayMoney: resp.TotalInfo.VipPayMoney, ValidDirectCount: resp.TotalInfo.ValidDirectCount, ValidDeductionCount: resp.TotalInfo.ValidDeductionCount, ValidPayMoney: resp.TotalInfo.ValidPayMoney, ValidPayCoin: resp.TotalInfo.ValidPayCoin, Status: channelsettlementrecord.SettlementInit, CreatedAt: time.Now(), } ruleInfo, _ := channelmod.FindOneChannelDeductionRule(ctx, filter) if ruleInfo != nil { newRecord.AppType = ruleInfo.AppType newRecord.PromoteMode = ruleInfo.PromoteMode newRecord.Settlement = ruleInfo.Settlement newRecord.CPAPrice = ruleInfo.CPAPrice newRecord.CPSRate = ruleInfo.CPSRate } return channelsettlementrecord.InsertOneChannelSettlementRecord(ctx, newRecord) } // AddSettlementInfo 添加一个媒体视频 func AddSettlementInfo2(ctx context.Context, req *proto.SettlementInfoAdd2, accType define.ActType, account string) error { // 参数检查 if len(req.SettleIds) <= 0 { return errors.New("没有提交要结算的统计记录id数组") } if req.SettleMoney <= 0 { return errors.New("结算的真实金额必须大于零") } // 在已结算表中检查是否有重复结算 settlementInfoFilter := bson.M{ channelsettlementrecord.FN.ChannelSettlementRecord.SettleIds: bson.M{"$in": req.SettleIds}, } settlementInfoList, err := channelsettlementrecord.FindManyChannelSettlementRecord(ctx, settlementInfoFilter) if err != nil { return err } if len(settlementInfoList) > 0 { return errors.New("重复结算") } // 获取扣量规则 ruleFilter := bson.M{ channelmod.FN.ChannelDeductionRule.ID: req.RuleId, channelmod.FN.ChannelDeductionRule.AppId: req.AppId, channelmod.FN.ChannelDeductionRule.Channel: req.Channel, } channelRule, err1 := channelmod.FindOneChannelDeductionRuleEx(ctx, ruleFilter) // 检查扣量规则 if err1 != nil { return fmt.Errorf("查找渠道结算规则失败:%s", err1.Error()) } // 开始时间和结束时间 req.BeginTime, req.EndTime, err = CheckAndFixTime(req.BeginTime, req.EndTime) if err != nil { return err } // ids数据查找 opt := &options.FindOptions{} opt.SetSort(bson.M{channelmod.FN.ChannelStat.SumDate: 1}) parentFilter := bson.M{ channelmod.FN.ChannelStat.AppId: req.AppId, // 应用限定 channelmod.FN.ChannelStat.Channel: req.Channel, // 渠道限定 channelmod.FN.ChannelStat.Type: channelmod.ChannelTypeParent, // 类型限定 channelmod.FN.ChannelStat.IsPay: bson.M{"$ne": true}, // 未结算过 channelmod.FN.ChannelStat.SumDate: bson.M{"$gte": req.BeginTime, "$lte": req.EndTime}, // 时间限定 channelmod.FN.ChannelStat.ID: bson.M{"$in": req.SettleIds}, // 提交的IDs } ParentList, err2 := channelmod.FindManyChannelStat(ctx, parentFilter, opt) if err2 != nil { return err2 } if len(ParentList) != len(req.SettleIds) { return fmt.Errorf("提交的统计记录条数[%d]和查找到的[%d]不一致", len(req.SettleIds), len(ParentList)) } // 结算项检查 for _, stat := range ParentList { if stat.Type != channelmod.ChannelTypeParent { return fmt.Errorf("子渠道[%s]不能单独参与结算,只需提交其父渠道即可", stat.Channel) } } if len(ParentList) == 0 { return fmt.Errorf("没有找到渠道结算数据") } // 列表的起止时间 listBeginTime := ParentList[0].SumDate listEndTime := ParentList[len(ParentList)-1].SumDate // 查找子级渠道的数据 childTypes := channelmod.GetChildChannelTypes() childFilter := bson.M{ channelmod.FN.ChannelStat.ParentChannel: req.Channel, // 限定父渠道 channelmod.FN.ChannelStat.Type: bson.M{"$in": childTypes}, // 限定子渠道 channelmod.FN.ChannelStat.IsPay: bson.M{"$ne": true}, // 未结算过 channelmod.FN.ChannelStat.SumDate: bson.M{"$gte": listBeginTime, "$lte": listEndTime}, // 时间限定 } ChildList, err3 := channelmod.FindManyChannelStat(ctx, childFilter) if err3 != nil { return err3 } // 把二级记录汇总到一级记录上面 for _, parent := range ParentList { parent.CorrectValidData() for _, child := range ChildList { if child.ParentChannel == parent.Channel && child.SumDate.Equal(parent.SumDate) { channelmod.ChannelStatSummation(parent, child) // 把子渠道的数据累加到父渠道上面 } } } // 对一级记录进行统计汇总 sumInfo := &channelmod.ChannelStat{ Channel: channelRule.Channel, AppId: channelRule.AppId, Settlement: channelRule.Settlement, CPAPrice: channelRule.CPAPrice, CPSRate: channelRule.CPSRate, AppType: channelRule.AppType, PromoteMode: channelRule.PromoteMode, } for _, statInfo := range ParentList { channelmod.ChannelStatSummation(sumInfo, statInfo) } // 根据扣量规则计算结算金额 // TODO 这里按当前 Rule规则 扣量 income, _ := channelmod.CalculateChannelIncome(sumInfo) // 检查提交的和计算的是否一致 if income != req.TotalMoney { return fmt.Errorf("提交的结算金额[%d分]和服务器统计的[%d分]不一致", req.TotalMoney, income) } // 获取操作者名字 var accName string if accName, err = GetAccountName(ctx, accType, account); err != nil { return fmt.Errorf("获取账号[%s]的信息失败:%s", account, err.Error()) } // 获取渠道信息 channelInfo, err4 := channelmod.FindOneChannelInfoEx(ctx, bson.M{channelmod.FN.ChannelInfo.Channel: req.Channel}) if err4 != nil { return fmt.Errorf("渠道不存在") } // 渠道级别检查 if channelInfo.Type != channelmod.ChannelTypeParent { return fmt.Errorf("输入的渠道不是一级渠道,只有一级渠道才能进行结算") } // 结算钱包检查 if req.WalletAddr1 != nil && *req.WalletAddr1 != channelInfo.Wallet.Addr1 { return fmt.Errorf("钱包地址1,和渠道信息中填写的不一致") } if req.WalletAddr2 != nil && *req.WalletAddr2 != channelInfo.Wallet.Addr2 { return fmt.Errorf("钱包地址2,和渠道信息中填写的不一致") } if req.WalletAddr1 == nil && req.WalletAddr2 == nil { return fmt.Errorf("结算的收款地址信息必须至少填写一个") } // 下面两步用事务 fn := func(sctx mongo.SessionContext) error { // 更新是否已经结算 parentFilter = bson.M{ channelmod.FN.ChannelStat.ID: bson.M{"$in": req.SettleIds}, // 提交的IDs channelmod.FN.ChannelStat.IsPay: bson.M{"$in": bson.A{nil, false}}, } set := bson.M{ "$set": bson.M{ channelmod.FN.ChannelStat.IsPay: true, }, } if count, err := channelmod.UpdateManyChannelStatWithResult(sctx, parentFilter, set); err != nil { return err } else { if count != int64(len(req.SettleIds)) { return fmt.Errorf("数据修改冲突,同一结算,请不要多次点击提交") } } // 渠道备注 var remark string if req.Remark != nil { remark = *req.Remark } now := time.Now() newRecord := &channelsettlementrecord.ChannelSettlementRecord{ Channel: req.Channel, IncludeChild: true, AppId: req.AppId, BeginTime: listBeginTime, EndTime: listEndTime, TotalMoney: income, SettleMoney: req.SettleMoney, Settlement: channelRule.Settlement, CPAPrice: channelRule.CPAPrice, CPSRate: channelRule.CPSRate, AppType: channelRule.AppType, PromoteMode: channelRule.PromoteMode, NewDirectCount: sumInfo.NewDirectCount, NewFissionCount: sumInfo.NewFissionCount, NewDeductionCount: sumInfo.NewDeductionCount, RechargeAmount: sumInfo.DirectAmount + sumInfo.FissionAmount + sumInfo.DeductionAmount, VipPayMoney: sumInfo.VipPayMoney, VipPayCoin: sumInfo.VipPayCoin, VipPayDeduction: sumInfo.VipPayDeduction, ValidDirectCount: sumInfo.ValidDirectCount, ValidDeductionCount: sumInfo.ValidDeductionCount, ValidPayMoney: sumInfo.ValidPayMoney, ValidPayCoin: sumInfo.ValidPayCoin, Wallet: channelmod.Wallet{ Addr1: checkParam(req.WalletAddr1), Addr2: checkParam(req.WalletAddr2), }, PayMoney: 0, OperateHistory: []channelsettlementrecord.SettlementOperateItem{ { OperateTime: time.Now(), BeforeStatus: channelsettlementrecord.SettlementInit, AfterStatus: channelsettlementrecord.SettlementInit, OperatorAccount: account, OperatorAccountName: accName, OperatorAccountType: accType, Pictures: []string{}, Content: fmt.Sprintf("【%s】提交了结算申请", accName), Remark: remark, }, }, Status: channelsettlementrecord.SettlementInit, Remark: remark, SettleIds: req.SettleIds, CreatedAt: now, UpdatedAt: now, Applicant: accName, } err = channelsettlementrecord.InsertOneChannelSettlementRecord(sctx, newRecord) return err } if err := mongos.Transaction(ctx, fn); err != nil { return err } return nil } func checkParam(req *string) string { if req != nil { return *req } else { return "" } } // UpdateSettlementInfo 更新指定渠道 func UpdateSettlementInfo(ctx context.Context, info proto.SettlementInfoUpdate) error { filter := bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.ID: info.ID} set := bson.M{} if info.PayMoney != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.PayMoney] = info.PayMoney } if info.Remark != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.Remark] = info.Remark } if info.Status != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = info.Status } if info.Usdt != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.Usdt] = info.Usdt } //更新数据库 setDoc := bson.M{"$set": set} serr := channelsettlementrecord.UpdateOneChannelSettlementRecord(ctx, filter, setDoc) if serr != nil { return serr } return nil } // DeleteSettlementInfo 更新指定渠道 func DeleteSettlementInfo(ctx context.Context, req proto.SettlementInfoDelete, accType define.ActType, account string) error { filter := bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.ID: req.ID} settle, err := channelsettlementrecord.FindOneChannelSettlementRecordEx(ctx, filter) if err != nil { return err } // 获取操作者名字 var accName string if accName, err = GetAccountName(ctx, accType, account); err != nil { return fmt.Errorf("获取账号[%s]的信息失败:%s", account, err.Error()) } // 状态为【发起申请】【打款失败】这两种状态的结算记录可以删除 if settle.Status == channelsettlementrecord.SettlementInit || settle.Status == channelsettlementrecord.SettlementFailed { // 下面用事务 fn := func(sctx mongo.SessionContext) error { // 结算中的统计项全部修改为未结算状态 statFilter := bson.M{ channelmod.FN.ChannelStat.ID: bson.M{"$in": settle.SettleIds}, // 提交的IDs } set := bson.M{ "$set": bson.M{ channelmod.FN.ChannelStat.IsPay: false, }, } err := channelmod.UpdateManyChannelStat(sctx, statFilter, set) if err != nil { return err } // 插入要删除副本到删除备份表 settle.OperateHistory = append(settle.OperateHistory, channelsettlementrecord.SettlementOperateItem{ OperateTime: time.Now(), BeforeStatus: settle.Status, AfterStatus: channelsettlementrecord.SettlementDeleted, OperatorAccount: account, OperatorAccountType: accType, OperatorAccountName: accName, Content: fmt.Sprintf("状态改变:(%s => %s)\n", settle.Status.String(), channelsettlementrecord.SettlementDeleted.String()), Pictures: nil, Remark: "结算记录被删除", }) status := settle.Status settle.Status = channelsettlementrecord.SettlementDeleted err = channelsettlementrecord.InsertOneChannelSettlementDeleted(ctx, settle) if err != nil { return err } // 删除结算项 settleFilter := bson.M{ channelsettlementrecord.FN.ChannelSettlementRecord.ID: settle.ID, channelsettlementrecord.FN.ChannelSettlementRecord.Status: status, } _, err = channelsettlementrecord.DelOneChannelSettlementRecord(ctx, settleFilter) return err } if err := mongos.Transaction(ctx, fn); err != nil { return err } return nil } else { return fmt.Errorf("当前结算项的状态[%s][%d]不能被删除!", channelsettlementrecord.GetSettStatus(settle.Status), settle.Status) } } // DetailSettlementInfo 查看结算项详情 func DetailSettlementInfo(ctx context.Context, req proto.SettlementInfoDetail) (*proto.SettlementInfoDetailResp, error) { resp := &proto.SettlementInfoDetailResp{} settleFilter := bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.ID: req.ID} settle, err := channelsettlementrecord.FindOneChannelSettlementRecordEx(ctx, settleFilter) if err != nil { if err != mongo.ErrNoDocuments { return nil, err } settle, err = channelsettlementrecord.FindOneChannelSettlementDeletedEx(ctx, settleFilter) if err != nil { return nil, err } } // 找到所有一级记录 statFilter := bson.M{ channelmod.FN.ChannelStat.Type: channelmod.ChannelTypeParent, channelmod.FN.ChannelStat.ID: bson.M{"$in": settle.SettleIds}, } ParentsChannelStatList, err := channelmod.FindManyChannelStat(ctx, statFilter) if err != nil { return nil, err } if len(ParentsChannelStatList) == 0 { return resp, nil } // 找到分页一级记录 opt := &options.FindOptions{} opt.SetSort(bson.M{channelmod.FN.ChannelStat.CreatedAt: -1}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) PagedChannelStatList, err := channelmod.FindManyChannelStat(ctx, statFilter, opt) if err != nil { return nil, err } // 找到所有子级记录 childTypes := channelmod.GetChildChannelTypes() childFilter := bson.M{ channelmod.FN.ChannelStat.ParentChannel: settle.Channel, channelmod.FN.ChannelStat.Type: bson.M{"$in": childTypes}, channelmod.FN.ChannelStat.SumDate: bson.M{"$gte": settle.BeginTime, "$lte": settle.EndTime}, // 时间限定 } ChildsChannelStatList, err := channelmod.FindManyChannelStat(ctx, childFilter) if err != nil { return nil, err } // 把子级记录汇总到一级记录上面 for _, parent := range ParentsChannelStatList { for _, child := range ChildsChannelStatList { if child.ParentChannel == parent.Channel && child.SumDate.Equal(parent.SumDate) { channelmod.ChannelStatSummation(parent, child) // 把子渠道的数据累加到父渠道上面 } } } // 把Page列表项替换为带子渠道数据的项 for i, pageItem := range PagedChannelStatList { for j, parentItem := range ParentsChannelStatList { if pageItem.ID.String() == parentItem.ID.String() { PagedChannelStatList[i] = ParentsChannelStatList[j] break } } } // 计算金币的数值:金币等于渠道直推充值金额-购买VIP金额-扣量 for _, v := range PagedChannelStatList { //v.VipPayCoin = (v.DirectAmount + v.FissionAmount - v.VipPayMoney - v.VipPayDeduction) v.CorrectValidData() v.CalcIncomeRate() } resp.List = PagedChannelStatList resp.Count = int64(len(ParentsChannelStatList)) return resp, nil } // UpdateSettlementInfo2 更新指定渠道 func UpdateSettlementInfo2(ctx context.Context, req proto.SettlementInfoUpdate2, accType define.ActType, account string) error { chReq := proto.SettlementInfoUpdate2{} filter := bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.ID: req.ID} settle, err := channelsettlementrecord.FindOneChannelSettlementRecordEx(ctx, filter) if err != nil { return err } // 获取操作者名字 var accName string if accName, err = GetAccountName(ctx, accType, account); err != nil { return fmt.Errorf("获取操作者账号[%s]的信息失败:%s", account, err.Error()) } set := bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.UpdatedAt: time.Now()} if req.Remark != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.Remark] = req.Remark chReq.Remark = req.Remark } // 状态转换检查 switch settle.Status { case channelsettlementrecord.SettlementInit: // 【发起】结算状态,可以修改和确认 { if req.Status == nil { // 渠道可以修改要结算的钱数,及钱包地址 return fmt.Errorf("参数错误,要修改的状态字段为空") } else { if *req.Status == channelsettlementrecord.SettlementSuccess || *req.Status == channelsettlementrecord.SettlementFailed { set[channelsettlementrecord.FN.ChannelSettlementRecord.Status] = req.Status chReq.Status = req.Status if *req.Status == channelsettlementrecord.SettlementSuccess { if req.Usdt != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.Usdt] = req.Usdt chReq.Usdt = req.Usdt } if req.PayMoney != nil { set[channelsettlementrecord.FN.ChannelSettlementRecord.PayMoney] = req.PayMoney chReq.PayMoney = req.PayMoney } } } else { return fmt.Errorf("修改状态出错!因为结算状态为【发起申请】状态,只能修改为【打款成功】或【打款失败】") } } } case channelsettlementrecord.SettlementSuccess: { return fmt.Errorf("错误,已经成功的结算单不能修改") } case channelsettlementrecord.SettlementFailed: { return fmt.Errorf("错误,打款失败的结算单不能修改,只能删除后重新发起结算申请") } case channelsettlementrecord.SettlementDeleted: { return fmt.Errorf("错误,已删除的订单无法修改") } default: { return fmt.Errorf("状态无效") } } opItem := GetUpdateSettlementOperateItem(*settle, chReq, accType, account, accName, req.Pictures) push := bson.M{channelsettlementrecord.FN.ChannelSettlementRecord.OperateHistory: opItem} //更新数据库 setDoc := bson.M{"$set": set, "$push": push} serr := channelsettlementrecord.UpdateOneChannelSettlementRecord(ctx, filter, setDoc) if serr != nil { return serr } return nil } func ExportSettleApplyForm(rPath string, transInfos []*channelsettlementrecord.ChannelSettlementRecord, appList []appInfoMod.AppInfo) error { f := excelize.NewFile() sheetName := "Sheet1" // 设置列宽 f.SetColWidth("Sheet1", "A", "W", 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", "W1", headStyleID) idx := f.NewSheet(sheetName) var exportTransWaterTitle = map[string]string{ "A1": "申请人", "B1": "产品", "C1": "渠道码", "D1": "分成", "E1": "结算期间", "F1": "", "G1": "新增", "H1": "", "I1": "金币充值", "J1": "充值金额", "K1": "结算金额", "L1": "ARPU", "M1": "收益比例", "N1": "打款日期", "O1": "打款金额", "P1": "实际打款", "Q1": "汇率", "R1": "U金额", "S1": "方式", "T1": "钱包", "U1": "打款资料", "V1": "打款资料", "W1": "备注", } for k, v := range exportTransWaterTitle { f.SetCellValue(sheetName, k, v) } f.SetCellValue(sheetName, "E2", "开:") f.SetCellValue(sheetName, "F2", "结:") f.SetCellValue(sheetName, "G2", "未") f.SetCellValue(sheetName, "H2", "扣") f.MergeCell(sheetName, "E1", "F1") f.MergeCell(sheetName, "G1", "H1") SetCellValue := func(file *excelize.File, sheet, axis string, value string) { //fmt.Printf("sheet: %s, axis:%s, value:%s\n", sheet, axis, value) file.SetCellValue(sheetName, axis, value) } for index, item := range transInfos { col := 'A' // col 不能超过Z,即列输不能超过26列,超出将出现bug row := index + 3 axis := func() string { ret := fmt.Sprintf("%s%d", string(col), row) col++ return ret } operator := "" remark := "" if len(item.OperateHistory) > 0 { operator = item.OperateHistory[0].OperatorAccountName remark = item.OperateHistory[0].Remark if len(item.OperateHistory) > 1 { if remark == "" { remark = item.OperateHistory[len(item.OperateHistory)-1].Remark } else { s := item.OperateHistory[len(item.OperateHistory)-1].Remark if s != "" { remark += " / " + s } } } } begin := item.BeginTime.Local().Format("2006-01-02") end := item.EndTime.Local().Format("2006-01-02") divide := "" if item.Settlement == channelmod.SettlementCPA { divide = fmt.Sprintf("%.2f", float64(item.CPAPrice)/100) } else if item.Settlement == channelmod.SettlementCPS { divide = fmt.Sprintf("%d%%", item.CPSRate) } payMoneyStr := "" if item.Status == channelsettlementrecord.SettlementSuccess { payMoneyStr = IndentationDisplay(item.SettleMoney) } realMoneyStr := "" realUsdtStr := "" if item.Status == channelsettlementrecord.SettlementSuccess { realMoneyStr = IndentationDisplay(item.PayMoney) realUsdtStr = IndentationDisplay(item.Usdt) } //fmt.Printf("----- ROW %d -----\n", row) product := fmt.Sprintf("%s%s", GetAppName(appList, item.AppId), strings.ToUpper(string(item.Settlement))) SetCellValue(f, sheetName, axis(), operator) //申请人 SetCellValue(f, sheetName, axis(), product) //产品 SetCellValue(f, sheetName, axis(), item.Channel) //渠道码 SetCellValue(f, sheetName, axis(), divide) //分成 SetCellValue(f, sheetName, axis(), begin) //开 SetCellValue(f, sheetName, axis(), end) //结 SetCellValue(f, sheetName, axis(), FenToYuanDisplay(item.NewDirectCount+item.NewFissionCount+item.NewDeductionCount)) //未 SetCellValue(f, sheetName, axis(), FenToYuanDisplay(item.NewDirectCount)) //扣 SetCellValue(f, sheetName, axis(), IndentationDisplay(item.VipPayCoin)) //金币充值 SetCellValue(f, sheetName, axis(), IndentationDisplay(item.VipPayMoney+item.VipPayDeduction)) //充值金额(这里显示的是:总充值-金币) SetCellValue(f, sheetName, axis(), IndentationDisplay(item.SettleMoney)) //结算金额 SetCellValue(f, sheetName, axis(), "") //ARPU SetCellValue(f, sheetName, axis(), "") //收益比例 SetCellValue(f, sheetName, axis(), "") //打款日期 SetCellValue(f, sheetName, axis(), payMoneyStr) //打款金额 SetCellValue(f, sheetName, axis(), realMoneyStr) //实际打款 SetCellValue(f, sheetName, axis(), "") //汇率 SetCellValue(f, sheetName, axis(), realUsdtStr) //U金额 SetCellValue(f, sheetName, axis(), "") //方式 SetCellValue(f, sheetName, axis(), "") //钱包 SetCellValue(f, sheetName, axis(), item.Wallet.Addr1) //打款资料 SetCellValue(f, sheetName, axis(), item.Wallet.Addr2) //打款资料 SetCellValue(f, sheetName, axis(), remark) //备注 } f.SetActiveSheet(idx) p, _ := os.Getwd() fileName := path.Join(p, rPath) if err := f.SaveAs(fileName); err != nil { log.Error("SettlementInfo:ExportSettleForm save file to disk failed", log.Any("fileName", fileName), log.E(err)) return err } return nil } func export(rPath string, transInfos []*channelsettlementrecord.ChannelSettlementRecord, appList []appInfoMod.AppInfo) error { f := excelize.NewFile() sheetName := "Sheet1" // 设置列宽 f.SetColWidth("Sheet1", "A", "U", 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", "U1", headStyleID) idx := f.NewSheet(sheetName) var exportTransWaterTitle = map[string]string{ "A1": "开始时间", "B1": "结束时间", "C1": "渠道", "D1": "结算方式", "E1": "收款信息1", "F1": "收款信息2", "G1": "单价", "H1": "分成比列", "I1": "应用名称", "J1": "新增", "K1": "扣量", "L1": "充值金额", "M1": "金币充值", "N1": "统计结算金额", "O1": "申请结算金额", "P1": "打款金额(CNY)", "Q1": "打款金额(USDT)", "R1": "备注", "S1": "状态", "T1": "创建时间", "U1": "申请结算人", } for k, v := range exportTransWaterTitle { f.SetCellValue(sheetName, k, v) } for k, v := range transInfos { i := 'A' // i 不能超过Z,即列输不能超过26列,超出将出现bug begin := v.BeginTime.Local().Format("2006-01-02") f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), begin) //开始时间 i++ end := v.EndTime.Local().Format("2006-01-02") f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), end) //结束时间 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.Settlement) //结算方式 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Wallet.Addr1) //打款地址1 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Wallet.Addr2) //打款地址2 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.CPAPrice)/100) //单价 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.CPSRate) //分成比列 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), GetAppName(appList, v.AppId)) //应用名称 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.NewDeductionCount) //扣量 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.VipPayMoney+v.VipPayDeduction)/100) //充值金额 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.VipPayCoin)/100) //金币充值 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.TotalMoney)/100) //统计结算金额 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.SettleMoney)/100) //申请结算金额 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.PayMoney)/100) //打款金额 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), float64(v.Usdt)/100) i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Remark) //备注 i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), v.Status.String()) i++ timeStr := v.CreatedAt.Local().Format("2006-01-02") f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), timeStr) opName := "" if len(v.OperateHistory) > 0 { opName = v.OperateHistory[0].OperatorAccountName } i++ f.SetCellValue(sheetName, fmt.Sprintf("%s%d", string(i), k+2), opName) } 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 GetAppName(appList []appInfoMod.AppInfo, appId int32) string { for _, app := range appList { if app.AppId == appId { return app.Name } } return "未知app" }