package buried import ( "context" "fmt" "go_dreamfactory/comm" "go_dreamfactory/lego/base" "go_dreamfactory/lego/core" "go_dreamfactory/lego/sys/event" "go_dreamfactory/lego/sys/log" "go_dreamfactory/modules" "go_dreamfactory/pb" cfg "go_dreamfactory/sys/configure/structs" "go_dreamfactory/sys/db" "time" ) /* 模块名:用户埋点完成条件触发系统 模块描述:用户埋点数据中心管理模块 开发人员:李伟 */ const moduleName = "埋点统计中心" type Buried struct { modules.ModuleBase service base.IRPCXService configure *configureComp modelBuried *modelBuried } func NewModule() core.IModule { return &Buried{} } func (this *Buried) GetType() core.M_Modules { return comm.ModuleBuried } func (this *Buried) Init(service core.IService, module core.IModule, options core.IModuleOptions) (err error) { err = this.ModuleBase.Init(service, module, options) this.service = service.(base.IRPCXService) return } func (this *Buried) Start() (err error) { err = this.ModuleBase.Start() this.service.RegisterFunctionName(string(comm.Rpc_ModuleBuriedTrigger), this.Rpc_ModuleBuriedTrigger) return } //装备组件 func (this *Buried) OnInstallComp() { this.ModuleBase.OnInstallComp() this.configure = this.RegisterComp(new(configureComp)).(*configureComp) this.modelBuried = this.RegisterComp(new(modelBuried)).(*modelBuried) } // 跨服埋点触发 func (this *Buried) Rpc_ModuleBuriedTrigger(ctx context.Context, args *pb.Rpc_ModuleBuriedTriggerReq, reply *pb.Rpc_ModuleBuriedTriggerResp) (err error) { this.Debug("跨服埋点触发!", log.Field{Key: "uid", Value: args.Uid}, log.Field{Key: "burieds", Value: args.Burieds}) this.trigger(args.Uid, args.Burieds...) return } //激活数据采集点 func (this *Buried) ActiveCondition(uid string, condiIds ...int32) (err error) { var ( conf *cfg.GameBuriedCondiData bdatas *pb.DBBuried chanage bool ) if bdatas, err = this.modelBuried.getUserBurieds(uid); err != nil { return } for _, v := range condiIds { if conf, err = this.configure.getburiedcondidata(v); err != nil { return } if bdata, ok := bdatas.Items[conf.Type]; ok { if conf.Rtype == rtype2 { ok = false for _, v1 := range bdata.Condi { if v1.Conid == v { ok = true v1.Value = 0 v1.Statistics = make([]string, 0) v1.Timestamp = time.Now().Unix() v1.State = pb.BuriedItemState_Activated break } } if !ok { bdata.Condi = append(bdata.Condi, &pb.DBBuriedConItem{ Conid: v, State: pb.BuriedItemState_Activated, Value: 0, Statistics: make([]string, 0), Finish: pb.BuriedItemFinishState_unfinish, Timestamp: time.Now().Unix(), }) } chanage = true } } } if chanage { err = this.modelBuried.updateUserBurieds(uid, bdatas) } return } //激活数据采集点 func (this *Buried) CheckCondition(uid string, condiIds ...int32) (condis []*pb.ConIProgress, err error) { var ( bdatas *pb.DBBuried conf *cfg.GameBuriedCondiData ) if bdatas, err = this.modelBuried.getUserBurieds(uid); err != nil { return } condis = make([]*pb.ConIProgress, 0) for _, v := range condiIds { if conf, err = this.configure.getburiedcondidata(v); err != nil { return } if bdata, ok := bdatas.Items[conf.Type]; ok { for _, v1 := range bdata.Condi { if v1.Conid == v { condis = append(condis, &pb.ConIProgress{ Btype: conf.Type, Conid: v1.Conid, Value: v1.Value, Target: conf.Value, State: v1.Finish, }) } } } } return } //触发埋点 func (this *Buried) TriggerBuried(uid string, burieds ...*pb.BuriedParam) { if db.IsCross() { var ( stag string err error ) if stag, err = comm.UidToSTag(uid); err != nil { this.Error("远程触发埋点错误!", log.Field{Key: "uid", Value: uid}, log.Field{Key: "err", Value: err.Error()}) return } if _, err = this.service.AcrossClusterRpcGo( context.Background(), stag, comm.Service_Worker, string(comm.Rpc_ModuleBuriedTrigger), pb.Rpc_ModuleBuriedTriggerReq{ Uid: uid, Burieds: burieds, }, nil); err != nil { this.Error("远程触发埋点错误!", log.Field{Key: "burieds", Value: burieds}, log.Field{Key: "err", Value: err.Error()}) return } } else { this.trigger(uid, burieds...) } } func (this *Buried) trigger(uid string, burieds ...*pb.BuriedParam) { var ( pass map[*pb.BuriedParam][]*cfg.GameBuriedCondiData = make(map[*pb.BuriedParam][]*cfg.GameBuriedCondiData) bconf *cfg.GameBuriedTypeData bdatas *pb.DBBuried bdata *pb.DBBuriedItem bitem *pb.DBBuriedConItem ok bool change bool changes []*pb.ConIProgress // completeConIds []int32 //完成id列表 err error ) this.Debug("触发埋点!", log.Field{Key: "burieds", Value: burieds}) lock, _ := this.modelBuried.userlock(uid) err = lock.Lock() if err != nil { this.Error("埋点分布式锁失效 err!", log.Field{Key: "uid", Value: uid}, log.Field{Key: "err", Value: err.Error()}) return } defer lock.Unlock() for _, buried := range burieds { conds := this.configure.getCondiDatas(buried.TaskType) if bconf, err = this.configure.getburiedtypedata(buried.TaskType); err != nil { this.Error("未找到目标埋点类型配置", log.Field{Key: "type", Value: buried.TaskType}) continue } for _, cond := range conds { if this.checkburied(buried, bconf, cond) { //判断此埋点数据是否有效 if _, ok := pass[buried]; !ok { pass[buried] = make([]*cfg.GameBuriedCondiData, 0) } pass[buried] = append(pass[buried], cond) this.Debug("校验通过埋点条件!", log.Field{Key: "埋点id", Value: buried.TaskType}, log.Field{Key: "条件id", Value: cond.Id}) } } } if len(pass) > 0 { if bdatas, err = this.modelBuried.getUserBurieds(uid); err != nil { return } } changes = make([]*pb.ConIProgress, 0) //处理校验通过埋点数据 for buried, conds := range pass { this.Debug("更新埋点数据", log.Field{Key: "埋点类型", Value: buried.TaskType}) if bconf, err = this.configure.getburiedtypedata(buried.TaskType); err != nil { this.Error("未找到目标埋点类型配置", log.Field{Key: "type", Value: buried.TaskType}) continue } if bdata, ok = bdatas.Items[int32(buried.TaskType)]; !ok { bdatas.Items[int32(buried.TaskType)] = &pb.DBBuriedItem{ Btype: int32(buried.TaskType), Condi: make([]*pb.DBBuriedConItem, 0), } bdata = bdatas.Items[int32(buried.TaskType)] } for _, cond := range conds { this.Debug("更新埋点数据", log.Field{Key: "埋点类型", Value: buried.TaskType}, log.Field{Key: "条件类型", Value: cond.Id}) if cond.Rtype == rtype1 { //创号后入录 if change, bitem, err = this.updateAndCheckBuried(bconf, bdata, buried, cond, true); change { changes = append(changes, &pb.ConIProgress{ Btype: bdata.Btype, Conid: cond.Id, Value: bitem.Value, Target: cond.Value, State: bitem.Finish, }) } } else if cond.Rtype == rtype2 { //任务接取后才会录入 判断用户埋点数据是否存在 不存在等待任务系统调用接口 ActivationBuried 激活 if change, bitem, err = this.updateAndCheckBuried(bconf, bdata, buried, cond, false); change { changes = append(changes, &pb.ConIProgress{ Btype: bdata.Btype, Conid: cond.Id, Value: bitem.Value, Target: cond.Value, State: bitem.Finish, }) } } else { this.Error("未知的任务类型", log.Field{Key: "埋点类型", Value: buried.TaskType}, log.Field{Key: "条件Id", Value: cond.Id}, log.Field{Key: "条件类型", Value: cond.Rtype}) } } change = true } if change { //同步数据 if err = this.modelBuried.updateUserBurieds(uid, bdatas); err != nil { this.Error("更新用户埋点数据错误!", log.Field{Key: "err", Value: err.Error()}) return } } //通知事件 if len(changes) > 0 { this.Debug("条件达成通知", log.Field{Key: "ConIds", Value: changes}) event.TriggerEvent(comm.EventBuriedComplete, uid, changes) } } //更新并校验完成 func (this *Buried) updateAndCheckBuried(bconf *cfg.GameBuriedTypeData, bdata *pb.DBBuriedItem, collec *pb.BuriedParam, cond *cfg.GameBuriedCondiData, autoActivated bool) (chanage bool, bitem *pb.DBBuriedConItem, err error) { var ( ok bool ) for _, v := range bdata.Condi { if v.Conid == cond.Id { bitem = v ok = true break } } if !ok { if autoActivated { //自动激活 bitem = &pb.DBBuriedConItem{ Conid: cond.Id, State: pb.BuriedItemState_Activated, Value: 0, Statistics: make([]string, 0), Timestamp: time.Now().Unix(), } bdata.Condi = append(bdata.Condi, bitem) } else { this.Debug("任务需激活才可写入!", log.Field{Key: "埋点Id", Value: bdata.Btype}, log.Field{Key: "条件Id", Value: cond.Id}) return } } if bitem.State == pb.BuriedItemState_Inactivated || bitem.State == pb.BuriedItemState_Freeze { //未激活和冻结 不在处理 this.Debug("检测到任务状态不可写入!", log.Field{Key: "埋点Id", Value: bdata.Btype}, log.Field{Key: "条件Id", Value: bitem.Conid}, log.Field{Key: "State", Value: bitem.State}) return } switch bconf.Insert { //数据接入方式 case overlay: //累加数据 bitem.Value += collec.Value chanage = true case cover: if bitem.Value != collec.Value { chanage = true } bitem.Value = collec.Value case statistics: ok = true for _, v := range bitem.Statistics { if v == collec.Statistics { //已统计过 ok = false } } if ok { bitem.Statistics = append(bitem.Statistics, collec.Statistics) bitem.Value = int32(len(bitem.Statistics)) chanage = true } default: err = fmt.Errorf("未知的埋点数据处理类型:%d", bconf.Insert) this.Error("未知的埋点数据处理类型!", log.Field{Key: "Insert", Value: bconf.Insert}) return } if bitem.Value >= cond.Value { //完成进度 bitem.Finish = pb.BuriedItemFinishState_finish if cond.Lock == autolock { //完成后自动锁定 bitem.State = pb.BuriedItemState_Freeze } } else { this.Debug("完成条件未达成!", log.Field{Key: "埋点Id", Value: bdata.Btype}, log.Field{Key: "条件Id", Value: bitem.Conid}, log.Field{Key: "当前进度", Value: bitem.Value}, log.Field{Key: "目标进度", Value: cond.Value}) } return } //判断埋点数据的有效性 func (this *Buried) checkburied(buried *pb.BuriedParam, bconf *cfg.GameBuriedTypeData, conf *cfg.GameBuriedCondiData) (efficient bool) { if !(len(buried.Filter) == len(conf.Filter) && len(bconf.Filter) == len(conf.Filter)) { this.Error("校验埋点错误!", log.Field{Key: "buried", Value: buried}, log.Field{Key: "conf", Value: conf}) return } for i, v := range conf.Filter { efficient = false value := buried.Filter[i] symbol := bconf.Filter[i] target := conf.Filter[i] switch symbol { case eq: //== if value == target { efficient = true } case gt: //> if value > target { efficient = true } case gte: //>= if value >= target { efficient = true } case lt: //< if value < target { efficient = true } case lte: //<= if value <= target { efficient = true } case ne: //!= if value != target { efficient = true } default: this.Error("校验埋点配置错误!", log.Field{Key: "不存在的比较符号", Value: v}, log.Field{Key: "buried", Value: buried}, log.Field{Key: "conf", Value: conf}) return } if !efficient { //校验不过 this.Debug("校验不通!", log.Field{Key: "埋点id", Value: buried.TaskType}, log.Field{Key: "条件id", Value: conf.Id}, log.Field{Key: "判断公式", Value: fmt.Sprintf("%d %s %d", value, symbol, target)}) return } } efficient = true return }