diff --git a/README.md b/README.md
index b738b7664387406ac98b5205dac5f0b35a5554b7..3f32806a123f486432908d16a526943f434bfca5 100644
--- a/README.md
+++ b/README.md
@@ -167,3 +167,749 @@ https://gitee.com/mengtree/workflow-engine/tree/sample/
3. 其它的诸如新建流程设计、流程审批记录等,可通过接口查询,或者自行实现相应的查询接口。
+## API
+
+### 通用
+ - 响应格式
+ ```
+ {
+ "code": "string", 编号 success|fail
+ "msg": "string", 对应编号的 成功|失败
+ "data": //接口业务
+ }
+ ```
+
+- 部分标准实体返回:
+ - 这些参数 顾名思义。其中 id 为该实体的数据库标识,有时也作业业务唯一标识。
+ ```
+ "modifiedTime": "2023-01-08T02:43:38.640Z", //更新时间
+ "modifiedUserId": "string", //更新人id
+ "createdUserId": "string", //
+ "creationTime": "2023-01-08T02:43:38.640Z",
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "deletedUserId": "string",
+ "deletedTime": "2023-01-08T02:43:38.640Z",
+ ```
+
+- 接口为 非标准 restful (method 遵循)
+
+### 流程设计相关
+
+在流程设计服务里,主要涉及设计和版本的管理。
+设计内容则为流程的设计信息,包括节点,连线,人员,条件以及前端其它设计代码管理。
+版本则为流程的版本管理,服务的设计为,一个流程可以有多个版本,随着业务的调整可以不断的调整流程,通过版本管理,可以保证现有流程与新流程区分开来,使旧流程不受新流程影响。
+- #### 创建流程(设计)***/api/WorkFlow/CreateWorkFlow***
+
+ 这里的创建很简单,这里实际上是将设计信息的创建统一放到更新操作,一来为了简化新增逻辑,统一在更新处理,二来也是配合前端的交互进行的设计。
+
+
+ - name:流程设计名称
+ - des :描述
+
+ 该操作返回流程基本信息(这时候设计信息还是为空)
+ ```
+ {
+
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
+ "workflowNo": "string",//流程编号
+ "name": "string",//流程名称
+ "activeVersion": 0, //当前使用版本
+ "description": "string" //描述
+ ......
+ }
+
+ ```
+
+
+
+
+- #### 流程设计更新 ***/api/WorkFlow/UpdateWorkflow***
+ 该接口用于保存流程设计信息,当流程版本有多个时,每个版本单独存储,每个版本有自己的设计id。
+
+ - **name**:流程设计名称
+ - **des**: 描述
+ - **drawingInfo**: 前端设计绘制信息,考虑到有些前端流程框架的绘制数据可能比较复杂,为便于回显,可以直接将绘制信息记录起来,需要回显可直接读取
+
+ - **versionDescription**: 版本描述。
+ - **workflowId**: 流程唯一标识。 由于一个流程可以有多个版本,所以唯一标识 由 流程id 和 版本确定
+ ```
+ {
+ versionId: 0 //版本号
+ id: //流程设计id 为 CreateWorkFlow 接口返回的 id
+ }
+ ```
+
+
+ - **workflowNodes**: 流程节点。系统的设计思路为节点负责人员选择,连线负责条件判断。一个节点除了有基本的节点信息,还包括 用户选择器(userSelector 用于在流程流转过程中,解析审批人员)。在这里,节点信息允许括驳回节点指定,即可根据不同的条件判定驳回场景下,应流转到哪个节点(补充一点,在当前系统,不管是拒绝还是驳回,都会构建一条记录信息,而不是“删除”上一步操作,所以每一步都是“下一步”)。
+
+ 流程节点数据格式如下:
+
+ ```
+ [
+ {
+ id: 3fa85f64-5717-4562-b3fc-2c963f66afa6 //节点唯一标识,应由前端生成
+ name: string //节点名称
+ nodeType: 0 //节点类型
+ drawingInfo: string //节点绘制信息。预留字段,如果节点的绘制数据较复杂,可以考虑直接保存绘制信息,便于回显
+ isWaitingAllUser: true //是否等待所有用户(处理)。
+ userSelectors: //用户选择器,指定通过哪个选择器来解析审批人员
+ [
+ {
+ selectorId: string // 选择器id
+ selectorName: string //选择器名称
+ selections: //选项值
+ [
+ {
+ id: string //选项值id
+ name: string //选项名称
+ }
+ ]
+ parameter: string //参数,指定参数将作为选择器使用(参数是否有用,具体看选择器的实现,在后台解析审批人员时,会传递该参数)
+ description: string //描述
+ handleType: 0 //处理类型。分为 只读(类似 抄送,只能看但不能处理)和处理。
+ }
+ ]
+ rejectNodes: //驳回(拒绝)回退的节点配置
+ [
+ {
+ conditions: //条件
+ [
+ {
+ conditionId: string //条件id
+ conditionName: string //条件名称
+ parameter: string //参数
+ description: string // 描述
+ }
+ ]
+ nodeId: 3fa85f64-5717-4562-b3fc-2c963f66afa6 //退回的节点id
+ nodeName: string // 退回的节点名称
+ }
+ ]
+ }
+ ]
+
+ ```
+
+ 其中:
+ - **nodeType** 节点类型。系统会自动从“开始”节点解析流程。
+ 其中比较特别的类型是“会签”,使用会签节点的效果与 **isWaitingAllUser** 值为 *true* 的类似,但它是需要连接到它的节点审批通过才会进行下一个节点,它等待的不同来源节点审批完成。有以下枚举类型:
+ ```
+ ///
+ /// 开始
+ ///
+ Begin,
+ ///
+ /// 结束
+ ///
+ End,
+ ///
+ /// 普通
+ ///
+ Judge,
+ ///
+ /// 会签,所有指向该节点的节点都要审批完成后到达
+ ///
+ Sign
+ ```
+ - **isWaitingAllUser** : 一个节点可能会分配到多个人审批,通常同一个节点有其中一个人处理后,即可往下一个节点,但是,如果勾选等待所有用户审批,那这个节点就相当会签节点,所有人都通过才会走下一步。
+
+ - **userSelectors** : 用户选择器集合。用户选择器用于在流转到节点时,解析目标审批人员。用户选择器信息主要包括几个信息:哪个选择器,选了哪个选项,额外参数,人员处理方式。
+
+ - 用户选择器通过 实现 IUserSelector 接口自定义用户选择
+ ```
+
+ [UserSelector("按用户选择","从所有用户选择")]
+ public class UserSelectorB : IUserSelector
+ {
+ //获取选项值
+ public List GetSelections()
+ {
+
+ return UserList.Users.Select(u => new Selection { Id = u.Id, Name = u.Name }).ToList();
+ }
+ //根据选项和参数获取用户(审批人员)
+ public List GetUsers(SelectorInput input)
+ {
+ var result = new List();
+ switch (input.SelectionId)
+ {
+ default:
+ result.Add(new User { Id = input.SelectionId, Name = UserList.GetUserById(input.SelectionId).Name });
+ break;
+ }
+ return result;
+ }
+ }
+ ```
+ - ***/api/WorkFlow/GetAllUserSelectors*** 前端通过该接口获取所有的用户选择器。默认返回的是 实现了 IUserSelector 接口的类型的基本信息,selectorId 为 类型(在文档这里是 UserSelectorB)全称,selectorName 为 UserSelectorAttribute 的第一个参数:
+ ```
+ [UserSelector("按用户选择","从所有用户选择")]
+ public class UserSelectorB : IUserSelector
+
+ ```
+ - 返回选择器集
+ ```
+ [
+ {
+ "id": "string", //选择器id(类名全称)
+ "name": "string", //选择器名称 UserSelectorAttribute 的第一个参数
+ "description": "string" //选择器描述 UserSelectorAttribute 的第二个参数
+ }
+ ]
+
+ ```
+
+
+ - ***/api/WorkFlow/GetUserSelectionsOfUserSelector*** 通过该接口根据用户选择器id获取该选择器的选项 。
+ - userSelectorId 选择器id。
+ - 返回选项集
+ ```
+ [
+ {
+ "id": "string", //选项id
+ "name": "string" 选项名称
+ }
+ ]
+ ```
+
+
+ - **rejectNodes** :拒绝(驳回、回退、回滚)节点配置。通过该配置,根据条件“回到”指定的节点,在必要情况下可以直接退到指定的节点,默认情况下,不配置时,则按原路返回。该配置主要有两项内容:条件,目标节点
+ - **nodeId、nodeName** 目标节点id 和名称,
+ - **conditions** 条件处理器的配置集合,可配置多个条件,满足其一则跳转到指定节点。条件处理器信息相对简单,主要包含哪个条件处理器以及额外参数。
+ - 通过实现 ICondition 接口实现自定义条件处理器。
+ ```
+ [Condition("条件处理器A")]
+ public class ConditionA : ICondition
+ {
+ public bool CanAccept(ConditionInput input)
+ {
+ try
+ {
+ //简单的表达式解析
+ var keyvalue = input.Expression.Split('=');
+ JObject jObject = JObject.Parse(input.WorkTask.FormData);
+ var token = jObject.SelectToken(keyvalue[0]);
+ var value = token.Value();
+ return value.Equals(keyvalue[1]);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+ }
+ ```
+ - ***/api/WorkFlow/GetAllconditions*** 通过该接口可以获取所有的条件处理器信息。
+ ```
+ [Condition("条件处理器A")]
+ public class ConditionA : ICondition
+
+ ```
+ - 返回基本的条件处理器信息集
+ ```
+ [
+ {
+ "id": "string", //条件处理器id(类名全称)
+ "name": "string", //条件处理器名称 ConditionAttribute 的第一个参数
+ "description": "string" //条件处理器描述 ConditionAttribute 的第二个参数
+ }
+ ]
+
+ ```
+ - **workflowLines** : 节点连线信息。该属性记录节点间的连线关系(从哪个节点到哪个节点)以及通行(条件是否满足)条件。
+ - **name: 条件名称。只用于显示
+ - **fromNodeId**: 起始节点
+ - **toNodeId**: 目标节点
+ - **drawingInfo**: 绘制信息。也是冗余备用。万一需要存储前端设计信息,也便于回显
+ - **conditions** :条件选择器信息,用于判断改连线流转是否满足条件,满足才能进行(同 用户选择器 rejectNodes 下的 conditions ),相当于一个“判断”+连线。
+
+
+
+ - ***/api/WorkFlow/UpdateWorkflowActiveVersion*** 用户切换当前激活(应用)的是哪个版本的流程
+ - **workflowId** :流程id
+ - **activeVersion** :激活版本(versionId)
+
+ - ***/api/WorkFlow/GetAllWorkflowVersions*** 通过该接口根据流程id获取所有的流程和版本信息(一个流程有多个版本)
+ - **workflowId** 流程id
+ - 返回流程版本集合
+ ```
+ [
+ {
+ "workflowId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
+ "versionNo": 0, //流程版本
+ "description": "string", //版本描述
+ "modifiedTime": "2023-01-07T13:40:38.791Z",
+ "creationTime": "2023-01-07T13:40:38.791Z"
+ }
+ ]
+
+ ```
+
+- #### 获取所有的流程(设计)基本信息列表 ***/api/WorkFlow/GetAllWorkflows***
+ - 返回基本信息列表
+ ```
+ [
+ {
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //流程id
+ "workflowNo": "string", //流程编号
+ "name": "string", //流程名称
+ "activeVersion": 0, //当前应用版本
+ "description": "string" //描述
+ }
+ ]
+
+ ```
+
+- #### 根据流程id和版本号获取详细的流程设计信息 ***/api/WorkFlow/GetWorkflowVersion*** 。
+ - **versionId** 版本号
+ - **id** 工作流id
+
+ - 返回详细信息
+ ```
+ {
+ "code": "string",
+ "msg": "string",
+ "data": {
+ "modifiedTime": "2023-01-07T13:48:42.386Z",
+ "modifiedUserId": "string",
+ "createdUserId": "string",
+ "creationTime": "2023-01-07T13:48:42.386Z",
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "deletedUserId": "string",
+ "deletedTime": "2023-01-07T13:48:42.386Z",
+ "deleted": true,
+ "workflowId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "versionNo": 0,
+ "drawingInfo": "string",
+ "description": "string",
+ "nodeMaps": [], //节点映射。连线和节点的映射信息集合。
+ "allNodes": [] //所有节点
+ }
+ }
+
+ ```
+ 其中
+ - **nodeMaps** 和 allNodes 数据差不多。对于 allNodes ,类似 接口 ***/api/WorkFlow/UpdateWorkflow*** 的参数。 nodeMaps 是 lines 和 nodes 关联的信息的明细,相当于把 连线line 里的 fromNodeId toNodeId 替换为 fromNode toNode ,对应 allNode 里相同nodeId的 node 节点:
+ ```
+ [
+ {
+ "mapType": 0,
+ "fromNode": {
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "workflowId": {
+ "versionId": 0,
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+ },
+ "name": "string",
+ "nodeType": 0,
+ "drawingInfo": "string",
+ "isWaitingAllUser": true,
+ "userSelectors": [
+ {
+ "selectorId": "string",
+ "selectorName": "string",
+ "selections": [
+ {
+ "id": "string",
+ "name": "string"
+ }
+ ],
+ "parameter": "string",
+ "description": "string",
+ "handleType": 0
+ }
+ ],
+ "rejectNodes": [
+ {
+ "conditions": [
+ {
+ "conditionId": "string",
+ "conditionName": "string",
+ "parameter": "string",
+ "description": "string"
+ }
+ ],
+ "nodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "nodeName": "string"
+ }
+ ]
+ },
+ "toNode": {
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "workflowId": {
+ "versionId": 0,
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+ },
+ "name": "string",
+ "nodeType": 0,
+ "drawingInfo": "string",
+ "isWaitingAllUser": true,
+ "userSelectors": [
+ {
+ "selectorId": "string",
+ "selectorName": "string",
+ "selections": [
+ {
+ "id": "string",
+ "name": "string"
+ }
+ ],
+ "parameter": "string",
+ "description": "string",
+ "handleType": 0
+ }
+ ],
+ "rejectNodes": [
+ {
+ "conditions": [
+ {
+ "conditionId": "string",
+ "conditionName": "string",
+ "parameter": "string",
+ "description": "string"
+ }
+ ],
+ "nodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "nodeName": "string"
+ }
+ ]
+ },
+ "conditions": [
+ {
+ "conditionId": "string",
+ "conditionName": "string",
+ "parameter": "string",
+ "description": "string"
+ }
+ ]
+ }
+ ]
+ ```
+
+
+
+
+
+
+### 流程审批相关
+
+ - #### 新建审批 ***/api/WorkFlow/CreateWorkTask***
+ -
+ ```
+ {
+ "workflowId":
+ {
+ "versionId": 0,
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+ },
+ "name": "string",
+ "formData": "string",
+ "entityFullName": "string",
+ "entityKeyValue": "string",
+ "createdUserId": "string"
+ }
+ ```
+
+ - ***workflowId** :流程id :原始流程id + 版本号。通过接口 ***/api/WorkFlow/GetAllWorkflows*** 获取所有的流程设计。返回流程设计基本信息。workflowId.versionId 对应 GetAllWorkflows 结果集里的 activeVersion。 即这里要创建审批,只需要知道某个流程的以及当前应用的版本(不需要知道所有版本,原则上发起审批的人不需要知道这些)。
+
+ - ***name*** 审批名称(标题)。
+ - ***formData*** 表单数据。这个参数是核心。表单数据可以是平台的任意格式,相应的 **userSelector** 和 **condition** 在处理过程也会传递表单数据共解析和判断。
+ - ***entityFullName,entityKeyValue*** 外部系统表单类型全称和唯一标识。这两个参数主要是为了便于外部系统自己的分类和查询。在系统表单处需要查询流程信息时,借助这两个数据可以精确查询。
+ - ***createdUserId*** 创建用户id。为系统平台的用户id,也可以不传,前提是自定义实现 IWorkflowSession 接口解析当前请求的用户信息。
+ - **IWorkflowSession** 的抽象在 **WorkFlowCore.Authorization** 。在**WorkFlowCore.Framework** 实现自定义的 session,并在 **WorkFlowCoreFrameworkService** 替换现有的注册:
+ ```
+ services.Replace(new ServiceDescriptor(typeof(IWorkflowSession),typeof(DefaultSession), ServiceLifetime.Scoped));
+ ```
+
+ - 返回基本流程信息:
+ ```
+ {
+
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "deleted": true,
+ "workflowId": {
+ "versionId": 0,
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+ },
+ "name": "string",
+ "formData": "string",
+ "entityFullName": "string",
+ "entityKeyValue": "string",
+ "workTaskStatus": 0,
+ "isProcessed": true,
+ "isPending": true,
+ "isProcessing": true,
+ "isSimulation": true
+ }
+
+ ```
+ 其中有几个的数据:
+ - ***workTaskStatus*** 审批状态
+ ```
+ {
+ ///
+ /// 待处理
+ ///
+ Pending,
+ ///
+ /// 处理中
+ ///
+ Processing,
+ ///
+ /// 已完成
+ ///
+ Processed
+ }
+ ```
+
+ - ***isProcessed/isPending/isProcessing*** 为 **workTaskStatus** 的常用判断值
+
+ - ***isSimulation*** 是否是模拟流程。 该属性不在前端传递,而是通过 **创建模拟流程接口** ***/api/WorkFlow/CreateSimulationWorkTask*** 创建的任务。其参数与 **CreateWorkTask** 一致。
+
+ - #### 发起审批 ***/api/WorkFlow/StartWorkTask***
+ -
+ ```
+ {
+ "worktaskId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+ }
+ ```
+ - ***worktaskId*** 流程审批id。
+
+ - 返回发起审批后的处理待审批记录(每一条记录对应 每一个审批用户):
+ ```
+ [
+ {
+
+ "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "workTaskId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "workStepType": 0,
+ "fromNodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "fromNodeName": "string",
+ "nodeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "nodeName": "string",
+ "handleUser": {
+ "id": "string",
+ "name": "string"
+ },
+ "isHandled": true,
+ "handleType": 0,
+ "comment": "string",
+ "resourceIds": "string",
+ "isRead": true,
+ "readTime": "2023-01-08T02:43:38.627Z",
+ "handlerTime": "2023-01-08T02:43:38.627Z",
+ "groupId": "string",
+ "preStepGroupId": "string",
+ "formData": "string",
+ "fromForwardStepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+ }
+ ]
+
+ ```
+ 其中:
+ - ***workStepType*** 处理类型。与 流程设计(***/api/WorkFlow/UpdateWorkflow*** )中,节点 Node 配置中的 handleType对应: 0 //处理类型。分为 只读(类似 抄送,只能看但不能处理)和处理。
+ ```
+ ///
+ /// 处理(正常需要处理的情况)
+ ///
+ Handle,
+ ///
+ /// 只读(抄送情况下)
+ ///
+ ReadOnly,
+ ```
+
+ - ***fromNodeId,fromNodeName*** 来源节点。即是从哪个节点流转到当前的(新发起的节点来源节点为 开始节点)。
+
+ - ***nodeId,nodeName*** 当前审批节点。
+ - ***handleUser*** 当前审批人。
+ - ***isHandled** 是否已经处理(**workStepType** 为”处理“的情况)
+ - ***handleType*** 处理方式:
+ ```
+ {
+ ///
+ /// 通过
+ ///
+ Pass,
+ ///
+ /// 拒绝
+ ///
+ Reject,
+ ///
+ /// 撤回
+ ///
+ Withdraw,
+ ///
+ /// 转发(转给某人审批)
+ ///
+ Forward,
+ ///
+ /// 未处理(在会签节点其它节点驳回,但是部分未处理的则更新为该状态)
+ ///
+ UnWork
+ }
+
+ ```
+ - ***handlerTime** 处理时间
+
+ - ***comment*** 批文。
+
+ - ***resourceIds*** 附件id。这里设计为只记录附件id。每一次审批都可以根据情况上传附件(附件由平台自己管理附件信息)。通常多个附件英文逗号隔开,但具体什么存和解析,由接入平台定义。
+ - ***isRead,readTime*** 是否已读,以及已读时间。(目前这两个状态仅在执行”撤回(***/api/WorkFlow/WithdrawProve***)“操作时,才标记已读,设计考虑在用户查看审批明细时,也应该标记已读)
+ - ***groupId*** 分组id。分组id 在业务上没意义,在流程控制上便于”撤回“和”驳回“。*设计上,每一次的节点流转都是一个分组,一个节点可能会根据条件流转到一个或者多个节点上,而这些在同一个节点审批流转的审批就归属为同一组*。
+ - ***preStepGroupId*** 前一组id
+ - ***formData*** 表单数据。这里审批的表单数据不是发起是的表单数据。下面审批接口(***/api/WorkFlow/PassProve***)会提到。每次审批也都可以有自己的表单数据,当存在这些数据并且 **用户选择器** 和 **条件处理器** 都会应用这些数据进行解析时,也会影响到流程的流转。考虑到的这个场景是应用到每次审批都有可能更新表单的情况(具体在审批过程能否编辑数据,这个得看实现情况)。
+ - ***comment*** 批文。
+ - ***fromForwardStepId** 来源步骤id。如果是转发才会有值。即标记是哪个审批记录转发过来的。
+
+
+ - #### 通过审批 ***/api/WorkFlow/PassProve***
+ -
+ ```
+ {
+ "comment": "string",
+ "stepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
+ "resourceIds": "string"
+ }
+ ```
+
+ - ***comment*** 批文
+ - ***stepId*** 待审批记录id
+ - ***resourceIds*** 附件id(前面提到过)。
+ - 返回发起审批后的处理待审批记录(与 发起审批 ***/api/WorkFlow/StartWorkTask*** 接口返回一致):
+
+ - #### 驳回审批 ***/api/WorkFlow/RejectProve*** (参考发起审批 ***/api/WorkFlow/StartWorkTask***)
+ - #### 撤回审批 ***/api/WorkFlow/WithdrawProve*** (参考发起审批 ***/api/WorkFlow/ StartWorkTask***)
+
+ - #### 转发审批 ***/api/WorkFlow/RejectProve***
+ 转发审批为转发当前审批操作转给另一个人进行审批。与其他审批处理操作相似,但多出一个传参。
+
+ ```
+ {
+ "comment": "string", //批文
+ "stepId": "3fa85f64-5717-4562-b3fc-2c963f66afa6", //待审批记录id
+ "resourceIds": "string", //附件id
+ "userSelectors":
+ [
+ {
+ "selectorId": "string",
+ "selectorName": "string",
+ "selections": [
+ {
+ "id": "string",
+ "name": "string"
+ }
+ ],
+ "parameter": "string",
+ "description": "string",
+ "handleType": 0
+ }
+ ]
+ }
+ ```
+
+ - ***userSelectors*** 用户选择器。此处转发操作也采用用户选择器进行解析,一来统一规范,二来可扩展性也 高。参考流程设计接口 (流程设计更新 ***/api/WorkFlow/UpdateWorkflow*** )
+
+
+ - #### 获取所有审批步骤 ***/api/WorkFlow/GetAllTaskStepsOfWorkTask***
+ - ***worktaskId*** 审批id。
+ - 返回该审批流所有的审批记录。(与 发起审批 ***/api/WorkFlow/StartWorkTask*** 接口返回一致)。
+
+ - #### 获取所有审批步骤(根据entity 信息) ***/api/WorkFlow/ GetAllTaskStepsOfWorkTaskByEntityInfo***
+ - ***entityFullName*** 外部分类全称。
+ - ***entityKeyValue*** 外部表单标识。
+ - (参考 新建审批 ***/api/WorkFlow/CreateWorkTask*** 里的)
+
+ - #### 分页获取用户待处理审批 ***/api/WorkFlow/GetUnHandledWorkTasksOfUser***
+ - ***CurrentPage*** 当前页(默认从1开始)
+ - ***MaxResultCount*** 分页大小
+ - 返回流程审批集合.(属性参考 **新建审批** ***/api/WorkFlow/CreateWorkTask***)
+
+
+ - #### 分页获取用户已处理审批 ***/api/WorkFlow/GetHandledWorkTasksOfUser***
+ 参考 **分页获取用户待处理审批** ***/api/WorkFlow/GetUnHandledWorkTasksOfUser***
+ - 这里的已处理包括自己发起的,也包括别人发起经由自己审批的。
+
+
+ - #### 分页根据entity信息获取处理中的审批 ***/api/WorkFlow/ GetAllProcessingWorkTasksByEntityType***
+ - ***entityFullName*** 外部分类全称。
+ - ***entityKeyValues*** 外部表单标识,多个之间通过英文逗号隔开。
+ - 返回流程审批集合.(属性参考 **新建审批** ***/api/WorkFlow/CreateWorkTask***)
+
+ - #### 分页获取用发起的审批 ***/api/WorkFlow/GetWorkTasksOfCreator***
+ - ***CurrentPage*** 当前页(默认从1开始)
+ - ***MaxResultCount*** 分页大小
+ - 返回流程审批集合.(属性参考 **新建审批** ***/api/WorkFlow/CreateWorkTask***)
+
+ - #### 接口现状
+ - 目前已实现的接口只能保证基本的流程运作,实际上应用过程可能还有更多的场景没考虑到。特别是对于查询接口,这就需要根据业务自己实现。
+
+ ## 扩展点
+
+ ### 用户选择器
+ 通过自定义实现 IUserSelector 接口实现所需的用户选择器。
+ - 实现插件(具体参考 Plugins/UserSelectors/UserSelector_PluginDemo)
+ - 新建自定义类库
+ - 引入项目依赖 WorkFlowCore,或者手动引入dll (WorkFlowCore.Common.dll,WorkFlowCore.dll)
+ - 实现自定义逻辑。并创建对应的 manifest.json
+ ```
+ {
+ "Entry": "Condition_PluginDemo.dll", //入口文件dll
+ "Version": "1.0" //版本号(目前没应用到,预留)
+ }
+
+ ```
+ - 编译生成。并将生成的文件按如下结构按如下格式复制到 Host发布目录下 Plugins\UserSelectors
+ 
+
+ - 重启(启动) Host 服务
+ - 目前插件的自定义实现类暂时还无法通过ioc 注入参数。除非插件自己维护 ServiceProvider。
+
+
+
+
+ ### 条件处理器
+ 通过自定义实现 ICondition 接口实现所需的用户选择器。
+ - 实现插件(具体参考 Plugins/Conditions/UserSelector_PluginDemo)
+ - 其他步骤类似用户选择器。只是生成后的插件导入到Host 的路径为 Plugins\Conditions
+
+-
+
+### 自定义session 实现
+- 自定义实现 IWorkflowSession 接口解析当前请求的用户信息。
+ - **IWorkflowSession** 的抽象在 **WorkFlowCore.Authorization** 。在**WorkFlowCore.Framework** 实现自定义的 session,并在 **WorkFlowCoreFrameworkService** 替换现有的注册:
+ ```
+ services.Replace(new ServiceDescriptor(typeof(IWorkflowSession),typeof(DefaultSession), ServiceLifetime.Scoped));
+ ```
+
+### 切换数据库读写工具
+- 目前默认使用 EF 进行数据库读写。参考(***项目运行***切换)。
+- 如需要实现其他数据库工具,则需要在 **WorkFlowCore.Framework** 实现 WorkflowCore.IRepositories 下的所有接口。并在**WorkFlowCoreFrameworkService** 替换现有的注册,类似:
+ ```
+ if (conf.OrmType== FrameworkConfigOrmType.Default)
+ {
+ services.AddScoped(typeof(IBasicRepository<,>), typeof(BasicRepository<,>));
+ services.AddScoped(typeof(IBasicRepository<>), typeof(BasicRepository<>));
+ services.AddScoped(typeof(IWorkStepRepository), typeof(WorkStepRepository));
+ services.AddScoped(typeof(IWorkTaskRepository), typeof(WorkTaskRepository));
+ services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));
+ services.AddScoped(typeof(UnitOfWork));
+ services.AddScoped(typeof(IUnitOfWorkManager), typeof(UnitOfWorkManager));
+ }
+ else if (conf.OrmType == FrameworkConfigOrmType.EF)
+ {
+ services.AddScoped(typeof(IBasicRepository<,>), typeof(BasicRepository4EF<,>));
+ services.AddScoped(typeof(IBasicRepository<>), typeof(BasicRepository4EF<>));
+ services.AddScoped(typeof(IWorkStepRepository), typeof(WorkStepRepository4EF));
+ services.AddScoped(typeof(IWorkTaskRepository), typeof(WorkTaskRepository4EF));
+ services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork4EF));
+ services.AddScoped(typeof(IUnitOfWorkManager), typeof(UnitOfWorkManager4EF));
+ }
+ ```
+
+ ### 自定义 EventBus
+ - 实现 **WorkFlowCore.Common.EventBus** 下 ***IEventBus*** 接口,参考 **DefaultEventBus** 实现。并在 **EventBusService** 进行服务的注册扩展。之后在 Setup services 配置载入即可。
+
+
+
+
\ No newline at end of file
diff --git a/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/ConditionDemo.cs b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/ConditionDemo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..89725344b6ffe00d75c0ead0875586a52221306e
--- /dev/null
+++ b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/ConditionDemo.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WorkFlowCore.Conditions;
+
+namespace Condition_PluginDemo
+{
+ [Condition("条件处理器插件demo", "这只是一个demo,当传入参数(parameter)为 'true'(包括引号) 时通过")]
+ public class ConditionDemo : ICondition
+ {
+ public bool CanAccept(ConditionInput input)
+ {
+ return "true".Equals(input.Expression);
+ }
+ }
+}
diff --git a/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/Condition_PluginDemo.csproj b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/Condition_PluginDemo.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..6b5d6145a2ca37287350f07365b67a5ac3a7e968
--- /dev/null
+++ b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/Condition_PluginDemo.csproj
@@ -0,0 +1,28 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+ libs\WorkFlowCore.dll
+
+
+ libs\WorkFlowCore.Common.dll
+
+
+
+
+
+ Never
+
+
+ Never
+
+
+ Always
+
+
+
+
diff --git a/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/libs/WorkFlowCore.Common.dll b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/libs/WorkFlowCore.Common.dll
new file mode 100644
index 0000000000000000000000000000000000000000..bfd62e75139d867f57cb32dbb2d605d6540edba1
Binary files /dev/null and b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/libs/WorkFlowCore.Common.dll differ
diff --git a/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/libs/WorkFlowCore.dll b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/libs/WorkFlowCore.dll
new file mode 100644
index 0000000000000000000000000000000000000000..9bfdd622f58d698ec76425fe747afb0b59bb99e1
Binary files /dev/null and b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/libs/WorkFlowCore.dll differ
diff --git a/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/manifest.json b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..74868514c333f353eda60c245c4db86a6e62a36e
--- /dev/null
+++ b/WorkFlowCore/Plugins/Conditions/Condition_PluginDemo/manifest.json
@@ -0,0 +1,4 @@
+{
+ "Entry": "Condition_PluginDemo.dll",
+ "Version": "1.0"
+}
diff --git a/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/UserSelectorDemo.cs b/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/UserSelectorDemo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4f907240b0921d208c7190aceffa1b2630be29fc
--- /dev/null
+++ b/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/UserSelectorDemo.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WorkFlowCore.UserSelectors;
+
+namespace UserSelector_PluginDemo
+{
+ [UserSelector("用户选择器插件demo","这是一个插件demo")]
+ public class UserSelectorDemo : IUserSelector
+ {
+ private static List selections = null;
+ private static Dictionary> selectionUsers = null;
+
+ static UserSelectorDemo()
+ {
+ selections = new List();
+ selectionUsers = new Dictionary>();
+ for (int i = 0; i < 4; i++)
+ {
+ selections.Add(new Selection { Id = i.ToString(), Name = $"类型选项{i}" });
+ var users = new List();
+ for (int j = 0; j < 5; j++)
+ {
+ users.Add(new User($"UserSelectorDemo-{i}-{j}", $"选项{i}-用{j}"));
+ }
+ selectionUsers.Add(i.ToString(), users);
+ }
+ }
+
+ public List GetSelections()
+ {
+ return selections;
+ }
+
+ public List GetUsers(SelectorInput input)
+ {
+ try
+ {
+ return selectionUsers[input.SelectionId];
+ }
+ catch (Exception)
+ {
+ return new List();
+ }
+ }
+ }
+}
diff --git a/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/UserSelector_PluginDemo.csproj b/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/UserSelector_PluginDemo.csproj
new file mode 100644
index 0000000000000000000000000000000000000000..f295c94724587307610a4bc8a061c130349c7e9b
--- /dev/null
+++ b/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/UserSelector_PluginDemo.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
diff --git a/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/manifest.json b/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..6c978b771b68f4fdf093d58c0e04a54436ae036f
--- /dev/null
+++ b/WorkFlowCore/Plugins/UserSelectors/UserSelector_PluginDemo/manifest.json
@@ -0,0 +1,4 @@
+{
+ "Entry": "UserSelector_PluginDemo.dll",
+ "Version": "1.0"
+}
diff --git "a/WorkFlowCore/ReadmeImges/\347\224\250\346\210\267\351\200\211\346\213\251\345\231\250\346\217\222\344\273\266\347\233\256\345\275\225.png" "b/WorkFlowCore/ReadmeImges/\347\224\250\346\210\267\351\200\211\346\213\251\345\231\250\346\217\222\344\273\266\347\233\256\345\275\225.png"
new file mode 100644
index 0000000000000000000000000000000000000000..a19623551f2c8b66a8043136e5e79fb513467648
Binary files /dev/null and "b/WorkFlowCore/ReadmeImges/\347\224\250\346\210\267\351\200\211\346\213\251\345\231\250\346\217\222\344\273\266\347\233\256\345\275\225.png" differ
diff --git a/WorkFlowCore/WorkFlowCore.Common/EventBus/EventBusService.cs b/WorkFlowCore/WorkFlowCore.Common/EventBus/EventBusService.cs
index 25dbe32bae0a37654d6c5954fb0c3032a8e9e1dc..30e4b492209a2e88269174092e128213b3352f6f 100644
--- a/WorkFlowCore/WorkFlowCore.Common/EventBus/EventBusService.cs
+++ b/WorkFlowCore/WorkFlowCore.Common/EventBus/EventBusService.cs
@@ -34,19 +34,27 @@ namespace WorkFlowCore.Common.EventBus
return services;
}
public static IApplicationBuilder InitGlobalEventBus(this IApplicationBuilder app)
+ {
+ InitGlobalEventBus(app.ApplicationServices);
+
+ return app;
+
+ }
+
+ public static IServiceProvider InitGlobalEventBus(this IServiceProvider serviceProvider)
{
//注册普通事件,该事件订阅在单应用有效无法分布式
- EventBusManager.Init(app.ApplicationServices);
+ EventBusManager.Init(serviceProvider);
//注册kafka作为分布式事件
- var kafkaEventBus = app.ApplicationServices.GetService();
- var config = app.ApplicationServices.GetService();
- var configuration = app.ApplicationServices.GetService();
+ var kafkaEventBus = serviceProvider.GetService();
+ var config = serviceProvider.GetService();
+ var configuration = serviceProvider.GetService();
Console.WriteLine("servers:" + configuration["KafkaBootstrapServers"]);
- if (kafkaEventBus!=null&&config!=null && config.RegisterAssemblies != null)
+ if (kafkaEventBus != null && config != null && config.RegisterAssemblies != null)
kafkaEventBus.RegistSubscriptions(config.RegisterAssemblies);
- return app;
+ return serviceProvider;
}
}
diff --git a/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/Manifest.cs b/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/Manifest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9e4e8a41ca6ef8cf3ac87e320a024fb20042ae3f
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/Manifest.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace WorkFlowCore.Common.SimplePluginLoaders
+{
+ public class Manifest
+ {
+ public string Entry { get; set; }
+ public string Version { get; set; }
+ public Assembly Assembly { get; set; }
+ }
+}
diff --git a/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/PluginAssemblyLoadContext.cs b/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/PluginAssemblyLoadContext.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c59ad9efb9eafcc06322ae83e3a9e18064017156
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/PluginAssemblyLoadContext.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.Loader;
+using System.Text;
+
+namespace WorkFlowCore.Common.SimplePluginLoaders
+{
+ public class PluginAssemblyLoadContext : AssemblyLoadContext
+ {
+ private AssemblyDependencyResolver _resolver;
+
+ public PluginAssemblyLoadContext(string mainAssemblyToLoadPath) : base(isCollectible: true)
+ {
+ _resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath);
+ }
+
+ protected override Assembly Load(AssemblyName name)
+ {
+ string assemblyPath = _resolver.ResolveAssemblyToPath(name);
+ if (assemblyPath != null)
+ {
+ //***这里需要使用 Default 来载入程序集,因为有共享 程序集。否则会出现类型转换问题***
+ //https://learn.microsoft.com/zh-cn/dotnet/core/dependency-loading/understanding-assemblyloadcontext#shared-dependencies
+ return Default.LoadFromAssemblyPath(assemblyPath);
+ }
+ return base.Load(name);
+ }
+ }
+}
diff --git a/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/SimplePluginLoader.cs b/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/SimplePluginLoader.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f0d4b919cc3c678682477c1330c576b521920817
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Common/SimplePluginLoaders/SimplePluginLoader.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+
+namespace WorkFlowCore.Common.SimplePluginLoaders
+{
+ public static class SimplePluginLoader
+ {
+ ///
+ /// 载入插件
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IServiceCollection LoadPlugins(this IServiceCollection services, string pluginDir, Action> loaded) where TManifest : Manifest
+ {
+ if (!Directory.Exists(pluginDir)) return services;
+
+ var plugins = new List();
+
+ var dirs = Directory.GetDirectories(pluginDir);
+ foreach (var dir in dirs)
+ {
+ var manifestPath = Path.Combine(dir, "manifest.json");
+ if (!File.Exists(manifestPath)) continue;
+ try
+ {
+ var manifest = JsonConvert.DeserializeObject(File.ReadAllText(manifestPath));
+ if (manifest == null) continue;
+
+ var context = new PluginAssemblyLoadContext(System.IO.Path.Combine(dir, "src", manifest.Entry));
+ var EntryLib = manifest.Entry;
+ var assembly = context.LoadFromAssemblyName(new System.Reflection.AssemblyName(EntryLib.Substring(0, EntryLib.Length - 4)));
+ manifest.Assembly = assembly;
+ plugins.Add(manifest);
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"{ex.Message},{ex}");
+ //
+ }
+
+ }
+ loaded?.Invoke(plugins);
+
+ return services;
+ }
+
+ public static IServiceCollection LoadPlugins(this IServiceCollection services, string pluginDir, Action> loaded)
+ {
+ if (!Directory.Exists(pluginDir)) Directory.CreateDirectory(pluginDir);
+ return LoadPlugins(services, pluginDir, loaded);
+ }
+ }
+}
diff --git a/WorkFlowCore/WorkFlowCore.Framework/Repositories/WorkTaskRepository.cs b/WorkFlowCore/WorkFlowCore.Framework/Repositories/WorkTaskRepository.cs
index 031f57d8308026a9b05919bc06333328e0b4f2c0..8517ebe07e9015e1c136fa1a0adaddd460e585bf 100644
--- a/WorkFlowCore/WorkFlowCore.Framework/Repositories/WorkTaskRepository.cs
+++ b/WorkFlowCore/WorkFlowCore.Framework/Repositories/WorkTaskRepository.cs
@@ -93,5 +93,23 @@ namespace WorkFlowCore.Framework.Repositories
else result.Items = (await GetListAsync(wt => workTaskIds.Contains(wt.Id))).OrderByDescending(ws => ws.CreationTime).Select(ws => ws.ToWorkTask()).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
return await Task.FromResult(result);
}
+
+ public async Task> GetWorkTasksOfCreatorAsync(string userId, int pageIndex = 1, int pageSize = -1)
+ {
+ var workTaskIds = (await workStepRepository.GetListAsync(ws => ws.CreatedUserId == userId && ws.IsHandled)).Select(ws => ws.WorkTaskId);
+
+ var workTasks = (await GetListAsync(wt => workTaskIds.Contains(wt.Id) && !wt.IsSimulation)).OrderByDescending(ws => ws.CreationTime);
+
+ var result = new PageResult
+ {
+ Total = await GetCountAsync(wt => workTaskIds.Contains(wt.Id) && !wt.IsSimulation)
+ };
+
+ if (pageSize < 1)
+ result.Items = (await Task.FromResult(workTasks.Select(w => w.ToWorkTask()))).ToList();
+ else result.Items = (await Task.FromResult(workTasks.Select(w => w.ToWorkTask()).Skip((pageIndex - 1) * pageSize).Take(pageSize))).ToList();
+
+ return await Task.FromResult(result);
+ }
}
}
diff --git a/WorkFlowCore/WorkFlowCore.Framework/Repositories4EF/WorkTaskRepository4EF.cs b/WorkFlowCore/WorkFlowCore.Framework/Repositories4EF/WorkTaskRepository4EF.cs
index 071650f60113ed242a66587c802ac4251e4067ef..bc02c86de87e194aac00ddabf0dc6ee2686ffe80 100644
--- a/WorkFlowCore/WorkFlowCore.Framework/Repositories4EF/WorkTaskRepository4EF.cs
+++ b/WorkFlowCore/WorkFlowCore.Framework/Repositories4EF/WorkTaskRepository4EF.cs
@@ -101,5 +101,18 @@ namespace WorkFlowCore.Framework.Repositories4EF
else result.Items = worktaskQuery.Skip((pageIndex - 1) * pageSize).Take(pageSize).OrderByDescending(w => w.CreationTime).Select(w => w.ToWorkTask()).ToList();
return await Task.FromResult(result);
}
+
+ public async Task> GetWorkTasksOfCreatorAsync(string userId, int pageIndex = 1, int pageSize = -1)
+ {
+ var worktaskQuery = workflowDbContext.Set().Where(wt => !wt.Deleted && wt.CreatedUserId == userId && !wt.IsSimulation);
+ var result = new PageResult
+ {
+ Total = worktaskQuery.Count()
+ };
+ if (pageSize < 1)
+ result.Items = worktaskQuery.Select(w => w.ToWorkTask()).ToList();
+ else result.Items = worktaskQuery.Skip((pageIndex - 1) * pageSize).Take(pageSize).OrderByDescending(w => w.CreationTime).Select(w => w.ToWorkTask()).ToList();
+ return await Task.FromResult(result);
+ }
}
}
diff --git a/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCore.Framework.csproj b/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCore.Framework.csproj
index a9f729199920ecf760be2e519e226be47d6adbc2..aae58818df1f0ac7915446147b6c3ca90d6b158e 100644
--- a/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCore.Framework.csproj
+++ b/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCore.Framework.csproj
@@ -6,6 +6,7 @@
+
diff --git a/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCoreFrameworkService.cs b/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCoreFrameworkService.cs
index 23c5b77cd82fb0cac78dd3684b279eda872c33b3..a8f72cd349bae393f88de95582135c8459538281 100644
--- a/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCoreFrameworkService.cs
+++ b/WorkFlowCore/WorkFlowCore.Framework/WorkFlowCoreFrameworkService.cs
@@ -1,8 +1,16 @@
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
using System.Text;
+using WorkFlowCore.Authorization;
+using WorkFlowCore.Common.SimplePluginLoaders;
using WorkFlowCore.Conditions;
+using WorkFlowCore.Framework.Authorization;
using WorkFlowCore.Framework.Conditions;
using WorkFlowCore.Framework.EventHandlers;
using WorkFlowCore.Framework.Repositories;
@@ -53,21 +61,36 @@ namespace WorkFlowCore.Framework
services.AddScoped(typeof(IUnitOfWorkManager), typeof(UnitOfWorkManager4EF));
}
-
+ services.AddSingleton();
//事件处理
services.AddScoped();
services.AddScoped();
var assembly = typeof(WorkFlowCoreFrameworkService).Assembly;
+ services.Replace(new ServiceDescriptor(typeof(IWorkflowSession),typeof(DefaultSession), ServiceLifetime.Scoped));
//注册条件和选择器
- UserSelectorManager.RegisterSelector(assembly);
- ConditionManager.RegisterCondition(assembly);
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
- services.AddScoped();
+ #region 注册条件和选择器
+ UserSelectorManager.RegisterSelector(services, assembly);
+ services.LoadPlugins(Path.Combine(AppContext.BaseDirectory, "Plugins", "UserSelectors"), plugins =>
+ {
+ UserSelectorManager.RegisterSelector(services, plugins.Select(p => p.Assembly).ToArray());
+
+ });
+ ConditionManager.Registercondition(services, assembly);
+
+ services.LoadPlugins(Path.Combine(AppContext.BaseDirectory, "Plugins", "Conditions"), plugins =>
+ {
+ ConditionManager.Registercondition(services, plugins.Select(p => p.Assembly).ToArray());
+ });
+ #endregion
+
+
+ //services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
+ //services.AddScoped();
}
}
}
diff --git a/WorkFlowCore/WorkFlowCore.Host/Controllers/WorkFlowController.cs b/WorkFlowCore/WorkFlowCore.Host/Controllers/WorkFlowController.cs
index fa3be5cc9d95af382e890f60d14545d1cefde5f8..53c48a569d3201e9aff7aaaaf5a0f62ada4244c3 100644
--- a/WorkFlowCore/WorkFlowCore.Host/Controllers/WorkFlowController.cs
+++ b/WorkFlowCore/WorkFlowCore.Host/Controllers/WorkFlowController.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using WorkFlowCore.Authorization;
using WorkFlowCore.Conditions;
using WorkFlowCore.Framework.UserSelectors;
using WorkFlowCore.Host.ViewModels;
@@ -11,6 +12,7 @@ using WorkFlowCore.IRepositories;
using WorkFlowCore.UserSelectors;
using WorkFlowCore.Workflows;
using WorkFlowCore.WorkTasks;
+using Mapster;
namespace WorkFlowCore.Host.Controllers
{
@@ -21,12 +23,13 @@ namespace WorkFlowCore.Host.Controllers
private ConditionManager conditionManager;
private WorkflowManager workflowManager;
private IBasicRepository workflowRepository;
- private IBasicRepository worktaskRepository;
+ private IWorkTaskRepository worktaskRepository;
private IBasicRepository versionRepository;
private IBasicRepository workStepRepository;
+ private IWorkflowSession workflowSession;
- public WorkFlowController(ConditionManager conditionManager, WorkflowManager workflowManager, IBasicRepository workflowRepository, IBasicRepository worktaskRepository, IBasicRepository versionRepository, IBasicRepository workStepRepository)
+ public WorkFlowController(ConditionManager conditionManager, WorkflowManager workflowManager, IBasicRepository workflowRepository, IWorkTaskRepository worktaskRepository, IBasicRepository versionRepository, IBasicRepository workStepRepository)
{
this.conditionManager = conditionManager;
this.workflowManager = workflowManager;
@@ -115,7 +118,7 @@ namespace WorkFlowCore.Host.Controllers
///
///
///
- [HttpGet("DeleteWorkflowVersion")]
+ [HttpDelete("DeleteWorkflowVersion")]
public async Task> GetWorkflowVersion(Guid id)
{
var result = await workflowManager.DeleteWorkflow(id);
@@ -138,7 +141,7 @@ namespace WorkFlowCore.Host.Controllers
/// 更新流程
///
///
- ///
+ /// f
[HttpPut("UpdateWorkFlow")]
public async Task> UpdateWorkFlow(UpdateWorkFlowInput input)
{
@@ -160,6 +163,18 @@ namespace WorkFlowCore.Host.Controllers
return OutputDto.Succeed(worktask);
}
+ ///
+ /// 创建流程任务
+ ///
+ ///
+ ///
+ [HttpPost("CreateSimulationWorkTask")]
+ public async Task> CreateSimulationWorkTask(CreateWorkTaskInput input)
+ {
+ var worktask = await workflowManager.CreateSimulationWorkTask(input.WorkflowId, input.Name, input.FormData, input.EntityFullName, input.EntityKeyValue, input.CreatedUserId);
+ return OutputDto.Succeed(worktask);
+ }
+
///
/// 获取流程任务
///
@@ -272,8 +287,93 @@ namespace WorkFlowCore.Host.Controllers
{
return OutputDto.Succeed>(UserList.Users.Select(u=>new UserSelectors.User { Id=u.Id,Name=u.Name}).ToList());
}
-
-
+ ///
+ /// 获取所有审批步骤
+ ///
+ ///
+ ///
+ [HttpGet("GetAllTaskStepsOfWorkTaskByEntityInfo")]
+ public async Task> GetAllTaskStepsOfWorkTaskByEntityInfo(string entityFullName, string entityKeyValue)
+ {
+ //获取所有过程输出
+ var historySteps = await workflowManager.GetAllTaskStepsOfWorkTaskByEntityInfoAsync(entityFullName, entityKeyValue);
+ return historySteps;
+ }
+
+
+
+ private async Task FillWorktaskInfo(List workTasks)
+ {
+ var workflows = await workflowRepository.GetListAsync(w => workTasks.Select(s => s.WorkflowId.Id).Contains(w.Id));
+ workTasks.ForEach(i =>
+ {
+ i.WorkflowId.WorkflowName = workflows.FirstOrDefault(w => w.Id == i.WorkflowId.Id && w.ActiveVersion == i.WorkflowId.VersionId)?.Name;
+ });
+ }
+
+ ///
+ /// 获取用户待处理的流程
+ ///
+ ///
+ [HttpGet("GetUnHandledWorkTasksOfUser")]
+ public async Task> GetUnHandledWorkTasksOfUser([FromQuery] GetUnHandledWorkTasksOfUserInput input)
+ {
+ var worktasks = await workflowManager.GetUnHandledWorkTasksOfUserAsync(workflowSession.User.Id, input.CurrentPage, input.MaxResultCount);
+ var result = new PageResult
+ {
+ Items = worktasks.Items.Adapt>(),// mapper.Map, List>(worktasks.Items),
+ Total = worktasks.Total
+ };
+ await FillWorktaskInfo(result.Items);
+
+ return result;
+
+ }
+
+ ///
+ /// 获取用户已处理的流程
+ ///
+ ///
+ [HttpGet("GetHandledWorkTasksOfUser")]
+ public async Task> GetHandledWorkTasksOfUser([FromQuery] GetHandledWorkTasksOfUserInput input)
+ {
+ var worktasks = await workflowManager.GetHandledWorkTasksOfUserAsync(workflowSession.User.Id, input.CurrentPage, input.MaxResultCount);
+ var result = new PageResult
+ {
+ Items = worktasks.Items.Adapt>(),// mapper.Map, List>(worktasks.Items),
+ Total = worktasks.Total
+ };
+
+ await FillWorktaskInfo(result.Items);
+
+ return result;
+ }
+ ///
+ /// 根据实体类型获取所有的处理中的工作流
+ ///
+ ///
+ ///
+ [HttpGet("GetAllProcessingWorkTasksByEntityType")]
+ public async Task> GetAllProcessingWorkTasksByEntityType([FromQuery] GetAllProcessingWorkTasksByEntityTypeInput input)
+ {
+ var worktaskInfos = await worktaskRepository.GetListAsync(wt => input.EntityKeyValues.Contains(wt.EntityKeyValue) && !wt.Deleted && wt.EntityFullName == input.EntityFullName);
+ return worktaskInfos.Select(wt => wt.ToWorkTask()).ToList();
+ }
+ [HttpGet("GetWorkTasksOfCreator")]
+ public async Task> GetWorkTasksOfCreator([FromQuery] GetWorkTasksOfCreatorInput input)
+ {
+ var worktasks = await worktaskRepository.GetWorkTasksOfCreatorAsync(workflowSession.User.Id, input.CurrentPage, input.MaxResultCount);
+ var result = new PageResult
+ {
+ Items = worktasks.Items.Adapt>(), //mapper.Map, List>(worktasks.Items),
+ Total = worktasks.Total
+ };
+
+ await FillWorktaskInfo(result.Items);
+
+ return result;
+ }
+
}
}
diff --git a/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetAllProcessingWorkTasksByEntityTypeInput.cs b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetAllProcessingWorkTasksByEntityTypeInput.cs
new file mode 100644
index 0000000000000000000000000000000000000000..86900b8dddc91b42803fc52190e44ed1ff2a7f9c
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetAllProcessingWorkTasksByEntityTypeInput.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WorkFlowCore.Host.ViewModels.WorkFlowCore
+{
+ public class GetAllProcessingWorkTasksByEntityTypeInput
+ {
+ public string EntityFullName { get; set; }
+ public string[] EntityKeyValues { get;set; }
+ }
+}
diff --git a/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetHandledWorkTasksOfUserInput.cs b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetHandledWorkTasksOfUserInput.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1edbcf404a8e8fc4978aeaadfcecc4810c7dde54
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetHandledWorkTasksOfUserInput.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace WorkFlowCore.Host.ViewModels.WorkFlowCore
+{
+ public class GetHandledWorkTasksOfUserInput
+ {
+ public int CurrentPage { get; set; } = 1;
+ public int MaxResultCount { get; set; } = -1;
+ }
+
+}
diff --git a/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetUnHandlerWorkTasksOfUserInput.cs b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetUnHandlerWorkTasksOfUserInput.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7571e42409f84e52bcdf465f76c8c6f1825ae60f
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetUnHandlerWorkTasksOfUserInput.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace WorkFlowCore.Host.ViewModels.WorkFlowCore
+{
+ public class GetUnHandledWorkTasksOfUserInput
+ {
+ public int CurrentPage { get; set; } = 1;
+ public int MaxResultCount { get; set; } = -1;
+ }
+
+}
diff --git a/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetWorkTasksOfCreatorInput.cs b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetWorkTasksOfCreatorInput.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eb559da687e3cd6ba1cbdf2e7cabf13ebfe89e4b
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/GetWorkTasksOfCreatorInput.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace WorkFlowCore.Host.ViewModels.WorkFlowCore
+{
+ public class GetWorkTasksOfCreatorInput
+ {
+ public int CurrentPage { get; set; } = 1;
+ public int MaxResultCount { get; set; } = -1;
+ }
+
+}
diff --git a/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/WorkTaskDto.cs b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/WorkTaskDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6df6e7af33874e6e758daee17fe578894b63df25
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/WorkTaskDto.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WorkFlowCore.IRepositories;
+using WorkFlowCore.Workflows;
+using WorkFlowCore.WorkTasks;
+
+namespace WorkFlowCore.Host.ViewModels.WorkFlowCore
+{
+ ///
+ /// 工作流dto
+ ///
+ public class WorkTaskDto: WithBaseInfoEntity
+ {
+ ///
+ /// 流程id
+ ///
+ public WorkflowIdDto WorkflowId { get; set; }
+ ///
+ /// 任务名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 表单数据(json)
+ ///
+ public string FormData { get; set; }
+ ///
+ /// 实体全称
+ ///
+ public string EntityFullName { get; set; }
+ ///
+ /// 实体主键值
+ ///
+ public string EntityKeyValue { get; set; }
+ ///
+ /// 审批状态
+ ///
+ public WorkTaskStatus WorkTaskStatus { get; set; } = WorkTaskStatus.Pending;
+
+ public bool IsProcessed { get => WorkTaskStatus == WorkTaskStatus.Processed; }
+ public bool IsPending { get => WorkTaskStatus == WorkTaskStatus.Pending; }
+ public bool IsProcessing { get => WorkTaskStatus == WorkTaskStatus.Processing; }
+ public bool IsSimulation { get; set; }
+ }
+}
diff --git a/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/WorkflowIdDto.cs b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/WorkflowIdDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..eedcbefcf91896afea1fe9b5dbafc9b865adaf57
--- /dev/null
+++ b/WorkFlowCore/WorkFlowCore.Host/ViewModels/WorkFlowCore/WorkflowIdDto.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WorkFlowCore.Workflows;
+
+namespace WorkFlowCore.Host.ViewModels.WorkFlowCore
+{
+ public class WorkflowIdDto: WorkflowId
+ {
+ ///
+ /// 流程名称
+ ///
+ public string WorkflowName { get; set; }
+ }
+}
diff --git a/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.csproj b/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.csproj
index 9c9fb5dac9021baf4cd21f9433f7653ed03d0c95..f5b6934874c780bdf4ba1ba5f05fe4e0d0ddf0cb 100644
--- a/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.csproj
+++ b/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.csproj
@@ -15,6 +15,8 @@
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.xml b/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.xml
index ddbc8fbba4e083c5203d26bae83019db604f72cf..896c91c19714e547fdc2b26d28004de893c84e58 100644
--- a/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.xml
+++ b/WorkFlowCore/WorkFlowCore.Host/WorkFlowCore.Host.xml
@@ -69,7 +69,7 @@
更新流程
-
+ f
@@ -78,6 +78,13 @@
+
+
+ 创建流程任务
+
+
+
+
获取流程任务
@@ -139,6 +146,32 @@
+
+
+ 获取所有审批步骤
+
+
+
+
+
+
+ 获取用户待处理的流程
+
+
+
+
+
+ 获取用户已处理的流程
+
+
+
+
+
+ 根据实体类型获取所有的处理中的工作流
+
+
+
+
流程id
@@ -234,6 +267,11 @@
流程连线
+
+
+ 流程名称
+
+
绘制信息,前端绘制所需信息
@@ -269,5 +307,40 @@
拒绝节点
+
+
+ 工作流dto
+
+
+
+
+ 流程id
+
+
+
+
+ 任务名称
+
+
+
+
+ 表单数据(json)
+
+
+
+
+ 实体全称
+
+
+
+
+ 实体主键值
+
+
+
+
+ 审批状态
+
+
diff --git a/WorkFlowCore/WorkFlowCore.Host/appsettings.Development.json b/WorkFlowCore/WorkFlowCore.Host/appsettings.Development.json
index b58cdf3c21c122c76319378af36480362036fee5..678228f8bab6813952ae0db9bf01aa82bb8f2770 100644
--- a/WorkFlowCore/WorkFlowCore.Host/appsettings.Development.json
+++ b/WorkFlowCore/WorkFlowCore.Host/appsettings.Development.json
@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
- "Default": "Database=Workflow;Data Source=81.71.14.205;Port=3308;UserId=root;Password=ShiHuiDai123!;Charset=utf8;TreatTinyAsBoolean=false;Allow User Variables=True",
+ "Default": "Database=WorkflowCore;Data Source=39.101.74.14;Port=3308;UserId=root;Password=ShiHuiDai123!;Charset=utf8;TreatTinyAsBoolean=false;Allow User Variables=True",
"DefaultVersion": "8.0.26"
},
"Logging": {
diff --git a/WorkFlowCore/WorkFlowCore.Test/WorkFlowCore.Test.csproj b/WorkFlowCore/WorkFlowCore.Test/WorkFlowCore.Test.csproj
index d518e33520471501937e81580efdd2658e6dc1a3..4d6037e44f559b55dfd9a5da5758b6e245583c45 100644
--- a/WorkFlowCore/WorkFlowCore.Test/WorkFlowCore.Test.csproj
+++ b/WorkFlowCore/WorkFlowCore.Test/WorkFlowCore.Test.csproj
@@ -7,6 +7,7 @@
+
diff --git a/WorkFlowCore/WorkFlowCore.Test/Workflow/Workflow_Test.cs b/WorkFlowCore/WorkFlowCore.Test/Workflow/Workflow_Test.cs
index 2e32235cde7b36a4d00e31cb9316092a87539426..c27856715f612e9a22857c70f78abdaf3ba35057 100644
--- a/WorkFlowCore/WorkFlowCore.Test/Workflow/Workflow_Test.cs
+++ b/WorkFlowCore/WorkFlowCore.Test/Workflow/Workflow_Test.cs
@@ -1,10 +1,12 @@
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using WorkFlowCore.Common.EventBus;
using WorkFlowCore.Conditions;
using WorkFlowCore.Framework;
using WorkFlowCore.Framework.Repositories;
@@ -37,12 +39,20 @@ namespace WorkFlowCore.Test.Workflow
services.AddScoped();
services.AddScoped();
+ var configurationBuilder = new ConfigurationBuilder();
+ var configuration = configurationBuilder.Build();
+ services.AddSingleton(configuration);
+
+ services.AddDefautEventBus(typeof(WorkFlowCoreFrameworkService).Assembly);
+
+
BasicRepository.ClearData();
BasicRepository.ClearData();
BasicRepository.ClearData();
BasicRepository.ClearData();
serviceProvider = services.BuildServiceProvider();
+ serviceProvider.InitGlobalEventBus();
}
[Test]
diff --git a/WorkFlowCore/WorkFlowCore.sln b/WorkFlowCore/WorkFlowCore.sln
index 716539896243e2e45a6a613ee77d0af92b11d0e9..59c4e831c6b7e40ff2d7dca974b9bb2204aa8d4b 100644
--- a/WorkFlowCore/WorkFlowCore.sln
+++ b/WorkFlowCore/WorkFlowCore.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30413.136
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32516.85
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlowCore", "WorkFlowCore\WorkFlowCore.csproj", "{8B678BB1-F787-448D-8714-4D34E165B4DF}"
EndProject
@@ -11,7 +11,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlowCore.Test", "WorkFl
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlowCore.Host", "WorkFlowCore.Host\WorkFlowCore.Host.csproj", "{1C0D8C51-D49C-4C1D-9C66-1522546360E0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkFlowCore.Common", "WorkFlowCore.Common\WorkFlowCore.Common.csproj", "{8F14A61F-C202-4ED1-A167-B426EE428DDA}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkFlowCore.Common", "WorkFlowCore.Common\WorkFlowCore.Common.csproj", "{8F14A61F-C202-4ED1-A167-B426EE428DDA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{3D75B3EF-C8FF-42E0-A3B0-A0C63FDC6110}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UserSelectors", "UserSelectors", "{F371EB9A-F50A-4FF3-BBC4-1C9C50AF4D78}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Conditions", "Conditions", "{A81DC2DC-4670-40CE-AFA8-708A9E05E93D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserSelector_PluginDemo", "Plugins\UserSelectors\UserSelector_PluginDemo\UserSelector_PluginDemo.csproj", "{D018E18A-B871-4C25-8A79-44EFF4A76228}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Condition_PluginDemo", "Plugins\Conditions\Condition_PluginDemo\Condition_PluginDemo.csproj", "{83CEABEA-4230-4C5E-AA80-D3EC302B7D19}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -39,10 +49,24 @@ Global
{8F14A61F-C202-4ED1-A167-B426EE428DDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F14A61F-C202-4ED1-A167-B426EE428DDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F14A61F-C202-4ED1-A167-B426EE428DDA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D018E18A-B871-4C25-8A79-44EFF4A76228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D018E18A-B871-4C25-8A79-44EFF4A76228}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D018E18A-B871-4C25-8A79-44EFF4A76228}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D018E18A-B871-4C25-8A79-44EFF4A76228}.Release|Any CPU.Build.0 = Release|Any CPU
+ {83CEABEA-4230-4C5E-AA80-D3EC302B7D19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {83CEABEA-4230-4C5E-AA80-D3EC302B7D19}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {83CEABEA-4230-4C5E-AA80-D3EC302B7D19}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {83CEABEA-4230-4C5E-AA80-D3EC302B7D19}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {F371EB9A-F50A-4FF3-BBC4-1C9C50AF4D78} = {3D75B3EF-C8FF-42E0-A3B0-A0C63FDC6110}
+ {A81DC2DC-4670-40CE-AFA8-708A9E05E93D} = {3D75B3EF-C8FF-42E0-A3B0-A0C63FDC6110}
+ {D018E18A-B871-4C25-8A79-44EFF4A76228} = {F371EB9A-F50A-4FF3-BBC4-1C9C50AF4D78}
+ {83CEABEA-4230-4C5E-AA80-D3EC302B7D19} = {A81DC2DC-4670-40CE-AFA8-708A9E05E93D}
+ EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B47C6B7-D14F-4E7A-AC89-493A7F10350B}
EndGlobalSection
diff --git a/WorkFlowCore/WorkFlowCore/Conditions/ConditionManager.cs b/WorkFlowCore/WorkFlowCore/Conditions/ConditionManager.cs
index df1c17c277f3c60667a4c022d02247ed736d15e3..7aa1e8721282b78620ae61807930ad7a671d69c5 100644
--- a/WorkFlowCore/WorkFlowCore/Conditions/ConditionManager.cs
+++ b/WorkFlowCore/WorkFlowCore/Conditions/ConditionManager.cs
@@ -1,4 +1,5 @@
-using System;
+using Microsoft.Extensions.DependencyInjection;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -101,5 +102,66 @@ namespace WorkFlowCore.Conditions
}
}
}
+
+ ///
+ /// 注册条件
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void Registercondition(string conditionId, string conditionName, Type conditionType, string description)
+ {
+ lock (objLock)
+ {
+ if (AllConditions.Where(s => s.Id == conditionId).Any())
+ return;
+ //throw new Exception($"相同的转换器id[{conditionId}]已存在");
+ }
+
+ AllConditions.Add(new Condition(conditionId, conditionName, conditionType, description));
+ }
+
+ ///
+ /// 从 程序集注册
+ ///
+ ///
+ public static void Registercondition(params Assembly[] assemblies)
+ {
+ foreach (var assembly in assemblies)
+ {
+ var types = assembly.GetTypes().Where(t => t.GetCustomAttribute() != null);
+
+ foreach (var type in types)
+ {
+ var attr = type.GetCustomAttribute();
+ var conditionId = type.FullName;
+ var conditionName = attr.Name ?? type.FullName;
+ Registercondition(conditionId, conditionName, type, attr.Description);
+ }
+ }
+ }
+
+ ///
+ /// 从 程序集注册
+ ///
+ ///
+ ///
+ public static void Registercondition(IServiceCollection services, params Assembly[] assemblies)
+ {
+ foreach (var assembly in assemblies)
+ {
+ var types = assembly.GetTypes().Where(t => t.GetCustomAttribute() != null);
+
+ foreach (var type in types)
+ {
+ var attr = type.GetCustomAttribute();
+ var conditionId = type.FullName;
+ var conditionName = attr.Name ?? type.FullName;
+ Registercondition(conditionId, conditionName, type, attr.Description);
+ services.AddScoped(type);
+ }
+ }
+ }
}
}
diff --git a/WorkFlowCore/WorkFlowCore/IRepositories/IWorkTaskRepository.cs b/WorkFlowCore/WorkFlowCore/IRepositories/IWorkTaskRepository.cs
index 5e84b689dbe483ebec9749c0660a1903db1c73ca..1f005c5f05d66160f21e12f9265f0b6b8b19d365 100644
--- a/WorkFlowCore/WorkFlowCore/IRepositories/IWorkTaskRepository.cs
+++ b/WorkFlowCore/WorkFlowCore/IRepositories/IWorkTaskRepository.cs
@@ -13,5 +13,13 @@ namespace WorkFlowCore.IRepositories
Task> GetUnHandledWorkTasksOfUserAsync(string userId, int pageIndex = 1, int pageSize = -1);
Task> GetHandledWorkTasksOfUserAsync(string userId, int pageIndex = 1, int pageSize = -1);
Task> GetTasksOfStartUserAsync(string userId, int pageIndex = 1, int pageSize = -1);
+ ///
+ /// 获取用户发起的流程
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> GetWorkTasksOfCreatorAsync(string userId, int pageIndex = 1, int pageSize = -1);
}
}
diff --git a/WorkFlowCore/WorkFlowCore/UserSelectors/UserSelectorManager.cs b/WorkFlowCore/WorkFlowCore/UserSelectors/UserSelectorManager.cs
index a21bdb14df9abc72f7b9489cd32760b1289a7aa3..22e3e0af1eff2d7ccc6bcfa6a9ce35e9b5f89392 100644
--- a/WorkFlowCore/WorkFlowCore/UserSelectors/UserSelectorManager.cs
+++ b/WorkFlowCore/WorkFlowCore/UserSelectors/UserSelectorManager.cs
@@ -1,4 +1,5 @@
-using System;
+using Microsoft.Extensions.DependencyInjection;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -74,6 +75,47 @@ namespace WorkFlowCore.UserSelectors
}
}
+ ///
+ /// 从 程序集注册
+ ///
+ ///
+ ///
+ public static void RegisterSelector(IServiceCollection services, params Assembly[] assemblies)
+ {
+ foreach (var assembly in assemblies)
+ {
+ var types = assembly.GetTypes().Where(t => t.GetCustomAttribute() != null);
+
+ foreach (var type in types)
+ {
+ var attr = type.GetCustomAttribute();
+ var selectorId = type.FullName;
+ var selectorName = attr.Name ?? type.FullName;
+ RegisterSelector(selectorId, selectorName, type, attr.Description);
+ services.AddScoped(type);
+ }
+ }
+ }
+
+ ///
+ /// 从 程序集注册
+ ///
+ ///
+ ///
+ public static void RegisterSelector(IServiceCollection services, params Type[] types)
+ {
+ var _types = types.Where(t => t.GetCustomAttribute() != null);
+
+ foreach (var type in _types)
+ {
+ var attr = type.GetCustomAttribute();
+ var selectorId = type.FullName;
+ var selectorName = attr.Name ?? type.FullName;
+ RegisterSelector(selectorId, selectorName, type, attr.Description);
+ services.AddScoped(type);
+ }
+ }
+
///
/// 获取选择器
diff --git a/WorkFlowCore/WorkFlowCore/WorkflowManager.cs b/WorkFlowCore/WorkFlowCore/WorkflowManager.cs
index 63bcf4c80ce69d68d818307f777220ed5a4c30f0..fbc53e7e871978ecefa7dbbbf5053eae3e900849 100644
--- a/WorkFlowCore/WorkFlowCore/WorkflowManager.cs
+++ b/WorkFlowCore/WorkFlowCore/WorkflowManager.cs
@@ -1099,6 +1099,7 @@ namespace WorkFlowCore
return userSelector.GetSelections();
}
+
///
/// 删除流程
/// 同时删除审批记录
@@ -1107,10 +1108,24 @@ namespace WorkFlowCore
///
public async Task DeleteWorkTask(Guid worktaskId)
{
- await workStepRepository.DeleteManyAsync(ws => ws.WorkTaskId == worktaskId);
- await workTaskRepository.DeleteAsync(worktaskId);
+ var worktaskInfo = await workTaskRepository.GetAsync(worktaskId);
+
+ if (worktaskInfo == null)
+ throw new Exception($"查无此项:{worktaskId}");
+
+ var worktask = worktaskInfo.ToWorkTask();
+
+ if (!worktask.IsPending)
+ throw new Exception("非待审批状态无法删除");
+
+ using (var unitOfWork = unitOfWorkManager.Begin())
+ {
+ await workStepRepository.DeleteManyAsync(ws => ws.WorkTaskId == worktaskId);
+ await workTaskRepository.DeleteAsync(worktaskId);
+ }
}
+
#endregion
}
}
diff --git a/WorkFlowCore/WorkFlowCore/Workflows/RejectNode.cs b/WorkFlowCore/WorkFlowCore/Workflows/RejectNode.cs
index 7cdd308dc58a2244227e3d130eda6360bfe964b6..9a984c5df0927c6c54ec4d01ba5f94d57a95891f 100644
--- a/WorkFlowCore/WorkFlowCore/Workflows/RejectNode.cs
+++ b/WorkFlowCore/WorkFlowCore/Workflows/RejectNode.cs
@@ -7,7 +7,7 @@ namespace WorkFlowCore.Workflows
public class RejectNode
{
public List Conditions { get; set; }
- public Guid NodeId;
- public Guid NodeName;
+ public Guid NodeId { get; set; }
+ public string NodeName { get; set; }
}
}