zhaolei
5 days ago 4a2e5b9a21940f11757be37d99f0944e240e908b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
using Prow.Cache;
using Prow.Data;
using PetaPoco;
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace Bootstrap.Security.DataAccess
{
    /// <summary>
    /// Bootstrap Admin 后台管理数据库操作类,内部使用 ba 连接字符串进行数据库连接
    /// </summary>
    public static class DbHelper
    {
        /// <summary>
        /// 通过当前用户名获取所有角色缓存键名称 "RoleHelper-RetrieveRolesByUserName"
        /// </summary>
        public const string RetrieveRolesByUserNameDataKey = "RoleHelper-RetrieveRolesByUserName";
 
        /// <summary>
        /// 通过当前访问菜单地址获取所有角色缓存键名称 "RoleHelper-RetrieveRolesByUrl"
        /// </summary>
        public const string RetrieveRolesByUrlDataKey = "RoleHelper-RetrieveRolesByUrl";
 
        /// <summary>
        /// 通过当前用户名获取所有授权应用缓存键名称 "AppHelper-RetrieveAppsByUserName"
        /// </summary>
        public const string RetrieveAppsByUserNameDataKey = "AppHelper-RetrieveAppsByUserName";
 
        /// <summary>
        /// 通过当前用户名获取用户数据缓存键名称 "BootstrapUser-RetrieveUsersByName"
        /// </summary>
        public const string RetrieveUsersByNameDataKey = "BootstrapUser-RetrieveUsersByName";
 
        /// <summary>
        /// 通过当前用户获取所有菜单数据缓存键名称 "BootstrapMenu-RetrieveMenus"
        /// </summary>
        public const string RetrieveMenusAll = "BootstrapMenu-RetrieveMenus";
 
        /// <summary>
        /// 获取所有字典数据缓存键名称 "BootstrapMenu-RetrieveMenus"
        /// </summary>
        public const string RetrieveDictsDataKey = "BootstrapDict-RetrieveDicts";
 
        /// <summary>
        /// 通过当前用户获取所有组数据缓存键名称 "GroupHelper-RetrieveGroupsByUserName"
        /// </summary>
        public const string RetrieveGroupsByUserNameDataKey = "GroupHelper-RetrieveGroupsByUserName";
 
        /// <summary>
        /// 获取所有组数据缓存键名称 "GroupHelper-RetrieveGroups"
        /// </summary>
        public const string RetrieveGroupsDataKey = "GroupHelper-RetrieveGroups";
 
        /// <summary>
        /// 获得带层次关系的菜单集合
        /// </summary>
        /// <param name="navs">未层次化菜单集合</param>
        /// <param name="url">设置此菜单状态为 active</param>
        /// <returns>带层次化的菜单集合</returns>
        public static IEnumerable<BootstrapMenu> CascadeMenus(IEnumerable<BootstrapMenu> navs, string? url = null)
        {
            var root = navs.Where(m => m.ParentId == "0").OrderBy(m => m.Category).ThenBy(m => m.Application).ThenBy(m => m.Order).ToList();
            CascadeMenus(navs, null, root, url);
            return root;
        }
 
        private static void CascadeMenus(IEnumerable<BootstrapMenu> navs, BootstrapParentMenu? parent, IEnumerable<BootstrapMenu> level, string? url = null)
        {
            level.ToList().ForEach(m =>
            {
                m.Menus = navs.Where(sub => sub.ParentId == m.Id).OrderBy(sub => sub.Order);
                if (!string.IsNullOrEmpty(url))
                {
                    // Active menu
                    m.Active = m.Url.Equals(url, StringComparison.OrdinalIgnoreCase) ? "active" : "";
                    var pm = parent;
                    while (pm != null && m.Active != "")
                    {
                        pm.Current.Active = m.Active;
                        pm = pm.Parent;
                    }
                }
                CascadeMenus(navs, new BootstrapParentMenu(parent, m), m.Menus, url);
            });
        }
 
        private static void ActiveMenu(BootstrapParentMenu? parent, IEnumerable<BootstrapMenu> menus, string? url)
        {
            if (menus == null || !menus.Any() || string.IsNullOrEmpty(url))
            {
                return;
            }
 
            menus.AsParallel().ForAll(m =>
            {
                m.Active = m.Url.Equals(url, StringComparison.OrdinalIgnoreCase) ? "active" : "";
                ActiveMenu(new BootstrapParentMenu(parent, m), m.Menus, url);
                if (parent != null && m.Active != "")
                {
                    parent.Current.Active = m.Active;
                }
            });
        }
 
        /// <summary>
        /// 在菜单集合中查找指定 Url 的菜单并设置 Active
        /// </summary>
        /// <param name="menus">菜单集合(兼容层次化集合)</param>
        /// <param name="url">指定菜单地址</param>
        public static void ActiveMenu(IEnumerable<BootstrapMenu> menus, string? url) => ActiveMenu(null, menus, url);
 
        /// <summary>
        /// 获得指定用户名可访问的所有菜单集合
        /// </summary>
        /// <param name="userName">当前用户名</param>
        /// <returns>未层次化的菜单集合</returns>
        public static IEnumerable<BootstrapMenu> RetrieveAllMenus(string userName)
        {
            if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException(nameof(userName));
 
            var db = DbAccess;
            var order = db.Provider.EscapeSqlIdentifier("Order");
            return db.Fetch<BootstrapMenu>($"select n.ID, n.ParentId, n.Name, n.{order}, n.Icon, n.Url, n.Category, n.Target, n.IsResource, n.Application, d.Name as CategoryName, ln.Name as ParentName from Navigations n inner join Dicts d on n.Category = d.Code and d.Category = @Category and d.Define = 0 left join Navigations ln on n.ParentId = ln.ID inner join (select nr.NavigationID from Users u inner join UserRole ur on ur.UserID = u.ID inner join NavigationRole nr on nr.RoleID = ur.RoleID where u.UserName = @UserName union select nr.NavigationID from Users u inner join UserGroup ug on u.ID = ug.UserID inner join RoleGroup rg on rg.GroupID = ug.GroupID inner join NavigationRole nr on nr.RoleID = rg.RoleID where u.UserName = @UserName union select n.ID from Navigations n where EXISTS (select UserName from Users u inner join UserRole ur on u.ID = ur.UserID inner join Roles r on ur.RoleID = r.ID where u.UserName = @UserName and r.RoleName = @RoleName)) nav on n.ID = nav.NavigationID", new { UserName = userName, Category = "菜单", RoleName = "Administrators" });
        }
 
        /// <summary>
        /// 获得指定用户名可访问的所有菜单集合
        /// </summary>
        /// <param name="userName">当前用户名</param>
        /// <returns>未层次化的菜单集合</returns>
        public static IEnumerable<BootstrapMenu> RetrieveAllMenusWithCache(string userName) => CacheManager.GetOrAdd($"{RetrieveMenusAll}-{userName}", key => RetrieveAllMenus(userName), RetrieveMenusAll);
 
        /// <summary>
        /// 获得指定用户名与当前应用的所有层次化菜单
        /// </summary>
        /// <param name="userName">当前用户名</param>
        /// <param name="activeUrl">设置此菜单状态为 active</param>
        /// <param name="appId">App 应用ID</param>
        /// <returns>带层次化的菜单集合</returns>
        public static IEnumerable<BootstrapMenu> RetrieveAppCascadeMenus(string userName, string activeUrl, string appId) => CascadeMenus(RetrieveAppMenus(userName, appId), activeUrl);
 
        /// <summary>
        /// 获得指定用户名与当前应用的所有菜单
        /// </summary>
        /// <param name="userName">当前用户名</param>
        /// <param name="appId">App 应用ID</param>
        /// <returns>未层次化的菜单集合</returns>
        public static IEnumerable<BootstrapMenu> RetrieveAppMenus(string userName, string appId) => RetrieveAllMenusWithCache(userName).Where(m => m.Category == "1" && m.IsResource == 0 && m.Application == appId);
 
        /// <summary>
        /// 获得可访问指定菜单的所有角色集合
        /// </summary>
        /// <param name="url">指定菜单地址</param>
        /// <param name="appId">App 应用ID</param>
        /// <returns>角色集合</returns>
        public static IEnumerable<string> RetrieveRolesByUrl(string url, string appId)
        {
            if (string.IsNullOrEmpty(url)) throw new ArgumentNullException(nameof(url));
            if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId));
 
            var ret = DbAccess.Fetch<string>("select distinct r.RoleName from Roles r inner join NavigationRole nr on r.ID = nr.RoleID inner join Navigations n on nr.NavigationID = n.ID and n.Application = @1 and n.Url like @0", $"{url}%", appId);
            if (ret.Count == 0) ret.Add("Administrators");
            return ret;
        }
 
        /// <summary>
        /// 获得可访问指定菜单的所有角色集合 内部缓存 Key 为 RetrieveRolesByUrlDataKey = "RoleHelper-RetrieveRolesByUrl"
        /// </summary>
        /// <param name="url">指定菜单地址</param>
        /// <param name="appId">App 应用ID</param>
        /// <returns>角色集合</returns>
        public static IEnumerable<string> RetrieveRolesByUrlWithCache(string url, string appId) => CacheManager.GetOrAdd($"{RetrieveRolesByUrlDataKey}-{url}-{appId}", key => RetrieveRolesByUrl(url, appId), RetrieveRolesByUrlDataKey);
 
        /// <summary>
        /// 获得指定用户所属的所有角色
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>角色集合</returns>
        public static IEnumerable<string> RetrieveRolesByUserName(string userName) => string.IsNullOrEmpty(userName) ? new List<string>() : DbAccess.Fetch<string>($"select r.RoleName from Roles r inner join UserRole ur on r.ID=ur.RoleID inner join Users u on ur.UserID = u.ID and u.UserName = @0 union select r.RoleName from Roles r inner join RoleGroup rg on r.ID = rg.RoleID inner join {DbAccess.Provider.EscapeSqlIdentifier("Groups")} g on rg.GroupID = g.ID inner join UserGroup ug on ug.GroupID = g.ID inner join Users u on ug.UserID = u.ID and u.UserName=@0", userName);
 
        /// <summary>
        /// 获得指定用户所属的所有角色 内部缓存 Key 为 RetrieveRolesByUserNameDataKey = "RoleHelper-RetrieveRolesByUserName"
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>角色集合</returns>
        public static IEnumerable<string> RetrieveRolesByUserNameWithCache(string userName) => CacheManager.GetOrAdd($"{RetrieveRolesByUserNameDataKey}-{userName}", key => RetrieveRolesByUserName(userName), RetrieveRolesByUserNameDataKey);
 
        /// <summary>
        /// 获得指定用户所属的所有部门
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>部门集合</returns>
        public static IEnumerable<BootstrapGroup> RetrieveGroupsByUserName(string userName) => string.IsNullOrEmpty(userName) ? new List<BootstrapGroup>() : DbAccess.Fetch<BootstrapGroup>($"select g.Id, g.GroupCode, g.GroupName from {DbAccess.Provider.EscapeSqlIdentifier("Groups")} g inner join UserGroup ug on g.ID = ug.GroupID inner join Users u on ug.UserID = u.ID where UserName = @0 union select g.Id, g.GroupCode, g.GroupName from {DbAccess.Provider.EscapeSqlIdentifier("Groups")} g inner join RoleGroup rg on g.ID = rg.GroupID inner join UserRole ur on rg.RoleID = ur.RoleID inner join Users u on ur.UserID = u.ID where UserName = @0 union select g.Id, g.GroupCode, g.GroupName from {DbAccess.Provider.EscapeSqlIdentifier("Groups")} g where exists(select 1 from Users u inner join UserRole ur on u.ID = ur.UserID inner join Roles r on ur.RoleID = r.ID where u.UserName = @0 and r.RoleName = 'Administrators')", userName);
 
        /// <summary>
        /// 获得指定用户所属的所有部门 内部缓存 Key 为 RetrieveGroupsByUserNameDataKey = "GroupHelper-RetrieveGroupsByUserName"
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>部门集合</returns>
        public static IEnumerable<BootstrapGroup> RetrieveGroupsByUserNameWithCache(string userName) => CacheManager.GetOrAdd($"{RetrieveGroupsByUserNameDataKey}-{userName}", key => RetrieveGroupsByUserName(userName), RetrieveGroupsByUserNameDataKey);
 
        /// <summary>
        /// 获得所有部门数据
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<BootstrapGroup> RetrieveGroups() => DbAccess.Fetch<BootstrapGroup>();
 
        /// <summary>
        /// 获得所有部门数据 内部缓存 Key 为 RetrieveGroupsDataKey = "GroupHelper-RetrieveGroups";
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<BootstrapGroup> RetrieveGroupsWithCache() => CacheManager.GetOrAdd(RetrieveGroupsDataKey, key => RetrieveGroups());
 
        /// <summary>
        /// 获得指定用户所属的所有授权 App
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>应用程序集合</returns>
        public static IEnumerable<string> RetrieveAppsByUserName(string userName) => string.IsNullOrEmpty(userName) ? new List<string>() : DbAccess.Fetch<string>($"select d.Code from Dicts d inner join RoleApp ra on d.Code = ra.AppId inner join (select r.Id from Roles r inner join UserRole ur on r.ID = ur.RoleID inner join Users u on ur.UserID = u.ID where u.UserName = @0 union select r.Id from Roles r inner join RoleGroup rg on r.ID = rg.RoleID inner join {DbAccess.Provider.EscapeSqlIdentifier("Groups")} g on rg.GroupID = g.ID inner join UserGroup ug on ug.GroupID = g.ID inner join Users u on ug.UserID = u.ID where u.UserName = @0) r on ra.RoleId = r.ID union select Code from Dicts where Category = @1 and exists(select r.ID from Roles r inner join UserRole ur on r.ID = ur.RoleID inner join Users u on ur.UserID = u.ID where u.UserName = @0 and r.RoleName = @2 union select r.ID from Roles r inner join RoleGroup rg on r.ID = rg.RoleID inner join {DbAccess.Provider.EscapeSqlIdentifier("Groups")} g on rg.GroupID = g.ID inner join UserGroup ug on ug.GroupID = g.ID inner join Users u on ug.UserID = u.ID where u.UserName = @0 and r.RoleName = @2)", userName, "应用程序", "Administrators");
 
        /// <summary>
        /// 获得指定用户所属的所有授权 App 内部缓存 Key 为 RetrieveAppsByUserNameDataKey = "AppHelper-RetrieveAppsByUserName"
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>应用程序集合</returns>
        public static IEnumerable<string> RetrieveAppsByUserNameWithCache(string userName) => CacheManager.GetOrAdd($"{RetrieveAppsByUserNameDataKey}-{userName}", key => RetrieveAppsByUserName(userName), RetrieveAppsByUserNameDataKey);
 
        /// <summary>
        /// 获得系统所有字典表
        /// </summary>
        /// <returns>字典表数据集合</returns>
        public static IEnumerable<BootstrapDict> RetrieveDicts() => DbAccess.Fetch<BootstrapDict>("select * from Dicts");
 
        /// <summary>
        /// 获得系统所有字典表 内部缓存 Key 为 RetrieveDictsDataKey = "BootstrapDict-RetrieveDicts"
        /// </summary>
        /// <param name="category">字典分类 默认获取所有分类</param>
        /// <returns>字典表数据集合</returns>
        public static IEnumerable<BootstrapDict> RetrieveDictsWithCache(string? category = null)
        {
            var dicts = CacheManager.GetOrAdd(RetrieveDictsDataKey, key => RetrieveDicts());
            if (!string.IsNullOrEmpty(category)) dicts = dicts.Where(d => (d.Category ?? "").Equals(category, StringComparison.OrdinalIgnoreCase));
            return dicts;
        }
 
        /// <summary>
        /// 获取当前应用程序关联的个人中心地址
        /// </summary>
        /// <param name="appId">App 应用ID</param>
        /// <returns></returns>
        public static string RetrieveProfilesUrl(string appId)
        {
            return RetrieveAppName("个人中心地址", appId);
        }
 
        /// <summary>
        /// 获取当前应用程序关联的系统设置地址
        /// </summary>
        /// <param name="appId">App 应用ID</param>
        /// <returns></returns>
        public static string RetrieveSettingsUrl(string appId)
        {
            return RetrieveAppName("系统设置地址", appId);
        }
 
        /// <summary>
        /// 获取当前应用程序关联的系统通知地址
        /// </summary>
        /// <param name="appId">App 应用ID</param>
        /// <returns></returns>
        public static string RetrieveNotisUrl(string appId)
        {
            return RetrieveAppName("系统通知地址", appId);
        }
 
        /// <summary>
        /// 获取当前应用程序关联的网站标题
        /// </summary>
        /// <param name="appId">App 应用ID</param>
        /// <returns></returns>
        public static string RetrieveTitle(string appId)
        {
            return RetrieveAppName("网站标题", appId);
        }
 
        /// <summary>
        /// 获取当前应用程序关联的网站页脚
        /// </summary>
        /// <param name="appId">App 应用ID 默认为 "" 表示后台管理程序</param>
        /// <returns></returns>
        public static string RetrieveFooter(string appId)
        {
            return RetrieveAppName("网站页脚", appId);
        }
 
        private static string RetrieveAppName(string name, string appId, string defaultValue = "未设置")
        {
            if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
            if (string.IsNullOrEmpty(appId)) throw new ArgumentNullException(nameof(appId));
 
            var dicts = RetrieveDictsWithCache();
            var appName = dicts.FirstOrDefault(d => d.Category == "应用程序" && d.Code == appId)?.Name;
            return dicts.FirstOrDefault(d => d.Category == appName && d.Name == name)?.Code ?? $"{name}{defaultValue}";
        }
 
        /// <summary>
        /// 获得网站设置中的当前样式
        /// </summary>
        /// <returns></returns>
        public static string RetrieveActiveTheme()
        {
            var theme = RetrieveDictsWithCache().Where(d => d.Name == "使用样式" && d.Category == "当前样式" && d.Define == 0).FirstOrDefault()?.Code;
            return theme == null ? string.Empty : theme.Equals("site.css", StringComparison.OrdinalIgnoreCase) ? string.Empty : theme;
        }
 
        /// <summary>
        /// 获取头像路径 默认 ~/images/uploader/
        /// </summary>
        /// <returns></returns>
        public static string RetrieveIconFolderPath(string defaultPath = "~/images/uploader/") => RetrieveDictsWithCache().FirstOrDefault(d => d.Name == "头像路径" && d.Category == "头像地址" && d.Define == 0)?.Code ?? defaultPath;
 
        /// <summary>
        /// 获取系统图床路径 默认 http://images.sdgxgz.com/
        /// </summary>
        /// <returns></returns>
        public static string RetrieveImagesLibUrl(string defaultUrl = "http://images.sdgxgz.com/") => RetrieveDictsWithCache().FirstOrDefault(d => d.Name == "验证码图床" && d.Category == "系统设置" && d.Define == 0)?.Code ?? defaultUrl;
 
        /// <summary>
        /// 获得指定用户名的用户信息
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>BootstrapUser 实例</returns>
        public static BootstrapUser? RetrieveUserByUserName(string userName)
        {
            if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException(nameof(userName));
 
            var user = DbAccess.SingleOrDefault<BootstrapUser>("select UserName, DisplayName, Icon, Css, App from Users where ApprovedTime is not null and UserName = @0", userName);
            if (user != null)
            {
                if (string.IsNullOrEmpty(user.Icon))
                {
                    user.Icon = "default.jpg";
                }
            }
            return user;
        }
 
        /// <summary>
        /// 获得指定用户名的用户信息
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns>BootstrapUser 实例</returns>
        public static BootstrapUser? RetrieveUserByUserNameWithCache(string userName) => CacheManager.GetOrAdd(string.Format("{0}-{1}", RetrieveUsersByNameDataKey, userName), key => RetrieveUserByUserName(userName), RetrieveUsersByNameDataKey);
 
        /// <summary>
        /// 通过当前用户名与指定菜单路径获取此菜单下所有授权按钮集合 (userName, url, auths) => bool
        /// </summary>
        /// <param name="menus">当前授权可用的菜单集合</param>
        /// <param name="url">资源按钮所属菜单</param>
        /// <param name="auths">资源授权码</param>
        /// <returns></returns>
        public static bool AuthorizateButtons(IEnumerable<BootstrapMenu> menus, string url, string auths)
        {
            var activeMenu = menus.FirstOrDefault(m => m.Url.Equals(url, StringComparison.OrdinalIgnoreCase));
            if (activeMenu == null) return false;
 
            var authorKeys = menus.Where(m => m.ParentId == activeMenu.Id && m.IsResource == 2).Select(m => m.Url);
            var keys = auths.SpanSplitAny(",. ;", StringSplitOptions.RemoveEmptyEntries);
            return keys.Any(m => authorKeys.Any(k => k == m));
        }
 
        private static IDatabase DbAccess
        {
            get
            {
                if (Mappers.GetMapper(typeof(BootstrapDict), null) == null) Mappers.Register(typeof(BootstrapDict).Assembly, new BootstrapAdminConventionMapper());
                return DbManager.Create("ba");
            }
        }
    }
}