bugfix:规则批量启用

This commit is contained in:
尹峰
2026-07-16 12:32:17 +08:00
parent ba8e998919
commit 19a7b34e84
3 changed files with 148 additions and 30 deletions
@@ -0,0 +1,10 @@
namespace YLErp.Modules.RiskEngine.Dto
{
/// <summary>
/// 批量操作请求(兼容前端 {ids: [...]} 格式)
/// </summary>
public class BatchIdsReq
{
public List<long> Ids { get; set; }
}
}
+125 -23
View File
@@ -540,6 +540,19 @@ namespace YLErp.Modules.RiskEngine
return ids;
}
/// <summary>
/// EF Core 可翻译的逗号分隔字段精确匹配表达式
/// </summary>
private static Expression<Func<glms_risk_rule_application, bool>> RuleIdsMatchExpr(long ruleId)
{
var idStr = ruleId.ToString();
return a => a.RuleIds != null &&
(a.RuleIds == idStr ||
a.RuleIds.StartsWith(idStr + ",") ||
a.RuleIds.EndsWith("," + idStr) ||
a.RuleIds.Contains("," + idStr + ","));
}
/// <summary>
/// 校验规则状态可用性并确保编译缓存就绪。
/// 返回不可用规则信息列表,列表为空表示全部通过。
@@ -722,7 +735,12 @@ namespace YLErp.Modules.RiskEngine
OptDate = r.OptDate.GetValueOrDefault(),
UpdateOptName = r.UpdateOptName,
UpdateDate = r.UpdateDate ?? r.OptDate.GetValueOrDefault(),
ApplicationCount = DbContext.glms_risk_rule_application.Count(a => a.RuleIds.Contains(r.id.ToString()) && a.Status != RiskRuleStatus.Deleted)
ApplicationCount = DbContext.glms_risk_rule_application
.Count(a => a.RuleIds != null &&
(a.RuleIds == r.id.ToString() ||
a.RuleIds.StartsWith(r.id.ToString() + ",") ||
a.RuleIds.EndsWith("," + r.id.ToString()) ||
a.RuleIds.Contains("," + r.id.ToString() + ",")))
});
return result.ToSearchList(req);
@@ -735,9 +753,9 @@ namespace YLErp.Modules.RiskEngine
{
var rule = GetRuleOrThrow(ruleId);
var ruleIdStr = ruleId.ToString();
var applications = DbContext.glms_risk_rule_application
.Where(a => a.RuleIds.Contains(ruleIdStr) && a.Status != RiskRuleStatus.Deleted)
.Where(RuleIdsMatchExpr(ruleId))
.Select(a => new RiskRuleApplicationSummary
{
Id = a.id,
@@ -858,9 +876,10 @@ namespace YLErp.Modules.RiskEngine
{
var rule = GetRuleOrThrow(ruleId);
var ruleIdStr = ruleId.ToString();
var activeCount = DbContext.glms_risk_rule_application
.Count(a => a.RuleIds.Contains(ruleIdStr) && a.Status == RiskRuleStatus.Active);
.Where(a => a.Status == RiskRuleStatus.Active)
.Count(RuleIdsMatchExpr(ruleId));
if (activeCount > 0)
throw new ServiceException($"该规则仍有 {activeCount} 个生效中的应用配置,请先停用或删除相关应用");
@@ -965,24 +984,58 @@ namespace YLErp.Modules.RiskEngine
/// </summary>
public BatchOperationResult BatchDeleteRules(List<long> ruleIds)
{
if (ruleIds == null || ruleIds.Count == 0)
throw new ServiceException("规则ID列表不能为空");
_logger.Info($"[批量删除] 开始 - 传入规则数: {ruleIds.Count}, IDs: {string.Join(",", ruleIds)}");
var rules = DbContext.glms_risk_rule
.Where(r => ruleIds.Contains(r.id) && r.Status != RiskRuleStatus.Deleted)
.ToList();
if (rules.Count != ruleIds.Count)
throw new ServiceException("部分规则不存在或已删除");
_logger.Info($"[批量删除] DB查询完成 - 匹配到 {rules.Count} 条规则");
var foundIds = rules.Select(r => (long)r.id).ToList();
var missingIds = ruleIds.Except(foundIds).ToList();
if (missingIds.Any())
{
return new BatchOperationResult
{
Success = false,
TotalCount = ruleIds.Count,
SuccessCount = 0,
ErrorMessage = $"以下规则不存在或已删除:{string.Join(", ", missingIds)}"
};
}
var blockedRules = new List<string>();
foreach (var rule in rules)
{
var ruleIdStr = rule.id.ToString();
var activeCount = DbContext.glms_risk_rule_application
.Count(a => a.RuleIds.Contains(ruleIdStr) && a.Status == RiskRuleStatus.Active);
_logger.Info($"[批量删除] 检查关联应用 - 规则ID: {rule.id}");
var activeApps = DbContext.glms_risk_rule_application
.Where(a => a.Status == RiskRuleStatus.Active)
.Where(RuleIdsMatchExpr(rule.id))
.Select(a => (long)a.id)
.ToList();
var activeCount = activeApps.Count;
_logger.Info($"[批量删除] 关联应用检查结果 - 规则ID: {rule.id}, ActiveCount: {activeCount}, AppIds: [{string.Join(",", activeApps)}]");
if (activeCount > 0)
throw new ServiceException($"规则 '{rule.RuleName}' 仍有 {activeCount} 个生效中的应用配置,请先停用或删除相关应用");
blockedRules.Add($"规则 '{rule.RuleName}'(ID:{rule.id}) 仍有 {activeCount} 个生效中的应用配置(IDs: {string.Join(",", activeApps)})");
}
if (blockedRules.Any())
{
return new BatchOperationResult
{
Success = false,
TotalCount = ruleIds.Count,
SuccessCount = 0,
ErrorMessage = string.Join("", blockedRules)
};
}
foreach (var rule in rules)
{
_logger.Info($"[批量删除] 写入状态 - 规则ID: {rule.id}");
rule.Status = RiskRuleStatus.Deleted;
rule.Version = rule.Version + 1;
rule.UpdateOptId = UserId;
@@ -992,11 +1045,14 @@ namespace YLErp.Modules.RiskEngine
WriteAuditLog("RULE_BATCH_DELETE", "RULE", rule.id, rule.RuleName, "批量删除规则");
}
_logger.Info("[批量删除] 保存数据库...");
DbContext.SaveChanges();
_logger.Info("[批量删除] 刷新规则缓存...");
foreach (var ruleId in ruleIds)
{
RiskEngineService.GetInstance().RefreshOneRuleCache(ruleId);
}
_logger.Info("[批量删除] 完成");
return new BatchOperationResult
{
@@ -1013,9 +1069,8 @@ namespace YLErp.Modules.RiskEngine
{
GetRuleOrThrow(ruleId);
var ruleIdStr = ruleId.ToString();
var apps = DbContext.glms_risk_rule_application
.Where(a => a.RuleIds.Contains(ruleIdStr) && a.Status != RiskRuleStatus.Deleted)
.Where(RuleIdsMatchExpr(ruleId))
.OrderByDescending(a => a.UpdateDate)
.ToList();
@@ -1093,8 +1148,12 @@ namespace YLErp.Modules.RiskEngine
{
query = query.Where(a => DbContext.glms_risk_rule
.Any(r => r.Status != RiskRuleStatus.Deleted
&& a.RuleIds.Contains(r.id.ToString())
&& r.RuleName.Contains(req.RuleName)));
&& r.RuleName.Contains(req.RuleName)
&& a.RuleIds != null
&& (a.RuleIds == r.id.ToString()
|| a.RuleIds.StartsWith(r.id.ToString() + ",")
|| a.RuleIds.EndsWith("," + r.id.ToString())
|| a.RuleIds.Contains("," + r.id.ToString() + ","))));
}
if (!string.IsNullOrWhiteSpace(req.ScopeClientIds))
@@ -1342,16 +1401,46 @@ namespace YLErp.Modules.RiskEngine
/// </summary>
public BatchOperationResult BatchEnableApplications(List<long> applicationIds)
{
_logger.Info($"[批量启用] 开始 - 传入应用数: {applicationIds?.Count ?? 0}, IDs: {string.Join(",", applicationIds ?? new List<long>())}");
if (applicationIds == null || applicationIds.Count == 0)
throw new ServiceException("应用配置ID列表不能为空");
_logger.Info($"[批量启用] 开始 - 传入应用数: {applicationIds.Count}, IDs: {string.Join(",", applicationIds)}");
var apps = DbContext.glms_risk_rule_application
.Where(a => applicationIds.Contains(a.id) && a.Status == RiskRuleStatus.Disabled)
.Where(a => applicationIds.Contains(a.id))
.ToList();
_logger.Info($"[批量启用] DB查询完成 - 匹配到 {apps.Count} 条已停用应用");
_logger.Info($"[批量启用] DB查询完成 - 匹配到 {apps.Count} 条应用");
if (apps.Count != applicationIds.Count)
throw new ServiceException("部分应用配置不存在或当前状态不允许启用");
var foundIds = apps.Select(a => (long)a.id).ToList();
var missingIds = applicationIds.Except(foundIds).ToList();
if (missingIds.Any())
{
return new BatchOperationResult
{
Success = false,
TotalCount = applicationIds.Count,
SuccessCount = 0,
ErrorMessage = $"以下应用配置不存在:{string.Join(", ", missingIds)}"
};
}
// 跳过已启用的应用,只处理已停用的
var skippedIds = apps.Where(a => a.Status == RiskRuleStatus.Active).Select(a => (long)a.id).ToList();
if (skippedIds.Any())
_logger.Info($"[批量启用] 跳过已启用应用 - IDs: {string.Join(",", skippedIds)}");
apps = apps.Where(a => a.Status != RiskRuleStatus.Active).ToList();
if (!apps.Any())
{
_logger.Info("[批量启用] 所有应用均已启用,无需操作");
return new BatchOperationResult
{
Success = true,
TotalCount = applicationIds.Count,
SuccessCount = applicationIds.Count
};
}
var allErrors = new List<string>();
foreach (var app in apps)
@@ -1404,7 +1493,10 @@ namespace YLErp.Modules.RiskEngine
/// </summary>
public BatchOperationResult BatchDisableApplications(List<long> applicationIds)
{
_logger.Info($"[批量停用] 开始 - 传入应用数: {applicationIds?.Count ?? 0}, IDs: {string.Join(",", applicationIds ?? new List<long>())}");
if (applicationIds == null || applicationIds.Count == 0)
throw new ServiceException("应用配置ID列表不能为空");
_logger.Info($"[批量停用] 开始 - 传入应用数: {applicationIds.Count}, IDs: {string.Join(",", applicationIds)}");
var apps = DbContext.glms_risk_rule_application
.Where(a => applicationIds.Contains(a.id) && a.Status == RiskRuleStatus.Active)
@@ -1412,8 +1504,18 @@ namespace YLErp.Modules.RiskEngine
_logger.Info($"[批量停用] DB查询完成 - 匹配到 {apps.Count} 条已启用应用");
if (apps.Count != applicationIds.Count)
throw new ServiceException("部分应用配置不存在或当前状态不允许停用");
var foundIds = apps.Select(a => (long)a.id).ToList();
var missingIds = applicationIds.Except(foundIds).ToList();
if (missingIds.Any())
{
return new BatchOperationResult
{
Success = false,
TotalCount = applicationIds.Count,
SuccessCount = apps.Count,
ErrorMessage = $"以下应用配置不存在或当前状态不允许停用:{string.Join(", ", missingIds)}"
};
}
foreach (var app in apps)
{
+13 -7
View File
@@ -1,4 +1,4 @@
using Qdp.Foundation.Utilities;
using Qdp.Foundation.Utilities;
using System.Threading.Tasks;
using YLErp.DBModels;
using YLErp.Modules.RiskEngine;
@@ -151,12 +151,14 @@ namespace YLErp.Web.Controllers
[HttpDelete("risk-rules/batch")]
[MyAuthorize("风险控制-风控规则删除")]
/// <summary>批量删除风控规则</summary>
public async Task<JsonResult> BatchDeleteRiskRules([FromBody] List<long> ids)
public async Task<JsonResult> BatchDeleteRiskRules([FromBody] BatchIdsReq req)
{
try
{
var service = GetRiskRuleService();
var result = await Task.Run(() => service.BatchDeleteRules(ids));
var result = await Task.Run(() => service.BatchDeleteRules(req?.Ids));
if (!result.Success)
return Json(new { success = false, message = result.ErrorMessage });
return Json(new { success = true, data = result });
}
catch (ServiceException ex)
@@ -419,12 +421,14 @@ namespace YLErp.Web.Controllers
[HttpPost("risk-applications/batch-enable")]
[MyAuthorize("风险控制-风控应用启停")]
/// <summary>批量启用应用配置</summary>
public async Task<JsonResult> BatchEnableRiskApplications([FromBody] List<long> ids)
public async Task<JsonResult> BatchEnableRiskApplications([FromBody] BatchIdsReq req)
{
try
{
var service = GetRiskRuleService();
var result = await Task.Run(() => service.BatchEnableApplications(ids));
var result = await Task.Run(() => service.BatchEnableApplications(req?.Ids));
if (!result.Success)
return Json(new { success = false, message = result.ErrorMessage });
return Json(new { success = true, data = result });
}
catch (ServiceException ex)
@@ -441,12 +445,14 @@ namespace YLErp.Web.Controllers
[HttpPost("risk-applications/batch-disable")]
[MyAuthorize("风险控制-风控应用启停")]
/// <summary>批量停用应用配置</summary>
public async Task<JsonResult> BatchDisableRiskApplications([FromBody] List<long> ids)
public async Task<JsonResult> BatchDisableRiskApplications([FromBody] BatchIdsReq req)
{
try
{
var service = GetRiskRuleService();
var result = await Task.Run(() => service.BatchDisableApplications(ids));
var result = await Task.Run(() => service.BatchDisableApplications(req?.Ids));
if (!result.Success)
return Json(new { success = false, message = result.ErrorMessage });
return Json(new { success = true, data = result });
}
catch (ServiceException ex)