package sn import ( "context" "encoding/json" "errors" "fmt" "github.com/go-redis/redis/v8" "io/ioutil" "server/call" "server/common" "server/db" "server/modules/web/app" "server/modules/web/providers/base" "server/util" "strconv" "strings" "time" "github.com/gin-gonic/gin" "github.com/liangdas/mqant/log" ) func Sn(e *gin.RouterGroup) { e.POST("/wallet_request", GetBalance) e.POST("/wallet_bet", GameBet) e.POST("/wallet_settlement", Settle) e.POST("/wallet_bet_rollback", Rollback) e.POST("/wallet_update", Update) e.POST("/game_control_callback", GameControlCallback) } func GetBalance(c *gin.Context) { a := app.NewApp(c) defer func() { a.ResponseB() }() req := &GetBalanceReq{} resp := &GetBalanceResp{} a.RetData = resp body, err := ioutil.ReadAll(c.Request.Body) if err != nil { log.Error("read body err: %v", err) resp.Code = INVALIDREQUESTERR return } account := c.GetHeader("account") log.Debug("sn get balance, %s:%s", account, string(body)) err = json.Unmarshal(body, &req) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } headers := make(map[string]string) for key, values := range c.Request.Header { if len(values) > 0 { headers[key] = values[0] // 取第一个值 } } if a.ShouldRouteByAccount(&account, common.ProviderAPITypeJson, req, headers) { return } newBody, _ := json.Marshal(req) var tmpValue map[string]interface{} json.Unmarshal(newBody, &tmpValue) tmpSign := GeneratedSign(tmpValue, WalletKey) if tmpSign != req.Sign { log.Error("sign is wrong, %+v", req) resp.Code = INVALIDREQUESTERR return } uids := account uid, err := strconv.Atoi(uids) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } currency, err := db.Redis().GetInt(common.GetRedisKeyGameCurrency(uid)) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } resp.Msg = "success" resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(currency), 0) * common.DecimalDigits) log.Debug("sn getBalanceResp, %s:%+v", account, resp) } func GameBet(c *gin.Context) { a := app.NewApp(c) defer func() { a.ResponseB() }() req := &GameBetReq{} resp := &GameBetResp{} a.RetData = resp body, err := ioutil.ReadAll(c.Request.Body) if err != nil { log.Error("read body err: %v", err) resp.Code = INVALIDREQUESTERR return } account := c.GetHeader("account") log.Debug("sn game bet, %s:%s", account, string(body)) err = json.Unmarshal(body, &req) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } headers := make(map[string]string) for key, values := range c.Request.Header { if len(values) > 0 { headers[key] = values[0] // 取第一个值 } } if a.ShouldRouteByAccount(&account, common.ProviderAPITypeJson, req, headers) { return } //reqBody, _ := json.Marshal(req) var tmpValue map[string]interface{} err = json.Unmarshal(body, &tmpValue) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } tmpSign := GeneratedSign(tmpValue, WalletKey) if tmpSign != req.Sign { log.Error("sign is wrong, %+v", req) resp.Code = INVALIDREQUESTERR return } uids := account uid, err := strconv.Atoi(uids) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } provider := call.GetConfigGameProvider(common.ProviderSn) now := time.Now().Unix() configGame := call.GetGameListByByID(common.ProviderSn, req.GameId) if configGame == nil { log.Error("get game config err") resp.Code = INVALIDREQUESTERR return } betAmount := req.Amount if betAmount < 0 { betAmount = -req.Amount } betReq := &base.BetReq{ UID: uid, CurrencyType: common.CurrencyINR, SessionType: base.SessionTypeBet, GameID: req.GameId, GameName: configGame.GameCode, Provider: provider, BetID: req.OrderId, SessionID: req.BetId, Time: now, BetAmount: int64(betAmount), TurnOver: int64(betAmount), IsFinish: 1, } betResp := base.SessionBet(betReq) if betResp.Code != base.CodeOk { log.Error("sn gameBetResp err,%s:%v", account, betResp.Code) resp.Code = INVALIDREQUESTERR if betResp.Code == base.CodeAccepted { resp.Msg = "duplicate order" } else if betResp.Code == base.CodeNotEnoughAmount { resp.Msg = "insufficient balance" } else { resp.Msg = "operation failed." } return } resp.Data.DeductionAmount = betAmount resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) resp.Msg = "success" log.Debug("sn gameBetResp, %s:%+v", account, resp) a.Data = resp checkControl(uid) } func Settle(c *gin.Context) { a := app.NewApp(c) defer func() { a.ResponseB() }() req := &SettleReq{} resp := &SettleResp{} a.RetData = resp body, err := ioutil.ReadAll(c.Request.Body) if err != nil { log.Error("read body err: %v", err) resp.Code = INVALIDREQUESTERR return } account := c.GetHeader("account") log.Debug("sn settle, %s:%s", account, string(body)) err = json.Unmarshal(body, &req) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } headers := make(map[string]string) for key, values := range c.Request.Header { if len(values) > 0 { headers[key] = values[0] // 取第一个值 } } if a.ShouldRouteByAccount(&account, common.ProviderAPITypeJson, req, headers) { return } var tmpValue map[string]interface{} err = json.Unmarshal(body, &tmpValue) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } tmpSign := GeneratedSign(tmpValue, WalletKey) if tmpSign != req.Sign { log.Error("sign is wrong, %+v", req) resp.Code = INVALIDREQUESTERR return } uids := account uid, err := strconv.Atoi(uids) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } provider := call.GetConfigGameProvider(common.ProviderSn) var betId string if len(req.Details) > 0 { betId = req.Details[0].BetId } { // todo 订单是否合法 record := &common.ProviderBetRecord{ UID: uid, Provider: provider.ProviderID, Type: base.SessionTypeBet, SessionID: betId, } db.Mysql().Get(record) if record.ID == 0 { resp.Code = INVALIDREQUESTERR resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) log.Debug("sn settleResp, %s:%+v", account, resp) return } } now := time.Now().Unix() configGame := call.GetGameListByByID(common.ProviderSn, req.GameId) if configGame == nil { log.Error("get game config err") resp.Code = INVALIDREQUESTERR return } settleAmount := int64(req.JpBonus + req.BackAmount) betReq := &base.BetReq{ UID: uid, CurrencyType: common.CurrencyINR, SessionType: base.SessionTypeSettle, GameID: req.GameId, GameName: configGame.GameCode, Provider: provider, BetID: req.OrderId, SessionID: betId, Time: now, } isFinish := 1 for _, detail := range req.Details { settleAmount += int64(detail.WinAmount) if detail.IsFinish { isFinish = 2 } } betReq.IsFinish = isFinish betReq.SettleAmount = settleAmount betResp := base.SessionBet(betReq) if betResp.Code != base.CodeOk { resp.Code = INVALIDREQUESTERR if betResp.Code == base.CodeAccepted { resp.Msg = "duplicate order" } else if betResp.Code == base.CodeNotEnoughAmount { resp.Msg = "insufficient balance" } else { resp.Msg = "operation failed." } log.Error("sn gameBetResp err, %s:%+v", account, betResp) return } resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) resp.Msg = "success" log.Debug("sn settleResp, %s:%+v", account, resp) a.Data = resp } func Rollback(c *gin.Context) { a := app.NewApp(c) defer func() { a.ResponseB() }() req := &RollbackReq{} resp := &RollbackResp{} a.RetData = resp body, err := ioutil.ReadAll(c.Request.Body) if err != nil { log.Error("read body err: %v", err) resp.Code = INVALIDREQUESTERR return } account := c.GetHeader("account") log.Debug("sn rollback, %s:%s", account, string(body)) err = json.Unmarshal(body, &req) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } headers := make(map[string]string) for key, values := range c.Request.Header { if len(values) > 0 { headers[key] = values[0] // 取第一个值 } } if a.ShouldRouteByAccount(&account, common.ProviderAPITypeJson, req, headers) { return } var tmpValue map[string]interface{} err = json.Unmarshal(body, &tmpValue) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } tmpSign := GeneratedSign(tmpValue, WalletKey) if tmpSign != req.Sign { log.Error("sign is wrong, %+v", req) resp.Code = INVALIDREQUESTERR return } uids := account uid, err := strconv.Atoi(uids) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } provider := call.GetConfigGameProvider(common.ProviderSn) configGame := call.GetGameListByByID(common.ProviderSn, req.GameId) if configGame == nil { log.Error("get game config err") resp.Code = INVALIDREQUESTERR return } // 订单是否合法 record := &common.ProviderBetRecord{ UID: uid, Provider: provider.ProviderID, SessionID: req.OrderId, } db.Mysql().Get(record) if record.ID == 0 { resp.Code = INVALIDREQUESTERR resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) return } var rollbackAmount int64 if record.Type == base.SessionTypeBet { rollbackAmount = record.Amount } else if record.Type == base.SessionTypeSettle { rollbackAmount = record.Settle - record.Amount } adjustReq := &base.AdjustmentReq{ UID: uid, Amount: rollbackAmount, GameID: req.GameId, GameName: configGame.GameCode, Provider: provider, BetID: req.OrderId, SessionID: req.OrderId, Time: time.Now().Unix(), Type: base.SessionTypeAdjustment, Ess: fmt.Sprintf("%s rollback", configGame.Name), } adjustResp := base.Adjustment(adjustReq) if adjustResp.Code != CodeSuccess { log.Debug("rollback adjustResp code:%d", adjustResp.Code) resp.Code = INVALIDREQUESTERR resp.Msg = "operation failed." return } resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) resp.Msg = "success" log.Debug("sn rollback, %s:%+v", account, resp) a.Data = resp } func Update(c *gin.Context) { a := app.NewApp(c) defer func() { a.ResponseB() }() req := &UpdateReq{} resp := &UpdateResp{} a.RetData = resp body, err := ioutil.ReadAll(c.Request.Body) if err != nil { log.Error("read body err: %v", err) resp.Code = INVALIDREQUESTERR return } account := c.GetHeader("account") log.Debug("sn update, %s:%s", account, string(body)) err = json.Unmarshal(body, &req) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } headers := make(map[string]string) for key, values := range c.Request.Header { if len(values) > 0 { headers[key] = values[0] // 取第一个值 } } if a.ShouldRouteByAccount(&account, common.ProviderAPITypeJson, req, headers) { return } var tmpValue map[string]interface{} err = json.Unmarshal(body, &tmpValue) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } tmpSign := GeneratedSign(tmpValue, WalletKey) if tmpSign != req.Sign { log.Error("sign is wrong, %+v", req) resp.Code = INVALIDREQUESTERR return } uids := account uid, err := strconv.Atoi(uids) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } provider := call.GetConfigGameProvider(common.ProviderSn) configGame := call.GetGameListByByID(common.ProviderSn, req.GameId) if configGame == nil { log.Error("get game config err") resp.Code = INVALIDREQUESTERR return } // 订单是否合法 record := &common.ProviderBetRecord{ UID: uid, Provider: provider.ProviderID, SessionID: req.OrderId, } db.Mysql().Get(record) if record.ID == 0 { resp.Code = INVALIDREQUESTERR resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) return } updateAmount := int64(req.Amount) desc := "game update" if req.SubTypeId == 80006 || req.SubTypeId == 80007 || req.SubTypeId == 80031 || req.SubTypeId == 101010 { if updateAmount < 0 { updateAmount *= -1 } } else if req.SubTypeId == 80016 { } else { if updateAmount > 0 { updateAmount *= -1 } } switch req.SubTypeId { case 80005: // 小费 desc = "game tip" case 80006: desc = "game activity" case 80007: desc = "game jp" case 800016: desc = "game award" case 80030: desc = "create room free" case 80031: desc = "create room award" case 101010: desc = "create room rollback" } adjustReq := &base.AdjustmentReq{ UID: uid, Amount: updateAmount, GameID: req.GameId, GameName: configGame.GameCode, Provider: provider, BetID: req.OrderId, SessionID: req.OrderId, Time: time.Now().Unix(), Type: base.SessionTypeAdjustment, Ess: desc, } adjustResp := base.Adjustment(adjustReq) if adjustResp.Code != CodeSuccess { log.Debug("update adjustResp code:%d", adjustResp.Code) resp.Code = INVALIDREQUESTERR resp.Msg = "operation failed." return } resp.Data.Chips = int(call.GetUserCurrencyFloat(uid, common.CurrencyType(0), 0) * common.DecimalDigits) resp.Msg = "success" log.Debug("sn rollback, %s:%+v", account, resp) a.Data = resp } func GameControlCallback(c *gin.Context) { a := app.NewApp(c) defer func() { a.ResponseB() }() req := &GameControlCallbackReq{} resp := &GameControlCallbackResp{} a.RetData = resp body, err := ioutil.ReadAll(c.Request.Body) if err != nil { log.Error("read body err: %v", err) resp.Code = INVALIDREQUESTERR return } account := c.GetHeader("account") log.Debug("sn gameControlCallback, %s:%s", account, string(body)) err = json.Unmarshal(body, &req) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } headers := make(map[string]string) for key, values := range c.Request.Header { if len(values) > 0 { headers[key] = values[0] // 取第一个值 } } if a.ShouldRouteByAccount(&account, common.ProviderAPITypeJson, req, headers) { return } var tmpValue map[string]interface{} err = json.Unmarshal(body, &tmpValue) if err != nil { log.Error("unmarshal err, %s", err.Error()) resp.Code = INVALIDREQUESTERR return } tmpSign := GeneratedSign(tmpValue, ControlKey) if tmpSign != req.Sign { log.Error("sign is wrong, %+v", req) resp.Code = INVALIDREQUESTERR return } uids := account uid, err := strconv.Atoi(uids) if err != nil { log.Error("err:%v", err) resp.Code = INVALIDREQUESTERR return } resp.Msg = "success" log.Debug("gameControlCallback resp:%+v", resp) a.Data = resp if req.Type == "3" { // 退出回调 return } defer func() { err = Control(uid, util.ToInt(req.ControlId), "", 0) if err != nil { log.Error("sn control err, %s", err.Error()) } }() } var controlGameId = []int{ 23007913, } func checkSpecialControl(result string) bool { if result == "" { return false } for _, v := range controlGameId { if result == fmt.Sprintf("%d_%d", common.ProviderSn, v) { return true } } return false } const specialControlRtp = 85 func checkControl(uid int) { result, err := db.Redis().GetRedis().Get(context.Background(), fmt.Sprintf("player:%v:sn:status", uid)).Result() if err != nil { if errors.Is(err, redis.Nil) { return } log.Error("get user sn status err, %s", err.Error()) return } if result == "" { log.Error("get user sn status is nil, %d", uid) return } //log.Debug("sn control update, uid:%d status:%s", uid, result) controlInfos := strings.Split(result, "|") rtp := util.ToInt(controlInfos[0]) templateId := controlInfos[1] rtpNow := getControlRtp(uid) totalRecharge, defaultValue, templateIdNow := getControlTemplate(uid) var update bool if templateId != "" && templateId != templateIdNow { // 直接更新 update = true } if templateId == "" && rtp != rtpNow { update = true } if update { log.Debug("sn control update, uid:%d old info:%d|%s new info:%d|%s(%d|%d)", uid, rtp, templateId, rtpNow, templateIdNow, totalRecharge, defaultValue) // todo 主动推会一直报错,兼容处理 err = db.Redis().GetRedis().Set(context.Background(), fmt.Sprintf("player:%v:sn:status", uid), fmt.Sprintf("%d|%s", rtpNow, templateIdNow), 0).Err() if err != nil { log.Error("sn control, set user sn status err, %s", err.Error()) } Control(uid, 0, templateIdNow, rtpNow) } } func getControlRtp(uid int) int { rtp := call.GetRtpControlV1(uid) result, _ := db.Redis().GetRedis().Get(context.Background(), fmt.Sprintf("player:%v:games:at", uid)).Result() if rtp > specialControlRtp && checkSpecialControl(result) { rtp = specialControlRtp log.Debug("rtpControl special, uid:%d result:%s, rtp:%d", uid, result, specialControlRtp) } return rtp } func getControlTemplate(uid int) (int64, int, string) { templateId := call.GetConfigRtpTemplateId() if templateId == "" && DefaultTemplateId != "" { log.Debug("sn cfg templateId is nil") templateId = DefaultTemplateId } defaultTemplateRecharge := 300 * common.DecimalDigits userInfo, _ := call.GetUserInfo(uid) channelInfo := call.GetChannelByID(userInfo.ChannelID) if channelInfo.TemplateRecharge > 0 { defaultTemplateRecharge = channelInfo.TemplateRecharge } rechargeInfo := call.GetRechargeInfo(uid) if rechargeInfo.TotalRecharge >= int64(defaultTemplateRecharge) { templateId = "" } //log.Debug("sn control template, uid:%d %d|%d %s", uid, rechargeInfo.TotalRecharge, defaultTemplateRecharge, templateId) return rechargeInfo.TotalRecharge, defaultTemplateRecharge, templateId } func Control(uid int, controlId int, templateId string, rtp int) error { if controlId == 0 { controlId = 1 } var totalRecharge int64 var defaultValue int if rtp == 0 && templateId == "" { rtp = getControlRtp(uid) totalRecharge, defaultValue, templateId = getControlTemplate(uid) } req := &ControlReq{ BaseReq: BaseReq{ SnAccount: SnAccount, Time: time.Now().Unix(), }, ThirdName: call.GetProviderUserName(fmt.Sprintf("%d", uid)), ControlId: controlId, Sn: SnId, AgentId: AgentId, TemplateId: templateId, Data: []struct { TargetRtp int `json:"target_rtp"` }{ { TargetRtp: rtp, }, }, } log.Debug("sn control req, %+v templateInfo(%d|%d)", *req, totalRecharge, defaultValue) reqBody, _ := json.Marshal(req) var tmpValue map[string]interface{} json.Unmarshal(reqBody, &tmpValue) req.Sign = GeneratedSign(tmpValue, ApiKey) var resp ControlResp err := util.HttpPost(APIControl, req, &resp, nil) if err != nil { log.Error("err:%v", err) return err } if resp.Code != CodeRequestSuccess && resp.Code != CodeRequestExist { log.Error("sn control err, %+v", resp) return fmt.Errorf("sn control err, %+v ", resp) } err = db.Redis().GetRedis().Set(context.Background(), fmt.Sprintf("player:%v:sn:status", uid), fmt.Sprintf("%d|%s", rtp, templateId), 0).Err() if err != nil { log.Error("sn control, set user sn status err, %s", err.Error()) } return nil } func InitGameControlTemplate() error { req := &ControlTemplateReq{ BaseReq: BaseReq{ SnAccount: SnAccount, Time: time.Now().Unix(), }, PageSize: 100, } log.Debug("sn get control template req, %+v", *req) reqBody, _ := json.Marshal(req) var tmpValue map[string]interface{} json.Unmarshal(reqBody, &tmpValue) req.Sign = GeneratedSign(tmpValue, ApiKey) var resp ControlTemplateResp err := util.HttpPost(APIGameControlTemplate, req, &resp, nil) if err != nil { log.Error("err:%v", err) return err } if resp.Code != CodeRequestSuccess && resp.Code != CodeRequestExist { log.Error("sn get control template err, %+v", resp) return fmt.Errorf("sn get control template err, %+v ", resp) } for _, v := range resp.Data.List { v.Desc = strings.ReplaceAll(v.Desc, "倍", "") descList := strings.Split(v.Desc, "%") log.Debug("sn template desc:%s", descList) err = db.Mysql().C().Model(&common.ConfigRtpTemplate{}).Where("rtp = ? and max_odds = ?", util.ToInt(descList[0]), util.ToInt(descList[1])). Updates(map[string]interface{}{ "template_id": v.TemplateId, }).Error if err != nil { log.Error("update rtp template err, %s", err.Error()) continue } if strings.Contains(v.Desc, "90%50倍") { DefaultTemplateId = v.TemplateId } } call.LoadConfigRtpTemplate() return nil }