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. 办理节点时判断是否立即结束会签或者等全部节点办完再结束 */