我们在开发过程中,一般都会遇到这样的需求吧。
审批通过的时候要执行多个动作:
1.更改A表的状态为“已审批”,我们记为操作A
2.插入B表一条数据 ,我们记为操作B
3.更改C表的多条数据的状态,我们记为操作C
N多
.......
但是当N多天后,在业务发生需要逆向这个审批的时候,我们需要回滚这些操作。
我们需要一个不漏的逆向刚刚审批做的操作。
这个时候如果我们这样写:
审批方法() { 1.动作A 2.动作B 3.动作C ...... } 逆向方法() { 1.逆向A 2.逆向B 3.逆向C ...... }
这样的话,如果后续业务再增加一个动作的时候,需要改动两处地方,万一漏掉一处(往往都是逆向处) ,并且测试都没测出来,那么问题就可大可小了。
我们从程序设计上就可以避免这个问题的产生。
我这边是生成付款清单*(情形类似)为例:
1.先定义一个接口,包含两个方法:生成与删除
public interface IPayToDealerOperate { /// <summary> /// 生成付款明细接口 /// </summary> /// <param name="db"></param> /// <param name="dbQuery"></param> /// <param name="listWarranty"></param> /// <param name="currentId"></param> /// <returns></returns> List<ClmPayToDealer> GeneratePayDetail(CEPEntities db, CepOneViewEntities dbQuery, List<ClmWarranty> listWarranty, long currentId); /// <summary> /// 删除付款明细接口 /// </summary> /// <param name="DbContext"></param> /// <param name="dbQuery"></param> /// <param name="payMasterId"></param> /// <param name="currentId"></param> /// <param name="payDetailId"></param> /// <returns></returns> string DeletePayDetail(CEPEntities DbContext, List<ClmPayToDealer> payItemAllList, ClmPayToDealer pDetail, long currentId); }
2.为每个子动作分别创建类,并分别实现上面接口
public class PayNormalDetailImpl : IPayToDealerOperate { /// <summary> /// 生成付款明细接口 /// </summary> /// <param name="db"></param> /// <param name="dbQuery"></param> /// <param name="listWarranty"></param> /// <param name="currentId"></param> /// <returns></returns> public List<ClmPayToDealer> GeneratePayDetail(CEPEntities db, CepOneViewEntities dbQuery, List<ClmWarranty> listWarranty, long currentId) { //内含正常保修单生成付款项操作,此动作非重点已省略 return listPayToDealer; } /// <summary> /// 删除付款明细接口 /// </summary> /// <param name="DbContext"></param> /// <param name="dbQuery"></param> /// <param name="payMasterId"></param> /// <param name="currentId"></param> /// <param name="payDetailId"></param> /// <returns></returns> public string DeletePayDetail(CEPEntities DbContext, List<ClmPayToDealer> payItemAllList, ClmPayToDealer pDetail, long currentId) { //内含收货入库删除操作,此动作非重点已省略 return string.Empty; } }
public class PayPartReceiveInstockDetailImpl : IPayToDealerOperate { /// <summary> /// 生成付款明细接口 /// </summary> /// <param name="db"></param> /// <param name="dbQuery"></param> /// <param name="listWarranty"></param> /// <param name="currentId"></param> /// <returns></returns> public List<ClmPayToDealer> GeneratePayDetail(CEPEntities db, CepOneViewEntities dbQuery, List<ClmWarranty> listWarranty, long currentId) { //内含审计扣款生成付款项操作,此动作非重点已省略 return listPayToDealer; } /// <summary> /// 删除付款明细接口 /// </summary> /// <param name="DbContext"></param> /// <param name="dbQuery"></param> /// <param name="payMasterId"></param> /// <param name="currentId"></param> /// <param name="payDetailId"></param> /// <returns></returns> public string DeletePayDetail(CEPEntities DbContext, List<ClmPayToDealer> payItemAllList, ClmPayToDealer pDetail, long currentId) { //内含收货入库删除操作,此动作非重点已省略 return string.Empty; } }
public class PayAuditDebitImpl : IPayToDealerOperate { /// <summary> /// 生成付款明细接口 /// </summary> /// <param name="db"></param> /// <param name="dbQuery"></param> /// <param name="listWarranty"></param> /// <param name="currentId"></param> /// <returns></returns> public List<ClmPayToDealer> GeneratePayDetail(CEPEntities db, CepOneViewEntities dbQuery, List<ClmWarranty> listWarranty, long currentId) { //内含审计扣款生成付款项操作,此动作非重点已省略 return listPayToDealer; } /// <summary> /// 删除付款明细接口 /// </summary> /// <param name="DbContext"></param> /// <param name="dbQuery"></param> /// <param name="payMasterId"></param> /// <param name="currentId"></param> /// <param name="payDetailId"></param> /// <returns></returns> public string DeletePayDetail(CEPEntities DbContext, List<ClmPayToDealer> payItemAllList, ClmPayToDealer pDetail, long currentId) { //内含审计删除操作,此动作非重点已省略 return string.Empty; } }
3.在调用处定义接口数组:
/// <summary> /// 付款清单新增数据与删除数据,统一管理入口 /// </summary> private IPayToDealerOperate[] _payDetailOperateArray = new IPayToDealerOperate[] { new PayNormalDetailImpl(),//正常付款清单 new PayPartReceiveInstockDetailImpl(),//零件收货入库扣款 new PayAuditDebitImpl()//保修审计扣款 };
4.在调用处循环数组执行相应动作。
4.1 在生成付款单处执行生成动作。
foreach (var action in _payDetailOperateArray) { listPayTotalItem.AddRange(action.GeneratePayDetail(db, dbQuery, item.ToList(), currentId)); }
4.2 在删除付款单时执行删除动作。
//循环处理_payDetailOperateArray中包含的还原扣款信息 foreach (var action in _payDetailOperateArray) { action.DeletePayDetail(DbContext, payItemAllList, pdForDel, currentId); }
至此,大功告成。如若后续业务需要增加扣款项,那么我只需新建一个类,这个类实现了最初定义的接口,那么我就必须去实现生成和退回操作,从而避免了漏操作的尴尬,同时也遵循着开闭原则。
补充,如果我增加了一个业务,只需要在生成付款项的时候进行动作,删除的时候不要有动作,那么只要实现这个删除动作,直接返回即可。