package statcenterctl import ( "net/http" "strings" "time" "91porn-server/common" "91porn-server/common/log" "91porn-server/common/stderr" "91porn-server/models/l/visitlogmod" "91porn-server/models/v/rchgordmod" "91porn-server/models/v/txnmod" "91porn-server/models/v/usermod" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" ) // 数据中心同步协议(与哔哩 statcenter 第三版协议保持一致) // statAppId 数据中心同步使用的应用ID const statAppId int32 = 3 type StatJob string const ( StatJobRegister StatJob = "register" // 用户注册 StatJobRecharge StatJob = "recharge" // 用户充值 StatJobVisit StatJob = "visit" // 用户访问 StatJobVip StatJob = "vip" // VIP StatJobRechargeAll StatJob = "rechargeAll" // 充值订单 StatJobRegisterLight StatJob = "registerLight" // 注册轻量(H5注册) ) type StatcenterSyncReq struct { Job string `json:"job" bson:"job"` // 同步 UserId uint64 `json:"userId" bson:"userId"` // 用户(注册) PlatformId string `json:"platformId" bson:"platformId"` // 原始平台Id(访问) SuccessTime time.Time `json:"successTime"` // 成功时间(购买会员、提现、充值) MaxSize int64 `json:"maxSize" bson:"maxSize"` // 最大条数 } type StatcenterSyncResp struct { Code int `json:"code"` // 200正常 其他异常 Msg string `json:"msg"` // 错误消息 Job string `json:"job" bson:"job"` // 类型 VisitList []UserVisitMsg `json:"visitList"` // 日活记录 RegisterList []UserRegisterMsg `json:"registerList"` // 注册记录 WithdrawList []UserWithdrawMsg `json:"withdrawList"` // 提现记录 RechargeList []UserRechargeMsg `json:"rechargeList"` // 充值记录 VipList []UserBuyVIPMsg `json:"buyVipList"` // VIP购买记录 GameRecharge []UserRechargeMsg `json:"gameRecharge"` // 游戏充值订单 GameWithdraw []UserWithdrawMsg `json:"gameWithdraw"` // 游戏提现订单 } type UserVisitMsg struct { UserId uint64 `json:"userId" bson:"userId"` // 用户ID PlatformId string `json:"platformId" bson:"platformId"` // 原始平台流水Id AppId int32 `json:"appId" bson:"appId"` // appID SysType string `json:"sysType" bson:"sysType"` // 操作系统类型 安卓 IOS DevType string `json:"devType,omitempty" bson:"devType"` // 设备型号 IP string `json:"ip,omitempty" bson:"ip"` // IP Version string `json:"version,omitempty" bson:"version"` // APP版本号 DevID string `json:"devID,omitempty" bson:"devID"` // 设备ID VisitAt time.Time `json:"visitAt,omitempty" bson:"visitAt"` // 访问时间 } type UserRegisterMsg struct { UserId uint64 `json:"userId" bson:"userId"` // 用户ID AppId int32 `json:"appId" bson:"appId"` // appID SysType string `json:"sysType" bson:"sysType"` // 操作系统类型 安卓 IOS PlatformId string `json:"platformId" bson:"platformId"` // 原始平台流水Id DevType string `json:"devType" bson:"devType"` // 设备类型 Mobile string `json:"mobile" bson:"mobile"` // 手机号 IP string `json:"ip,omitempty" bson:"ip"` // IP Name string `json:"name" bson:"name"` // 名称 IsDirect bool `json:"isDirect" bson:"isDirect"` // true:是直推用户 DistrictCode string `json:"districtCode" bson:"districtCode" ` // 渠道码 PromSeqe string `json:"promSeqe" bson:"promSeqe"` // 全名代理 PromCode string `json:"promCode" bson:"promCode"` // 推广序列 RegisterTime time.Time `json:"registerTime" bson:"registerTime"` // 注册时间时间 } type UserRechargeMsg struct { UserId uint64 `json:"userId" bson:"userId"` // 用户ID AppId int32 `json:"appId" bson:"appId"` // appID PlatformId string `json:"platformId" bson:"platformId"` // 原始平台流水Id SysType string `json:"sysType" bson:"sysType"` // 操作系统类型 安卓 IOS DevType string `json:"devType" bson:"devType"` // 设备类型 ChannelName string `json:"channelName" bson:"channelName"` // 支付渠道名字 CID string `json:"cid" bson:"cid"` // 渠道id Type string `bson:"type" json:"type"` // 充值方式 OrderId string `json:"orderId" bson:"orderId"` // 流水id Money int64 `json:"money" bson:"money"` // 充值金额 订单金额 PayMoney int64 `json:"payMoney" bson:"payMoney"` // 实际到账金额 用户实际支付金额 Status int `json:"status" bson:"status"` // 2付款失败 3付款成功(目前只有成功才发送) Rate string `json:"rate"` // 渠道费率 SuccessAt time.Time `json:"successAt" bson:"successAt"` // 成功时间 CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间 } type UserWithdrawMsg struct { AppId int32 `json:"appId" bson:"appId"` //appID UID uint64 `json:"uid" bson:"uid"` //用户id PlatformId string `json:"platformId" bson:"platformId"` //原始平台流水Id Name string `json:"name" bson:"name"` //用户名 OID string `json:"oid" bson:"oid"` //第三方支付平台id Money int64 `json:"money" bson:"money"` //提现金额 PayMoney int64 `json:"payMoney" bson:"payMoney"` //实际到账 PayType string `json:"payType" bson:"payType"` //提现方式,alipay,bankcard ActName string `json:"actName" bson:"actName"` //交易账户持有人 Act string `json:"act" bson:"act"` //交易账户 DeviceType string `json:"deviceType" bson:"deviceType"` //设备类型 DevID string `json:"devID" bson:"devID"` //设备id Status int `json:"status" bson:"status"` //5提现成功 SuccessAt time.Time `json:"successAt" bson:"successAt"` //回调成功时间 UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` //刷新时间 CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //提现申请时间 IsDirect bool `json:"isDirect" bson:"isDirect"` //true:是直推用户 DistrictCode string `json:"districtCode" bson:"districtCode"` //渠道码 PromCode string `json:"promCode" bson:"promCode"` //用户推广序列 } type BuyVIPType int64 const ( BuyVIPTypeMoney BuyVIPType = 0 + iota // 访问 BuyVIPTypeGold // 金币 ) type UserBuyVIPMsg struct { AppId int32 `json:"appId" bson:"appId"` //appID UID uint64 `json:"uid" bson:"uid"` //用户id PlatformId string `json:"platformId" bson:"platformId"` //原始平台流水Id Name string `json:"name" bson:"name"` //用户名 Money int64 `json:"money" bson:"money"` //提现金额 Amount int64 `json:"amount" bson:"amount"` //金币 BuyVIPType BuyVIPType `json:"buyVIPType" bson:"buyVIPType"` //支付方式 OID string `json:"oid" bson:"oid"` //第三方支付平台id DeviceType string `json:"deviceType" bson:"deviceType"` //设备类型 Status int `json:"status" bson:"status"` //1未审核 5提现成功 SuccessAt time.Time `json:"successTime"` // 成功时间(购买会员、提现、充值) IsDirect bool `json:"isDirect" bson:"isDirect"` //true:是直推用户 DistrictCode string `json:"districtCode" bson:"districtCode"` //渠道码 PromCode string `json:"promCode" bson:"promCode"` //用户推广序列 } // StatcenterSyncList doc // @Tags 数据通过 // @Summary 拉去数据同步信息 // @Description 返回用户收益详情 // @Accept json // @Produce json // @Param param body StatcenterSyncReq true "参数" // @Success 200 {object} StatcenterSyncResp "成功" // @Success 400 {string} string "失败" // @Router /api/web/statcenter/sync [POST] func StatcenterSyncList(ctx *gin.Context) { var request StatcenterSyncReq if err := ctx.ShouldBind(&request); err != nil { common.ServeJSON(ctx, stderr.ErrParamError, err.Error()) return } var ( resp StatcenterSyncResp err error ) resp.Job = request.Job resp.Code = 200 log.Info("StatcenterSyncList", log.Any("job", request.Job)) switch StatJob(request.Job) { case StatJobVisit: resp.VisitList, err = syncUserVisitList(request) case StatJobRegister: resp.RegisterList, err = syncUserRegisterList(request) case StatJobRegisterLight: resp.RegisterList, err = syncH5UserRegisterList(request) case StatJobRecharge: resp.RechargeList, err = syncUserRechargeList(request) case StatJobRechargeAll: resp.RechargeList, err = syncUserAllRechargeList(request) case StatJobVip: resp.VipList, err = syncUserVipList(request) default: resp.Code = 501 resp.Msg = "job is error" } if err != nil { resp.Code = 502 resp.Msg = err.Error() } ctx.JSON(http.StatusOK, resp) } // syncUserVisitList 日活数据同步 func syncUserVisitList(request StatcenterSyncReq) ([]UserVisitMsg, error) { visitList, err := visitlogmod.AccessSyncById(request.PlatformId, request.MaxSize) if err != nil { log.Error("syncUserVisitList visitlogmod.AccessSyncById", log.Any("request", request), log.E(err)) return nil, err } list := make([]UserVisitMsg, 0, len(visitList)) for _, v := range visitList { list = append(list, UserVisitMsg{ UserId: v.UID, AppId: statAppId, SysType: v.SysType, DevType: v.SysType, IP: v.IP, Version: v.Ver, DevID: v.DevID, VisitAt: v.CreatedAt, PlatformId: v.ID.Hex(), }) } return list, nil } // syncUserRegisterList 注册数据同步 func syncUserRegisterList(request StatcenterSyncReq) ([]UserRegisterMsg, error) { filter := bson.M{ "createdAt": bson.M{"$gt": request.SuccessTime}, "userType": bson.M{"$in": []usermod.UserType{usermod.UserUnkown, usermod.UserLoufengAgent}}, } opts := options.Find().SetLimit(request.MaxSize).SetSort(bson.D{{Key: "uid", Value: 1}}) userList, _, err := usermod.FetchList(filter, opts) if err != nil { log.Error("syncUserRegisterList usermod.FetchList", log.Any("request", request), log.E(err)) return nil, err } list := make([]UserRegisterMsg, 0, len(userList)) for _, u := range userList { list = append(list, toUserRegisterMsg(u, u.CreatedAt)) } return list, nil } // syncH5UserRegisterList H5注册数据同步(按绑定手机时间) func syncH5UserRegisterList(request StatcenterSyncReq) ([]UserRegisterMsg, error) { userList, err := usermod.StatcenterSyncBindUserList(request.SuccessTime, request.MaxSize) if err != nil { log.Error("syncH5UserRegisterList usermod.StatcenterSyncBindUserList", log.Any("request", request), log.E(err)) return nil, err } list := make([]UserRegisterMsg, 0, len(userList)) for _, u := range userList { if strings.Contains(u.DevType, "aosp:") { continue } if !isH5SysType(u.SysType) { continue } var registerTime time.Time if u.MobileBindAt != nil { registerTime = *u.MobileBindAt } list = append(list, toUserRegisterMsg(&u, registerTime)) } return list, nil } func toUserRegisterMsg(u *usermod.User, registerTime time.Time) UserRegisterMsg { return UserRegisterMsg{ UserId: u.UID, AppId: statAppId, SysType: u.SysType, DevType: u.DevType, Mobile: u.Mobile, Name: u.Name, IP: u.RegisterIP, IsDirect: u.IsDirect, DistrictCode: u.DistrictCode, PromSeqe: u.PromSeqe, PromCode: u.PromCode, RegisterTime: registerTime, } } // isH5SysType 是否H5端 func isH5SysType(sysType string) bool { s := strings.ToLower(sysType) switch s { case "light", "tg", "web": return true } return strings.Contains(s, "h5") } // syncUserRechargeList 成功充值订单同步 func syncUserRechargeList(request StatcenterSyncReq) ([]UserRechargeMsg, error) { orders, err := rchgordmod.StatCenterSyncRecharge(request.SuccessTime, request.MaxSize) if err != nil { log.Error("syncUserRechargeList rchgordmod.StatCenterSyncRecharge", log.Any("request", request), log.E(err)) return nil, err } list := make([]UserRechargeMsg, 0, len(orders)) for _, o := range orders { msg := toUserRechargeMsg(o) msg.SuccessAt = o.SuccessAt list = append(list, msg) } return list, nil } // syncUserAllRechargeList 全部充值订单同步(不含失败订单) func syncUserAllRechargeList(request StatcenterSyncReq) ([]UserRechargeMsg, error) { orders, err := rchgordmod.StatCenterSyncOrder(request.SuccessTime, request.MaxSize) if err != nil { log.Error("syncUserAllRechargeList rchgordmod.StatCenterSyncOrder", log.Any("request", request), log.E(err)) return nil, err } list := make([]UserRechargeMsg, 0, len(orders)) for _, o := range orders { if o.Status == rchgordmod.FAILURE { continue } msg := toUserRechargeMsg(o) msg.CreatedAt = o.CreatedAt list = append(list, msg) } return list, nil } func toUserRechargeMsg(o rchgordmod.RechargeOrder) UserRechargeMsg { return UserRechargeMsg{ UserId: o.UID, AppId: statAppId, SysType: o.DevType, DevType: o.DevType, PlatformId: o.ID.Hex(), ChannelName: o.Channel, CID: o.OID, Type: o.RechargeType, OrderId: o.ID.Hex(), Money: o.Money, PayMoney: o.PayMoney, Status: o.Status, Rate: o.Rate, } } // syncUserVipList 充值购买会员卡同步 func syncUserVipList(request StatcenterSyncReq) ([]UserBuyVIPMsg, error) { endTime := time.Now().Add(-time.Minute * 2) filter := bson.M{ "tranTypeInt": int64(txnmod.BuyVIP), "createdAt": bson.M{"$gt": request.SuccessTime, "$lt": endTime}, } opts := options.Find().SetLimit(request.MaxSize).SetSort(bson.D{{Key: "createdAt", Value: 1}}) _, logs, err := txnmod.FindTransactionLogs(filter, opts) if err != nil { log.Error("syncUserVipList txnmod.FindTransactionLogs", log.Any("request", request), log.E(err)) return nil, err } list := make([]UserBuyVIPMsg, 0, len(logs)) for _, l := range logs { money := l.Amount if money < 0 { money = -money } list = append(list, UserBuyVIPMsg{ UID: l.UID, AppId: statAppId, PlatformId: l.ID.Hex(), Money: money * 10, // 流水金额为会员卡价格(角),转换为分 BuyVIPType: BuyVIPTypeMoney, OID: l.TransNo.Hex(), DeviceType: l.SysType, Status: 3, SuccessAt: l.CreatedAt, }) } return list, nil }