通用自动分配小类库AutoAllot,专注于分配逻辑

因为项目中遇到了审核分配的需求,而且可以预见的是后面这种类似的分配需求还会出现,所以就有了这样一个小类库的产生

首先先说明下需求原因:现在有审核人员,有待审核的订单,因为审核人员的奖金跟审核过的订单金额同比挂钩(即审的订单总额越多,奖金越高,也可能跟绩效挂钩),造成审核人员只抢大单,小单子长时间无人审,对外影响恶劣,为了解决这个问题,以及为后续的类似问题提供一个通用的解决方案,故而设计了这么一个小类库

首先对应各部分存在三个接口:

1、待分配的用户集合,该接口定义了用户相关的各种行为,用户是否能进行分配取决于用户是否登入

    /// <summary>
    /// 自动分配用户
    /// </summary>
    public interface IAllotUsers<T>
    {
        /// <summary>
        /// 登入
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        bool LoginIn(T user);
        /// <summary>
        /// 登出
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        bool LoginOut(T user);
        /// <summary>
        /// 验证是否已登入
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        bool IsLogin(T user);
        /// <summary>
        /// 获取所有已登入用户
        /// </summary>
        /// <returns></returns>
        IEnumerable<T> GetAllLoginUsers();
    }
2、待分配项的集合,该接口定义了分配相关的各种行为,此部分重点需要注意的是Add方法和Get方法:Add方法存在一个无参但返回int的委托,该委托指示分配的先后顺序,Get方法则是对遍历时的分配项进行是否可分配判定,返回true标志可以分配
    /// <summary>
    /// 自动分配项
    /// </summary>
    public interface IAllotItems<T>
    {
        /// <summary>
        /// 添加可分配项
        /// </summary>
        /// <param name="item">可分配项</param>
        /// <param name="fun">添加规则(影响Get方法的顺序),如果传入null,则默认为第一分配队列,如果超出允许的队列范围,则修正为最后一个队列</param>
        void Add(T item, Func<int> fun);
        /// <summary>
        /// 获取可分配项
        /// </summary>
        /// <param name="fun">分配规则</param>
        /// <param name="item">分配到的分配项</param>
        /// <returns>是否分配成功</returns>
        bool Get(Func<T, bool> fun, ref T item);
        /// <summary>
        /// 取消获取
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        bool GetBack(T item);
        /// <summary>
        /// 设定指定分配项已经处于分配状态
        /// </summary>
        /// <param name="item"></param>
        void Set(T item);
        /// <summary>
        /// 移除可分配项
        /// </summary>
        /// <param name="item">可分配项</param>
        /// <returns>返回移除是否成功</returns>
        bool Remove(T item);
        /// <summary>
        /// 获取所有可分配项
        /// </summary>
        /// <returns></returns>
        IEnumerable<T> GetAllAllotItems();
    }
3、分配关系,该类库保持了分配用户与分配项之间的分配关系,需要指明的是该类库继承了IAllotUsers<Tu>, IAllotItems<Ti>
    /// <summary>
    /// 自动分配
    /// </summary>
    public interface IAutoAllot<Tu, Ti> : IAllotUsers<Tu>, IAllotItems<Ti>
    {
        /// <summary>
        /// 是否已经存在分配记录
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        bool IsAllot(Tu user);
        /// <summary>
        /// 获取可分配项,如存在分配记录,则直接返回记录对应的分配项目
        /// </summary>
        /// <typeparam name="T1">待分配者类型</typeparam>
        /// <param name="user">待分配者</param>
        /// <param name="fun">分配规则</param>
        /// <param name="item">分配到的分配项</param>
        /// <returns>是否分配成功</returns>
        bool GetAllotItem(Tu user, Func<Ti, bool> fun, ref Ti item);
        /// <summary>
        /// 撤销分配
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        bool GetBackAllotItem(Tu user);
        /// <summary>
        /// 分配结束,移除分配关系以及可分配项
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        bool Finished(Tu user);
    }

然后针对接口定义,实现了相应的默认的分配类

1、GeneralAllotUsers,该类库内部包含一个HashSet作为集合载体,保证每个用户在分配队列中的唯一性

    /// <summary>
    /// 通用分配用户
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GeneralAllotUsers<T> : IAllotUsers<T>
    {
        private HashSet<T> _users = new HashSet<T>();
        #region IAllotUsers<T> 成员

        public bool LoginIn(T user)
        {
            bool ret = true;
            if (!this._users.Contains(user))
            {
                ret = this._users.Add(user);
            }
            return ret;
        }

        public bool LoginOut(T user)
        {
            bool ret = true;
            if (this._users.Contains(user))
            {
                ret = this._users.Remove(user);
            }
            return ret;
        }

        public bool IsLogin(T user)
        {
            return this._users.Contains(user);
        }

        public IEnumerable<T> GetAllLoginUsers()
        {
            return this._users.ToArray();
        }

        #endregion
    }

2、GeneralAllotItems,该类库内部包含一个Dictionary来作为待分配项的载体,Key对应待分配项,Value对应该分配项是否已经被分配(分配项不允许被重复分配),然后通过List<List<T>>来作为分配顺序的一个载体,另外通过一个private的object对象来保证该类在多线程时的分配唯一性

    /// <summary>
    /// 通用分配项
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GeneralAllotItems<T> : IAllotItems<T>
    {
        private Dictionary<T, bool> _dic = new Dictionary<T, bool>();
        private List<List<T>> _list = new List<List<T>>();
        private object _obj = new object();

        public GeneralAllotItems()
            : this(1)
        {
        }
        /// <summary>
        /// 分配初始化
        /// </summary>
        /// <param name="levels">存在多少个分配队列</param>
        public GeneralAllotItems(int levels)
        {
            if (levels <= 0)
            {
                throw new ArgumentException();
            }
            for (int i = 0; i < levels; i++)
            {
                this._list.Add(new List<T>());
            }
        }

        #region IAllotItems<T> 成员

        public void Add(T item, Func<int> fun)
        {
            if (!this._dic.ContainsKey(item))
            {
                int lv = 0;
                if (fun != null)
                {
                    lv = fun();
                }
                //修正lv的值范围
                if (lv < 0)
                {
                    lv = 0;
                }
                else if (lv >= this._list.Count)
                {
                    lv = this._list.Count - 1;
                }
                this._list[lv].Add(item);
                this._dic.Add(item, false);
            }
        }

        public bool Get(Func<T, bool> fun, ref T item)
        {
            foreach (var l in this._list)
            {
                foreach (T k in l)
                {
                    if (!this._dic[k])//如果该分配项尚未分配过
                    {
                        lock (this._obj)
                        {
                            if (!this._dic[k])
                            {
                                if (fun == null || fun(k))//不存在分配规则或者符合分配规则
                                {
                                    item = k;
                                    this._dic[k] = true;
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
            return false;
        }

        public bool GetBack(T item)
        {
            bool ret = false;
            if (this._dic.ContainsKey(item))
            {
                ret = true;
                this._dic[item] = false;
            }
            return ret;
        }

        public void Set(T item)
        {
            if (this._dic.ContainsKey(item))
            {
                this._dic[item] = true;
            }
        }

        public bool Remove(T item)
        {
            for (int i = 0; i < this._list.Count; i++)
            {
                if (this._list[i].Contains(item))
                {
                    this._list[i].Remove(item);
                    break;
                }
            }
            return this._dic.Remove(item);
        }

        public IEnumerable<T> GetAllAllotItems()
        {
            return (from l in this._list
                    let p = l
                    from t in p
                    select t).ToArray();
        }

        #endregion
    }
3、GeneralAutoAllot,该类库存在三个构造方法,传入的Dictionary是为了保证一旦程序异常,可以恢复分配关系,至于传入的IAllotUsers<Tu> users, IAllotItems<Ti> items则是允许传入自定义的分配方式
	/// <summary>
    /// 自动分配通用类
    /// </summary>
    /// <typeparam name="Tu"></typeparam>
    /// <typeparam name="Ti"></typeparam>
    public class GeneralAutoAllot<Tu, Ti> : IAutoAllot<Tu, Ti>
    {
        private IAllotItems<Ti> _items;
        private IAllotUsers<Tu> _users;
        private Dictionary<Tu, Ti> _dic;

        public GeneralAutoAllot()
            : this(null)
        {
        }
        public GeneralAutoAllot(Dictionary<Tu, Ti> dic)
            : this(dic, null, null)
        {
        }
        public GeneralAutoAllot(Dictionary<Tu, Ti> dic, IAllotUsers<Tu> users, IAllotItems<Ti> items)
        {
            this._dic = dic;
            if (this._dic == null)
            {
                this._dic = new Dictionary<Tu, Ti>();
            }
            this._users = users;
            if (this._users == null)
            {
                this._users = new GeneralAllotUsers<Tu>();
            }
            this._items = items;
            if (this._items == null)
            {
                this._items = new GeneralAllotItems<Ti>();
            }
        }
        #region IAutoAllot<Tu,Ti> 成员

        public bool IsAllot(Tu user)
        {
            return this._dic.ContainsKey(user);
        }
        /// <summary>
        /// FIFO,先入先出
        /// </summary>
        /// <param name="user"></param>
        /// <param name="fun"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public bool GetAllotItem(Tu user, Func<Ti, bool> fun, ref Ti item)
        {
            bool ret = false;
            if (this._dic.ContainsKey(user))
            {//已经有分配记录,则直接获取分配记录中对应的分配项
                ret = true;
                item = this._dic[user];
            }
            else
            {
                //是否已经登入
                if (this._users.IsLogin(user))
                {
                    //从items中获取可分配项
                    ret = this._items.Get(fun, ref item);
                    if (ret)
                    {//成功分配后记录分配关系
                        this._dic.Add(user, item);
                    }
                }
            }
            return ret;
        }

        public bool GetBackAllotItem(Tu user)
        {
            bool ret = false;
            if (this._dic.ContainsKey(user))
            {
                ret = this._items.GetBack(this._dic[user]);
                this._dic.Remove(user);
            }
            return ret;
        }

        public bool Finished(Tu user)
        {
            bool ret = false;
            if (this._dic.ContainsKey(user))
            {
                ret = this._items.Remove(this._dic[user]);
                this._dic.Remove(user);
            }
            return ret;
        }

        public IEnumerable<Tu> GetAllLoginUsers()
        {
            return this._users.GetAllLoginUsers();
        }

        #endregion

        #region IAllotUsers<Tu> 成员

        public bool LoginIn(Tu user)
        {
            return _users.LoginIn(user);
        }

        public bool LoginOut(Tu user)
        {
            return _users.LoginOut(user);
        }

        public bool IsLogin(Tu user)
        {
            return _users.IsLogin(user);
        }

        #endregion

        #region IAllotItems<Ti> 成员

        public void Add(Ti item, Func<int> fun)
        {
            _items.Add(item, fun);
        }

        public bool Get(Func<Ti, bool> fun, ref Ti item)
        {
            return _items.Get(fun, ref item);
        }

        public bool GetBack(Ti item)
        {
            return _items.GetBack(item);
        }

        public void Set(Ti item)
        {
            _items.Set(item);
        }

        public bool Remove(Ti item)
        {
            return _items.Remove(item);
        }

        public IEnumerable<Ti> GetAllAllotItems()
        {
            return this._items.GetAllAllotItems();
        }

        #endregion
    }
至于用法就是简单的实现一个分配的代理类,该代理类内部静态化一个IAutoAllot对象(其实就是在这个代理类内部只能有一个实例化后的IAutoAllot对象)
    public class AutoAllotProxy
    {
        /// <summary>
        /// 自动分配类
        /// </summary>
        private static IAutoAllot<string, int> AutoAllot;
        static AutoAllotProxy()
        {
            InitData(null);
        }
        /// <summary>
        /// 政策待分配数据初始化
        /// </summary>
        private static void InitData(IEnumerable<string> loginUsers)
        {
            //数据恢复
            List<KeyValuePair<int, string>> list;//list=XXXXXX此部分读取数据库
            //从读取出来的数据中恢复分配关系
            Dictionary<string, int> tmpDic = new Dictionary<string, int>();
            HashSet<int> alloting = new HashSet<int>();//正在审核
            List<int> allot = new List<int>();//待审核政策,需要按时间排序

            if (list != null && list.Count > 0)
            {
                //为了防止出现单个用户存在多个正在审核记录,采用foreach遍历
                foreach (var k in list)
                {
                    if (!string.IsNullOrEmpty(k.Value))
                    {
                        //如果单个用户有多条审核记录,则只接受第一条审核关系,其余审核关系被排除
                        if (!tmpDic.ContainsKey(k.Value))
                        {
                            tmpDic.Add(k.Value, k.Key);
                            alloting.Add(k.Key);
                            allot.Add(k.Key);
                        }
                    }
                    else
                    {
                        allot.Add(k.Key);
                    }
                }
            }
            //恢复用户和政策的分配关系
            AutoAllot = new GeneralAutoAllot<string, int>(tmpDic);
            foreach (var k in allot)
            {//恢复政策列表
                AutoAllot.Add(k, null);
            }
            foreach (var k in alloting)
            {//恢复政策分配状态
                AutoAllot.Set(k);
            }
            var users = loginUsers;
            if (users == null || !users.Any())
            {
                users = tmpDic.Keys;
            }
            foreach (var k in users)
            {//恢复用户登入状态
                AutoAllot.LoginIn(k);
            }
        }
        /// <summary>
        /// 用户登入
        /// </summary>
        /// <param name="userName"></param>
        public void LoginIn(string userName)
        {
            if (!string.IsNullOrEmpty(userName))
            {
                AutoAllot.LoginIn(userName);
            }
        }
        /// <summary>
        /// 用户退出
        /// </summary>
        /// <param name="userName"></param>
        /// <returns>0:失败,1:成功,2:退出但存在分配记录</returns>
        public int LoginOut(string userName)
        {
            int ret = 0;
            if (!string.IsNullOrEmpty(userName))
            {
                ret = AutoAllot.LoginOut(userName) ? 1 : 0;
                if (ret == 1 && AutoAllot.IsAllot(userName))
                {
                    ret = 2;
                }
            }
            return ret;
        }
        /// <summary>
        /// 用户是否已经登入
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool IsLogin(string userName)
        {
            if (!string.IsNullOrEmpty(userName))
            {
                return AutoAllot.IsLogin(userName);
            }
            return true;
        }
        /// <summary>
        /// 添加待审核项
        /// </summary>
        /// <param name="pid"></param>
        public void Add(int pid)
        {
            if (pid > 0)
            {
                AutoAllot.Add(pid, null);//因为分配顺序没有特殊要求,只是简单的先入先出,所以直接传入null
            }
        }

        /// <summary>
        /// 获取分配的待审核项,该方法只负责分配,将分配关系持久化到数据库的事情由外部来实现
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public int Get(string userName)
        {
            int pid = 0;
            if (!string.IsNullOrEmpty(userName))
            {
                AutoAllot.GetAllotItem(userName,
                        null,
                        ref pid);
            }
            return pid;
        }
        /// <summary>
        /// 政策审核结束
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool Finished(string userName)
        {
            if (!string.IsNullOrEmpty(userName))
            {
                return AutoAllot.Finished(userName);
            }
            return false;
        }
        /// <summary>
        /// 获取当前内存中存在的分配项
        /// </summary>
        /// <returns></returns>
        public int[] GetAllAllotPolicies()
        {
            return AutoAllot.GetAllAllotItems().ToArray();
        }
        /// <summary>
        /// 获取所有登入的用户
        /// </summary>
        /// <returns></returns>
        public string[] GetAllLoginUsers()
        {
            return AutoAllot.GetAllLoginUsers().ToArray();
        }
        /// <summary>
        /// 分配重新开启
        /// </summary>
        public void ResetAllot()
        {
            InitData(AutoAllot.GetAllLoginUsers());//将当前已登录的用户传入进行重新初始化分配关系
        }
    }
需要特别说明的是,该类库设计之初就没考虑过跟数据库进行交互,所有的数据都存在于内存中,所以受服务重启影响较大,因此在设计时也就提供了相关的初始化入口,以保证数据恢复功能


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值