通用自动分配小类库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
- 通用自动分配小类库AutoAllot,专注于分配逻辑
- 通用资源分配算法
- 通用职责分配软件模式
- div自动分配高度
- win7 IP 自动分配
- 自动分配会议桌demo
- 自动分配座位
- centos 自动分配ip
- 用dTrace捕捉内存分配于释放
- 有关于字符串内存分配的问题
- 自动分配UDP本地端口
- DHCP多VLAN自动分配
- ipv6 地址自动分配解析
- 学生分配管理系统逻辑思想
- 订单生产线分配以及拆分逻辑实现
- GRASP 通用职责软件分配模式
- GRASP 通用职责分配软件模式
- GRASP通用职责软件分配模式
- SAP部分快捷键
- mysqldump
- RAC DBCA 找不到共享磁盘
- Apple Swift编程语言入门教程
- 一晃经年
- 通用自动分配小类库AutoAllot,专注于分配逻辑
- 命令行下Android AVD命令解析
- 使用markdown 语法编辑文字
- oj的处理成绩
- Fedora 在线升级
- Java基础篇----toString()方法
- 不懂技术的人不要对懂技术的人说这很容易实现
- 问题笔记
- 这是测试二这是测试二这是测试二这是测试二这是测试二这是测试二这是测试二这是测试二这是测试二