/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package engine import ( "math/rand" "sort" "strings" "time" "github.com/cgrates/cgrates/utils" ) const ( MINE_PREFIX = "*mine_" STRATEGY_MINE_LOWEST = "*mine_lowest" STRATEGY_MINE_HIGHEST = "*mine_highest" STRATEGY_MINE_RANDOM = "*mine_random" STRATEGY_LOWEST = "*lowest" STRATEGY_HIGHEST = "*highest" STRATEGY_RANDOM = "*random" ) type SharedGroup struct { Id string AccountParameters map[string]*SharingParameters MemberIds utils.StringMap //members []*Account // accounts caching } // SharedGroupWithOpts is used in replicatorV1 for dispatcher type SharedGroupWithAPIOpts struct { *SharedGroup Tenant string APIOpts map[string]any } type SharingParameters struct { Strategy string RatingSubject string } // Clone clones *SharedGroup func (sg *SharedGroup) Clone() (sharedGroup *SharedGroup) { if sg == nil { return } sharedGroup = &SharedGroup{ Id: sg.Id, } if sg.AccountParameters != nil { sharedGroup.AccountParameters = make(map[string]*SharingParameters, len(sg.AccountParameters)) } if sg.MemberIds != nil { sharedGroup.MemberIds = sg.MemberIds.Clone() } for id := range sg.AccountParameters { sharedGroup.AccountParameters[id] = sg.AccountParameters[id].Clone() } return } // Clone clones *SharingParameters func (sp *SharingParameters) Clone() *SharingParameters { if sp == nil { return nil } return &SharingParameters{ Strategy: sp.Strategy, RatingSubject: sp.RatingSubject, } } func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc Balances) Balances { sharingParameters := sg.AccountParameters[utils.MetaAny] if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.ID]; hasParamsForAccount { sharingParameters = sp } strategy := STRATEGY_MINE_RANDOM if sharingParameters != nil && sharingParameters.Strategy != "" { strategy = sharingParameters.Strategy } switch strategy { case STRATEGY_LOWEST, STRATEGY_MINE_LOWEST: sort.Sort(LowestBalancesSorter(bc)) case STRATEGY_HIGHEST, STRATEGY_MINE_HIGHEST: sort.Sort(HighestBalancesSorter(bc)) case STRATEGY_RANDOM, STRATEGY_MINE_RANDOM: rbc := RandomBalancesSorter(bc) (&rbc).Sort() bc = Balances(rbc) default: // use mine random for anything else strategy = STRATEGY_MINE_RANDOM rbc := RandomBalancesSorter(bc) (&rbc).Sort() bc = Balances(rbc) } if strings.HasPrefix(strategy, MINE_PREFIX) { // find index of my balance index := 0 for i, b := range bc { if b.Uuid == myBalance.Uuid { index = i break } } // move my balance first bc[0], bc[index] = bc[index], bc[0] } return bc } // Returns all shared group's balances collected from user accounts' func (sg *SharedGroup) GetBalances(destination, category, balanceType string, ub *Account, aTime time.Time) (bc Balances) { // if len(sg.members) == 0 { for ubId := range sg.MemberIds { var nUb *Account if ubId == ub.ID { // skip the initiating user nUb = ub } else { nUb, _ = dm.GetAccount(ubId) if nUb == nil || nUb.Disabled { continue } } //sg.members = append(sg.members, nUb) sb := nUb.getBalancesForPrefix(destination, category, balanceType, sg.Id, aTime) bc = append(bc, sb...) } /* } else { for _, m := range sg.members { sb := m.getBalancesForPrefix(destination, m.BalanceMap[balanceType], sg.Id) bc = append(bc, sb...) } }*/ return } type LowestBalancesSorter []*Balance func (lbcs LowestBalancesSorter) Len() int { return len(lbcs) } func (lbcs LowestBalancesSorter) Swap(i, j int) { lbcs[i], lbcs[j] = lbcs[j], lbcs[i] } func (lbcs LowestBalancesSorter) Less(i, j int) bool { return lbcs[i].GetValue() < lbcs[j].GetValue() } type HighestBalancesSorter []*Balance func (hbcs HighestBalancesSorter) Len() int { return len(hbcs) } func (hbcs HighestBalancesSorter) Swap(i, j int) { hbcs[i], hbcs[j] = hbcs[j], hbcs[i] } func (hbcs HighestBalancesSorter) Less(i, j int) bool { return hbcs[i].GetValue() > hbcs[j].GetValue() } type RandomBalancesSorter []*Balance func (rbcs *RandomBalancesSorter) Sort() { src := *rbcs // randomize balance chain dest := make([]*Balance, len(src)) perm := rand.Perm(len(src)) for i, v := range perm { dest[v] = src[i] } *rbcs = dest }