From 935f5e5cc146ee3b9c51a0395e3571878ed4c27d Mon Sep 17 00:00:00 2001 From: yubaolee Date: Tue, 22 Dec 2015 15:04:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=B5=84=E6=BA=90=E6=8E=88?= =?UTF-8?q?=E6=9D=83=EF=BC=8C=E5=85=83=E6=97=A6=E8=8A=82=E6=AD=A3=E5=BC=8F?= =?UTF-8?q?=E5=8F=91=E5=B8=831.0=E7=89=88=EF=BC=8C=E6=95=AC=E8=AF=B7?= =?UTF-8?q?=E6=9C=9F=E5=BE=85=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenAuth.App/ModuleElementManagerApp.cs | 15 +-- OpenAuth.App/OpenAuth.App.csproj | 1 + OpenAuth.App/ResourceManagerApp.cs | 59 +++++++- OpenAuth.App/ViewModel/ResourceVM.cs | 39 ++++++ OpenAuth.App/ViewModel/RoleVM.cs | 2 +- .../Controllers/OrgManagerController.cs | 5 - .../Controllers/ResourceManagerController.cs | 96 ++++++++++--- OpenAuth.Mvc/OpenAuth.Mvc.csproj | 2 + .../ResourceManager/LookupMultiForRole.cshtml | 126 ++++++++++++++++++ .../ResourceManager/LookupMultiForUser.cshtml | 126 ++++++++++++++++++ OpenAuth.Mvc/Views/RoleManager/Index.cshtml | 17 +++ OpenAuth.Mvc/Views/UserManager/Index.cshtml | 17 +++ 建表&初始化数据.sql | Bin 138730 -> 120230 bytes 13 files changed, 463 insertions(+), 42 deletions(-) create mode 100644 OpenAuth.App/ViewModel/ResourceVM.cs create mode 100644 OpenAuth.Mvc/Views/ResourceManager/LookupMultiForRole.cshtml create mode 100644 OpenAuth.Mvc/Views/ResourceManager/LookupMultiForUser.cshtml diff --git a/OpenAuth.App/ModuleElementManagerApp.cs b/OpenAuth.App/ModuleElementManagerApp.cs index bea1ff3f..77ed2bad 100644 --- a/OpenAuth.App/ModuleElementManagerApp.cs +++ b/OpenAuth.App/ModuleElementManagerApp.cs @@ -68,20 +68,7 @@ namespace OpenAuth.App public List LoadWithAccess(string accessType, int firstId, int moduleId) { //TODO:多个Repository使用的是不同的Context不能进行联表查询,要用UnitOfWork处理 - //var results = from element in _repository.Find(u => u.ModuleId == moduleId) - // join module in _moduleRepository.Find(null) on element.ModuleId equals module.Id - // join relev in _relevanceRepository.Find(u => u.Key == accessType && u.FirstId == firstId) - // on element.Id equals relev.SecondId into temp - // from t in temp.DefaultIfEmpty() - // select new ModuleElementVM - // { - // DomId = element.DomId, - // Id = element.Id, - // ModuleId = element.ModuleId, - // ModuleName = module.Name, - // Name = element.Name, - // Accessed = t != null - // }; + var listVms = new List(); if (moduleId == 0) return listVms; string modulename = _moduleRepository.FindSingle(u => u.Id == moduleId).Name; diff --git a/OpenAuth.App/OpenAuth.App.csproj b/OpenAuth.App/OpenAuth.App.csproj index 85d85cc9..51bcc9fc 100644 --- a/OpenAuth.App/OpenAuth.App.csproj +++ b/OpenAuth.App/OpenAuth.App.csproj @@ -55,6 +55,7 @@ + diff --git a/OpenAuth.App/ResourceManagerApp.cs b/OpenAuth.App/ResourceManagerApp.cs index 70c5b934..4cbef49b 100644 --- a/OpenAuth.App/ResourceManagerApp.cs +++ b/OpenAuth.App/ResourceManagerApp.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Infrastructure; +using OpenAuth.App.ViewModel; namespace OpenAuth.App { @@ -12,12 +13,15 @@ namespace OpenAuth.App { private IResourceRepository _repository; private readonly ICategoryRepository _categoryRepository; + private IRelevanceRepository _relevanceRepository; public ResourceManagerApp(IResourceRepository repository, - ICategoryRepository categoryRepository) + ICategoryRepository categoryRepository, + IRelevanceRepository relevanceRepository) { _repository = repository; _categoryRepository = categoryRepository; + _relevanceRepository = relevanceRepository; } public int GetResourceCntInOrg(int orgId) @@ -102,6 +106,57 @@ namespace OpenAuth.App } - + /// + /// 获取带有授权状态的菜单列表 + /// + /// 授权类型,当前有RoleResource/UserResource + /// + /// 当为RoleResource时,表示RoleId + /// 当为UserResource时,表示UserId + /// + /// 分类ID + public List LoadWithAccess(string accessType, int firstId, int cId) + { + var listVms = new List(); + if (cId == 0) return listVms; + + foreach (var element in _repository.LoadInOrgs(cId)) + { + var accessed = _relevanceRepository.FindSingle(u => u.Key == accessType + && u.FirstId == firstId && u.SecondId == element.Id); + listVms.Add(new ResourceVM + { + Id = element.Id, + Name = element.Name, + IsBelongUser = accessed != null, + Description = element.Description, + Key = element.Key, + Status = element.Status + }); + } + return listVms; + } + + /// + /// 为用户分配资源 + /// + /// 用户ID + /// 资源ID数组 + public void AssignResForUser(int userId, int[] resIds) + { + _relevanceRepository.DeleteBy("UserResource", resIds); + _relevanceRepository.AddRelevance("UserResource", resIds.ToLookup(u => userId)); + } + + /// + /// 为角色分配资源 + /// + /// 角色ID + /// 资源ID数组 + public void AssignResForRole(int roleId, int[] resIds) + { + _relevanceRepository.DeleteBy("RoleResource", resIds); + _relevanceRepository.AddRelevance("RoleResource", resIds.ToLookup(u => roleId)); + } } } \ No newline at end of file diff --git a/OpenAuth.App/ViewModel/ResourceVM.cs b/OpenAuth.App/ViewModel/ResourceVM.cs new file mode 100644 index 00000000..d5b36832 --- /dev/null +++ b/OpenAuth.App/ViewModel/ResourceVM.cs @@ -0,0 +1,39 @@ +namespace OpenAuth.App.ViewModel +{ + public class ResourceVM + { + /// + /// 资源表ID + /// + /// + public int Id { get; set; } + + + /// + /// + /// + /// + public string Key { get; set; } + + /// + /// 组织名称 + /// + /// + public string Name { get; set; } + + /// + /// 资源分类标识 + /// + /// + public int Status { get; set; } + + + /// + /// 描述 + /// + /// + public string Description { get; set; } + + public bool IsBelongUser { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/ViewModel/RoleVM.cs b/OpenAuth.App/ViewModel/RoleVM.cs index 69168606..9787d096 100644 --- a/OpenAuth.App/ViewModel/RoleVM.cs +++ b/OpenAuth.App/ViewModel/RoleVM.cs @@ -57,7 +57,7 @@ namespace OpenAuth.App.ViewModel public static implicit operator Role(RoleVM rolevm) { - return rolevm.MapTo(); + return rolevm.MapTo(); } } diff --git a/OpenAuth.Mvc/Controllers/OrgManagerController.cs b/OpenAuth.Mvc/Controllers/OrgManagerController.cs index 3e05bb26..d46855a8 100644 --- a/OpenAuth.Mvc/Controllers/OrgManagerController.cs +++ b/OpenAuth.Mvc/Controllers/OrgManagerController.cs @@ -32,11 +32,6 @@ namespace OpenAuth.Mvc.Controllers return View(); } - public ActionResult LookupMulti() - { - return View(); - } - public ActionResult AddOrg() { return View(); diff --git a/OpenAuth.Mvc/Controllers/ResourceManagerController.cs b/OpenAuth.Mvc/Controllers/ResourceManagerController.cs index b521f6a2..ded0facb 100644 --- a/OpenAuth.Mvc/Controllers/ResourceManagerController.cs +++ b/OpenAuth.Mvc/Controllers/ResourceManagerController.cs @@ -1,13 +1,13 @@ - -using System; -using System.Web.Mvc; using Infrastructure; using OpenAuth.App; using OpenAuth.Domain; +using System; +using System.Linq; +using System.Web.Mvc; namespace OpenAuth.Mvc.Controllers { - public class ResourceManagerController : BaseController + public class ResourceManagerController : BaseController { private ResourceManagerApp _app; @@ -35,7 +35,6 @@ namespace OpenAuth.Mvc.Controllers try { _app.AddOrUpdate(model); - } catch (Exception ex) { @@ -52,20 +51,20 @@ namespace OpenAuth.Mvc.Controllers { return JsonHelper.Instance.Serialize(_app.Load(categoryId, pageCurrent, pageSize)); } - - public string LoadForTree() - { - var models = _app.LoadAll(); - //添加根节点 - models.Add(new Resource - { - Id = 0, - ParentId = -1, - Name = "根结点", - CascadeId = "0" - }); - return JsonHelper.Instance.Serialize(models); - } + + public string LoadForTree() + { + var models = _app.LoadAll(); + //添加根节点 + models.Add(new Resource + { + Id = 0, + ParentId = -1, + Name = "根结点", + CascadeId = "0" + }); + return JsonHelper.Instance.Serialize(models); + } public string Delete(int Id) { @@ -82,6 +81,63 @@ namespace OpenAuth.Mvc.Controllers return JsonHelper.Instance.Serialize(BjuiResponse); } - + #region 为用户分配资源 + + public ActionResult LookupMultiForUser(int userId) + { + ViewBag.UserId = userId; + return View(); + } + + public string LoadWithUserAccess(int cId, int userId) + { + return JsonHelper.Instance.Serialize(_app.LoadWithAccess("UserResource",userId, cId)); + } + + public string AccessForUser(int userId, string ids) + { + try + { + var resIds = ids.Split(',').Select(id => int.Parse(id)).ToArray(); + _app.AssignResForUser(userId, resIds); + } + catch (Exception e) + { + BjuiResponse.message = e.Message; + BjuiResponse.statusCode = "300"; + } + + return JsonHelper.Instance.Serialize(BjuiResponse); + } + #endregion 为用户分配资源 + + #region 为角色分配资源 + public ActionResult LookupMultiForRole(int roleId) + { + ViewBag.RoleId = roleId; + return View(); + } + + public string LoadWithRoleAccess(int cId, int roleId) + { + return JsonHelper.Instance.Serialize(_app.LoadWithAccess("RoleResource", roleId, cId)); + } + + public string AccessForRole(int roleId, string ids) + { + try + { + var resIds = ids.Split(',').Select(id => int.Parse(id)).ToArray(); + _app.AssignResForRole(roleId, resIds); + } + catch (Exception e) + { + BjuiResponse.message = e.Message; + BjuiResponse.statusCode = "300"; + } + + return JsonHelper.Instance.Serialize(BjuiResponse); + } + #endregion } } \ No newline at end of file diff --git a/OpenAuth.Mvc/OpenAuth.Mvc.csproj b/OpenAuth.Mvc/OpenAuth.Mvc.csproj index 4807e3a9..6c7a66e8 100644 --- a/OpenAuth.Mvc/OpenAuth.Mvc.csproj +++ b/OpenAuth.Mvc/OpenAuth.Mvc.csproj @@ -575,6 +575,8 @@ + + diff --git a/OpenAuth.Mvc/Views/ResourceManager/LookupMultiForRole.cshtml b/OpenAuth.Mvc/Views/ResourceManager/LookupMultiForRole.cshtml new file mode 100644 index 00000000..fa9b7a18 --- /dev/null +++ b/OpenAuth.Mvc/Views/ResourceManager/LookupMultiForRole.cshtml @@ -0,0 +1,126 @@ +@{ + string _prefix = "assignResForRole"; + var _treeId = _prefix + "Tree"; + var _gridId = _prefix + "Grid"; + var _treeDetail = _prefix + "Detail"; +} +
+ +
+ +
+
+
+
    +
    + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/OpenAuth.Mvc/Views/ResourceManager/LookupMultiForUser.cshtml b/OpenAuth.Mvc/Views/ResourceManager/LookupMultiForUser.cshtml new file mode 100644 index 00000000..6e276783 --- /dev/null +++ b/OpenAuth.Mvc/Views/ResourceManager/LookupMultiForUser.cshtml @@ -0,0 +1,126 @@ +@{ + string _prefix = "assignResForUser"; + var _treeId = _prefix + "Tree"; + var _gridId = _prefix + "Grid"; + var _treeDetail = _prefix + "Detail"; +} +
    + +
    + +
    +
    +
    +
      +
      + +
      +
      +
      +
      + + \ No newline at end of file diff --git a/OpenAuth.Mvc/Views/RoleManager/Index.cshtml b/OpenAuth.Mvc/Views/RoleManager/Index.cshtml index a8c58d9e..1e46cfb3 100644 --- a/OpenAuth.Mvc/Views/RoleManager/Index.cshtml +++ b/OpenAuth.Mvc/Views/RoleManager/Index.cshtml @@ -169,6 +169,23 @@ }); } + //为角色分配资源 + function openRoleReourceAccess(obj) { + var selected = getSelected('#@_gridId', 2); + if (selected == null) return; + + $(obj).dialog({ + id: 'accessUserRole', + url: '/ResourceManager/LookupMultiForRole', + title: '为角色分配资源', + width: 600, + height: 380, + data: { + roleId: selected + } + }); + } + //为角色分配菜单 function assignRoleElement(obj) { var selected = getSelected('#@_gridId', 2); diff --git a/OpenAuth.Mvc/Views/UserManager/Index.cshtml b/OpenAuth.Mvc/Views/UserManager/Index.cshtml index 502519c9..e3a7e0d2 100644 --- a/OpenAuth.Mvc/Views/UserManager/Index.cshtml +++ b/OpenAuth.Mvc/Views/UserManager/Index.cshtml @@ -190,6 +190,23 @@ }); } + //为用户分配资源 + function openUserReourceAccess(obj) { + var selected = getSelected('#@_gridId', 2); + if (selected == null) return; + + $(obj).dialog({ + id: 'accessUserRole', + url: '/ResourceManager/LookupMultiForUser', + title: '为用户分配资源', + width: 600, + height: 380, + data: { + userId: selected + } + }); + } + //分配菜单 function openAssignUserElement(obj) { var selected = getSelected('#@_gridId', 2); diff --git a/建表&初始化数据.sql b/建表&初始化数据.sql index 96dc88c666e7ee5d78f303c81435329482189730..c221e0e827034060f8344c06a844985821c4e887 100644 GIT binary patch delta 4771 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 F{|BLswr~Id