diff --git a/YLErpDAL/Modules/RiskEngine/RiskTrialService.cs b/YLErpDAL/Modules/RiskEngine/RiskTrialService.cs index 442c84ed..ccabf681 100644 --- a/YLErpDAL/Modules/RiskEngine/RiskTrialService.cs +++ b/YLErpDAL/Modules/RiskEngine/RiskTrialService.cs @@ -53,24 +53,33 @@ namespace YLErp.Modules.RiskEngine } var riskResult = new RiskEngineService(this).EvaluateRisk(context, context.TriggerPoint); + // 与交易确认流程保持一致:二次确认放行范围包含“需审批”和“提示”,避免已确认过的提示规则重复弹出。 var currentApprovalRules = riskResult.TriggeredRules .Where(rule => rule.ControlStrategy == RiskControlStrategy.Approval) .ToList(); + var currentTipRules = riskResult.TriggeredRules + .Where(rule => rule.ControlStrategy == RiskControlStrategy.ShowTip) + .ToList(); + // 二次提交时,若首次试算记录有效,则剔除已确认过的审批和提示规则;记录过期或上下文不匹配时重新展示全部当前命中的审批和提示规则。 var pendingApprovalRules = confirmationExpired ? currentApprovalRules : currentApprovalRules.Where(rule => !confirmedRuleIds.Contains(rule.RuleId)).ToList(); - var trial = CreateQuotaTrial(context, trialSource, riskResult, pendingApprovalRules); + var pendingTipRules = confirmationExpired + ? currentTipRules + : currentTipRules.Where(rule => !confirmedRuleIds.Contains(rule.RuleId)).ToList(); + var trial = CreateQuotaTrial(context, trialSource, riskResult, pendingApprovalRules, pendingTipRules); + // 目前复用 quotaTrial 承载风控试算结果,兼容交易确认、平仓等现有详情页和二次确认流程;后续若表语义收敛,再考虑统一重命名为风控试算。 new Modules.RiskModule.QuotaMonitorService(this).SaveQuotaTrial(trial); var result = new RiskTrialResult { Blocked = riskResult.Blocked, NeedApproval = pendingApprovalRules.Any(), - ShowTip = riskResult.ShowTip, + ShowTip = pendingTipRules.Any(), ConfirmationExpired = confirmationExpired, TrialDataId = trial.id, Message = trial.RiskWarningDetails, - ApprovalRuleIds = pendingApprovalRules + ApprovalRuleIds = pendingApprovalRules.Concat(pendingTipRules) .Select(rule => rule.RuleId) .Where(ruleId => !string.IsNullOrWhiteSpace(ruleId)) .Distinct() @@ -83,7 +92,7 @@ namespace YLErp.Modules.RiskEngine } /// - /// 校验二次确认记录,并恢复首次试算中已确认的审批规则ID。 + /// 校验二次确认记录,并恢复首次试算中已确认的审批和提示规则ID。 /// private bool TryGetConfirmedRuleIds(RiskContext context, int trialSource, RiskTrialConfirmation confirmation, ISet confirmedRuleIds) { @@ -106,7 +115,7 @@ namespace YLErp.Modules.RiskEngine return false; } - foreach (var ruleId in ParseApprovalRuleIds(trial.RiskWarningDetails)) + foreach (var ruleId in ParseConfirmedRuleIds(trial.RiskWarningDetails)) { confirmedRuleIds.Add(ruleId); } @@ -125,21 +134,21 @@ namespace YLErp.Modules.RiskEngine } /// - /// 从系统生成的审批规则明细中恢复规则ID,避免直接信任前端提交的放行规则列表。 + /// 从系统生成的审批和提示规则明细中恢复规则ID,避免直接信任前端提交的放行规则列表。 /// - private static IEnumerable ParseApprovalRuleIds(string details) + private static IEnumerable ParseConfirmedRuleIds(string details) { - const string approvalTitle = "[风控引擎] 规则触发:需审批"; const string ruleIdPrefix = "规则ID:"; - var inApprovalSection = false; + var inConfirmableSection = false; foreach (var line in (details ?? string.Empty).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) { if (line.StartsWith("[风控引擎] 规则触发:", StringComparison.Ordinal)) { - inApprovalSection = line.StartsWith(approvalTitle, StringComparison.Ordinal); + inConfirmableSection = line.StartsWith("[风控引擎] 规则触发:需审批", StringComparison.Ordinal) + || line.StartsWith("[风控引擎] 规则触发:提示", StringComparison.Ordinal); continue; } - if (!inApprovalSection) + if (!inConfirmableSection) { continue; } @@ -162,7 +171,7 @@ namespace YLErp.Modules.RiskEngine /// /// 将新风控引擎结果转换为现有试算记录,供详情页面和二次确认复用。 /// - private QuotaTrial CreateQuotaTrial(RiskContext context, int trialSource, RiskResult riskResult, IReadOnlyCollection pendingApprovalRules) + private QuotaTrial CreateQuotaTrial(RiskContext context, int trialSource, RiskResult riskResult, IReadOnlyCollection pendingApprovalRules, IReadOnlyCollection pendingTipRules) { // 业务入口已通过Find加载交易,优先复用当前上下文跟踪的实体,避免重复查询数据库。 var trade = DbContext.trade.Local.FirstOrDefault(item => item.id == context.TradeId) @@ -178,9 +187,9 @@ namespace YLErp.Modules.RiskEngine : pendingApprovalRules.Any() ? QuotaTrialStatusEnum.RiskWarning : QuotaTrialStatusEnum.Success, NewRiskBlocked = riskResult.Blocked, NewRiskNeedApproval = pendingApprovalRules.Any(), - RiskWarningDetails = BuildRiskDetails(context, riskResult, pendingApprovalRules) + RiskWarningDetails = BuildRiskDetails(context, riskResult, pendingApprovalRules, pendingTipRules) }; - trial.ApprovalRuleIds = pendingApprovalRules + trial.ApprovalRuleIds = pendingApprovalRules.Concat(pendingTipRules) .Select(rule => rule.RuleId) .Where(ruleId => !string.IsNullOrWhiteSpace(ruleId)) .Distinct() @@ -191,7 +200,7 @@ namespace YLErp.Modules.RiskEngine /// /// 按控制策略生成与现有试算详情页面兼容的规则明细。 /// - private static string BuildRiskDetails(RiskContext context, RiskResult riskResult, IReadOnlyCollection pendingApprovalRules) + private static string BuildRiskDetails(RiskContext context, RiskResult riskResult, IReadOnlyCollection pendingApprovalRules, IReadOnlyCollection pendingTipRules) { var lines = new List { @@ -199,7 +208,7 @@ namespace YLErp.Modules.RiskEngine }; AppendRuleDetails(lines, "禁止", riskResult.TriggeredRules.Where(rule => rule.ControlStrategy == RiskControlStrategy.Block)); AppendRuleDetails(lines, "需审批", pendingApprovalRules); - AppendRuleDetails(lines, "提示", riskResult.TriggeredRules.Where(rule => rule.ControlStrategy == RiskControlStrategy.ShowTip)); + AppendRuleDetails(lines, "提示", pendingTipRules); return string.Join("\n", lines); } @@ -208,7 +217,19 @@ namespace YLErp.Modules.RiskEngine /// private static string BuildRequestContext(RiskContext context) { - return $"[风控引擎] 请求上下文:触发点:{context.TriggerPoint ?? string.Empty};平仓日期:{context.UnwindDate?.Date:yyyy-MM-dd}"; + return $"[风控引擎] 请求上下文:触发点:{GetTriggerPointDisplayName(context.TriggerPoint)};平仓日期:{context.UnwindDate?.Date:yyyy-MM-dd};支付日期:{context.PayDate?.Date:yyyy-MM-dd}"; + } + + private static string GetTriggerPointDisplayName(string triggerPoint) + { + return triggerPoint switch + { + "BOOK_CONFIRM" => "簿记交易确认", + "CLOSE_REVIEW" => "平仓审核提交", + "UPLOAD_CONFIRMATION" => "上传确认书", + "EVENT_TRIGGER" => "事件发生时", + _ => triggerPoint ?? string.Empty + }; } ///