From f761461e0bd733d5766cd75b0054454478162e16 Mon Sep 17 00:00:00 2001
From: "agile.zhou" <kklldog@live.cn>
Date: Fri, 4 Oct 2024 22:22:27 +0800
Subject: [PATCH] Refactor app search

---
 .../Controllers/AppController.cs              | 171 +++++-------------
 .../IAppService.cs                            |  11 ++
 src/AgileConfig.Server.Service/AppService.cs  | 128 ++++++++++++-
 3 files changed, 176 insertions(+), 134 deletions(-)

diff --git a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs
index 3c297a34..6fe1eae5 100644
--- a/src/AgileConfig.Server.Apisite/Controllers/AppController.cs
+++ b/src/AgileConfig.Server.Apisite/Controllers/AppController.cs
@@ -35,156 +35,64 @@ public AppController(IAppService appService,
             _premissionService = premissionService;
         }
 
-        public async Task<IActionResult> Search(string name, string id, string group, string sortField, string ascOrDesc, bool tableGrouped, int current = 1, int pageSize = 20)
+        public async Task<IActionResult> Search(string name, string id, string group, string sortField,
+            string ascOrDesc, bool tableGrouped, int current = 1, int pageSize = 20)
         {
             if (current < 1)
             {
                 throw new ArgumentException("current cant less then 1 .");
             }
+
             if (pageSize < 1)
             {
                 throw new ArgumentException("pageSize cant less then 1 .");
             }
 
-            var query = await _appService.GetAllAppsAsync();
-            if (!string.IsNullOrWhiteSpace(name))
-            {
-                query = query.Where(x => x.Name.Contains(name)).ToList();
-            }
-            if (!string.IsNullOrWhiteSpace(id))
-            {
-                query = query.Where(x => x.Id.Contains(id)).ToList();
-            }
-            if (!string.IsNullOrWhiteSpace(group))
-            {
-                query = query.Where(x => x.Group == group).ToList();
-            }
-
-            var appvms = new List<AppListVM>();
-            foreach (var app in query)
+            var appListVms = new List<AppListVM>();
+            long count = 0;
+            if (!tableGrouped)
             {
-                appvms.Add(await AppToListVM(app, false));
-            }
-            if (tableGrouped)
-            {
-                var appGroups = appvms.GroupBy(x => x.Group);
-                var appGroupList = new List<AppListVM>();
-                foreach (var appGroup in appGroups)
+                var searchResult =
+                    await _appService.SearchAsync(id, name, group, sortField, ascOrDesc, current, pageSize);
+                foreach (var app in searchResult.Apps)
                 {
-                    var first = appGroup.First();
-                    var children = new List<AppListVM>();
-                    if (appGroup.Count() > 1)
-                    {
-                        foreach (var item in appGroup)
-                        {
-                            if (first.Id != item.Id)
-                            {
-                                children.Add(item);
-                            }
-                        }
-                    }
-
-                    if (children.Count > 0)
-                    {
-                        first.children = children;
-                    }
-                    appGroupList.Add(first);
+                    appListVms.Add(app.ToAppListVM());
                 }
 
-                appvms = appGroupList;
-            }
-
-            if (tableGrouped)
-            {
-                if (sortField == "group" && ascOrDesc.StartsWith("desc"))
-                {
-                    appvms = appvms.OrderByDescending(x => x.Group).ToList();
-                }
-                else
-                {
-                    appvms = appvms.OrderBy(x => x.Group).ToList();
-                }
+                count = searchResult.Count;
             }
             else
             {
-                if (sortField == "createTime")
-                {
-                    if (ascOrDesc.StartsWith("asc"))
-                    {
-                        appvms = appvms.OrderBy(x => x.CreateTime).ToList();
-                    }
-                    else
-                    {
-                        appvms = appvms.OrderByDescending(x => x.CreateTime).ToList();
-                    }
-                }
-                if (sortField == "id")
-                {
-                    if (ascOrDesc.StartsWith("asc"))
-                    {
-                        appvms = appvms.OrderBy(x => x.Id).ToList();
-                    }
-                    else
-                    {
-                        appvms = appvms.OrderByDescending(x => x.Id).ToList();
-                    }
-                }
-                if (sortField == "name")
+                var searchResult =
+                    await _appService.SearchGroupedAsync(id, name, group, sortField, ascOrDesc, current, pageSize);
+                foreach (var groupedApp in searchResult.GroupedApps)
                 {
-                    if (ascOrDesc.StartsWith("asc"))
+                    var app = groupedApp.App;
+                    var vm = app.ToAppListVM();
+                    vm.children = new List<AppListVM>();
+                    foreach (var child in groupedApp.Children ?? [])
                     {
-                        appvms = appvms.OrderBy(x => x.Name).ToList();
-                    }
-                    else
-                    {
-                        appvms = appvms.OrderByDescending(x => x.Name).ToList();
-                    }
-                }
-                if (sortField == "group")
-                {
-                    if (ascOrDesc.StartsWith("asc"))
-                    {
-                        appvms = appvms.OrderBy(x => x.Group).ToList();
-                    }
-                    else
-                    {
-                        appvms = appvms.OrderByDescending(x => x.Group).ToList();
+                        vm.children.Add(child.App.ToAppListVM());
                     }
+
+                    appListVms.Add(vm);
                 }
+
+                count = searchResult.Count;
             }
 
-            var count = appvms.Count;
-            var pageList = appvms.ToList().Skip((current - 1) * pageSize).Take(pageSize).ToList();
-            await AppendInheritancedInfo(pageList);
+            await AppendInheritancedInfo(appListVms);
+
             return Json(new
             {
                 current,
                 pageSize,
                 success = true,
                 total = count,
-                data = pageList
+                data = appListVms
             });
         }
 
-        private async Task<AppListVM> AppToListVM(App item, bool appendInheritancedInfo)
-        {
-            var vm = item.ToAppListVM();
-
-            if (appendInheritancedInfo)
-            {
-                var inheritancedApps = await _appService.GetInheritancedAppsAsync(item.Id);
-                vm.inheritancedApps = item.Type == AppType.Inheritance
-                    ? new List<string>()
-                    : (inheritancedApps).Select(ia => ia.Id).ToList();
-                vm.inheritancedAppNames = item.Type == AppType.Inheritance
-                    ? new List<string>()
-                    : (inheritancedApps).Select(ia => ia.Name).ToList();
-                vm.AppAdminName = (await _userService.GetUserAsync(item.AppAdmin))?.UserName;
-            }
-
-            return vm;
-        }
-
         private async Task AppendInheritancedInfo(List<AppListVM> list)
         {
             foreach (var appListVm in list)
@@ -213,7 +121,6 @@ public async Task<IActionResult> Add([FromBody] AppVM model)
             var oldApp = await _appService.GetAsync(model.Id);
             if (oldApp != null)
             {
-
                 return Json(new
                 {
                     success = false,
@@ -303,6 +210,7 @@ public async Task<IActionResult> Edit([FromBody] AppVM model)
             {
                 _tinyEventBus.Fire(new EditAppSuccessful(app, this.GetCurrentUserName()));
             }
+
             return Json(new
             {
                 success = result,
@@ -318,8 +226,9 @@ public async Task<IActionResult> All()
             foreach (var app in apps)
             {
                 var vm = app.ToAppListVM();
-                vm.inheritancedAppNames = app.Type == AppType.Inheritance ? new List<string>() :
-                                                                            (await _appService.GetInheritancedAppsAsync(app.Id)).Select(ia => ia.Id).ToList();
+                vm.inheritancedAppNames = app.Type == AppType.Inheritance
+                    ? new List<string>()
+                    : (await _appService.GetInheritancedAppsAsync(app.Id)).Select(ia => ia.Id).ToList();
                 vms.Add(vm);
             }
 
@@ -363,7 +272,8 @@ public async Task<IActionResult> Get(string id)
         /// </summary>
         /// <param name="id"></param>
         /// <returns></returns>
-        [TypeFilter(typeof(PremissionCheckAttribute), Arguments = new object[] { "App.DisableOrEanble", Functions.App_Edit })]
+        [TypeFilter(typeof(PremissionCheckAttribute),
+            Arguments = new object[] { "App.DisableOrEanble", Functions.App_Edit })]
         [HttpPost]
         public async Task<IActionResult> DisableOrEanble(string id)
         {
@@ -440,6 +350,7 @@ public async Task<IActionResult> InheritancedApps(string currentAppId)
                 //过滤本身
                 apps.Remove(self);
             }
+
             var vms = apps.Select(x =>
             {
                 return new
@@ -467,8 +378,10 @@ public async Task<IActionResult> SaveAppAuth([FromBody] AppAuthVM model)
         {
             ArgumentNullException.ThrowIfNull(model);
 
-            var result = await _appService.SaveUserAppAuth(model.AppId, model.EditConfigPermissionUsers, _premissionService.EditConfigPermissionKey);
-            var result1 = await _appService.SaveUserAppAuth(model.AppId, model.PublishConfigPermissionUsers, _premissionService.PublishConfigPermissionKey);
+            var result = await _appService.SaveUserAppAuth(model.AppId, model.EditConfigPermissionUsers,
+                _premissionService.EditConfigPermissionKey);
+            var result1 = await _appService.SaveUserAppAuth(model.AppId, model.PublishConfigPermissionUsers,
+                _premissionService.PublishConfigPermissionKey);
 
             return Json(new
             {
@@ -485,8 +398,12 @@ public async Task<IActionResult> GetUserAppAuth(string appId)
             {
                 AppId = appId
             };
-            result.EditConfigPermissionUsers = (await _appService.GetUserAppAuth(appId, _premissionService.EditConfigPermissionKey)).Select(x => x.Id).ToList();
-            result.PublishConfigPermissionUsers = (await _appService.GetUserAppAuth(appId, _premissionService.PublishConfigPermissionKey)).Select(x => x.Id).ToList();
+            result.EditConfigPermissionUsers =
+                (await _appService.GetUserAppAuth(appId, _premissionService.EditConfigPermissionKey)).Select(x => x.Id)
+                .ToList();
+            result.PublishConfigPermissionUsers =
+                (await _appService.GetUserAppAuth(appId, _premissionService.PublishConfigPermissionKey))
+                .Select(x => x.Id).ToList();
 
             return Json(new
             {
@@ -506,4 +423,4 @@ public async Task<IActionResult> GetAppGroups()
             });
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/AgileConfig.Server.IService/IAppService.cs b/src/AgileConfig.Server.IService/IAppService.cs
index 9d363ac6..c1cd2c26 100644
--- a/src/AgileConfig.Server.IService/IAppService.cs
+++ b/src/AgileConfig.Server.IService/IAppService.cs
@@ -5,6 +5,11 @@
 
 namespace AgileConfig.Server.IService
 {
+    public class GroupedApp
+    {
+        public App App { get; set; }
+        public List<GroupedApp> Children { get; set; }
+    }
     public interface IAppService : IDisposable
     {
         Task<App> GetAsync(string id);
@@ -20,6 +25,12 @@ public interface IAppService : IDisposable
 
         Task<List<App>> GetAllAppsAsync();
 
+        Task<(List<App> Apps, long Count)> SearchAsync(string id, string name, string group,string sortField, string ascOrDesc,
+          int current, int pageSize);
+        
+        Task<(List<GroupedApp> GroupedApps, long Count)> SearchGroupedAsync(string id, string name, string group,string sortField, string ascOrDesc,
+            int current, int pageSize);
+        
         Task<List<App>> GetAllInheritancedAppsAsync();
 
         Task<int> CountEnabledAppsAsync();
diff --git a/src/AgileConfig.Server.Service/AppService.cs b/src/AgileConfig.Server.Service/AppService.cs
index 5318066c..1ba4a64d 100644
--- a/src/AgileConfig.Server.Service/AppService.cs
+++ b/src/AgileConfig.Server.Service/AppService.cs
@@ -4,8 +4,9 @@
 using System.Collections.Generic;
 using System.Threading.Tasks;
 using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
 using AgileConfig.Server.Data.Abstraction;
-using static FreeSql.Internal.GlobalFilter;
 
 namespace AgileConfig.Server.Service
 {
@@ -27,7 +28,7 @@ public AppService(
             IUserRepository userRepository,
             IUserAppAuthRepository userAppAuthRepository,
             ISettingService settingService
-            )
+        )
         {
             _appRepository = repository;
             _appInheritancedRepository = appInheritancedRepository;
@@ -44,6 +45,7 @@ public async Task<bool> AddAsync(App app)
 
             return true;
         }
+
         public async Task<bool> AddAsync(App app, List<AppInheritanced> appInheritanceds)
         {
             await _appRepository.InsertAsync(app);
@@ -54,13 +56,14 @@ public async Task<bool> AddAsync(App app, List<AppInheritanced> appInheritanceds
 
             return true;
         }
+
         public async Task<bool> DeleteAsync(App app)
         {
             app = await _appRepository.GetAsync(app.Id);
             if (app != null)
             {
                 await _appRepository.DeleteAsync(app);
-                    
+
                 var envs = await _settingService.GetEnvironmentList();
                 var updatedConfigIds = new List<string>();
                 var updatedConfigPublishedIds = new List<string>();
@@ -71,7 +74,8 @@ public async Task<bool> DeleteAsync(App app)
                     using var configPublishedRepository = _configPublishedRepositoryAccessor(env);
 
                     //怕有的同学误删app导致要恢复,所以保留配置项吧。
-                    var configs = await configRepository.QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled);
+                    var configs =
+                        await configRepository.QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled);
                     var waitDeleteConfigs = new List<Config>();
                     foreach (var item in configs)
                     {
@@ -80,14 +84,16 @@ public async Task<bool> DeleteAsync(App app)
                             // 因为根据 env 构造的 provider 最终可能都定位到 default provider 上去,所以可能重复更新数据行,这里进行判断以下。
                             continue;
                         }
+
                         item.Status = ConfigStatus.Deleted;
                         waitDeleteConfigs.Add(item);
                         updatedConfigIds.Add(item.Id);
                     }
+
                     await configRepository.UpdateAsync(waitDeleteConfigs);
                     //删除发布的配置项
                     var publishedConfigs = await configPublishedRepository
-                        .QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled)
+                            .QueryAsync(x => x.AppId == app.Id && x.Status == ConfigStatus.Enabled)
                         ;
                     var waitDeletePublishedConfigs = new List<ConfigPublished>();
                     foreach (var item in publishedConfigs)
@@ -97,10 +103,12 @@ public async Task<bool> DeleteAsync(App app)
                             // 因为根据 env 构造的 provider 最终可能都定位到 default provider 上去,所以可能重复更新数据行,这里进行判断以下。
                             continue;
                         }
+
                         item.Status = ConfigStatus.Deleted;
                         waitDeletePublishedConfigs.Add(item);
                         updatedConfigPublishedIds.Add(item.Id);
                     }
+
                     await configPublishedRepository.UpdateAsync(waitDeletePublishedConfigs);
                 }
             }
@@ -129,6 +137,109 @@ public Task<List<App>> GetAllAppsAsync()
             return _appRepository.AllAsync();
         }
 
+        public async Task<(List<App> Apps, long Count)> SearchAsync(string id, string name, string group,
+            string sortField, string ascOrDesc,
+            int current, int pageSize)
+        {
+            Expression<Func<App, bool>> exp = app => true;
+
+            if (!string.IsNullOrWhiteSpace(id))
+            {
+                exp = exp.And(a => a.Id.Contains(id));
+            }
+
+            if (!string.IsNullOrWhiteSpace(name))
+            {
+                exp = exp.And(a => a.Name.Contains(name));
+            }
+
+            if (!string.IsNullOrWhiteSpace(group))
+            {
+                exp = exp.And(a => a.Group == group);
+            }
+
+            var apps = await _appRepository.QueryPageAsync(exp, current, pageSize, sortField,
+                ascOrDesc.StartsWith("asc") ? "ASC" : "DESC");
+            var count = await _appRepository.CountAsync(exp);
+
+            return (apps, count);
+        }
+
+        public async Task<(List<GroupedApp> GroupedApps, long Count)> SearchGroupedAsync(string id, string name,
+            string group, string sortField, string ascOrDesc, int current,
+            int pageSize)
+        {
+            Expression<Func<App, bool>> exp = app => true;
+
+            if (!string.IsNullOrWhiteSpace(id))
+            {
+                exp = exp.And(a => a.Id.Contains(id));
+            }
+
+            if (!string.IsNullOrWhiteSpace(name))
+            {
+                exp = exp.And(a => a.Name.Contains(name));
+            }
+
+            if (!string.IsNullOrWhiteSpace(group))
+            {
+                exp = exp.And(a => a.Group == group);
+            }
+
+            var apps = await _appRepository.QueryAsync(exp);
+
+            var appGroups = apps.GroupBy(x => x.Group);
+            var appGroupList = new List<GroupedApp>();
+            foreach (var appGroup in appGroups)
+            {
+                var app = appGroup.First();
+                var firstGroup = new GroupedApp()
+                {
+                    App = app
+                };
+                var children = new List<GroupedApp>();
+                if (appGroup.Count() > 1)
+                {
+                    foreach (var item in appGroup)
+                    {
+                        if (firstGroup.App.Id != item.Id)
+                        {
+                            children.Add(new GroupedApp()
+                            {
+                                App = item
+                            });
+                        }
+                    }
+                }
+
+                if (children.Count > 0)
+                {
+                    firstGroup.Children = children;
+                }
+
+                appGroupList.Add(firstGroup);
+            }
+
+            var sortProperty = new Dictionary<string, PropertyInfo>()
+            {
+                { "id", typeof(App).GetProperty("Id") },
+                { "name", typeof(App).GetProperty("Name") },
+                { "group", typeof(App).GetProperty("Group") },
+                { "createTime", typeof(App).GetProperty("CreateTime") }
+            };
+
+            if (sortProperty.TryGetValue(sortField, out var propertyInfo))
+            {
+                appGroupList = ascOrDesc.StartsWith("asc")
+                    ? appGroupList.OrderBy(x => propertyInfo.GetValue(x.App, null)).ToList()
+                    : appGroupList.OrderByDescending(x => propertyInfo.GetValue(x.App, null)).ToList();
+            }
+
+            var page = appGroupList.Skip(current - 1 * pageSize).Take(pageSize).ToList();
+
+            return (page, appGroupList.Count);
+        }
+
         public async Task<bool> UpdateAsync(App app)
         {
             await _appRepository.UpdateAsync(app);
@@ -217,6 +328,7 @@ public async Task<bool> SaveUserAppAuth(string appId, List<string> userIds, stri
             {
                 userIds = new List<string>();
             }
+
             foreach (var userId in userIds)
             {
                 userAppAuthList.Add(new UserAppAuth
@@ -227,7 +339,9 @@ public async Task<bool> SaveUserAppAuth(string appId, List<string> userIds, stri
                     Permission = permission
                 });
             }
-            var removeApps = await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission);
+
+            var removeApps =
+                await _userAppAuthRepository.QueryAsync(x => x.AppId == appId && x.Permission == permission);
             await _userAppAuthRepository.DeleteAsync(removeApps);
             await _userAppAuthRepository.InsertAsync(userAppAuthList);
 
@@ -266,4 +380,4 @@ public async Task<List<string>> GetAppGroups()
             return groups.Where(x => !string.IsNullOrEmpty(x)).ToList();
         }
     }
-}
+}
\ No newline at end of file