zoukankan      html  css  js  c++  java
  • .net erp(办公oa)开发平台架构之流程服务概要介绍

    背景

    搭建一个适合公司erp业务的开发平台。

    架构概要图
     

     

    流程引擎开发平台
      包含流程引擎设计器,流程管理平台,流程引擎服务。目前只使用单个数据库进行管理。
      流程引擎设计器

       采用silverlight进行开发,本质是对流程模型进行设计,并生成xml。包含:人工节点,自动节点,并行开始节点,并行结束节点,消息节点,文本节点。
      示例模型定义图形:
        
      示例模型定义xml:

    <?xml version="1.0" encoding="gb2312"?>
    <ProcessModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <BaseInfo>
        <Key>ddd</Key>
        <Title>新建模型</Title>
        <Description>发起人</Description>
        <CreateTime>0001-01-01T00:00:00</CreateTime>
        <Author>
          <StaffNo>34</StaffNo>
          <UserName>车江毅1</UserName>
        </Author>
        <Type>Common</Type>
        <Compilation>Debug</Compilation>
      </BaseInfo>
      <Variables>
        <Variable Name="myValue1" Value="今天天气好晴朗" Mode="Flow" />
        <Variable Name="myValue2" Value="v2" Mode="Flow" />
        <Variable Name="myValue3" Value="v3" Mode="Flow" />
        <Variable Name="myValue4" Value="true" Mode="Flow" />
      </Variables>
      <Nodes>
        <BaseNode xsi:type="StartNode" Key="start1" Text="开始" Location="236 10">
          <Routes>
            <Route To="auto1" Text="" Location="0 0">
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
        </BaseNode>
        <BaseNode xsi:type="AutoNode" Key="auto1" Text="系统判断" Location="236 95">
          <Routes>
            <Route To="man1" Text="线1" Location="0 0">
              <Code><![CDATA[myValue4=='true']]></Code>
              <RouteScripts />
            </Route>
            <Route To="人工2" Text="线" Location="0 0">
              <Code><![CDATA[myValue4=='false']]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Scripts>
            <Script>
              <Code><![CDATA[api.Forword('http://webservice.webxml.com.cn/webservices/ChinaTVprogramWebService.asmx','getTVstationDataSet','theAreaID:2')]]></Code>
            </Script>
            <Script>
              <Code><![CDATA[api.UpdateVariable('063550')]]></Code>
            </Script>
          </Scripts>
        </BaseNode>
        <BaseNode xsi:type="ManNode" Key="man1" Text="人工1" Location="167 205">
          <Routes>
            <Route To="并行开始1" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Participants>
            <Participant Mode="Code">
              <Code><![CDATA[api.Split('034','#')]]></Code>
            </Participant>
            <Participant Mode="Variable">
              <Code><![CDATA[myValue3]]></Code>
            </Participant>
          </Participants>
          <Solt OrderCount="2" Mode="Once" />
          <Decisions>
            <Decision Text="同意" />
            <Decision Text="不同意" />
          </Decisions>
        </BaseNode>
        <BaseNode xsi:type="EndNode" Key="结束" Text="结束" Location="238 732">
          <Routes />
          <BeginScripts />
          <EndScripts />
        </BaseNode>
        <BaseNode xsi:type="ManNode" Key="人工2" Text="人工2" Location="324 203">
          <Routes>
            <Route To="并行开始1" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Participants />
          <Decisions />
        </BaseNode>
        <BaseNode xsi:type="ParallelBeginNode" Key="并行开始1" Text="并行签入" Location="237.047607421875 312">
          <Routes>
            <Route To="人工3" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
            <Route To="人工4" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
        </BaseNode>
        <BaseNode xsi:type="ParallelFinishNode" Key="并行结束1" Text="并行结束1" Location="240 571">
          <Routes>
            <Route To="消息1" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
        </BaseNode>
        <BaseNode xsi:type="ManNode" Key="人工3" Text="人工3" Location="95 378">
          <Routes>
            <Route To="自动1" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Participants />
          <Decisions />
        </BaseNode>
        <BaseNode xsi:type="ManNode" Key="人工4" Text="人工4" Location="392 379">
          <Routes>
            <Route To="自动2" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Participants />
          <Decisions />
        </BaseNode>
        <BaseNode xsi:type="AutoNode" Key="自动1" Text="自动1" Location="98 483">
          <Routes>
            <Route To="并行结束1" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Scripts />
        </BaseNode>
        <BaseNode xsi:type="AutoNode" Key="自动2" Text="自动2" Location="394.714294433594 474.952362060547">
          <Routes>
            <Route To="并行结束1" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Scripts />
        </BaseNode>
        <BaseNode xsi:type="MessageNode" Key="消息1" Text="消息1" Location="239 656">
          <Routes>
            <Route To="结束" Text="线" Location="0 0">
              <Code><![CDATA[]]></Code>
              <RouteScripts />
            </Route>
          </Routes>
          <BeginScripts />
          <EndScripts />
          <Participants />
          <MessageVariables />
          <TemplateKey>0</TemplateKey>
          <TemplateName />
        </BaseNode>
      </Nodes>
    </ProcessModel>


      常规界面如下:
      
     

    流程管理平台
      包含:流程模型管理,流程管理,流程任务管理,流程操作记录,流程服务接口日志查询,流程异常查询,流程流转调试日志查询,开发者用户管理等。
      可以管理流程模型历史版本及版本切换,流程异常时候的流程节点切换,流程任务的转交等。
     界面如下:

     

    流程服务
      包含:流程接口和流程任务接口。

       
    /// <summary>
        /// 业务流程服务接口
        /// </summary>
        public interface IProcessService
        {
            /// <summary>
            /// 获取业务流程
            /// </summary>
            /// <param name="guid">业务流程ID</param>
            ProcessInfo GetProcessById(Guid guid);
            /// <summary>
            /// 批量获取业务流程
            /// </summary>
            /// <param name="guids">多个业务流程ID</param>
            ProcessListInfo GetProcessesByIds(List<Guid> guids);
            /// <summary>
            /// 根据流程发布者,获取业务流程列表
            /// </summary>
            /// <param name="user">用户的信息 参看UserInfo对象注释</param>
            /// <param name="modelkeys">模型key  可选(多个)</param>
            /// <param name="keyword">关键词(标题) 可选</param>
            /// <param name="pageIndex"></param>
            /// <param name="pageSize"></param>
            /// <returns></returns>
            ProcessListInfo GetProcessListByOriginator(UserInfo user, List<string> modelkeys, string keyword, int pageIndex, int pageSize);
            /// <summary>
            /// 根据流程参与者,获取业务流程列表
            /// </summary>
            /// <param name="user">用户的信息 参看UserInfo对象注释</param>
            /// <param name="modelkeys">模型key 可选(多个)</param>
            /// <param name="keyword">关键词(标题) 可选</param>
            /// <param name="nodekey">任务所在节点key 可选</param>
            /// <param name="state">任务完成状态</param>
            /// <param name="pageIndex"></param>
            /// <param name="pageSize"></param>
            /// <returns></returns>
            ProcessListInfo GetProcessListByParticipant(UserInfo user, List<string> modelkeys, string keyword, string nodekey, EnumTaskStateQuery state, int pageIndex, int pageSize);
            /// <summary>
            /// 根据模型keys, 获取业务流程列表
            /// </summary>
            /// <param name="modelkeys">模型关键词 (多个)</param>
            /// <param name="keyword">关键词(标题) 可选</param>
            /// <param name="pageIndex"></param>
            /// <param name="pageSize"></param>
            /// <returns></returns>
            ProcessListInfo GetProcessListByModel(List<string> modelkeys, string keyword, int pageIndex, int pageSize);
            /// <summary>
            /// 创建业务流程
            /// </summary>
            /// <param name="modelkey">业务流程对应的模型key</param>
            /// <param name="title">业务流程标题</param>
            /// <param name="description">业务流程描述</param>
            /// <param name="variables">业务流程使用变量 可选</param>
            /// <param name="user">业务流程发起人 参看UserInfo对象注释</param>
            /// <param name="operationArgs">操作记录 可选</param>
            /// <returns></returns>
            ProcessInfo CreateProcess(string modelkey, string title, string description, List<Variable> variables, UserInfo user, OperationRecordInfoArgs operationArgs);
            /// <summary>
            /// 更新业务流程
            /// </summary>
            /// <param name="guid">业务流程的ID</param>
            /// <param name="title">业务流程的标题</param>
            /// <param name="description">业务流程的描述</param>
            /// <param name="variables">业务员流程的变量 可选</param>
            void UpdateProcess(Guid guid, string title, string description, List<Variable> variables);
            /// <summary>
            /// 结束业务流程
            /// </summary>
            /// <param name="guid">业务流程的ID</param>
            void FinishProcess(Guid guid, UserInfo user, OperationRecordInfoArgs operationArgs);
            /// <summary>
            /// 创建流程操作记录
            /// </summary>
            /// <param name="user">操作用户不能为空</param>
            /// <param name="operationArgs">操作记录不能为空</param>
            void CreateProcessOperationRecord(Guid guid, UserInfo user, OperationRecordInfoArgs operationArgs);
        }
       /// <summary>
        /// 任务服务接口
        /// </summary>
        public interface ITaskService
        {
            /// <summary>
            /// 根据任务id,获取任务
            /// </summary>
            /// <param name="id">任务id</param>
            /// <returns></returns>
            TaskInfo GetTaskByID(string id);
            /// <summary>
            /// 根据任务id,批量获取任务
            /// </summary>
            /// <param name="ids">多个任务id</param>
            /// <returns></returns>
            TaskListInfo GetTasksByIDs(List<string> ids);
            /// <summary>
            /// 根据业务流程id,获取任务信息列表
            /// </summary>
            /// <param name="state">任务状态</param>
            /// <param name="processId">业务流程id</param>
            /// <returns></returns>
            TaskListInfo GetTaskListByProcessID(EnumTaskStateQuery state, Guid processId);
            /// <summary>
            /// 根据用户,获取任务信息列表
            /// </summary>
            /// <param name="user">用户的信息 参看UserInfo对象注释</param>
            /// <param name="state">任务状态</param>
            /// <param name="pageIndex"></param>
            /// <param name="pageSize"></param>
            /// <returns></returns>
            TaskListInfo GetTaskListByUser(UserInfo user, EnumTaskStateQuery state, int pageIndex, int pageSize);
            /// <summary>
            /// 根据用户和流程,获取任务信息列表
            /// </summary>
            /// <param name="user">用户的信息 参看UserInfo对象注释</param>
            /// <param name="processId">业务流程id</param>
            /// <param name="nodekey">当前活动节点key 可选</param>
            /// <param name="state">任务状态</param>
            /// <param name="pageIndex"></param>
            /// <param name="pageSize"></param>
            /// <returns></returns>
            TaskListInfo GetTaskListByUserAndProcess(UserInfo user, Guid processId, string nodekey,EnumTaskStateQuery state, int pageIndex, int pageSize);
            /// <summary>
            /// 根据业务流程模型key,获取任务信息列表
            /// </summary>
            /// <param name="user">用户的信息 参看UserInfo对象注释</param>
            /// <param name="modelkeys">业务模型key 可选(多个)</param>
            /// <param name="nodekey">当前活动节点key 可选</param>
            /// <param name="state">任务状态</param>
            /// <param name="pageIndex"></param>
            /// <param name="pageSize"></param>
            /// <returns></returns>
            TaskListInfo GetTaskListByUserAndModel(UserInfo user, List<string> modelkeys, string nodekey, EnumTaskStateQuery state, int pageIndex, int pageSize);
            /// <summary>
            /// 执行任务
            /// </summary>
            /// <param name="id">任务id</param>
            /// <param name="action">任务的动作 例如:“同意”“不同意”</param>
            /// <param name="variables">流程参数 可选</param>
            /// <param name="operationArgs">操作信息 必填</param>
            TaskInfo ExecuteTask(string id, string action, List<Variable> variables, OperationRecordInfoArgs operationArgs);
    
            /// <summary>
            /// 转交任务
            /// </summary>
            /// <param name="id">任务id</param>
            /// <param name="toUser">任务转交人信息 参看UserInfo对象注释</param>
            /// <param name="operationArgs">操作信息 传null则无转交操作记录</param>
            TaskInfo TransferTaskWithOperationRecordInfoArgs(string id, UserInfo toUser, OperationRecordInfoArgs operationArgs);

    分布式架构概想
      分布式拆库方案:流程维度和用户维度,流程维度数据库按照流程定义模型的唯一标识hash,来拆分到不同的数据库。所有流程创建和流程任务的创建都在不同的数据库中。用户维度数据库按照用户标识hash,来拆分到不同的数据库。
      拆库优点:数据被拆分,不影响同一个业务流程流转,性能会得到提升。
      拆库缺点:采用异步消息通知,做用户代办任务和完成任务冗余及同步。采用BI工具或者hadoop等进行数据报表等分析。开发,维护复杂度等都会提升。
      结论:如果不是做大型产品或服务,不会进行分布式架构。

    (此文只做阶段性的总结,也许对同样做流程引擎的人有些启发,也欢迎交流。分布式相关架构可以参考本人其他文章)

  • 相关阅读:
    golang 数据结构 优先队列(堆)
    leetcode刷题笔记5210题 球会落何处
    leetcode刷题笔记5638题 吃苹果的最大数目
    leetcode刷题笔记5637题 判断字符串的两半是否相似
    剑指 Offer 28. 对称的二叉树
    剑指 Offer 27. 二叉树的镜像
    剑指 Offer 26. 树的子结构
    剑指 Offer 25. 合并两个排序的链表
    剑指 Offer 24. 反转链表
    剑指 Offer 22. 链表中倒数第k个节点
  • 原文地址:https://www.cnblogs.com/chejiangyi/p/5113358.html
Copyright © 2011-2022 走看看