using Hotline.Share.Dtos;
using Hotline.Share.Dtos.File;
using Hotline.Share.Dtos.FlowEngine;
using Hotline.Share.Dtos.FlowEngine.Definition;
using Hotline.Share.Dtos.FlowEngine.Workflow;
using Hotline.Share.Enums.FlowEngine;
using Hotline.Share.Enums.Order;
using SqlSugar;
using XF.Domain.Entities;
using XF.Domain.Repository;
namespace Hotline.FlowEngine.Workflows;
public abstract class StepBasicEntity : CreationEntity
{
public string WorkflowId { get; set; }
///
/// 业务唯一标识
///
public string? ExternalId { get; set; }
#region 业务模块(冗余)
public string? ModuleId { get; set; }
public string? ModuleName { get; set; }
public string? ModuleCode { get; set; }
#endregion
///
/// 是否转办
///
public bool? IsForwarded { get; set; }
///
/// 流程指派类型
///
public EFlowAssignType? FlowAssignType { get; set; }
///
/// 该节点指派办理对象(依据不同指派方式可能为:orgId或userId),该字段subStep才会存在,stepBox不存在
/// 采用list类型,兼容或签
///
[SugarColumn(ColumnDataType = "json", IsJson = true)]
public List Handlers { get; set; } = new();
///
/// 上一节点办理时,nextStepCode下拉框中选中的值
/// config模式:当前节点的difinition.code, dynamic模式:x级部门办理的x:int
///
public string? PrevChosenStepCode { get; set; }
///
/// 是否实际办理过该工单
///
public bool IsActualHandled { get; set; }
///
/// 节点超期状态
///
public EExpiredStatus? ExpiredStatus { get; set; }
///
/// 配置下一步节点 & 谁被选中
///
[SugarColumn(ColumnDataType = "json", IsJson = true)]
public List? NextSteps { get; set; } = new();
///
/// 前一级节点Id,会签汇总节点无此字段(因可能有多个上级来源)
///
public string? PrevStepId { get; set; }
public string? PrevStepCode { get; set; }
///
/// 主办
///
[SugarColumn(DefaultValue = "f")]
public bool IsMain { get; set; }
///
/// 原生节点(区别动态生成)
///
[SugarColumn(DefaultValue = "f")]
public bool IsOrigin { get; set; }
///
/// 节点办理状态
///
public EWorkflowStepStatus Status { get; set; }
///
/// 备注
///
[SugarColumn(ColumnDescription = "备注", ColumnDataType = "varchar(5000)")]
public string? Remark { get; set; }
#region 会签
///
/// 会签id(或外层会签的id)
///
public string? CountersignId { get; set; }
///
/// 节点处于会签流程中的位置(区别直接办理会签和会签内非会签节点)
/// outer属于特殊会签
/// 最顶层的发起会签节点为none
///
[SugarColumn(DefaultValue = "0")]
public ECountersignPosition CountersignPosition { get; set; }
///
/// 会签直属办理节点(弃用)
///
[SugarColumn(ColumnDataType = "json", IsJson = true)]
public List? CountersignSteps { get; set; } = new();
#region 发起会签节点特有
///
/// 发起会签生成会签Id(不发起会签节点无此字段)
///
public string? StartCountersignId { get; set; }
///
/// 发起的会签是否汇总
///
[SugarColumn(DefaultValue = "f")]
public bool IsStartedCountersignEnd { get; set; }
#endregion
#region 会签汇总节点特有
///
/// 是否为会签汇总节点
///
[SugarColumn(DefaultValue = "f")]
public bool IsCountersignEndStep { get; set; }
///
/// 开启会签节点id(会签汇总节点对应会签发起节点id)
///
public string? CountersignStartStepId { get; set; }
#endregion
#endregion
#region 接办
///
/// 接办人
///
public string? AcceptorId { get; set; }
public string? AcceptorName { get; set; }
///
/// 接办人部门code
///
public string? AcceptorOrgId { get; set; }
public string? AcceptorOrgName { get; set; }
///
/// 接办人部门行政区划代码
///
public string? AcceptorOrgAreaCode { get; set; }
///
/// 接办人部门行政区划名称
///
public string? AcceptorOrgAreaName { get; set; }
///
/// 接办时间
///
public DateTime? AcceptTime { get; set; }
#endregion
#region 办理
///
/// 办理人
///
public string? HandlerId { get; set; }
public string? HandlerName { get; set; }
///
/// 办理人部门code
///
public string? HandlerOrgId { get; set; }
public bool? HandlerOrgIsCenter { get; set; }
///
/// 办理人部门名称
///
public string? HandlerOrgName { get; set; }
///
/// 办理人部门行政区划代码
///
public string? HandlerOrgAreaCode { get; set; }
///
/// 办理人部门行政区划名称
///
public string? HandlerOrgAreaName { get; set; }
///
/// 办理完成时间
///
public DateTime? HandleTime { get; set; }
///
/// 角色id(如果指派给角色)
///
public string? RoleId { get; set; }
public string? RoleName { get; set; }
#endregion
#region Definition
public string Name { get; set; }
///
/// 模板内唯一
///
public string Code { get; set; }
public EStepType StepType { get; init; }
///
/// 节点业务类型
///
public EBusinessType BusinessType { get; set; }
///
/// 办理人类型
///
public EHandlerType HandlerType { get; set; }
///
/// 是否有否决按钮
///
public bool CanReject { get; set; }
///
/// 执行模式(自动与否)
/// 只有普通节点支持自动,会签、动态节点均不支持自动
///
public EExecuteMode ExecuteMode { get; set; }
#region 会签相关配置
///
/// 是否支持发起会签(即使支持发起,当下一节点为汇总或结束节点时亦不可发起)
///
public bool CanStartCountersign { get; set; }
///
/// 会签策略
///
public ECountersignPolicy? CountersignPolicy { get; set; }
#endregion
#region 依据配置生成节点的方式
///
/// 实例化模式
///
public EInstanceMode InstanceMode { get; set; }
///
/// 动态实例化策略(多次模式才有)
///
public EDynamicPolicy? InstancePolicy { get; set; }
///
/// 到此标记终止动态实例化(多次模式才有)
///
/// 按直属部门重复既保存orgLevel:int
///
///
public string? TerminalDynamicMark { get; set; }
#endregion
///
/// 标签
///
public string? Tag { get; set; }
#endregion
#region 办理参数
#region 办理时赋值
///
/// (下一节点办理人)根据审批者类型不同,此字段为不同内容
///
/// 部门等级/分类为:orgCodes, 角色为:userIds
///
///
[SugarColumn(ColumnDataType = "json", IsJson = true)]
public List NextHandlers { get; set; } = new();
///
/// 下一节点主办,(NextHandlers其中一个, 如果不是会签则只有一个)
///
public string? NextMainHandler { get; set; }
///
/// 下一节点code(stepBox无值)
///
public string? NextStepCode { get; set; }
///
/// 是否短信通知
///
public bool IsSms { get; set; }
///
/// 办理意见
///
[SugarColumn(Length = 8000)]
public string? Opinion { get; set; }
///
/// 附件
///
[SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
public List? FileJson { get; set; }
///
/// 发起会签
///
public bool IsStartCountersign { get; set; }
#endregion
#region 创建时赋值
///
/// 节点期满时间
///
public DateTime? StepExpiredTime { get; set; }
/// nextStep 信息
///
/// 是否回到会签发起节点汇总
///
public bool BackToCountersignEnd { get; set; }
public EFlowDirection? FlowDirection { get; set; }
#endregion
#endregion
[Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
public Workflow Workflow { get; set; }
#region method
///
/// 开启会签
///
public void StartCountersign(string startCountersignId)
{
IsStartCountersign = true;
StartCountersignId = startCountersignId;
IsStartedCountersignEnd = false;
}
public bool HasAccepted() => !string.IsNullOrEmpty(AcceptorId);
public void Accept(
string userId, string? userName,
string orgId, string? orgName,
string? orgAreaCode, string? orgAreaName)
{
if (string.IsNullOrEmpty(AcceptorId))
{
AcceptorId = userId;
AcceptorName = userName;
}
if (string.IsNullOrEmpty(AcceptorOrgId))
AcceptorOrgId = orgId;
if (string.IsNullOrEmpty(AcceptorOrgName))
AcceptorOrgName = orgName;
if (string.IsNullOrEmpty(AcceptorOrgAreaCode))
AcceptorOrgAreaCode = orgAreaCode;
if (string.IsNullOrEmpty(AcceptorOrgAreaName))
AcceptorOrgAreaName = orgAreaName;
AcceptTime = DateTime.Now;
Status = EWorkflowStepStatus.WaitForHandle;
}
public void Handle(
string userId, string? userName,
string orgId, string? orgName,
string? orgAreaCode, string? orgAreaName,
bool orgIsCenter, string opinion,
string? nextStepCode = null)
{
if (!HasAccepted())
Accept(userId, userName, orgId, orgName, orgAreaCode, orgAreaName);
Status = EWorkflowStepStatus.Handled;
if (string.IsNullOrEmpty(HandlerId))
{
HandlerId = userId;
HandlerName = userName;
}
if (string.IsNullOrEmpty(HandlerOrgId))
HandlerOrgId = orgId;
if (string.IsNullOrEmpty(HandlerOrgName))
HandlerOrgName = orgName;
if (string.IsNullOrEmpty(HandlerOrgAreaCode))
HandlerOrgAreaCode = orgAreaCode;
if (string.IsNullOrEmpty(HandlerOrgAreaName))
HandlerOrgAreaName = orgAreaName;
HandlerOrgIsCenter ??= orgIsCenter;
HandleTime = DateTime.Now;
if (!string.IsNullOrEmpty(opinion))
Opinion = opinion;
ExpiredStatus = HandleTime > StepExpiredTime
? EExpiredStatus.Expired
: EExpiredStatus.Normal;
if (!IsInCountersign()
&& InstanceMode is EInstanceMode.Config
&& StepType is not EStepType.End)
{
var step = NextSteps.FirstOrDefault(d => d.Code == nextStepCode);
if (step != null)
step.Selected = true;
}
}
///
/// 指派
///
public void Assign(
string? handlerId, string? handlerName,
string? handlerOrgId, string? handlerOrgName,
string? roleId = null, string? roleName = null)
{
HandlerId = handlerId;
HandlerName = handlerName;
HandlerOrgId = handlerOrgId;
HandlerOrgName = handlerOrgName;
RoleId = roleId;
RoleName = roleName;
}
///
/// 是否处于会签流程中(不包含顶层发起会签节点)
///
public bool IsInCountersign() => CountersignPosition != ECountersignPosition.None;
public Kv GetHandler()
{
return FlowAssignType switch
{
EFlowAssignType.Org => new Kv(HandlerOrgId, HandlerOrgName),
EFlowAssignType.User => new Kv(HandlerId, HandlerName),
EFlowAssignType.Role => new Kv(RoleId, RoleName),
EFlowAssignType.OrgAndRole => new Kv(RoleId, $"{HandlerOrgName} - {RoleName}"),
_ => throw new ArgumentOutOfRangeException()
};
}
public FlowStepHandler GetWorkflowStepHandler()
{
switch (FlowAssignType)
{
case EFlowAssignType.Org:
return new FlowStepHandler
{
Key = HandlerOrgId,
Value = HandlerOrgName,
UserId = HandlerId,
Username = HandlerName,
OrgId = HandlerOrgId,
OrgName = HandlerOrgName,
RoleId = RoleId,
RoleName = RoleName
};
case EFlowAssignType.User:
return new FlowStepHandler
{
Key = HandlerId,
Value = HandlerName,
UserId = HandlerId,
Username = HandlerName,
OrgId = HandlerOrgId,
OrgName = HandlerOrgName,
RoleId = RoleId,
RoleName = RoleName
};
case EFlowAssignType.Role:
return new FlowStepHandler
{
Key = RoleId,
Value = RoleName,
UserId = HandlerId,
Username = HandlerName,
OrgId = HandlerOrgId,
OrgName = HandlerOrgName,
RoleId = RoleId,
RoleName = RoleName
};
default:
throw new ArgumentOutOfRangeException();
}
}
#endregion
}
/*
feature:
1. step增加字段记录roleId, roleName
2. 指派时为对应办理对象赋值,办理时为所有办理对象字段赋值
3. 配置办理对象为角色时,如果未指定办理人则指派给角色办理
4. thk从默认派单池中分配给对应办理人时指定办理对象
refactor:
1. step增加字段记录发起会签或是或签
2. status增加或签无需办理状态
3. 办理节点时判断是否立即结束会签或者等全部节点办完再结束
*/