增加撤销与启动,详见:#I3ILBG

调整工程结构,采用模块化机制
This commit is contained in:
yubaolee
2021-04-15 00:40:30 +08:00
parent 484daa48f0
commit cf23d0025f
86 changed files with 3032 additions and 2819 deletions

View File

@@ -0,0 +1,30 @@
namespace Infrastructure.Const
{
/// <summary>
/// 流程状态
/// </summary>
public struct FlowInstanceStatus
{
/// <summary>
/// 撤销、召回
/// </summary>
public const int Draft = -1;
/// <summary>
/// 正在运行
/// </summary>
public const int Running = 0;
/// <summary>
/// 完成
/// </summary>
public const int Finished = 1;
/// <summary>
/// 不同意
/// </summary>
public const int Disagree = 3;
/// <summary>
/// 驳回
/// </summary>
public const int Rejected = 4;
}
}

View File

@@ -381,6 +381,7 @@ namespace Infrastructure.Helpers
/// <param name="addStr">追加内容</param>
public static void RegxAddContentByParenthesis(string path, string addStr)
{
path = StringExtension.ReplacePath(path);
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
string originStr = sr.ReadToEnd();

View File

@@ -325,6 +325,17 @@ namespace OpenAuth.App.Flow
return previousId;
}
/// <summary>
/// 撤销流程,清空所有节点
/// </summary>
public void ReCall()
{
foreach (var item in Nodes)
{
item.Value.setInfo = null;
}
}
///<summary>
/// 标记节点1通过-1不通过0驳回
/// </summary>

View File

@@ -25,6 +25,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Threading.Tasks;
using Infrastructure.Const;
using Infrastructure.Helpers;
using OpenAuth.Repository;
@@ -33,7 +34,7 @@ namespace OpenAuth.App
/// <summary>
/// 工作流实例表操作
/// </summary>
public class FlowInstanceApp : BaseStringApp<FlowInstance,OpenAuthDBContext>
public class FlowInstanceApp : BaseStringApp<FlowInstance, OpenAuthDBContext>
{
private RevelanceManagerApp _revelanceApp;
private FlowSchemeApp _flowSchemeApp;
@@ -41,6 +42,19 @@ namespace OpenAuth.App
private IHttpClientFactory _httpClientFactory;
private IServiceProvider _serviceProvider;
public FlowInstanceApp(IUnitWork<OpenAuthDBContext> unitWork,
IRepository<FlowInstance, OpenAuthDBContext> repository
, RevelanceManagerApp app, FlowSchemeApp flowSchemeApp, FormApp formApp,
IHttpClientFactory httpClientFactory, IAuth auth, IServiceProvider serviceProvider)
: base(unitWork, repository, auth)
{
_revelanceApp = app;
_flowSchemeApp = flowSchemeApp;
_formApp = formApp;
_httpClientFactory = httpClientFactory;
_serviceProvider = serviceProvider;
}
#region API
/// <summary>
@@ -93,14 +107,16 @@ namespace OpenAuth.App
flowInstance.CreateUserId = user.User.Id;
flowInstance.CreateUserName = user.User.Account;
flowInstance.MakerList = (wfruntime.GetNextNodeType() != 4 ? GetNextMakers(wfruntime) : "");
flowInstance.IsFinish = (wfruntime.GetNextNodeType() == 4 ? 1 : 0);
flowInstance.IsFinish = (wfruntime.GetNextNodeType() == 4
? FlowInstanceStatus.Finished
: FlowInstanceStatus.Running);
UnitWork.Add(flowInstance);
wfruntime.flowInstanceId = flowInstance.Id;
if (flowInstance.FrmType == 1) //如果是开发者自定义的表单
{
var t = Type.GetType("OpenAuth.App."+ flowInstance.DbName +"App");
var t = Type.GetType("OpenAuth.App." + flowInstance.DbName + "App");
ICustomerForm icf = (ICustomerForm) _serviceProvider.GetService(t);
icf.Add(flowInstance.Id, flowInstance.FrmData);
}
@@ -144,23 +160,24 @@ namespace OpenAuth.App
CreateUserId = tag.UserId,
CreateUserName = tag.UserName,
CreateDate = DateTime.Now
};//操作记录
}; //操作记录
FlowRuntime wfruntime = new FlowRuntime(flowInstance);
#region
if (flowInstance.ActivityType == 0)//当前节点是会签节点
if (flowInstance.ActivityType == 0) //当前节点是会签节点
{
//会签时的【当前节点】一直是会签开始节点
//TODO: 标记会签节点的状态,这个地方感觉怪怪的
wfruntime.MakeTagNode(wfruntime.currentNodeId, tag);
string canCheckId = ""; //寻找当前登录用户可审核的节点Id
foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[wfruntime.currentNodeId].Select(u => u.to))
foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[wfruntime.currentNodeId]
.Select(u => u.to))
{
var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点
var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点
canCheckId = GetOneForkLineCanCheckNodeId(fromForkStartNode, wfruntime, tag);
if(!string.IsNullOrEmpty(canCheckId)) break;
if (!string.IsNullOrEmpty(canCheckId)) break;
}
if (canCheckId == "")
@@ -177,15 +194,17 @@ namespace OpenAuth.App
string res = wfruntime.NodeConfluence(canCheckId, tag);
if (res == TagState.No.ToString("D"))
{
flowInstance.IsFinish = 3;
flowInstance.IsFinish = FlowInstanceStatus.Disagree;
}
else if(!string.IsNullOrEmpty(res))
else if (!string.IsNullOrEmpty(res))
{
flowInstance.PreviousId = flowInstance.ActivityId;
flowInstance.ActivityId = wfruntime.nextNodeId;
flowInstance.ActivityType = wfruntime.nextNodeType;
flowInstance.ActivityName = wfruntime.nextNode.name;
flowInstance.IsFinish = (wfruntime.nextNodeType == 4 ? 1 : 0);
flowInstance.IsFinish = (wfruntime.nextNodeType == 4
? FlowInstanceStatus.Finished
: FlowInstanceStatus.Running);
flowInstance.MakerList =
(wfruntime.nextNodeType == 4 ? "" : GetNextMakers(wfruntime));
@@ -197,8 +216,8 @@ namespace OpenAuth.App
flowInstance.MakerList = GetForkNodeMakers(wfruntime, wfruntime.currentNodeId);
AddTransHistory(wfruntime);
}
}
#endregion
#region
@@ -213,13 +232,16 @@ namespace OpenAuth.App
flowInstance.ActivityType = wfruntime.nextNodeType;
flowInstance.ActivityName = wfruntime.nextNode.name;
flowInstance.MakerList = wfruntime.nextNodeType == 4 ? "" : GetNextMakers(wfruntime);
flowInstance.IsFinish = (wfruntime.nextNodeType == 4 ? 1 : 0);
flowInstance.IsFinish = (wfruntime.nextNodeType == 4
? FlowInstanceStatus.Finished
: FlowInstanceStatus.Running);
AddTransHistory(wfruntime);
}
else
{
flowInstance.IsFinish = 3; //表示该节点不同意
flowInstance.IsFinish = FlowInstanceStatus.Disagree; //表示该节点不同意
}
flowInstanceOperationHistory.Content = "【" + wfruntime.currentNode.name
+ "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm")
+ "】" + (tag.Taged == 1 ? "同意" : "不同意") + ",备注:"
@@ -241,13 +263,14 @@ namespace OpenAuth.App
//会签时,获取一条会签分支上面是否有用户可审核的节点
private string GetOneForkLineCanCheckNodeId(FlowNode fromForkStartNode, FlowRuntime wfruntime, Tag tag)
{
string canCheckId="";
string canCheckId = "";
var node = fromForkStartNode;
do //沿一条分支线路执行,直到遇到会签结束节点
do //沿一条分支线路执行,直到遇到会签结束节点
{
var makerList = GetNodeMarkers(node);
if (node.setInfo.Taged == null && !string.IsNullOrEmpty(makerList) && makerList.Split(',').Any(one => tag.UserId == one))
if (node.setInfo.Taged == null && !string.IsNullOrEmpty(makerList) &&
makerList.Split(',').Any(one => tag.UserId == one))
{
canCheckId = node.id;
break;
@@ -272,8 +295,10 @@ namespace OpenAuth.App
FlowRuntime wfruntime = new FlowRuntime(flowInstance);
string rejectNode = ""; //驳回的节点
rejectNode = string.IsNullOrEmpty(reqest.NodeRejectStep) ? wfruntime.RejectNode(reqest.NodeRejectType) : reqest.NodeRejectStep;
string rejectNode = ""; //驳回的节点
rejectNode = string.IsNullOrEmpty(reqest.NodeRejectStep)
? wfruntime.RejectNode(reqest.NodeRejectType)
: reqest.NodeRejectStep;
var tag = new Tag
{
@@ -284,7 +309,7 @@ namespace OpenAuth.App
};
wfruntime.MakeTagNode(wfruntime.currentNodeId, tag);
flowInstance.IsFinish = 4;//4表示驳回需要申请者重新提交表单
flowInstance.IsFinish = FlowInstanceStatus.Rejected; //4表示驳回需要申请者重新提交表单
if (rejectNode != "")
{
flowInstance.PreviousId = flowInstance.ActivityId;
@@ -300,14 +325,10 @@ namespace OpenAuth.App
UnitWork.Add(new FlowInstanceOperationHistory
{
InstanceId = reqest.FlowInstanceId
,
CreateUserId = user.Id
,
CreateUserName = user.Name
,
CreateDate = DateTime.Now
,
InstanceId = reqest.FlowInstanceId,
CreateUserId = user.Id,
CreateUserName = user.Name,
CreateDate = DateTime.Now,
Content = "【"
+ wfruntime.currentNode.name
+ "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】驳回,备注:"
@@ -324,6 +345,7 @@ namespace OpenAuth.App
#endregion API
#region
/// <summary>
/// 寻找下一步的执行人
/// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能
@@ -336,7 +358,8 @@ namespace OpenAuth.App
{
throw (new Exception("无法寻找到下一个节点"));
}
if (wfruntime.nextNodeType == 0)//如果是会签节点
if (wfruntime.nextNodeType == 0) //如果是会签节点
{
makerList = GetForkNodeMakers(wfruntime, wfruntime.nextNodeId);
}
@@ -359,7 +382,7 @@ namespace OpenAuth.App
/// <returns></returns>
private string GetForkNodeMakers(FlowRuntime wfruntime, string forkNodeId)
{
string makerList="";
string makerList = "";
foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[forkNodeId].Select(u => u.to))
{
var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点
@@ -377,24 +400,27 @@ namespace OpenAuth.App
//获取会签一条线上的审核者,该审核者应该是已审核过的节点的下一个人
private string GetOneForkLineMakers(FlowNode fromForkStartNode, FlowRuntime wfruntime)
{
string markers="";
string markers = "";
var node = fromForkStartNode;
do //沿一条分支线路执行,直到遇到第一个没有审核的节点
do //沿一条分支线路执行,直到遇到第一个没有审核的节点
{
if (node.setInfo != null && node.setInfo.Taged != null)
{
if (node.type != FlowNode.FORK && node.setInfo.Taged != (int) TagState.Ok) //如果节点是不同意或驳回,则不用再找了
if (node.type != FlowNode.FORK && node.setInfo.Taged != (int) TagState.Ok) //如果节点是不同意或驳回,则不用再找了
{
break;
}
node = wfruntime.GetNextNode(node.id); //下一个节点
node = wfruntime.GetNextNode(node.id); //下一个节点
continue;
}
var marker = GetNodeMarkers(node);
if (marker == "")
{
throw (new Exception($"节点{node.name}没有审核者,请检查!"));
}
if (marker == "1")
{
throw (new Exception($"节点{node.name}是会签节点,不能用所有人,请检查!"));
@@ -404,6 +430,7 @@ namespace OpenAuth.App
{
markers += ",";
}
markers += marker;
break;
} while (node.type != FlowNode.JOIN);
@@ -416,7 +443,7 @@ namespace OpenAuth.App
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
private string GetNodeMarkers(FlowNode node,string flowinstanceCreateUserId="")
private string GetNodeMarkers(FlowNode node, string flowinstanceCreateUserId = "")
{
string makerList = "";
if (node.type == FlowNode.START && (!string.IsNullOrEmpty(flowinstanceCreateUserId))) //如果是开始节点,通常情况下是驳回到开始了
@@ -425,26 +452,28 @@ namespace OpenAuth.App
}
else if (node.setInfo != null)
{
if (node.setInfo.NodeDesignate == Setinfo.ALL_USER)//所有成员
if (node.setInfo.NodeDesignate == Setinfo.ALL_USER) //所有成员
{
makerList = "1";
}
else if (node.setInfo.NodeDesignate == Setinfo.SPECIAL_USER)//指定成员
else if (node.setInfo.NodeDesignate == Setinfo.SPECIAL_USER) //指定成员
{
makerList = GenericHelpers.ArrayToString(node.setInfo.NodeDesignateData.users, makerList);
}
else if (node.setInfo.NodeDesignate == Setinfo.SPECIAL_ROLE) //指定角色
else if (node.setInfo.NodeDesignate == Setinfo.SPECIAL_ROLE) //指定角色
{
var users = _revelanceApp.Get(Define.USERROLE, false, node.setInfo.NodeDesignateData.roles);
makerList = GenericHelpers.ArrayToString(users, makerList);
}
}
else //如果没有设置节点信息,默认所有人都可以审核
else //如果没有设置节点信息,默认所有人都可以审核
{
makerList = "1";
}
return makerList;
}
#endregion
/// <summary>
@@ -462,7 +491,7 @@ namespace OpenAuth.App
Taged = Int32.Parse(request.VerificationFinally)
};
bool isReject = TagState.Reject.Equals((TagState) tag.Taged);
if (isReject) //驳回
if (isReject) //驳回
{
NodeReject(request);
}
@@ -482,9 +511,12 @@ namespace OpenAuth.App
var result = new TableData();
var user = _auth.GetCurrentUser();
if (request.type == "wait") //待办事项
if (request.type == "wait") //待办事项
{
Expression<Func<FlowInstance, bool>> waitExp = u => (u.MakerList == "1" || u.MakerList.Contains(user.User.Id)) && (u.IsFinish == 0|| u.IsFinish==4);
Expression<Func<FlowInstance, bool>> waitExp = u => (u.MakerList == "1"
|| u.MakerList.Contains(user.User.Id)) &&
(u.IsFinish == FlowInstanceStatus.Running ||
u.IsFinish == FlowInstanceStatus.Rejected);
// 加入搜索自定义标题
if (!string.IsNullOrEmpty(request.key))
@@ -496,13 +528,13 @@ namespace OpenAuth.App
result.data = UnitWork.Find(request.page, request.limit, "CreateDate descending", waitExp).ToList();
}
else if (request.type == "disposed") //已办事项(即我参与过的流程)
else if (request.type == "disposed") //已办事项(即我参与过的流程)
{
var instances = UnitWork.Find<FlowInstanceTransitionHistory>(u => u.CreateUserId == user.User.Id)
.Select(u => u.InstanceId).Distinct();
var query = from ti in instances
join ct in UnitWork.Find<FlowInstance>(null) on ti equals ct.Id
select ct;
join ct in UnitWork.Find<FlowInstance>(null) on ti equals ct.Id
select ct;
// 加入搜索自定义标题
if (!string.IsNullOrEmpty(request.key))
@@ -515,7 +547,7 @@ namespace OpenAuth.App
.Take(request.limit).ToList();
result.count = instances.Count();
}
else //我的流程
else //我的流程
{
Expression<Func<FlowInstance, bool>> myFlowExp = u => u.CreateUserId == user.User.Id;
@@ -550,26 +582,93 @@ namespace OpenAuth.App
ToNodeId = wfruntime.nextNodeId,
ToNodeName = wfruntime.nextNode.name,
ToNodeType = wfruntime.nextNodeType,
IsFinish = wfruntime.nextNodeType == 4 ? 1 : 0,
IsFinish = wfruntime.nextNodeType == 4 ? FlowInstanceStatus.Finished : FlowInstanceStatus.Running,
TransitionSate = 0
});
}
public FlowInstanceApp(IUnitWork<OpenAuthDBContext> unitWork, IRepository<FlowInstance,OpenAuthDBContext> repository
, RevelanceManagerApp app, FlowSchemeApp flowSchemeApp, FormApp formApp, IHttpClientFactory httpClientFactory,IAuth auth, IServiceProvider serviceProvider)
: base(unitWork, repository, auth)
{
_revelanceApp = app;
_flowSchemeApp = flowSchemeApp;
_formApp = formApp;
_httpClientFactory = httpClientFactory;
_serviceProvider = serviceProvider;
}
public List<FlowInstanceOperationHistory> QueryHistories(QueryFlowInstanceHistoryReq request)
{
return UnitWork.Find<FlowInstanceOperationHistory>(u => u.InstanceId == request.FlowInstanceId)
.OrderByDescending(u => u.CreateDate).ToList();
}
/// <summary>
/// 召回流程
/// </summary>
public void ReCall(RecallFlowInstanceReq request)
{
var user = _auth.GetCurrentUser().User;
FlowInstance flowInstance = Get(request.FlowInstanceId);
FlowRuntime wfruntime = new FlowRuntime(flowInstance);
string startNodeId = wfruntime.startNodeId; //起始节点
wfruntime.ReCall();
flowInstance.IsFinish = FlowInstanceStatus.Draft;
flowInstance.PreviousId = flowInstance.ActivityId;
flowInstance.ActivityId = startNodeId;
flowInstance.ActivityType = wfruntime.GetNodeType(startNodeId);
flowInstance.ActivityName = wfruntime.Nodes[startNodeId].name;
flowInstance.MakerList = GetNodeMarkers(wfruntime.Nodes[startNodeId], flowInstance.CreateUserId);
AddTransHistory(wfruntime);
UnitWork.Update(flowInstance);
UnitWork.Add(new FlowInstanceOperationHistory
{
InstanceId = request.FlowInstanceId,
CreateUserId = user.Id,
CreateUserName = user.Name,
CreateDate = DateTime.Now,
Content = $"【撤销】由{user.Name}撤销,备注:{request.Description}"
});
UnitWork.Save();
}
/// <summary>启动流程</summary>
/// <remarks> 通常是对状态为【草稿】的流程进行操作,进入运行状态 </remarks>
public void Start(StartFlowInstanceReq request)
{
FlowInstance flowInstance = Get(request.FlowInstanceId);
var wfruntime = new FlowRuntime(flowInstance);
var user = _auth.GetCurrentUser();
#region
flowInstance.ActivityId = wfruntime.nextNodeId;
flowInstance.ActivityType = wfruntime.GetNextNodeType();
flowInstance.ActivityName = wfruntime.nextNode.name;
flowInstance.PreviousId = wfruntime.currentNodeId;
flowInstance.CreateUserId = user.User.Id;
flowInstance.CreateUserName = user.User.Account;
flowInstance.MakerList = (wfruntime.GetNextNodeType() != 4 ? GetNextMakers(wfruntime) : "");
flowInstance.IsFinish = (wfruntime.GetNextNodeType() == 4
? FlowInstanceStatus.Finished
: FlowInstanceStatus.Running);
UnitWork.Update(flowInstance);
#endregion
#region
FlowInstanceOperationHistory processOperationHistoryEntity = new FlowInstanceOperationHistory
{
InstanceId = flowInstance.Id,
CreateUserId = user.User.Id,
CreateUserName = user.User.Name,
CreateDate = DateTime.Now,
Content = $"【启动】由用户{user.User.Name}启动"
};
UnitWork.Add(processOperationHistoryEntity);
#endregion
AddTransHistory(wfruntime);
UnitWork.Save();
}
}
}

View File

@@ -0,0 +1,18 @@
namespace OpenAuth.App.Request
{
/// <summary>
/// 召回、撤销流程
/// </summary>
public class RecallFlowInstanceReq
{
/// <summary>
/// 召回、撤销的流程实例ID
/// </summary>
public string FlowInstanceId { get; set; }
/// <summary>
/// 撤回备注
/// </summary>
public string Description { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
namespace OpenAuth.App.Request
{
/// <summary>
/// 启动流程
/// </summary>
public class StartFlowInstanceReq
{
/// <summary>
/// 启动流程的实例Id
/// </summary>
public string FlowInstanceId { get; set; }
}
}

View File

@@ -11,6 +11,7 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using Infrastructure.Const;
using OpenAuth.Repository.Core;
namespace OpenAuth.Repository.Domain
@@ -44,7 +45,7 @@ namespace OpenAuth.Repository.Domain
this.CreateUserName= string.Empty;
this.FlowLevel= 0;
this.Description= string.Empty;
this.IsFinish= 0;
this.IsFinish= FlowInstanceStatus.Running;
this.MakerList= string.Empty;
}

View File

@@ -10,6 +10,7 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using Infrastructure.Const;
using OpenAuth.Repository.Core;
namespace OpenAuth.Repository.Domain
@@ -28,7 +29,7 @@ namespace OpenAuth.Repository.Domain
this.ToNodeId= string.Empty;
this.ToNodeName= string.Empty;
this.TransitionSate= 0;
this.IsFinish= 0;
this.IsFinish= FlowInstanceStatus.Running;
this.CreateDate= DateTime.Now;
this.CreateUserId= string.Empty;
this.CreateUserName= string.Empty;

View File

@@ -84,6 +84,44 @@ namespace OpenAuth.WebApi.Controllers
return result;
}
/// <summary>召回流程</summary>
/// <remarks> 召回后流程状态为【草稿】状态,可以再次发起流程。所有的流程节点状态还原,但保留审批记录 </remarks>
[HttpPost]
public Response ReCall(RecallFlowInstanceReq obj)
{
var result = new Response();
try
{
_app.ReCall(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>启动流程</summary>
/// <remarks> 通常是对状态为【草稿】的流程进行操作,进入运行状态 </remarks>
[HttpPost]
public Response Start(StartFlowInstanceReq obj)
{
var result = new Response();
try
{
_app.Start(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
//添加或修改
[HttpPost]
public Response Update(FlowInstance obj)

View File

@@ -10,7 +10,7 @@ namespace OpenAuth.WebApi
{
public static void Main(string[] args)
{
Console.WriteLine(@"
Console.WriteLine($@"
____ _ _ _ _ _
/ __ \ /\ | | | | | \ | | | |
| | | |_ __ ___ _ __ / \ _ _| |_| |__ | \| | ___| |_
@@ -24,7 +24,7 @@ namespace OpenAuth.WebApi
.Net 5 Repository: https://gitee.com/dotnetchina/OpenAuth.Net
.Net core 3.1 : https://gitee.com/yubaolee/OpenAuth.Core
-------------------------------------------------------------------
");
Start Time:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
CreateHostBuilder(args).Build().Run();
}