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

来源:互联网 发布:淘宝导航css代码 透明 编辑:程序博客网 时间:2024/05/22 03:31

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

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

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

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 0
原创粉丝点击