chore: 增加数据服务实现层

This commit is contained in:
Argo-Tianyi 2022-01-12 22:41:38 +08:00
parent 663580b8ae
commit 1317b2828f
19 changed files with 840 additions and 142 deletions

View File

@ -149,7 +149,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapAdmin.DataAccess.P
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapAdmin.Web.Core", "src\blazor\admin\BootstrapAdmin.Web.Core\BootstrapAdmin.Web.Core.csproj", "{DA143654-C258-410D-B5DC-FE446ED99CE4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapClient.Web.DataAccess", "src\blazor\client\BootstrapClient.DataAccess\BootstrapClient.Web.DataAccess.csproj", "{640F598B-6586-4AD6-B544-78CFF2602DFB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapClient.DataAccess.PetaPoco", "src\blazor\client\BootstrapClient.DataAccess\BootstrapClient.DataAccess.PetaPoco.csproj", "{640F598B-6586-4AD6-B544-78CFF2602DFB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapClient.Web.Shared", "src\blazor\client\BootstrapClient.Shared\BootstrapClient.Web.Shared.csproj", "{93770088-3463-427B-9CD8-88B8D7945C83}"
EndProject
@ -159,6 +159,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootStarpAdmin.DataAccess.F
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootStarpAdmin.DataAccess.SqlSugar", "src\blazor\admin\BootStarpAdmin.DataAccess.SqlSugar\BootStarpAdmin.DataAccess.SqlSugar.csproj", "{1D20E6CF-9825-4CDE-B732-AE586BD1AABA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapClient.Web.Core", "src\blazor\client\BootstrapClient.Web.Core\BootstrapClient.Web.Core.csproj", "{FFDF9FF9-0B29-47D3-AD42-53A476B570EC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapClient.DataAccess.Models", "src\blazor\client\BootstrapClient.Web.Models\BootstrapClient.DataAccess.Models.csproj", "{CC3DF23A-2880-438F-BDEB-DB093E919ABA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -233,6 +237,14 @@ Global
{1D20E6CF-9825-4CDE-B732-AE586BD1AABA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D20E6CF-9825-4CDE-B732-AE586BD1AABA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D20E6CF-9825-4CDE-B732-AE586BD1AABA}.Release|Any CPU.Build.0 = Release|Any CPU
{FFDF9FF9-0B29-47D3-AD42-53A476B570EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FFDF9FF9-0B29-47D3-AD42-53A476B570EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFDF9FF9-0B29-47D3-AD42-53A476B570EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFDF9FF9-0B29-47D3-AD42-53A476B570EC}.Release|Any CPU.Build.0 = Release|Any CPU
{CC3DF23A-2880-438F-BDEB-DB093E919ABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC3DF23A-2880-438F-BDEB-DB093E919ABA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC3DF23A-2880-438F-BDEB-DB093E919ABA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC3DF23A-2880-438F-BDEB-DB093E919ABA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -272,6 +284,8 @@ Global
{6CD7A35B-93A8-4DB2-B078-EE5A81F40032} = {55A2459A-6BDE-4493-B2C0-5BE1673E99EE}
{11122D97-B349-4A3E-B7DD-73B8B363C47C} = {45ADEF9B-C8BD-4224-9E12-F6716E85A22C}
{1D20E6CF-9825-4CDE-B732-AE586BD1AABA} = {45ADEF9B-C8BD-4224-9E12-F6716E85A22C}
{FFDF9FF9-0B29-47D3-AD42-53A476B570EC} = {55A2459A-6BDE-4493-B2C0-5BE1673E99EE}
{CC3DF23A-2880-438F-BDEB-DB093E919ABA} = {55A2459A-6BDE-4493-B2C0-5BE1673E99EE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {221EAE38-5F75-4391-9A48-E462A9F3B8FC}

View File

@ -0,0 +1,73 @@
using PetaPoco;
using System.Reflection;
namespace BootstrapClient.DataAccess.PetaPoco;
class BootstrapAdminConventionMapper : ConventionMapper
{
/// <summary>
///
/// </summary>
/// <param name="pocoType"></param>
/// <returns></returns>
public override TableInfo GetTableInfo(Type pocoType)
{
var ti = base.GetTableInfo(pocoType);
ti.AutoIncrement = true;
// 支持 Oracle 数据库
ti.SequenceName = $"SEQ_{ti.TableName.ToUpperInvariant()}_ID";
ti.TableName = pocoType.Name switch
{
"Error" => "Exceptions",
_ => $"{pocoType.Name}s"
};
return ti;
}
///// <summary>
/////
///// </summary>
///// <param name="pocoProperty"></param>
///// <returns></returns>
//public override ColumnInfo GetColumnInfo(PropertyInfo pocoProperty) => pocoProperty.DeclaringType?.Name switch
//{
// nameof(Models.User) => GetUserColumnInfo(pocoProperty),
// nameof(Models.Navigation) => GetNavigationColumnInfo(pocoProperty),
// _ => base.GetColumnInfo(pocoProperty)
//};
//private ColumnInfo GetUserColumnInfo(PropertyInfo pocoProperty)
//{
// var ci = base.GetColumnInfo(pocoProperty);
// var resultColumns = new List<string>
// {
// nameof(Models.User.NewPassword),
// nameof(Models.User.ConfirmPassword),
// nameof(Models.User.Period),
// nameof(Models.User.IsReset)
// };
// ci.ResultColumn = resultColumns.Any(c => c == ci.ColumnName);
// return ci;
//}
//private ColumnInfo GetNavigationColumnInfo(PropertyInfo pocoProperty)
//{
// var ci = base.GetColumnInfo(pocoProperty);
// var resultColumns = new List<string>
// {
// nameof(Models.Navigation.HasChildren)
// };
// ci.ResultColumn = resultColumns.Any(c => c == ci.ColumnName);
// return ci;
//}
//public override Func<object?, object?> GetFromDbConverter(PropertyInfo targetProperty, Type sourceType) => targetProperty.PropertyType.IsEnum && sourceType == typeof(string)
// ? new StringToEnumConverter(targetProperty.PropertyType).ConvertFromDb
// : base.GetFromDbConverter(targetProperty, sourceType);
//public override Func<object?, object?> GetToDbConverter(PropertyInfo targetProperty) => targetProperty.PropertyType.IsEnum
// ? new StringToEnumConverter(targetProperty.PropertyType).ConvertToDb
// : base.GetToDbConverter(targetProperty);
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="BootstrapBlazor" Version="6.2.7-beta03" />
<PackageReference Include="Longbow.Security.Cryptography" Version="5.2.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.1" />
<PackageReference Include="PetaPoco.Extensions" Version="5.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BootstrapClient.Web.Core\BootstrapClient.Web.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -1,8 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\admin\BootstrapAdmin.DataAccess.PetaPoco\BootstrapAdmin.DataAccess.PetaPoco.csproj" />
<ProjectReference Include="..\..\admin\BootstrapAdmin.Web.Core\BootstrapAdmin.Web.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,75 @@
using BootstrapBlazor.Components;
using BootstrapClient.DataAccess.PetaPoco;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PetaPoco;
using System.Collections.Specialized;
using System.Data.Common;
using System.Text;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
///
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
///
/// </summary>
/// <param name="services"></param>
/// <param name="builder"></param>
/// <returns></returns>
public static IServiceCollection AddPetaPocoDataAccessServices(this IServiceCollection services, Action<IServiceProvider, IDatabaseBuildConfiguration> builder)
{
services.TryAddSingleton<IDatabase>(provider =>
{
var option = DatabaseConfiguration.Build();
builder(provider, option);
option.UsingDefaultMapper<BootstrapAdminConventionMapper>();
var db = new Database(option);
var logger = provider.GetRequiredService<ILogger<Database>>();
db.ExceptionThrown += (sender, e) =>
{
var message = e.Exception.Format(new NameValueCollection()
{
[nameof(db.LastCommand)] = db.LastCommand,
[nameof(db.LastArgs)] = string.Join(",", db.LastArgs)
});
logger.LogError(new EventId(1001, "GlobalException"), e.Exception, message);
};
var env = provider.GetRequiredService<IWebHostEnvironment>();
if (env.IsDevelopment())
{
db.CommandExecuted += (sender, args) =>
{
var parameters = new StringBuilder();
foreach (DbParameter p in args.Command.Parameters)
{
parameters.AppendFormat("{0}: {1} ", p.ParameterName, p.Value);
}
logger.LogInformation(args.Command.CommandText);
logger.LogInformation(parameters.ToString());
};
};
return db;
});
//// 增加数据服务
//services.AddSingleton(typeof(IDataService<>), typeof(DefaultDataService<>));
//// 增加业务服务
//services.AddSingleton<IApp, AppService>();
//services.AddSingleton<IDict, DictService>();
//services.AddSingleton<IException, ExceptionService>();
//services.AddSingleton<IGroup, GroupService>();
//services.AddSingleton<ILogin, LoginService>();
//services.AddSingleton<INavigation, NavigationService>();
//services.AddSingleton<IRole, RoleService>();
//services.AddSingleton<IUser, UserService>();
return services;
}
}

View File

@ -0,0 +1,68 @@
using BootstrapAdmin.DataAccess.Models;
using BootstrapClient.Web.Core;
using PetaPoco;
namespace BootstrapClient.DataAccess.PetaPoco.Services;
/// <summary>
///
/// </summary>
class NavigationService : INavigation
{
private IDatabase Database { get; }
/// <summary>
///
/// </summary>
/// <param name="db"></param>
public NavigationService(IDatabase db)
{
Database = db;
}
/// <summary>
/// 获得指定用户名可访问的所有菜单集合
/// </summary>
/// <param name="userName">当前用户名</param>
/// <returns>未层次化的菜单集合</returns>
public List<Navigation> GetMenus(string userName)
{
var order = Database.Provider.EscapeSqlIdentifier("Order");
return Database.Fetch<Navigation>($"select n.ID, n.ParentId, n.Name, n.{order}, n.Icon, n.Url, n.Category, n.Target, n.IsResource, n.Application, ln.Name as ParentName from Navigations n inner join Dicts d on n.Category = d.Code and d.Category = @Category and d.Define = @Define 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 ORDER BY n.Application, n.{order}", new { UserName = userName, Category = "菜单", RoleName = "Administrators", Define = 1 });
}
/// <summary>
///
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
public List<string> GetMenusByRoleId(string? roleId)
{
return Database.Fetch<string>("select NavigationID from NavigationRole where RoleID = @0", roleId);
}
/// <summary>
///
/// </summary>
/// <param name="roleId"></param>
/// <param name="menuIds"></param>
/// <returns></returns>
public bool SaveMenusByRoleId(string? roleId, List<string> menuIds)
{
var ret = false;
try
{
Database.BeginTransaction();
Database.Execute("delete from NavigationRole where RoleID = @0", roleId);
Database.InsertBatch("NavigationRole", menuIds.Select(g => new { NavigationID = g, RoleID = roleId }));
Database.CompleteTransaction();
ret = true;
}
catch (Exception)
{
Database.AbortTransaction();
throw;
}
return ret;
}
}

View File

@ -0,0 +1,51 @@
using BootstrapAdmin.DataAccess.Models;
using BootstrapClient.Web.Core;
using PetaPoco;
namespace BootstrapClient.DataAccess.PetaPoco.Services;
class UserService : IUser
{
private IDatabase Database { get; }
/// <summary>
///
/// </summary>
/// <param name="db"></param>
public UserService(IDatabase db)
{
Database = db;
}
/// <summary>
///
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public User? GetUserByUserName(string? userName)
{
return string.IsNullOrEmpty(userName) ? null : Database.FirstOrDefault<User>("Where UserName = @0", userName);
}
/// <summary>
///
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public List<string> GetApps(string userName)
{
return Database.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 {Database.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 {Database.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>
///
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public List<string> GetRoles(string userName)
{
return Database.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 {Database.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);
}
}

View File

@ -10,7 +10,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BootstrapClient.DataAccess\BootstrapClient.Web.DataAccess.csproj" />
<ProjectReference Include="..\BootstrapClient.DataAccess\BootstrapClient.DataAccess.PetaPoco.csproj" />
</ItemGroup>
</Project>

View File

@ -14,16 +14,19 @@ public static class MenuExtensions
/// </summary>
/// <param name="menu"></param>
/// <returns></returns>
public static MenuItem Parse(this Navigation menu) => new()
public static MenuItem Parse(this Navigation menu)
{
Text = menu.Name,
Url = menu.Url.Replace("~", ""),
Icon = menu.Icon,
Match = NavLinkMatch.All,
Target = menu.Target,
Id = menu.Id,
ParentId = menu.ParentId
};
return new()
{
Text = menu.Name,
Url = menu.Url.Replace("~", ""),
Icon = menu.Icon,
Match = NavLinkMatch.All,
Target = menu.Target,
Id = menu.Id,
ParentId = menu.ParentId
};
}
/// <summary>
/// 获取前台菜单
@ -31,7 +34,7 @@ public static class MenuExtensions
/// <returns></returns>
public static IEnumerable<MenuItem> ToClientMenus(this List<Navigation> navigations)
{
var menus = navigations.Where(m => m.Category == EnumNavigationCategory.Customer && m.IsResource == 0);
var menus = navigations.Where(m => m.IsResource == 0);
return CascadeMenus(menus);
}

View File

@ -1,6 +1,6 @@
using Bootstrap.Security.Blazor;
using BootstrapAdmin.Web.Core;
using BootstrapBlazor.Components;
using BootstrapClient.Web.Core;
using BootstrapClient.Web.Shared.Extensions;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Components;
@ -8,130 +8,132 @@ using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace BootstrapClient.Web.Shared.Shared
namespace BootstrapClient.Web.Shared.Shared;
/// <summary>
///
/// </summary>
public sealed partial class MainLayout
{
private bool UseTabSet { get; set; } = true;
private string Theme { get; set; } = "";
private bool IsOpen { get; set; }
private bool IsFixedHeader { get; set; } = true;
private bool IsFixedFooter { get; set; } = true;
private bool IsFullSide { get; set; } = true;
private bool ShowFooter { get; set; } = true;
private IEnumerable<MenuItem>? MenuItems { get; set; }
private string? ProfileUrl { get; set; }
private string? SettingsUrl { get; set; }
private string? NotificationUrl { get; set; }
/// <summary>
///
/// 获得 当前用户登录显示名称
/// </summary>
public sealed partial class MainLayout
[NotNull]
public string? DisplayName { get; private set; }
private string? Title { get; set; }
private string? Footer { get; set; }
/// <summary>
/// 获得 当前用户登录名
/// </summary>
[NotNull]
public string? UserName { get; private set; }
[Inject]
[NotNull]
private IBootstrapAdminService? SecurityService { get; set; }
[Inject]
[NotNull]
private AuthenticationStateProvider? AuthenticationStateProvider { get; set; }
[Inject]
[NotNull]
private IDict? DictsService { get; set; }
[Inject]
[NotNull]
private IUser? UsersService { get; set; }
[Inject]
[NotNull]
private INavigation? NavigationsService { get; set; }
[Inject]
[NotNull]
private IOptions<BootstrapAdminAuthenticationOptions>? AuthorizationOption { get; set; }
[Inject]
[NotNull]
private NavigationManager? NavigationManager { get; set; }
[Inject]
[NotNull]
private IConfiguration? Configuration { get; set; }
private string? AppId { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
private bool UseTabSet { get; set; } = true;
base.OnInitialized();
private string Theme { get; set; } = "";
private bool IsOpen { get; set; }
private bool IsFixedHeader { get; set; } = true;
private bool IsFixedFooter { get; set; } = true;
private bool IsFullSide { get; set; } = true;
private bool ShowFooter { get; set; } = true;
private IEnumerable<MenuItem>? MenuItems { get; set; }
private string? ProfileUrl { get; set; }
private string? SettingsUrl { get; set; }
private string? NotificationUrl { get; set; }
/// <summary>
/// 获得 当前用户登录显示名称
/// </summary>
[NotNull]
public string? DisplayName { get; private set; }
private string? Title { get; set; }
private string? Footer { get; set; }
/// <summary>
/// 获得 当前用户登录名
/// </summary>
[NotNull]
public string? UserName { get; private set; }
[Inject]
[NotNull]
private IBootstrapAdminService? SecurityService { get; set; }
[Inject]
[NotNull]
private AuthenticationStateProvider? AuthenticationStateProvider { get; set; }
[Inject]
[NotNull]
private IDict? DictsService { get; set; }
[Inject]
[NotNull]
private IUser? UsersService { get; set; }
[Inject]
[NotNull]
private INavigation? NavigationsService { get; set; }
[Inject]
[NotNull]
private IOptions<BootstrapAdminAuthenticationOptions>? AuthorizationOption { get; set; }
[Inject]
[NotNull]
private NavigationManager? NavigationManager { get; set; }
[Inject]
[NotNull]
private IConfiguration? Configuration { get; set; }
private string? AppId { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
AppId = Configuration.GetValue("AppId", "Blazor");
ProfileUrl = CombinePath(DictsService.GetProfileUrl(AppId));
SettingsUrl = CombinePath(DictsService.GetSettingsUrl(AppId));
NotificationUrl = CombinePath(DictsService.GetNotificationUrl(AppId));
}
private string CombinePath(string? url)
{
url ??= "";
var hostUrl = AuthorizationOption.Value.AuthHost.TrimEnd('/');
return string.Join('/', hostUrl, url.TrimStart('/'));
}
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override async Task OnInitializedAsync()
{
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var userName = state.User.Identity?.Name;
if (!string.IsNullOrEmpty(userName))
{
UserName = userName;
var user = UsersService.GetUserByUserName(userName);
MenuItems = NavigationsService.GetAllMenus(userName).ToClientMenus();
DisplayName = user?.DisplayName ?? "未注册账户";
Title = DictsService.GetWebTitle();
Footer = DictsService.GetWebFooter();
}
}
private Task<bool> OnAuthorizing(string url) => SecurityService.AuhorizingNavigation(UserName, url);
private string LogoutUrl => CombinePath($"/Account/Logout?AppId={AppId}");
private string AuthorUrl => CombinePath($"/Account/Login?ReturnUrl={NavigationManager.Uri}&AppId={AppId}");
AppId = Configuration.GetValue("AppId", "Blazor");
ProfileUrl = CombinePath(DictsService.GetProfileUrl(AppId));
SettingsUrl = CombinePath(DictsService.GetSettingsUrl(AppId));
NotificationUrl = CombinePath(DictsService.GetNotificationUrl(AppId));
}
private string CombinePath(string? url)
{
url ??= "";
var hostUrl = AuthorizationOption.Value.AuthHost.TrimEnd('/');
return string.Join('/', hostUrl, url.TrimStart('/'));
}
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override async Task OnInitializedAsync()
{
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var userName = state.User.Identity?.Name;
if (!string.IsNullOrEmpty(userName))
{
UserName = userName;
var user = UsersService.GetUserByUserName(userName);
MenuItems = NavigationsService.GetMenus(userName).ToClientMenus();
DisplayName = user?.DisplayName ?? "未注册账户";
Title = DictsService.GetWebTitle();
Footer = DictsService.GetWebFooter();
}
}
private Task<bool> OnAuthorizing(string url)
{
return SecurityService.AuhorizingNavigation(UserName, url);
}
private string LogoutUrl => CombinePath($"/Account/Logout?AppId={AppId}");
private string AuthorUrl => CombinePath($"/Account/Login?ReturnUrl={NavigationManager.Uri}&AppId={AppId}");
}

View File

@ -0,0 +1,55 @@
using Bootstrap.Security.Blazor;
namespace BootstrapClient.Web.Core.Services;
/// <summary>
///
/// </summary>
public class AdminService : IBootstrapAdminService
{
private IUser User { get; set; }
private INavigation Navigations { get; set; }
/// <summary>
///
/// </summary>
/// <param name="user"></param>
/// <param name="navigations"></param>
public AdminService(IUser user, INavigation navigations)
{
User = user;
Navigations = navigations;
}
/// <summary>
/// 通过用户名获取角色集合方法
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public List<string> GetRoles(string userName) => User.GetRoles(userName);
/// <summary>
/// 通过用户名获取授权 App 集合方法
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public List<string> GetApps(string userName) => User.GetApps(userName);
/// <summary>
/// 通过用户名检查当前请求 Url 是否已授权方法
/// </summary>
/// <param name="userName"></param>
/// <param name="url"></param>
/// <returns></returns>
public Task<bool> AuhorizingNavigation(string userName, string url)
{
var ret = false;
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out var uri))
{
ret = Navigations.GetMenus(userName)
.Any(m => m.Url.Contains(uri.AbsolutePath, StringComparison.OrdinalIgnoreCase));
}
return Task.FromResult(ret);
}
}

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Bootstrap.Security.Blazor" Version="6.0.1-beta01" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BootstrapClient.Web.Models\BootstrapClient.DataAccess.Models.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,52 @@
namespace BootstrapClient.Web.Core;
/// <summary>
/// Dict 字典表接口
/// </summary>
public interface IDict
{
/// <summary>
/// 获取当前系统配置是否为演示模式
/// </summary>
/// <returns></returns>
bool IsDemo();
/// <summary>
/// 获取 站点 Title 配置信息
/// </summary>
/// <returns></returns>
string GetWebTitle();
/// <summary>
/// 获取站点 Footer 配置信息
/// </summary>
/// <returns></returns>
string GetWebFooter();
/// <summary>
///
/// </summary>
/// <param name="appId"></param>
/// <returns></returns>
string? GetProfileUrl(string appId);
/// <summary>
///
/// </summary>
/// <param name="appId"></param>
/// <returns></returns>
string? GetSettingsUrl(string appId);
/// <summary>
///
/// </summary>
/// <param name="appId"></param>
/// <returns></returns>
string? GetNotificationUrl(string appId);
/// <summary>
///
/// </summary>
/// <returns></returns>
string RetrieveIconFolderPath();
}

View File

@ -0,0 +1,15 @@
using BootstrapAdmin.DataAccess.Models;
namespace BootstrapClient.Web.Core;
/// <summary>
///
/// </summary>
public interface INavigation
{
/// <summary>
///
/// </summary>
/// <returns></returns>
List<Navigation> GetMenus(string userName);
}

View File

@ -0,0 +1,30 @@
using BootstrapAdmin.DataAccess.Models;
namespace BootstrapClient.Web.Core;
/// <summary>
///
/// </summary>
public interface IUser
{
/// <summary>
///
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
User? GetUserByUserName(string? userName);
/// <summary>
/// 通过用户名获取角色列表
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
List<string> GetRoles(string userName);
/// <summary>
/// 通过用户名获得授权 App 集合
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
List<string> GetApps(string userName);
}

View File

@ -0,0 +1,3 @@
<Project Sdk="Microsoft.NET.Sdk">
</Project>

View File

@ -0,0 +1,79 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
/// Bootstrap Admin 后台管理菜单相关操作实体类
/// </summary>
[Table("Navigations")]
public class Navigation
{
/// <summary>
/// 获得/设置 菜单主键ID
/// </summary>
[NotNull]
public string? Id { set; get; }
/// <summary>
/// 获得/设置 父级菜单ID 默认为 0
/// </summary>
public string ParentId { set; get; } = "0";
/// <summary>
/// 获得/设置 菜单名称
/// </summary>
[Display(Name = "名称")]
[NotNull]
public string? Name { get; set; }
/// <summary>
/// 获得/设置 菜单序号
/// </summary>
[Display(Name = "序号")]
public int Order { get; set; }
/// <summary>
/// 获得/设置 菜单图标
/// </summary>
[Display(Name = "图标")]
public string? Icon { get; set; }
/// <summary>
/// 获得/设置 菜单URL地址
/// </summary>
[NotNull]
[Display(Name = "地址")]
public string? Url { get; set; }
/// <summary>
/// 获得/设置 菜单分类, 0 表示系统菜单 1 表示用户自定义菜单
/// </summary>
[Display(Name = "类别")]
public int Category { get; set; }
/// <summary>
/// 获得/设置 链接目标
/// </summary>
[Display(Name = "目标")]
public string? Target { get; set; }
/// <summary>
/// 获得/设置 是否为资源文件, 0 表示菜单 1 表示资源 2 表示按钮
/// </summary>
[Display(Name = "类型")]
public int IsResource { get; set; }
/// <summary>
/// 获得/设置 所属应用程序此属性由BA后台字典表分配
/// </summary>
[Display(Name = "所属应用")]
public string? Application { get; set; }
/// <summary>
///
/// </summary>
public bool HasChildren { get; set; }
}

View File

@ -0,0 +1,161 @@
using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
///
/// </summary>
public class User
{
/// <summary>
/// 获得/设置 系统登录用户名
/// </summary>
[Display(Name = "登录名称")]
[Required(ErrorMessage = "{0}不可为空")]
[RegularExpression("^[a-zA-Z0-9_@.]*$", ErrorMessage = "")]
[MaxLength(16, ErrorMessage = "{0}不能超过 16 个字符")]
[NotNull]
public string? UserName { get; set; }
/// <summary>
/// 获得/设置 用户显示名称
/// </summary>
[Display(Name = "显示名称")]
[Required(ErrorMessage = "{0}不可为空")]
[MaxLength(20, ErrorMessage = "{0}不能超过 20 个字符")]
[NotNull]
public string? DisplayName { get; set; }
/// <summary>
/// 获得/设置 用户头像图标路径
/// </summary>
[Display(Name = "用户头像")]
public string? Icon { get; set; }
/// <summary>
/// 获得/设置 用户设置样式表名称
/// </summary>
[Display(Name = "主题")]
public string? Css { get; set; }
/// <summary>
/// 获得/设置 用户默认登陆 App 标识
/// </summary>
[Display(Name = "默认 APP")]
public string? App { get; set; }
/// <summary>
/// 获得/设置 用户主键ID
/// </summary>
public string? Id { get; set; }
/// <summary>
/// 获取/设置 密码
/// </summary>
[Display(Name = "密码")]
[Required(ErrorMessage = "{0}不可为空")]
[MaxLength(16, ErrorMessage = "{0}不能超过 16 个字符")]
[NotNull]
public string? Password { get; set; }
/// <summary>
/// 获取/设置 密码盐
/// </summary>
public string? PassSalt { get; set; }
/// <summary>
/// 获得/设置 用户注册时间
/// </summary>
[Display(Name = "注册时间")]
public DateTime RegisterTime { get; set; } = DateTime.Now;
/// <summary>
/// 获得/设置 用户被批复时间
/// </summary>
[Display(Name = "授权时间")]
public DateTime? ApprovedTime { get; set; }
/// <summary>
/// 获得/设置 用户批复人
/// </summary>
[Display(Name = "授权人")]
public string? ApprovedBy { get; set; }
/// <summary>
/// 获得/设置 用户的申请理由
/// </summary>
[Display(Name = "说明")]
[NotNull]
public string? Description { get; set; }
/// <summary>
/// 获得/设置 通知描述 2分钟内为刚刚
/// </summary>
public string? Period { get; set; }
/// <summary>
/// 获得/设置 新密码
/// </summary>
[Display(Name = "新密码")]
[Required(ErrorMessage = "{0}不可为空")]
[MaxLength(16, ErrorMessage = "{0}不能超过 16 个字符")]
[NotNull]
public string? NewPassword { get; set; }
/// <summary>
/// 获得/设置 新密码
/// </summary>
[Display(Name = "确认密码")]
[Required(ErrorMessage = "{0}不可为空")]
[Compare("NewPassword", ErrorMessage = "{0}与{1}不一致")]
[MaxLength(16, ErrorMessage = "{0}不能超过 16 个字符")]
[NotNull]
public string? ConfirmPassword { get; set; }
/// <summary>
/// 获得/设置 是否重置密码
/// </summary>
public int IsReset { get; set; }
/// <summary>
/// 获得/设置 默认格式为 DisplayName (UserName)
/// </summary>
/// <returns></returns>
public override string ToString() => $"{DisplayName} ({UserName})";
}
/// <summary>
/// 用户状态枚举类型
/// </summary>
public enum UserStates
{
/// <summary>
/// 更改密码
/// </summary>
ChangePassword,
/// <summary>
/// 更改样式
/// </summary>
ChangeTheme,
/// <summary>
/// 更改显示名称
/// </summary>
ChangeDisplayName,
/// <summary>
/// 审批用户
/// </summary>
ApproveUser,
/// <summary>
/// 拒绝用户
/// </summary>
RejectUser,
/// <summary>
/// 保存默认应用
/// </summary>
SaveApp
}

View File

@ -1,4 +1,4 @@
using BootstrapAdmin.Web.Core.Services;
using BootstrapClient.Web.Core.Services;
using PetaPoco;
using PetaPoco.Providers;