|
|
@@ -1215,387 +1215,86 @@ namespace OASystem.API.Controllers
|
|
|
}
|
|
|
|
|
|
return Ok(jw);
|
|
|
-
|
|
|
- #region 智能搜索
|
|
|
- //try
|
|
|
- //{
|
|
|
- // // 验证请求参数
|
|
|
- // if (string.IsNullOrEmpty(keyword))
|
|
|
- // {
|
|
|
- // return Ok(JsonView(true, $"暂无数据!"));
|
|
|
- // }
|
|
|
-
|
|
|
- // var searchRequest = new DynamicSearchRequest
|
|
|
- // {
|
|
|
- // Keyword = keyword,
|
|
|
- // PageIndex = 1,
|
|
|
- // PageSize = 9999,
|
|
|
- // FieldWeights = new Dictionary<string, int>
|
|
|
- // {
|
|
|
- // { "TeamName", 10 },
|
|
|
- // { "ClientUnit", 8 },
|
|
|
- // { "ClientName", 6 }
|
|
|
- // },
|
|
|
- // Filters = new List<SearchFilter>()
|
|
|
- // {
|
|
|
- // new(){Field = "IsDel",Operator="eq",Value="1" }
|
|
|
- // },
|
|
|
- // OrderBy = "CreateTime",
|
|
|
- // ReturnFields = new List<string>() { "TeamName", "ClientUnit", "ClientName" }
|
|
|
- // };
|
|
|
-
|
|
|
- // // 验证字段配置
|
|
|
- // var validation = _groupSearchService.ValidateFieldConfig(
|
|
|
- // searchRequest.FieldWeights,
|
|
|
- // searchRequest.ReturnFields);
|
|
|
-
|
|
|
- // if (!validation.IsValid)
|
|
|
- // {
|
|
|
- // return BadRequest(new { message = validation.Message });
|
|
|
- // }
|
|
|
-
|
|
|
- // var result = await _groupSearchService.SearchAsync(searchRequest);
|
|
|
-
|
|
|
- // if (result.Success)
|
|
|
- // {
|
|
|
- // return Ok(JsonView(true, "搜索成功!", result.Items, result.Items.Count));
|
|
|
- // }
|
|
|
-
|
|
|
- // return Ok(JsonView(true, $"暂无数据!"));
|
|
|
- //}
|
|
|
- //catch (Exception ex)
|
|
|
- //{
|
|
|
- // return Ok(JsonView(true, $"搜索服务暂时不可用!"));
|
|
|
- //}
|
|
|
-
|
|
|
-
|
|
|
- //var stopwatch = Stopwatch.StartNew();
|
|
|
-
|
|
|
- //if (string.IsNullOrWhiteSpace(keyword))
|
|
|
- //{
|
|
|
- // var result = await GetDefaultResultsAsync();
|
|
|
- // stopwatch.Stop();
|
|
|
- // return Ok(JsonView(true, $"操作成功!耗时:{stopwatch.ElapsedMilliseconds}ms", result, result.Count));
|
|
|
- //}
|
|
|
-
|
|
|
- //// 分析搜索模式
|
|
|
- //var searchAnalysis = AnalyzeSearchPattern(keyword);
|
|
|
-
|
|
|
- //if (!searchAnalysis.HasSearchContent)
|
|
|
- //{
|
|
|
- // var result = await GetDefaultResultsAsync();
|
|
|
- // stopwatch.Stop();
|
|
|
- // return Ok(JsonView(true, $"操作成功!耗时:{stopwatch.ElapsedMilliseconds}ms", result, result.Count));
|
|
|
- //}
|
|
|
-
|
|
|
- //// 原生SQL查询
|
|
|
- //var results = await SearchWithNativeSql(searchAnalysis);
|
|
|
- //var results1 = CalculateUnifiedMatchScore(results, searchAnalysis);
|
|
|
- //stopwatch.Stop();
|
|
|
- //return Ok(JsonView(true, $"搜索成功!耗时:{stopwatch.ElapsedMilliseconds}ms", results1, results1.Count));
|
|
|
- #endregion
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- #region 搜索辅助方法
|
|
|
-
|
|
|
- private List<GroupInfoWithScore> CalculateUnifiedMatchScore(List<GroupInfoWithScore> results, SearchAnalysis analysis)
|
|
|
- {
|
|
|
- return results.Select(info => new
|
|
|
- {
|
|
|
- Info = info,
|
|
|
- Score = CalculateComprehensiveMatchScore(info, analysis)
|
|
|
- })
|
|
|
- .Where(x => x.Score > 0)
|
|
|
- .OrderByDescending(x => x.Score)
|
|
|
- //.ThenByDescending(x => x.Info.CreateTime)
|
|
|
- .Select(x =>
|
|
|
- {
|
|
|
- x.Info.MatchScore = x.Score;
|
|
|
- return x.Info;
|
|
|
- })
|
|
|
- .ToList();
|
|
|
}
|
|
|
|
|
|
- private int CalculateComprehensiveMatchScore(GroupInfoWithScore info, SearchAnalysis analysis)
|
|
|
+ /// <summary>
|
|
|
+ /// 接团信息列表 关键字输入提示(智能版)
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="keyword">关键字</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet]
|
|
|
+ [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
|
|
|
+ public async Task<IActionResult> GroupItemNativeKeywordSearch(string keyword)
|
|
|
{
|
|
|
- int totalScore = 0;
|
|
|
-
|
|
|
- var targetFields = new[]
|
|
|
- {
|
|
|
- new { Value = info.TeamName, Weight = 10 },
|
|
|
- new { Value = info.ClientUnit, Weight = 8 },
|
|
|
- new { Value = info.ClientName, Weight = 6 }
|
|
|
- };
|
|
|
-
|
|
|
- // 关键值匹配得分
|
|
|
- foreach (var field in targetFields)
|
|
|
+ try
|
|
|
{
|
|
|
- foreach (var keyValue in analysis.KeyValues)
|
|
|
+ // 验证请求参数
|
|
|
+ if (string.IsNullOrEmpty(keyword))
|
|
|
{
|
|
|
- totalScore += CalculateFieldMatchScore(field.Value, keyValue, field.Weight);
|
|
|
+ return Ok(JsonView(true, $"暂无数据!"));
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // 符号分割关键字匹配得分
|
|
|
- foreach (var field in targetFields)
|
|
|
- {
|
|
|
- foreach (var segment in analysis.SymbolSegments)
|
|
|
+ var searchRequest = new DynamicSearchRequest
|
|
|
{
|
|
|
- var cleanSegment = Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
|
|
|
- if (!string.IsNullOrEmpty(cleanSegment))
|
|
|
+ Keyword = keyword,
|
|
|
+ PageIndex = 1,
|
|
|
+ PageSize = 9999,
|
|
|
+ FieldWeights = new Dictionary<string, int>
|
|
|
{
|
|
|
- totalScore += CalculateFieldMatchScore(field.Value, cleanSegment, field.Weight);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 单字匹配得分
|
|
|
- foreach (var field in targetFields)
|
|
|
- {
|
|
|
- foreach (var singleChar in analysis.SingleChars)
|
|
|
- {
|
|
|
- totalScore += CalculateSingleCharScore(field.Value, singleChar, (int)(field.Weight * 0.3));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return totalScore;
|
|
|
- }
|
|
|
-
|
|
|
- private int CalculateFieldMatchScore(string fieldValue, string keyword, int baseWeight)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(fieldValue) || string.IsNullOrEmpty(keyword))
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (fieldValue.Contains(keyword))
|
|
|
- {
|
|
|
- int score = baseWeight;
|
|
|
-
|
|
|
- if (fieldValue.Equals(keyword))
|
|
|
- score += 15;
|
|
|
- else if (fieldValue.StartsWith(keyword))
|
|
|
- score += 10;
|
|
|
- else if (fieldValue.EndsWith(keyword))
|
|
|
- score += 5;
|
|
|
-
|
|
|
- return score;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- private int CalculateSingleCharScore(string fieldValue, char singleChar, int baseWeight)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(fieldValue))
|
|
|
- return 0;
|
|
|
-
|
|
|
- int count = fieldValue.Count(c => c == singleChar);
|
|
|
- int score = count * baseWeight;
|
|
|
-
|
|
|
- if (fieldValue.Length > 0 && fieldValue[0] == singleChar)
|
|
|
- {
|
|
|
- score += baseWeight * 2;
|
|
|
- }
|
|
|
-
|
|
|
- return score;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 分析搜索模式 - 关键值查询改为单字查询
|
|
|
- /// </summary>
|
|
|
- private SearchAnalysis AnalyzeSearchPattern(string keyword)
|
|
|
- {
|
|
|
- var analysis = new SearchAnalysis { OriginalKeyword = keyword };
|
|
|
-
|
|
|
- // 1. 检查是否包含特殊符号
|
|
|
- if (ContainsSpecialSymbols(keyword))
|
|
|
- {
|
|
|
- analysis.HasSpecialSymbols = true;
|
|
|
- analysis.SymbolSegments = SplitBySpecialSymbols(keyword);
|
|
|
- }
|
|
|
+ { "TeamName", 10 },
|
|
|
+ { "ClientUnit", 8 },
|
|
|
+ { "ClientName", 6 }
|
|
|
+ },
|
|
|
+ Filters = new List<SearchFilter>()
|
|
|
+ {
|
|
|
+ new(){Field = "IsDel",Operator="eq",Value="0" }
|
|
|
+ },
|
|
|
+ OrderBy = "TeamName",
|
|
|
+ ReturnFields = new List<string>() { "TeamName", "ClientUnit", "ClientName" }
|
|
|
+ };
|
|
|
|
|
|
- // 2. 将所有输入字符都作为单字处理
|
|
|
- var cleanKeyword = Regex.Replace(keyword, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
|
|
|
+ // 验证字段配置
|
|
|
+ var validation = _groupSearchService.ValidateFieldConfig(
|
|
|
+ searchRequest.FieldWeights,
|
|
|
+ searchRequest.ReturnFields);
|
|
|
|
|
|
- if (!string.IsNullOrEmpty(cleanKeyword))
|
|
|
- {
|
|
|
- // 将所有字符都作为单字处理
|
|
|
- foreach (char c in cleanKeyword)
|
|
|
+ if (!validation.IsValid)
|
|
|
{
|
|
|
- analysis.SingleChars.Add(c);
|
|
|
+ return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
|
|
|
}
|
|
|
|
|
|
- analysis.IsSingleChar = true;
|
|
|
+ var result = await _groupSearchService.SearchAsync(searchRequest);
|
|
|
|
|
|
- // 同时将长度>1的连续字符也作为符号分割段处理(为了兼容性)
|
|
|
- if (cleanKeyword.Length > 1 && !analysis.HasSpecialSymbols)
|
|
|
+ if (result.Success)
|
|
|
{
|
|
|
- analysis.SymbolSegments.Add(cleanKeyword);
|
|
|
- }
|
|
|
- }
|
|
|
+ var data = new List<dynamic>();
|
|
|
|
|
|
- // 3. 如果包含符号,也从分割结果中提取单字
|
|
|
- if (analysis.HasSpecialSymbols)
|
|
|
- {
|
|
|
- foreach (var segment in analysis.SymbolSegments)
|
|
|
- {
|
|
|
- var cleanSegment = Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
|
|
|
- if (!string.IsNullOrEmpty(cleanSegment))
|
|
|
+ foreach (var item in result.Items)
|
|
|
{
|
|
|
- // 将分割段中的每个字符都作为单字
|
|
|
- foreach (char c in cleanSegment)
|
|
|
- {
|
|
|
- if (!analysis.SingleChars.Contains(c))
|
|
|
+ data.Add(new {
|
|
|
+ Data = new
|
|
|
{
|
|
|
- analysis.SingleChars.Add(c);
|
|
|
- }
|
|
|
- }
|
|
|
+ item.Data.TeamName,
|
|
|
+ item.Data.ClientUnit,
|
|
|
+ item.Data.ClientName
|
|
|
+ },
|
|
|
+ MatchScore = item.MatchScore,
|
|
|
+ MatchFields = item.MatchFields
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+ return Ok(JsonView(true, "搜索成功!", data, data.Count));
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // 去重
|
|
|
- analysis.SingleChars = analysis.SingleChars.Distinct().ToList();
|
|
|
- analysis.SymbolSegments = analysis.SymbolSegments.Distinct().ToList();
|
|
|
|
|
|
- return analysis;
|
|
|
- }
|
|
|
-
|
|
|
- private bool ContainsSpecialSymbols(string keyword)
|
|
|
- {
|
|
|
- var specialSymbols = new[] { ' ', ',', ',', ';', ';', '、', '/', '\\', '|', '-', '_' };
|
|
|
- return keyword.Any(c => specialSymbols.Contains(c));
|
|
|
- }
|
|
|
-
|
|
|
- private List<string> SplitBySpecialSymbols(string keyword)
|
|
|
- {
|
|
|
- var separators = new[] { ' ', ',', ',', ';', ';', '、', '/', '\\', '|', '-', '_' };
|
|
|
- return keyword.Split(separators, StringSplitOptions.RemoveEmptyEntries)
|
|
|
- .Select(s => s.Trim())
|
|
|
- .Where(s => !string.IsNullOrEmpty(s))
|
|
|
- .ToList();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 使用原生SQL条件(最稳定)
|
|
|
- /// </summary>
|
|
|
- private async Task<List<GroupInfoWithScore>> SearchWithNativeSql(SearchAnalysis analysis)
|
|
|
- {
|
|
|
- var whereConditions = new List<string>();
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
-
|
|
|
- // 构建关键值条件
|
|
|
- foreach (var keyValue in analysis.KeyValues)
|
|
|
- {
|
|
|
- whereConditions.Add($"(TeamName LIKE @keyValue{parameters.Count} OR ClientUnit LIKE @keyValue{parameters.Count} OR ClientName LIKE @keyValue{parameters.Count})");
|
|
|
- parameters.Add(new SugarParameter($"@keyValue{parameters.Count}", $"%{keyValue}%"));
|
|
|
- }
|
|
|
-
|
|
|
- // 构建符号分割条件
|
|
|
- foreach (var segment in analysis.SymbolSegments)
|
|
|
- {
|
|
|
- var cleanSegment = Regex.Replace(segment, @"[^\u4e00-\u9fa5a-zA-Z0-9]", "");
|
|
|
- if (!string.IsNullOrEmpty(cleanSegment))
|
|
|
- {
|
|
|
- whereConditions.Add($"(TeamName LIKE @segment{parameters.Count} OR ClientUnit LIKE @segment{parameters.Count} OR ClientName LIKE @segment{parameters.Count})");
|
|
|
- parameters.Add(new SugarParameter($"@segment{parameters.Count}", $"%{cleanSegment}%"));
|
|
|
- }
|
|
|
+ return Ok(JsonView(true, result.Message));
|
|
|
}
|
|
|
-
|
|
|
- // 构建单字条件
|
|
|
- foreach (var singleChar in analysis.SingleChars)
|
|
|
+ catch (Exception ex)
|
|
|
{
|
|
|
- var charStr = singleChar.ToString();
|
|
|
- whereConditions.Add($"(TeamName LIKE @char{parameters.Count} OR ClientUnit LIKE @char{parameters.Count} OR ClientName LIKE @char{parameters.Count})");
|
|
|
- parameters.Add(new SugarParameter($"@char{parameters.Count}", $"%{charStr}%"));
|
|
|
+ return Ok(JsonView(true, $"搜索服务暂时不可用!"));
|
|
|
}
|
|
|
|
|
|
- // 构建SQL
|
|
|
- var whereClause = whereConditions.Any()
|
|
|
- ? "AND (" + string.Join(" OR ", whereConditions) + ")"
|
|
|
- : "";
|
|
|
-
|
|
|
- var sql = $@"
|
|
|
- SELECT * FROM Grp_DelegationInfo
|
|
|
- WHERE IsDel = 0 {whereClause}
|
|
|
- ORDER BY CreateTime DESC";
|
|
|
-
|
|
|
- var result = await _sqlSugar.Ado.SqlQueryAsync<Grp_DelegationInfo>(sql, parameters);
|
|
|
-
|
|
|
- return result == null
|
|
|
- ? new List<GroupInfoWithScore>()
|
|
|
- : result.Select(x => new GroupInfoWithScore() { Id = x.Id, TeamName = x.TeamName, ClientUnit = x.ClientUnit, ClientName = x.ClientName }).ToList();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取默认结果
|
|
|
- /// </summary>
|
|
|
- private async Task<List<GroupInfoWithScore>> GetDefaultResultsAsync()
|
|
|
- {
|
|
|
- return await _sqlSugar.Queryable<Grp_DelegationInfo>()
|
|
|
- .Where(x => x.IsDel == 1)
|
|
|
- .OrderByDescending(x => x.CreateTime)
|
|
|
- .Select(x => new GroupInfoWithScore() { Id = x.Id, TeamName = x.TeamName, ClientUnit = x.ClientUnit, ClientName = x.ClientName })
|
|
|
- .ToListAsync();
|
|
|
- }
|
|
|
-
|
|
|
- private class GroupInfoWithScore
|
|
|
- {
|
|
|
- public int Id { get; set; }
|
|
|
- public string TeamName { get; set; }
|
|
|
- public string ClientUnit { get; set; }
|
|
|
- public string ClientName { get; set; }
|
|
|
- public int MatchScore { get; set; }
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 搜索分析结果
|
|
|
- /// </summary>
|
|
|
- public class SearchAnalysis
|
|
|
- {
|
|
|
- /// <summary>
|
|
|
- /// 原始关键词
|
|
|
- /// </summary>
|
|
|
- public string OriginalKeyword { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否有关键值
|
|
|
- /// </summary>
|
|
|
- public bool HasKeyValue { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 关键值列表
|
|
|
- /// </summary>
|
|
|
- public List<string> KeyValues { get; set; } = new List<string>();
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否有特殊符号
|
|
|
- /// </summary>
|
|
|
- public bool HasSpecialSymbols { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 符号分割的段
|
|
|
- /// </summary>
|
|
|
- public List<string> SymbolSegments { get; set; } = new List<string>();
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否是单字
|
|
|
- /// </summary>
|
|
|
- public bool IsSingleChar { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 单字列表
|
|
|
- /// </summary>
|
|
|
- public List<char> SingleChars { get; set; } = new List<char>();
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否有搜索内容
|
|
|
- /// </summary>
|
|
|
- public bool HasSearchContent => HasKeyValue || HasSpecialSymbols || IsSingleChar;
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// 接团信息列表 Page
|
|
|
/// </summary>
|