背景
搭建一个适合公司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等进行数据报表等分析。开发,维护复杂度等都会提升。
结论:如果不是做大型产品或服务,不会进行分布式架构。
(此文只做阶段性的总结,也许对同样做流程引擎的人有些启发,也欢迎交流。分布式相关架构可以参考本人其他文章)