package provider import ( "context" "joybuywork/internal/app" "joybuywork/internal/consts" "joybuywork/internal/consts/ckey" "joybuywork/internal/plugins/jsonm" "joybuywork/internal/repository/cache" "joybuywork/internal/repository/dao" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/repository/withdefault" "joybuywork/internal/utils" "joybuywork/internal/utils/logic" "joybuywork/internal/utils/merr" "joybuywork/internal/utils/verify" "fmt" "strings" "time" "joybuywork/proto/go_proto/workerr" "gits.branchcn.com/backend/microcode/plugins/derivative/lock" "gorm.io/gorm/clause" ) type FissionUserAttr struct { logic.Context user *model.FissionUser attr map[string]*model.FissionUserAttr } func NewFissionUserAttr(ctx context.Context, user *model.FissionUser) *FissionUserAttr { attr := &FissionUserAttr{ user: user, } attr.Init(ctx, dao.Q, merr.NewBE(ctx)) return attr } func (s *FissionUserAttr) GetAttr(mark string) (*model.FissionUserAttr, error) { if s.attr != nil { return s.attr[mark], nil } var attrMarks = []string{mtype.FissionUserAttrMarkPlatform, mtype.FissionUserAttrMarkLang, mtype.FissionUserAttrMarkUserLv} s.attr = make(map[string]*model.FissionUserAttr, len(attrMarks)) for _, v := range attrMarks { attr, err := cache.NewFissionUserAttr(s.Ctx).FindByUniqIdx(s.user.ID, v) if err != nil { return nil, s.BE().CacheError(err) } s.attr[v] = attr } return s.attr[mark], nil } func (s *FissionUserAttr) WithUser(platform, lang, levelMark, lvMarks, country, centerHashId, moduleId string, channelId int64) (err error) { user := s.user var attrs []*model.FissionUserAttr if lang != "" { attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkLang, Value: user.LangMark, Values: user.LangMark, })) } if levelMark != "" { setLv := strings.Split(lvMarks, ",") attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkUserLv, Value: utils.Ternary[string](verify.InArray(levelMark, setLv...), levelMark, setLv[0]), Values: lvMarks, })) } if country != "" { attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkCountry, Value: country, Values: country, })) } if platform != "" { platform = utils.SupportPlatform(platform) attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkPlatform, Value: platform, Values: platform, })) } if channelId != 0 { webChId := fmt.Sprint(channelId) attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkWebChannel, Value: webChId, Values: webChId, })) } if centerHashId != "" { attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkCenter, Value: centerHashId, Values: centerHashId, })) } if moduleId != "" { attrs = append(attrs, withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: mtype.FissionUserAttrMarkCenterModule, Value: moduleId, Values: moduleId, })) } if len(attrs) == 0 { return } return s.creates(attrs...) } // Platform 更新平台 func (s *FissionUserAttr) Platform(platform string) (err error) { if platform == "" || s.ignore() { return } // 用户关联的平台 platform = utils.SupportPlatform(platform) pfAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkPlatform, platform, platform) if err != nil { return } // 如果没更新,或者平台是其他,就不用处理了 if platform == pfAttr.Value || platform == consts.PlatformOther { return } return s.update(pfAttr, platform) } // WebChannel 更新站外渠道 func (s *FissionUserAttr) WebChannel(channelId int64) (err error) { if channelId == 0 || s.ignore() { return } webChId := fmt.Sprint(channelId) wbcAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkWebChannel, webChId, webChId) if err != nil { return } // 如果没更新,就不用处理了 if webChId == wbcAttr.Value { return } return s.update(wbcAttr, webChId) } // Country 更新国家属性 func (s *FissionUserAttr) Country(country string) (err error) { if country == "" || s.ignore() { return } // 用户关联的国家 pfAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkCountry, country, country) if err != nil { return } // 如果没更新,或者平台是其他,就不用处理了 if country == pfAttr.Value { return } return s.update(pfAttr, country) } // UserLv 更新用户等级 func (s *FissionUserAttr) UserLv(userLv string, setLv []string) (err error) { if userLv == "" || s.ignore() { return } // 用户关联的用户等级 attrVal := utils.Ternary[string](verify.InArray[string](userLv, setLv...), userLv, setLv[0]) values := strings.Join(setLv, ",") pfAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkUserLv, attrVal, values) if err != nil { return } // 如果没更新,或者平台是其他,就不用处理了 if attrVal == pfAttr.Value && verify.AllInArray(setLv, pfAttr.GetValues()) { return } return s.update(pfAttr, setLv...) } // Lang 更新语言 func (s *FissionUserAttr) Lang(lang string) (err error) { if lang == "" || s.ignore() { return } // 用户关联的语言 pfAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkLang, lang, lang) if err != nil { return } // 如果没更新,或者平台是其他,就不用处理了 if lang == pfAttr.Value { return } return s.update(pfAttr, lang) } // Center 更新任务中心来源 func (s *FissionUserAttr) Center(centerHashId string) (err error) { if centerHashId == "" || s.ignore() { return } // 用户关联的任务中心 pfAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkCenter, centerHashId, centerHashId) if err != nil { return } // 如果没更新,就不用处理了 if centerHashId == pfAttr.Value { return } return s.update(pfAttr, centerHashId) } // CenterModule 更新任务中心模块来源 func (s *FissionUserAttr) CenterModule(moduleId string) (err error) { if moduleId == "" || s.ignore() { return } // 用户关联的任务中心模块 pfAttr, err := s.firstOrCreate(mtype.FissionUserAttrMarkCenterModule, moduleId, moduleId) if err != nil { return } // 如果没更新,就不用处理了 if moduleId == pfAttr.Value { return } return s.update(pfAttr, moduleId) } func (s *FissionUserAttr) firstOrCreate(attrMark, value, values string) (attr *model.FissionUserAttr, err error) { user := s.user attr, err = cache.NewFissionUserAttr(s.Ctx).FindByUniqIdx(user.ID, attrMark, cache.WithNilSet(func() ([]byte, error) { createAttr := withdefault.FissionUserAttr(&model.FissionUserAttr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: attrMark, Value: value, Values: values, }) err = s.creates(createAttr) if err != nil { return nil, err } return jsonm.Marshal(createAttr) })) if err != nil { err = s.BE().CacheError(err) return } return } func (s *FissionUserAttr) creates(attrs ...*model.FissionUserAttr) (err error) { err = s.GetDao().FissionUserAttr.WithContext(s.Ctx).Clauses( clause.OnConflict{ DoNothing: true, }).Create(attrs...) if err != nil { err = s.BE().MysqlError(err) return } for _, attr := range attrs { if s.canTotalAttr(attr.Attr) { _ = s.changeTotal(attr.Attr, attr.Value, "") } } return } func (s *FissionUserAttr) createsJ(attrs ...*model.FissionUserJattr) (err error) { err = s.GetDao().FissionUserJattr.WithContext(s.Ctx).Clauses( clause.OnConflict{ DoNothing: true, }).Create(attrs...) if err != nil { err = s.BE().MysqlError(err) return } return } func (s *FissionUserAttr) update(attr *model.FissionUserAttr, vals ...string) (err error) { attrM := s.GetDao().FissionUserAttr attrVals := attr.GetValues() for _, val := range vals { if !verify.InArray(val, attrVals...) { attrVals = append(attrVals, val) } } _, err = attrM.WithContext(s.Ctx).Where(attrM.ID.Eq(attr.ID)).UpdateSimple( attrM.Value.Value(vals[0]), attrM.Values.Value(strings.Join(attrVals, ",")), ) if err != nil { err = s.BE().MysqlError(err) return } if s.canTotalAttr(attr.Attr) { _ = s.changeTotal(attr.Attr, vals[0], attr.Value) } // 清除缓存 _ = cache.NewFissionUserAttr(s.Ctx).Clear(attr) return } func (s *FissionUserAttr) changeTotal(attr string, newVal, oldVal string) (err error) { m := s.GetDao().FissionUserAttrTotal if newVal != "" { var id int64 id, err = s.getTotalId(attr, newVal) if err != nil { return } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(id)).UpdateSimple(m.Num.Add(1)) if err != nil { err = s.BE().MysqlError(err) return } } if oldVal != "" { var id int64 id, err = s.getTotalId(attr, oldVal) if err != nil { return } _, err = m.WithContext(s.Ctx).Where(m.ID.Eq(id)).UpdateSimple(m.Num.Sub(1)) if err != nil { err = s.BE().MysqlError(err) return } } return } func (s *FissionUserAttr) ignore() bool { return s.user.IsView == mtype.Yes } func (s *FissionUserAttr) canTotalAttr(attr string) bool { return verify.InArray[string](attr, mtype.FissionUserAttrMarkPlatform) } func (s *FissionUserAttr) getTotalId(attr, val string) (id int64, err error) { cc := cache.NewFissionUserAttrTotal(s.Ctx) id, err = cc.FindByUniqIdx(s.user.CampaignID, attr, val) if err != nil { err = s.BE().CacheError(err) return } if id != 0 { return } lc := app.NewFollower(s.Ctx, ckey.FissionUserAttrCreateOnce(s.user.CampaignID, attr, val), lock.SetExpire(10*time.Second)).Lock() defer lc.Release() if lc.Error() != nil { err = workerr.BusyError return } id, err = cc.FindByUniqIdx(s.user.CampaignID, attr, val) if err != nil { err = s.BE().CacheError(err) return } if id != 0 { return } total := withdefault.FissionUserAttrTotal(&model.FissionUserAttrTotal{ CampaignID: s.user.CampaignID, Attr: attr, Val: val, Num: 0, }) err = s.GetDao().FissionUserAttrTotal.WithContext(s.Ctx).Create(total) if err != nil { err = s.BE().MysqlError(err) return } id = total.ID return } func (s *FissionUserAttr) CustomParam(paramStr string) (err error) { if paramStr == "" || s.ignore() { return } pfAttr, err := s.firstOrCreateJ(mtype.FissionUserAttrMarkCustomParam, paramStr) if err != nil { return } // 如果没更新,就不用处理了 if paramStr == pfAttr.Value && verify.InArray[string](paramStr, pfAttr.GetValues()...) { return } return s.updateJ(pfAttr, paramStr) } func (s *FissionUserAttr) firstOrCreateJ(attrMark string, values ...string) (attr *model.FissionUserJattr, err error) { user := s.user valj, _ := jsonm.ToSliceString(values) attr, err = cache.NewFissionUserJattr(s.Ctx).FindByUniqIdx(user.ID, attrMark, cache.WithNilSet(func() ([]byte, error) { createAttr := withdefault.FissionUserJattr(&model.FissionUserJattr{ CampaignID: user.CampaignID, UserID: user.ID, Attr: attrMark, Value: values[0], Values: valj, }) err = s.createsJ(createAttr) if err != nil { return nil, err } return jsonm.Marshal(createAttr) })) if err != nil { err = s.BE().CacheError(err) return } return } func (s *FissionUserAttr) updateJ(attr *model.FissionUserJattr, vals ...string) (err error) { attrM := s.GetDao().FissionUserJattr attrVals := attr.GetValues() for _, val := range vals { if !verify.InArray(val, attrVals...) { attrVals = append(attrVals, val) } } values, _ := jsonm.ToSliceString(attrVals) _, err = attrM.WithContext(s.Ctx).Where(attrM.ID.Eq(attr.ID)).UpdateSimple( attrM.Value.Value(vals[0]), attrM.Values.Value(values), ) if err != nil { err = s.BE().MysqlError(err) return } // 清除缓存 _ = cache.NewFissionUserJattr(s.Ctx).Clear(attr) return }