SearchController.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  1. using OASystem.API.OAMethodLib;
  2. using OASystem.API.OAMethodLib.GenericSearch;
  3. using OASystem.Domain.AesEncryption;
  4. using OASystem.Domain.Entities.Customer;
  5. using OASystem.Domain.Entities.Groups;
  6. using OASystem.Domain.ViewModels.Search;
  7. using OASystem.Infrastructure.Repositories.CRM;
  8. using OASystem.Infrastructure.Repositories.Groups;
  9. using System.Diagnostics;
  10. namespace OASystem.API.Controllers
  11. {
  12. /// <summary>
  13. /// 搜索
  14. /// </summary>
  15. [Route("api/search")]
  16. [ApiController]
  17. public class SearchController : ControllerBase
  18. {
  19. private readonly SqlSugarClient _sqlSugar;
  20. private readonly DelegationInfoRepository _groupRep;
  21. private readonly DynamicSearchService<Grp_DelegationInfo> _groupSearchService;
  22. private readonly DynamicSearchService<NewClientDataView> _clientSearchService;
  23. private readonly DynamicSearchService<InvitationAIInvNameView> _invAISearchService;
  24. private readonly DynamicSearchService<AirInfo> _airSearchService;
  25. private readonly NewClientDataRepository _clientDataRepository;
  26. public SearchController(
  27. SqlSugarClient sqlSugar,
  28. DelegationInfoRepository groupRep,
  29. DynamicSearchService<Grp_DelegationInfo> groupSearchService,
  30. DynamicSearchService<NewClientDataView> clientSearchService,
  31. DynamicSearchService<InvitationAIInvNameView> invAISearchService,
  32. DynamicSearchService<AirInfo> airSearchService,
  33. NewClientDataRepository clientDataRepository
  34. )
  35. {
  36. _sqlSugar = sqlSugar;
  37. _groupRep = groupRep;
  38. _clientDataRepository = clientDataRepository;
  39. _groupSearchService = groupSearchService;
  40. _clientSearchService = clientSearchService;
  41. _airSearchService = airSearchService;
  42. _invAISearchService = invAISearchService;
  43. }
  44. /// <summary>
  45. /// 接团信息 关键字输入提示(单字段)
  46. /// </summary>
  47. /// <param name="keyword">关键字</param>
  48. /// <returns></returns>
  49. [HttpGet("group/{keyword}")]
  50. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  51. public async Task<IActionResult> GroupKeywordSearch(string keyword)
  52. {
  53. try
  54. {
  55. // 验证请求参数
  56. if (string.IsNullOrEmpty(keyword))
  57. {
  58. return Ok(JsonView(true, $"暂无数据!"));
  59. }
  60. var searchRequest = new DynamicSearchRequest
  61. {
  62. Keyword = keyword,
  63. RequireAllSingleChars = true,
  64. PageIndex = 1,
  65. PageSize = 999999,
  66. FieldWeights = new Dictionary<string, int>
  67. {
  68. { "TeamName", 10 },
  69. //{ "ClientUnit", 8 },
  70. //{ "ClientName", 6 }
  71. },
  72. Filters = new List<SearchFilter>()
  73. {
  74. new(){Field = "IsDel",Operator="eq",Value="0" }
  75. },
  76. OrderBy = "TeamName",
  77. ReturnFields = new List<string>() { "TeamName" }
  78. };
  79. // 验证字段配置
  80. var validation = _groupSearchService.ValidateFieldConfig(
  81. searchRequest.FieldWeights,
  82. searchRequest.ReturnFields);
  83. if (!validation.IsValid)
  84. {
  85. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  86. }
  87. var result = await _groupSearchService.SearchAsync(searchRequest);
  88. if (result.Success)
  89. {
  90. var data = result.Items.Select(x => new { x.Data.Id, x.Data.TeamName }).ToList();
  91. return Ok(JsonView(true, result.Message, data, data.Count));
  92. }
  93. return Ok(JsonView(true, result.Message));
  94. }
  95. catch (Exception ex)
  96. {
  97. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  98. }
  99. }
  100. /// <summary>
  101. /// 客户资料 关键字输入提示(多字段)
  102. /// </summary>
  103. /// <param name="userId">关键字</param>
  104. /// <param name="keyword">关键字</param>
  105. /// <returns></returns>
  106. [HttpGet("group/{userId}/{keyword}")]
  107. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  108. public async Task<IActionResult> ClientKeywordSearch(int userId, string keyword)
  109. {
  110. try
  111. {
  112. // 验证请求参数
  113. if (string.IsNullOrEmpty(keyword))
  114. {
  115. return Ok(JsonView(true, $"暂无数据!"));
  116. }
  117. //查询有权限的数据
  118. var clientIds = await _sqlSugar
  119. .Queryable<Crm_ClientDataAndUser>()
  120. .Where(x => x.IsDel == 0 && x.usersId == userId)
  121. .Select(x => x.NewClientDataId)
  122. .ToListAsync();
  123. if (clientIds == null || clientIds.Count < 1)
  124. {
  125. return Ok(JsonView(true, $"暂无数据!"));
  126. }
  127. var data = _sqlSugar.Queryable<Crm_NewClientData>()
  128. .Where(x => x.IsDel == 0 && clientIds.Contains(x.Id))
  129. .OrderByDescending(x => x.CreateTime)
  130. .Select(x => new NewClientDataView()
  131. {
  132. Id = x.Id,
  133. Client = x.Client,
  134. Contact = x.Contact,
  135. Job = x.Job,
  136. Telephone = x.Telephone,
  137. Location = x.Location,
  138. })
  139. .ToList();
  140. int index = 0;
  141. data.ForEach(x =>
  142. {
  143. index++;
  144. x.RowNumber = index;
  145. x.Client = AesEncryptionHelper.Decrypt(x.Client);
  146. x.Contact = AesEncryptionHelper.Decrypt(x.Contact);
  147. x.Job = AesEncryptionHelper.Decrypt(x.Job);
  148. x.Telephone = AesEncryptionHelper.Decrypt(x.Telephone);
  149. x.Location = AesEncryptionHelper.Decrypt(x.Location);
  150. });
  151. var searchRequest = new DynamicSearchRequest
  152. {
  153. Keyword = keyword,
  154. RequireAllSingleChars = true,
  155. PageIndex = 1,
  156. PageSize = 999999,
  157. FieldWeights = new Dictionary<string, int>
  158. {
  159. { "Client", 10 },
  160. { "Contact", 8 },
  161. { "Location", 6 }
  162. },
  163. OrderBy = "CreateTime"
  164. };
  165. // 验证字段配置
  166. var validation = _clientSearchService.ValidateFieldConfig(
  167. searchRequest.FieldWeights,
  168. searchRequest.ReturnFields);
  169. if (!validation.IsValid)
  170. {
  171. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  172. }
  173. var result = _clientSearchService.SearchDataSource(searchRequest, data);
  174. if (result.Success)
  175. {
  176. var view = result.Items.Select(x => x.Data).ToList();
  177. int resetIndex = 0;
  178. view.ForEach(x =>
  179. {
  180. resetIndex++;
  181. x.RowNumber = resetIndex;
  182. });
  183. return Ok(JsonView(true, result.Message, view, view.Count));
  184. }
  185. return Ok(JsonView(true, "暂无数据"));
  186. }
  187. catch (Exception ex)
  188. {
  189. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  190. }
  191. }
  192. /// <summary>
  193. /// 客户资料
  194. /// </summary>
  195. /// <param name="userId">关键字</param>
  196. /// <param name="keyword">关键字</param>
  197. /// <returns></returns>
  198. [HttpGet("ClientKeywordSearch/{userId}/{keyword}")]
  199. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  200. public async Task<IActionResult> ClientKeywordSearchAll(int userId, string keyword)
  201. {
  202. try
  203. {
  204. // 验证请求参数
  205. if (string.IsNullOrEmpty(keyword))
  206. {
  207. return Ok(JsonView(true, $"暂无数据!"));
  208. }
  209. var userIds = _clientDataRepository.GetNewExistClient(userId).Select(x => x.Id).ToList();
  210. //查询有权限的数据(包括负责用户)
  211. var clientIds = await _sqlSugar
  212. .Queryable<Crm_ClientDataAndUser>()
  213. .Where(x => x.IsDel == 0 && userIds.Contains(x.usersId))
  214. .Select(x => x.NewClientDataId)
  215. .ToListAsync();
  216. if (clientIds == null || clientIds.Count < 1)
  217. {
  218. return Ok(JsonView(true, $"暂无数据!"));
  219. }
  220. var data = _sqlSugar.Queryable<Crm_NewClientData>()
  221. .Where(x => x.IsDel == 0 && clientIds.Contains(x.Id))
  222. .OrderByDescending(x => x.CreateTime)
  223. .Select(x => new NewClientDataView()
  224. {
  225. Id = x.Id,
  226. Client = x.Client,
  227. Contact = x.Contact,
  228. Job = x.Job,
  229. Telephone = x.Telephone,
  230. Location = x.Location,
  231. })
  232. .ToList();
  233. int index = 0;
  234. data.ForEach(x =>
  235. {
  236. index++;
  237. x.RowNumber = index;
  238. x.Client = AesEncryptionHelper.Decrypt(x.Client);
  239. x.Contact = AesEncryptionHelper.Decrypt(x.Contact);
  240. x.Job = AesEncryptionHelper.Decrypt(x.Job);
  241. x.Telephone = AesEncryptionHelper.Decrypt(x.Telephone);
  242. x.Location = AesEncryptionHelper.Decrypt(x.Location);
  243. });
  244. var searchRequest = new DynamicSearchRequest
  245. {
  246. Keyword = keyword,
  247. RequireAllSingleChars = true,
  248. PageIndex = 1,
  249. PageSize = 999999,
  250. FieldWeights = new Dictionary<string, int>
  251. {
  252. { "Client", 10 },
  253. { "Contact", 8 },
  254. { "Location", 6 }
  255. },
  256. OrderBy = "CreateTime"
  257. };
  258. // 验证字段配置
  259. var validation = _clientSearchService.ValidateFieldConfig(
  260. searchRequest.FieldWeights,
  261. searchRequest.ReturnFields);
  262. if (!validation.IsValid)
  263. {
  264. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  265. }
  266. var result = _clientSearchService.SearchDataSource(searchRequest, data);
  267. if (result.Success)
  268. {
  269. var view = result.Items.Select(x => x.Data).ToList();
  270. int resetIndex = 0;
  271. view.ForEach(x =>
  272. {
  273. resetIndex++;
  274. x.RowNumber = resetIndex;
  275. });
  276. return Ok(JsonView(true, result.Message, view, view.Count));
  277. }
  278. return Ok(JsonView(true, "暂无数据"));
  279. }
  280. catch (Exception ex)
  281. {
  282. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  283. }
  284. }
  285. /// <summary>
  286. /// 团组、会务流程 关键字输入提示(智能版)
  287. /// </summary>
  288. /// <param name="keyword">关键字</param>
  289. /// <returns></returns>
  290. [HttpGet]
  291. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  292. public async Task<IActionResult> ProcessKeywordSearch(string keyword)
  293. {
  294. try
  295. {
  296. // 验证请求参数
  297. if (string.IsNullOrEmpty(keyword))
  298. {
  299. return Ok(JsonView(true, $"暂无数据!"));
  300. }
  301. var searchRequest = new DynamicSearchRequest
  302. {
  303. Keyword = keyword,
  304. RequireAllSingleChars = true,
  305. PageIndex = 1,
  306. PageSize = 999999,
  307. FieldWeights = new Dictionary<string, int>
  308. {
  309. { "TeamName", 10 }
  310. },
  311. Filters = new List<SearchFilter>()
  312. {
  313. new(){Field = "IsDel",Operator="eq",Value="0" }
  314. },
  315. OrderBy = "VisitDate",
  316. ReturnFields = new List<string>() { "TeamName" }
  317. };
  318. // 验证字段配置
  319. var validation = _groupSearchService.ValidateFieldConfig(
  320. searchRequest.FieldWeights,
  321. searchRequest.ReturnFields);
  322. if (!validation.IsValid)
  323. {
  324. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  325. }
  326. var result = await _groupSearchService.SearchAsync(searchRequest);
  327. if (result.Success)
  328. {
  329. var data = new List<dynamic>();
  330. foreach (var item in result.Items)
  331. {
  332. data.Add(new
  333. {
  334. item.Data.Id,
  335. item.Data.TeamName,
  336. });
  337. }
  338. return Ok(JsonView(true, result.Message, data, data.Count));
  339. }
  340. return Ok(JsonView(true, result.Message));
  341. }
  342. catch (Exception ex)
  343. {
  344. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  345. }
  346. }
  347. /// <summary>
  348. /// 团组会务成本 关键字输入提示(智能版)
  349. /// </summary>
  350. /// <param name="keyword">关键字</param>
  351. /// <returns></returns>
  352. [HttpGet("ConferenceAffairsKeywordSearch/{keyword}")]
  353. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  354. public async Task<IActionResult> ConferenceAffairsKeywordSearch(string keyword)
  355. {
  356. try
  357. {
  358. // 验证请求参数
  359. if (string.IsNullOrEmpty(keyword))
  360. {
  361. return Ok(JsonView(true, $"暂无数据!"));
  362. }
  363. var hwIds = _sqlSugar.Queryable<Sys_SetData>()
  364. .Where(x => x.IsDel == 0 && x.STid == 10 && x.Name.Contains("会务活动"))
  365. .Select(x => x.Id)
  366. .ToList();
  367. var object_hwIds = hwIds.ConvertAll<object>(x => x);
  368. var searchRequest = new DynamicSearchRequest
  369. {
  370. Keyword = keyword,
  371. RequireAllSingleChars = true,
  372. PageIndex = 1,
  373. PageSize = 999999,
  374. FieldWeights = new Dictionary<string, int>
  375. {
  376. { "TeamName", 10 }
  377. },
  378. Filters = new List<SearchFilter>()
  379. {
  380. new(){Field = "IsDel",Operator="eq",Value="0" },
  381. new(){Field = "TeamDid",Operator="in",Values=object_hwIds }
  382. },
  383. OrderBy = "VisitDate",
  384. ReturnFields = new List<string>() { "TeamName" }
  385. };
  386. // 验证字段配置
  387. var validation = _groupSearchService.ValidateFieldConfig(
  388. searchRequest.FieldWeights,
  389. searchRequest.ReturnFields);
  390. if (!validation.IsValid)
  391. {
  392. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  393. }
  394. var result = await _groupSearchService.SearchAsync(searchRequest);
  395. if (result.Success)
  396. {
  397. var data = new List<dynamic>();
  398. foreach (var item in result.Items)
  399. {
  400. data.Add(new
  401. {
  402. item.Data.Id,
  403. item.Data.TeamName,
  404. });
  405. }
  406. return Ok(JsonView(true, result.Message, data, data.Count));
  407. }
  408. return Ok(JsonView(true, result.Message));
  409. }
  410. catch (Exception ex)
  411. {
  412. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  413. }
  414. }
  415. #region 团组费用-机票费用
  416. /// <summary>
  417. /// 团组机票费用基本信息
  418. /// </summary>
  419. /// <param name="groupIds"></param>
  420. /// <returns></returns>
  421. private async Task<List<AirInfo>> GetAirInfos(List<int> groupIds)
  422. {
  423. // 1. 查询
  424. var groupInfos = await _sqlSugar.Queryable<Grp_DelegationInfo>()
  425. .Where(x => x.IsDel == 0 && groupIds.Contains(x.Id))
  426. .Select(x => new { x.Id, x.TeamName })
  427. .ToListAsync();
  428. var airInfos = await _sqlSugar.Queryable<Grp_AirTicketReservations>()
  429. .Where(x => x.IsDel == 0 && groupIds.Contains(x.DIId) && !string.IsNullOrEmpty(x.TicketCode))
  430. .Select(x => new { x.DIId, x.TicketCode, x.ClientName })
  431. .ToListAsync();
  432. // 2. 解析客户名称ID
  433. var clientIds = new HashSet<int>();
  434. foreach (var item in airInfos)
  435. {
  436. if (string.IsNullOrWhiteSpace(item.ClientName))
  437. continue;
  438. var ids = item.ClientName.Split(',')
  439. .Select(s => s.Trim())
  440. .Where(s => int.TryParse(s, out _))
  441. .Select(int.Parse);
  442. foreach (var id in ids)
  443. {
  444. clientIds.Add(id);
  445. }
  446. }
  447. // 3. 批量查询客户名称
  448. var clientNames = new Dictionary<int, string>();
  449. if (clientIds.Any())
  450. {
  451. var nameInfos = await _sqlSugar.Queryable<Crm_DeleClient>()
  452. .Where(x => x.IsDel == 0 && clientIds.Contains(x.Id) &&
  453. !string.IsNullOrEmpty(x.FirstName) && !string.IsNullOrEmpty(x.LastName))
  454. .Select(x => new { x.Id, x.FirstName, x.LastName })
  455. .ToListAsync();
  456. clientNames = nameInfos.ToDictionary(
  457. x => x.Id,
  458. x => AesEncryptionHelper.Decrypt(x.LastName) + AesEncryptionHelper.Decrypt(x.FirstName)
  459. );
  460. }
  461. // 4. 构建 ClientName 到客户名称列表的映射(预解析)
  462. var clientNameCache = new Dictionary<string, List<string>>();
  463. foreach (var item in airInfos)
  464. {
  465. if (string.IsNullOrWhiteSpace(item.ClientName))
  466. continue;
  467. if (!clientNameCache.ContainsKey(item.ClientName))
  468. {
  469. var ids = item.ClientName.Split(',')
  470. .Select(s => s.Trim())
  471. .Where(s => int.TryParse(s, out _))
  472. .Select(int.Parse)
  473. .ToList();
  474. var names = ids.SelectMany(id =>
  475. clientNames.ContainsKey(id)
  476. ? new[] { clientNames[id] }
  477. : Array.Empty<string>()
  478. ).ToList();
  479. clientNameCache[item.ClientName] = names;
  480. }
  481. }
  482. // 5. 构建分组数据
  483. var airLookup = airInfos
  484. .GroupBy(x => x.DIId)
  485. .ToDictionary(g => g.Key, g => g.Select(x => x.TicketCode).Distinct().ToList());
  486. var clientLookup = airInfos
  487. .Where(x => !string.IsNullOrEmpty(x.ClientName) && clientNameCache.ContainsKey(x.ClientName))
  488. .GroupBy(x => x.DIId)
  489. .ToDictionary(g => g.Key, g => g.SelectMany(x => clientNameCache[x.ClientName]).Distinct().ToList());
  490. // 6. 组装结果
  491. var result = groupInfos.Select(group => new AirInfo
  492. {
  493. Id = group.Id,
  494. TeamName = group.TeamName,
  495. TicketCodes = airLookup.GetValueOrDefault(group.Id, new List<string>()),
  496. Clients = clientLookup.GetValueOrDefault(group.Id, new List<string>())
  497. }).ToList();
  498. return result;
  499. }
  500. public class AirInfo
  501. {
  502. public int Id { get; set; }
  503. public string TeamName { get; set; }
  504. public string TicketCodesLabel => string.Join("、", TicketCodes);
  505. public string ClientsLabel => string.Join("、", Clients);
  506. public List<string> TicketCodes { get; set; }
  507. public List<string> Clients { get; set; }
  508. }
  509. #endregion
  510. /// <summary>
  511. /// 团组各项费用录入 关键字输入提示(智能版)
  512. /// 76 酒店预订
  513. /// 79 车/导游地接
  514. /// 80 签证
  515. /// 81 邀请/公务活动
  516. /// 82 团组客户保险
  517. /// 85 机票预订
  518. /// 98 其他款项
  519. /// 285 收款退还
  520. /// 1015 超支费用
  521. /// 1081 文档下载
  522. /// 1466 会务相关
  523. /// </summary>
  524. /// <param name="userId">用户Id</param>
  525. /// <param name="feeType">费用类型</param>
  526. /// <param name="keyword">关键字</param>
  527. /// <returns></returns>
  528. [HttpGet("GroupFeeKeywordSearch/{userId}/{feeType}/{keyword}")]
  529. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  530. public async Task<IActionResult> GroupFeeKeywordSearch(int userId, int feeType, string keyword)
  531. {
  532. var stopwatch = Stopwatch.StartNew();
  533. try
  534. {
  535. #region 参数验证
  536. // 基本参数验证
  537. if (userId <= 0)
  538. return Ok(JsonView(false, "用户ID必须大于0!"));
  539. if (feeType <= 0)
  540. return Ok(JsonView(false, "费用类型必须大于0!"));
  541. // 验证用户是否存在
  542. var userExists = await _sqlSugar.Queryable<Sys_Users>()
  543. .Where(x => x.IsDel == 0 && x.Id == userId)
  544. .AnyAsync();
  545. if (!userExists) return Ok(JsonView(false, "用户不存在或已被删除"));
  546. // 验证费用类型是否有效
  547. var feeTypeExists = await _sqlSugar.Queryable<Sys_SetData>()
  548. .Where(x => x.IsDel == 0 && x.STid == 16 && x.Id == feeType)
  549. .AnyAsync();
  550. if (!feeTypeExists) return Ok(JsonView(false, "无效的费用类型"));
  551. // 验证关键字
  552. if (string.IsNullOrWhiteSpace(keyword)) return Ok(JsonView(false, "请输入搜索关键字"));
  553. #endregion
  554. #region 获取用户有权限的团组ID
  555. // 获取用户有权限访问的团组ID列表
  556. var authorizedGroupIds = await _sqlSugar.Queryable<Grp_GroupsTaskAssignment>()
  557. .Where(x => x.IsDel == 0 && x.UId == userId && x.CTId == feeType)
  558. .Select(x => x.DIId)
  559. .Distinct()
  560. .ToListAsync();
  561. if (!authorizedGroupIds.Any()) return Ok(JsonView(true, "暂无数据", new List<object>(), 0));
  562. #endregion
  563. // 类型等于 85 时单独处理
  564. if (feeType == 85)
  565. {
  566. var airInfos = await GetAirInfos(authorizedGroupIds);
  567. if (airInfos == null) return Ok(JsonView(false, "获取机票费用信息失败"));
  568. var airSearchReq = new DynamicSearchRequest()
  569. {
  570. Keyword = keyword.Trim(),
  571. RequireAllSingleChars = true,
  572. FieldWeights = new Dictionary<string, int>
  573. {
  574. { "TeamName", 10 },
  575. { "TicketCodesLabel", 10 },
  576. { "ClientsLabel", 10 }
  577. }
  578. };
  579. var airResult = _airSearchService.SearchDataSource(airSearchReq, airInfos);
  580. if (!airResult.Success)
  581. {
  582. return Ok(JsonView(false, airResult.Message ?? "搜索失败"));
  583. }
  584. if (airResult.Items == null || !airResult.Items.Any())
  585. {
  586. return Ok(JsonView(true, "未找到匹配的团组", new List<object>(), 0));
  587. }
  588. var airRespData = airResult.Items
  589. .Where(item => item.Data != null)
  590. .Select(item => new
  591. {
  592. item.Data.Id,
  593. item.Data.TeamName,
  594. item.Data.TicketCodes,
  595. item.Data.Clients,
  596. })
  597. .Where(x => !string.IsNullOrWhiteSpace(x.TeamName)) // 过滤空名称
  598. .Distinct() // 去重
  599. .ToList();
  600. stopwatch.Stop();
  601. return Ok(JsonView(true, $"搜索成功,耗时:{stopwatch.ElapsedMilliseconds}ms", airRespData, airRespData.Count));
  602. }
  603. #region 构建搜索请求
  604. var searchRequest = new DynamicSearchRequest
  605. {
  606. Keyword = keyword.Trim(),
  607. RequireAllSingleChars = true,
  608. FieldWeights = new Dictionary<string, int>
  609. {
  610. { "TeamName", 10 }
  611. },
  612. Filters = new List<SearchFilter>
  613. {
  614. new SearchFilter { Field = "IsDel", Operator = "eq", Value = "0" },
  615. new SearchFilter { Field = "Id", Operator = "in", Values = authorizedGroupIds.ConvertAll<object>(x => x) }
  616. },
  617. OrderBy = "VisitDate", // 添加排序方向
  618. ReturnFields = new List<string> { "Id", "TeamName" } // 添加ID字段
  619. };
  620. #endregion
  621. #region 字段配置验证
  622. var validation = _groupSearchService.ValidateFieldConfig(
  623. searchRequest.FieldWeights,
  624. searchRequest.ReturnFields);
  625. if (!validation.IsValid)
  626. {
  627. return Ok(JsonView(false, $"字段配置错误: {validation.Message}"));
  628. }
  629. #endregion
  630. #region 执行搜索
  631. var result = await _groupSearchService.SearchAsync(searchRequest);
  632. if (!result.Success)
  633. {
  634. return Ok(JsonView(false, result.Message ?? "搜索失败"));
  635. }
  636. if (result.Items == null || !result.Items.Any())
  637. {
  638. return Ok(JsonView(true, "未找到匹配的团组", new List<object>(), 0));
  639. }
  640. #endregion
  641. #region 构建返回数据
  642. var responseData = result.Items
  643. .Where(item => item.Data != null)
  644. .Select(item => new
  645. {
  646. Id = item.Data.Id,
  647. TeamName = item.Data.TeamName
  648. })
  649. .Where(x => !string.IsNullOrWhiteSpace(x.TeamName)) // 过滤空名称
  650. .Distinct() // 去重
  651. .ToList();
  652. #endregion
  653. stopwatch.Stop();
  654. return Ok(JsonView(true, $"搜索成功,耗时:{stopwatch.ElapsedMilliseconds} ms", responseData, responseData.Count));
  655. }
  656. catch (Exception ex)
  657. {
  658. // 记录日志(实际项目中应该使用日志框架)
  659. // _logger.LogError(ex, "团组费用关键字搜索失败,用户ID: {UserId}, 费用类型: {FeeType}", userId, feeType);
  660. // 生产环境中不要返回详细的错误信息
  661. return Ok(JsonView(false, "搜索服务暂时不可用,请稍后重试"));
  662. }
  663. }
  664. /// <summary>
  665. /// 三公-接团信息 关键字输入提示(单字段)
  666. /// </summary>
  667. /// <param name="currUserId">用户名称ID</param>
  668. /// <param name="keyword">关键字</param>
  669. /// <returns></returns>
  670. [HttpGet("GroupEnterExitCostKeywordSearch/{currUserId}/{keyword}")]
  671. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  672. public async Task<IActionResult> GroupEnterExitCostKeywordSearch(int currUserId, string keyword)
  673. {
  674. try
  675. {
  676. // 验证请求参数
  677. if (currUserId < 1)
  678. {
  679. return Ok(JsonView(true, $"请传入有效的用户ID!"));
  680. }
  681. if (string.IsNullOrEmpty(keyword))
  682. {
  683. return Ok(JsonView(true, $"暂无数据!"));
  684. }
  685. var searchRequest = new DynamicSearchRequest
  686. {
  687. Keyword = keyword,
  688. RequireAllSingleChars = true,
  689. FieldWeights = new Dictionary<string, int>
  690. {
  691. { "TeamName", 10 },
  692. //{ "ClientUnit", 8 },
  693. //{ "ClientName", 6 }
  694. },
  695. Filters = new List<SearchFilter>()
  696. {
  697. new(){Field = "IsDel",Operator="eq",Value="0" }
  698. },
  699. OrderBy = "TeamName",
  700. ReturnFields = new List<string>() { "TeamName" }
  701. };
  702. // 验证字段配置
  703. var validation = _groupSearchService.ValidateFieldConfig(
  704. searchRequest.FieldWeights,
  705. searchRequest.ReturnFields);
  706. if (!validation.IsValid)
  707. {
  708. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  709. }
  710. var result = await _groupSearchService.SearchAsync(searchRequest);
  711. if (result.Success)
  712. {
  713. var groupIds = result.Items.Select(x => x.Data.Id).ToList();
  714. var isNullDatas = _sqlSugar.Queryable<Grp_EnterExitCost>()
  715. .Where(x => x.IsDel == 0 && groupIds.Contains(x.DiId))
  716. .Select(x => x.DiId)
  717. .Distinct()
  718. .ToList();
  719. var isViewDatas = _sqlSugar.Queryable<Grp_EnterExitCostPermission>()
  720. .Where(x1 => x1.IsDel == 0 && x1.UserId == currUserId && groupIds.Contains(x1.GroupId))
  721. .Select(x1 => x1.GroupId)
  722. .Distinct()
  723. .ToList();
  724. var provCityDatas = await _groupRep.ProvinceCityBasicSource();
  725. var data = result.Items.Select(x =>
  726. {
  727. int groupId = x.Data.Id;
  728. int cityId = x.Data.CityId;
  729. bool isNull = !isNullDatas.Where(x => x == groupId).Any();
  730. bool isView = isViewDatas.Where(x => x == groupId).Any();
  731. var provinceId = 122; //默认四川
  732. if (provinceId > 0)
  733. {
  734. var parentId = _groupRep.FindParentIdByChildId(provCityDatas, cityId);
  735. if (parentId != null)
  736. {
  737. provinceId = (int)parentId;
  738. }
  739. }
  740. return new
  741. {
  742. x.Data.Id,
  743. groupName = x.Data.TeamName,
  744. provinceId,
  745. isNull,
  746. isView,
  747. };
  748. }).ToList();
  749. return Ok(JsonView(true, result.Message, data, data.Count));
  750. }
  751. return Ok(JsonView(true, result.Message));
  752. }
  753. catch (Exception ex)
  754. {
  755. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  756. }
  757. }
  758. /// <summary>
  759. /// 商邀AI invName关键字输入提示(单字段)
  760. /// </summary>
  761. /// <param name="keyword">关键字</param>
  762. /// <returns></returns>
  763. [HttpGet("InvAIKeywordSearch/{keyword}")]
  764. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  765. public async Task<IActionResult> InvAIKeywordSearch(string keyword)
  766. {
  767. try
  768. {
  769. // 验证请求参数 _invAISearchService
  770. if (string.IsNullOrEmpty(keyword))
  771. {
  772. return Ok(JsonView(true, $"暂无数据!"));
  773. }
  774. var invNames = await GeneralMethod.InvitationAIInvName();
  775. var searchRequest = new DynamicSearchRequest
  776. {
  777. Keyword = keyword,
  778. RequireAllSingleChars = true,
  779. PageIndex = 1,
  780. PageSize = 999999,
  781. FieldWeights = new Dictionary<string, int>
  782. {
  783. { "Name", 10 },
  784. },
  785. OrderBy = "SortTime"
  786. };
  787. // 验证字段配置
  788. var validation = _invAISearchService.ValidateFieldConfig(
  789. searchRequest.FieldWeights,
  790. searchRequest.ReturnFields);
  791. if (!validation.IsValid)
  792. {
  793. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  794. }
  795. var result = _invAISearchService.SearchDataSource(searchRequest, invNames);
  796. if (result.Success)
  797. {
  798. var view = result.Items.Select(x => x.Data).ToList();
  799. return Ok(JsonView(true, result.Message, view, view.Count));
  800. }
  801. return Ok(JsonView(true, "暂无数据"));
  802. }
  803. catch (Exception ex)
  804. {
  805. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  806. }
  807. }
  808. /// <summary>
  809. /// 商邀AI ClientName 关键字输入提示(单字段)
  810. /// </summary>
  811. /// <param name="keyword">关键字</param>
  812. /// <returns></returns>
  813. [HttpGet("InvAIClientKeywordSearch/{keyword}")]
  814. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  815. public async Task<IActionResult> InvAIClientKeywordSearch(string keyword)
  816. {
  817. try
  818. {
  819. // 验证请求参数 _invAISearchService
  820. if (string.IsNullOrEmpty(keyword))
  821. {
  822. return Ok(JsonView(true, $"暂无数据!"));
  823. }
  824. var invNames = await GeneralMethod.InvitationAIClientName();
  825. var data = invNames.Select(x =>
  826. {
  827. return new InvitationAIInvNameView
  828. {
  829. Name = x,
  830. SortTime = DateTime.Now
  831. };
  832. }).ToList();
  833. var searchRequest = new DynamicSearchRequest
  834. {
  835. Keyword = keyword,
  836. RequireAllSingleChars = true,
  837. PageIndex = 1,
  838. PageSize = 999999,
  839. FieldWeights = new Dictionary<string, int>
  840. {
  841. { "Name", 10 },
  842. },
  843. OrderBy = "SortTime"
  844. };
  845. // 验证字段配置
  846. var validation = _invAISearchService.ValidateFieldConfig(
  847. searchRequest.FieldWeights,
  848. searchRequest.ReturnFields);
  849. if (!validation.IsValid)
  850. {
  851. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  852. }
  853. var result = _invAISearchService.SearchDataSource(searchRequest, data);
  854. if (result.Success)
  855. {
  856. var view = result.Items.Select(x => x.Data.Name).ToList();
  857. if (view.Count == 0)
  858. {
  859. return Ok(JsonView(true, "暂无数据"));
  860. }
  861. return Ok(JsonView(true, result.Message, view, view.Count));
  862. }
  863. return Ok(JsonView(true, "暂无数据"));
  864. }
  865. catch (Exception ex)
  866. {
  867. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  868. }
  869. }
  870. /// <summary>
  871. /// 商邀AI CountryName 关键字输入提示(单字段)
  872. /// </summary>
  873. /// <param name="keyword">关键字</param>
  874. /// <returns></returns>
  875. [HttpGet("InvAICountryKeywordSearch/{keyword}")]
  876. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  877. public async Task<IActionResult> InvAICountryKeywordSearch(string keyword)
  878. {
  879. try
  880. {
  881. // 验证请求参数 _invAISearchService
  882. if (string.IsNullOrEmpty(keyword))
  883. {
  884. return Ok(JsonView(true, $"暂无数据!"));
  885. }
  886. var countryNames = await GeneralMethod.InvitationAICountryName();
  887. var data = countryNames.Select(x =>
  888. {
  889. return new InvitationAIInvNameView
  890. {
  891. Name = x,
  892. SortTime = DateTime.Now
  893. };
  894. }).ToList();
  895. var searchRequest = new DynamicSearchRequest
  896. {
  897. Keyword = keyword,
  898. RequireAllSingleChars = true,
  899. PageIndex = 1,
  900. PageSize = 999999,
  901. FieldWeights = new Dictionary<string, int>
  902. {
  903. { "Name", 10 },
  904. },
  905. OrderBy = "SortTime"
  906. };
  907. // 验证字段配置
  908. var validation = _invAISearchService.ValidateFieldConfig(
  909. searchRequest.FieldWeights,
  910. searchRequest.ReturnFields);
  911. if (!validation.IsValid)
  912. {
  913. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  914. }
  915. var result = _invAISearchService.SearchDataSource(searchRequest, data);
  916. if (result.Success)
  917. {
  918. var view = result.Items.Select(x => x.Data.Name).ToList();
  919. if (view.Count == 0)
  920. {
  921. return Ok(JsonView(true, "暂无数据"));
  922. }
  923. return Ok(JsonView(true, result.Message, view, view.Count));
  924. }
  925. return Ok(JsonView(true, "暂无数据"));
  926. }
  927. catch (Exception ex)
  928. {
  929. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  930. }
  931. }
  932. /// <summary>
  933. /// OpLocal AI invName关键字输入提示(单字段)
  934. /// </summary>
  935. /// <param name="keyword">关键字</param>
  936. /// <returns></returns>
  937. [HttpGet("OpLocalAI/{keyword}")]
  938. [ProducesResponseType(typeof(JsonView), StatusCodes.Status200OK)]
  939. public async Task<IActionResult> OpLocalAIKeywordSearch(string keyword)
  940. {
  941. try
  942. {
  943. // 验证请求参数 _invAISearchService
  944. if (string.IsNullOrEmpty(keyword))
  945. {
  946. return Ok(JsonView(true, $"暂无数据!"));
  947. }
  948. var invNames = await GeneralMethod.OpLocalServiceAINames();
  949. var searchRequest = new DynamicSearchRequest
  950. {
  951. Keyword = keyword,
  952. RequireAllSingleChars = true,
  953. PageIndex = 1,
  954. PageSize = 999999,
  955. FieldWeights = new Dictionary<string, int>
  956. {
  957. { "Name", 10 },
  958. },
  959. OrderBy = "SortTime"
  960. };
  961. // 验证字段配置
  962. var validation = _invAISearchService.ValidateFieldConfig(
  963. searchRequest.FieldWeights,
  964. searchRequest.ReturnFields);
  965. if (!validation.IsValid)
  966. {
  967. return Ok(JsonView(true, $"暂无数据!{validation.Message}"));
  968. }
  969. var result = _invAISearchService.SearchDataSource(searchRequest, invNames);
  970. if (result.Success)
  971. {
  972. var view = result.Items.Select(x => x.Data).ToList();
  973. return Ok(JsonView(true, result.Message, view, view.Count));
  974. }
  975. return Ok(JsonView(true, "暂无数据"));
  976. }
  977. catch (Exception ex)
  978. {
  979. return Ok(JsonView(true, $"搜索服务暂时不可用!"));
  980. }
  981. }
  982. }
  983. }