• 适合WebApi的简单的C#状态机实现


    目标

    采用了Restful WebApi的架构,再把业务逻辑状态转移放到后端就有点违背初衷了。实际上只要后端Api的权限设置的好,把状态转移放到前端也未尝不可。我考虑的结果是,一般如果变更这个状态本身就需要特定权限才可操作,比如xxx审批者,在前端处理状态逻辑问题不大,因为本身这个人就是有权限的,如果伪造请求破坏了数据也可以通过Log定位到个人,但是如果是面向大众的情况,比如团购下个订单什么的不对数据有特定权限要求的就需要在后端单独做动作类的WebApi了,欢迎讨论。

    相关传送门:Restful WebApi开发实践

    先来看下最后的请求效果:(插件服务总线用的是服务定位器设计模式)

    var result = BundleServiceBus.Current.GetInstance<IEventHandleService>()
     .EventTrigger<ExamEvent>(new ExamEvent
     {
         RootDataId = Id,
         Type = ET,
     });
    if (result.IsSuccess)
    {
        Dispatcher.Invoke(() => MessageBox.Show("提交成功"));
        UpdateStudentInfo(ThisStudentInfo.Id);
        return;
    }
    

    实现

    这里我们将用到 C#对WebApi数据操作 里的方案来解决数据操作环节。

    首先定义状态基类:

    abstract class StatusBase<RootDataType>
    {
        public ReturnHTTPData ReturnHttpData { get; set; }
        public abstract string Name { get; }
        public virtual bool Entry(BaseEvent arg) { return true; }
        public virtual bool Exit() { return true; }
        public abstract List<BaseTransferModel> TransferList { get; }
    
        public IModelDataService MDS = BundleServiceBus.Current.GetInstance<IModelDataService>();
    
        public RootDataType RootData { get; set; }
    }
    

    这里需要根据具体数据类型自定义一个业务状态基类,比如:

    abstract class XXXStatusBase : StatusBase<StudentInfoModel>
    {
        public LoginUserInfoModel CurrentLoginUser
        {
            get
            {
                return MDS.GetDataByModel<LoginUserInfoModel, string>(DataParameter.Empty).Data.SingleOrDefault();
            }
        }
    
        public bool ChangeRootDataStatus(string TargetStatus)
        {
            RootData.Status = TargetStatus;
            return ChangeRootDataInfo(RootData);
        }
    
        public bool ChangeRootDataInfo(StudentInfoModel model)
        {
            var result = MDS.PutDataByModel(new DataParameter<Guid>
            {
                TenantId = CurrentLoginUser.GetDataParameter().TenantId,
                AggregationId = CurrentLoginUser.GetDataParameter().AggregationId,
                SiteId = CurrentLoginUser.GetDataParameter().SiteId,
                DataId = model.Id,
                CMD = CurrentLoginUser.GetDataParameter().CMD
            }, model);
            //ReturnHttpData = new ReturnHTTPData { Content = result.Message };
            return result.IsSuccess;
        }
    
        public bool AddBusinessLog(BusinessLogModel model)
        {
            //TODO 提交队列
            var result = MDS.PostDataByModel<BusinessLogModel, Guid>(CurrentLoginUser.GetDataParameter(), model);
            //ReturnHttpData = new ReturnHTTPData { Content = result.Message };
            return result.IsSuccess;
        }
    }
    

    接下来定义一下状态转移模型基类:

    abstract class BaseTransferModel
    {
        public string Target { get; set; }
        public abstract bool Condition(BaseEvent arg);
        public abstract bool Action(string target, BaseEvent arg);
    }
    

    实现一个基于事件的状态转移模型:

    class TransferModel<TEvent> : BaseTransferModel
        where TEvent : BaseEvent
    {
        public override bool Condition(BaseEvent arg)
        {
            var carg = arg as TEvent;
            return carg != null && ConditionFunc(carg);
        }
    
        public override bool Action(string target, BaseEvent arg)
        {
            var carg = arg as TEvent;
            return carg != null && ActionFunc(target, carg);
        }
    
        public Func<TEvent, bool> ConditionFunc { private get; set; }
        public Func<string, TEvent, bool> ActionFunc { private get; set; }
    }
    

    剩下的就是状态机主体了,通过反射添加状态,并检查状态转移关系:

    class StatusManager
    {
        public List<string> LostStatus { get; set; }
        private Dictionary<string, XXXStatusBase> StatusData = new Dictionary<string, XXXStatusBase>();
        private IModelDataService MDS = BundleServiceBus.Current.GetInstance<IModelDataService>();
    
        LoginUserInfoModel CurrentLoginUser
        {
            get
            {
                return MDS.GetDataByModel<LoginUserInfoModel, string>(DataParameter.Empty).Data.SingleOrDefault();
            }
        }
    
        public StatusManager()
        {
            LostStatus = new List<string>();
    
            var data = Assembly.GetExecutingAssembly().DefinedTypes.Where(s => s.BaseType == typeof(XXXStatusBase));
            foreach (var typeInfo in data)
            {
                var status = Activator.CreateInstance(typeInfo) as XXXStatusBase;
                try
                {
                    StatusData.Add(status.Name, status);
                }
                catch (Exception)
                {
                    LostStatus.Add("AddError " + status.Name);
                }
            }
    
            //自检
            foreach (var st in StatusData.Values)
            {
                foreach (var tr in st.TransferList)
                {
                    if (StatusData.All(s => s.Key != tr.Target))
                    {
                        LostStatus.Add(st.Name + " [To] " + tr.Target);
                    }
                }
            }
            if (LostStatus.Count > 0)
            {
                Debug.WriteLine("Status has Errors!");
            }
        }
    
        public List<Type> GetCurrentCanUseEvent(string Status)
        {
            var data = new List<Type>();
            if (StatusData.Keys.All(s => s != Status)) return data;
    
            foreach (var transfer in StatusData[Status].TransferList)
            {
                data.AddRange(transfer.GetType().GenericTypeArguments);
            }
            return data;
        }
    
        public EventResult Trigger(BaseEvent arg)
        {
            //准备状态转移主体
            var RootData = new StudentInfoModel { Status = "" };
    
            if (arg.RootDataId != Guid.Empty)
            {
                var data = MDS.GetDataByModel<StudentInfoModel, Guid>(new DataParameter<Guid>
                {
                    TenantId = CurrentLoginUser.GetDataParameter().TenantId,
                    SiteId = CurrentLoginUser.GetDataParameter().SiteId,
                    DataId = arg.RootDataId,
                });
                if (!data.IsSuccess)
                {
                    return new EventResult
                    {
                        IsSuccess = false,
                        Result = data.Message
                    };
                }
                RootData = data.Data.SingleOrDefault();
            }
    
            //状态转移引擎
            if (StatusData.Keys.All(s => s != RootData.Status)) return new EventResult
            {
                IsSuccess = false,
                Result = "状态[" + RootData.Status + "]不存在"
            };
            var status = StatusData[RootData.Status];
            if (status == null)
            {
                return new EventResult
                {
                    IsSuccess = false,
                    Result = "NotFoundStatus"
                };
            }
            status.RootData = RootData;
    
            foreach (var transfer in status.TransferList)
            {
                if (!transfer.Condition(arg)) continue;
                status.Exit();
                if (!transfer.Action(transfer.Target, arg))
                {
    
                    if (status.ReturnHttpData == null)
                    {
                        status.ReturnHttpData = new ReturnHTTPData();
                    }
                    return new EventResult
                    {
                        IsSuccess = false,
                        Result = status.ReturnHttpData.Content,
                    };
                }
                if (StatusData.Keys.All(s => s != RootData.Status))
                {
                    if (status.ReturnHttpData == null)
                    {
                        status.ReturnHttpData = new ReturnHTTPData();
                    }
                    return new EventResult
                    {
                        IsSuccess = true,
                        Result = status.ReturnHttpData.Content,
                    };
                }
                if (StatusData[transfer.Target].Entry(arg))
                {
                    if (status.ReturnHttpData == null)
                    {
                        status.ReturnHttpData = new ReturnHTTPData();
                    }
                    return new EventResult
                    {
                        IsSuccess = true,
                        Result = status.ReturnHttpData.Content,
                    };
                }
                if (status.ReturnHttpData == null)
                {
                    status.ReturnHttpData = new ReturnHTTPData();
                }
                return new EventResult
                {
                    IsSuccess = false,
                    Result = status.ReturnHttpData.Content,
                };
            }
            return new EventResult
            {
                IsSuccess = false,
                Result = "[" + arg.GetType() + "]操作不被允许"
            };
        }
    }
    

    具体添加一个状态:

    class Status_XXX : XXXStatusBase
    {
        public override string Name
        {
            get { return "状态一"; }
        }
    
        public override List<BaseTransferModel> TransferList
        {
            get
            {
                var data = new List<BaseTransferModel>();
                data.Add(
                    new TransferModel<TargetEvent>
                    {
                        Target = "状态二",
                        ConditionFunc = arg => arg.Type == CommitType.Final,
                        ActionFunc = (target, model) =>
                        {
                            if (model != null)
                            {
                                //变更状态
                                if (!ChangeRootDataStatus(target)) return false;
    
                                //日志
                                return true;
                            }
                            return false;
                        }
                    });
    
                return data;
            }
        }
    }
    
  • 相关阅读:
    background和background-size
    获取表单的初始值,模拟placeholder属性
    input[type=checkbox]
    background-size
    input的type属性的修改
    选项卡切换
    2016.12.13
    3. 如何封装查询条件与查询结果到map中
    Java 实现网站当前在线用户统计
    sell-- wordPOI
  • 原文地址:https://www.cnblogs.com/zhang740/p/3865636.html
Copyright © 2020-2023  润新知