fix: avoid SqlSugar invalid SQL from null/HasValue KB filters
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
95940331ce
commit
7e442180cd
@ -209,33 +209,68 @@ public class KbArticleService : ApplicationService
|
||||
|
||||
private static Expression<Func<KbArticle, bool>> BuildAdminPredicate(KbArticleListInput input)
|
||||
{
|
||||
var keyword = input.Keyword?.Trim();
|
||||
var keyword = NormalizeOptional(input.Keyword);
|
||||
var status = NormalizeOptional(input.Status);
|
||||
var audience = NormalizeOptional(input.Audience);
|
||||
return x =>
|
||||
(!input.CategoryId.HasValue || x.CategoryId == input.CategoryId.Value)
|
||||
&& (status == null || x.Status == status)
|
||||
&& (audience == null || x.Audience == audience)
|
||||
&& (!input.IsPinned.HasValue || x.IsPinned == input.IsPinned.Value)
|
||||
&& (keyword == null
|
||||
|| x.Title.Contains(keyword)
|
||||
|| (x.Summary != null && x.Summary.Contains(keyword))
|
||||
|| (x.Tags != null && x.Tags.Contains(keyword)));
|
||||
var predicate = ExpressionPredicate.True<KbArticle>();
|
||||
|
||||
if (input.CategoryId is int categoryId)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(predicate, x => x.CategoryId == categoryId);
|
||||
}
|
||||
|
||||
if (status != null)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(predicate, x => x.Status == status);
|
||||
}
|
||||
|
||||
if (audience != null)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(predicate, x => x.Audience == audience);
|
||||
}
|
||||
|
||||
if (input.IsPinned is bool isPinned)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(predicate, x => x.IsPinned == isPinned);
|
||||
}
|
||||
|
||||
if (keyword != null)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(
|
||||
predicate,
|
||||
x => x.Title.Contains(keyword)
|
||||
|| (x.Summary != null && x.Summary.Contains(keyword))
|
||||
|| (x.Tags != null && x.Tags.Contains(keyword)));
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private static Expression<Func<KbArticle, bool>> BuildPortalPredicate(string? keyword, int? categoryId)
|
||||
{
|
||||
var kw = NormalizeOptional(keyword);
|
||||
var now = DateTime.Now;
|
||||
return x =>
|
||||
x.Status == KbArticleStatusCodes.Published
|
||||
&& x.Audience == KbAudienceCodes.Customer
|
||||
&& (x.ExpireTime == null || x.ExpireTime > now)
|
||||
&& (!categoryId.HasValue || x.CategoryId == categoryId.Value)
|
||||
&& (kw == null
|
||||
|| x.Title.Contains(kw)
|
||||
|| (x.Summary != null && x.Summary.Contains(kw))
|
||||
|| (x.Tags != null && x.Tags.Contains(kw)));
|
||||
var predicate = ExpressionPredicate.AndAlso(
|
||||
ExpressionPredicate.True<KbArticle>(),
|
||||
x => x.Status == KbArticleStatusCodes.Published
|
||||
&& x.Audience == KbAudienceCodes.Customer
|
||||
&& (x.ExpireTime == null || x.ExpireTime > now));
|
||||
|
||||
if (categoryId is int cid)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(predicate, x => x.CategoryId == cid);
|
||||
}
|
||||
|
||||
if (kw != null)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(
|
||||
predicate,
|
||||
x => x.Title.Contains(kw)
|
||||
|| (x.Summary != null && x.Summary.Contains(kw))
|
||||
|| (x.Tags != null && x.Tags.Contains(kw)));
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private static void EnsurePortalVisible(KbArticle entity)
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
using System.Linq.Expressions;
|
||||
using Fake.Application;
|
||||
using Fake.Domain.Exceptions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oms.Application.Dtos.Kb;
|
||||
using Oms.Application.Dtos.Shared;
|
||||
using Oms.Application.Shared;
|
||||
using Oms.Domain.KbAggregate.Entities;
|
||||
using Oms.Domain.KbAggregate.Repositories;
|
||||
|
||||
@ -23,8 +25,13 @@ public class KbCategoryService : ApplicationService
|
||||
[HttpGet("/kb/admin/category/list")]
|
||||
public async Task<OmsItemsResult<KbCategoryOutput>> AdminListAsync([FromQuery] bool? enabledOnly = null)
|
||||
{
|
||||
var items = await CategoryRepository.GetListAsync(x =>
|
||||
!enabledOnly.HasValue || x.Enabled == enabledOnly.Value);
|
||||
Expression<Func<KbCategory, bool>> predicate = ExpressionPredicate.True<KbCategory>();
|
||||
if (enabledOnly is bool enabled)
|
||||
{
|
||||
predicate = ExpressionPredicate.AndAlso(predicate, x => x.Enabled == enabled);
|
||||
}
|
||||
|
||||
var items = await CategoryRepository.GetListAsync(predicate);
|
||||
var ordered = items.OrderBy(x => x.SortOrder).ThenBy(x => x.Id).ToList();
|
||||
return new OmsItemsResult<KbCategoryOutput>
|
||||
{
|
||||
|
||||
38
src/Oms.Application/Shared/ExpressionPredicate.cs
Normal file
38
src/Oms.Application/Shared/ExpressionPredicate.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Oms.Application.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// 组合 LINQ 谓词,避免把 `HasValue` / `== null` 写进表达式导致 SqlSugar 生成非法 SQL。
|
||||
/// </summary>
|
||||
public static class ExpressionPredicate
|
||||
{
|
||||
public static Expression<Func<T, bool>> True<T>() => static _ => true;
|
||||
|
||||
public static Expression<Func<T, bool>> AndAlso<T>(
|
||||
Expression<Func<T, bool>> left,
|
||||
Expression<Func<T, bool>> right)
|
||||
{
|
||||
var parameter = Expression.Parameter(typeof(T), "x");
|
||||
var leftBody = new ReplaceParameterVisitor(left.Parameters[0], parameter).Visit(left.Body)
|
||||
?? throw new InvalidOperationException("left predicate body is null");
|
||||
var rightBody = new ReplaceParameterVisitor(right.Parameters[0], parameter).Visit(right.Body)
|
||||
?? throw new InvalidOperationException("right predicate body is null");
|
||||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(leftBody, rightBody), parameter);
|
||||
}
|
||||
|
||||
private sealed class ReplaceParameterVisitor : ExpressionVisitor
|
||||
{
|
||||
private readonly ParameterExpression _from;
|
||||
private readonly ParameterExpression _to;
|
||||
|
||||
public ReplaceParameterVisitor(ParameterExpression from, ParameterExpression to)
|
||||
{
|
||||
_from = from;
|
||||
_to = to;
|
||||
}
|
||||
|
||||
protected override Expression VisitParameter(ParameterExpression node) =>
|
||||
ReferenceEquals(node, _from) ? _to : base.VisitParameter(node);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user