Files
2026-05-20 16:13:58 +08:00

589 lines
18 KiB
Go

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
}