Files
2026-06-03 23:13:12 +08:00

646 lines
20 KiB
Go

package agentSer
import (
"bytes"
"context"
"errors"
"fmt"
"stat/api/proto"
"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
} 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
}