玩家游戏记录上报

This commit is contained in:
sk 2024-11-19 10:29:34 +08:00
parent 949e6f89e3
commit 46ad7ea75f
54 changed files with 333 additions and 3837 deletions

View File

@ -1,312 +0,0 @@
// --------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------
package internal
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"
modelpkg "mongo.games.com/game/db/model"
"time"
)
type UserFilterFunc func(cols *UserColumns) interface{}
type UserUpdateFunc func(cols *UserColumns) interface{}
type UserPipelineFunc func(cols *UserColumns) interface{}
type UserCountOptionsFunc func(cols *UserColumns) *options.CountOptions
type UserAggregateOptionsFunc func(cols *UserColumns) *options.AggregateOptions
type UserFindOneOptionsFunc func(cols *UserColumns) *options.FindOneOptions
type UserFindManyOptionsFunc func(cols *UserColumns) *options.FindOptions
type UserUpdateOptionsFunc func(cols *UserColumns) *options.UpdateOptions
type UserDeleteOptionsFunc func(cols *UserColumns) *options.DeleteOptions
type UserInsertOneOptionsFunc func(cols *UserColumns) *options.InsertOneOptions
type UserInsertManyOptionsFunc func(cols *UserColumns) *options.InsertManyOptions
type User struct {
Columns *UserColumns
Database *mongo.Database
Collection *mongo.Collection
}
type UserColumns struct {
ID string
Account string // 用户账号
Password string // 用户密码
Salt string // 密码
Mobile string // 用户手机
Email string // 用户邮箱
Nickname string // 用户昵称
Signature string // 用户签名
Level string // 用户等级
Experience string // 用户经验
Coin string // 用户金币
DeviceID string // 设备ID
RegisterIP string // 注册IP
RegisterTime string // 注册时间
LastLoginIP string // 最近登录IP
LastLoginTime string // 最近登录时间
}
var userColumns = &UserColumns{
ID: "_id",
Account: "account", // 用户账号
Password: "password", // 用户密码
Salt: "salt", // 密码
Mobile: "mobile", // 用户手机
Email: "email", // 用户邮箱
Nickname: "nickname", // 用户昵称
Signature: "signature", // 用户签名
Level: "level", // 用户等级
Experience: "experience", // 用户经验
Coin: "coin", // 用户金币
DeviceID: "device_id", // 设备ID
RegisterIP: "register_ip", // 注册IP
RegisterTime: "register_time", // 注册时间
LastLoginIP: "last_login_ip", // 最近登录IP
LastLoginTime: "last_login_time", // 最近登录时间
}
func NewUser(db *mongo.Database) *User {
return &User{
Columns: userColumns,
Database: db,
Collection: db.Collection("user"),
}
}
// Count returns the number of documents in the collection.
func (dao *User) Count(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserCountOptionsFunc) (int64, error) {
var (
opts *options.CountOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.CountDocuments(ctx, filter, opts)
}
// Aggregate executes an aggregate command against the collection and returns a cursor over the resulting documents.
func (dao *User) Aggregate(ctx context.Context, pipelineFunc UserPipelineFunc, optionsFunc ...UserAggregateOptionsFunc) (*mongo.Cursor, error) {
var (
opts *options.AggregateOptions
pipeline = pipelineFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.Aggregate(ctx, pipeline, opts)
}
// InsertOne executes an insert command to insert a single document into the collection.
func (dao *User) InsertOne(ctx context.Context, model *modelpkg.User, optionsFunc ...UserInsertOneOptionsFunc) (*mongo.InsertOneResult, error) {
if model == nil {
return nil, errors.New("model is nil")
}
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
var opts *options.InsertOneOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertOne(ctx, model, opts)
}
// InsertMany executes an insert command to insert multiple documents into the collection.
func (dao *User) InsertMany(ctx context.Context, models []*modelpkg.User, optionsFunc ...UserInsertManyOptionsFunc) (*mongo.InsertManyResult, error) {
if len(models) == 0 {
return nil, errors.New("models is empty")
}
documents := make([]interface{}, 0, len(models))
for i := range models {
model := models[i]
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
documents = append(documents, model)
}
var opts *options.InsertManyOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertMany(ctx, documents, opts)
}
// UpdateOne executes an update command to update at most one document in the collection.
func (dao *User) UpdateOne(ctx context.Context, filterFunc UserFilterFunc, updateFunc UserUpdateFunc, optionsFunc ...UserUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateOne(ctx, filter, update, opts)
}
// UpdateOneByID executes an update command to update at most one document in the collection.
func (dao *User) UpdateOneByID(ctx context.Context, id string, updateFunc UserUpdateFunc, optionsFunc ...UserUpdateOptionsFunc) (*mongo.UpdateResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.UpdateOne(ctx, func(cols *UserColumns) interface{} {
return bson.M{"_id": objectID}
}, updateFunc, optionsFunc...)
}
// UpdateMany executes an update command to update documents in the collection.
func (dao *User) UpdateMany(ctx context.Context, filterFunc UserFilterFunc, updateFunc UserUpdateFunc, optionsFunc ...UserUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateMany(ctx, filter, update, opts)
}
// FindOne executes a find command and returns a model for one document in the collection.
func (dao *User) FindOne(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserFindOneOptionsFunc) (*modelpkg.User, error) {
var (
opts *options.FindOneOptions
model = &modelpkg.User{}
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
err := dao.Collection.FindOne(ctx, filter, opts).Decode(model)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return model, nil
}
// FindOneByID executes a find command and returns a model for one document in the collection.
func (dao *User) FindOneByID(ctx context.Context, id string, optionsFunc ...UserFindOneOptionsFunc) (*modelpkg.User, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.FindOne(ctx, func(cols *UserColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// FindMany executes a find command and returns many models the matching documents in the collection.
func (dao *User) FindMany(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserFindManyOptionsFunc) ([]*modelpkg.User, error) {
var (
opts *options.FindOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
cur, err := dao.Collection.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
models := make([]*modelpkg.User, 0)
if err = cur.All(ctx, &models); err != nil {
return nil, err
}
return models, nil
}
// DeleteOne executes a delete command to delete at most one document from the collection.
func (dao *User) DeleteOne(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteOne(ctx, filter, opts)
}
// DeleteOneByID executes a delete command to delete at most one document from the collection.
func (dao *User) DeleteOneByID(ctx context.Context, id string, optionsFunc ...UserDeleteOptionsFunc) (*mongo.DeleteResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.DeleteOne(ctx, func(cols *UserColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// DeleteMany executes a delete command to delete documents from the collection.
func (dao *User) DeleteMany(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteMany(ctx, filter, opts)
}
// autofill when inserting data
func (dao *User) autofill(ctx context.Context, model *modelpkg.User) error {
if model.ID.IsZero() {
model.ID = primitive.NewObjectID()
}
if model.RegisterTime == 0 {
model.RegisterTime = primitive.NewDateTimeFromTime(time.Now())
}
if model.LastLoginTime == 0 {
model.LastLoginTime = primitive.NewDateTimeFromTime(time.Now())
}
return nil
}

View File

@ -1,22 +0,0 @@
package dao
import (
"go.mongodb.org/mongo-driver/mongo"
"mongo.games.com/game/db/dao/internal"
)
type UserColumns = internal.UserColumns
type User struct {
*internal.User
}
func NewUser(db *mongo.Database, c *mongo.Collection) *User {
v := internal.NewUser(nil)
v.Database = db
v.Collection = c
panic("创建索引")
//c.Indexes().CreateOne()
//c.Indexes().CreateMany()
return &User{User: v}
}

View File

@ -1,2 +0,0 @@
mongoctl -model-dir=./model -model-names=User -dao-dir=./dao
protoc --proto_path=./proto --go_out=../../../ --go-grpc_out=../../../ ./proto/*.proto

View File

@ -1,25 +0,0 @@
package model
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
//go:generate mongoctl -model-dir=. -model-names=User -dao-dir=../dao
type User struct {
ID primitive.ObjectID `bson:"_id" gen:"autoFill"`
Account string `bson:"account"` // 用户账号
Password string `bson:"password"` // 用户密码
Salt string `bson:"salt"` // 密码
Mobile string `bson:"mobile"` // 用户手机
Email string `bson:"email"` // 用户邮箱
Nickname string `bson:"nickname"` // 用户昵称
Signature string `bson:"signature"` // 用户签名
Level int `bson:"level"` // 用户等级
Experience int `bson:"experience"` // 用户经验
Coin int `bson:"coin"` // 用户金币
DeviceID string `bson:"device_id"` // 设备ID
RegisterIP string `bson:"register_ip"` // 注册IP
RegisterTime primitive.DateTime `bson:"register_time" gen:"autoFill"` // 注册时间
LastLoginIP string `bson:"last_login_ip"` // 最近登录IP
LastLoginTime primitive.DateTime `bson:"last_login_time" gen:"autoFill"` // 最近登录时间
}

View File

@ -1,10 +0,0 @@
syntax = "proto3";
package public;
option go_package = "mongo.games.com/game/db/proto/public";
//
message Item {
int32 Id = 1; // id
int64 Num = 2; //
}

View File

@ -1,151 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1-devel
// protoc v3.19.4
// source: public.proto
package public
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Item struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id int32 `protobuf:"varint,1,opt,name=Id,proto3" json:"Id,omitempty"` // id
Num int64 `protobuf:"varint,2,opt,name=Num,proto3" json:"Num,omitempty"` // 数量
}
func (x *Item) Reset() {
*x = Item{}
if protoimpl.UnsafeEnabled {
mi := &file_public_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Item) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Item) ProtoMessage() {}
func (x *Item) ProtoReflect() protoreflect.Message {
mi := &file_public_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Item.ProtoReflect.Descriptor instead.
func (*Item) Descriptor() ([]byte, []int) {
return file_public_proto_rawDescGZIP(), []int{0}
}
func (x *Item) GetId() int32 {
if x != nil {
return x.Id
}
return 0
}
func (x *Item) GetNum() int64 {
if x != nil {
return x.Num
}
return 0
}
var File_public_proto protoreflect.FileDescriptor
var file_public_proto_rawDesc = []byte{
0x0a, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x22, 0x28, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e,
0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x49, 0x64, 0x12, 0x10,
0x0a, 0x03, 0x4e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x4e, 0x75, 0x6d,
0x42, 0x26, 0x5a, 0x24, 0x6d, 0x6f, 0x6e, 0x67, 0x6f, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x73, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x6d, 0x65, 0x2f, 0x64, 0x62, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_public_proto_rawDescOnce sync.Once
file_public_proto_rawDescData = file_public_proto_rawDesc
)
func file_public_proto_rawDescGZIP() []byte {
file_public_proto_rawDescOnce.Do(func() {
file_public_proto_rawDescData = protoimpl.X.CompressGZIP(file_public_proto_rawDescData)
})
return file_public_proto_rawDescData
}
var file_public_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_public_proto_goTypes = []interface{}{
(*Item)(nil), // 0: public.Item
}
var file_public_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_public_proto_init() }
func file_public_proto_init() {
if File_public_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_public_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Item); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_public_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_public_proto_goTypes,
DependencyIndexes: file_public_proto_depIdxs,
MessageInfos: file_public_proto_msgTypes,
}.Build()
File_public_proto = out.File
file_public_proto_rawDesc = nil
file_public_proto_goTypes = nil
file_public_proto_depIdxs = nil
}

View File

@ -1,21 +0,0 @@
syntax = "proto3";
package user;
option go_package = "mongo.games.com/game/db/proto/user";
import "public.proto";
service UserServer {
rpc Save (SaveReq) returns (SaveRsp){}
}
//
message SaveReq {
string Platform = 1;
string name = 2;
repeated public.Item Items = 3;
}
//
message SaveRsp {
string msg = 1 ;
}

View File

@ -1,235 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1-devel
// protoc v3.19.4
// source: user.proto
package user
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
public "mongo.games.com/game/db/proto/public"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
//客户端发送给服务端
type SaveReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Platform string `protobuf:"bytes,1,opt,name=Platform,proto3" json:"Platform,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Items []*public.Item `protobuf:"bytes,3,rep,name=Items,proto3" json:"Items,omitempty"`
}
func (x *SaveReq) Reset() {
*x = SaveReq{}
if protoimpl.UnsafeEnabled {
mi := &file_user_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SaveReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SaveReq) ProtoMessage() {}
func (x *SaveReq) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SaveReq.ProtoReflect.Descriptor instead.
func (*SaveReq) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{0}
}
func (x *SaveReq) GetPlatform() string {
if x != nil {
return x.Platform
}
return ""
}
func (x *SaveReq) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *SaveReq) GetItems() []*public.Item {
if x != nil {
return x.Items
}
return nil
}
//服务端返回给客户端
type SaveRsp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
}
func (x *SaveRsp) Reset() {
*x = SaveRsp{}
if protoimpl.UnsafeEnabled {
mi := &file_user_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SaveRsp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SaveRsp) ProtoMessage() {}
func (x *SaveRsp) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SaveRsp.ProtoReflect.Descriptor instead.
func (*SaveRsp) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{1}
}
func (x *SaveRsp) GetMsg() string {
if x != nil {
return x.Msg
}
return ""
}
var File_user_proto protoreflect.FileDescriptor
var file_user_proto_rawDesc = []byte{
0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73,
0x65, 0x72, 0x1a, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x22, 0x5d, 0x0a, 0x07, 0x53, 0x61, 0x76, 0x65, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x50,
0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50,
0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x05, 0x49,
0x74, 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x22,
0x1b, 0x0a, 0x07, 0x53, 0x61, 0x76, 0x65, 0x52, 0x73, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73,
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x32, 0x34, 0x0a, 0x0a,
0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x04, 0x53, 0x61,
0x76, 0x65, 0x12, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x52, 0x65,
0x71, 0x1a, 0x0d, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x52, 0x73, 0x70,
0x22, 0x00, 0x42, 0x24, 0x5a, 0x22, 0x6d, 0x6f, 0x6e, 0x67, 0x6f, 0x2e, 0x67, 0x61, 0x6d, 0x65,
0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x6d, 0x65, 0x2f, 0x64, 0x62, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_user_proto_rawDescOnce sync.Once
file_user_proto_rawDescData = file_user_proto_rawDesc
)
func file_user_proto_rawDescGZIP() []byte {
file_user_proto_rawDescOnce.Do(func() {
file_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_user_proto_rawDescData)
})
return file_user_proto_rawDescData
}
var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_user_proto_goTypes = []interface{}{
(*SaveReq)(nil), // 0: user.SaveReq
(*SaveRsp)(nil), // 1: user.SaveRsp
(*public.Item)(nil), // 2: public.Item
}
var file_user_proto_depIdxs = []int32{
2, // 0: user.SaveReq.Items:type_name -> public.Item
0, // 1: user.UserServer.Save:input_type -> user.SaveReq
1, // 2: user.UserServer.Save:output_type -> user.SaveRsp
2, // [2:3] is the sub-list for method output_type
1, // [1:2] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_user_proto_init() }
func file_user_proto_init() {
if File_user_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SaveReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SaveRsp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_user_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_user_proto_goTypes,
DependencyIndexes: file_user_proto_depIdxs,
MessageInfos: file_user_proto_msgTypes,
}.Build()
File_user_proto = out.File
file_user_proto_rawDesc = nil
file_user_proto_goTypes = nil
file_user_proto_depIdxs = nil
}

View File

@ -1,109 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v3.19.4
// source: user.proto
package user
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
UserServer_Save_FullMethodName = "/user.UserServer/Save"
)
// UserServerClient is the client API for UserServer service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserServerClient interface {
Save(ctx context.Context, in *SaveReq, opts ...grpc.CallOption) (*SaveRsp, error)
}
type userServerClient struct {
cc grpc.ClientConnInterface
}
func NewUserServerClient(cc grpc.ClientConnInterface) UserServerClient {
return &userServerClient{cc}
}
func (c *userServerClient) Save(ctx context.Context, in *SaveReq, opts ...grpc.CallOption) (*SaveRsp, error) {
out := new(SaveRsp)
err := c.cc.Invoke(ctx, UserServer_Save_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServerServer is the server API for UserServer service.
// All implementations must embed UnimplementedUserServerServer
// for forward compatibility
type UserServerServer interface {
Save(context.Context, *SaveReq) (*SaveRsp, error)
mustEmbedUnimplementedUserServerServer()
}
// UnimplementedUserServerServer must be embedded to have forward compatible implementations.
type UnimplementedUserServerServer struct {
}
func (UnimplementedUserServerServer) Save(context.Context, *SaveReq) (*SaveRsp, error) {
return nil, status.Errorf(codes.Unimplemented, "method Save not implemented")
}
func (UnimplementedUserServerServer) mustEmbedUnimplementedUserServerServer() {}
// UnsafeUserServerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to UserServerServer will
// result in compilation errors.
type UnsafeUserServerServer interface {
mustEmbedUnimplementedUserServerServer()
}
func RegisterUserServerServer(s grpc.ServiceRegistrar, srv UserServerServer) {
s.RegisterService(&UserServer_ServiceDesc, srv)
}
func _UserServer_Save_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SaveReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServerServer).Save(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserServer_Save_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServerServer).Save(ctx, req.(*SaveReq))
}
return interceptor(ctx, in, info, handler)
}
// UserServer_ServiceDesc is the grpc.ServiceDesc for UserServer service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var UserServer_ServiceDesc = grpc.ServiceDesc{
ServiceName: "user.UserServer",
HandlerType: (*UserServerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Save",
Handler: _UserServer_Save_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "user.proto",
}

View File

@ -1,58 +0,0 @@
package rpc
import (
"errors"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/game/db/proto/user"
"mongo.games.com/game/db/rpc/server"
"mongo.games.com/game/db/rpc/svc"
)
// GrpcServer grpc服务
var GrpcServer *grpc.Server
// GrpcClientConn grpc客户端连接
var GrpcClientConn *grpc.ClientConn
// RunGrpcServer 启动grpc服务端
func RunGrpcServer(addr string) {
GrpcServer = grpc.NewServer()
registerGrpcServer()
ln, err := net.Listen("tcp", addr)
if err != nil {
panic(errors.New("db grpc failed to listen: " + err.Error()))
}
err = GrpcServer.Serve(ln)
if err != nil {
panic(errors.New("db grpc failed to serve: " + err.Error()))
}
logger.Logger.Infof("db grpc start success")
}
// NewGrpcClientConn 创建grpc客户端连接
func NewGrpcClientConn(addr string) {
var err error
GrpcClientConn, err = grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
panic(errors.New("db grpc failed to dial: " + err.Error()))
}
registerGrpcClient()
}
// registerGrpcServer 注册grpc服务
func registerGrpcServer() {
ctx := svc.NewServiceContext()
user.RegisterUserServerServer(GrpcServer, server.NewUserServer(ctx))
}
var UserClient user.UserServerClient
func registerGrpcClient() {
UserClient = user.NewUserServerClient(GrpcClientConn)
}

View File

@ -1,34 +0,0 @@
package logic
import (
"context"
"mongo.games.com/game/db/dao"
"mongo.games.com/game/db/model"
"mongo.games.com/game/db/proto/user"
"mongo.games.com/game/db/rpc/svc"
)
type UserLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLogic {
return &UserLogic{
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *UserLogic) Save(in *user.SaveReq) (*user.SaveRsp, error) {
u, err := svc.GetUserCollection(in.GetPlatform(), model.User{}, dao.NewUser)
if err != nil {
return nil, err
}
_, err = u.InsertOne(l.ctx, &model.User{})
if err != nil {
return nil, err
}
return &user.SaveRsp{}, nil
}

View File

@ -1,29 +0,0 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.7.2
// Source: user.proto
package server
import (
"context"
"mongo.games.com/game/db/proto/user"
"mongo.games.com/game/db/rpc/logic"
"mongo.games.com/game/db/rpc/svc"
)
type UserServer struct {
svcCtx *svc.ServiceContext
user.UnimplementedUserServerServer
}
func NewUserServer(svcCtx *svc.ServiceContext) *UserServer {
return &UserServer{
svcCtx: svcCtx,
}
}
func (s *UserServer) Save(ctx context.Context, in *user.SaveReq) (*user.SaveRsp, error) {
l := logic.NewUserLogic(ctx, s.svcCtx)
return l.Save(in)
}

View File

@ -1,97 +0,0 @@
package svc
import (
"fmt"
"reflect"
"strings"
"go.mongodb.org/mongo-driver/mongo"
mon "mongo.games.com/game/mongo"
"mongo.games.com/game/util/viperx"
"mongo.games.com/goserver/core/logger"
)
// ServiceContext 服务上下文
// 依赖注入
type ServiceContext struct {
}
func NewServiceContext() *ServiceContext {
vp := viperx.GetViper("mgo", "json")
// mongo初始化
conf := &mon.Config{}
err := vp.Unmarshal(conf)
if err != nil {
panic(fmt.Errorf("mongo config error: %v", err))
}
mon.Init(conf)
return &ServiceContext{}
}
type TableName interface {
TableName() string
}
// GetTableName 获取表名
func GetTableName(model any) string {
if m, ok := model.(TableName); ok {
return m.TableName()
}
t := reflect.TypeOf(model)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
panic("model must be a struct or a pointer to a struct")
}
return strings.ToLower(t.Name())
}
// GetUserCollection 用户库
func GetUserCollection[T any](platform string, model any, f func(database *mongo.Database, c *mongo.Collection) T) (T, error) {
c, err := mon.GetUserCollection(platform, GetTableName(model))
if err != nil {
var z T
logger.Logger.Errorf("GetUserCollection error: %v", err)
return z, err
}
return f(c.Database.Database, c.Collection), nil
}
// GetLogCollection 日志库
func GetLogCollection[T any](platform string, model any, f func(database *mongo.Database, c *mongo.Collection) T) (T, error) {
c, err := mon.GetLogCollection(platform, GetTableName(model))
if err != nil {
var z T
logger.Logger.Errorf("GetLogCollection error: %v", err)
return z, err
}
return f(c.Database.Database, c.Collection), nil
}
// GetGlobalUserCollection 全局用户库
func GetGlobalUserCollection[T any](model any, f func(database *mongo.Database, c *mongo.Collection) T) (T, error) {
c, err := mon.GetGlobalUserCollection(GetTableName(model))
if err != nil {
var z T
logger.Logger.Errorf("GetGlobalUserCollection error: %v", err)
return z, err
}
return f(c.Database.Database, c.Collection), nil
}
// GetGlobalLogCollection 全局日志库
func GetGlobalLogCollection[T any](model any, f func(database *mongo.Database, c *mongo.Collection) T) (T, error) {
c, err := mon.GetGlobalLogCollection(GetTableName(model))
if err != nil {
var z T
logger.Logger.Errorf("GetGlobalLogCollection error: %v", err)
return z, err
}
return f(c.Database.Database, c.Collection), nil
}

View File

@ -640,8 +640,8 @@ func (this *Player) ReportGameEvent(tax, taxex, changeCoin, validbet, validFlow,
gamingTime := int32(time.Now().Sub(this.scene.GameNowTime).Seconds())
mq.Write(model.CreatePlayerGameRecEvent(this.SnId, tax, taxex, changeCoin, validbet, validFlow, in, out,
int32(this.scene.GameId), this.scene.GetGameFreeId(), int32(this.scene.GameMode),
this.scene.GetRecordId(), this.Channel, this.BeUnderAgentCode, this.Platform, this.City, this.DeviceOS,
this.scene.GameId, this.scene.GetGameFreeId(), int32(this.scene.GameMode),
this.scene.GetRecordId(), this.Channel, this.ChannelId, this.BeUnderAgentCode, this.Platform, this.City, this.DeviceOS,
this.CreateTime, gamingTime, gameFirstTime, gameFreeFirstTime, gameTimes, gameFreeTimes, this.LastLoginTime,
this.TelephonePromoter, this.DeviceId), mq.BackGameRecord)
}

4
go.mod
View File

@ -18,10 +18,8 @@ require (
github.com/howeyc/fsnotify v0.9.0
github.com/idealeak/goserver v0.0.0-20201014040547-b8f686262078
github.com/jinzhu/now v1.1.5
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/mojocn/base64Captcha v1.3.6
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cast v1.7.0
github.com/spf13/viper v1.19.0
github.com/tealeg/xlsx v1.0.5
@ -63,6 +61,7 @@ require (
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/reedsolomon v1.12.4 // indirect
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
github.com/lestrrat-go/strftime v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@ -76,6 +75,7 @@ require (
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
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/afero v1.11.0 // indirect

7
go.sum
View File

@ -168,8 +168,9 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@ -296,8 +297,8 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=

View File

@ -1,8 +1,6 @@
package model
import (
"encoding/json"
"strconv"
"time"
"github.com/jinzhu/now"
@ -90,30 +88,30 @@ type LuckyDiceGameNoteData struct {
}
// 在线统计
type PlayerOnlineEvent struct {
Online map[int]int
Time time.Time
}
func MarshalPlayerOnlineEvent(source int32, online map[string]int) (data string, err error) {
m := map[int]int{}
for k, v := range online {
i, _ := strconv.Atoi(k)
m[i] = v
}
raw := &RabbitMQDataRaw{
Source: source,
Data: &PlayerOnlineEvent{
Online: m,
Time: time.Now(),
},
}
d, err := json.Marshal(raw)
if err != nil {
return
}
return string(d), nil
}
//type PlayerOnlineEvent struct {
// Online map[int]int
// Time time.Time
//}
//
//func MarshalPlayerOnlineEvent(source int32, online map[string]int) (data string, err error) {
// m := map[int]int{}
// for k, v := range online {
// i, _ := strconv.Atoi(k)
// m[i] = v
// }
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &PlayerOnlineEvent{
// Online: m,
// Time: time.Now(),
// },
// }
// d, err := json.Marshal(raw)
// if err != nil {
// return
// }
// return string(d), nil
//}
// 玩家登录
type PlayerLoginEvent struct {
@ -166,34 +164,34 @@ func CreatePlayerLoginEvent(snid int32, channel, promoter, platform, city, os, i
}
// 用户升级账号
type PlayerBindPhoneEvent struct {
SnId int32 //用户ID
Channel string //渠道
Promoter string //推广
Platform string //平台
City string //城市
OS string //操作系统
Value int32 //占位用
TelephonePromoter int32 //电销
CreateTime int64 //创建日期
BindTime int64 //绑定日期
}
func CreatePlayerBindPhoneEvent(snid int32, channel, promoter, platform, city, os string,
createTime time.Time, telephonePromoter int32) *PlayerBindPhoneEvent {
return &PlayerBindPhoneEvent{
SnId: snid,
Channel: channel,
Promoter: promoter,
TelephonePromoter: telephonePromoter,
Platform: platform,
City: city,
OS: os,
Value: 1,
CreateTime: createTime.Unix(),
BindTime: time.Now().Unix(),
}
}
//type PlayerBindPhoneEvent struct {
// SnId int32 //用户ID
// Channel string //渠道
// Promoter string //推广
// Platform string //平台
// City string //城市
// OS string //操作系统
// Value int32 //占位用
// TelephonePromoter int32 //电销
// CreateTime int64 //创建日期
// BindTime int64 //绑定日期
//}
//
//func CreatePlayerBindPhoneEvent(snid int32, channel, promoter, platform, city, os string,
// createTime time.Time, telephonePromoter int32) *PlayerBindPhoneEvent {
// return &PlayerBindPhoneEvent{
// SnId: snid,
// Channel: channel,
// Promoter: promoter,
// TelephonePromoter: telephonePromoter,
// Platform: platform,
// City: city,
// OS: os,
// Value: 1,
// CreateTime: createTime.Unix(),
// BindTime: time.Now().Unix(),
// }
//}
//func MarshalPlayerBindPhoneEvent(source, snid int32, channel, promoter, platform, city, os string,
// createTime time.Time, telephonePromoter int32) (data string, err error) {
@ -210,46 +208,46 @@ func CreatePlayerBindPhoneEvent(snid int32, channel, promoter, platform, city, o
//}
// 用户升级账号
type PlayerBindAlipayEvent struct {
SnId int32 //用户ID
Channel string //渠道
Promoter string //推广
TelephonePromoter int32 //电销
Platform string //平台
City string //城市
OS string //操作系统
Value int32 //占位用
BindTime int64 //绑定日期
}
func MarshalPlayerBindAlipayEvent(source, snid int32, channel, promoter, platform, city, os string, telephonePromoter int32) (data string, err error) {
raw := &RabbitMQDataRaw{
Source: source,
Data: &PlayerBindAlipayEvent{
SnId: snid,
Channel: channel,
Promoter: promoter,
Platform: platform,
TelephonePromoter: telephonePromoter,
City: city,
OS: os,
Value: 1,
BindTime: time.Now().Local().Unix(),
},
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
//type PlayerBindAlipayEvent struct {
// SnId int32 //用户ID
// Channel string //渠道
// Promoter string //推广
// TelephonePromoter int32 //电销
// Platform string //平台
// City string //城市
// OS string //操作系统
// Value int32 //占位用
// BindTime int64 //绑定日期
//}
//
//func MarshalPlayerBindAlipayEvent(source, snid int32, channel, promoter, platform, city, os string, telephonePromoter int32) (data string, err error) {
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &PlayerBindAlipayEvent{
// SnId: snid,
// Channel: channel,
// Promoter: promoter,
// Platform: platform,
// TelephonePromoter: telephonePromoter,
// City: city,
// OS: os,
// Value: 1,
// BindTime: time.Now().Local().Unix(),
// },
// }
// d, e := json.Marshal(raw)
// if e == nil {
// data = string(d[:])
// }
// err = e
// return
//}
// 玩家游戏记录
type PlayerGameRecEvent struct {
RecordId string //游戏记录ID
SnId int32 //用户ID
Channel string //渠道
Channel string //包类型
Promoter string //推广
Platform string //平台
City string //城市
@ -258,26 +256,26 @@ type PlayerGameRecEvent struct {
GameId int32 //游戏id
ModeId int32 //游戏模式
Tax int64 //税收
//Taxex int64 //税收2
Amount int64 //金币变化(正值为赢;负值为输)
CreateTime int64 //创建时间
CreateDayTime int64 //账号创建时间0点
ValidBet int64 //有效下注数量
ValidFlow int64 //有效流水数量
Out int64 //产出
In int64 //投入
IsNew int32 //是否是新人
GameFreeID int32 //游戏freeid
GamingTime int32 //游戏开始到玩家结算的时长 单位:秒
FirstTime int64 //首次玩该场次游戏时间
PlayTimes int64 //该场次游戏次数
FirstGameTime int64 //首次玩游戏时间
PlayGameTimes int64 //该游戏总次数
LastLoginTime int64 //最后登录时间
DeviceId string //设备id
Amount int64 //金币变化(正值为赢;负值为输)
CreateTime int64 //创建时间
CreateDayTime int64 //账号创建时间0点
ValidBet int64 //有效下注数量
ValidFlow int64 //有效流水数量
Out int64 //产出
In int64 //投入
IsNew int32 //是否是新人
GameFreeID int32 //游戏freeid
GamingTime int32 //游戏开始到玩家结算的时长 单位:秒
FirstTime int64 //首次玩该场次游戏时间
PlayTimes int64 //该场次游戏次数
FirstGameTime int64 //首次玩游戏时间
PlayGameTimes int64 //该游戏总次数
LastLoginTime int64 //最后登录时间
DeviceId string //设备id
ChannelId string //推广渠道id
}
func CreatePlayerGameRecEvent(snid int32, tax, taxex, amount, validbet, validflow, in, out int64, gameid, gameFreeId, modeid int32, recordId, channel, promoter,
func CreatePlayerGameRecEvent(snid int32, tax, taxex, amount, validbet, validflow, in, out int64, gameid, gameFreeId, modeid int32, recordId, channel, channelId, promoter,
platform, city, os string, createDayTime time.Time, gamingTime int32, firstGameFreeTime, firstGameTime time.Time,
playGameFreeTimes, playerGameTimes int64, lastLoginTime time.Time, teleponePromoter int32, deviceId string) *PlayerGameRecEvent {
isNewbie := int32(0)
@ -315,242 +313,227 @@ func CreatePlayerGameRecEvent(snid int32, tax, taxex, amount, validbet, validflo
PlayTimes: playGameFreeTimes,
PlayGameTimes: playerGameTimes,
LastLoginTime: lastLoginTime.Unix(),
DeviceId: deviceId}
}
func MarshalPlayerGameRecEvent(source, snid int32, tax, taxex, amount, validbet, validflow, in, out int64, gameid, gameFreeId, modeid int32, recordId, channel, promoter,
platform, city, os string, createDayTime time.Time, gamingTime int32, firstGameFreeTime time.Time,
playGameFreeTimes int64, lastLoginTime time.Time, telephonePromoter int32, firstGameTime time.Time,
playGameTimes int64, deviceId string) (data string, err error) {
raw := &RabbitMQDataRaw{
Source: source,
Data: CreatePlayerGameRecEvent(snid, tax, taxex, amount, validbet, validflow, in, out, gameid, gameFreeId, modeid, recordId, channel, promoter,
platform, city, os, createDayTime, gamingTime, firstGameFreeTime, firstGameTime, playGameFreeTimes, playGameTimes, lastLoginTime, telephonePromoter, deviceId),
DeviceId: deviceId,
ChannelId: channelId,
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
// 玩家游戏记录
type PlayerGameRecPayEvent struct {
SnId int32 //用户ID
Channel string //渠道
Promoter string //推广
Platform string //平台
City string //城市
OS string //操作系统
TelephonePromoter int32 //电销标签
IsNew int32 //是否新人
IsPay int32 //是否付费
IsGame int32 //是否游戏
CreateTime int64 //记录创建时间
CreateDayTime int64 //记录创建时间0点
Time int64 //当前时间
RegisterDayTime int64 //玩家注册时间
}
func MarshalPlayerGameRecPayEvent(source, snid, isPay, isGame int32, channel, promoter, platform, city, os string,
createDayTime time.Time, orderCreateTime int64, telephonePromoter int32) (data string, err error) {
isNewbie := int32(0)
if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) {
isNewbie = 1
}
tNow := time.Now()
raw := &RabbitMQDataRaw{
Source: source,
Data: &PlayerGameRecPayEvent{
SnId: snid,
Channel: channel,
Promoter: promoter,
Platform: platform,
City: city,
OS: os,
IsNew: isNewbie,
TelephonePromoter: telephonePromoter,
IsPay: isPay,
IsGame: isGame,
RegisterDayTime: createDayTime.Local().Unix(),
CreateTime: time.Unix(orderCreateTime, 0).Local().Unix(),
CreateDayTime: now.New(time.Unix(orderCreateTime, 0)).BeginningOfDay().Local().Unix(),
Time: tNow.Local().Unix(),
},
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
// 破产统计
type BankruptcyEvent struct {
SnId int32 //用户id
Channel string //渠道
Promoter string //推广
Platform string //平台
City string //城市
Value int32 //值
TelephonePromoter int32 //电销标签
IsNew int32 //是否新人
Time int64 //操作时间
GameId int32 //游戏id
GameMode int32 //游戏模式id
GameFreeId int32 //游戏场次id
}
func MarshalBankruptcyEvent(source, snid, telephonePromoter int32, channel, promoter, platform, city string, createDayTime time.Time, gameId, gameMode, gameFreeId int32) (data string, err error) {
isNewbie := int32(0)
if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) {
isNewbie = 1
}
raw := &RabbitMQDataRaw{
Source: source,
Data: &BankruptcyEvent{
SnId: snid,
Channel: channel,
Promoter: promoter,
TelephonePromoter: telephonePromoter,
Platform: platform,
City: city,
IsNew: isNewbie,
Value: 0,
Time: time.Now().Local().Unix(),
GameId: gameId,
GameMode: gameMode,
GameFreeId: gameFreeId,
},
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
// 充值统计
type PlayerPayEvent struct {
SnId int32 //用户id
Channel string //渠道
Promoter string //推广
Platform string //平台
City string //城市
TelephonePromoter int32 //电销标记
Tag int32 //#充值类型 0 API直接充值 1在线充值
BeforeCoin int32 //充值前钱包数量
BeforeBank int32 //充值前保险柜数量
Amount int32 //充值金额
IsNew int32 //是否是新人
Time int64 //操作时间
}
func MarshalPlayerPayEvent(source, snid, tag, beforeCoin, beforeBank, amount int32, channel,
promoter, platform, city string, createDayTime time.Time, orderCreateTime int64,
telephonePromoter int32) (data string, err error) {
isNewbie := int32(0)
if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) {
isNewbie = 1
}
raw := &RabbitMQDataRaw{
Source: source,
Data: &PlayerPayEvent{
SnId: snid,
Channel: channel,
Promoter: promoter,
Platform: platform,
City: city,
Tag: tag,
TelephonePromoter: telephonePromoter,
BeforeCoin: beforeCoin,
BeforeBank: beforeBank,
Amount: amount,
IsNew: isNewbie,
Time: time.Unix(orderCreateTime, 0).Local().Unix(),
},
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
// 系统赠送
type SystemGiveEvent struct {
SnId int32 //用户id
Channel string //渠道
Promoter string //推广
Platform string //平台
City string //城市
TelephonePromoter int32 //电销
Tag int32 //#充值类型 0 API直接充值 1在线充值
Amount int32 //充值金额
Time int64 //操作时间
}
func MarshalSystemGiveEvent(source, snid, tag, amount int32, channel, promoter, platform, city string,
telephonePromoter int32) (data string, err error) {
raw := &RabbitMQDataRaw{
Source: source,
Data: &SystemGiveEvent{
SnId: snid,
Channel: channel,
Promoter: promoter,
Platform: platform,
TelephonePromoter: telephonePromoter,
City: city,
Tag: tag,
Amount: amount,
Time: time.Now().Local().Unix(),
},
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
// 水池变化记录
type GameCoinPoolEvent struct {
Platform string //平台
GameId int32 //游戏id
GroupId int32 //组id
ChangeCoin int64 //变化金币
CurCoin int64 //变化后金币
UpCoin int64 //上限
DownCoin int64 //下限
Time int64 //操作时间
}
func MarshalGameCoinPoolEvent(source int32, platform string, gameid, groupId int32, changeCoin,
curCoin, upCoin, downCoin int64) (data string, err error) {
raw := &RabbitMQDataRaw{
Source: source,
Data: &GameCoinPoolEvent{
Platform: platform,
GameId: gameid,
GroupId: groupId,
ChangeCoin: changeCoin,
CurCoin: curCoin,
UpCoin: upCoin,
DownCoin: downCoin,
Time: time.Now().Local().Unix(),
},
}
d, e := json.Marshal(raw)
if e == nil {
data = string(d[:])
}
err = e
return
}
//type PlayerGameRecPayEvent struct {
// SnId int32 //用户ID
// Channel string //渠道
// Promoter string //推广
// Platform string //平台
// City string //城市
// OS string //操作系统
// TelephonePromoter int32 //电销标签
// IsNew int32 //是否新人
// IsPay int32 //是否付费
// IsGame int32 //是否游戏
// CreateTime int64 //记录创建时间
// CreateDayTime int64 //记录创建时间0点
// Time int64 //当前时间
// RegisterDayTime int64 //玩家注册时间
//}
//
//func MarshalPlayerGameRecPayEvent(source, snid, isPay, isGame int32, channel, promoter, platform, city, os string,
// createDayTime time.Time, orderCreateTime int64, telephonePromoter int32) (data string, err error) {
// isNewbie := int32(0)
// if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) {
// isNewbie = 1
// }
// tNow := time.Now()
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &PlayerGameRecPayEvent{
// SnId: snid,
// Channel: channel,
// Promoter: promoter,
// Platform: platform,
// City: city,
// OS: os,
// IsNew: isNewbie,
// TelephonePromoter: telephonePromoter,
// IsPay: isPay,
// IsGame: isGame,
// RegisterDayTime: createDayTime.Local().Unix(),
// CreateTime: time.Unix(orderCreateTime, 0).Local().Unix(),
// CreateDayTime: now.New(time.Unix(orderCreateTime, 0)).BeginningOfDay().Local().Unix(),
// Time: tNow.Local().Unix(),
// },
// }
// d, e := json.Marshal(raw)
// if e == nil {
// data = string(d[:])
// }
// err = e
// return
//}
//
//// 破产统计
//type BankruptcyEvent struct {
// SnId int32 //用户id
// Channel string //渠道
// Promoter string //推广
// Platform string //平台
// City string //城市
// Value int32 //值
// TelephonePromoter int32 //电销标签
// IsNew int32 //是否新人
// Time int64 //操作时间
// GameId int32 //游戏id
// GameMode int32 //游戏模式id
// GameFreeId int32 //游戏场次id
//}
//
//func MarshalBankruptcyEvent(source, snid, telephonePromoter int32, channel, promoter, platform, city string, createDayTime time.Time, gameId, gameMode, gameFreeId int32) (data string, err error) {
// isNewbie := int32(0)
// if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) {
// isNewbie = 1
// }
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &BankruptcyEvent{
// SnId: snid,
// Channel: channel,
// Promoter: promoter,
// TelephonePromoter: telephonePromoter,
// Platform: platform,
// City: city,
// IsNew: isNewbie,
// Value: 0,
// Time: time.Now().Local().Unix(),
// GameId: gameId,
// GameMode: gameMode,
// GameFreeId: gameFreeId,
// },
// }
// d, e := json.Marshal(raw)
// if e == nil {
// data = string(d[:])
// }
// err = e
// return
//}
//
//// 充值统计
//type PlayerPayEvent struct {
// SnId int32 //用户id
// Channel string //渠道
// Promoter string //推广
// Platform string //平台
// City string //城市
// TelephonePromoter int32 //电销标记
// Tag int32 //#充值类型 0 API直接充值 1在线充值
// BeforeCoin int32 //充值前钱包数量
// BeforeBank int32 //充值前保险柜数量
// Amount int32 //充值金额
// IsNew int32 //是否是新人
// Time int64 //操作时间
//}
//
//func MarshalPlayerPayEvent(source, snid, tag, beforeCoin, beforeBank, amount int32, channel,
// promoter, platform, city string, createDayTime time.Time, orderCreateTime int64,
// telephonePromoter int32) (data string, err error) {
// isNewbie := int32(0)
// if now.BeginningOfDay().Equal(now.New(createDayTime).BeginningOfDay()) {
// isNewbie = 1
// }
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &PlayerPayEvent{
// SnId: snid,
// Channel: channel,
// Promoter: promoter,
// Platform: platform,
// City: city,
// Tag: tag,
// TelephonePromoter: telephonePromoter,
// BeforeCoin: beforeCoin,
// BeforeBank: beforeBank,
// Amount: amount,
// IsNew: isNewbie,
// Time: time.Unix(orderCreateTime, 0).Local().Unix(),
// },
// }
// d, e := json.Marshal(raw)
// if e == nil {
// data = string(d[:])
// }
// err = e
// return
//}
//
//// 系统赠送
//type SystemGiveEvent struct {
// SnId int32 //用户id
// Channel string //渠道
// Promoter string //推广
// Platform string //平台
// City string //城市
// TelephonePromoter int32 //电销
// Tag int32 //#充值类型 0 API直接充值 1在线充值
// Amount int32 //充值金额
// Time int64 //操作时间
//}
//
//func MarshalSystemGiveEvent(source, snid, tag, amount int32, channel, promoter, platform, city string,
// telephonePromoter int32) (data string, err error) {
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &SystemGiveEvent{
// SnId: snid,
// Channel: channel,
// Promoter: promoter,
// Platform: platform,
// TelephonePromoter: telephonePromoter,
// City: city,
// Tag: tag,
// Amount: amount,
// Time: time.Now().Local().Unix(),
// },
// }
// d, e := json.Marshal(raw)
// if e == nil {
// data = string(d[:])
// }
// err = e
// return
//}
//
//// 水池变化记录
//type GameCoinPoolEvent struct {
// Platform string //平台
// GameId int32 //游戏id
// GroupId int32 //组id
// ChangeCoin int64 //变化金币
// CurCoin int64 //变化后金币
// UpCoin int64 //上限
// DownCoin int64 //下限
// Time int64 //操作时间
//}
//
//func MarshalGameCoinPoolEvent(source int32, platform string, gameid, groupId int32, changeCoin,
// curCoin, upCoin, downCoin int64) (data string, err error) {
//
// raw := &RabbitMQDataRaw{
// Source: source,
// Data: &GameCoinPoolEvent{
// Platform: platform,
// GameId: gameid,
//
// GroupId: groupId,
// ChangeCoin: changeCoin,
// CurCoin: curCoin,
// UpCoin: upCoin,
// DownCoin: downCoin,
// Time: time.Now().Local().Unix(),
// },
// }
// d, e := json.Marshal(raw)
// if e == nil {
// data = string(d[:])
// }
// err = e
// return
//}

View File

@ -1,145 +0,0 @@
package mongo
import (
"errors"
"go.mongodb.org/mongo-driver/mongo"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/game/mongo/internal"
)
type DatabaseType string
const (
KeyGlobal = "global"
DatabaseUser DatabaseType = "user"
DatabaseLog DatabaseType = "log"
)
var NotInitError = errors.New("mongo manager is nil, please call Init() first")
type Config = internal.Config
type DatabaseConfig = internal.DatabaseConfig
type Collection = internal.Collection
type Database = internal.Database
var _manager *internal.Manager
// GetConfig 获取配置
func GetConfig() *Config {
if _manager == nil {
return nil
}
return _manager.GetConfig()
}
// Init 初始化
func Init(conf *Config) {
_manager = internal.NewManager(conf)
}
// Restart 重启
func Restart() {
if _manager == nil {
logger.Logger.Error(NotInitError)
return
}
_manager.Restart(_manager.GetConfig())
}
// Close 关闭
func Close() {
internal.Close(_manager)
}
// GetDatabase 获取数据库
// platform: 平台id
// database: 数据库名称
func GetDatabase(platform string, database DatabaseType) (*Database, error) {
if _manager == nil {
return nil, NotInitError
}
return _manager.GetDatabase(platform, string(database))
}
func GetUserDatabase(platform string) (*Database, error) {
return GetDatabase(platform, DatabaseUser)
}
func GetLogDatabase(platform string) (*Database, error) {
return GetDatabase(platform, DatabaseLog)
}
// GetGlobalDatabase 获取全局库
// database: 数据库名称
func GetGlobalDatabase(database DatabaseType) (*Database, error) {
if _manager == nil {
return nil, NotInitError
}
return _manager.GetDatabase(KeyGlobal, string(database))
}
func GetGlobalUserDatabase() (*Database, error) {
return GetGlobalDatabase(DatabaseUser)
}
func GetGlobalLogDatabase() (*Database, error) {
return GetGlobalDatabase(DatabaseLog)
}
// GetGlobalCollection 获取全局库
// database: 数据库名称
// collection: 集合名称
func GetGlobalCollection(database DatabaseType, collection string) (*Collection, error) {
if _manager == nil {
return nil, NotInitError
}
return _manager.GetCollection(KeyGlobal, string(database), collection)
}
func GetGlobalUserCollection(collection string) (*Collection, error) {
return GetGlobalCollection(DatabaseUser, collection)
}
func GetGlobalLogCollection(collection string) (*Collection, error) {
return GetGlobalCollection(DatabaseLog, collection)
}
// GetCollection 获取平台库
// platform: 平台id
// database: 数据库名称
// collection: 集合名称
func GetCollection(platform string, database DatabaseType, collection string) (*Collection, error) {
if _manager == nil {
return nil, NotInitError
}
return _manager.GetCollection(platform, string(database), collection)
}
func GetUserCollection(platform string, collection string) (*Collection, error) {
return GetCollection(platform, DatabaseUser, collection)
}
func GetLogCollection(platform string, collection string) (*Collection, error) {
return GetCollection(platform, DatabaseLog, collection)
}
// GetClient 获取数据库连接
// 默认获取的是 Global, log 的数据库连接
func GetClient() (*mongo.Client, error) {
if _manager == nil {
return nil, NotInitError
}
c, err := _manager.GetCollection(KeyGlobal, string(DatabaseLog), "empty")
if err != nil {
return nil, err
}
return c.Database.Client, nil
}

View File

@ -1,196 +0,0 @@
package internal
import (
"context"
"fmt"
"sync"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"mongo.games.com/goserver/core/logger"
)
type Config struct {
Global map[string]*DatabaseConfig
Platforms map[string]map[string]*DatabaseConfig
}
type DatabaseConfig struct {
HostName string // 主机地址
HostPort int32 // 端口
Database string // 数据库名
Username string // 用户名
Password string // 密码
Options string // 配置
}
type Collection struct {
Database *Database
*mongo.Collection
}
type Database struct {
*DatabaseConfig
Client *mongo.Client
Database *mongo.Database
Collection sync.Map
}
func (d *Database) Connect() error {
if d.DatabaseConfig == nil {
err := fmt.Errorf("mongo Connect error, DatabaseConifg not found")
logger.Logger.Error(err)
return err
}
login := ""
if d.DatabaseConfig.Username != "" {
login = d.DatabaseConfig.Username + ":" + d.DatabaseConfig.Password + "@"
}
host := d.DatabaseConfig.HostName
if d.DatabaseConfig.HostName == "" {
host = "127.0.0.1"
}
port := d.DatabaseConfig.HostPort
if d.DatabaseConfig.HostPort == 0 {
port = 27017
}
myOptions := d.DatabaseConfig.Options
if myOptions != "" {
myOptions = "?" + myOptions
}
s := fmt.Sprintf("mongodb://%s%s:%d/admin%s", login, host, port, myOptions)
client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(s))
if err != nil {
logger.Logger.Errorf("mongo Connect %v error: %v config:%+v", s, err, *d.DatabaseConfig)
return err
}
logger.Logger.Tracef("mongo connect success %+v", *d.DatabaseConfig)
d.Client = client
d.Database = client.Database(d.DatabaseConfig.Database)
return nil
}
func (d *Database) GetCollection(name string) (*Collection, error) {
if d.Database == nil {
err := fmt.Errorf("mongo GetCollection error, collection:%v, database is nil", name)
logger.Logger.Error(err)
return nil, err
}
v, ok := d.Collection.Load(name)
if !ok {
v = &Collection{
Database: d,
Collection: d.Database.Collection(name),
}
d.Collection.Store(name, v)
}
c, _ := v.(*Collection)
return c, nil
}
type Manager struct {
conf *Config
global *sync.Map // 内部库名称:Database
platforms *sync.Map // 平台id:内部库名称:Database
}
func (m *Manager) GetCollection(key, database, collection string) (*Collection, error) {
d, err := m.GetDatabase(key, database)
if err != nil {
return nil, err
}
return d.GetCollection(collection)
}
func (m *Manager) GetDatabase(key, database string) (*Database, error) {
switch key {
case "global":
v, ok := m.global.Load(database)
if !ok {
db := &Database{
DatabaseConfig: m.conf.Global[database],
Collection: sync.Map{},
}
if err := db.Connect(); err != nil {
return nil, err
}
v = db
m.global.Store(database, v)
}
d, _ := v.(*Database)
return d, nil
default:
var mp *sync.Map
v, ok := m.platforms.Load(key) // 平台id
if !ok {
mp = new(sync.Map)
m.platforms.Store(key, mp)
} else {
mp = v.(*sync.Map)
}
v, ok = mp.Load(database)
if !ok {
db := &Database{
DatabaseConfig: m.conf.Platforms[key][database],
Collection: sync.Map{},
}
if err := db.Connect(); err != nil {
return nil, err
}
v = db
mp.Store(database, v)
}
d, _ := v.(*Database)
return d, nil
}
}
func (m *Manager) Restart(conf *Config) {
logger.Logger.Infof("mongo manager restart...")
old := *m
time.AfterFunc(time.Minute, func() {
Close(&old)
})
m.conf = conf
m.global = &sync.Map{}
m.platforms = &sync.Map{}
}
func Close(m *Manager) {
logger.Logger.Infof("mongo manager close")
m.global.Range(func(key, value any) bool {
if v, ok := value.(*Database); ok {
v.Client.Disconnect(nil)
}
return true
})
m.platforms.Range(func(key, value any) bool {
if v, ok := value.(*sync.Map); ok {
v.Range(func(key, value any) bool {
if v, ok := value.(*Database); ok {
v.Client.Disconnect(nil)
}
return true
})
}
return true
})
}
func (m *Manager) GetConfig() *Config {
return m.conf
}
func NewManager(conf *Config) *Manager {
return &Manager{
conf: conf,
global: &sync.Map{},
platforms: &sync.Map{},
}
}

27
statistics/.gitignore vendored
View File

@ -1,27 +0,0 @@
# ---> 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
# Test binary, built with `go test -c`
*.test
# 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

View File

@ -1,8 +0,0 @@
# statistics
数据分析服务
* 定时查询注册表和登录日志获取玩家id根据玩家id触发相关的数据统计
- [x] 新手离开记录
- [ ] 在线时长

View File

@ -1,8 +0,0 @@
set GOPATH=D:\godev
go env -w GO111MODULE=on
set CGO_ENABLED=0
set GOOS=linux
set GOARCH=amd64
go build
pause

View File

@ -1,13 +0,0 @@
package constant
const (
User = "user" // 用户库内部名称
Log = "log" // 日志库内部名称
)
const (
InviteScoreTypeBind = 1 // 绑定邀请码
InviteScoreTypePay = 2 // 充值返佣
InviteScoreTypeRecharge = 3 // 充值完成
InviteScoreTypePayMe = 4 // 充值(自己)
)

View File

@ -1,27 +0,0 @@
# 平台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

View File

@ -1,53 +0,0 @@
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:

View File

@ -1,31 +0,0 @@
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
# 最大空闲连接数
MaxIdleConns: 10
# 最大连接数
MaxOpenConns: 100
# 连接可复用的最大时间
ConnMaxLifetime: 3600
# 连接最大空闲时间
ConnMaxIdletime: 0

View File

@ -1,53 +0,0 @@
package main
import (
"context"
"fmt"
"testing"
"time"
)
type A struct {
}
func B() *A {
return nil
}
func Test1(t *testing.T) {
var a interface{}
a = B()
fmt.Println(a == nil) // false
}
func Test2(t *testing.T) {
c1 := context.Background()
c2, cancel2 := context.WithCancel(c1)
c3, _ := context.WithCancel(c2)
go func() {
select {
case <-c3.Done():
fmt.Println("c3 cancel")
}
}()
time.Sleep(time.Second * 5)
cancel2()
fmt.Println("cancel2")
//time.Sleep(time.Second * 5)
//cancel3()
//fmt.Println("cancel3")
time.Sleep(time.Minute)
}
func Test3(t *testing.T) {
n := time.Now()
y, m, d := n.Date()
n = time.Date(y, m, d, 0, 0, 0, 0, time.Local)
st := n.AddDate(0, 0, -int(n.Weekday()))
et := n.AddDate(0, 0, 7-int(n.Weekday()))
fmt.Println(st, et)
}

View File

@ -1,22 +0,0 @@
<?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>

View File

@ -1,239 +0,0 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"time"
"github.com/spf13/viper"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/game/mongo"
"mongo.games.com/game/mysql"
mongomodel "mongo.games.com/game/statistics/mongo/model"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
"mongo.games.com/game/statistics/static"
"mongo.games.com/game/statistics/syn"
"mongo.games.com/game/statistics/tools"
"mongo.games.com/game/util/viperx"
)
var VP *viper.Viper
//func init() {
// // 日志
// *log.StandardLogger() = *log.New()
//
// // 日志等级
// level, err := log.ParseLevel(VP.GetString("log.level"))
// if err != nil {
// panic(err)
// }
// log.SetLevel(level)
//
// // 打印文件路径及行号
// log.AddHook(tools.NewFileLineHook(log.ErrorLevel))
//
// // 日志切分
// for _, v := range VP.Get("log.rotate").([]interface{}) {
// conf := &tools.RotateLogConfig{}
// b, err := json.Marshal(v)
// if err != nil {
// panic(err)
// }
// if err = json.Unmarshal(b, conf); err != nil {
// panic(err)
// }
// log.AddHook(tools.NewRotateLogHook(conf))
// }
//}
// 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):
tools.RecoverPanicFunc() // 捕获异常
fu(ctx)
}
}
}()
}
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):
tools.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 := &mongo.Config{}
err := vp.Unmarshal(conf)
if err != nil {
panic(fmt.Errorf("mongo config error: %v", err))
}
mongo.Init(conf)
defer mongo.Close()
// mysql
vp = viperx.GetViper("mysql", "yaml")
myConf := &mysql.Config{}
err = vp.Unmarshal(myConf)
if err != nil {
panic(fmt.Errorf("mysql config error: %v", err))
}
mysql.Init(myConf)
defer mysql.Close()
mysql.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 := mysql.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()
}

View File

@ -1,47 +0,0 @@
package model
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 //拉霸专用 输赢
}

View File

@ -1,19 +0,0 @@
package model
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 // 代理层级 例如 1DownSnid 是 UpSnid 的 1 级代理; 2: DownSnid 是 UpSnid 的 2 级代理
Tp int // 返佣类型
Rate int // 返佣比例
Score int // 积分
Money int // 充值金额
Ts int // 时间戳
}

View File

@ -1,27 +0,0 @@
package model
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兑换失败
}

View File

@ -1,33 +0,0 @@
package model
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
}

View File

@ -1,24 +0,0 @@
package model
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"`
}

View File

@ -1,17 +0,0 @@
package model
// 需要自动迁移的表添加在这里 Tables
var Tables = []interface{}{
&LogLogin{},
&LogLoginMid{},
&UserAccount{},
&UserLogin{},
&UserID{},
&LogInviteScoreMid{},
&LogInviteScore{},
&LogInviteUser{},
&LogMid{},
&ItemGain{},
&ItemTotalGain{},
}

View File

@ -1,26 +0,0 @@
package model
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"` // 代理层级 例如 1DownSnid 是 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"` // 代理层级 例如 1DownSnid 是 UpSnid 的 1 级代理; 2: DownSnid 是 UpSnid 的 2 级代理
Ts int `gorm:"index"` // 绑定时间
}

View File

@ -1,16 +0,0 @@
package model
// 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 // 道具数量
}

View File

@ -1,26 +0,0 @@
package model
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"`
}

View File

@ -1,6 +0,0 @@
package model
type LogLoginMid struct {
ID uint `gorm:"primaryKey"`
MID string
}

View File

@ -1,11 +0,0 @@
package model
const (
MidTypeItem = 1 // 道具记录
)
type LogMid struct {
ID uint `gorm:"primaryKey"`
Tp int `gorm:"index"` // 类型
MID string
}

View File

@ -1,18 +0,0 @@
package model
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"`
}

View File

@ -1,9 +0,0 @@
package model
/*
服务定期查询注册和登录信息然后获取玩家id,保存到这张表中用于后续触发和玩家相关的数据统计
*/
type UserID struct {
Snid int `gorm:"primaryKey"`
}

View File

@ -1,29 +0,0 @@
package model
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"` // 推广渠道
}

View File

@ -1,4 +0,0 @@
#!/bin/bash
pkill -2 statistics
echo "close ..."
tail -f log/all_log

View File

@ -1,2 +0,0 @@
#!/bin/bash
nohup ./statistics > /dev/null &

View File

@ -1 +0,0 @@
package static

View File

@ -1,372 +0,0 @@
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/game/mongo"
mymysql "mongo.games.com/game/mysql"
"mongo.games.com/game/statistics/constant"
mongomodel "mongo.games.com/game/statistics/mongo/model"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
)
func getAccountTel(platform string, id int) (string, error) {
acc := &mongomodel.Account{}
cc, err := mymongo.GetCollection(platform, constant.User, mongomodel.UserAccount)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.User, 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.GetCollection(platform, constant.Log, mongomodel.LogLogin)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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.GetCollection(platform, constant.Log, mongomodel.LogLogin)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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.GetCollection(platform, constant.Log, mongomodel.LogLogin)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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.GetCollection(platform, constant.User, mongomodel.UserAccount)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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
}

View File

@ -1,102 +0,0 @@
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"
mymongo "mongo.games.com/game/mongo"
mymysql "mongo.games.com/game/mysql"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
"mongo.games.com/goserver/core/logger"
)
// Data 数据同步方法
// T mongodb数据表结构
// F mongodb中的每条数据的处理操作自行实现
type Data[T any] struct {
Platform string // 平台
MidType int // 数据类型 例如 model.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
}

View File

@ -1,291 +0,0 @@
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"
mymongo "mongo.games.com/game/mongo"
mymysql "mongo.games.com/game/mysql"
"mongo.games.com/game/statistics/constant"
mongomodel "mongo.games.com/game/statistics/mongo/model"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
"mongo.games.com/goserver/core/logger"
)
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.GetCollection(platform, constant.Log, mongomodel.LogInviteScore)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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
}

View File

@ -1,61 +0,0 @@
package syn
import (
"errors"
"time"
"gorm.io/gorm"
"mongo.games.com/game/statistics/constant"
mongomodel "mongo.games.com/game/statistics/mongo/model"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
)
func ItemGainDone(data *Data[mongomodel.ItemLog]) error {
data.MidType = mysqlmodel.MidTypeItem
data.Database = constant.Log
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()
}

View File

@ -1,182 +0,0 @@
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"
mymongo "mongo.games.com/game/mongo"
mymysql "mongo.games.com/game/mysql"
"mongo.games.com/game/statistics/constant"
mongomodel "mongo.games.com/game/statistics/mongo/model"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
"mongo.games.com/goserver/core/logger"
)
/*
登录日志同步使用了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.GetCollection(platform, constant.Log, mongomodel.LogLogin)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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
}

View File

@ -1,106 +0,0 @@
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"
mymongo "mongo.games.com/game/mongo"
mymysql "mongo.games.com/game/mysql"
"mongo.games.com/game/statistics/constant"
mongomodel "mongo.games.com/game/statistics/mongo/model"
mysqlmodel "mongo.games.com/game/statistics/mysql/model"
"mongo.games.com/goserver/core/logger"
)
/*
注册信息同步使用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.GetCollection(platform, constant.User, mongomodel.UserAccount)
if err != nil {
logger.Logger.Errorf("get collection %s %s error %v", constant.Log, 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
}

View File

@ -1,95 +0,0 @@
package tools
import (
"fmt"
"runtime"
"strings"
"time"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/writer"
)
// FileLineHook 新增一个字段用来打印文件路径及行号
type FileLineHook struct {
LogLevels []logrus.Level // 需要打印的日志级别
FieldName string // 字段名称
Skip int // 跳过几层调用栈
Num int // Skip后的查找范围
Test bool // 打印所有调用栈信息,找出合适的 Skip 配置
filename string // 文件名
line int // 行号
}
func (e *FileLineHook) Levels() []logrus.Level {
return e.LogLevels
}
func (e *FileLineHook) Fire(entry *logrus.Entry) error {
for i := 0; i < e.Num; i++ {
_, e.filename, e.line, _ = runtime.Caller(e.Skip + i)
if !strings.Contains(e.filename, "logrus") {
break
}
}
entry.Data[e.FieldName] = fmt.Sprintf("%s:%d", e.filename, e.line)
if e.Test {
buf := [4096]byte{}
n := runtime.Stack(buf[:], false)
fmt.Println(string(buf[:n]))
}
return nil
}
// NewFileLineHook 打印文件路径及行号
// levels 指定日志级别
func NewFileLineHook(levels ...logrus.Level) logrus.Hook {
return &FileLineHook{
LogLevels: levels,
FieldName: "source",
Skip: 8,
Num: 2,
}
}
type RotateLogConfig struct {
Levels []string `json:"levels"`
Pattern string `json:"pattern"`
LinkName string `json:"link_name"`
MaxAge int `json:"max_age"`
RotationTime int `json:"rotation_time"`
RotationCount int `json:"rotation_count"`
RotationSize int `json:"rotation_size"`
}
func NewRotateLogHook(config *RotateLogConfig) logrus.Hook {
var levels []logrus.Level
for _, v := range config.Levels {
level, err := logrus.ParseLevel(v)
if err != nil {
panic(err)
}
levels = append(levels, level)
}
if len(levels) == 0 {
levels = logrus.AllLevels
}
l, err := rotatelogs.New(config.Pattern,
rotatelogs.WithLinkName(config.LinkName),
rotatelogs.WithMaxAge(time.Duration(config.MaxAge)*time.Hour),
rotatelogs.WithRotationTime(time.Duration(config.RotationTime)*time.Hour),
rotatelogs.WithRotationCount(uint(config.RotationCount)),
rotatelogs.WithRotationSize(int64(config.RotationSize)),
)
if err != nil {
panic(err)
}
return &writer.Hook{
Writer: l,
LogLevels: levels,
}
}

View File

@ -1,37 +0,0 @@
package tools
import (
"bytes"
"fmt"
"os"
"runtime"
"sync"
)
var RecoverPanicFunc func(args ...interface{})
func init() {
bufPool := &sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
RecoverPanicFunc = func(args ...interface{}) {
if r := recover(); r != nil {
buf := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(buf)
buf.Reset()
buf.WriteString(fmt.Sprintf("panic: %v\n", r))
for _, v := range args {
buf.WriteString(fmt.Sprintf("%v\n", v))
}
pcs := make([]uintptr, 10)
n := runtime.Callers(3, pcs)
frames := runtime.CallersFrames(pcs[:n])
for f, again := frames.Next(); again; f, again = frames.Next() {
buf.WriteString(fmt.Sprintf("%v:%v %v\n", f.File, f.Line, f.Function))
}
fmt.Fprint(os.Stderr, buf.String())
}
}
}