这是我在 www.cnblogs.com 发的第一片文章,如果有任何不足之处请各位指正。。。“漫骂”
本片文章面向对像是对,WF稍微有一点了解的朋友


Java报销单审批 报销单据的审核_Java报销单审批

业务描述

就是一个简单的报销单流转审批的业务

业务讲解

角色/功能

  1. 报销者

      申请、填写、修改、报销单
  2. 财务

      默认1000元以下金额由财务进行审核, 功能:打回(让报销者重新填写)、中止(工作流)、同意(流转到出纳)、加签(遇到特殊情况可以让老板加签就是让老板在审核一下)
  3. 老板

      默认 1000 元以上有老板审核,功能和财务基本一样(没加签)
  4. 出纳

      如果一个报销单,财务或老板审批完成有财务给钱,功能:没钱(等待,自己流转到自己),有钱(流转到取钱确认)

说明 除了“报销者” 都可以填写审核意见

工作流设计

明显是一个很标准的状态机的工作流

ExternalDataExchangeService 接口设计 (MyWorkflows.IBILLService.cs,MyWorkflows.IBILLService)

  由需求中可知分析出 7 种事件


具体分析

  1. 小金额

      由财务和老板的功能分析得出 (财务 默认1000元以下金额由财务进行审核,老板 默认 1000 元以上有老板审核)
  2. 大金额

      同上
  3. 打回重新修改

      财务老板都有 "打回"
  4. 向下流转

      这个不用说了,状态机工作流必然有的向下流转的
  5. 结束工作流

      财务和老板都有 "中止"
  6. 等待

      出纳分析得出(没钱的时候)
  7. 加签

      由财务业务分析得出(遇到特殊情况可以让老板加签)

代码 IBILLService 工作流设计时使用

[ExternalDataExchange]

    public interface IBILLService

    {

        /// <summary>

        /// 小金额

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillInitMoneyMin;

        /// <summary>

        /// 大金额

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillInitMoneyMax;

        /// <summary>

        /// 打回重新修改

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillUpdated;

        /// <summary>

        /// 向下流转

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillNext;

        /// <summary>

        /// 结束工作流

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillCanceled;

        /// <summary>

        /// 等待

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillWait;

        /// <summary>

        /// 加签

        /// </summary>
        event EventHandler<ExternalDataEventArgs> BillInsert;        

    }


代码 IBILLService 的实现 BillServer
(也在 IBILLService)

 

/// <summary>

    /// IBILLService 实现,本类在 web项目 <ExternalDataExchangeService/> 节配置加载

    /// </summary>
    [Serializable]

    public class BillServer : IBILLService

    {

        public BillServer()

        {

        

            System.Diagnostics.Debug.WriteLine("Create OrderService");

       

        }

        /// <summary>

        /// 事件检索字典(不一定非要这么设计,不过个人感觉这么方便触发事件比较简单而且容易被配置化)

        /// 不过这么做事件子可以被映射一次如果需要映射多次自己改

        /// </summary>
        Dictionary<string, EventHandler<ExternalDataEventArgs>> _EventList = new Dictionary<string, EventHandler<ExternalDataEventArgs>>();


        /// <summary>

        /// 事件触发函数

        /// </summary>

        /// <param name="name"></param>

        /// <param name="instanceId"></param>
        public void RaiseEvent(string name, Guid instanceId)

        {

            if (_EventList[name] != null)

            {

                EventHandler<ExternalDataEventArgs> eventHand = _EventList[name];

                ExternalDataEventArgs ede = new ExternalDataEventArgs(instanceId);

                //ede.WorkItem = parms;
                

                eventHand(this, ede);

            }

        }

        

        

        #region IBILLService 成员


        public event EventHandler<ExternalDataEventArgs> BillInitMoneyMin

        {

            add

            {

                _EventList.Add("BillInitMoneyMin", value);

            }

            remove

            {

                _EventList.Remove("BillInitMoneyMin");

            }

        }


        public event EventHandler<ExternalDataEventArgs> BillInitMoneyMax

        {

            add

            {

                _EventList.Add("BillInitMoneyMax", value);

            }

            remove

            {

                _EventList.Remove("BillInitMoneyMax");

            }

        }



        public event EventHandler<ExternalDataEventArgs> BillUpdated

        {

            add

            {

                _EventList.Add("BillUpdated",value);

                

            }

            remove

            {

                _EventList.Remove("BillUpdated");

            }

        }



        public event EventHandler<ExternalDataEventArgs> BillNext

        {

            add

            {

                _EventList.Add("BillNext", value);

            }

            remove

            {

                _EventList.Remove("BillNext");

            }

        }


        public event EventHandler<ExternalDataEventArgs> BillCanceled

        {

            add

            {

                _EventList.Add("BillCanceled", value);

            }

            remove

            {

                _EventList.Remove("BillCanceled");

            }

        }


        public event EventHandler<ExternalDataEventArgs> BillWait

        {

            add

            {

                _EventList.Add("BillWait", value);

            }

            remove

            {

                _EventList.Remove("BillWait");

            }        

        }


        public event EventHandler<ExternalDataEventArgs> BillInsert

        {

            add

            {

                _EventList.Add("BillInsert", value);

            }

            remove

            {

                _EventList.Remove("BillInsert");

            }        

        }

        #endregion

    }正常业务不可能没其他数据的虚拟了一个数据实体类保存,报销金额啥的 BillModel (也在 IBILLService) 
 
    [Serializable]

    public class BillModel

    {

        string _userName;

        /// <summary>

        /// 报销人

        /// </summary>
        public string UserName

        {

            get { return _userName; }

            set { _userName = value; }

        }

        string _billId;

        /// <summary>

        /// 报销编号,数据库里的

        /// </summary>
        public string BillId

        {

            get { return _billId; }

            set { _billId = value; }

        }

        

        decimal _money;

        /// <summary>

        /// 报销金额

        /// </summary>
        public decimal Money

        {

            get { return _money; }

            set { _money = value; }

        }


        private string fremark;

        /// <summary>

        /// 财务批示

        /// </summary>
        public string Fremark

        {

            get { return fremark; }

            set { fremark = value; }

        }

        private string bremark;

        /// <summary>

        /// 老板批示

        /// </summary>
        public string Bremark

        {

            get { return bremark; }

            set { bremark = value; }

        }

        private string premark;

        /// <summary>

        /// 出纳批示

        /// </summary>
        public string Premark

        {

            get { return premark; }

            set { premark = value; }

        }

        private string lastRemark;

        /// <summary>

        /// 最后批示

        /// </summary>
        public string LastRemark

        {

            get { return lastRemark; }

            set { lastRemark = value; }

        }

        

    }



工作流设计 (MyWorkflows.BillWorkflow.cs)

Java报销单审批 报销单据的审核_System_02


上图中的所有 EventDrivenActivity 里面都子有一个 HandleExternalEventActivity(外部事件) 和一个 SetStateActivity(设置流转)
一下说明工作流和 IBILLService 的对应关系

stateInit/eveTo01 对应 BillInitMoneyMin
stateInit/eveTo01 对应 BillInitMoneyMax
statePayBox/eveWait 对应 BillWait
其他名称(name)是OK 的如 eveomOk,evePok 等对应 BillNext
是 Update 的对应 BillUpdated
是 BillCanceled 对应 BillCanceled
还有一个加签在以后代码中\动态加入,不再设计器中表现

 

 

涉及技术  在Web.config 配置  

  取得工作流结构

  动态加签

  动态取得审批方式

  启动工作流流转等(这个好多列子中都有不再详细描述)

Java报销单审批 报销单据的审核_Java报销单审批

Web 项目文件主要功能描述

  1. Web.Config

      配置了一些工作流服务
      WorkflowRuntime 加载的,ManualWorkflowSchedulerService 和 ExternalDataExchangeService
      ExternalDataExchangeService 加载的,上篇文章中的 MyWorkflows.BillServer
  2. Global.asax,App_Code\Global.asax.cs   启动 WorkflowRuntime,加载 FileWorkflowPersistenceService(自定义的状态保存服务,下篇文章详细讲解)代码
protected void Application_Start(object sender, EventArgs e)

        {

            Console.WriteLine("Application_Start:");

            System.Workflow.Runtime.WorkflowRuntime workflowRuntime = new System.Workflow.Runtime.WorkflowRuntime(MyWorkHelpr.WorkflowRuntimeName);

        


            //加载状态保持服务(自己的类),构造函数设置保存状态的路径
        FileWorkflowPersistenceService f28s = new FileWorkflowPersistenceService(Server.MapPath("~/App_Data/XOM.WFDB/"));

            workflowRuntime.AddService(f28s);


            

            Application[MyWorkHelpr.WorkflowRuntimeName] = workflowRuntime;

            //映射事件没用上
        workflowRuntime.WorkflowCompleted += new EventHandler<System.Workflow.Runtime.WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);

            workflowRuntime.WorkflowTerminated += new EventHandler<System.Workflow.Runtime.WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated);

            workflowRuntime.WorkflowSuspended += new EventHandler<System.Workflow.Runtime.WorkflowSuspendedEventArgs>(workflowRuntime_WorkflowSuspended);

            workflowRuntime.WorkflowPersisted += new EventHandler<System.Workflow.Runtime.WorkflowEventArgs>(workflowRuntime_WorkflowPersisted);

            workflowRuntime.StartRuntime();

            

            //重状态保存读取所有没执行完的了类.
        foreach (Guid id in f28s.GetAllWorkflows())

            {

                workflowRuntime.GetWorkflow(id);

            }


        }
  1. Default.aspx (用户登陆)

      导航页面,可以模拟各种角色登陆,可以显示全部正在进行的工作流信息、可以查看工作流的结构



    代码说明
1.     取得工作流结构
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)

        {

            int i = 0;

            if (e.CommandName.ToLower() == "select")

            {//就想取两级就不递归了
        

                i = Convert.ToInt32(e.CommandArgument);

                string sguid = GridView1.Rows[i].Cells[1].Text.Trim();

                Guid instanceId = new Guid(sguid);

                

                WorkflowInstance workInstance = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetWorkflow(instanceId);

                StateMachineWorkflowActivity MainActivity = workInstance.GetWorkflowDefinition() as StateMachineWorkflowActivity;

                TreeView1.Nodes.Clear();

                

                TreeNode MainNode = new TreeNode();

                TreeNode NodeL1 = null;

                TreeNode NodeL2 = null;

                MainNode.Text = sguid;

                foreach(StateActivity state in MainActivity.Activities)

                {

                    

                    NodeL1 = new TreeNode();

                    NodeL1.NavigateUrl="#";

                    NodeL1.Text = state.Name + ":" +state.Description;

                    foreach(CompositeActivity cactive in state.Activities)

                    {

                        NodeL2 = new TreeNode();

                        NodeL2.NavigateUrl = "#";

                        NodeL2.Text = cactive.Name + ":" + cactive.Description;

                        NodeL1.ChildNodes.Add(NodeL2);

                        

                    }

                    MainNode.ChildNodes.Add(NodeL1);

                }

                MainNode.ExpandAll();

                TreeView1.Nodes.Add(MainNode);

                

                //btnReadGuid_Click(btnReadGuid, null);
        }

        }1. 
2. CreateBill.aspx (创建修改报销单)


      填写表单,根据金额判断跳转到老板,还是财务(跳转是在ASP.net程序中控制的,其实也可以在工作流里控制,有情趣的可以自己改改)


代码protected void btnOk_Click(object sender, EventArgs e)

        {

            if(TextBox1.Text.Length>0)

            {

                Guid InstanceId = new Guid(TextBox1.Text);

                BillServer billserver = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetService<BillServer>();


                ManualWorkflowSchedulerService scheduler = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetService<ManualWorkflowSchedulerService>();

                


                decimal money = decimal.Parse(txt3.Text);

                if (money < 1) return;




                string wsPath = HttpContext.Current.Server.MapPath("~/App_Data/WorkflowSave/");

                //如果已经有数据取得
            BillModel model = FileTools.XmlDeserializeObject(wsPath

                , InstanceId

                , typeof(BillModel)) as BillModel;


                if (model==null) model = new BillModel();

                

                            

                model.BillId = txt1.Text;

                model.Money = money;

                model.UserName = txt2.Text;            

                

                

                if(model.Money > 1000)

                {//流转到老板
                billserver.RaiseEvent("BillInitMoneyMax", InstanceId);

                }

                else

                {//流转到财务
                billserver.RaiseEvent("BillInitMoneyMin",InstanceId);

                }

                

                //流转工作流
            scheduler.RunWorkflow(InstanceId);

                TextBox1.Text="";

                //保存更改的数据
            FileTools.XmlSerializeObject(wsPath

                , InstanceId

                , model);

                //this.Response.Redirect("default.aspx", false);
        }

        }
  1. Finance.aspx (财务)

       页面加载显示财务应该处理的流程,选择一个正在进行的流程进行审批,动态取得审批方式到下拉框(审批方式是工作流设计器上设计的)、如果需要可以动态增加一个老板加签


    代码说明
    动态取得审批方式到下拉框的代码
protected void btnReadGuid_Click(object sender, EventArgs e)

        {

            

            string s = txtGuid.Text.Trim();

            if(s.Length>0)

            {

                Guid instanceId = new Guid(s);

                //取得当前工作流里正在进行的 StateMachineWorkflow (这里是 stateFinance)
            StateMachineWorkflowInstance stateInstance = new StateMachineWorkflowInstance(Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime, instanceId);

                

                using(DataTable dt = new DataTable())

                {


                    //创建一个下拉框用的 DataTable
                dt.Columns.Add("NID", typeof(string)); //保存 EventDrivenActivity 的名字,在确定时使用
                dt.Columns.Add("NTEXT", typeof(string)); //保存 EventDrivenActivity 的说明
                dt.Rows.Add("-1","==请选择==");

                    

                    int i=0;

                    

                    foreach(Activity act in stateInstance.CurrentState.EnabledActivities)

                    {//循环当前流程可以用的 Activitie (在本步骤就是设计器里的 stateFinance 中的那些 EventDrivenActivity)
                    

                        if (act is EventDrivenActivity )

                        {

                            EventDrivenActivity edact = (EventDrivenActivity)act;

                            

                            if (edact.EnabledActivities.Count >0 && edact.EnabledActivities[0] is HandleExternalEventActivity)

                            {//取得每个 EventDrivenActivity 中第一个 HandleExternalEventActivity 把名字和说明放到 dt 中
                            dt.Rows.Add(edact.Name, act.Description);

                            }

                        }

                        i++;

                    }

                    

                    //DataBind
                DropDownList1.DataValueField = "NID";

                    DropDownList1.DataTextField = "NTEXT";

                    DropDownList1.DataSource = dt;

                    DropDownList1.DataBind();

                    

                    

                }

            }

        }1. 

    动态加签的代码
protected void Button2_Click(object sender, EventArgs e)

        {

            string s = txtGuid.Text.Trim();

            

            if(s.Length>0)

            {

                Guid instanceId = new Guid(s);

                WorkflowInstance workInstance = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetWorkflow(instanceId);

                //取得整个工作流对象
            StateMachineWorkflowActivity activity = (StateMachineWorkflowActivity)workInstance.GetWorkflowDefinition();

                //用 WorkflowChanges 榜定 activity
            WorkflowChanges wfc = new WorkflowChanges(activity);

                //取得财务的 StateActivity
            StateActivity state = (StateActivity)wfc.TransientWorkflow.Activities["stateFinance"];

                        

                if(state.Activities["eveToBoss"]!= null ) return;


            

                


                //声明一个外部事件
            HandleExternalEventActivity externalEveTmp = new HandleExternalEventActivity();

                externalEveTmp.EventName = "BillInsert";

                externalEveTmp.InterfaceType = typeof(MyWorkflows.IBILLService);

                externalEveTmp.Name = "externalEveTmp";

                

                //设置一个跳转
            SetStateActivity setActTmp = new SetStateActivity();

                setActTmp.Name = "setActTmp";

                setActTmp.TargetStateName = "stateBoss";

                

            


                EventDrivenActivity EventDrivenTmp = new EventDrivenActivity();


                EventDrivenTmp.Activities.Add(externalEveTmp);

                EventDrivenTmp.Activities.Add(setActTmp);

                EventDrivenTmp.Description = "*老板加签*";

                EventDrivenTmp.Name = "eveToBoss";            

                //挂起
            workInstance.Suspend("正在加签");



                //财务的 StateActivity 中加入一个 EventDrivenActivity
            state.Activities.Add(EventDrivenTmp);

                

                ValidationErrorCollection err = wfc.Validate();

                //应用更改
            workInstance.ApplyWorkflowChanges(wfc);

                //恢复
            workInstance.Resume();

                

                btnReadGuid_Click(btnReadGuid,new EventArgs());

                

            }

        }1. 

    确定按扭时使用 下拉框 里的审批方式

protected void Button1_Click(object sender, EventArgs e)

        {

            string s = txtGuid.Text.Trim();

            if (s.Length > 0 && DropDownList1.SelectedValue != "-1")

            {

                //取得下拉框选择的 value btnReadGuid_Click 中取得的
            string nid = DropDownList1.SelectedValue;

                

                Guid instanceId = new Guid(s);


                BillServer billserver = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetService<BillServer>();

                ManualWorkflowSchedulerService scheduler = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetService<ManualWorkflowSchedulerService>();


                StateMachineWorkflowInstance stateInstance = new StateMachineWorkflowInstance(Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime, instanceId);

                //通过名称取得 EventDrivenActivity
            EventDrivenActivity edact = (EventDrivenActivity)stateInstance.CurrentState.Activities[nid];

                //和第一个 HandleExternalEventActivity
            HandleExternalEventActivity heva = (HandleExternalEventActivity)edact.EnabledActivities[0];

                

                



                //WorkflowInstance workInstance = stateInstance.WorkflowInstance;

                //BillWorkflow act = stateInstance.WorkflowInstance.GetWorkflowDefinition() as BillWorkflow;


                string wsPath = HttpContext.Current.Server.MapPath("~/App_Data/WorkflowSave/");

                BillModel model = FileTools.XmlDeserializeObject(wsPath

                , instanceId

                , typeof(BillModel)) as BillModel;


                model.LastRemark = model.Fremark = string.Concat("财务: ", DropDownList1.SelectedItem.Text, ":", txtRemark.Text);

                

                


                //通过  HandleExternalEventActivity.EventName 调用 BillServer.RaiseEvent 触发外部事件使工作流向前运行

                //这就是我为上一章何在 BillServer 重写那些事件的原因
            billserver.RaiseEvent(heva.EventName, instanceId);

                

                //工作流向下流转
            scheduler.RunWorkflow(instanceId);

                

                FileTools.XmlSerializeObject(wsPath,instanceId,model);

                DropDownList1.Items.Clear();

                txtGuid.Text = "";

                //stateInstance.CurrentStateName
            return;

            //    //如果用过一次要删除

            //    if(edact.Name == "eveToBoss") //删除加签

            //    {

                    

            //        //WorkflowInstance workInstance = (Application[MyWorkHelpr.WorkflowRuntimeName] as WorkflowRuntime).GetWorkflow(instanceId);

            //        workInstance.Suspend("正在删除加签");


            //        StateMachineWorkflowActivity activity = workInstance.GetWorkflowDefinition() as StateMachineWorkflowActivity;


            //        WorkflowChanges wfc = new WorkflowChanges(activity);


            //        //StateActivity sa = wfc.TransientWorkflow.Activities["stateFinance"] as StateActivity;


            //        StateActivity state = (StateActivity)wfc.TransientWorkflow.Activities["stateFinance"];

            //        state.Activities.Remove(state.Activities["eveToBoss"]);

                                    

            //        ValidationErrorCollection err = wfc.Validate();

            //        workInstance.ApplyWorkflowChanges(wfc);

            //        workInstance.Resume();

                    

            //        //this.Response.Redirect("");

                    

            //    }            
            

            }

        }



  1. Boss.aspx (老板)

      页面加载显示老板应该处理的流程,选择一个正在进行的流程进行审批,动态取得审批方式到下拉框
  2. PayBox.aspx (出纳)

      页面加载显示出纳应该处理的流程,选择一个正在进行的流程进行审批,动态取得审批方式到下拉框
  3. OutMoney.aspx(取钱确认)

      页面加载子显示出纳审批Ok 的流程,选择一个正在进行的流程确认以读取,动态取得审批方式到下拉框

说明 其实重财务以后的页面处理方式都基本是一样的,界面也都类似,不再多说...