add statistics
This commit is contained in:
parent
2b88155177
commit
801200f4bd
|
@ -204,3 +204,8 @@ func StrTimeToTime(s string) time.Time {
|
|||
t, _ := time.ParseInLocation("2006-01-02 15:04:05", s, time.Local)
|
||||
return t
|
||||
}
|
||||
|
||||
func StrRFC3339TimeToTime(s string) time.Time {
|
||||
t, _ := time.Parse(time.RFC3339, s)
|
||||
return t
|
||||
}
|
||||
|
|
10
go.mod
10
go.mod
|
@ -26,13 +26,13 @@ require (
|
|||
github.com/tomas-qstarrs/boost v1.0.3
|
||||
github.com/tomas-qstarrs/excel-converter v1.0.2
|
||||
github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec
|
||||
github.com/xuri/excelize/v2 v2.9.0
|
||||
github.com/zegoim/zego_server_assistant/token/go/src v0.0.0-20231013093807-4e80bab42ec3
|
||||
github.com/zeromicro/go-zero v1.7.3
|
||||
go.etcd.io/etcd/client/v3 v3.5.16
|
||||
go.mongodb.org/mongo-driver v1.17.1
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
|
||||
google.golang.org/protobuf v1.35.1
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/gorm v1.25.12
|
||||
mongo.games.com/goserver v0.0.0-00010101000000-000000000000
|
||||
)
|
||||
|
@ -70,7 +70,7 @@ require (
|
|||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.4 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
|
@ -92,7 +92,8 @@ require (
|
|||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/xtaci/kcp-go v5.4.20+incompatible // indirect
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
|
||||
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d // indirect
|
||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
|
||||
|
@ -103,7 +104,7 @@ require (
|
|||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
|
@ -115,4 +116,5 @@ require (
|
|||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.5.7 // indirect
|
||||
)
|
||||
|
|
15
go.sum
15
go.sum
|
@ -279,8 +279,8 @@ github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7
|
|||
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
|
||||
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
|
||||
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
|
@ -386,8 +386,12 @@ github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45
|
|||
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
|
||||
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
|
||||
github.com/xuri/efp v0.0.0-20200605144744-ba689101faaf/go.mod h1:uBiSUepVYMhGTfDeBKKasV4GpgBlzJ46gXUBAqV8qLk=
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d h1:llb0neMWDQe87IzJLS4Ci7psK/lVsjIS2otl+1WyRyY=
|
||||
github.com/xuri/efp v0.0.0-20240408161823-9ad904a10d6d/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/excelize/v2 v2.9.0 h1:1tgOaEq92IOEumR1/JfYS/eR0KHOCsRv/rYXXh6YJQE=
|
||||
github.com/xuri/excelize/v2 v2.9.0/go.mod h1:uqey4QBZ9gdMeWApPLdhm9x+9o2lq4iVmjiLfBS5hdE=
|
||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7 h1:hPVCafDV85blFTabnqKgNhDCkJX25eik94Si9cTER4A=
|
||||
github.com/xuri/nfp v0.0.0-20240318013403-ab9948c2c4a7/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
@ -441,8 +445,9 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
|||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/image v0.0.0-20200922025426-e59bae62ef32/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
|
||||
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# ---> Go
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.xlsx
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
local_test.go
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
log
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# statistics
|
||||
|
||||
数据分析服务
|
||||
* mongodb同步到mysql
|
||||
* 接收消息队列数据
|
||||
* 历史数据查询
|
||||
* 数据统计
|
|
@ -0,0 +1,8 @@
|
|||
set GOPATH=D:\godev
|
||||
go env -w GO111MODULE=on
|
||||
|
||||
set CGO_ENABLED=0
|
||||
set GOOS=linux
|
||||
set GOARCH=amd64
|
||||
go build
|
||||
pause
|
|
@ -0,0 +1,8 @@
|
|||
package constant
|
||||
|
||||
const (
|
||||
InviteScoreTypeBind = 1 // 绑定邀请码
|
||||
InviteScoreTypePay = 2 // 充值返佣
|
||||
InviteScoreTypeRecharge = 3 // 充值完成
|
||||
InviteScoreTypePayMe = 4 // 充值(自己)
|
||||
)
|
|
@ -0,0 +1,27 @@
|
|||
# 平台id
|
||||
platforms:
|
||||
- 1
|
||||
|
||||
# 几秒同步一次数据
|
||||
# 注册表,登录日志表
|
||||
update_second: 60
|
||||
# 注册表每次同步多少条数据
|
||||
update_account_num: 100
|
||||
# 登录日志每次同步多少条数据
|
||||
update_login_num: 100
|
||||
# 几秒读取一次玩家id列表
|
||||
update_second_snid: 30
|
||||
# 最多触发几个玩家数据更新
|
||||
update_snid_num: 100
|
||||
|
||||
# 邀请数据统计
|
||||
# 几秒读取一次邀请记录
|
||||
update_second_invite: 10
|
||||
# 一次最多读取多少条邀请记录
|
||||
update_invite_num: 30
|
||||
|
||||
# 道具获得数量统计
|
||||
# 几秒读取一次道具日志
|
||||
update_second_item: 10
|
||||
# 一次最多读取多少道具日志
|
||||
update_item_num: 100
|
|
@ -0,0 +1,53 @@
|
|||
global:
|
||||
user:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_global
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
log:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_log
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
monitor:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_monitor
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
platforms:
|
||||
0:
|
||||
user:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_user_plt_000
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
log:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_log_plt_000
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
1:
|
||||
user:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_user_plt_001
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
log:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_log_plt_001
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
|
@ -0,0 +1,38 @@
|
|||
platforms:
|
||||
global:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: win88_user
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
0:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: win88_plt_000
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
1:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: win88_plt_001
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
count: # 破产日志库
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: dbmis_count
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
|
||||
# 最大空闲连接数
|
||||
MaxIdleConns: 10
|
||||
# 最大连接数
|
||||
MaxOpenConns: 100
|
||||
# 连接可复用的最大时间
|
||||
ConnMaxLifetime: 3600
|
||||
# 连接最大空闲时间
|
||||
ConnMaxIdletime: 0
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<seelog type="adaptive" mininterval="2000000" maxinterval="100000000" critmsgcount="500" minlevel="trace">
|
||||
<exceptions>
|
||||
<exception filepattern="test*" minlevel="error"/>
|
||||
</exceptions>
|
||||
<outputs formatid="all">
|
||||
<rollingfile formatid="all" type="size" filename="./all.log" maxsize="50000000" maxrolls="5" />
|
||||
<filter levels="info,trace,warn">
|
||||
<console formatid="fmtinfo"/>
|
||||
</filter>
|
||||
<filter levels="error,critical" formatid="fmterror">
|
||||
<console/>
|
||||
<file path="errors.log"/>
|
||||
</filter>
|
||||
</outputs>
|
||||
<formats>
|
||||
<format id="fmtinfo" format="[%Date][%Time] [%Level] %Msg%n"/>
|
||||
<format id="fmterror" format="[%Date][%Time] [%LEVEL] [%FuncShort @ %File.%Line] %Msg%n"/>
|
||||
<format id="all" format="[%Date][%Time] [%Level] [@ %File.%Line] %Msg%n"/>
|
||||
<format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/>
|
||||
</formats>
|
||||
</seelog>
|
|
@ -0,0 +1,212 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
"mongo.games.com/goserver/core/mongox"
|
||||
"mongo.games.com/goserver/core/mysqlx"
|
||||
"mongo.games.com/goserver/core/utils"
|
||||
"mongo.games.com/goserver/core/viperx"
|
||||
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
"mongo.games.com/game/statistics/static"
|
||||
"mongo.games.com/game/statistics/syn"
|
||||
)
|
||||
|
||||
var VP *viper.Viper
|
||||
|
||||
// DoTick 定时执行
|
||||
func DoTick(ctx context.Context, wg *sync.WaitGroup, duration time.Duration, fu func(ctx context.Context)) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(duration):
|
||||
utils.RecoverPanicFunc() // 捕获异常
|
||||
fu(ctx)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// DoTickPlatform 定时执行,根据platform执行
|
||||
func DoTickPlatform(ctx context.Context, wg *sync.WaitGroup, duration time.Duration, batchSize int,
|
||||
fu func(ctx context.Context, platform string, batchSize int)) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(duration):
|
||||
utils.RecoverPanicFunc() // 捕获异常
|
||||
wg := new(sync.WaitGroup)
|
||||
for _, v := range VP.GetStringSlice("platforms") {
|
||||
platform := v
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
fu(ctx, platform, batchSize)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
VP = viperx.GetViper("config", "yaml")
|
||||
// mongo
|
||||
vp := viperx.GetViper("mongo", "yaml")
|
||||
// mongo初始化
|
||||
conf := &mongox.Config{}
|
||||
err := vp.Unmarshal(conf)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("mongo config error: %v", err))
|
||||
}
|
||||
mongox.Init(conf)
|
||||
defer mongox.Close()
|
||||
|
||||
// mysql
|
||||
vp = viperx.GetViper("mysql", "yaml")
|
||||
myConf := &mysqlx.Config{}
|
||||
err = vp.Unmarshal(myConf)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("mysql config error: %v", err))
|
||||
}
|
||||
mysqlx.Init(myConf)
|
||||
defer mysqlx.Close()
|
||||
|
||||
mysqlx.SetAutoMigrateTables(mysqlmodel.Tables)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
DoTick(ctx, wg, time.Duration(VP.GetInt64("update_second"))*time.Second, SyncSnId)
|
||||
|
||||
DoTick(ctx, wg, time.Duration(VP.GetInt64("update_second_snid"))*time.Second, func(ctx context.Context) {
|
||||
wg := new(sync.WaitGroup)
|
||||
for _, v := range VP.GetStringSlice("platforms") {
|
||||
platform := v
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
Static(platform)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
DoTick(ctx, wg, time.Duration(VP.GetInt64("update_second_invite"))*time.Second, SyncInvite)
|
||||
|
||||
DoTickPlatform(ctx, wg, time.Duration(VP.GetInt64("update_second_item"))*time.Second, VP.GetInt("update_item_num"),
|
||||
func(ctx context.Context, platform string, batchSize int) {
|
||||
err := syn.ItemGainDone(&syn.Data[mongomodel.ItemLog]{
|
||||
Platform: platform,
|
||||
BatchSize: batchSize,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("SyncItem error:%v", err)
|
||||
}
|
||||
})
|
||||
|
||||
logger.Logger.Info("start")
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, os.Kill)
|
||||
sig := <-c
|
||||
logger.Logger.Infof("closing down (signal: %v)", sig)
|
||||
|
||||
// release
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
logger.Logger.Info("closed")
|
||||
}
|
||||
|
||||
// SyncSnId 同步注册和登录日志
|
||||
func SyncSnId(ctx context.Context) {
|
||||
wg := new(sync.WaitGroup)
|
||||
for _, v := range VP.GetStringSlice("platforms") {
|
||||
platform := v
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_, err := syn.UserAccount(platform, VP.GetInt("update_account_num"))
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("SyncUserAccount error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = syn.LogLogin(platform, VP.GetInt("update_login_num"))
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("SyncLogLogin error: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Static 玩家id触发数据统计
|
||||
func Static(platform string) {
|
||||
// 查询需要更新的玩家id
|
||||
var ids []*mysqlmodel.UserID
|
||||
db, err := mysqlx.GetDatabase(platform)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GetDatabase error: %v", err)
|
||||
return
|
||||
}
|
||||
if err := db.Limit(VP.GetInt("update_snid_num")).Find(&ids).Error; err != nil {
|
||||
logger.Logger.Warnf("Get UserID error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(ids) == 0 {
|
||||
logger.Logger.Tracef("Static: no need to update")
|
||||
return
|
||||
}
|
||||
|
||||
// 统计玩家跳出记录
|
||||
if err := static.UserLogin(platform, ids); err != nil {
|
||||
logger.Logger.Errorf("StaticUserLogin error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 删除更新过的玩家id
|
||||
if err := db.Delete(ids).Error; err != nil {
|
||||
logger.Logger.Errorf("Delete error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// SyncInvite 同步邀请数据
|
||||
func SyncInvite(ctx context.Context) {
|
||||
wg := new(sync.WaitGroup)
|
||||
for _, v := range VP.GetStringSlice("platforms") {
|
||||
platform := v
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := syn.SyncInviteScore(platform, VP.GetInt("update_invite_num"))
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("SyncInviteScore error: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package modelmongo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
const LogGamePlayerListLog = "log_gameplayerlistlog"
|
||||
|
||||
type GamePlayerListLog struct {
|
||||
LogId primitive.ObjectID `bson:"_id"`
|
||||
SnId int32 //用户Id
|
||||
Name string //名称
|
||||
GameId int32 //游戏id
|
||||
BaseScore int32 //游戏底注
|
||||
ClubId int32 //俱乐部Id
|
||||
ClubRoom string //俱乐部包间
|
||||
TaxCoin int64 //税收
|
||||
ClubPumpCoin int64 //俱乐部额外抽水
|
||||
Platform string //平台id
|
||||
Channel string //渠道
|
||||
Promoter string //推广员
|
||||
PackageTag string //包标识
|
||||
SceneId int32 //场景ID
|
||||
GameMode int32 //游戏类型
|
||||
GameFreeid int32 //游戏类型房间号
|
||||
GameDetailedLogId string //游戏记录Id
|
||||
IsFirstGame bool //是否第一次游戏
|
||||
//对于拉霸类:BetAmount=100 WinAmountNoAnyTax=0 (表示投入多少、收益多少,值>=0)
|
||||
//拉霸类小游戏会是:BetAmount=0 WinAmountNoAnyTax=100 (投入0、收益多少,值>=0)
|
||||
//对战场:BetAmount=0 WinAmountNoAnyTax=100 (投入会有是0、收益有正负,WinAmountNoAnyTax=100则盈利,WinAmountNoAnyTax=-100则输100)
|
||||
BetAmount int64 //下注金额
|
||||
WinAmountNoAnyTax int64 //盈利金额,不包含任何税
|
||||
TotalIn int64 //本局投入
|
||||
TotalOut int64 //本局产出
|
||||
Time time.Time //记录时间
|
||||
RoomType int32 //房间类型
|
||||
GameDif string //游戏标识
|
||||
GameClass int32 //游戏类型 1棋牌 2电子 3百人 4捕鱼 5视讯 6彩票 7体育
|
||||
MatchId int32
|
||||
MatchType int32 //0.普通场 1.锦标赛 2.冠军赛 3.vip专属
|
||||
Ts int32
|
||||
IsFree bool //拉霸专用 是否免费
|
||||
WinSmallGame int64 //拉霸专用 小游戏奖励
|
||||
WinTotal int64 //拉霸专用 输赢
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package modelmongo
|
||||
|
||||
import (
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
const LogInviteScore = "log_invitescore"
|
||||
|
||||
type InviteScore struct {
|
||||
Id primitive.ObjectID `bson:"_id"`
|
||||
UpSnid int // 上级代理
|
||||
DownSnid int // 下级代理
|
||||
Level int // 代理层级 例如 1:DownSnid 是 UpSnid 的 1 级代理; 2: DownSnid 是 UpSnid 的 2 级代理
|
||||
Tp int // 返佣类型
|
||||
Rate int // 返佣比例
|
||||
Score int // 积分
|
||||
Money int // 充值金额
|
||||
Ts int // 时间戳
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package modelmongo
|
||||
|
||||
import "go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
const LogItem = "log_itemlog"
|
||||
|
||||
type ItemInfo struct {
|
||||
ItemId int32
|
||||
ItemNum int64
|
||||
}
|
||||
|
||||
type ItemLog struct {
|
||||
LogId primitive.ObjectID `bson:"_id"`
|
||||
Platform string //平台
|
||||
SnId int32 //玩家id
|
||||
LogType int32 //记录类型 0.获取 1.消耗
|
||||
ItemId int32 //道具id
|
||||
ItemName string //道具名称
|
||||
Count int64 //个数
|
||||
CreateTs int64 //记录时间
|
||||
Remark string //备注
|
||||
TypeId int32 // 变化类型
|
||||
GameId int32 // 游戏id,游戏中获得时有值
|
||||
GameFreeId int32 // 场次id,游戏中获得时有值
|
||||
Cost []*ItemInfo // 消耗的道具
|
||||
Id string // 撤销的id,兑换失败
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package modelmongo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
const LogLogin = "log_login"
|
||||
|
||||
const (
|
||||
LogTypeLogin int32 = iota // 登录
|
||||
LogTypeLogout // 登出
|
||||
LogTypeRehold // 重连
|
||||
LogTypeDrop // 掉线
|
||||
)
|
||||
|
||||
type LoginLog struct {
|
||||
LogId primitive.ObjectID `bson:"_id"`
|
||||
Platform string //平台id
|
||||
SnId int32
|
||||
LogType int32
|
||||
Ts int64
|
||||
Time time.Time
|
||||
GameId int // 玩家掉线时所在游戏id
|
||||
LastGameID int // 玩家最后所在游戏id
|
||||
ChannelId string // 推广渠道
|
||||
|
||||
DeviceName string
|
||||
AppVersion string
|
||||
BuildVersion string
|
||||
AppChannel string
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package modelmongo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
const UserAccount = "user_account"
|
||||
|
||||
type Account struct {
|
||||
AccountId primitive.ObjectID `bson:"_id"`
|
||||
SnId int32 // 玩家账号直接在这里生成
|
||||
Platform string // 平台
|
||||
RegisterTs int64 // 注册时间戳
|
||||
RegisteTime time.Time
|
||||
ChannelId string // 推广渠道
|
||||
|
||||
Tel string `gorm:"index"`
|
||||
DeviceName string `gorm:"index"`
|
||||
AppVersion string `gorm:"index"`
|
||||
BuildVersion string `gorm:"index"`
|
||||
AppChannel string `gorm:"index"`
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package modelmysql
|
||||
|
||||
type Bankrupt struct {
|
||||
Id int `json:"id" gorm:"column:id"`
|
||||
Platform int `json:"platform" gorm:"column:platform"`
|
||||
Snid int `json:"snid" gorm:"column:snid"`
|
||||
RegTs int `json:"register_time" gorm:"column:register_time"`
|
||||
GameId int `json:"game_id" gorm:"column:game_id"`
|
||||
GameFreeId int `json:"game_free_id" gorm:"column:game_free_id"`
|
||||
Coin int `json:"use_coin" gorm:"column:use_coin"`
|
||||
Ts int `json:"bankrupt_time" gorm:"column:bankrupt_time"`
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package modelmysql
|
||||
|
||||
type LogInviteScoreMid struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
MID string
|
||||
}
|
||||
|
||||
type LogInviteScore struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
UpSnid int `gorm:"index"` // 上级代理
|
||||
DownSnid int `gorm:"index"` // 下级代理
|
||||
Level int `gorm:"index"` // 代理层级 例如 1:DownSnid 是 UpSnid 的 1 级代理; 2: DownSnid 是 UpSnid 的 2 级代理
|
||||
Tp int `gorm:"index"` // 返佣类型
|
||||
Rate int `gorm:"index"` // 返佣比例
|
||||
Score int `gorm:"index"` // 积分
|
||||
Money int `gorm:"index"` // 充值金额
|
||||
Ts int `gorm:"index"` // 时间戳
|
||||
}
|
||||
|
||||
type LogInviteUser struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Psnid int `gorm:"index"` // 当前玩家
|
||||
Snid int `gorm:"index"` // 一级代理
|
||||
Level int `gorm:"index"` // 代理层级 例如 1:DownSnid 是 UpSnid 的 1 级代理; 2: DownSnid 是 UpSnid 的 2 级代理
|
||||
Ts int `gorm:"index"` // 绑定时间
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package modelmysql
|
||||
|
||||
// ItemGain 道具获得数量,以小时,道具id,做主键
|
||||
type ItemGain struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Hour int64 `gorm:"index:idx_item"` // 小时时间戳,每小时统计一次
|
||||
ItemId int32 `gorm:"index:idx_item"` // 道具id
|
||||
ItemNum int64 // 道具数量
|
||||
}
|
||||
|
||||
// ItemTotalGain 道具获得总数
|
||||
type ItemTotalGain struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
ItemId int32 `gorm:"index"` // 道具id
|
||||
ItemNum int64 // 道具数量
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package modelmysql
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
LogTypeLogin = 1 // 登录
|
||||
LogTypeRehold = 2 // 重连
|
||||
LogTypeOffline = 3 // 离线
|
||||
)
|
||||
|
||||
type LogLogin struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Snid int `gorm:"index"`
|
||||
OnlineType int `gorm:"index"`
|
||||
//OnlineTs int `gorm:"index"`
|
||||
OnlineTime time.Time `gorm:"index"`
|
||||
OfflineType int `gorm:"index"`
|
||||
//OfflineTs int `gorm:"index"`
|
||||
OfflineTime time.Time `gorm:"index"`
|
||||
ChannelId string `gorm:"index"` // 推广渠道
|
||||
|
||||
DeviceName string `gorm:"index"`
|
||||
AppVersion string `gorm:"index"`
|
||||
BuildVersion string `gorm:"index"`
|
||||
AppChannel string `gorm:"index"`
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package modelmysql
|
||||
|
||||
type LogLoginMid struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
MID string
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package modelmysql
|
||||
|
||||
const (
|
||||
MidTypeItem = 1 // 道具记录
|
||||
)
|
||||
|
||||
type LogMid struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Tp int `gorm:"index"` // 类型
|
||||
MID string
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package modelmysql
|
||||
|
||||
// 需要自动迁移的表添加在这里 Tables
|
||||
|
||||
var Tables = []interface{}{
|
||||
&LogLogin{},
|
||||
&LogLoginMid{},
|
||||
&UserAccount{},
|
||||
&UserLogin{},
|
||||
&UserID{},
|
||||
&LogInviteScoreMid{},
|
||||
&LogInviteScore{},
|
||||
&LogInviteUser{},
|
||||
&LogMid{},
|
||||
&ItemGain{},
|
||||
&ItemTotalGain{},
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package modelmysql
|
||||
|
||||
import "time"
|
||||
|
||||
type UserAccount struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
MID string
|
||||
Snid int `gorm:"index"`
|
||||
//RegisterTs int `gorm:"index"`
|
||||
RegisterTime time.Time `gorm:"index"`
|
||||
ChannelId string `gorm:"index"` // 推广渠道
|
||||
|
||||
DeviceName string `gorm:"index"`
|
||||
AppVersion string `gorm:"index"`
|
||||
BuildVersion string `gorm:"index"`
|
||||
AppChannel string `gorm:"index"`
|
||||
Tel string `gorm:"index"`
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package modelmysql
|
||||
|
||||
/*
|
||||
服务定期查询注册和登录信息,然后获取玩家id,保存到这张表中;用于后续触发和玩家相关的数据统计
|
||||
*/
|
||||
|
||||
type UserID struct {
|
||||
Snid int `gorm:"primaryKey"`
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package modelmysql
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
OutTypRegister = 1 // 注册
|
||||
OutTypeLogin = 2 // 登录
|
||||
OutTypeGaming = 3 // 游戏中
|
||||
OutTypeGameOver = 4 // 游戏结束
|
||||
)
|
||||
|
||||
type UserLogin struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
Snid int `gorm:"uniqueIndex"`
|
||||
//OnlineTs int `gorm:"index"`
|
||||
OnlineTime time.Time `gorm:"index"`
|
||||
//OfflineTs int `gorm:"index"`
|
||||
OfflineTime time.Time `gorm:"index"`
|
||||
OutType int `gorm:"index"` // 跳出类型
|
||||
GameID int `gorm:"index"` // 游戏id
|
||||
Age int
|
||||
Sex int
|
||||
DeviceName string `gorm:"index"`
|
||||
AppVersion string `gorm:"index"`
|
||||
BuildVersion string `gorm:"index"`
|
||||
AppChannel string `gorm:"index"`
|
||||
Tel string `gorm:"index"`
|
||||
ChannelId string `gorm:"index"` // 推广渠道
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package mq
|
|
@ -0,0 +1 @@
|
|||
接收消息队列
|
|
@ -0,0 +1 @@
|
|||
业务统计
|
|
@ -0,0 +1,371 @@
|
|||
package static
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
)
|
||||
|
||||
func getAccountTel(platform string, id int) (string, error) {
|
||||
acc := &mongomodel.Account{}
|
||||
cc, err := mymongo.GetUserCollection(platform, mongomodel.UserAccount)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.UserAccount, err)
|
||||
return "", err
|
||||
}
|
||||
dd := cc.FindOne(context.TODO(), bson.M{"snid": id}, options.FindOne().SetProjection(bson.M{"tel": 1}))
|
||||
err = dd.Err()
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Tracef("getAccountTel %v not found in user_account", id)
|
||||
return "", nil
|
||||
}
|
||||
logger.Logger.Errorf("getAccountTel %v get user_account err: %v", id, err)
|
||||
return "", err
|
||||
}
|
||||
if err := dd.Decode(acc); err != nil {
|
||||
logger.Logger.Errorf("getAccountTel %v decode user_account err: %v", id, err)
|
||||
return "", err
|
||||
}
|
||||
return acc.Tel, nil
|
||||
}
|
||||
|
||||
// 游戏结束离开
|
||||
func checkGameOver(db *mymysql.Database, login *mysqlmodel.UserLogin, platform string, id int) (bool, error) {
|
||||
// 最早的一条掉线记录并且是游戏结束离开
|
||||
a := &mongomodel.LoginLog{}
|
||||
c, err := mymongo.GetLogCollection(platform, mongomodel.LogLogin)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.LogLogin, err)
|
||||
return false, err
|
||||
}
|
||||
d := c.FindOne(context.TODO(), bson.M{"snid": id, "logtype": mongomodel.LogTypeDrop, "gameid": 0, "lastgameid": bson.D{{"$gt", 0}}},
|
||||
options.FindOne().SetSort(bson.D{{"time", 1}}))
|
||||
err = d.Err()
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Tracef("checkGameOver %v not found in log_login", id)
|
||||
return false, nil
|
||||
}
|
||||
logger.Logger.Errorf("checkGameOver %v get log_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
if err := d.Decode(a); err != nil {
|
||||
logger.Logger.Errorf("checkGameOver %v decode log_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// account tel
|
||||
tel, err := getAccountTel(platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Warnf("get account tel %v err: %v", id, err)
|
||||
}
|
||||
|
||||
update := &mysqlmodel.UserLogin{
|
||||
//OfflineTs: int(a.Ts),
|
||||
OfflineTime: a.Time,
|
||||
OutType: mysqlmodel.OutTypeGameOver,
|
||||
GameID: a.LastGameID,
|
||||
Tel: tel,
|
||||
DeviceName: a.DeviceName,
|
||||
AppVersion: a.AppVersion,
|
||||
BuildVersion: a.BuildVersion,
|
||||
AppChannel: a.AppChannel,
|
||||
ChannelId: a.ChannelId,
|
||||
}
|
||||
|
||||
if err := db.Model(login).Select(
|
||||
"OfflineTime", "OutType", "GameID", "DeviceName", "AppVersion", "BuildVersion", "AppChannel", "Tel",
|
||||
).Updates(update).Error; err != nil {
|
||||
logger.Logger.Errorf("checkLogin %v update user_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 游戏中离开
|
||||
func checkGaming(db *mymysql.Database, login *mysqlmodel.UserLogin, platform string, id int) (bool, error) {
|
||||
// 最早的一条掉线记录并且是游戏中掉线
|
||||
a := &mongomodel.LoginLog{}
|
||||
c, err := mymongo.GetLogCollection(platform, mongomodel.LogLogin)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.LogLogin, err)
|
||||
return false, err
|
||||
}
|
||||
d := c.FindOne(context.TODO(), bson.M{"snid": id, "logtype": mongomodel.LogTypeDrop, "gameid": bson.D{{"$gt", 0}}},
|
||||
options.FindOne().SetSort(bson.D{{"time", 1}}))
|
||||
err = d.Err()
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Tracef("checkGaming %v not found in log_login", id)
|
||||
return false, nil
|
||||
}
|
||||
logger.Logger.Errorf("checkGaming %v get log_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
if err := d.Decode(a); err != nil {
|
||||
logger.Logger.Errorf("checkGaming %v decode log_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// account tel
|
||||
tel, err := getAccountTel(platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Warnf("get account tel %v err: %v", id, err)
|
||||
}
|
||||
|
||||
update := &mysqlmodel.UserLogin{
|
||||
//OfflineTs: int(a.Ts),
|
||||
OfflineTime: a.Time,
|
||||
OutType: mysqlmodel.OutTypeGaming,
|
||||
GameID: a.GameId,
|
||||
Tel: tel,
|
||||
DeviceName: a.DeviceName,
|
||||
AppVersion: a.AppVersion,
|
||||
BuildVersion: a.BuildVersion,
|
||||
AppChannel: a.AppChannel,
|
||||
ChannelId: a.ChannelId,
|
||||
}
|
||||
|
||||
if err := db.Model(login).Select(
|
||||
"OfflineTime", "OutType", "GameID", "DeviceName", "AppVersion", "BuildVersion", "AppChannel", "Tel",
|
||||
).Updates(update).Error; err != nil {
|
||||
logger.Logger.Errorf("checkLogin %v update user_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 登录后离开
|
||||
func checkLogin(db *mymysql.Database, login *mysqlmodel.UserLogin, platform string, id int) (bool, error) {
|
||||
// 最早的一条掉线记录
|
||||
a := &mongomodel.LoginLog{}
|
||||
c, err := mymongo.GetLogCollection(platform, mongomodel.LogLogin)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.LogLogin, err)
|
||||
return false, err
|
||||
}
|
||||
d := c.FindOne(context.TODO(), bson.M{"snid": id, "logtype": mongomodel.LogTypeDrop}, options.FindOne().SetSort(bson.D{{"time", 1}}))
|
||||
err = d.Err()
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Tracef("checkLogin %v not found in log_login", id)
|
||||
return false, nil
|
||||
}
|
||||
logger.Logger.Errorf("checkLogin %v get log_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
if err := d.Decode(a); err != nil {
|
||||
logger.Logger.Errorf("checkLogin %v decode log_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// account tel
|
||||
tel, err := getAccountTel(platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Warnf("get account tel %v err: %v", id, err)
|
||||
}
|
||||
|
||||
update := &mysqlmodel.UserLogin{
|
||||
//OfflineTs: int(a.Ts),
|
||||
OfflineTime: a.Time,
|
||||
OutType: mysqlmodel.OutTypeLogin,
|
||||
Tel: tel,
|
||||
DeviceName: a.DeviceName,
|
||||
AppVersion: a.AppVersion,
|
||||
BuildVersion: a.BuildVersion,
|
||||
AppChannel: a.AppChannel,
|
||||
ChannelId: a.ChannelId,
|
||||
}
|
||||
|
||||
if err := db.Model(login).Select(
|
||||
"OfflineTime", "OutType", "DeviceName", "AppVersion", "BuildVersion", "AppChannel", "Tel",
|
||||
).Updates(update).Error; err != nil {
|
||||
logger.Logger.Errorf("checkLogin %v update user_login err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// 注册后离开
|
||||
func checkRegister(db *mymysql.Database, login *mysqlmodel.UserLogin, platform string, id int) (bool, error) {
|
||||
a := &mongomodel.Account{}
|
||||
c, err := mymongo.GetUserCollection(platform, mongomodel.UserAccount)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.UserAccount, err)
|
||||
return false, err
|
||||
}
|
||||
d := c.FindOne(context.TODO(), bson.M{"snid": id})
|
||||
err = d.Err()
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Warnf("checkRegister %v not found in user_account", id)
|
||||
return false, nil
|
||||
}
|
||||
logger.Logger.Errorf("checkRegister %v get user_account err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
if err := d.Decode(a); err != nil {
|
||||
logger.Logger.Errorf("checkRegister %v decode user_account err: %v", id, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// account tel
|
||||
tel, err := getAccountTel(platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Warnf("get account tel %v err: %v", id, err)
|
||||
}
|
||||
|
||||
login.Snid = id
|
||||
//login.OnlineTs = int(a.RegisterTs)
|
||||
login.OnlineTime = a.RegisteTime
|
||||
//login.OfflineTs = int(a.RegisterTs)
|
||||
login.OfflineTime = a.RegisteTime
|
||||
login.OutType = mysqlmodel.OutTypRegister
|
||||
login.Tel = tel
|
||||
login.DeviceName = a.DeviceName
|
||||
login.AppVersion = a.AppVersion
|
||||
login.BuildVersion = a.BuildVersion
|
||||
login.AppChannel = a.AppChannel
|
||||
login.ChannelId = a.ChannelId
|
||||
|
||||
if err := db.Create(login).Error; err != nil {
|
||||
logger.Logger.Errorf("checkRegister create err: %v", err)
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// UserLogin 玩家跳出统计
|
||||
func UserLogin(platform string, ids []*mysqlmodel.UserID) error {
|
||||
f := func(id int) error {
|
||||
// 玩家是否已经统计结束,已经是游戏结束状态
|
||||
login := &mysqlmodel.UserLogin{}
|
||||
db, err := mymysql.GetDatabase(platform)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin get db err: %v", err)
|
||||
return err
|
||||
}
|
||||
if err = db.Where("snid = ?", id).Find(login).Error; err != nil {
|
||||
logger.Logger.Errorf("UserLogin find %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
switch login.OutType {
|
||||
case mysqlmodel.OutTypeGameOver:
|
||||
return nil
|
||||
|
||||
case mysqlmodel.OutTypeGaming:
|
||||
_, err := checkGameOver(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGameOver %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
case mysqlmodel.OutTypeLogin:
|
||||
ret, err := checkGameOver(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGameOver %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
ret, err = checkGaming(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGaming %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
|
||||
case mysqlmodel.OutTypRegister:
|
||||
ret, err := checkGameOver(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGameOver %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
ret, err = checkGaming(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGaming %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
ret, err = checkLogin(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkLogin %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
ret, err := checkRegister(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkRegister %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if !ret {
|
||||
logger.Logger.Warnf("UserLogin not found user_account checkRegister %v err: %v", id, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
ret, err = checkGameOver(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGameOver %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
ret, err = checkGaming(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkGaming %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
ret, err = checkLogin(db, login, platform, id)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("UserLogin checkLogin %v err: %v", id, err)
|
||||
return err
|
||||
}
|
||||
if ret {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range ids {
|
||||
if err := f(v.Snid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package syn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
)
|
||||
|
||||
// Data 数据同步方法
|
||||
// T mongodb数据表结构
|
||||
// F mongodb中的每条数据的处理操作,自行实现
|
||||
type Data[T any] struct {
|
||||
Platform string // 平台
|
||||
MidType int // 数据类型 例如 modelmysql.MidTypeItem
|
||||
Database string // 库名称
|
||||
CollectionName string // 集合名称
|
||||
BatchSize int // 一次读取数量
|
||||
// F 自定义数据处理方法
|
||||
// data: mongodb中的一条日志
|
||||
F func(data *T, db *gorm.DB) (string, error)
|
||||
}
|
||||
|
||||
// CommonDone 数据获取方式,根据mongodb集合主键按时间顺序批量读取
|
||||
func (d *Data[T]) CommonDone() error {
|
||||
db, err := mymysql.GetDatabase(d.Platform)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: failed to get database: %v", err)
|
||||
return err
|
||||
}
|
||||
loginMID := &mysqlmodel.LogMid{Tp: d.MidType}
|
||||
var n int64
|
||||
err = db.Model(&mysqlmodel.LogMid{}).Find(loginMID).Count(&n).Error
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: failed to get log_mid: %v", err)
|
||||
return err
|
||||
}
|
||||
if n == 0 {
|
||||
if err = db.Create(loginMID).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: failed to create log_mid: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Logger.Tracef("start log_mid tp:%v _id:%v", loginMID.Tp, loginMID.MID)
|
||||
|
||||
_id, _ := primitive.ObjectIDFromHex(loginMID.MID)
|
||||
filter := bson.M{"_id": bson.M{"$gt": _id}}
|
||||
c, err := mymongo.GetCollection(d.Platform, mymongo.DatabaseType(d.Database), d.CollectionName)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s %s error %v", d.Database, d.CollectionName, err)
|
||||
return err
|
||||
}
|
||||
l, err := c.Find(context.TODO(), filter,
|
||||
options.Find().SetSort(bson.D{primitive.E{Key: "_id", Value: 1}}), options.Find().SetLimit(int64(d.BatchSize)))
|
||||
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Errorf("mongo: failed to get %v: %v", d.CollectionName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
var logs []*T
|
||||
if err = l.All(context.TODO(), &logs); err != nil {
|
||||
l.Close(context.TODO())
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Logger.Errorf("mongo: failed to get %v: %v", d.CollectionName, err)
|
||||
return err
|
||||
}
|
||||
l.Close(context.TODO())
|
||||
if len(logs) == 0 {
|
||||
logger.Logger.Infof("sync %v finished", d.CollectionName)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
for _, v := range logs {
|
||||
loginMID.MID, err = d.F(v, tx)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("Process %v error:%v", d.CollectionName, err)
|
||||
return err
|
||||
}
|
||||
if err = tx.Model(loginMID).Updates(loginMID).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: failed to update %v log_mid: %v", d.CollectionName, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
package syn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"mongo.games.com/game/statistics/constant"
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
)
|
||||
|
||||
func SyncInviteScore(platform string, batchSize int) error {
|
||||
db, err := mymysql.GetDatabase(platform)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to get database: %v", err)
|
||||
return err
|
||||
}
|
||||
inviteMID := &mysqlmodel.LogInviteScoreMid{ID: 1}
|
||||
var n int64
|
||||
err = db.Model(&mysqlmodel.LogInviteScoreMid{}).Find(inviteMID).Count(&n).Error
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to get log_invitescore_mid: %v", err)
|
||||
return err
|
||||
}
|
||||
if n == 0 {
|
||||
if err = db.Create(inviteMID).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to create log_invitescore_mid: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Logger.Tracef("start SyncInviteScore log_invitescore _id:%v", inviteMID.MID)
|
||||
|
||||
_id, _ := primitive.ObjectIDFromHex(inviteMID.MID)
|
||||
filter := bson.M{"_id": bson.M{"$gt": _id}}
|
||||
c, err := mymongo.GetLogCollection(platform, mongomodel.LogInviteScore)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.LogInviteScore, err)
|
||||
return err
|
||||
}
|
||||
l, err := c.Find(context.TODO(), filter,
|
||||
options.Find().SetSort(bson.D{primitive.E{Key: "_id", Value: 1}}), options.Find().SetLimit(int64(batchSize)))
|
||||
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Errorf("mongo: SyncInviteScore failed to get log_invitescore: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
var logs []*mongomodel.InviteScore
|
||||
if err = l.All(context.TODO(), &logs); err != nil {
|
||||
l.Close(context.TODO())
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Logger.Errorf("mongo: SyncInviteScore failed to get log_invitescore: %v", err)
|
||||
return err
|
||||
}
|
||||
l.Close(context.TODO())
|
||||
|
||||
getPSnId := func(tx *gorm.DB, snid int) (int, error) {
|
||||
if snid <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
ret := new(mysqlmodel.LogInviteUser)
|
||||
if err = tx.First(ret, "snid = ? and level = 1", snid).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to getPSnId: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return ret.Psnid, nil
|
||||
}
|
||||
|
||||
getDownSnId := func(tx *gorm.DB, snid []int) ([]int, error) {
|
||||
if len(snid) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var ret []int
|
||||
var us []*mysqlmodel.LogInviteUser
|
||||
if err = tx.Select("snid").Where("psnid IN ? AND level = 1", snid).Find(&us).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to getDownSnId: %v", err)
|
||||
return ret, err
|
||||
}
|
||||
for _, v := range us {
|
||||
ret = append(ret, v.Snid)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
bind := func(tx *gorm.DB, psnid, snid int, ts int) ([]*mysqlmodel.LogInviteUser, error) {
|
||||
var lu []*mysqlmodel.LogInviteUser
|
||||
var a1, a2, a3, a4, b1 int
|
||||
var b2, b3, b4 []int
|
||||
a4 = psnid
|
||||
a3, err = getPSnId(tx, a4)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a2, err = getPSnId(tx, a3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a1, err = getPSnId(tx, a2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b1 = snid
|
||||
b2, err = getDownSnId(tx, []int{b1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b3, err = getDownSnId(tx, b2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b4, err = getDownSnId(tx, b3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Logger.Tracef("a1:%d, a2:%d, a3:%d, a4:%d, b1:%d, b2:%v, b3:%v, b4:%v", a1, a2, a3, a4, b1, b2, b3, b4)
|
||||
if a1 > 0 {
|
||||
if b1 > 0 {
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a1,
|
||||
Snid: b1,
|
||||
Level: 4,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a1: %v %v %v", b1, 4, ts)
|
||||
}
|
||||
}
|
||||
if a2 > 0 {
|
||||
if b1 > 0 {
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a2,
|
||||
Snid: b1,
|
||||
Level: 3,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a2: %v %v %v", b1, 3, ts)
|
||||
}
|
||||
for _, v := range b2 {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a2,
|
||||
Snid: v,
|
||||
Level: 4,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a2: %v %v %v", v, 4, ts)
|
||||
}
|
||||
}
|
||||
if a3 > 0 {
|
||||
if b1 > 0 {
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a3,
|
||||
Snid: b1,
|
||||
Level: 2,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a3: %v %v %v", b1, 2, ts)
|
||||
}
|
||||
for _, v := range b2 {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a3,
|
||||
Snid: v,
|
||||
Level: 3,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a3: %v %v %v", v, 3, ts)
|
||||
}
|
||||
for _, v := range b3 {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a3,
|
||||
Snid: v,
|
||||
Level: 4,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a3: %v %v %v", v, 4, ts)
|
||||
}
|
||||
}
|
||||
if a4 > 0 {
|
||||
if b1 > 0 {
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a4,
|
||||
Snid: b1,
|
||||
Level: 1,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a4: %v %v %v", b1, 1, ts)
|
||||
}
|
||||
for _, v := range b2 {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a4,
|
||||
Snid: v,
|
||||
Level: 2,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a4: %v %v %v", v, 2, ts)
|
||||
}
|
||||
for _, v := range b3 {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a4,
|
||||
Snid: v,
|
||||
Level: 3,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a4: %v %v %v", v, 3, ts)
|
||||
}
|
||||
for _, v := range b4 {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
lu = append(lu, &mysqlmodel.LogInviteUser{
|
||||
Psnid: a4,
|
||||
Snid: v,
|
||||
Level: 4,
|
||||
Ts: ts,
|
||||
})
|
||||
logger.Logger.Tracef("a4: %v %v %v", v, 4, ts)
|
||||
}
|
||||
}
|
||||
return lu, nil
|
||||
}
|
||||
|
||||
for _, v := range logs {
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
inviteMID.MID = v.Id.Hex()
|
||||
if err = tx.Model(inviteMID).Updates(inviteMID).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to update log_invitescore_mid: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Save(&mysqlmodel.LogInviteScore{
|
||||
UpSnid: v.UpSnid,
|
||||
DownSnid: v.DownSnid,
|
||||
Level: v.Level,
|
||||
Tp: v.Tp,
|
||||
Rate: v.Rate,
|
||||
Score: v.Score,
|
||||
Money: v.Money,
|
||||
Ts: v.Ts,
|
||||
}).Error
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to insert: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if v.Tp == constant.InviteScoreTypeBind && v.Level == 0 {
|
||||
// 绑定关系
|
||||
lu, err := bind(tx, v.UpSnid, v.DownSnid, v.Ts)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to bind: %v", err)
|
||||
return err
|
||||
}
|
||||
if err = tx.CreateInBatches(lu, len(lu)).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to create log_invite_user: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncInviteScore failed to transaction: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package syn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"mongo.games.com/goserver/core/mongox"
|
||||
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
)
|
||||
|
||||
func ItemGainDone(data *Data[mongomodel.ItemLog]) error {
|
||||
data.MidType = mysqlmodel.MidTypeItem
|
||||
data.Database = string(mongox.DatabaseLog)
|
||||
data.CollectionName = mongomodel.LogItem
|
||||
data.F = func(data *mongomodel.ItemLog, db *gorm.DB) (string, error) {
|
||||
if data == nil || data.LogId.Hex() == "" {
|
||||
return "", errors.New("null")
|
||||
}
|
||||
if data.LogType != 0 || data.Id != "" {
|
||||
return data.LogId.Hex(), nil
|
||||
}
|
||||
|
||||
hourTime := time.Unix(data.CreateTs, 0).Local()
|
||||
hourTs := time.Date(hourTime.Year(), hourTime.Month(), hourTime.Day(), hourTime.Hour(), 0, 0, 0, time.Local).Unix()
|
||||
|
||||
item := &mysqlmodel.ItemGain{}
|
||||
err := db.Model(item).Where("hour = ? and item_id = ?", hourTs, data.ItemId).First(item).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return "", err
|
||||
}
|
||||
item.Hour = hourTs
|
||||
item.ItemId = data.ItemId
|
||||
item.ItemNum += data.Count
|
||||
if item.ID == 0 {
|
||||
err = db.Create(item).Error
|
||||
} else {
|
||||
err = db.Model(item).Updates(item).Error
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
itemTotal := &mysqlmodel.ItemTotalGain{}
|
||||
err = db.Model(itemTotal).Where("item_id = ?", data.ItemId).First(itemTotal).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return "", err
|
||||
}
|
||||
itemTotal.ItemId = data.ItemId
|
||||
itemTotal.ItemNum += data.Count
|
||||
if itemTotal.ID == 0 {
|
||||
err = db.Create(itemTotal).Error
|
||||
} else {
|
||||
err = db.Model(itemTotal).Updates(itemTotal).Error
|
||||
}
|
||||
return data.LogId.Hex(), err
|
||||
}
|
||||
return data.CommonDone()
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
package syn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm"
|
||||
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
)
|
||||
|
||||
/*
|
||||
登录日志同步使用了mongo的_id,从小到大每次同步n个
|
||||
*/
|
||||
|
||||
// LogLogin 同步登录日志
|
||||
func LogLogin(platform string, batchSize int) ([]*mysqlmodel.LogLogin, error) {
|
||||
db, err := mymysql.GetDatabase(platform)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to get database: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
loginMID := &mysqlmodel.LogLoginMid{ID: 1}
|
||||
var n int64
|
||||
err = db.Model(&mysqlmodel.LogLoginMid{}).Find(loginMID).Count(&n).Error
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to get log_login_mid: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if n == 0 {
|
||||
if err = db.Create(loginMID).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to create log_login_mid: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
logger.Logger.Tracef("start SyncLogLogin log_login _id:%v", loginMID.MID)
|
||||
|
||||
_id, _ := primitive.ObjectIDFromHex(loginMID.MID)
|
||||
filter := bson.M{"_id": bson.M{"$gt": _id}}
|
||||
c, err := mymongo.GetLogCollection(platform, mongomodel.LogLogin)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.LogLogin, err)
|
||||
return nil, err
|
||||
}
|
||||
l, err := c.Find(context.TODO(), filter,
|
||||
options.Find().SetSort(bson.D{primitive.E{Key: "_id", Value: 1}}), options.Find().SetLimit(int64(batchSize)))
|
||||
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Errorf("mongo: SyncLogLogin failed to get log_login: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var logs []*mongomodel.LoginLog
|
||||
if err = l.All(context.TODO(), &logs); err != nil {
|
||||
l.Close(context.TODO())
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logger.Logger.Errorf("mongo: SyncLogLogin failed to get loginlog: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
l.Close(context.TODO())
|
||||
|
||||
var ls []*mysqlmodel.LogLogin
|
||||
for _, v := range logs {
|
||||
logger.Logger.Tracef("mongo SyncLogLogin log_login: %+v", *v)
|
||||
var e *mysqlmodel.LogLogin
|
||||
switch v.LogType {
|
||||
case mongomodel.LogTypeLogin, mongomodel.LogTypeRehold:
|
||||
onlineType := mysqlmodel.LogTypeLogin
|
||||
if v.LogType == mongomodel.LogTypeRehold {
|
||||
onlineType = mysqlmodel.LogTypeRehold
|
||||
}
|
||||
|
||||
// 创建数据
|
||||
var n int64
|
||||
if err = db.Model(&mysqlmodel.LogLogin{}).Where("snid = ? AND online_type = ? AND online_time = ?",
|
||||
v.SnId, onlineType, v.Time).Count(&n).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to get log_login count: %v", err)
|
||||
return ls, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
e = &mysqlmodel.LogLogin{
|
||||
Snid: int(v.SnId),
|
||||
OnlineType: onlineType,
|
||||
//OnlineTs: int(v.Ts),
|
||||
OnlineTime: v.Time,
|
||||
OfflineType: 0,
|
||||
//OfflineTs: 0,
|
||||
OfflineTime: v.Time,
|
||||
DeviceName: v.DeviceName,
|
||||
AppVersion: v.AppVersion,
|
||||
BuildVersion: v.BuildVersion,
|
||||
AppChannel: v.AppChannel,
|
||||
ChannelId: v.ChannelId,
|
||||
}
|
||||
if err = db.Create(e).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to create log_login: %v", err)
|
||||
return ls, err
|
||||
}
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
case mongomodel.LogTypeLogout, mongomodel.LogTypeDrop:
|
||||
// 修改数据
|
||||
e = &mysqlmodel.LogLogin{}
|
||||
err = db.Model(&mysqlmodel.LogLogin{}).Where("snid = ?", v.SnId).Order("online_time DESC").First(e).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to find log_login: %v", err)
|
||||
return ls, err
|
||||
}
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Warnf("mysql: SyncLogLogin not found log_login: %v", v)
|
||||
continue
|
||||
}
|
||||
|
||||
if e.OfflineType != 0 {
|
||||
logger.Logger.Tracef("mysql: SyncLogLogin already offline: %+v", *e)
|
||||
continue
|
||||
}
|
||||
|
||||
e.OfflineType = mysqlmodel.LogTypeOffline
|
||||
//e.OfflineTs = int(v.Ts)
|
||||
e.OfflineTime = v.Time
|
||||
if err = db.Model(e).Select("OfflineType", "OfflineTime").Updates(e).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to update log_login: %v", err)
|
||||
return ls, err
|
||||
}
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
ls = append(ls, e)
|
||||
}
|
||||
}
|
||||
|
||||
if len(logs) > 0 {
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
loginMID.MID = logs[len(logs)-1].LogId.Hex()
|
||||
if err = tx.Model(loginMID).Updates(loginMID).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to update log_login_mid: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range ls {
|
||||
if err = tx.First(&mysqlmodel.UserID{}, "snid = ?", v.Snid).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to find user_id: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err = tx.Create(&mysqlmodel.UserID{Snid: v.Snid}).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to create user_id: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: SyncLogLogin failed to transaction: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ls, nil
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
游戏服mongodb同步到mysql
|
|
@ -0,0 +1,105 @@
|
|||
package syn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"gorm.io/gorm"
|
||||
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
mysqlmodel "mongo.games.com/game/statistics/modelmysql"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
)
|
||||
|
||||
/*
|
||||
注册信息同步,使用mongo的_id,从小到大每次同步n个
|
||||
*/
|
||||
|
||||
// UserAccount 同步注册表
|
||||
func UserAccount(platform string, batchSize int) ([]*mysqlmodel.UserAccount, error) {
|
||||
db, err := mymysql.GetDatabase(platform)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: UserAccount failed to get database: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
account := &mysqlmodel.UserAccount{}
|
||||
err = db.Model(&mysqlmodel.UserAccount{}).Last(account).Error
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Errorf("mysql: UserAccount failed to get account: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Logger.Tracef("start UserAccount account _id:%v", account.MID)
|
||||
|
||||
_id, _ := primitive.ObjectIDFromHex(account.MID)
|
||||
filter := bson.M{"_id": bson.M{"$gt": _id}}
|
||||
c, err := mymongo.GetUserCollection(platform, mongomodel.UserAccount)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("get collection %s error %v", mongomodel.UserAccount, err)
|
||||
return nil, err
|
||||
}
|
||||
l, err := c.Find(context.TODO(), filter,
|
||||
options.Find().SetSort(bson.D{primitive.E{Key: "_id", Value: 1}}), options.Find().SetLimit(int64(batchSize)))
|
||||
if err != nil && !errors.Is(err, mongo.ErrNoDocuments) {
|
||||
logger.Logger.Errorf("mongo: UserAccount failed to get account: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var accounts []*mongomodel.Account
|
||||
if err = l.All(context.TODO(), &accounts); err != nil {
|
||||
l.Close(context.TODO())
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logger.Logger.Errorf("mongo: UserAccount failed to get account: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
l.Close(context.TODO())
|
||||
|
||||
var as []*mysqlmodel.UserAccount
|
||||
err = db.Transaction(func(tx *gorm.DB) error {
|
||||
for _, v := range accounts {
|
||||
logger.Logger.Tracef("mongo account: %+v", *v)
|
||||
a := &mysqlmodel.UserAccount{
|
||||
MID: v.AccountId.Hex(),
|
||||
Snid: int(v.SnId),
|
||||
//RegisterTs: int(v.RegisterTs),
|
||||
RegisterTime: v.RegisteTime,
|
||||
Tel: v.Tel,
|
||||
ChannelId: v.ChannelId,
|
||||
}
|
||||
|
||||
if err = tx.Create(a).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: UserAccount failed to create account: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.First(&mysqlmodel.UserID{}, "snid = ?", v.SnId).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
logger.Logger.Errorf("mysql: UserAccount failed to find user_id: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
if err = tx.Create(&mysqlmodel.UserID{Snid: int(v.SnId)}).Error; err != nil {
|
||||
logger.Logger.Errorf("mysql: UserAccount failed to create user_id: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
as = append(as, a)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("mysql: UserAccount failed to transaction: %v", err)
|
||||
return as, err
|
||||
}
|
||||
return as, nil
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
set CGO_ENABLED=0
|
||||
set GOOS=linux
|
||||
set GOARCH=amd64
|
||||
go build -o ./task2
|
||||
pause
|
Binary file not shown.
|
@ -0,0 +1,221 @@
|
|||
StartTime: "2024-11-26T00:00:00+08:00"
|
||||
EndTime: "2024-11-27T00:00:00+08:00"
|
||||
|
||||
Switch: # 1: open, 0: close
|
||||
- 0 # 新用户游戏破产率
|
||||
- 0 # 新用户平均游戏时长
|
||||
- 0 # 新用户平均局数
|
||||
- 0 # 平均倍数
|
||||
- 1 # 活跃破产率
|
||||
- 1 # 控输赢胜率
|
||||
- 1 # 机器人胜率
|
||||
- 1 # 人均获得金币
|
||||
- 1 # 破产后离线
|
||||
- 1 # 充值玩家金币余额
|
||||
|
||||
Gamefreeids:
|
||||
- 2070001
|
||||
- 2070002
|
||||
- 2070003
|
||||
- 2080001
|
||||
- 2080002
|
||||
- 2080003
|
||||
- 2090001
|
||||
- 2090002
|
||||
- 2090003
|
||||
- 2100001
|
||||
- 2100002
|
||||
- 2100003
|
||||
- 2400001
|
||||
- 2400002
|
||||
- 2400003
|
||||
- 2400004
|
||||
- 2400005
|
||||
- 2400006
|
||||
- 2440001
|
||||
- 2440002
|
||||
- 2440003
|
||||
- 2440004
|
||||
- 2440005
|
||||
- 2440006
|
||||
- 2410001
|
||||
- 2410002
|
||||
- 2410003
|
||||
- 2410004
|
||||
- 2410005
|
||||
- 2410006
|
||||
- 2450001
|
||||
- 2450002
|
||||
- 2450003
|
||||
- 2450004
|
||||
- 2450005
|
||||
- 2450006
|
||||
- 2420001
|
||||
- 2420002
|
||||
- 2420003
|
||||
- 2420004
|
||||
- 2420005
|
||||
- 2420006
|
||||
- 2460001
|
||||
- 2460002
|
||||
- 2460003
|
||||
- 2460004
|
||||
- 2460005
|
||||
- 2460006
|
||||
- 2430001
|
||||
- 2430002
|
||||
- 2430003
|
||||
- 2430004
|
||||
- 2430005
|
||||
- 2430006
|
||||
- 2470001
|
||||
- 2470002
|
||||
- 2470003
|
||||
- 2470004
|
||||
- 2470005
|
||||
- 2470006
|
||||
- 2150001
|
||||
- 2160001
|
||||
- 2170001
|
||||
- 2180001
|
||||
- 5210001
|
||||
- 5210002
|
||||
- 5210003
|
||||
- 5210004
|
||||
- 5210005
|
||||
- 2110001
|
||||
- 2110002
|
||||
- 2110003
|
||||
- 2110004
|
||||
- 2110005
|
||||
- 2110006
|
||||
- 2120001
|
||||
- 2120002
|
||||
- 2120003
|
||||
- 2120004
|
||||
- 2120005
|
||||
- 2120006
|
||||
- 2130001
|
||||
- 2130002
|
||||
- 2130003
|
||||
- 2140001
|
||||
- 2140002
|
||||
- 2140003
|
||||
- 6070001
|
||||
- 3010001
|
||||
- 3010002
|
||||
- 3010003
|
||||
- 3010004
|
||||
- 3020001
|
||||
- 3020002
|
||||
- 3020003
|
||||
- 3020004
|
||||
- 3030001
|
||||
- 3030002
|
||||
- 3030003
|
||||
- 3030004
|
||||
- 3040001
|
||||
- 3040002
|
||||
- 3040003
|
||||
- 3050001
|
||||
- 3050002
|
||||
- 3050003
|
||||
- 3060001
|
||||
- 3060002
|
||||
- 3060003
|
||||
- 3060004
|
||||
- 3070001
|
||||
- 3070002
|
||||
- 3070003
|
||||
- 3070004
|
||||
- 3080001
|
||||
- 3090001
|
||||
- 3100001
|
||||
- 3110001
|
||||
- 3120001
|
||||
|
||||
Tienlen:
|
||||
- 2070001
|
||||
- 2070002
|
||||
- 2070003
|
||||
- 2080001
|
||||
- 2080002
|
||||
- 2080003
|
||||
- 2090001
|
||||
- 2090002
|
||||
- 2090003
|
||||
- 2100001
|
||||
- 2100002
|
||||
- 2100003
|
||||
- 2400001
|
||||
- 2400002
|
||||
- 2400003
|
||||
- 2400004
|
||||
- 2400005
|
||||
- 2400006
|
||||
- 2440001
|
||||
- 2440002
|
||||
- 2440003
|
||||
- 2440004
|
||||
- 2440005
|
||||
- 2440006
|
||||
- 2410001
|
||||
- 2410002
|
||||
- 2410003
|
||||
- 2410004
|
||||
- 2410005
|
||||
- 2410006
|
||||
- 2450001
|
||||
- 2450002
|
||||
- 2450003
|
||||
- 2450004
|
||||
- 2450005
|
||||
- 2450006
|
||||
- 2420001
|
||||
- 2420002
|
||||
- 2420003
|
||||
- 2420004
|
||||
- 2420005
|
||||
- 2420006
|
||||
- 2460001
|
||||
- 2460002
|
||||
- 2460003
|
||||
- 2460004
|
||||
- 2460005
|
||||
- 2460006
|
||||
- 2430001
|
||||
- 2430002
|
||||
- 2430003
|
||||
- 2430004
|
||||
- 2430005
|
||||
- 2430006
|
||||
- 2470001
|
||||
- 2470002
|
||||
- 2470003
|
||||
- 2470004
|
||||
- 2470005
|
||||
- 2470006
|
||||
|
||||
|
||||
Thirteen:
|
||||
- 2110001
|
||||
- 2110002
|
||||
- 2110003
|
||||
- 2110004
|
||||
- 2110005
|
||||
- 2110006
|
||||
- 2120001
|
||||
- 2120002
|
||||
- 2120003
|
||||
- 2120004
|
||||
- 2120005
|
||||
- 2120006
|
||||
- 2130001
|
||||
- 2130002
|
||||
- 2130003
|
||||
- 2140001
|
||||
- 2140002
|
||||
- 2140003
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
global:
|
||||
user:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_global
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
log:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_log
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
monitor:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_monitor
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
platforms:
|
||||
0:
|
||||
user:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_user_plt_000
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
log:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_log_plt_000
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
1:
|
||||
user:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_user_plt_001
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
||||
log:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 27017
|
||||
Database: win88_log_plt_001
|
||||
Username:
|
||||
Password:
|
||||
Options:
|
|
@ -0,0 +1,38 @@
|
|||
platforms:
|
||||
global:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: win88_user
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
0:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: win88_plt_000
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
1:
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: win88_plt_001
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
count: # 破产日志库
|
||||
HostName: 127.0.0.1
|
||||
HostPort: 3306
|
||||
Database: dbmis_count
|
||||
Username: root
|
||||
Password: 123456
|
||||
Options: charset=utf8mb4&parseTime=True&loc=Local
|
||||
|
||||
# 最大空闲连接数
|
||||
MaxIdleConns: 10
|
||||
# 最大连接数
|
||||
MaxOpenConns: 100
|
||||
# 连接可复用的最大时间
|
||||
ConnMaxLifetime: 3600
|
||||
# 连接最大空闲时间
|
||||
ConnMaxIdletime: 0
|
|
@ -0,0 +1,88 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
type ExcelData struct {
|
||||
*excelize.File
|
||||
Head []string
|
||||
Index int
|
||||
IndexCell int
|
||||
}
|
||||
|
||||
func (e *ExcelData) SetHead(head []string) *ExcelData {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
e.Index = 1
|
||||
e.SetSheetRow("Sheet1", "A1", &head)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *ExcelData) SetRow(row []string) *ExcelData {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
e.Index++
|
||||
e.SetSheetRow("Sheet1", "A"+fmt.Sprintf("%d", e.Index), &row)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *ExcelData) SetCell(val interface{}) *ExcelData {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
e.IndexCell++
|
||||
cell := fmt.Sprintf("%c%d", 'A'+e.IndexCell-1, e.Index)
|
||||
e.SetCellValue("Sheet1", cell, val)
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *ExcelData) NewLine() *ExcelData {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
e.Index++
|
||||
e.IndexCell = 0
|
||||
return e
|
||||
}
|
||||
|
||||
type ExcelMgr struct {
|
||||
List map[int]*ExcelData
|
||||
}
|
||||
|
||||
func NewExcelMgr() *ExcelMgr {
|
||||
return &ExcelMgr{
|
||||
List: make(map[int]*ExcelData),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) Register(id int, head []string) *ExcelData {
|
||||
e.List[id] = &ExcelData{
|
||||
File: excelize.NewFile(),
|
||||
Head: head,
|
||||
Index: 0,
|
||||
}
|
||||
|
||||
e.List[id].NewSheet("Sheet1")
|
||||
|
||||
if len(head) > 0 {
|
||||
e.List[id].SetHead(head)
|
||||
}
|
||||
|
||||
return e.List[id]
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) Get(id int) *ExcelData {
|
||||
return e.List[id]
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) Save(id int, fileName string) error {
|
||||
d := e.List[id]
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
return d.SaveAs(fileName)
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package gamefree
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
"mongo.games.com/game/protocol/server"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
buf, err := os.ReadFile("./etc/DB_GameFree.dat")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = PBDB_GameFreeMgr.unmarshal(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var PBDB_GameFreeMgr = &DB_GameFreeMgr{
|
||||
Datas: &server.DB_GameFreeArray{},
|
||||
pool: make(map[int32]*server.DB_GameFree),
|
||||
}
|
||||
|
||||
type DB_GameFreeMgr struct {
|
||||
Datas *server.DB_GameFreeArray
|
||||
pool map[int32]*server.DB_GameFree
|
||||
}
|
||||
|
||||
func (this *DB_GameFreeMgr) unmarshal(data []byte) error {
|
||||
err := proto.Unmarshal(data, this.Datas)
|
||||
if err == nil {
|
||||
this.arrangeData()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *DB_GameFreeMgr) reunmarshal(data []byte) error {
|
||||
newDatas := &server.DB_GameFreeArray{}
|
||||
err := proto.Unmarshal(data, newDatas)
|
||||
if err == nil {
|
||||
for _, item := range newDatas.Arr {
|
||||
existItem := this.GetData(item.GetId())
|
||||
if existItem == nil {
|
||||
this.pool[item.GetId()] = item
|
||||
this.Datas.Arr = append(this.Datas.Arr, item)
|
||||
|
||||
} else {
|
||||
*existItem = *item
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *DB_GameFreeMgr) arrangeData() {
|
||||
if this.Datas == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dataArr := this.Datas.GetArr()
|
||||
if dataArr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, data := range dataArr {
|
||||
this.pool[data.GetId()] = data
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DB_GameFreeMgr) GetData(id int32) *server.DB_GameFree {
|
||||
if data, ok := this.pool[id]; ok {
|
||||
return data
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<seelog type="adaptive" mininterval="2000000" maxinterval="100000000" critmsgcount="500" minlevel="trace">
|
||||
<exceptions>
|
||||
<exception filepattern="test*" minlevel="error"/>
|
||||
</exceptions>
|
||||
<outputs formatid="all">
|
||||
<rollingfile formatid="all" type="size" filename="./all.log" maxsize="50000000" maxrolls="5" />
|
||||
<filter levels="info,trace,warn">
|
||||
<console formatid="fmtinfo"/>
|
||||
</filter>
|
||||
<filter levels="error,critical" formatid="fmterror">
|
||||
<console/>
|
||||
<file path="errors.log"/>
|
||||
</filter>
|
||||
</outputs>
|
||||
<formats>
|
||||
<format id="fmtinfo" format="[%Date][%Time] [%Level] %Msg%n"/>
|
||||
<format id="fmterror" format="[%Date][%Time] [%LEVEL] [%FuncShort @ %File.%Line] %Msg%n"/>
|
||||
<format id="all" format="[%Date][%Time] [%Level] [@ %File.%Line] %Msg%n"/>
|
||||
<format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/>
|
||||
</formats>
|
||||
</seelog>
|
|
@ -0,0 +1,427 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
"mongo.games.com/goserver/core/mongox"
|
||||
"mongo.games.com/goserver/core/mysqlx"
|
||||
"mongo.games.com/goserver/core/viperx"
|
||||
|
||||
"mongo.games.com/game/statistics/task/gamefree"
|
||||
"mongo.games.com/game/statistics/task/task"
|
||||
)
|
||||
|
||||
const (
|
||||
ExcelTypeNewPlayerBankrupt = iota // 新用户游戏破产率
|
||||
ExcelTypeGameTimeAvg // 新用户平均游戏时长
|
||||
ExcelTypeGameCountAvg // 新用户平均局数
|
||||
ExcelTypeGameRate // 平均倍数
|
||||
ExcelTypeActiveRate // 活跃破产率
|
||||
ExcelTypeCtrlWinRate // 控输赢胜率
|
||||
ExcelTypeRobotWinRate // 机器人胜率
|
||||
ExcelTypeCoinAvg // 人均获得金币
|
||||
ExcelTypeBankruptOffline // 破产后离线
|
||||
ExcelTypeOfflineCoin // 充值当天最后金币
|
||||
)
|
||||
|
||||
var VP *viper.Viper
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
logger.Logger.Flush()
|
||||
logger.Logger.Close()
|
||||
}()
|
||||
VP = viperx.GetViper("config", "yaml")
|
||||
// mongo
|
||||
vp := viperx.GetViper("mongo", "yaml")
|
||||
// mongo初始化
|
||||
conf := &mongox.Config{}
|
||||
err := vp.Unmarshal(conf)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("mongo config error: %v", err))
|
||||
}
|
||||
mongox.Init(conf)
|
||||
defer mongox.Close()
|
||||
|
||||
// mysql
|
||||
vp = viperx.GetViper("mysql", "yaml")
|
||||
myConf := &mysqlx.Config{}
|
||||
err = vp.Unmarshal(myConf)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("mysql config error: %v", err))
|
||||
}
|
||||
mysqlx.Init(myConf)
|
||||
defer mysqlx.Close()
|
||||
|
||||
startTime, err := time.Parse(time.RFC3339, VP.GetString("StartTime"))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("time.Parse err: %v", err))
|
||||
return
|
||||
}
|
||||
endTime, err := time.Parse(time.RFC3339, VP.GetString("EndTime"))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("time.Parse err: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
mgr := NewExcelMgr()
|
||||
mgr.Register(ExcelTypeNewPlayerBankrupt, []string{"日期", "场次id", "破产人数", "参与人数", "破产率"})
|
||||
mgr.Register(ExcelTypeGameTimeAvg, []string{"日期", "场次id", "参与人数", "平均游戏时长"})
|
||||
mgr.Register(ExcelTypeGameCountAvg, []string{"日期", "场次id", "参与人数", "平均局数"})
|
||||
mgr.Register(ExcelTypeGameRate, []string{"日期", "场次id", "总局数", "平均倍数", "有炸弹分局数", "炸弹分平均倍数", "2留在手里的局数"})
|
||||
mgr.Register(ExcelTypeActiveRate, []string{"日期", "场次id", "破产人数", "参与人数", "破产率"})
|
||||
mgr.Register(ExcelTypeCtrlWinRate, []string{"日期", "场次id", "总局数", "平均倍数", "有炸弹分局数", "炸弹分平均倍数", "2留在手里的局数"})
|
||||
mgr.Register(ExcelTypeCtrlWinRate*10, []string{"日期", "场次id", "总局数", "平均倍数", "有炸弹分局数", "炸弹分平均倍数", "2留在手里的局数"})
|
||||
mgr.Register(ExcelTypeRobotWinRate, []string{"日期", "场次id", "总局数", "平均倍数", "有炸弹分局数", "炸弹分平均倍数", "2留在手里的局数"})
|
||||
mgr.Register(ExcelTypeCoinAvg, []string{"日期", "场次id", "参与人数", "人均获得金币"})
|
||||
mgr.Register(ExcelTypeBankruptOffline, []string{"日期", "场次id", "破产人数", "参与人数", "破产率"})
|
||||
mgr.Register(ExcelTypeOfflineCoin, []string{"日期", "场次id", "破产人数", "参与人数", "破产率"})
|
||||
switchArr := VP.GetIntSlice("Switch")
|
||||
|
||||
for {
|
||||
if startTime.Unix() >= endTime.Unix() {
|
||||
break
|
||||
}
|
||||
startTimeStr := startTime.Format(time.RFC3339)
|
||||
et := startTime.AddDate(0, 0, 1)
|
||||
endTimeStr := et.Format(time.RFC3339)
|
||||
logger.Logger.Infof("startTime: %v endTime: %v", startTimeStr, endTimeStr)
|
||||
|
||||
if switchArr[ExcelTypeNewPlayerBankrupt] == 1 {
|
||||
// 新用户游戏破产率
|
||||
mgr.GenNewPlayerBankruptRateExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeGameTimeAvg] == 1 {
|
||||
// 新用户平均游戏时长
|
||||
mgr.GenNewPlayerGameTimeAvgExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeGameCountAvg] == 1 {
|
||||
// 新用户平均局数
|
||||
mgr.GenGameCountExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeGameRate] == 1 {
|
||||
// 平均倍数
|
||||
mgr.GenGameRateExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeActiveRate] == 1 {
|
||||
// 活跃破产率
|
||||
mgr.GenActiveBankruptRateExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeCtrlWinRate] == 1 {
|
||||
// 控赢胜率
|
||||
mgr.GenCtrlWinRateExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeRobotWinRate] == 1 {
|
||||
// 机器人胜率
|
||||
mgr.GenRobotWinRateExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeCoinAvg] == 1 {
|
||||
// 人均获得金币
|
||||
mgr.GenCoinAvgExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeBankruptOffline] == 1 {
|
||||
// 破产后离线
|
||||
mgr.GenBankruptOfflineExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
if switchArr[ExcelTypeOfflineCoin] == 1 {
|
||||
// 离线金币
|
||||
mgr.GenOfflineCoinExcel("1", startTimeStr, endTimeStr)
|
||||
}
|
||||
|
||||
startTime = et
|
||||
}
|
||||
|
||||
mgr.SaveAll(VP.GetString("StartTime")[:10], endTime.AddDate(0, 0, -1).Format(time.DateOnly))
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) SaveAll(startTime, endTime string) {
|
||||
switchArr := VP.GetIntSlice("Switch")
|
||||
if switchArr[ExcelTypeNewPlayerBankrupt] == 1 {
|
||||
e.Save(ExcelTypeNewPlayerBankrupt, fmt.Sprintf("新用户破产率_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeGameTimeAvg] == 1 {
|
||||
e.Save(ExcelTypeGameTimeAvg, fmt.Sprintf("新用户平局游戏时长_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeGameCountAvg] == 1 {
|
||||
e.Save(ExcelTypeGameCountAvg, fmt.Sprintf("新用户平均局数_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeGameRate] == 1 {
|
||||
e.Save(ExcelTypeGameRate, fmt.Sprintf("平均倍数_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeActiveRate] == 1 {
|
||||
e.Save(ExcelTypeActiveRate, fmt.Sprintf("活跃破产率_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeCtrlWinRate] == 1 {
|
||||
e.Save(ExcelTypeCtrlWinRate, fmt.Sprintf("控赢胜率_%s_%s.xlsx", startTime, endTime))
|
||||
e.Save(ExcelTypeCtrlWinRate*10, fmt.Sprintf("控输胜率_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeRobotWinRate] == 1 {
|
||||
e.Save(ExcelTypeRobotWinRate, fmt.Sprintf("机器人输率_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeCoinAvg] == 1 {
|
||||
e.Save(ExcelTypeCoinAvg, fmt.Sprintf("人均获得金币_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeBankruptOffline] == 1 {
|
||||
e.Save(ExcelTypeBankruptOffline, fmt.Sprintf("破产后离线_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
if switchArr[ExcelTypeOfflineCoin] == 1 {
|
||||
e.Save(ExcelTypeOfflineCoin, fmt.Sprintf("离线金币_%s_%s.xlsx", startTime, endTime))
|
||||
}
|
||||
}
|
||||
|
||||
func GetGameFreeName(id int) string {
|
||||
d := gamefree.PBDB_GameFreeMgr.GetData(int32(id))
|
||||
return fmt.Sprintf("%s_%s", d.Name, d.Title)
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenNewPlayerBankruptRateExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range VP.GetIntSlice("Gamefreeids") {
|
||||
_, a, b, err := task.NewPlayerBankruptRate(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("NewPlayerBankruptRate get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
ex := e.Get(ExcelTypeNewPlayerBankrupt)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(a)
|
||||
ex.SetCell(b)
|
||||
if b > 0 {
|
||||
ex.SetCell(float64(a) / float64(b))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("NewPlayerBankruptRate GameFreeId: %v rate: %v", v, float64(a)/float64(b))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenNewPlayerGameTimeAvgExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range VP.GetIntSlice("Gamefreeids") {
|
||||
a, b, err := task.NewPlayerGameTimeAvg(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("NewPlayerGameTimeAvg get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
ex := e.Get(ExcelTypeGameTimeAvg)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(a)
|
||||
if a > 0 {
|
||||
avg := float64(b) / float64(a)
|
||||
show := fmt.Sprintf("%v", time.Second*time.Duration(avg))
|
||||
ex.SetCell(show)
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("NewPlayerGameTimeAvg GameFreeId: %v avg: %v", v, float64(b)/float64(a))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenGameCountExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range VP.GetIntSlice("Gamefreeids") {
|
||||
a, b, err := task.NewPlayerGameCountAvg(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("NewPlayerGameCountAvg get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
ex := e.Get(ExcelTypeGameCountAvg)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(a)
|
||||
if a > 0 {
|
||||
ex.SetCell(float64(b) / float64(a))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("NewPlayerGameCountAvg GameFreeId: %v avg: %v", v, float64(b)/float64(a))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenGameRateExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range VP.GetIntSlice("Gamefreeids") {
|
||||
total, bombTotal, remain2Total, rateAvg, bombRateAvg, err := task.PlayerGameRate(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("PlayerGameRate get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
ex := e.Get(ExcelTypeGameRate)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(total)
|
||||
ex.SetCell(rateAvg)
|
||||
ex.SetCell(bombTotal)
|
||||
ex.SetCell(bombRateAvg)
|
||||
ex.SetCell(remain2Total)
|
||||
logger.Logger.Tracef("PlayerGameRate GameFreeId: %v total: %v rateAvg: %v bombTotal: %v bombRateAvg: %v remain2Total: %v",
|
||||
v, total, rateAvg, bombTotal, bombRateAvg, remain2Total)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenActiveBankruptRateExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range VP.GetIntSlice("Gamefreeids") {
|
||||
b, t, err := task.ActivePlayerBankruptRate(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("ActivePlayerBankruptRate get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
|
||||
ex := e.Get(ExcelTypeActiveRate)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(b)
|
||||
ex.SetCell(t)
|
||||
if t > 0 {
|
||||
if b > t {
|
||||
b = t
|
||||
}
|
||||
ex.SetCell(float64(b) / float64(t))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("ActivePlayerBankruptRate GameFreeId: %v rate: %v", v, float64(b)/float64(t))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenCtrlWinRateExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range append(VP.GetIntSlice("Tienlen")) {
|
||||
ret, err := task.GameDetailWinRate(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailWinRate get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
if len(ret) > 0 {
|
||||
ex := e.Get(ExcelTypeCtrlWinRate)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(ret[0].First)
|
||||
ex.SetCell(ret[0].Second)
|
||||
ex.SetCell(ret[0].Third)
|
||||
ex.SetCell(ret[0].Total)
|
||||
if ret[0].Total > 0 {
|
||||
ex.SetCell(float64(ret[0].First+ret[0].Second) / float64(ret[0].Total))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("GameDetailWinRate GameFreeId:%v %+v", v, ret[0])
|
||||
}
|
||||
if len(ret) > 1 {
|
||||
ex := e.Get(ExcelTypeCtrlWinRate * 10)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(ret[1].First)
|
||||
ex.SetCell(ret[1].Second)
|
||||
ex.SetCell(ret[1].Third)
|
||||
ex.SetCell(ret[1].Total)
|
||||
if ret[1].Total > 0 {
|
||||
ex.SetCell(float64(ret[1].First+ret[1].Second) / float64(ret[1].Total))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("GameDetailWinRate GameFreeId:%v %+v", v, ret[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenRobotWinRateExcel(plt string, startTime, endTime string) {
|
||||
for _, v := range append(VP.GetIntSlice("Tienlen"), VP.GetIntSlice("Thirteen")...) {
|
||||
a, b, err := task.RobotWinRate(plt, startTime, endTime, v)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("RobotWinRate get StartTime:%v EndTime:%v GameFreeId:%v err: %v", startTime, endTime, v, err)
|
||||
continue
|
||||
}
|
||||
|
||||
ex := e.Get(ExcelTypeRobotWinRate)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(GetGameFreeName(v))
|
||||
ex.SetCell(a)
|
||||
ex.SetCell(b)
|
||||
if b > 0 {
|
||||
ex.SetCell(float64(b-a) / float64(b))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("RobotWinRate GameFreeId: %v rate: %v", v, float64(a)/float64(b))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenCoinAvgExcel(plt string, startTime, endTime string) {
|
||||
type KV struct {
|
||||
K int
|
||||
V string
|
||||
}
|
||||
|
||||
var list []KV
|
||||
for k, v := range task.CoinName {
|
||||
list = append(list, KV{K: k, V: v})
|
||||
}
|
||||
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].K < list[j].K
|
||||
})
|
||||
|
||||
for _, item := range list {
|
||||
k, v := item.K, item.V
|
||||
a, b, err := task.CoinAvg(plt, startTime, endTime, k)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("CoinAvg get StartTime:%v EndTime:%v tp:%v err: %v", startTime, endTime, k, err)
|
||||
continue
|
||||
}
|
||||
ex := e.Get(ExcelTypeCoinAvg)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(v)
|
||||
ex.SetCell(a)
|
||||
ex.SetCell(b)
|
||||
if a > 0 {
|
||||
ex.SetCell(float64(b) / float64(a))
|
||||
} else {
|
||||
ex.SetCell(0)
|
||||
}
|
||||
logger.Logger.Tracef("CoinAvg tp: %v rate: %v", k, float64(b)/float64(a))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenBankruptOfflineExcel(plt string, startTime, endTime string) {
|
||||
res, err := task.BankruptOffline(plt, startTime, endTime)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("BankruptOffline get StartTime:%v EndTime:%v err: %v", startTime, endTime, err)
|
||||
return
|
||||
}
|
||||
ex := e.Get(ExcelTypeBankruptOffline)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(res.One)
|
||||
ex.SetCell(res.Two)
|
||||
ex.SetCell(res.Three)
|
||||
ex.SetCell(res.Recharge)
|
||||
ex.SetCell(fmt.Sprintf("%v", res.D))
|
||||
logger.Logger.Tracef("BankruptOffline %+v", res)
|
||||
}
|
||||
|
||||
func (e *ExcelMgr) GenOfflineCoinExcel(plt string, startTime, endTime string) {
|
||||
res, err := task.OfflineCoin(plt, startTime, endTime)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("OfflineCoin get StartTime:%v EndTime:%v err: %v", startTime, endTime, err)
|
||||
return
|
||||
}
|
||||
for _, v := range res {
|
||||
ex := e.Get(ExcelTypeOfflineCoin)
|
||||
ex.NewLine()
|
||||
ex.SetCell(startTime[:10])
|
||||
ex.SetCell(v.Snid)
|
||||
ex.SetCell(v.Coin)
|
||||
}
|
||||
logger.Logger.Tracef("OfflineCoin %+v", res)
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
查询历史数据
|
|
@ -0,0 +1,189 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
|
||||
"mongo.games.com/game/common"
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
)
|
||||
|
||||
// 新用户id
|
||||
func GetNewPayerIds(plt string, startTime, endTime string) ([]int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
c, err := mymongo.GetUserCollection(plt, mongomodel.UserAccount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res []struct{ Snid int }
|
||||
dd, err := c.Find(context.TODO(), bson.M{"registetime": bson.M{"$gte": s, "$lt": e}}, options.Find().SetProjection(bson.M{"snid": 1}))
|
||||
if err != nil {
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
return nil, nil
|
||||
}
|
||||
logger.Logger.Errorf("find new player snid get err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if err := dd.All(context.TODO(), &res); err != nil {
|
||||
logger.Logger.Errorf("find new player snid decode err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
var ret []int
|
||||
for _, v := range res {
|
||||
ret = append(ret, v.Snid)
|
||||
}
|
||||
logger.Logger.Tracef("find new player snid: %v", ret)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 场次破产总人数
|
||||
func GameFreeIdBankruptPlayerCount(plt string, ids []int, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
db, err := mymysql.GetDatabase("count")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var ret int
|
||||
for _, v := range ids {
|
||||
var n int64
|
||||
if err = db.Table("bankrupt_log").Where("snid = ? AND bankrupt_time >= ? AND bankrupt_time < ? AND game_free_id = ?",
|
||||
v, s.Unix(), e.Unix(), gamefreeid).Count(&n).Error; err != nil {
|
||||
logger.Logger.Errorf("find bankrupt player count get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
if n > 0 {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 场次参与总人数
|
||||
func PlayingGameCount(plt string, ids []int, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
c, err := mymongo.GetLogCollection(plt, mongomodel.LogGamePlayerListLog)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var ret int
|
||||
for _, v := range ids {
|
||||
// 参与过游戏
|
||||
n, err := c.CountDocuments(context.TODO(), bson.M{"snid": v, "time": bson.M{"$gte": s, "$lt": e}, "gamefreeid": gamefreeid})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("find playing game count get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
if n > 0 {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// NewPlayerBankruptRate 新用户破产率
|
||||
// 新用户游戏破产率 = 新用户游戏破产玩家数量/新用户游戏参与总人数;破产数量,每个玩家每个游戏破产只统计一次;参与人数,每个玩家每个游戏只统计一次;
|
||||
// 返回 新用户数量,破产人数,参与人数
|
||||
func NewPlayerBankruptRate(plt string, startTime, endTime string, gamefreeid int) (n, a, b int, err error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return 0, 0, 0, fmt.Errorf("time format error")
|
||||
}
|
||||
ids, err := GetNewPayerIds(plt, startTime, endTime)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
a, err = GameFreeIdBankruptPlayerCount(plt, ids, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
b, err = PlayingGameCount(plt, ids, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
if b == 0 {
|
||||
return 0, 0, 0, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ActivePlayerCount 活跃玩家游戏总人数
|
||||
func ActivePlayerCount(plt string, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
c, err := mymongo.GetLogCollection(plt, mongomodel.LogGamePlayerListLog)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var count []struct {
|
||||
Count int
|
||||
}
|
||||
cur, err := c.Aggregate(context.TODO(), bson.A{
|
||||
bson.M{"$match": bson.M{"time": bson.M{"$gte": s, "$lt": e}, "gamefreeid": gamefreeid}},
|
||||
bson.M{"$group": bson.M{"_id": "$snid"}},
|
||||
bson.M{"$count": "count"},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("find active player count get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
if err := cur.All(context.TODO(), &count); err != nil {
|
||||
logger.Logger.Errorf("find active player count decode err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(count) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return count[0].Count, nil
|
||||
}
|
||||
|
||||
// ActivePlayerBankruptCount 活跃玩家破产总人数
|
||||
func ActivePlayerBankruptCount(plt string, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
db, err := mymysql.GetDatabase("count")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var n int64
|
||||
if err = db.Table("bankrupt_log").Where("bankrupt_time >= ? AND bankrupt_time < ? AND game_free_id = ?",
|
||||
s.Unix(), e.Unix(), gamefreeid).Group("snid").Count(&n).Error; err != nil {
|
||||
logger.Logger.Errorf("find bankrupt count get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// ActivePlayerBankruptRate 活跃玩家破产率
|
||||
// 活跃玩家游戏破产率 = 活跃玩家游戏破产玩家数量/活跃玩家游戏总人数;破产数量,每个玩家每个游戏破产只统计一次;参与人数,每个玩家每个游戏只统计一次;
|
||||
// 返回 破产人数,参与人数
|
||||
func ActivePlayerBankruptRate(plt string, startTime, endTime string, gamefreeid int) (a, b int, err error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return 0, 0, fmt.Errorf("time format error")
|
||||
}
|
||||
|
||||
a, err = ActivePlayerBankruptCount(plt, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
b, err = ActivePlayerCount(plt, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if b == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/game/common"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
)
|
||||
|
||||
// 破产后离线;破产后5分钟内有离线行为
|
||||
|
||||
type BankruptOfflineData struct {
|
||||
One int
|
||||
Two int
|
||||
Three int
|
||||
Recharge int
|
||||
D time.Duration
|
||||
}
|
||||
|
||||
func BankruptOffline(plt string, startTime, endTime string) (ret *BankruptOfflineData, err error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return nil, fmt.Errorf("time format error")
|
||||
}
|
||||
db, err := mymysql.GetDatabase("count")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/*
|
||||
SELECT
|
||||
snid,
|
||||
GROUP_CONCAT(bankrupt_time ORDER BY bankrupt_time ASC LIMIT 3) AS top_3_bankrupt_times,
|
||||
COUNT(*) AS record_count
|
||||
FROM
|
||||
bankrupt_log
|
||||
GROUP BY
|
||||
snid;
|
||||
*/
|
||||
|
||||
type BankruptLog struct {
|
||||
Snid int `gorm:"column:snid"`
|
||||
MaxBankruptTime string `gorm:"column:top_3_bankrupt_times"`
|
||||
Times []int64 `gorm:"-"`
|
||||
}
|
||||
|
||||
var logs []*BankruptLog
|
||||
tx := db.Raw(`
|
||||
WITH RankedData AS (
|
||||
SELECT
|
||||
snid,
|
||||
bankrupt_time,
|
||||
ROW_NUMBER() OVER (PARTITION BY snid ORDER BY bankrupt_time ASC) AS ranks
|
||||
FROM bankrupt_log
|
||||
WHERE bankrupt_time >= ? AND bankrupt_time < ?
|
||||
)
|
||||
SELECT
|
||||
snid,
|
||||
GROUP_CONCAT(bankrupt_time ORDER BY bankrupt_time ASC) AS top_3_bankrupt_times
|
||||
FROM RankedData
|
||||
WHERE ranks <= 3
|
||||
GROUP BY snid
|
||||
`, s.Unix(), e.Unix()).Scan(&logs)
|
||||
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
var timeSecond int
|
||||
var total int
|
||||
|
||||
ret = &BankruptOfflineData{}
|
||||
for _, v := range logs {
|
||||
if v == nil || len(v.MaxBankruptTime) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, vv := range strings.Split(v.MaxBankruptTime, ",") {
|
||||
n, err := strconv.Atoi(vv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.Times = append(v.Times, int64(n))
|
||||
}
|
||||
|
||||
// 破产后5分钟内有离线行为
|
||||
db, err = mymysql.GetDatabase(plt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var isOffline bool
|
||||
for k, vv := range v.Times {
|
||||
var n int64
|
||||
stime, etime := time.Unix(vv, 0), time.Unix(vv, 0).Add(5*time.Minute)
|
||||
if err = db.Table("user_logins").Where("snid = ? AND offline_time >= ? AND offline_time < ?",
|
||||
v.Snid, stime, etime).Count(&n).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch k {
|
||||
case 0:
|
||||
if n > 0 {
|
||||
ret.One++
|
||||
isOffline = true
|
||||
}
|
||||
case 1:
|
||||
if n > 0 {
|
||||
ret.Two++
|
||||
isOffline = true
|
||||
}
|
||||
case 2:
|
||||
if n > 0 {
|
||||
ret.Three++
|
||||
isOffline = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isOffline {
|
||||
// 充值
|
||||
c, err := mymongo.GetLogCollection(plt, "log_dbshop")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
count, err := c.CountDocuments(context.TODO(), bson.M{"snid": v.Snid, "ts": bson.M{"$gte": s.Unix(), "$lt": e.Unix()}, "consume": 3, "state": 1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
ret.Recharge++
|
||||
}
|
||||
|
||||
// 平均对局时长
|
||||
times, to, err := NewPlayerGameTime(plt, []int{v.Snid}, startTime, endTime, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
timeSecond += times
|
||||
total += to
|
||||
}
|
||||
}
|
||||
|
||||
if total > 0 {
|
||||
ret.D = time.Second * time.Duration(timeSecond/total)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/game/common"
|
||||
"mongo.games.com/goserver/core/mongox"
|
||||
)
|
||||
|
||||
var CoinName = map[int]string{
|
||||
44: "转盘",
|
||||
57: "轮盘视频",
|
||||
43: "签到",
|
||||
58: "签到视频",
|
||||
83: "累计签到",
|
||||
91: "累计签到进阶奖励",
|
||||
47: "vip每日礼包",
|
||||
105: "vip等级礼包",
|
||||
74: "集卡活动",
|
||||
67: "金币存钱罐",
|
||||
94: "赛季通行证等级奖励",
|
||||
95: "赛季通行证排行奖励",
|
||||
52: "排位赛段位奖励",
|
||||
65: "周卡奖励",
|
||||
5: "兑换",
|
||||
49: "礼包码兑换",
|
||||
38: "救济金",
|
||||
56: "救济金视频",
|
||||
}
|
||||
|
||||
func CoinHistory(plt string, startTime, endTime string, tp int, f func(data bson.M) error) error {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return fmt.Errorf("time format error")
|
||||
}
|
||||
c, err := mongox.GetLogCollection(plt, "log_coinex")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cur, err := c.Find(context.TODO(), bson.M{"time": bson.M{"$gte": s, "$lt": e}, "logtype": tp, "cointype": 0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cur.Close(context.Background())
|
||||
|
||||
for cur.TryNext(context.Background()) {
|
||||
data := bson.M{}
|
||||
if err := cur.Decode(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f(data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CoinAvg(plt string, startTime, endTime string, tp int) (playerNumber int, total int, err error) {
|
||||
player := make(map[int32]struct{})
|
||||
err = CoinHistory(plt, startTime, endTime, tp, func(data bson.M) error {
|
||||
snid := data["snid"].(int32)
|
||||
count := int(data["count"].(int64))
|
||||
player[snid] = struct{}{}
|
||||
if count > 0 {
|
||||
total += count
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
playerNumber = len(player)
|
||||
return
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
"mongo.games.com/goserver/core/mongox"
|
||||
|
||||
"mongo.games.com/game/common"
|
||||
)
|
||||
|
||||
func GameDetailFunc(plt string, startTime, endTime string, gamefreeid int, f func(data bson.M) error) error {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return fmt.Errorf("time format error")
|
||||
}
|
||||
|
||||
c, err := mongox.GetLogCollection(plt, "log_gamedetailed")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cur, err := c.Find(context.TODO(), bson.M{"time": bson.M{"$gte": s, "$lt": e}, "gamefreeid": gamefreeid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cur.Close(context.Background())
|
||||
|
||||
for cur.TryNext(context.Background()) {
|
||||
data := bson.M{}
|
||||
if err := cur.Decode(data); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f(data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type GameDetailWinRateRet struct {
|
||||
First int
|
||||
Second int
|
||||
Third int
|
||||
Total int
|
||||
IsWin bool
|
||||
}
|
||||
|
||||
func GameDetailWinRate(plt string, startTime, endTime string, gamefreeid int) (ret []GameDetailWinRateRet, err error) {
|
||||
ret = make([]GameDetailWinRateRet, 2)
|
||||
err = GameDetailFunc(plt, startTime, endTime, gamefreeid, func(data bson.M) error {
|
||||
ff, ss, tt, to, ct := GameDetailCount(data)
|
||||
if ct == 1 && to > 0 {
|
||||
ret[0].First += ff
|
||||
ret[0].Second += ss
|
||||
ret[0].Third += tt
|
||||
ret[0].Total += to
|
||||
}
|
||||
if ct == 2 && to > 0 {
|
||||
ret[1].First += ff
|
||||
ret[1].Second += ss
|
||||
ret[1].Third += tt
|
||||
ret[1].Total += to
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
type RabbitMQDataRaw struct {
|
||||
Source int32
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func GameDetailCount(data bson.M) (first, second, third, to int, ctrlType int) {
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
gameid := data["gameid"].(int32)
|
||||
gamefreeid := data["gamefreeid"].(int32)
|
||||
ctrlType = int(data["ctrltype"].(int32))
|
||||
logger.Logger.Tracef("GameDetail gameid:%d, gamefreeid:%d", gameid, gamefreeid)
|
||||
|
||||
if ctrlType != 1 && ctrlType != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
detail := data["gamedetailednote"]
|
||||
if detail == nil {
|
||||
return
|
||||
}
|
||||
|
||||
raw := new(RabbitMQDataRaw)
|
||||
if err := json.Unmarshal([]byte(detail.(string)), raw); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 1 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
switch gameid {
|
||||
case 207, 208, 209, 210, 240, 241, 242, 243, 244, 245, 246, 247: // tienlen
|
||||
data := new(TienLenType)
|
||||
b, err := json.Marshal(raw.Data)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Marshal error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 2 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
var has, hasPlayer bool
|
||||
for _, v := range data.PlayerData {
|
||||
if v.IsRob {
|
||||
has = true
|
||||
}
|
||||
if !v.IsRob {
|
||||
hasPlayer = true
|
||||
}
|
||||
}
|
||||
if !has || !hasPlayer {
|
||||
// 没有机器人
|
||||
return 0, 0, 0, 0, ctrlType
|
||||
}
|
||||
|
||||
sort.Slice(data.PlayerData, func(i, j int) bool {
|
||||
a, b := data.PlayerData[i].BillCoin+data.PlayerData[i].BillTaxCoin, data.PlayerData[j].BillCoin+data.PlayerData[j].BillTaxCoin
|
||||
if a != b {
|
||||
return a > b
|
||||
}
|
||||
if data.PlayerData[i].IsRob != data.PlayerData[j].IsRob {
|
||||
return !data.PlayerData[i].IsRob
|
||||
}
|
||||
return data.PlayerData[i].UserId < data.PlayerData[j].UserId
|
||||
})
|
||||
|
||||
for k, v := range data.PlayerData {
|
||||
if !v.IsRob && v.BillCoin > 0 && k <= 1 {
|
||||
if k == 0 {
|
||||
first = 1
|
||||
}
|
||||
if k == 1 {
|
||||
second = 1
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(data.PlayerData) > 2 && first+second == 0 && !data.PlayerData[2].IsRob {
|
||||
third = 1
|
||||
}
|
||||
to = 1
|
||||
|
||||
case 211, 212, 213, 214:
|
||||
data := new(ThirteenWaterType)
|
||||
b, err := json.Marshal(raw.Data)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Marshal error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 2 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
var has, hasPlayer bool
|
||||
for _, v := range data.PlayerData {
|
||||
if v.IsRob {
|
||||
has = true
|
||||
}
|
||||
if !v.IsRob {
|
||||
hasPlayer = true
|
||||
}
|
||||
}
|
||||
if !has || !hasPlayer {
|
||||
// 没有机器人
|
||||
return 0, 0, 0, 0, ctrlType
|
||||
}
|
||||
|
||||
sort.Slice(data.PlayerData, func(i, j int) bool {
|
||||
a, b := data.PlayerData[i].AllScore, data.PlayerData[j].AllScore
|
||||
if a != b {
|
||||
return a > b
|
||||
}
|
||||
if data.PlayerData[i].IsRob != data.PlayerData[j].IsRob {
|
||||
return !data.PlayerData[i].IsRob
|
||||
}
|
||||
return data.PlayerData[i].UserId < data.PlayerData[j].UserId
|
||||
})
|
||||
|
||||
for k, v := range data.PlayerData {
|
||||
if !v.IsRob && v.AllScore > 0 && k <= 1 {
|
||||
if k == 0 {
|
||||
first = 1
|
||||
}
|
||||
if k == 1 {
|
||||
second = 1
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(data.PlayerData) > 2 && first+second == 0 && !data.PlayerData[2].IsRob {
|
||||
third = 1
|
||||
}
|
||||
|
||||
to = 1
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/game/common"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
mymysql "mongo.games.com/goserver/core/mysqlx"
|
||||
)
|
||||
|
||||
// 场次破产率
|
||||
|
||||
// GameFreeIdBankruptcyTimes 场次破产次数
|
||||
func GameFreeIdBankruptcyTimes(plt string, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
db, err := mymysql.GetDatabase("count")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var n int64
|
||||
if err = db.Table("bankrupt_log").Where("bankrupt_time >= ? AND bankrupt_time < ? AND game_free_id = ?",
|
||||
s.Unix(), e.Unix(), gamefreeid).Count(&n).Error; err != nil {
|
||||
logger.Logger.Errorf("find bankrupt count get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// GameFreeIdTotalTimes 场次总局数
|
||||
func GameFreeIdTotalTimes(plt string, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
c, err := mymongo.GetLogCollection(plt, "log_gamedetailed")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err := c.CountDocuments(context.TODO(), bson.M{"time": bson.M{"$gte": s, "$lt": e}, "gamefreeid": gamefreeid})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("find game total times get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// GameBankruptcyRate 场次破产率
|
||||
// 返回 破产局数, 总局数, 错误
|
||||
func GameBankruptcyRate(plt string, startTime, endTime string, gamefreeid int) (int, int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return 0, 0, fmt.Errorf("time format error")
|
||||
}
|
||||
|
||||
b, err := GameFreeIdBankruptcyTimes(plt, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
t, err := GameFreeIdTotalTimes(plt, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return b, t, nil
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/game/common"
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
)
|
||||
|
||||
// 新用户平均局数
|
||||
|
||||
func NewPlayerGameCount(plt string, ids []int, startTime, endTime string, gamefreeid int) (int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
c, err := mymongo.GetLogCollection(plt, mongomodel.LogGamePlayerListLog)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var ret int
|
||||
for _, v := range ids {
|
||||
n, err := c.CountDocuments(context.TODO(), bson.M{"snid": v, "gamefreeid": gamefreeid, "time": bson.M{"$gte": s, "$lt": e}})
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("find player gamedetailedlogid get err: %v", err)
|
||||
return 0, err
|
||||
}
|
||||
ret += int(n)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// NewPlayerGameCountAvg 新用户平均局数
|
||||
// 返回 参与人数,总局数
|
||||
func NewPlayerGameCountAvg(plt string, startTime, endTime string, gamefreeid int) (int, int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return 0, 0, fmt.Errorf("time format error")
|
||||
}
|
||||
ids, err := GetNewPayerIds(plt, startTime, endTime)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
n, err := NewPlayerGameCount(plt, ids, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
b, err := PlayingGameCount(plt, ids, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
return b, n, nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,110 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// 场次平均倍数
|
||||
|
||||
func PlayerGameRate(plt string, startTime, endTime string, gamefreeid int) (total, bombTotal, remain2Total int, rateAvg, bombRateAvg float64, err error) {
|
||||
var totalRate, totalBombRate float64
|
||||
err = GameDetailFunc(plt, startTime, endTime, gamefreeid, func(data bson.M) error {
|
||||
rate, isBomb, bombRate, remain2 := GameDetailRate(data)
|
||||
total++
|
||||
if isBomb {
|
||||
bombTotal++
|
||||
}
|
||||
totalRate += rate
|
||||
totalBombRate += bombRate
|
||||
if remain2 {
|
||||
remain2Total++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if total > 0 {
|
||||
rateAvg = totalRate / float64(total)
|
||||
}
|
||||
if bombTotal > 0 {
|
||||
bombRateAvg = totalBombRate / float64(bombTotal)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// rate 赢分/底分
|
||||
// isBomb 是否有炸弹
|
||||
// bombRate 炸弹倍数,炸弹赢分/底分
|
||||
// remain2 是否有剩余2
|
||||
func GameDetailRate(data bson.M) (rate float64, isBomb bool, bombRate float64, remain2 bool) {
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
gameid := data["gameid"].(int32)
|
||||
gamefreeid := data["gamefreeid"].(int32)
|
||||
logger.Logger.Tracef("GameDetail gameid:%d, gamefreeid:%d", gameid, gamefreeid)
|
||||
|
||||
detail := data["gamedetailednote"]
|
||||
if detail == nil {
|
||||
return
|
||||
}
|
||||
|
||||
raw := new(RabbitMQDataRaw)
|
||||
if err := json.Unmarshal([]byte(detail.(string)), raw); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 1 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
switch gameid {
|
||||
case 207, 208, 209, 210, 240, 241, 242, 243, 244, 245, 246, 247: // tienlen
|
||||
d := new(TienLenType)
|
||||
b, err := json.Marshal(raw.Data)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Marshal error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(b, d); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 2 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range d.PlayerData {
|
||||
if v.BillCoin > 0 {
|
||||
rate = float64(v.BillCoin+v.BillTaxCoin) / float64(d.BaseScore)
|
||||
}
|
||||
if v.BombCoin > 0 {
|
||||
isBomb = true
|
||||
bombRate = float64(v.BombCoin+v.BombTaxCoin) / float64(d.BaseScore)
|
||||
}
|
||||
if slices.ContainsFunc(v.CardInfoEnd, func(i int32) bool {
|
||||
switch i {
|
||||
case 51, 38, 25, 12:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
remain2 = true
|
||||
}
|
||||
}
|
||||
|
||||
case 211, 212, 213, 214:
|
||||
d := new(ThirteenWaterType)
|
||||
b, err := json.Marshal(raw.Data)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Marshal error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(b, d); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 2 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range d.PlayerData {
|
||||
if v.AllScore > 0 {
|
||||
rate = float64(v.AllScore) / float64(d.BaseScore)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"mongo.games.com/game/common"
|
||||
mongomodel "mongo.games.com/game/statistics/modelmongo"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
)
|
||||
|
||||
// 新用户平均游戏时长
|
||||
|
||||
// 返回 总游戏时长,总局数,错误
|
||||
func NewPlayerGameTime(plt string, ids []int, startTime, endTime string, gamefreeid int) (int, int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
c, err := mymongo.GetLogCollection(plt, mongomodel.LogGamePlayerListLog)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
c2, err := mymongo.GetLogCollection(plt, "log_gamedetailed")
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
var ret int
|
||||
var total int
|
||||
for _, v := range ids {
|
||||
// 查询玩家游戏时长
|
||||
where := bson.M{"snid": v, "time": bson.M{"$gte": s, "$lt": e}}
|
||||
if gamefreeid > 0 {
|
||||
where["gamefreeid"] = gamefreeid
|
||||
}
|
||||
cur, err := c.Find(context.TODO(), where, options.Find().SetProjection(bson.M{"gamedetailedlogid": 1}))
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("find player gamedetailedlogid get err: %v", err)
|
||||
return 0, 0, err
|
||||
}
|
||||
for cur.TryNext(context.TODO()) {
|
||||
var vv struct{ Gamedetailedlogid string }
|
||||
if err = cur.Decode(&vv); err != nil {
|
||||
logger.Logger.Errorf("find player gamedetailedlogid decode err: %v", err)
|
||||
cur.Close(context.Background())
|
||||
return 0, 0, err
|
||||
}
|
||||
// 查询游戏时长
|
||||
var res2 struct{ Gametiming int }
|
||||
r := c2.FindOne(context.TODO(), bson.M{"logid": vv.Gamedetailedlogid}, options.FindOne().SetProjection(bson.M{"gametiming": 1}))
|
||||
if r.Err() != nil && !errors.Is(r.Err(), mongo.ErrNoDocuments) {
|
||||
logger.Logger.Errorf("find game time get err: %v", err)
|
||||
cur.Close(context.Background())
|
||||
return 0, 0, err
|
||||
}
|
||||
if err := r.Decode(&res2); err != nil {
|
||||
logger.Logger.Errorf("find game time decode err: %v", err)
|
||||
cur.Close(context.Background())
|
||||
return 0, 0, err
|
||||
}
|
||||
ret += res2.Gametiming
|
||||
total++
|
||||
}
|
||||
cur.Close(context.Background())
|
||||
}
|
||||
|
||||
return ret, total, nil
|
||||
}
|
||||
|
||||
// NewPlayerGameTimeAvg 新用户平均游戏时长
|
||||
// 新用户平均游戏时长(不算大厅时间):当天注册的玩家在房间中的总时长/当天注册总人数
|
||||
// 返回 参与人数,游戏时长
|
||||
func NewPlayerGameTimeAvg(plt string, startTime, endTime string, gamefreeid int) (int, int, error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return 0, 0, fmt.Errorf("time format error")
|
||||
}
|
||||
ids, err := GetNewPayerIds(plt, startTime, endTime)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
a, _, err := NewPlayerGameTime(plt, ids, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
b, err := PlayingGameCount(plt, ids, startTime, endTime, gamefreeid)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if b == 0 {
|
||||
return 0, 0, nil
|
||||
}
|
||||
return b, a, err
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/game/common"
|
||||
mymongo "mongo.games.com/goserver/core/mongox"
|
||||
)
|
||||
|
||||
type RechargeOfflineData struct {
|
||||
Snid int32
|
||||
Coin int64
|
||||
}
|
||||
|
||||
func OfflineCoin(plt string, startTime, endTime string) (res []*RechargeOfflineData, err error) {
|
||||
s, e := common.StrRFC3339TimeToTime(startTime), common.StrRFC3339TimeToTime(endTime)
|
||||
if s.IsZero() || e.IsZero() {
|
||||
return nil, fmt.Errorf("time format error")
|
||||
}
|
||||
c, err := mymongo.GetLogCollection(plt, "log_dbshop")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cur, err := c.Find(context.TODO(), bson.M{"ts": bson.M{"$gte": s.Unix(), "$lt": e.Unix()}, "consume": 3, "state": 1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cur.Close(context.Background())
|
||||
|
||||
var list []struct{ Snid int32 }
|
||||
if err = cur.All(context.Background(), &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
snids := map[int32]struct{}{}
|
||||
|
||||
for _, v := range list {
|
||||
if _, ok := snids[v.Snid]; ok {
|
||||
continue
|
||||
}
|
||||
snids[v.Snid] = struct{}{}
|
||||
|
||||
// 最后金币数量
|
||||
c, err := mymongo.GetLogCollection(plt, "log_coinex")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
one := c.FindOne(context.TODO(), bson.M{"snid": v.Snid, "cointype": 0, "ts": bson.M{"$lt": e.Unix()}}, options.FindOne().SetSort(bson.M{"ts": -1}))
|
||||
if one.Err() != nil {
|
||||
return nil, one.Err()
|
||||
}
|
||||
var data struct{ Restcount int64 }
|
||||
if err = one.Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = append(res, &RechargeOfflineData{Snid: v.Snid, Coin: data.Restcount})
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package task
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"mongo.games.com/goserver/core/logger"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// RobotWinRate 机器人胜利
|
||||
// 返回 玩家胜利局数,总局数
|
||||
func RobotWinRate(plt string, startTime, endTime string, gamefreeid int) (a, b int, err error) {
|
||||
err = GameDetailFunc(plt, startTime, endTime, gamefreeid, func(data bson.M) error {
|
||||
isPlayerWin, to := GameDetailRobot(data)
|
||||
if isPlayerWin {
|
||||
a++
|
||||
}
|
||||
b += to
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func GameDetailRobot(data bson.M) (isPlayerWin bool, to int) {
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
gameid := data["gameid"].(int32)
|
||||
gamefreeid := data["gamefreeid"].(int32)
|
||||
logger.Logger.Tracef("GameDetailRobot gameid:%d, gamefreeid:%d", gameid, gamefreeid)
|
||||
|
||||
detail := data["gamedetailednote"]
|
||||
if detail == nil {
|
||||
return
|
||||
}
|
||||
|
||||
raw := new(RabbitMQDataRaw)
|
||||
if err := json.Unmarshal([]byte(detail.(string)), raw); err != nil {
|
||||
logger.Logger.Errorf("GameDetailRobot Unmarshal 1 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
switch gameid {
|
||||
case 207, 208, 209, 210, 240, 241, 242, 243, 244, 245, 246, 247: // tienlen
|
||||
data := new(TienLenType)
|
||||
b, err := json.Marshal(raw.Data)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Marshal error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 2 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
var has, hasPlayer bool
|
||||
for _, v := range data.PlayerData {
|
||||
if v.IsRob {
|
||||
has = true
|
||||
}
|
||||
if !v.IsRob {
|
||||
hasPlayer = true
|
||||
}
|
||||
}
|
||||
if !has || !hasPlayer {
|
||||
// 没有机器人
|
||||
return
|
||||
}
|
||||
|
||||
sort.Slice(data.PlayerData, func(i, j int) bool {
|
||||
a, b := data.PlayerData[i].BillCoin+data.PlayerData[i].BillTaxCoin, data.PlayerData[j].BillCoin+data.PlayerData[j].BillTaxCoin
|
||||
if a != b {
|
||||
return a > b
|
||||
}
|
||||
if data.PlayerData[i].IsRob != data.PlayerData[j].IsRob {
|
||||
return !data.PlayerData[i].IsRob
|
||||
}
|
||||
return data.PlayerData[i].UserId < data.PlayerData[j].UserId
|
||||
})
|
||||
|
||||
for k, v := range data.PlayerData {
|
||||
if !v.IsRob && v.BillCoin > 0 && k <= 1 {
|
||||
isPlayerWin = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
to = 1
|
||||
|
||||
case 211, 212, 213, 214:
|
||||
data := new(ThirteenWaterType)
|
||||
b, err := json.Marshal(raw.Data)
|
||||
if err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Marshal error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(b, data); err != nil {
|
||||
logger.Logger.Errorf("GameDetailCount Unmarshal 2 error:%v %v", err, gameid)
|
||||
return
|
||||
}
|
||||
|
||||
var has, hasPlayer bool
|
||||
for _, v := range data.PlayerData {
|
||||
if v.IsRob {
|
||||
has = true
|
||||
}
|
||||
if !v.IsRob {
|
||||
hasPlayer = true
|
||||
}
|
||||
}
|
||||
if !has || !hasPlayer {
|
||||
// 没有机器人
|
||||
return
|
||||
}
|
||||
|
||||
sort.Slice(data.PlayerData, func(i, j int) bool {
|
||||
a, b := data.PlayerData[i].AllScore, data.PlayerData[j].AllScore
|
||||
if a != b {
|
||||
return a > b
|
||||
}
|
||||
if data.PlayerData[i].IsRob != data.PlayerData[j].IsRob {
|
||||
return !data.PlayerData[i].IsRob
|
||||
}
|
||||
return data.PlayerData[i].UserId < data.PlayerData[j].UserId
|
||||
})
|
||||
|
||||
for k, v := range data.PlayerData {
|
||||
if !v.IsRob && v.AllScore > 0 && k <= 1 {
|
||||
isPlayerWin = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
to = 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue