using SqlSugar;
using System.Diagnostics;
using System.DirectoryServices;
using System.Linq;
using System.Linq.Expressions;
namespace OASystem.API.OAMethodLib.GenericSearch
{
///
/// 动态检索服务
/// 支持动态字段权重配置、返回字段筛选、智能搜索等功能
///
/// 实体类型
public class DynamicSearchService where T : class, new()
{
private readonly SqlSugarClient _db;
private readonly ILogger> _logger;
public DynamicSearchService(SqlSugarClient db, ILogger> logger)
{
_db = db;
_logger = logger;
}
///
/// 执行动态搜索(应用层统计匹配度)
///
/// 搜索请求参数
/// 包含搜索结果和匹配度信息的结果对象
public async Task> SearchAsync(DynamicSearchRequest request)
{
var resultView = new SearchResult() { Success = false, Message = "异常错误" };
var stopwatch = Stopwatch.StartNew();
var searchId = Guid.NewGuid().ToString("N")[..8];
try
{
List data;
int totalCount;
// 使用原生SQL方式构建查询
if (!string.IsNullOrWhiteSpace(request.Keyword))
{
var result = await SearchWithNativeSqlAsync(request);
data = result.Data;
totalCount = result.TotalCount;
}
else
{
// 无关键词时使用简单查询
var query = BuildBaseQuery(request);
totalCount = await query.CountAsync();
data = await query.ToPageListAsync(request.PageIndex, request.PageSize);
}
// 在应用层计算匹配度
var scoredItems = CalculateMatchScore(data, request);
stopwatch.Stop();
return new SearchResult
{
Message = $"搜索成功!耗时:{stopwatch.ElapsedMilliseconds}ms",
Items = scoredItems,
TotalCount = totalCount,
Keyword = request.Keyword,
FieldWeights = request.FieldWeights,
ReturnFields = request.ReturnFields,
PageIndex = request.PageIndex,
PageSize = request.PageSize,
ResponseTime = stopwatch.ElapsedMilliseconds,
SearchId = searchId
};
}
catch (Exception ex)
{
stopwatch.Stop();
resultView.Message = string.Format("【{SearchId}】动态搜索失败: {ErrorMessage}", searchId, ex.Message);
return resultView;
}
}
///
/// 轻量级搜索 - 只返回指定字段,提升性能(应用层统计匹配度)
///
/// 返回的结果类型
/// 搜索请求参数
/// 字段选择表达式
/// 包含指定字段和匹配度信息的搜索结果
public async Task> LightweightSearchAsync(
DynamicSearchRequest request,
Expression> selector) where TResult : class, new()
{
var stopwatch = Stopwatch.StartNew();
var searchId = Guid.NewGuid().ToString("N")[..8];
_logger.LogInformation("【{SearchId}】开始轻量级搜索: 实体{Entity}, 返回类型{ResultType}",
searchId, typeof(T).Name, typeof(TResult).Name);
try
{
// 构建基础查询
var baseQuery = _db.Queryable();
// 应用过滤条件
baseQuery = ApplyFilters(baseQuery, request.Filters);
// 应用搜索条件
if (!string.IsNullOrWhiteSpace(request.Keyword))
{
var searchAnalysis = AnalyzeSearchPattern(request.Keyword);
if (searchAnalysis.HasSearchContent)
{
var searchFields = request.FieldWeights?.Keys.ToList() ?? GetDefaultSearchFields();
var searchConditions = BuildSearchConditions(searchAnalysis, searchFields);
if (searchConditions.Any())
{
baseQuery = baseQuery.Where(searchConditions);
}
}
}
// 应用字段选择 - 在数据库层面进行字段选择
var finalQuery = baseQuery.Select(selector);
// 应用排序
finalQuery = ApplyOrderByForLightweight(finalQuery, request.OrderBy, request.IsDescending);
// 执行查询获取轻量级数据
var totalCount = await finalQuery.CountAsync();
var lightweightData = await finalQuery.ToPageListAsync(request.PageIndex, request.PageSize);
// 为了计算匹配度,需要查询完整的实体数据
List fullDataForScoring;
if (!string.IsNullOrWhiteSpace(request.Keyword))
{
var fullResult = await SearchWithNativeSqlAsync(request);
fullDataForScoring = fullResult.Data;
}
else
{
var fullQuery = BuildBaseQuery(request);
fullDataForScoring = await fullQuery.ToPageListAsync(request.PageIndex, request.PageSize);
}
// 计算匹配度
var scoredItems = CalculateMatchScore(fullDataForScoring, request);
// 将匹配度信息与轻量级数据关联
var lightweightItems = AssociateMatchScores(lightweightData, scoredItems, selector);
stopwatch.Stop();
_logger.LogInformation("【{SearchId}】轻量级搜索完成: 找到 {Count} 条记录, 耗时 {TotalTime}ms",
searchId, lightweightItems.Count, stopwatch.ElapsedMilliseconds);
return new SearchResult
{
Items = lightweightItems,
TotalCount = totalCount,
Keyword = request.Keyword,
FieldWeights = request.FieldWeights,
PageIndex = request.PageIndex,
PageSize = request.PageSize,
ResponseTime = stopwatch.ElapsedMilliseconds,
SearchId = searchId
};
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "【{SearchId}】轻量级搜索失败", searchId);
throw;
}
}
///
/// 将匹配度信息与轻量级数据关联
///
private List> AssociateMatchScores(
List lightweightData,
List> scoredItems,
Expression> selector) where TResult : class, new()
{
var result = new List>();
// 构建一个字典来快速查找匹配度信息
var scoreDict = new Dictionary>();
foreach (var scoredItem in scoredItems)
{
var id = GetEntityId(scoredItem.Data);
if (id > 0)
{
scoreDict[id] = scoredItem;
}
}
// 关联匹配度信息
foreach (var lightItem in lightweightData)
{
var id = GetEntityId(lightItem);
if (id > 0 && scoreDict.TryGetValue(id, out var scoredItem))
{
result.Add(new SearchResultItem
{
Data = lightItem,
MatchScore = scoredItem.MatchScore,
MatchFields = scoredItem.MatchFields
});
}
else
{
result.Add(new SearchResultItem
{
Data = lightItem,
MatchScore = 0,
MatchFields = new List()
});
}
}
return result.OrderByDescending(x => x.MatchScore).ToList();
}
///
/// 获取实体ID(通过反射)
///
private int GetEntityId(TEntity entity)
{
if (entity == null) return 0;
var idProperty = typeof(TEntity).GetProperty("Id");
if (idProperty != null && idProperty.PropertyType == typeof(int))
{
return (int)(idProperty.GetValue(entity) ?? 0);
}
return 0;
}
///
/// 获取实体可搜索字段信息
///
/// 可搜索字段信息列表,按权重降序排列
public List GetSearchableFields()
{
var entityType = typeof(T);
var properties = entityType.GetProperties();
var searchableFields = new List();
foreach (var prop in properties)
{
var fieldInfo = new FieldInfo
{
FieldName = prop.Name,
DisplayName = GetDisplayName(prop),
DataType = prop.PropertyType.Name,
IsSearchable = prop.PropertyType == typeof(string),
DefaultWeight = GetDefaultWeight(prop.Name),
Description = GetFieldDescription(prop),
CanFilter = true,
CanSort = true
};
searchableFields.Add(fieldInfo);
}
return searchableFields
.OrderByDescending(f => f.DefaultWeight)
.ThenBy(f => f.FieldName)
.ToList();
}
///
/// 验证字段配置
///
/// 字段权重配置
/// 返回字段列表
/// 验证结果
public (bool IsValid, string Message) ValidateFieldConfig(
Dictionary fieldWeights,
List returnFields)
{
var allFields = GetSearchableFields();
var validFieldNames = allFields.Select(f => f.FieldName).ToList();
// 验证搜索字段
if (fieldWeights != null)
{
var invalidSearchFields = fieldWeights.Keys.Except(validFieldNames).ToList();
if (invalidSearchFields.Any())
{
return (false, $"无效的搜索字段: {string.Join(", ", invalidSearchFields)}");
}
}
// 验证返回字段
if (returnFields != null)
{
var invalidReturnFields = returnFields.Except(validFieldNames).ToList();
if (invalidReturnFields.Any())
{
return (false, $"无效的返回字段: {string.Join(", ", invalidReturnFields)}");
}
}
return (true, "字段配置有效");
}
#region 私有方法 - 搜索逻辑
///
/// 使用原生SQL进行搜索
///
private async Task<(List Data, int TotalCount)> SearchWithNativeSqlAsync(DynamicSearchRequest request)
{
var whereConditions = new List();
var parameters = new List();
// 获取搜索字段
var searchFields = request.FieldWeights?.Keys.ToList() ?? GetDefaultSearchFields();
var validFields = ValidateSearchFields(searchFields);
// 构建搜索条件
if (!string.IsNullOrWhiteSpace(request.Keyword))
{
#region and 构建
var searchAnalysis = AnalyzeSearchPattern(request.Keyword);
var keywordConditions = new List(); // 专门存放关键字相关条件
// 符号分割的关键字条件
foreach (var segment in searchAnalysis.SymbolSegments)
{
var cleanSegment = Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
if (!string.IsNullOrEmpty(cleanSegment))
{
var fieldConditions = validFields.Select(field =>
{
var paramName = $"@segment{parameters.Count}";
parameters.Add(new SugarParameter(paramName, $"%{cleanSegment}%"));
return $"{field} LIKE {paramName}";
});
// 每个片段内部使用 OR 连接不同字段
keywordConditions.Add($"({string.Join(" OR ", fieldConditions)})");
}
}
// 单字检索条件
foreach (var singleChar in searchAnalysis.SingleChars)
{
var charStr = singleChar.ToString();
var fieldConditions = validFields.Select(field =>
{
var paramName = $"@char{parameters.Count}";
parameters.Add(new SugarParameter(paramName, $"%{charStr}%"));
return $"{field} LIKE {paramName}";
});
// 每个单字内部使用 OR 连接不同字段
keywordConditions.Add($"({string.Join(" OR ", fieldConditions)})");
}
// 所有关键字条件使用 OR 连接
if (keywordConditions.Any())
{
whereConditions.Add($"({string.Join(" OR ", keywordConditions)})");
}
#endregion
#region or 构建
//var searchAnalysis = AnalyzeSearchPattern(request.Keyword);
//var keywordConditions = new List();
//// 统一处理所有搜索词
//var allSearchTerms = new List();
//// 添加符号分割的片段(去除特殊字符)
//allSearchTerms.AddRange(searchAnalysis.SymbolSegments
// .Select(segment => Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", ""))
// .Where(segment => !string.IsNullOrEmpty(segment)));
//// 添加单字(排除重复)
//foreach (var singleChar in searchAnalysis.SingleChars)
//{
// var charStr = singleChar.ToString();
// // 只有当单字不在任何符号片段中时才添加
// if (!allSearchTerms.Any(term => term.Contains(charStr)))
// {
// allSearchTerms.Add(charStr);
// }
//}
//// 处理每个搜索词
//foreach (var term in allSearchTerms.Distinct())
//{
// var fieldConditions = validFields.Select(field =>
// {
// var paramName = $"@term{parameters.Count}";
// parameters.Add(new SugarParameter(paramName, $"%{term}%"));
// return $"{field} LIKE {paramName}";
// });
// keywordConditions.Add($"({string.Join(" OR ", fieldConditions)})");
//}
//// 所有搜索条件使用 AND 连接
//if (keywordConditions.Any())
//{
// whereConditions.Add($"({string.Join(" AND ", keywordConditions)})");
//}
#endregion
}
// 构建过滤条件
var filterConditions = BuildNativeFilterConditions(request.Filters, parameters);
whereConditions.AddRange(filterConditions);
// 构建完整SQL
var whereClause = whereConditions.Any()
? "WHERE " + string.Join(" AND ", whereConditions)
: "";
var orderByClause = BuildNativeOrderByClause(request.OrderBy, request.IsDescending);
var tableName = _db.EntityMaintenance.GetTableName(typeof(T));
// 构建返回字段
//var returnFields = BuildReturnFields(request.ReturnFields);
// 先查询总数
var countSql = $"SELECT COUNT(1) FROM {tableName} {whereClause}";
var totalCount = await _db.Ado.GetIntAsync(countSql, parameters);
// 再查询数据
var offset = (request.PageIndex - 1) * request.PageSize;
var dataSql = $@"
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER ({orderByClause}) AS RowNumber
FROM {tableName}
{whereClause}
) AS Paginated
WHERE Paginated.RowNumber > {offset} AND Paginated.RowNumber <= {offset + request.PageSize}
{orderByClause}";
var data = await _db.Ado.SqlQueryAsync(dataSql, parameters);
return (data, totalCount);
}
///
/// 构建基础查询
///
private ISugarQueryable BuildBaseQuery(DynamicSearchRequest request)
{
var query = _db.Queryable();
// 应用过滤条件
query = ApplyFilters(query, request.Filters);
// 应用排序
query = ApplyOrderBy(query, request.OrderBy, request.IsDescending);
return query;
}
///
/// 构建原生过滤条件
///
private List BuildNativeFilterConditions(List filters, List parameters)
{
var conditions = new List();
if (filters == null) return conditions;
foreach (var filter in filters)
{
var condition = filter.Operator?.ToLower() switch
{
"eq" => BuildNativeCondition(filter, "=", parameters),
"neq" => BuildNativeCondition(filter, "!=", parameters),
"contains" => BuildNativeLikeCondition(filter, "%", "%", parameters),
"startswith" => BuildNativeLikeCondition(filter, "", "%", parameters),
"endswith" => BuildNativeLikeCondition(filter, "%", "", parameters),
"gt" => BuildNativeCondition(filter, ">", parameters),
"gte" => BuildNativeCondition(filter, ">=", parameters),
"lt" => BuildNativeCondition(filter, "<", parameters),
"lte" => BuildNativeCondition(filter, "<=", parameters),
"in" => BuildNativeInCondition(filter, parameters),
_ => null
};
if (!string.IsNullOrEmpty(condition))
{
conditions.Add(condition);
}
}
return conditions;
}
private string BuildNativeCondition(SearchFilter filter, string op, List parameters)
{
var paramName = $"@filter{parameters.Count}";
parameters.Add(new SugarParameter(paramName, filter.Value));
return $"{filter.Field} {op} {paramName}";
}
private string BuildNativeLikeCondition(SearchFilter filter, string prefix, string suffix, List parameters)
{
var paramName = $"@filter{parameters.Count}";
parameters.Add(new SugarParameter(paramName, $"{prefix}{filter.Value}{suffix}"));
return $"{filter.Field} LIKE {paramName}";
}
private string BuildNativeInCondition(SearchFilter filter, List parameters)
{
if (filter.Values == null || !filter.Values.Any())
return null;
var paramNames = new List();
foreach (var value in filter.Values)
{
var paramName = $"@filter{parameters.Count}";
parameters.Add(new SugarParameter(paramName, value));
paramNames.Add(paramName);
}
return $"{filter.Field} IN ({string.Join(",", paramNames)})";
}
#endregion
#region 私有方法 - 匹配度计算含出现位置权重(单字检索时全部出现)
///
/// 在应用层计算匹配度
///
private List> CalculateMatchScore(List data, DynamicSearchRequest request)
{
if (string.IsNullOrWhiteSpace(request.Keyword))
{
// 无关键词时,所有记录匹配度为0
return data.Select(item => new SearchResultItem
{
Data = item,
MatchScore = 0
}).ToList();
}
var searchAnalysis = AnalyzeSearchPattern(request.Keyword);
var searchFields = request.FieldWeights?.Keys.ToList() ?? GetDefaultSearchFields();
var fieldWeights = request.FieldWeights ?? GetDefaultFieldWeights(searchFields);
var scoredItems = data.Select(item =>
{
var matchResult = CalculateItemMatchScore(item, searchAnalysis, searchFields, fieldWeights, request.RequireAllSingleChars);
return new SearchResultItem
{
Data = item,
MatchScore = matchResult.TotalScore,
MatchFields = matchResult.MatchFields
};
})
.Where(item => item.MatchScore > 0) // 只保留有匹配的记录
.OrderByDescending(item => item.MatchScore)
.ThenByDescending(item => GetCreateTime(item.Data))
.ToList();
return scoredItems;
}
///
/// 计算单个项的匹配度详情
///
private (int TotalScore, List MatchFields) CalculateItemMatchScore(
T item,
SearchAnalysis analysis,
List searchFields,
Dictionary fieldWeights,
bool requireAllSingleChars)
{
int totalScore = 0;
var matchFields = new List();
// 新增:根据参数检查是否所有单字都出现
if (requireAllSingleChars && analysis.SingleChars.Any())
{
bool allSingleCharsExist = CheckAllSingleCharsExist(item, analysis.SingleChars, searchFields);
// 如果要求所有单字必须出现但未完全匹配,直接返回0分
if (!allSingleCharsExist)
{
return (0, matchFields);
}
}
foreach (var field in searchFields)
{
var fieldValue = GetFieldValue(item, field);
if (string.IsNullOrEmpty(fieldValue))
continue;
var weight = fieldWeights.ContainsKey(field) ? fieldWeights[field] : GetDefaultWeight(field);
int fieldScore = 0;
var fieldMatchReasons = new List();
// 符号分割关键字匹配
foreach (var segment in analysis.SymbolSegments)
{
var cleanSegment = Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
if (!string.IsNullOrEmpty(cleanSegment) && fieldValue.Contains(cleanSegment))
{
int segmentScore = weight;
if (fieldValue.Equals(cleanSegment))
{
segmentScore += 15;
fieldMatchReasons.Add($"完全匹配 '{cleanSegment}'");
}
else if (fieldValue.StartsWith(cleanSegment))
{
segmentScore += 10;
fieldMatchReasons.Add($"开头匹配 '{cleanSegment}'");
}
else if (fieldValue.EndsWith(cleanSegment))
{
segmentScore += 5;
fieldMatchReasons.Add($"结尾匹配 '{cleanSegment}'");
}
else
{
fieldMatchReasons.Add($"包含 '{cleanSegment}'");
}
fieldScore += segmentScore;
}
}
// 单字匹配 - 加入位置权重计算
foreach (var singleChar in analysis.SingleChars)
{
int count = fieldValue.Count(c => c == singleChar);
if (count > 0)
{
int charScore = count * (int)(weight * 0.3);
// 计算位置权重奖励
var positionBonus = CalculateSingleCharPositionBonus(fieldValue, singleChar, weight);
charScore += positionBonus.Bonus;
if (fieldValue.StartsWith(singleChar.ToString()))
{
charScore += weight;
fieldMatchReasons.Add($"开头单字 '{singleChar}' +{weight}");
}
else if (positionBonus.Bonus > 0)
{
fieldMatchReasons.Add($"靠前单字 '{singleChar}' +{positionBonus.Bonus}");
}
// 添加位置信息到原因
if (positionBonus.FirstPosition >= 0)
{
fieldMatchReasons.Add($"位置{positionBonus.FirstPosition + 1}");
}
fieldMatchReasons.Add($"包含单字 '{singleChar}'({count}次)");
fieldScore += charScore;
}
}
if (fieldScore > 0)
{
totalScore += fieldScore;
matchFields.Add(new MatchFieldInfo
{
FieldName = field,
FieldValue = GetDisplayFieldValue(fieldValue),
Score = fieldScore,
MatchReason = string.Join("; ", fieldMatchReasons)
});
}
}
// 按分数排序匹配字段
matchFields = matchFields.OrderByDescending(m => m.Score).ToList();
return (totalScore, matchFields);
}
///
/// 新增:检查所有单字是否在任意字段中出现
///
private bool CheckAllSingleCharsExist(T item, List singleChars, List searchFields)
{
foreach (var singleChar in singleChars)
{
bool charExists = false;
// 检查所有搜索字段中是否包含该单字
foreach (var field in searchFields)
{
var fieldValue = GetFieldValue(item, field);
if (!string.IsNullOrEmpty(fieldValue) && fieldValue.Contains(singleChar))
{
charExists = true;
break;
}
}
// 如果有一个单字不存在,直接返回false
if (!charExists)
{
return false;
}
}
return true;
}
///
/// 计算单字位置权重奖励
///
private (int Bonus, int FirstPosition) CalculateSingleCharPositionBonus(string fieldValue, char singleChar, int baseWeight)
{
var firstPosition = fieldValue.IndexOf(singleChar);
if (firstPosition == -1)
return (0, -1);
// 计算位置比例 (0-1),0表示开头,1表示结尾
double positionRatio = (double)firstPosition / Math.Max(fieldValue.Length - 1, 1);
// 位置权重系数:位置越靠前,奖励越高
double positionFactor = 1.0 - positionRatio;
// 计算奖励分数(基于基础权重)
int positionBonus = (int)(baseWeight * positionFactor * 0.8); // 最大奖励为基础权重的80%
// 确保奖励至少为1
positionBonus = Math.Max(positionBonus, 1);
return (positionBonus, firstPosition);
}
///
/// 获取显示字段值(截断过长的值)
///
private string GetDisplayFieldValue(string fieldValue, int maxLength = 50)
{
if (string.IsNullOrEmpty(fieldValue) || fieldValue.Length <= maxLength)
return fieldValue;
return fieldValue.Substring(0, maxLength) + "...";
}
#endregion
#region 私有方法 - 辅助功能
///
/// 应用过滤条件
///
private ISugarQueryable ApplyFilters(ISugarQueryable query, List filters)
{
if (filters == null || !filters.Any())
return query;
foreach (var filter in filters)
{
query = filter.Operator?.ToLower() switch
{
"eq" => query.Where($"{filter.Field} = @Value", new { filter.Value }),
"neq" => query.Where($"{filter.Field} != @Value", new { filter.Value }),
"contains" => query.Where($"{filter.Field} LIKE '%' + @Value + '%'", new { filter.Value }),
"startswith" => query.Where($"{filter.Field} LIKE @Value + '%'", new { filter.Value }),
"endswith" => query.Where($"{filter.Field} LIKE '%' + @Value", new { filter.Value }),
"gt" => query.Where($"{filter.Field} > @Value", new { filter.Value }),
"gte" => query.Where($"{filter.Field} >= @Value", new { filter.Value }),
"lt" => query.Where($"{filter.Field} < @Value", new { filter.Value }),
"lte" => query.Where($"{filter.Field} <= @Value", new { filter.Value }),
"in" => ApplyInFilter(query, filter),
_ => query
};
}
return query;
}
///
/// 使用SqlSugar条件构建器构建搜索条件
///
private List BuildSearchConditions(SearchAnalysis analysis, List searchFields)
{
var conditionalModels = new List();
// 获取有效的搜索字段
var validFields = ValidateSearchFields(searchFields);
if (!validFields.Any())
return conditionalModels;
// 1. 符号分割的关键字条件
foreach (var segment in analysis.SymbolSegments)
{
var cleanSegment = Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
if (!string.IsNullOrEmpty(cleanSegment))
{
var segmentGroup = new List();
foreach (var field in validFields)
{
segmentGroup.Add(new ConditionalModel
{
FieldName = field,
ConditionalType = ConditionalType.Like,
FieldValue = $"%{cleanSegment}%"
});
}
if (segmentGroup.Count > 1)
{
conditionalModels.Add(new ConditionalCollections
{
ConditionalList = new List>(
segmentGroup.Select((model, index) =>
new KeyValuePair(
index == 0 ? WhereType.And : WhereType.Or,
(ConditionalModel)model))
)
});
}
else if (segmentGroup.Count == 1)
{
conditionalModels.Add(segmentGroup[0]);
}
}
}
return conditionalModels;
}
///
/// 应用IN过滤条件
///
private ISugarQueryable ApplyInFilter(ISugarQueryable query, SearchFilter filter)
{
if (filter.Values == null || !filter.Values.Any())
return query;
var valueList = string.Join(",", filter.Values.Select(v => $"'{v}'"));
return query.Where($"{filter.Field} IN ({valueList})");
}
///
/// 应用排序
///
private ISugarQueryable ApplyOrderBy(ISugarQueryable query, string orderBy, bool isDescending)
{
if (string.IsNullOrWhiteSpace(orderBy))
{
// 默认按主键或创建时间排序
var entityType = typeof(T);
var idProperty = entityType.GetProperty("Id") ?? entityType.GetProperty("CreateTime");
if (idProperty != null)
{
orderBy = idProperty.Name;
}
}
if (!string.IsNullOrWhiteSpace(orderBy))
{
return isDescending
? query.OrderBy($"{orderBy} DESC")
: query.OrderBy($"{orderBy} ASC");
}
return query;
}
///
/// 为轻量级搜索应用排序
///
private ISugarQueryable ApplyOrderByForLightweight(
ISugarQueryable query,
string orderBy,
bool isDescending) where TResult : class, new()
{
if (string.IsNullOrWhiteSpace(orderBy))
{
// 检查结果类型是否有Id或CreateTime字段
var resultType = typeof(TResult);
var idProperty = resultType.GetProperty("Id") ?? resultType.GetProperty("CreateTime");
if (idProperty != null)
{
orderBy = idProperty.Name;
}
else
{
// 如果没有默认排序字段,返回原查询
return query;
}
}
if (!string.IsNullOrWhiteSpace(orderBy))
{
// 验证排序字段是否存在于结果类型中
var resultType = typeof(TResult);
var orderByProperty = resultType.GetProperty(orderBy);
if (orderByProperty != null)
{
return isDescending
? query.OrderBy($"{orderBy} DESC")
: query.OrderBy($"{orderBy} ASC");
}
else
{
_logger.LogWarning("排序字段 {OrderBy} 在返回类型 {ResultType} 中不存在", orderBy, resultType.Name);
}
}
return query;
}
///
/// 构建原生排序子句
///
private string BuildNativeOrderByClause(string orderBy, bool isDescending)
{
if (string.IsNullOrWhiteSpace(orderBy))
//return "ORDER BY Id DESC";
return "";
return $"ORDER BY {orderBy} {(isDescending ? "DESC" : "ASC")}";
}
///
/// 分析搜索模式
///
private SearchAnalysis AnalyzeSearchPattern(string keyword)
{
var analysis = new SearchAnalysis { OriginalKeyword = keyword };
// 检查是否包含特殊符号
var specialSymbols = new[] { ' ', ',', ',', ';', ';', '、', '/', '\\', '|', '-', '_' };
if (keyword.Any(c => specialSymbols.Contains(c)))
{
analysis.HasSpecialSymbols = true;
analysis.SymbolSegments = keyword.Split(specialSymbols, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.Where(s => !string.IsNullOrEmpty(s))
.ToList();
}
// 清理关键词并提取单字
var cleanKeyword = Regex.Replace(keyword, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
if (!string.IsNullOrEmpty(cleanKeyword))
{
foreach (char c in cleanKeyword)
{
analysis.SingleChars.Add(c);
}
analysis.IsSingleChar = true;
// 如果没有特殊符号但有关键词,也作为符号分割段
if (cleanKeyword.Length > 1 && !analysis.HasSpecialSymbols)
{
analysis.SymbolSegments.Add(cleanKeyword);
}
}
// 去重
analysis.SingleChars = analysis.SingleChars.Distinct().ToList();
analysis.SymbolSegments = analysis.SymbolSegments.Distinct().ToList();
return analysis;
}
///
/// 获取创建时间(用于排序)
///
private DateTime GetCreateTime(T item)
{
var createTimeProperty = typeof(T).GetProperty("CreateTime");
if (createTimeProperty != null)
{
return (DateTime)(createTimeProperty.GetValue(item) ?? DateTime.MinValue);
}
return DateTime.MinValue;
}
///
/// 获取字段值
///
private string GetFieldValue(T item, string fieldName)
{
var property = typeof(T).GetProperty(fieldName);
return property?.GetValue(item) as string;
}
///
/// 获取默认搜索字段
///
private List GetDefaultSearchFields()
{
return typeof(T).GetProperties()
.Where(p => p.PropertyType == typeof(string))
.Select(p => p.Name)
.Take(5)
.ToList();
}
///
/// 获取默认字段权重
///
private Dictionary GetDefaultFieldWeights(List fields)
{
var weights = new Dictionary();
foreach (var field in fields)
{
weights[field] = GetDefaultWeight(field);
}
return weights;
}
///
/// 验证搜索字段
///
private List ValidateSearchFields(List searchFields)
{
var validFields = typeof(T).GetProperties()
.Where(p => p.PropertyType == typeof(string))
.Select(p => p.Name)
.ToList();
return searchFields.Intersect(validFields).ToList();
}
///
/// 获取默认权重
///
private int GetDefaultWeight(string fieldName)
{
return fieldName.ToLower() switch
{
var name when name.Contains("name") => 10,
var name when name.Contains("title") => 10,
var name when name.Contains("code") => 8,
var name when name.Contains("no") => 8,
var name when name.Contains("desc") => 6,
var name when name.Contains("content") => 6,
var name when name.Contains("remark") => 5,
var name when name.Contains("phone") => 4,
var name when name.Contains("tel") => 4,
var name when name.Contains("address") => 3,
var name when name.Contains("email") => 3,
_ => 5
};
}
///
/// 获取显示名称
///
private string GetDisplayName(System.Reflection.PropertyInfo property)
{
var displayAttr = property.GetCustomAttribute();
return displayAttr?.Name ?? property.Name;
}
///
/// 获取字段描述
///
private string GetFieldDescription(System.Reflection.PropertyInfo property)
{
var descriptionAttr = property.GetCustomAttribute();
return descriptionAttr?.Description ?? string.Empty;
}
#endregion
}
}