支持审批流程中的分支网关和节点触发条件
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using YLErp.BLL;
|
||||
using YLErp.DBModels.Consts;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Helpers;
|
||||
|
||||
namespace YLErp.Modules.TradeModule
|
||||
{
|
||||
@@ -46,14 +47,38 @@ namespace YLErp.Modules.TradeModule
|
||||
return _tradeProcessCount.Value;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取所有交易审批节点
|
||||
/// 获取所有交易审批节点(开仓流程 TradeProcess)。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<approvalprocess> TradeProcess()
|
||||
{
|
||||
var tradeOrders = DbContext.approvalprocess.Where(t => t.processType == "TradeProcess").OrderBy(o => o.order).ToList();
|
||||
return tradeOrders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按交易推断流程类别(开仓/了结)获取审批节点(需求②)。
|
||||
/// <para>了结类(平仓待复核/行权待复核/互换待复核)优先取 CloseProcess;若未配置则回退 TradeProcess,避免卡单。</para>
|
||||
/// </summary>
|
||||
public List<approvalprocess> TradeProcessByCategory(trade td)
|
||||
{
|
||||
var category = ResolveProcessCategory(td);
|
||||
var orders = DbContext.approvalprocess.Where(t => t.processType == category).OrderBy(o => o.order).ToList();
|
||||
// 兼容回退:了结流程未配置时,回退到开仓流程。
|
||||
if (category == ProcessCategoryConst.Close && orders.Count == 0)
|
||||
{
|
||||
return TradeProcess();
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按类别统计审批节点数(需求②)。
|
||||
/// </summary>
|
||||
public int TradeProcessCountByCategory(trade td)
|
||||
{
|
||||
return TradeProcessByCategory(td).Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化交易 审批点
|
||||
/// </summary>
|
||||
@@ -62,7 +87,7 @@ namespace YLErp.Modules.TradeModule
|
||||
{
|
||||
td.ProcessOrderId = ProcessTradeLog.审批中;
|
||||
td.ProcessStatus = ProcessTradeStatus.审批中.ToString();
|
||||
var tradeProcess = TradeProcess();
|
||||
var tradeProcess = TradeProcessByCategory(td);
|
||||
var groupId = UserBLL.GetApprovalProcessGroup(userId);
|
||||
bool approvalBranch = tradeProcess.Any(x => x.approvalGroupId != 0);//审批流程有分支情况
|
||||
if (td.ProcessOrderId == 1)
|
||||
@@ -88,6 +113,111 @@ namespace YLErp.Modules.TradeModule
|
||||
{
|
||||
td.ProcessOrderId = 2;
|
||||
}
|
||||
|
||||
// 需求①:进入审批流程时即应用触发条件——从起始节点开始,跳过所有不满足触发条件的节点。
|
||||
// 若所有节点均不满足 → 直接审批通过(无需任何人审核)。
|
||||
ApplyTriggerFromStart(tradeProcess, td, userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从当前 ProcessOrderId 起点开始,按触发条件跳过无需审批的节点(需求①)。
|
||||
/// <para>场景:交易提交进入审批流程时,若节点1、2配置的触发条件均不满足,则直接跳到节点3;
|
||||
/// 若全部节点都不满足,则直接审批通过。</para>
|
||||
/// </summary>
|
||||
private void ApplyTriggerFromStart(List<approvalprocess> tradeProcess, trade td, int userId)
|
||||
{
|
||||
if (tradeProcess == null || tradeProcess.Count == 0) return;
|
||||
|
||||
// 取当前起点节点(主干 node=0)
|
||||
var current = tradeProcess.FirstOrDefault(x => x.order == td.ProcessOrderId && x.node == 0);
|
||||
// 若起点不在主干(如分支调整后 ProcessOrderId=2),取该 order 的主干节点
|
||||
if (current == null)
|
||||
{
|
||||
current = tradeProcess.FirstOrDefault(x => x.order >= td.ProcessOrderId && x.node == 0);
|
||||
}
|
||||
if (current == null) return;
|
||||
|
||||
var ctx = BuildTriggerContext(td, userId);
|
||||
var target = ConditionEvaluator.FindFirstTriggeredNode(tradeProcess, current, ctx);
|
||||
if (target == null)
|
||||
{
|
||||
// 所有节点都不满足触发条件 → 直接审批通过,无需审核
|
||||
td.ProcessOrderId = ProcessTradeLog.审批通过;
|
||||
td.ProcessStatus = ProcessTradeStatus.通过审批.ToString();
|
||||
td.ProcessOptDate = DateTime.Now;
|
||||
return;
|
||||
}
|
||||
// target 即为第一个满足触发条件、需实际审批的节点
|
||||
if (target.order != td.ProcessOrderId)
|
||||
{
|
||||
td.ProcessOrderId = target.order;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点触发条件(需求①):在已确定下一审批节点 nextOrder 后,若该节点配置了 triggerCondition,
|
||||
/// 则只有「满足触发条件」才进入该节点审批;不满足则跳过该节点,继续向后寻找,直到找到可进入的节点或抵达流程末尾。
|
||||
/// <para>语义:triggerCondition 为空 → 无条件进入审批;非空 → 满足才进入,不满足则跳过。</para>
|
||||
/// <para>非侵入式:原有分支推进逻辑不变,仅在其结果之上叠加触发判断循环。</para>
|
||||
/// </summary>
|
||||
/// <param name="tradeProcess">当前流程的全部节点(已按 order 排序)</param>
|
||||
/// <param name="nextOrder">原逻辑计算出的下一节点(可能为 null)</param>
|
||||
/// <param name="ctx">条件求值业务上下文(已含 trade、本次交易名义本金等)</param>
|
||||
/// <returns>最终应推进到的节点;若应结束流程则返回 null</returns>
|
||||
protected static approvalprocess AdvanceThroughTriggerNodes(
|
||||
List<approvalprocess> tradeProcess,
|
||||
approvalprocess nextOrder,
|
||||
ConditionContext ctx)
|
||||
{
|
||||
// 不满足触发条件的节点需跳过:循环向后找第一个可进入的节点。
|
||||
while (nextOrder != null && !string.IsNullOrWhiteSpace(nextOrder.triggerCondition))
|
||||
{
|
||||
if (ConditionEvaluator.Evaluate(nextOrder.triggerCondition, ctx))
|
||||
{
|
||||
break; // 满足触发条件 → 进入该节点审批,停止跳过。
|
||||
}
|
||||
|
||||
// 不满足触发条件 → 跳过该节点,向后取下一个主干节点(node=0),继续判断。
|
||||
nextOrder = tradeProcess.FirstOrDefault(x => x.order > nextOrder.order && x.node == 0);
|
||||
}
|
||||
return nextOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建触发条件求值上下文:从 trade 及其关联的 trade_cash 取本次交易名义本金(了结场景)。
|
||||
/// <para>本次交易名义本金 = 本次了结操作的 trade_cash.UnwindStockEqvNotional 绝对值。</para>
|
||||
/// </summary>
|
||||
protected ConditionContext BuildTriggerContext(trade td, int userId)
|
||||
{
|
||||
var ctx = new ConditionContext
|
||||
{
|
||||
UserId = userId,
|
||||
Trade = td,
|
||||
ProcessCategory = ResolveProcessCategory(td),
|
||||
InitGroupId = UserBLL.GetApprovalProcessGroup(userId)
|
||||
};
|
||||
// 了结场景:取本次影响的名义本金(trade_cash.UnwindStockEqvNotional 绝对值)
|
||||
if (td != null && ctx.ProcessCategory == ProcessCategoryConst.Close)
|
||||
{
|
||||
var tc = DbContext.trade_cash
|
||||
.Where(t => t.TradeId == td.id && t.ValidState == ConsGlobal.InValid && !t.IsDeleted)
|
||||
.OrderByDescending(t => t.id)
|
||||
.FirstOrDefault();
|
||||
if (tc != null && tc.UnwindStockEqvNotional.HasValue)
|
||||
{
|
||||
ctx.CurrentNotional = Math.Abs(tc.UnwindStockEqvNotional.Value);
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按交易状态推断流程类别:开仓(TradeProcess) / 了结(CloseProcess)。需求②。
|
||||
/// <para>委托给 ProcessCategoryConst.Resolve,供全局复用。</para>
|
||||
/// </summary>
|
||||
protected static string ResolveProcessCategory(trade td)
|
||||
{
|
||||
return ProcessCategoryConst.Resolve(td);
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加交易操作日志
|
||||
@@ -521,4 +651,31 @@ namespace YLErp.Modules.TradeModule
|
||||
return rateCalcModeValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交易流程类别常量(需求②):对应 approvalprocess.processType 的取值。
|
||||
/// </summary>
|
||||
public static class ProcessCategoryConst
|
||||
{
|
||||
/// <summary>开仓审批流程</summary>
|
||||
public const string Open = "TradeProcess";
|
||||
|
||||
/// <summary>了结/平仓/行权审批流程</summary>
|
||||
public const string Close = "CloseProcess";
|
||||
|
||||
/// <summary>
|
||||
/// 按交易状态推断流程类别:处于平仓/行权/互换待复核的交易视为「了结」类操作。
|
||||
/// </summary>
|
||||
public static string Resolve(trade td)
|
||||
{
|
||||
if (td == null) return Open;
|
||||
if (td.TradeStatus == ConsTrade.平仓待复核
|
||||
|| td.TradeStatus == ConsTrade.行权待复核
|
||||
|| td.TradeStatus == ConsTrade.互换待复核)
|
||||
{
|
||||
return Close;
|
||||
}
|
||||
return Open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user