From b87414aafd35929454dff9552d91794cd24dbef6 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Wed, 28 Oct 2026 15:40:14 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=87=AA=E5=8A=A8=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E5=85=AC=E5=BC=8F=E4=BF=AE=E5=A4=8D=E6=8B=AC=E5=8F=B7=E5=B8=83?= =?UTF-8?q?=E5=B0=94=E7=BB=84=E4=B8=8E=20||/&&=20=E7=9F=AD=E8=B7=AF?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../Orders/Issues/ChineseFormulaEvaluator.cs | 29 +++++++- src/Oms.Tests/ChineseFormulaEvaluatorTests.cs | 73 +++++++++++++++++++ 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/Oms.Tests/ChineseFormulaEvaluatorTests.cs diff --git a/src/Oms.Application/Services/Orders/Issues/ChineseFormulaEvaluator.cs b/src/Oms.Application/Services/Orders/Issues/ChineseFormulaEvaluator.cs index b4dd466..9ae8ed3 100644 --- a/src/Oms.Application/Services/Orders/Issues/ChineseFormulaEvaluator.cs +++ b/src/Oms.Application/Services/Orders/Issues/ChineseFormulaEvaluator.cs @@ -314,7 +314,9 @@ public static class ChineseFormulaEvaluator while (_current.Kind == TokenKind.Or) { Advance(); - value = value || ParseAnd(); + // 必须先解析右侧,禁止用 || 短路跳过,否则剩余 token 会让 ExpectEnd 失败 + var right = ParseAnd(); + value = value || right; } return value; @@ -326,7 +328,9 @@ public static class ChineseFormulaEvaluator while (_current.Kind == TokenKind.And) { Advance(); - value = value && ParseNot(); + // 同上:禁止 && 短路跳过右侧解析 + var right = ParseNot(); + value = value && right; } return value; @@ -510,8 +514,18 @@ public static class ChineseFormulaEvaluator _ => false }; + /// + /// 括号内是否应按布尔表达式解析。 + /// 注意:调用前已 Advance 掉 '(',第一个内部 token 在 _current;词法位置在其后。 + /// 旧逻辑只向后扫逻辑运算符,导致括号内的包含/有值/为空/比较被误判为数值式而解析失败。 + /// private bool LooksLikeBooleanGroup() { + if (IsBooleanAtomKind(_current.Kind)) + { + return true; + } + var savedPos = _lexer.SavePosition(); var depth = 1; while (depth > 0) @@ -534,7 +548,7 @@ public static class ChineseFormulaEvaluator continue; } - if (depth == 1 && token.Kind is TokenKind.And or TokenKind.Or) + if (depth == 1 && IsBooleanGroupIndicator(token.Kind)) { _lexer.RestorePosition(savedPos); return true; @@ -545,6 +559,15 @@ public static class ChineseFormulaEvaluator return false; } + private static bool IsBooleanAtomKind(TokenKind kind) => + kind is TokenKind.Contains or TokenKind.HasValue or TokenKind.IsEmpty or TokenKind.Not; + + private static bool IsBooleanGroupIndicator(TokenKind kind) => + kind is TokenKind.And or TokenKind.Or + or TokenKind.Contains or TokenKind.HasValue or TokenKind.IsEmpty or TokenKind.Not + or TokenKind.Greater or TokenKind.GreaterEqual or TokenKind.Less or TokenKind.LessEqual + or TokenKind.Equal or TokenKind.NotEqual; + private void Advance() => _current = _lexer.Next(); } } diff --git a/src/Oms.Tests/ChineseFormulaEvaluatorTests.cs b/src/Oms.Tests/ChineseFormulaEvaluatorTests.cs new file mode 100644 index 0000000..2a9390e --- /dev/null +++ b/src/Oms.Tests/ChineseFormulaEvaluatorTests.cs @@ -0,0 +1,73 @@ +using FluentAssertions; +using Oms.Application.Dtos.Orders; +using Oms.Application.Services.Orders.Issues; +using Oms.Domain.CustomersAggregate.Entities; +using Xunit; + +namespace Oms.Tests; + +public class ChineseFormulaEvaluatorTests +{ + [Theory] + [InlineData("产品标识编码为空()", true)] + [InlineData("产品标识编码有值()", false)] + [InlineData("!产品标识编码有值()", true)] + [InlineData("销售产品包含('US02-5DAY')", true)] + [InlineData("销售产品包含('US02-5DAY')&&产品标识编码为空()", true)] + [InlineData("(销售产品包含('US02-5DAY'))&&产品标识编码为空()", true)] + [InlineData("销售产品包含('US02-5DAY')&&(产品标识编码为空())", true)] + [InlineData("产品标识编码为空()||非标产品标识编码为空()", true)] + [InlineData("(销售产品包含('US02-5DAY')||销售产品包含('OTHER'))&&(产品标识编码为空()||非标产品标识编码为空())", true)] + [InlineData("(销售产品包含('3PE快递美国专线-5日达')||销售产品包含('US02-5DAY'))&&(产品标识编码为空()||非标产品标识编码为空())", true)] + [InlineData("(实重()>1)&&产品标识编码为空()", true)] + public void TryEvaluate_EmptyProductIds_ShouldMatch(string expression, bool expected) + { + var context = BuildPreCommitContext(productCode: "US02-5DAY", productId: "", nonStdProductId: "", actualWeight: 1.2m); + + var ok = ChineseFormulaEvaluator.TryEvaluate(expression, context, out var matched); + + ok.Should().BeTrue($"expression should parse: {expression}"); + matched.Should().Be(expected); + } + + [Fact] + public void TryEvaluate_FilledProductIds_ShouldNotMatchEmptyCheck() + { + var context = BuildPreCommitContext(productCode: "US02-5DAY", productId: "P1", nonStdProductId: "N1", actualWeight: 1.2m); + var expression = + "(销售产品包含('US02-5DAY'))&&(产品标识编码为空()||非标产品标识编码为空())"; + + var ok = ChineseFormulaEvaluator.TryEvaluate(expression, context, out var matched); + + ok.Should().BeTrue(); + matched.Should().BeFalse(); + } + + private static OrderRuleEvaluationContext BuildPreCommitContext( + string productCode, + string? productId, + string? nonStdProductId, + decimal actualWeight) + { + var input = new OrderParcelCreateInput + { + ProductCode = productCode, + ActualWeight = actualWeight, + Items = + [ + new OrderParcelItemInput + { + CnName = "手机壳", + ProductId = productId, + NonStdProductId = nonStdProductId + } + ] + }; + + return OrderRuleEvaluationContext.FromPreCommit( + input, + new Customer { CustomerName = "test" }, + "US", + "美国"); + } +}