!98 feat(#I4PYQ2): add freesql support

* feat: 增加 PageIf 扩展精简代码
* feat: 支持高级搜索与自定义搜索
* feat: 完善菜单搜索与过滤功能
* chore: 增加 xml 注释
* feat: 菜单维护界面开启二级菜单
* chore: 更新依赖包
* feat: 健康检查单独开启 ShowLoading
* refactor: 格式化代码
* Merge branch 'master' into dev-blazor-freesql
* refactor: 更新代码注释
* refactor: 修复编译错误
* feat: 改用 petapoco 服务
* fix: 修复合并代码错误
* feat: 增加异常日志字段排序
* Merge remote-tracking branch 'origin/master' into dev-blazor-freesql
* fix: 修复插入命令并未执行
* feat: 添加 Exception 服务
* feat: 添加 Exception 服务
* feat: 添加 App 服务
* feat: 添加 Navigation 服务
* feat: 添加 Group 和 Role 服务
* feat: 添加中间表实体
* feat: 添加用户功能
* feat: 完善用户服务
* feat: 完善字典服务
* feat: 增加 GetUserByUserName 实现
* chore: 更新配置
* feat: 增加实体类映射
* feat: 移除 LongExtensions 扩展方法
* feat: 完善数据服务方法
* chore: 格式化代码
* refactor: 优化注册服务接口
* Merge remote-tracking branch 'origin/master' into dev-blazor-freesql
* feat: 初始化 FreeSql
This commit is contained in:
Nine 2022-01-08 18:05:59 +00:00 committed by Argo
parent 57f78cc7f5
commit 5ee01d88be
84 changed files with 1836 additions and 532 deletions

View File

@ -155,6 +155,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapClient.Web.Shared"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BootstrapClient.Web", "src\blazor\client\BootstrapClient.Web\BootstrapClient.Web.csproj", "{6CD7A35B-93A8-4DB2-B078-EE5A81F40032}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootStarpAdmin.DataAccess.FreeSql", "src\blazor\admin\BootStarpAdmin.DataAccess.FreeSql\BootStarpAdmin.DataAccess.FreeSql.csproj", "{11122D97-B349-4A3E-B7DD-73B8B363C47C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -221,6 +223,10 @@ Global
{6CD7A35B-93A8-4DB2-B078-EE5A81F40032}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CD7A35B-93A8-4DB2-B078-EE5A81F40032}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CD7A35B-93A8-4DB2-B078-EE5A81F40032}.Release|Any CPU.Build.0 = Release|Any CPU
{11122D97-B349-4A3E-B7DD-73B8B363C47C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11122D97-B349-4A3E-B7DD-73B8B363C47C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11122D97-B349-4A3E-B7DD-73B8B363C47C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11122D97-B349-4A3E-B7DD-73B8B363C47C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -258,6 +264,7 @@ Global
{640F598B-6586-4AD6-B544-78CFF2602DFB} = {55A2459A-6BDE-4493-B2C0-5BE1673E99EE}
{93770088-3463-427B-9CD8-88B8D7945C83} = {55A2459A-6BDE-4493-B2C0-5BE1673E99EE}
{6CD7A35B-93A8-4DB2-B078-EE5A81F40032} = {55A2459A-6BDE-4493-B2C0-5BE1673E99EE}
{11122D97-B349-4A3E-B7DD-73B8B363C47C} = {45ADEF9B-C8BD-4224-9E12-F6716E85A22C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {221EAE38-5F75-4391-9A48-E462A9F3B8FC}

View File

@ -11,7 +11,7 @@
<RepositoryUrl>https://gitee.com/LongbowEnterprise/BootstrapAdmin.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Nullable>enable</Nullable>
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
</Project>

View File

@ -1,6 +1,6 @@
<Project>
<Import Project="..\..\Directory.Build.props" />
<Import Project="..\Directory.Build.props" />
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\BootstrapAdmin.DataAccess.Models\BootstrapAdmin.DataAccess.Models.csproj" />
<ProjectReference Include="..\BootstrapAdmin.Web.Core\BootstrapAdmin.Web.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor" Version="6.2.3" />
<PackageReference Include="FreeSql.Provider.Sqlite" Version="3.0.100" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,63 @@
using BootstrapBlazor.Components;
using FreeSql;
using FreeSql.Internal.Model;
namespace BootStarpAdmin.DataAccess.FreeSql.Extensions;
/// <summary>
///
/// </summary>
static class FilterExtensions
{
public static ISelect<TModel> PageIf<TModel>(this ISelect<TModel> source, int pageIndex, int pageItems, bool isPage) => isPage
? source.Page(pageIndex, pageItems)
: source;
/// <summary>
///
/// </summary>
/// <param name="filters"></param>
/// <returns></returns>
public static DynamicFilterInfo ToDynamicFilter(this IEnumerable<IFilterAction> filters)
{
var ret = new DynamicFilterInfo() { Filters = new List<DynamicFilterInfo>() };
// 处理 过滤 高级搜索 自定义搜索
foreach (var filter in filters)
{
var item = new DynamicFilterInfo() { Filters = new List<DynamicFilterInfo>() };
var actions = filter.GetFilterConditions();
foreach (var f in actions)
{
item.Logic = f.FilterLogic.ToDynamicFilterLogic();
item.Filters.Add(new DynamicFilterInfo()
{
Field = f.FieldKey,
Value = f.FieldValue,
Operator = f.FilterAction.ToDynamicFilterOperator()
});
}
ret.Filters.Add(item);
}
return ret;
}
private static DynamicFilterLogic ToDynamicFilterLogic(this FilterLogic logic) => logic switch
{
FilterLogic.And => DynamicFilterLogic.And,
_ => DynamicFilterLogic.Or
};
private static DynamicFilterOperator ToDynamicFilterOperator(this FilterAction action) => action switch
{
FilterAction.Equal => DynamicFilterOperator.Equal,
FilterAction.NotEqual => DynamicFilterOperator.NotEqual,
FilterAction.Contains => DynamicFilterOperator.Contains,
FilterAction.NotContains => DynamicFilterOperator.NotContains,
FilterAction.GreaterThan => DynamicFilterOperator.GreaterThan,
FilterAction.GreaterThanOrEqual => DynamicFilterOperator.GreaterThanOrEqual,
FilterAction.LessThan => DynamicFilterOperator.LessThan,
FilterAction.LessThanOrEqual => DynamicFilterOperator.LessThanOrEqual,
_ => throw new System.NotSupportedException()
};
}

View File

@ -0,0 +1,56 @@
using BootStarpAdmin.DataAccess.FreeSql.Models;
using BootstrapAdmin.DataAccess.Models;
namespace BootStarpAdmin.DataAccess.FreeSql.Extensions;
static class FreeSqlExtensions
{
public static void Mapper(this IFreeSql freeSql)
{
freeSql.CodeFirst.ConfigEntity<Navigation>(i =>
{
i.Name("Navigations");
i.Property(n => n.HasChildren).IsIgnore(true);
});
freeSql.CodeFirst.ConfigEntity<User>(i =>
{
i.Name("Users");
i.Property(n => n.NewPassword).IsIgnore(true);
i.Property(n => n.ConfirmPassword).IsIgnore(true);
i.Property(n => n.Period).IsIgnore(true);
i.Property(n => n.IsReset).IsIgnore(true);
});
freeSql.CodeFirst.ConfigEntity<Group>(i =>
{
i.Name("Groups");
});
freeSql.CodeFirst.ConfigEntity<Role>(i =>
{
i.Name("Roles");
});
freeSql.CodeFirst.ConfigEntity<Error>(i =>
{
i.Name("Exceptions");
});
freeSql.CodeFirst.ConfigEntity<UserRole>(i =>
{
i.Name("UserRole");
i.Property(s => s.ID).IsIgnore(true);
});
freeSql.CodeFirst.ConfigEntity<NavigationRole>(i =>
{
i.Name("NavigationRole");
i.Property(s => s.ID).IsIgnore(true);
});
freeSql.CodeFirst.ConfigEntity<UserGroup>(i =>
{
i.Name("UserGroup");
i.Property(s => s.ID).IsIgnore(true);
});
freeSql.CodeFirst.ConfigEntity<RoleGroup>(i =>
{
i.Name("RoleGroup");
i.Property(s => s.ID).IsIgnore(true);
});
}
}

View File

@ -0,0 +1,46 @@
using BootStarpAdmin.DataAccess.FreeSql.Extensions;
using BootStarpAdmin.DataAccess.FreeSql.Service;
using BootstrapAdmin.Web.Core;
using BootstrapBlazor.Components;
using FreeSql;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// FreeSql ORM 注入服务扩展类
/// </summary>
public static class ServicesExtensions
{
/// <summary>
/// 注入 FreeSql 数据服务类
/// </summary>
/// <param name="services"></param>
/// <param name="freeSqlBuilder"></param>
/// <returns></returns>
public static IServiceCollection AddFreeSql(this IServiceCollection services, Action<IServiceProvider, FreeSqlBuilder> freeSqlBuilder)
{
services.TryAddSingleton<IFreeSql>(provider =>
{
var builder = new FreeSqlBuilder();
freeSqlBuilder(provider, builder);
var instance = builder.Build();
instance.Mapper();
return instance;
});
// 增加数据服务
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,10 @@
namespace BootStarpAdmin.DataAccess.FreeSql.Models;
class NavigationRole
{
public string? ID { get; set; }
public string? NavigationID { get; set; }
public string? RoleID { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace BootStarpAdmin.DataAccess.FreeSql.Models;
class RoleApp
{
public string? RoleID { get; set; }
public string? AppID { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace BootStarpAdmin.DataAccess.FreeSql.Models;
class RoleGroup
{
public string? ID { get; set; }
public string? RoleID { get; set; }
public string? GroupID { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace BootStarpAdmin.DataAccess.FreeSql.Models;
class UserGroup
{
public string? ID { get; set; }
public string? UserID { get; set; }
public string? GroupID { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace BootStarpAdmin.DataAccess.FreeSql.Models;
class UserRole
{
public string? ID { get; set; }
public string? UserID { get; set; }
public string? RoleID { get; set; }
}

View File

@ -0,0 +1,32 @@
using BootStarpAdmin.DataAccess.FreeSql.Models;
using BootstrapAdmin.Web.Core;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class AppService : IApp
{
private IFreeSql FreeSql { get; }
public AppService(IFreeSql freeSql) => FreeSql = freeSql;
public List<string> GetAppsByRoleId(string? roleId) => FreeSql.Ado.Query<string>("select AppID from RoleApp where RoleID = @roleId", new { roleId });
public bool SaveAppsByRoleId(string? roleId, IEnumerable<string> appIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from RoleApp where RoleID = @roleId", new { roleId });
FreeSql.Insert(appIds.Select(g => new RoleApp { AppID = g, RoleID = roleId })).ExecuteAffrows();
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
}

View File

@ -0,0 +1,64 @@
using BootStarpAdmin.DataAccess.FreeSql.Extensions;
using BootstrapBlazor.Components;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class DefaultDataService<TModel> : DataServiceBase<TModel> where TModel : class, new()
{
private IFreeSql FreeSql { get; }
public DefaultDataService(IFreeSql freeSql) => FreeSql = freeSql;
/// <summary>
/// 删除方法
/// </summary>
/// <param name="models"></param>
/// <returns></returns>
public override async Task<bool> DeleteAsync(IEnumerable<TModel> models)
{
await FreeSql.Delete<TModel>(models).ExecuteAffrowsAsync();
return true;
}
/// <summary>
/// 保存方法
/// </summary>
/// <param name="model"></param>
/// <param name="changedType"></param>
/// <returns></returns>
public override async Task<bool> SaveAsync(TModel model, ItemChangedType changedType)
{
if (changedType == ItemChangedType.Add)
{
await FreeSql.Insert<TModel>(model).ExecuteAffrowsAsync();
}
else if (changedType == ItemChangedType.Update)
{
await FreeSql.Update<TModel>(model).ExecuteAffrowsAsync();
}
return true;
}
public override Task<QueryData<TModel>> QueryAsync(QueryPageOptions option)
{
var ret = new QueryData<TModel>()
{
IsSorted = true,
IsFiltered = true,
IsSearch = true,
IsAdvanceSearch = option.AdvanceSearchs.Any() || option.CustomerSearchs.Any()
};
ret.Items = FreeSql.Select<TModel>()
.WhereDynamicFilter(option.Searchs.ToDynamicFilter())
.WhereDynamicFilter(option.Filters
.Concat(option.AdvanceSearchs)
.Concat(option.CustomerSearchs)
.ToDynamicFilter())
.OrderByPropertyNameIf(option.SortOrder != SortOrder.Unset, option.SortName, option.SortOrder == SortOrder.Asc)
.Count(out var count)
.PageIf(option.PageIndex, option.PageItems, option.IsPage)
.ToList();
ret.TotalCount = Convert.ToInt32(count);
return Task.FromResult(ret);
}
}

View File

@ -0,0 +1,156 @@
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.Web.Core;
using Longbow.Security.Cryptography;
using Microsoft.Extensions.Configuration;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class DictService : IDict
{
private IFreeSql FreeSql { get; }
private string? AppId { get; set; }
public DictService(IFreeSql freeSql, IConfiguration configuration)
{
FreeSql = freeSql;
AppId = configuration.GetValue("AppId", "BA");
}
public bool AuthenticateDemo(string code)
{
var ret = false;
if (!string.IsNullOrEmpty(code))
{
var dicts = GetAll();
var salt = dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "授权盐值" && d.Define == EnumDictDefine.System)?.Code;
var authCode = dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "哈希结果" && d.Define == EnumDictDefine.System)?.Code;
if (!string.IsNullOrEmpty(salt))
{
ret = LgbCryptography.ComputeHash(code, salt) == authCode;
}
}
return ret;
}
public List<Dict> GetAll()
{
return FreeSql.Select<Dict>().ToList();
}
public Dictionary<string, string> GetApps()
{
var dicts = GetAll();
return dicts.Where(d => d.Category == "应用程序").Select(s => new KeyValuePair<string, string>(s.Code, s.Name)).ToDictionary(i => i.Key, i => i.Value);
}
public int GetCookieExpiresPeriod()
{
var dicts = GetAll();
var code = dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "Cookie保留时长" && d.Define == EnumDictDefine.System)?.Code ?? "0";
_ = int.TryParse(code, out var ret);
return ret;
}
public string GetCurrentLogin()
{
var dicts = GetAll();
return dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "登录界面" && d.Define == EnumDictDefine.System)?.Code ?? "Login";
}
public Dictionary<string, string> GetLogins()
{
var dicts = GetAll();
return dicts.Where(d => d.Category == "系统首页").Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).OrderBy(i => i.Value).ToDictionary(i => i.Key, i => i.Value);
}
public string? GetNotificationUrl(string appId) => GetUrlByName(appId, "系统通知地址");
public string? GetProfileUrl(string appId) => GetUrlByName(appId, "个人中心地址");
public string? GetSettingsUrl(string appId) => GetUrlByName(appId, "系统设置地址");
public Dictionary<string, string> GetThemes()
{
var dicts = GetAll();
return dicts.Where(d => d.Category == "网站样式").Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).ToDictionary(i => i.Key, i => i.Value);
}
public string GetWebFooter()
{
var dicts = GetAll();
var title = "网站页脚";
var name = dicts.FirstOrDefault(d => d.Category == "应用程序" && d.Code == AppId)?.Name;
if (!string.IsNullOrEmpty(name))
{
var dict = dicts.FirstOrDefault(d => d.Category == name && d.Name == "网站页脚") ?? dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "网站页脚");
title = dict?.Code ?? "网站标题";
}
return title;
}
public string GetWebTitle()
{
var dicts = GetAll();
var title = "网站标题";
var name = dicts.FirstOrDefault(d => d.Category == "应用程序" && d.Code == AppId)?.Name;
if (!string.IsNullOrEmpty(name))
{
var dict = dicts.FirstOrDefault(d => d.Category == name && d.Name == "网站标题") ?? dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "网站标题");
title = dict?.Code ?? "网站标题";
}
return title;
}
public bool IsDemo()
{
var dicts = GetAll();
var code = dicts.FirstOrDefault(d => d.Category == "网站设置" && d.Name == "演示系统" && d.Define == EnumDictDefine.System)?.Code ?? "0";
return code == "1";
}
public string RetrieveIconFolderPath()
{
var dicts = GetAll();
return dicts.FirstOrDefault(d => d.Name == "头像路径" && d.Category == "头像地址" && d.Define == EnumDictDefine.System)?.Code ?? "images/uploder/";
}
private bool SaveDict(Dict dict) => FreeSql.Update<Dict>().Where(s => s.Category == dict.Category && s.Name == dict.Code).Set(s => s.Code, dict.Code).ExecuteAffrows() > 0;
public bool SaveCookieExpiresPeriod(int expiresPeriod) => SaveDict(new Dict { Category = "网站设置", Name = "Cookie保留时长", Code = expiresPeriod.ToString() });
public bool SaveDemo(bool isDemo)
{
return FreeSql.Update<Dict>()
.Where(s => s.Category == "网站设置" && s.Name == "演示系统" && s.Define == EnumDictDefine.System)
.Set(s => s.Code, isDemo ? "1" : "0").ExecuteAffrows() > 0;
}
public bool SaveHealthCheck(bool enable = true)
{
return FreeSql.Update<Dict>()
.Where(s => s.Category == "网站设置" && s.Name == "健康检查" && s.Define == EnumDictDefine.System)
.Set(s => s.Code, enable ? "1" : "0").ExecuteAffrows() > 0;
}
public bool SaveLogin(string login) => SaveDict(new Dict { Category = "网站设置", Name = "登录界面", Code = login });
public bool SaveTheme(string theme) => SaveDict(new Dict { Category = "网站设置", Name = "使用样式", Code = theme });
public bool SaveWebFooter(string footer) => SaveDict(new Dict { Category = "网站设置", Name = "网站页脚", Code = footer });
public bool SaveWebTitle(string title) => SaveDict(new Dict { Category = "网站设置", Name = "网站标题", Code = title });
private string? GetUrlByName(string appId, string dictName)
{
string? url = null;
var dicts = GetAll();
var appName = dicts.FirstOrDefault(d => d.Category == "应用程序" && d.Code == appId && d.Define == EnumDictDefine.System)?.Name;
if (!string.IsNullOrEmpty(appName))
{
url = dicts.FirstOrDefault(d => d.Category == appName && d.Name == dictName && d.Define == EnumDictDefine.Customer)?.Code;
}
return url;
}
}

View File

@ -0,0 +1,60 @@
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.Web.Core;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class ExceptionService : IException
{
private IFreeSql FreeSql { get; }
public ExceptionService(IFreeSql freeSql) => FreeSql = freeSql;
public (IEnumerable<Error> Items, int ItemsCount) GetAll(string? searchText, ExceptionFilter filter, int pageIndex, int pageItems, List<string> sortList)
{
var items = FreeSql.Select<Error>();
if (!string.IsNullOrEmpty(searchText))
{
items.Where($"ErrorPage Like %@searchText% or Message Like %@searchText% or StackTrace Like %@searchText%", new { searchText });
}
if (!string.IsNullOrEmpty(filter.Category))
{
items.Where("Category = @Category", new { filter.Category });
}
if (!string.IsNullOrEmpty(filter.UserId))
{
items.Where("UserId Like %@UserId%", new { filter.UserId });
}
if (!string.IsNullOrEmpty(filter.ErrorPage))
{
items.Where("ErrorPage Like %{ErrorPage}%", new { filter.ErrorPage });
}
items.Where("LogTime >= @Star and LogTime <= @End", new { filter.Star, filter.End });
if (sortList.Any())
{
items.OrderBy(string.Join(", ", sortList));
}
else
{
items.OrderBy("UserId, ErrorPage, Logtime desc");
}
var errors = items.Count(out var count).Page(pageIndex, pageItems).ToList();
return (errors, Convert.ToInt32(count));
}
public bool Log(Error exception)
{
try
{
FreeSql.Insert(exception).ExecuteAffrows();
}
catch { }
return true;
}
}

View File

@ -0,0 +1,56 @@
using BootStarpAdmin.DataAccess.FreeSql.Models;
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.Web.Core;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class GroupService : IGroup
{
private IFreeSql FreeSql { get; }
public GroupService(IFreeSql freeSql) => FreeSql = freeSql;
public List<Group> GetAll() => FreeSql.Select<Group>().ToList();
public List<string> GetGroupsByRoleId(string? roleId) => FreeSql.Ado.Query<string>("select GroupID from RoleGroup where RoleID = @roleId", new { roleId });
public List<string> GetGroupsByUserId(string? userId) => FreeSql.Ado.Query<string>("select GroupID from UserGroup where UserID = @userId", new { userId });
public bool SaveGroupsByRoleId(string? roleId, IEnumerable<string> groupIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from RoleGroup where RoleID = @roleId", new { roleId });
FreeSql.Insert(groupIds.Select(g => new RoleGroup { GroupID = g, RoleID = roleId })).ExecuteAffrows();
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
public bool SaveGroupsByUserId(string? userId, IEnumerable<string> groupIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from UserGroup where UserID = @userId", new { userId });
FreeSql.Insert(groupIds.Select(g => new UserGroup { GroupID = g, UserID = userId }));
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
}

View File

@ -0,0 +1,11 @@
using BootstrapAdmin.Web.Core;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class LoginService : ILogin
{
public Task<bool> Log(string userName, bool result)
{
return Task.FromResult(true);
}
}

View File

@ -0,0 +1,38 @@
using BootStarpAdmin.DataAccess.FreeSql.Models;
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.Web.Core;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class NavigationService : INavigation
{
private IFreeSql FreeSql { get; }
public NavigationService(IFreeSql freeSql) => FreeSql = freeSql;
public List<Navigation> GetAllMenus(string userName)
{
return FreeSql.Ado.Query<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 = EnumDictDefine.System });
}
public List<string> GetMenusByRoleId(string? roleId) => FreeSql.Ado.Query<string>("select NavigationID from NavigationRole where RoleID = @roleId", new { roleId });
public bool SaveMenusByRoleId(string? roleId, List<string> menuIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from NavigationRole where RoleID = @roleId", new { roleId });
FreeSql.Insert(menuIds.Select(g => new NavigationRole { NavigationID = g, RoleID = roleId })).ExecuteAffrows();
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
}

View File

@ -0,0 +1,80 @@
using BootStarpAdmin.DataAccess.FreeSql.Models;
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.Web.Core;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class RoleService : IRole
{
private IFreeSql FreeSql { get; }
public RoleService(IFreeSql freeSql) => FreeSql = freeSql;
public List<Role> GetAll()
{
return FreeSql.Select<Role>().ToList();
}
public List<string> GetRolesByGroupId(string? groupId) => FreeSql.Ado.Query<string>("select RoleID from RoleGroup where GroupID = @groupId", new { groupId });
public List<string> GetRolesByMenuId(string? menuId) => FreeSql.Ado.Query<string>("select RoleID from NavigationRole where NavigationID = @menuId", new { menuId });
public List<string> GetRolesByUserId(string? userId) => FreeSql.Ado.Query<string>("select RoleID from UserRole where UserID = @userId", new { userId });
public bool SaveRolesByGroupId(string? groupId, IEnumerable<string> roleIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from RoleGroup where GroupID = @groupId", new { groupId });
FreeSql.Insert(roleIds.Select(g => new RoleGroup { RoleID = g, GroupID = groupId })).ExecuteAffrows();
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
public bool SaveRolesByMenuId(string? menuId, IEnumerable<string> roleIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from NavigationRole where NavigationID = @menuId", new { menuId });
FreeSql.Insert(roleIds.Select(g => new NavigationRole { RoleID = g, NavigationID = menuId })).ExecuteAffrows();
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
public bool SaveRolesByUserId(string? userId, IEnumerable<string> roleIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from UserRole where UserID = @userId", new { userId });
FreeSql.Insert(roleIds.Select(g => new UserRole { RoleID = g, UserID = userId })).ExecuteAffrows();
});
ret = true;
}
catch (Exception)
{
throw;
}
return ret;
}
}

View File

@ -0,0 +1,194 @@
using BootStarpAdmin.DataAccess.FreeSql.Models;
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.Web.Core;
using Longbow.Security.Cryptography;
namespace BootStarpAdmin.DataAccess.FreeSql.Service;
class UserService : IUser
{
private IFreeSql FreeSql { get; }
public UserService(IFreeSql freeSql) => FreeSql = freeSql;
public bool Authenticate(string userName, string password)
{
var user = FreeSql.Select<User>().Where(s => s.ApprovedTime != null && s.UserName == userName).ToOne(s => new User
{
DisplayName = s.DisplayName,
PassSalt = s.PassSalt,
Password = s.Password
});
var isAuth = false;
if (user != null && !string.IsNullOrEmpty(user.PassSalt))
{
isAuth = user.Password == LgbCryptography.ComputeHash(password, user.PassSalt);
}
return isAuth;
}
public List<User> GetAll()
{
return FreeSql.Select<User>().ToList();
}
public List<string> GetApps(string userName)
{
return FreeSql.Ado.Query<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 = @UserName union select r.Id from Roles r inner join RoleGroup rg on r.ID = rg.RoleID inner join 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 = @UserName) r on ra.RoleId = r.ID union select Code from Dicts where Category = @Category 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 = @UserName and r.RoleName = @RoleName union select r.ID from Roles r inner join RoleGroup rg on r.ID = rg.RoleID inner join 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 = @UserName and r.RoleName = @RoleName)", new { UserName = userName, Category = "应用程序", RoleName = "Administrators" }).ToList();
}
public string? GetDisplayName(string? userName)
{
return FreeSql.Select<User>().Where(s => s.UserName == userName).ToOne(s => s.DisplayName);
}
public List<string> GetRoles(string userName)
{
return FreeSql.Ado.Query<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 = @userName union select r.RoleName from Roles r inner join RoleGroup rg on r.ID = rg.RoleID inner join 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 = @userName", new { userName }).ToList();
}
public User? GetUserByUserName(string? userName) => string.IsNullOrEmpty(userName) ? null : FreeSql.Select<User>().Where(i => i.UserName == userName).ToOne();
public List<string> GetUsersByGroupId(string? groupId)
{
return FreeSql.Ado.Query<string>("select UserID from UserGroup where GroupID = @groupId", new { groupId }).ToList();
}
public List<string> GetUsersByRoleId(string? roleId)
{
return FreeSql.Ado.Query<string>("select UserID from UserRole where RoleID = @roleId", new { roleId }).ToList();
}
public bool SaveUser(string userName, string displayName, string password)
{
var salt = LgbCryptography.GenerateSalt();
var pwd = LgbCryptography.ComputeHash(password, salt);
var user = FreeSql.Select<User>().Where(s => s.UserName == userName).ToOne();
bool ret = default;
if (user == null)
{
// 开始事务
FreeSql.Transaction(() =>
{
user = new User()
{
Id = "0",
ApprovedBy = "System",
ApprovedTime = DateTime.Now,
DisplayName = "手机用户",
UserName = userName,
Icon = "default.jpg",
Description = "系统默认创建",
PassSalt = salt,
Password = pwd
};
FreeSql.Insert(user).ExecuteAffrows();
// 授权 Default 角色
FreeSql.Ado.ExecuteNonQuery("insert into UserRole (UserID, RoleID) select ID, (select ID from Roles where RoleName = 'Default') RoleId from Users where UserName = @userName", new { userName });
ret = true;
});
}
else
{
user.DisplayName = displayName;
user.PassSalt = salt;
user.Password = pwd;
FreeSql.Update<User>(user);
ret = true;
}
return ret;
}
public bool SaveUsersByGroupId(string? groupId, IEnumerable<string> userIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from UserGroup where GroupId = @groupId", new { groupId });
FreeSql.Insert(userIds.Select(g => new UserGroup { UserID = g, GroupID = groupId })).ExecuteAffrows();
});
ret = true;
}
catch (Exception)
{
throw;
}
return ret;
}
public bool SaveUsersByRoleId(string? roleId, IEnumerable<string> userIds)
{
var ret = false;
try
{
FreeSql.Transaction(() =>
{
FreeSql.Ado.ExecuteNonQuery("delete from UserRole where RoleID = @roleId", new { roleId });
FreeSql.Insert(userIds.Select(g => new UserRole { UserID = g, RoleID = roleId })).ExecuteAffrows();
ret = true;
});
}
catch (Exception)
{
throw;
}
return ret;
}
/// <summary>
///
/// </summary>
/// <param name="phone"></param>
/// <param name="code"></param>
/// <param name="appId"></param>
/// <param name="roles"></param>
/// <returns></returns>
public bool TryCreateUserByPhone(string phone, string code, string appId, ICollection<string> roles)
{
var ret = false;
try
{
var salt = LgbCryptography.GenerateSalt();
var pwd = LgbCryptography.ComputeHash(code, salt);
var user = FreeSql.Select<User>().Where(s => s.UserName == phone).ToOne();
if (user == null)
{
FreeSql.Transaction(() =>
{
user = new User()
{
ApprovedBy = "Mobile",
ApprovedTime = DateTime.Now,
DisplayName = "手机用户",
UserName = phone,
Icon = "default.jpg",
Description = "手机用户",
PassSalt = salt,
Password = LgbCryptography.ComputeHash(code, salt),
App = appId
};
FreeSql.Insert(user).ExecuteAffrows();
// Authorization
var roleIds = FreeSql.Ado.Query<string>("select ID from Roles where RoleName in (@roles)", new { roles });
FreeSql.Insert(roleIds.Select(g => new UserRole { RoleID = g, UserID = user.Id }));
});
}
else
{
user.PassSalt = salt;
user.Password = pwd;
FreeSql.Update<User>(user).ExecuteAffrows();
}
ret = true;
}
catch (Exception)
{
throw;
}
return ret;
}
}

View File

@ -1,4 +1,5 @@
using BootstrapAdmin.DataAccess.Models;
using BootstrapAdmin.DataAccess.EFCore.Models;
using BootstrapAdmin.DataAccess.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

View File

@ -114,4 +114,49 @@ class DictService : IDict
_ = int.TryParse(code, out var ret);
return ret;
}
public string GetCurrentLogin()
{
throw new NotImplementedException();
}
public bool SaveLogin(string login)
{
throw new NotImplementedException();
}
public bool SaveTheme(string theme)
{
throw new NotImplementedException();
}
public bool SaveWebTitle(string title)
{
throw new NotImplementedException();
}
public bool SaveWebFooter(string footer)
{
throw new NotImplementedException();
}
public bool SaveCookieExpiresPeriod(int expiresPeriod)
{
throw new NotImplementedException();
}
public string? GetProfileUrl(string appId)
{
throw new NotImplementedException();
}
public string? GetSettingsUrl(string appId)
{
throw new NotImplementedException();
}
public string? GetNotificationUrl(string appId)
{
throw new NotImplementedException();
}
}

View File

@ -136,4 +136,14 @@ public class UserService : IUser
}
return ret;
}
List<User> IUser.GetAll()
{
throw new NotImplementedException();
}
public bool TryCreateUserByPhone(string phone, string code, string appId, ICollection<string> roles)
{
throw new NotImplementedException();
}
}

View File

@ -2,6 +2,9 @@
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
///
/// </summary>
public class AppInfo
{
/// <summary>

View File

@ -1,33 +1,32 @@
using System.ComponentModel;
namespace BootstrapAdmin.DataAccess.Models
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
/// 后台数据库脚本执行日志实体类
/// </summary>
public class DBLog
{
/// <summary>
/// 后台数据库脚本执行日志实体类
/// 获得/设置 主键ID
/// </summary>
public class DBLog
{
/// <summary>
/// 获得/设置 主键ID
/// </summary>
public string? Id { get; set; }
public string? Id { get; set; }
/// <summary>
/// 获得/设置 当前登陆名
/// </summary>
[DisplayName("所属用户")]
public string? UserName { get; set; }
/// <summary>
/// 获得/设置 当前登陆名
/// </summary>
[DisplayName("所属用户")]
public string? UserName { get; set; }
/// <summary>
/// 获得/设置 数据库执行脚本
/// </summary>
[DisplayName("脚本内容")]
public string SQL { get; set; } = "";
/// <summary>
/// 获得/设置 数据库执行脚本
/// </summary>
[DisplayName("脚本内容")]
public string SQL { get; set; } = "";
/// <summary>
/// 获取/设置 用户角色关联状态 checked 标示已经关联 '' 标示未关联
/// </summary>
[DisplayName("执行时间")]
public DateTime LogTime { get; set; }
}
/// <summary>
/// 获取/设置 用户角色关联状态 checked 标示已经关联 '' 标示未关联
/// </summary>
[DisplayName("执行时间")]
public DateTime LogTime { get; set; }
}

View File

@ -1,12 +1,14 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
/// 字典配置项
/// </summary>
[Table("Dicts")]
public class Dict
{
/// <summary>

View File

@ -12,6 +12,9 @@ public class Error
/// </summary>
public string? Id { get; set; }
/// <summary>
///
/// </summary>
[DisplayName("应用程序")]
public string? AppDomainName { get; set; }

View File

@ -1,37 +1,40 @@
using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.DataAccess.Models
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
/// Group 实体类
/// </summary>
public class Group
{
/// <summary>
/// Group 实体类
/// 获得/设置 主键 ID
/// </summary>
public class Group
{
/// <summary>
/// 获得/设置 主键 ID
/// </summary>
public string? Id { get; set; }
public string? Id { get; set; }
/// <summary>
/// 获得/设置 群组名称
/// </summary>
[Display(Name = "群组名称")]
[NotNull]
public string? GroupName { get; set; }
/// <summary>
/// 获得/设置 群组名称
/// </summary>
[Display(Name = "群组名称")]
[NotNull]
public string? GroupName { get; set; }
/// <summary>
/// 获得/设置 群组编码
/// </summary>
[Display(Name = "群组编码")]
[NotNull]
public string? GroupCode { get; set; }
/// <summary>
/// 获得/设置 群组编码
/// </summary>
[Display(Name = "群组编码")]
[NotNull]
public string? GroupCode { get; set; }
/// <summary>
/// 获得/设置 群组描述
/// </summary>
[Display(Name = "群组描述")]
public string? Description { get; set; }
/// <summary>
/// 获得/设置 群组描述
/// </summary>
[Display(Name = "群组描述")]
public string? Description { get; set; }
public override string ToString() => $"{GroupName} ({GroupCode})";
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override string ToString() => $"{GroupName} ({GroupCode})";
}

View File

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

View File

@ -1,30 +1,29 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.DataAccess.Models
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
/// Role 实体类
/// </summary>
public class Role
{
/// <summary>
/// Role 实体类
/// 获得/设置 角色主键ID
/// </summary>
public class Role
{
/// <summary>
/// 获得/设置 角色主键ID
/// </summary>
public string? Id { get; set; }
public string? Id { get; set; }
/// <summary>
/// 获得/设置 角色名称
/// </summary>
[DisplayName("角色名称")]
[NotNull]
public string? RoleName { get; set; }
/// <summary>
/// 获得/设置 角色名称
/// </summary>
[DisplayName("角色名称")]
[NotNull]
public string? RoleName { get; set; }
/// <summary>
/// 获得/设置 角色描述
/// </summary>
[DisplayName("角色描述")]
[NotNull]
public string? Description { get; set; }
}
/// <summary>
/// 获得/设置 角色描述
/// </summary>
[DisplayName("角色描述")]
[NotNull]
public string? Description { get; set; }
}

View File

@ -1,162 +1,161 @@
using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.DataAccess.Models
namespace BootstrapAdmin.DataAccess.Models;
/// <summary>
///
/// </summary>
public class User
{
/// <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})";
}
[Display(Name = "登录名称")]
[Required(ErrorMessage = "{0}不可为空")]
[RegularExpression("^[a-zA-Z0-9_@.]*$", ErrorMessage = "")]
[MaxLength(16, ErrorMessage = "{0}不能超过 16 个字符")]
[NotNull]
public string? UserName { get; set; }
/// <summary>
/// 用户状态枚举类型
/// 获得/设置 用户显示名称
/// </summary>
public enum UserStates
{
/// <summary>
/// 更改密码
/// </summary>
ChangePassword,
[Display(Name = "显示名称")]
[Required(ErrorMessage = "{0}不可为空")]
[MaxLength(20, ErrorMessage = "{0}不能超过 20 个字符")]
[NotNull]
public string? DisplayName { get; set; }
/// <summary>
/// 更改样式
/// </summary>
ChangeTheme,
/// <summary>
/// 获得/设置 用户头像图标路径
/// </summary>
[Display(Name = "用户头像")]
public string? Icon { get; set; }
/// <summary>
/// 更改显示名称
/// </summary>
ChangeDisplayName,
/// <summary>
/// 获得/设置 用户设置样式表名称
/// </summary>
[Display(Name = "主题")]
public string? Css { get; set; }
/// <summary>
/// 审批用户
/// </summary>
ApproveUser,
/// <summary>
/// 获得/设置 用户默认登陆 App 标识
/// </summary>
[Display(Name = "默认 APP")]
public string? App { get; set; }
/// <summary>
/// 拒绝用户
/// </summary>
RejectUser,
/// <summary>
/// 获得/设置 用户主键ID
/// </summary>
public string? Id { get; set; }
/// <summary>
/// 保存默认应用
/// </summary>
SaveApp
}
/// <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

@ -3,7 +3,7 @@
/// <summary>
/// 字符串转枚举转换器
/// </summary>
public class StringToEnumConverter
class StringToEnumConverter
{
private Type TargetType { get; set; }
@ -21,7 +21,10 @@ public class StringToEnumConverter
/// <exception cref="NotImplementedException"></exception>
public object? ConvertFromDb(object? value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
object? ret;
if (value != null && Enum.TryParse(TargetType, value.ToString(), true, out var v))

View File

@ -25,6 +25,7 @@ public static class ServicesExtensions
///
/// </summary>
/// <param name="services"></param>
/// <param name="builder"></param>
/// <returns></returns>
public static IServiceCollection AddPetaPocoDataAccessServices(this IServiceCollection services, Action<IServiceProvider, IDatabaseBuildConfiguration> builder)
{
@ -66,14 +67,14 @@ public static class ServicesExtensions
services.AddSingleton(typeof(IDataService<>), typeof(DefaultDataService<>));
// 增加业务服务
services.AddSingleton<INavigation, NavigationService>();
services.AddSingleton<IDict, DictService>();
services.AddSingleton<IUser, UserService>();
services.AddSingleton<ILogin, LoginService>();
services.AddSingleton<IRole, RoleService>();
services.AddSingleton<IGroup, GroupService>();
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

@ -131,6 +131,7 @@ class UserService : BaseDatabase, IUser
/// 创建手机用户
/// </summary>
/// <param name="phone"></param>
/// <param name="code"></param>
/// <param name="appId"></param>
/// <param name="roles"></param>
/// <returns></returns>

View File

@ -1,14 +1,32 @@
namespace BootstrapAdmin.Web.Core;
/// <summary>
///
/// </summary>
public class ExceptionFilter
{
/// <summary>
///
/// </summary>
public DateTime Star { get; set; }
/// <summary>
///
/// </summary>
public DateTime End { get; set; }
/// <summary>
///
/// </summary>
public string? UserId { get; set; }
/// <summary>
///
/// </summary>
public string? ErrorPage { get; set; }
/// <summary>
///
/// </summary>
public string? Category { get; set; }
}

View File

@ -1,123 +1,145 @@
using BootstrapAdmin.DataAccess.Models;
namespace BootstrapAdmin.Web.Core
namespace BootstrapAdmin.Web.Core;
/// <summary>
/// Dict 字典表接口
/// </summary>
public interface IDict
{
/// <summary>
/// Dict 字典表接口
///
/// </summary>
public interface IDict
{
/// <returns></returns>
List<Dict> GetAll();
/// <summary>
///
/// </summary>
/// <returns></returns>
List<Dict> GetAll();
/// <summary>
/// 获得 配置所有的 App 集合
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetApps();
/// <summary>
/// 获得 配置所有的 App 集合
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetApps();
/// <summary>
/// 获得 配置所有的登录页集合
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetLogins();
/// <summary>
/// 获得 配置所有的登录页集合
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetLogins();
/// <summary>
/// 获得 当前配置登录页
/// </summary>
/// <returns></returns>
string GetCurrentLogin();
/// <summary>
/// 获得 当前配置登录页
/// </summary>
/// <returns></returns>
string GetCurrentLogin();
/// <summary>
///
/// </summary>
/// <param name="login"></param>
/// <returns></returns>
bool SaveLogin(string login);
bool SaveLogin(string login);
/// <summary>
/// 获得 配置所有的主题集合
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetThemes();
/// <summary>
/// 获得 配置所有的主题集合
/// </summary>
/// <returns></returns>
Dictionary<string, string> GetThemes();
/// <summary>
///
/// </summary>
/// <param name="theme"></param>
/// <returns></returns>
bool SaveTheme(string theme);
/// <summary>
///
/// </summary>
/// <param name="theme"></param>
/// <returns></returns>
bool SaveTheme(string theme);
/// <summary>
/// 获取当前系统配置是否为演示模式
/// </summary>
/// <returns></returns>
bool IsDemo();
/// <summary>
/// 获取当前系统配置是否为演示模式
/// </summary>
/// <returns></returns>
bool IsDemo();
/// <summary>
/// 保存当前网站是否为演示系统
/// </summary>
/// <param name="isDemo"></param>
/// <returns></returns>
bool SaveDemo(bool isDemo);
/// <summary>
/// 保存当前网站是否为演示系统
/// </summary>
/// <param name="isDemo"></param>
/// <returns></returns>
bool SaveDemo(bool isDemo);
/// <summary>
/// 保存健康检查
/// </summary>
/// <returns></returns>
bool SaveHealthCheck(bool enable = true);
/// <summary>
/// 保存健康检查
/// </summary>
/// <returns></returns>
bool SaveHealthCheck(bool enable = true);
/// <summary>
/// 获得当前授权码是否有效可更改网站设置
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
bool AuthenticateDemo(string code);
/// <summary>
/// 获得当前授权码是否有效可更改网站设置
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
bool AuthenticateDemo(string code);
/// <summary>
/// 获取 站点 Title 配置信息
/// </summary>
/// <returns></returns>
string GetWebTitle();
/// <summary>
/// 获取 站点 Title 配置信息
/// </summary>
/// <returns></returns>
string GetWebTitle();
/// <summary>
///
/// </summary>
/// <param name="title"></param>
/// <returns></returns>
bool SaveWebTitle(string title);
/// <summary>
///
/// </summary>
/// <param name="title"></param>
/// <returns></returns>
bool SaveWebTitle(string title);
/// <summary>
/// 获取站点 Footer 配置信息
/// </summary>
/// <returns></returns>
string GetWebFooter();
/// <summary>
/// 获取站点 Footer 配置信息
/// </summary>
/// <returns></returns>
string GetWebFooter();
/// <summary>
///
/// </summary>
/// <param name="footer"></param>
/// <returns></returns>
bool SaveWebFooter(string footer);
/// <summary>
///
/// </summary>
/// <param name="footer"></param>
/// <returns></returns>
bool SaveWebFooter(string footer);
/// <summary>
/// 获得 Cookie 登录持久化时长
/// </summary>
/// <returns></returns>
int GetCookieExpiresPeriod();
/// <summary>
/// 获得 Cookie 登录持久化时长
/// </summary>
/// <returns></returns>
int GetCookieExpiresPeriod();
/// <summary>
///
/// </summary>
/// <param name="expiresPeriod"></param>
/// <returns></returns>
bool SaveCookieExpiresPeriod(int expiresPeriod);
/// <summary>
///
/// </summary>
/// <param name="expiresPeriod"></param>
/// <returns></returns>
bool SaveCookieExpiresPeriod(int expiresPeriod);
/// <summary>
///
/// </summary>
/// <param name="appId"></param>
/// <returns></returns>
string? GetProfileUrl(string appId);
string? GetProfileUrl(string appId);
/// <summary>
///
/// </summary>
/// <param name="appId"></param>
/// <returns></returns>
string? GetSettingsUrl(string appId);
string? GetSettingsUrl(string appId);
/// <summary>
///
/// </summary>
/// <param name="appId"></param>
/// <returns></returns>
string? GetNotificationUrl(string appId);
string? GetNotificationUrl(string appId);
string RetrieveIconFolderPath();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
string RetrieveIconFolderPath();
}

View File

@ -2,9 +2,26 @@
namespace BootstrapAdmin.Web.Core;
/// <summary>
///
/// </summary>
public interface IException
{
/// <summary>
///
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
bool Log(Error exception);
/// <summary>
///
/// </summary>
/// <param name="searchText"></param>
/// <param name="filter"></param>
/// <param name="pageIndex"></param>
/// <param name="pageItems"></param>
/// <param name="sortList"></param>
/// <returns></returns>
(IEnumerable<Error> Items, int ItemsCount) GetAll(string? searchText, ExceptionFilter filter, int pageIndex, int pageItems, List<string> sortList);
}

View File

@ -1,30 +1,30 @@
using BootstrapAdmin.DataAccess.Models;
namespace BootstrapAdmin.Web.Core
namespace BootstrapAdmin.Web.Core;
/// <summary>
///
/// </summary>
public interface INavigation
{
/// <summary>
///
/// </summary>
public interface INavigation
{
/// <summary>
///
/// </summary>
/// <returns></returns>
List<Navigation> GetAllMenus(string userName);
/// <returns></returns>
List<Navigation> GetAllMenus(string userName);
/// <summary>
///
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
List<string> GetMenusByRoleId(string? roleId);
/// <summary>
///
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
List<string> GetMenusByRoleId(string? roleId);
/// <summary>
///
/// </summary>
/// <param name="roleId"></param>
/// <returns></returns>
bool SaveMenusByRoleId(string? roleId, List<string> menuIds);
}
/// <summary>
///
/// </summary>
/// <param name="roleId"></param>
/// <param name="menuIds"></param>
/// <returns></returns>
bool SaveMenusByRoleId(string? roleId, List<string> menuIds);
}

View File

@ -76,6 +76,7 @@ public interface IUser
///
/// </summary>
/// <param name="phone"></param>
/// <param name="code"></param>
/// <param name="appId"></param>
/// <param name="roles"></param>
/// <returns></returns>

View File

@ -10,7 +10,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BootstrapAdmin.DataAccess.PetaPoco\BootstrapAdmin.DataAccess.PetaPoco.csproj" />
<ProjectReference Include="..\BootStarpAdmin.DataAccess.FreeSql\BootStarpAdmin.DataAccess.FreeSql.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -2,17 +2,32 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class AdminAlert
{
/// <summary>
///
/// </summary>
[Parameter]
public string? Text { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public Color Color { get; set; } = Color.Danger;
/// <summary>
///
/// </summary>
[Parameter]
public bool IsShow { get; set; } = true;
}

View File

@ -3,6 +3,9 @@ using Microsoft.JSInterop;
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class AdminLogin
{
private string? Title { get; set; }
@ -19,6 +22,9 @@ public partial class AdminLogin
private string? PostUrl { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public string? ReturnUrl { get; set; }

View File

@ -7,7 +7,7 @@
ShowEmpty="true" EmptyText="暂无数据" EmptyImage="images/empty.svg"
UseInjectDataService="true" DataService="DataService" SortList="SortList"
OnQueryAsync="OnQueryAsync!" OnDeleteAsync="OnDeleteAsync!" OnSaveAsync="OnSaveAsync!"
ShowSkeleton="true" ShowLoading="true" ShowSearch="ShowSearch"
ShowSkeleton="true" ShowLoading="ShowLoading" ShowSearch="ShowSearch"
ShowToolbar="ShowToolbar" ShowExtendButtons="ShowExtendButtons"
ShowCardView="true" ShowColumnList="true" ExtendButtonColumnWidth="@ExtendButtonColumnWidth"
CustomerSearchModel="CustomerSearchModel" SelectedRows="SelectedRows"

View File

@ -82,6 +82,12 @@ namespace BootstrapAdmin.Web.Components
[Parameter]
public bool ShowToolbar { get; set; } = true;
/// <summary>
///
/// </summary>
[Parameter]
public bool ShowLoading { get; set; } = false;
/// <summary>
///
/// </summary>

View File

@ -1,5 +1,8 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class Assignment
{
private List<string> InternalValue

View File

@ -1,17 +1,30 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
/// <typeparam name="TItem"></typeparam>
public abstract class AssignmentBase<TItem> : ComponentBase
{
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<TItem>? Items { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]
public List<string>? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]

View File

@ -24,6 +24,9 @@ namespace BootstrapAdmin.Web.Components
[Parameter]
public EventCallback<ErrorSearchModel> ValueChanged { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -2,8 +2,14 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class HealthCheckDetails
{
/// <summary>
///
/// </summary>
[Parameter]
[EditorRequired]
[NotNull]

View File

@ -6,6 +6,9 @@ using BootstrapAdmin.Web.Utils;
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class MenusSearch
{
[NotNull]
@ -20,14 +23,22 @@ public partial class MenusSearch
private List<SelectedItem>? TargetItems { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public MenusSearchModel? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<MenusSearchModel> ValueChanged { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -3,6 +3,9 @@ using BootstrapAdmin.Web.Extensions;
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class NavigationTree
{
[NotNull]

View File

@ -2,6 +2,9 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class SMSLogin : IDisposable
{
private bool IsSendCode { get; set; } = true;
@ -64,6 +67,9 @@ public partial class SMSLogin : IDisposable
}
}
/// <summary>
///
/// </summary>
public void Dispose()
{
Dispose(true);

View File

@ -2,12 +2,21 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class TaskEditor
{
/// <summary>
///
/// </summary>
[Parameter]
[NotNull]
public TasksModel? Value { get; set; }
/// <summary>
///
/// </summary>
[Parameter]
public EventCallback<TasksModel> ValueChanged { get; set; }
@ -16,6 +25,9 @@ public partial class TaskEditor
[NotNull]
private List<SelectedItem>? Items { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -1,11 +1,17 @@
namespace BootstrapAdmin.Web.Components;
/// <summary>
///
/// </summary>
public partial class UserLogin
{
private string? UserName { get; set; }
private string? Password { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -26,6 +26,7 @@ namespace BootstrapAdmin.Web.Controllers
/// <param name="userName">User name.</param>
/// <param name="password">Password.</param>
/// <param name="remember">Remember.</param>
/// <param name="returnUrl"></param>
/// <param name="userService"></param>
/// <param name="dictService"></param>
/// <param name="loginService"></param>

View File

@ -6,6 +6,9 @@ using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
namespace BootstrapAdmin.Web.Controllers.api;
/// <summary>
///
/// </summary>
[Route("api/[controller]")]
[AllowAnonymous]
[ApiController]
@ -14,8 +17,10 @@ public class LoginController : ControllerBase
/// <summary>
/// 登录认证接口
/// </summary>
/// <param name="config"></param>
/// <param name="user"></param>
/// <param name="mobile"></param>
/// <param name="userService"></param>
/// <param name="provider"></param>
/// <returns></returns>
[HttpPost()]
public AuthenticateResult Post(LoginUser user, [FromQuery] bool mobile,
@ -58,17 +63,35 @@ public class LoginController : ControllerBase
return null;
}
/// <summary>
///
/// </summary>
public class AuthenticateResult
{
/// <summary>
///
/// </summary>
public string? Error { get; set; }
/// <summary>
///
/// </summary>
public bool Authenticated { get; set; }
}
/// <summary>
///
/// </summary>
public class LoginUser
{
/// <summary>
///
/// </summary>
public string? UserName { get; set; }
/// <summary>
///
/// </summary>
public string? Password { get; set; }
}
}

View File

@ -1,4 +1,4 @@
namespace Microsoft.AspNetCore.Builder
namespace BootstrapAdmin.Web.Extensions
{
/// <summary>
///

View File

@ -2,7 +2,7 @@
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
namespace BootstrapAdmin.Web.Extensions
{
/// <summary>
/// 邮件日志扩展方法

View File

@ -8,8 +8,14 @@ namespace BootstrapAdmin.Web.Extensions;
/// </summary>
public static class DialogExtensions
{
/// <summary>
///
/// </summary>
public static Task ShowAssignmentDialog(this DialogService dialogService, string title, List<SelectedItem> items, List<string> value, Func<Task<bool>> saveCallback, ToastService? toast) => dialogService.ShowDialog<Assignment, SelectedItem>(title, items, value, saveCallback, toast);
/// <summary>
///
/// </summary>
public static Task ShowNavigationDialog(this DialogService dialogService, string title, List<Navigation> items, List<string> value, Func<Task<bool>> saveCallback, ToastService? toast) => dialogService.ShowDialog<NavigationTree, Navigation>(title, items, value, saveCallback, toast);
private static Task ShowDialog<TBody, TItem>(this DialogService dialogService, string title, List<TItem> items, List<string> value, Func<Task<bool>> saveCallback, ToastService? toast) where TBody : AssignmentBase<TItem> => dialogService.ShowSaveDialog<TBody>(title, async () =>

View File

@ -17,21 +17,21 @@ public static class SelectedItemExtensions
/// <summary>
///
/// </summary>
/// <param name="users"></param>
/// <param name="roles"></param>
/// <returns></returns>
public static List<SelectedItem> ToSelectedItemList(this IEnumerable<Role> roles) => roles.Select(i => new SelectedItem { Value = i.Id!, Text = i.RoleName }).ToList();
/// <summary>
///
/// </summary>
/// <param name="users"></param>
/// <param name="groups"></param>
/// <returns></returns>
public static List<SelectedItem> ToSelectedItemList(this IEnumerable<Group> groups) => groups.Select(i => new SelectedItem { Value = i.Id!, Text = i.ToString() }).ToList();
/// <summary>
///
/// </summary>
/// <param name="users"></param>
/// <param name="dict"></param>
/// <returns></returns>
public static List<SelectedItem> ToSelectedItemList(this Dictionary<string, string> dict) => dict.Select(i => new SelectedItem { Value = i.Key, Text = i.Value }).ToList();
}

View File

@ -1,12 +1,11 @@
using BootstrapAdmin.Web.Core;
using BootstrapAdmin.Web.HealthChecks;
using BootstrapAdmin.Web.Services;
using BootstrapAdmin.Web.Services.SMS;
using BootstrapAdmin.Web.Services.SMS.Tencent;
using BootstrapAdmin.Web.Utils;
using PetaPoco;
using PetaPoco.Providers;
namespace Microsoft.Extensions.DependencyInjection
namespace BootstrapAdmin.Web.Extensions
{
/// <summary>
///
@ -47,14 +46,26 @@ namespace Microsoft.Extensions.DependencyInjection
//});
// 增加 PetaPoco 数据服务
services.AddPetaPocoDataAccessServices((provider, builder) =>
//services.AddPetaPocoDataAccessServices((provider, builder) =>
//{
// var configuration = provider.GetRequiredService<IConfiguration>();
// var connString = configuration.GetConnectionString("bb");
// builder.UsingProvider<SQLiteDatabaseProvider>()
// .UsingConnectionString(connString);
//});
services.AddFreeSql((provider, builder) =>
{
var configuration = provider.GetRequiredService<IConfiguration>();
var connString = configuration.GetConnectionString("bb");
builder.UsingProvider<SQLiteDatabaseProvider>()
.UsingConnectionString(connString);
builder.UseConnectionString(FreeSql.DataType.Sqlite, connString);
#if DEBUG
//调试sql语句输出
builder.UseMonitorCommand(cmd => System.Console.WriteLine(cmd.CommandText));
#endif
});
// 增加后台任务
services.AddTaskServices();
services.AddHostedService<AdminTaskService>();

View File

@ -3,8 +3,14 @@ using Longbow.Tasks;
namespace BootstrapAdmin.Web.Extensions;
/// <summary>
///
/// </summary>
public static class TasksExtensions
{
/// <summary>
///
/// </summary>
public static List<TasksModel> ToTasksModelList(this IEnumerable<IScheduler> schedulers) => schedulers.Select(i => new TasksModel
{
Name = i.Name,

View File

@ -2,8 +2,19 @@
namespace BootstrapAdmin.Web.Extensions;
/// <summary>
///
/// </summary>
public static class TreeItemExtensions
{
/// <summary>
///
/// </summary>
/// <param name="navigations"></param>
/// <param name="selectedItems"></param>
/// <param name="render"></param>
/// <param name="parentId"></param>
/// <returns></returns>
public static List<TreeItem> ToTreeItemList(this IEnumerable<Navigation> navigations, List<string> selectedItems, RenderFragment<Navigation> render, string? parentId = "0")
{
var trees = new List<TreeItem>();

View File

@ -14,6 +14,7 @@ public class GiteeHttpClient
/// 构造函数
/// </summary>
/// <param name="client"></param>
/// <param name="accessor"></param>
public GiteeHttpClient(HttpClient client, IHttpContextAccessor accessor)
{
client.Timeout = TimeSpan.FromSeconds(10);

View File

@ -14,7 +14,6 @@ class GiteeHttpHealthCheck : IHealthCheck
/// 构造函数
/// </summary>
/// <param name="client"></param>
/// <param name="accessor"></param>
public GiteeHttpHealthCheck(GiteeHttpClient client)
{
Client = client;

View File

@ -1,6 +1,4 @@
using BootstrapAdmin.Web.HealthChecks;
namespace Microsoft.Extensions.DependencyInjection;
namespace BootstrapAdmin.Web.HealthChecks;
/// <summary>
/// 健康检查扩展类

View File

@ -3,11 +3,20 @@ using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.Web.Models;
/// <summary>
///
/// </summary>
public class MenusSearchModel : ITableSearchModel
{
/// <summary>
///
/// </summary>
[Display(Name = "名称")]
public string? Name { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "地址")]
public string? Url { get; set; }
@ -35,6 +44,10 @@ public class MenusSearchModel : ITableSearchModel
[Display(Name = "所属应用")]
public string? Application { get; set; }
/// <summary>
///
/// </summary>
/// <returns></returns>
public IEnumerable<IFilterAction> GetSearchs()
{
var ret = new List<IFilterAction>();
@ -70,6 +83,9 @@ public class MenusSearchModel : ITableSearchModel
return ret;
}
/// <summary>
///
/// </summary>
public void Reset()
{
Name = null;

View File

@ -3,29 +3,53 @@ using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.Web.Models;
/// <summary>
///
/// </summary>
public class TasksModel
{
/// <summary>
///
/// </summary>
[Display(Name = "任务名称")]
[NotNull]
public string? Name { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "创建时间")]
public DateTimeOffset CreateTime { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "上次运行时间")]
public DateTimeOffset? LastRuntime { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "下次运行时间")]
public DateTimeOffset? NextRuntime { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "触发条件")]
[NotNull]
public string? Trigger { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "执行结果")]
[NotNull]
public TriggerResult LastRunResult { get; set; }
/// <summary>
///
/// </summary>
[Display(Name = "任务状态")]
[NotNull]
public SchedulerStatus Status { get; set; }

View File

@ -5,6 +5,9 @@
/// </summary>
public partial class Login
{
/// <summary>
///
/// </summary>
[SupplyParameterFromQuery]
[Parameter]
public string? ReturnUrl { get; set; }

View File

@ -5,6 +5,9 @@ using BootstrapAdmin.Web.Utils;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Exceptions
{
private List<int> PageItemsSource { get; } = new List<int> { 20, 40, 80, 100, 200 };
@ -18,6 +21,9 @@ public partial class Exceptions
[NotNull]
private List<SelectedItem>? CategroyLookup { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -4,6 +4,9 @@ using BootstrapAdmin.Web.Extensions;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Groups
{
[Inject]

View File

@ -12,7 +12,7 @@
</CardHeader>
<CardBody>
<AdminTable TItem="HealthCheckReportItem" IsFixedHeader="false" @ref="HealthTable"
ShowToolbar="false" ShowSearch="false" OnQueryAsync="OnQueryAsync">
ShowToolbar="false" ShowSearch="false" ShowLoading="true" OnQueryAsync="OnQueryAsync">
<TableColumns>
<TableColumn @bind-Field="@context.Name" Lookup="@GetNameLookup()" Text="检查项"></TableColumn>
<TableColumn @bind-Field="@context.Description" Text="描述"></TableColumn>

View File

@ -9,6 +9,9 @@ using System.Text.Json.Serialization;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Healths
{
[Inject]
@ -33,6 +36,9 @@ public partial class Healths
[NotNull]
private HttpClient? Client { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -1,13 +1,13 @@
@page "/Admin/Menus"
<AdminTable TItem="DataAccess.Models.Navigation"
IsTree="true" OnTreeExpand="OnTreeExpand" ExtendButtonColumnWidth="200"
CustomerSearchModel="SearchModel" OnQueryAsync="OnQueryAsync">
<AdminTable TItem="DataAccess.Models.Navigation"
IsTree="true" OnTreeExpand="OnTreeExpand" ExtendButtonColumnWidth="200"
CustomerSearchModel="SearchModel" OnQueryAsync="OnQueryAsync">
<TableToolbarTemplate>
<TableToolbarButton TItem="DataAccess.Models.Navigation" IsEnableWhenSelectedOneRow="true" Color="Color.Info" Icon="fa fa-sitemap" Text="分配角色" OnClickCallback="menus => OnAssignmentRoles(menus.First())" />
</TableToolbarTemplate>
<TableColumns>
<TableColumn @bind-Field="@context.Name" Filterable="true" Width="200"></TableColumn>
<TableColumn @bind-Field="@context.Name" Filterable="true" Searchable="true" Width="200"></TableColumn>
<TableColumn @bind-Field="@context.Order" Width="50"></TableColumn>
<TableColumn @bind-Field="@context.Icon" Width="50" Align="Alignment.Center">
<Template Context="v">

View File

@ -7,6 +7,9 @@ using BootstrapAdmin.Web.Utils;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Menus
{
[Inject]
@ -74,6 +77,19 @@ public partial class Menus
{
var navs = NavigationService.GetAllMenus(AppContext.UserName);
var menus = navs.Where(m => m.ParentId == "0");
// 处理模糊查询
if (options.Searchs.Any())
{
menus = menus.Where(options.Searchs.GetFilterFunc<Navigation>(FilterLogic.Or));
}
// 处理 Filter 高级搜索
if (options.CustomerSearchs.Any() || options.Filters.Any())
{
menus = menus.Where(options.CustomerSearchs.Concat(options.Filters).GetFilterFunc<Navigation>());
}
foreach (var item in menus)
{
item.HasChildren = navs.Any(i => i.ParentId == item.Id);

View File

@ -6,15 +6,19 @@ using Microsoft.AspNetCore.Components.Forms;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Profiles
{
private bool IsDemo { get; set; }
private string? ConfirmPassword { get; set; }
[NotNull]
private User? CurrentUser { get; set; }
/// <summary>
///
/// </summary>
[Inject]
[NotNull]
public BootstrapAppContext? AppContext { get; set; }

View File

@ -5,6 +5,9 @@ using BootstrapAdmin.Web.Services;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Roles
{
[Inject]

View File

@ -3,105 +3,107 @@ using BootstrapAdmin.Web.Core;
using BootstrapAdmin.Web.Extensions;
using Microsoft.AspNetCore.Components.Forms;
namespace BootstrapAdmin.Web.Pages.Admin
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Settings
{
public partial class Settings
private bool IsDemo { get; set; }
[NotNull]
private AppInfo? AppInfo { get; set; }
[NotNull]
private List<SelectedItem>? Logins { get; set; }
[NotNull]
private List<SelectedItem>? Themes { get; set; }
[Inject]
[NotNull]
private IDict? DictService { get; set; }
[Inject]
[NotNull]
private IUser? UserService { get; set; }
[Inject]
[NotNull]
private ToastService? ToastService { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
private bool IsDemo { get; set; }
base.OnInitialized();
[NotNull]
private AppInfo? AppInfo { get; set; }
[NotNull]
private List<SelectedItem>? Logins { get; set; }
[NotNull]
private List<SelectedItem>? Themes { get; set; }
[Inject]
[NotNull]
private IDict? DictService { get; set; }
[Inject]
[NotNull]
private IUser? UserService { get; set; }
[Inject]
[NotNull]
private ToastService? ToastService { get; set; }
/// <summary>
///
/// </summary>
protected override void OnInitialized()
IsDemo = DictService.IsDemo();
Logins = DictService.GetLogins().ToSelectedItemList();
Themes = DictService.GetThemes().ToSelectedItemList();
AppInfo = new()
{
base.OnInitialized();
IsDemo = IsDemo,
AuthCode = "123789",
Title = DictService.GetWebTitle(),
Footer = DictService.GetWebFooter(),
Login = DictService.GetCurrentLogin()
};
}
IsDemo = DictService.IsDemo();
Logins = DictService.GetLogins().ToSelectedItemList();
Themes = DictService.GetThemes().ToSelectedItemList();
AppInfo = new()
{
IsDemo = IsDemo,
AuthCode = "123789",
Title = DictService.GetWebTitle(),
Footer = DictService.GetWebFooter(),
Login = DictService.GetCurrentLogin()
};
private async Task ShowToast(bool result, string title)
{
if (result)
{
await ToastService.Success(title, $"保存{title}成功");
}
private async Task ShowToast(bool result, string title)
else
{
if (result)
{
await ToastService.Success(title, $"保存{title}成功");
}
else
{
await ToastService.Error(title, $"保存{title}失败");
}
}
private async Task OnSaveTitle(EditContext context)
{
var ret = DictService.SaveWebTitle(AppInfo.Title);
await ShowToast(ret, "网站标题");
}
private async Task OnSaveFooter(EditContext context)
{
var ret = DictService.SaveWebTitle(AppInfo.Title);
await ShowToast(ret, "网站页脚");
}
private async Task OnSaveLogin(EditContext context)
{
var ret = DictService.SaveLogin(AppInfo.Login);
await ShowToast(ret, "登录界面");
}
private async Task OnSaveAuthUrl(EditContext context)
{
var ret = DictService.SaveLogin(AppInfo.Login);
await ShowToast(ret, "授权后台地址");
}
private async Task OnSaveTheme(EditContext context)
{
var ret = DictService.SaveLogin(AppInfo.Login);
await ShowToast(ret, "网站主题");
}
private async Task OnSaveDemo(EditContext context)
{
var ret = false;
if (DictService.AuthenticateDemo(AppInfo.AuthCode))
{
IsDemo = AppInfo.IsDemo;
ret = DictService.SaveDemo(IsDemo);
StateHasChanged();
}
await ShowToast(ret, "演示系统");
await ToastService.Error(title, $"保存{title}失败");
}
}
private async Task OnSaveTitle(EditContext context)
{
var ret = DictService.SaveWebTitle(AppInfo.Title);
await ShowToast(ret, "网站标题");
}
private async Task OnSaveFooter(EditContext context)
{
var ret = DictService.SaveWebTitle(AppInfo.Title);
await ShowToast(ret, "网站页脚");
}
private async Task OnSaveLogin(EditContext context)
{
var ret = DictService.SaveLogin(AppInfo.Login);
await ShowToast(ret, "登录界面");
}
private async Task OnSaveAuthUrl(EditContext context)
{
var ret = DictService.SaveLogin(AppInfo.Login);
await ShowToast(ret, "授权后台地址");
}
private async Task OnSaveTheme(EditContext context)
{
var ret = DictService.SaveLogin(AppInfo.Login);
await ShowToast(ret, "网站主题");
}
private async Task OnSaveDemo(EditContext context)
{
var ret = false;
if (DictService.AuthenticateDemo(AppInfo.AuthCode))
{
IsDemo = AppInfo.IsDemo;
ret = DictService.SaveDemo(IsDemo);
StateHasChanged();
}
await ShowToast(ret, "演示系统");
}
}

View File

@ -5,6 +5,9 @@ using Longbow.Tasks;
namespace BootstrapAdmin.Web.Pages.Admin;
/// <summary>
///
/// </summary>
public partial class Tasks
{
private List<TasksModel> SelectedRows { get; set; } = new List<TasksModel>();
@ -29,6 +32,9 @@ public partial class Tasks
private List<string> SortList { get; } = new List<string>() { "Name", "LastRuntime desc" };
/// <summary>
///
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();

View File

@ -1,6 +1,9 @@
@page "/Admin/Users"
<AdminTable TItem="DataAccess.Models.User" ExtendButtonColumnWidth="270">
<Table TItem="DataAccess.Models.User" IsBordered="true" IsStriped="true" IsMultipleSelect="true"
ExtendButtonColumnWidth="270"
ShowToolbar="true" ShowExtendButtons="true" ShowSearch="true"
UseInjectDataService="true" OnSaveAsync="@OnSaveAsync">
<TableToolbarTemplate>
<TableToolbarButton TItem="DataAccess.Models.User" IsEnableWhenSelectedOneRow="true" Color="Color.Warning" Icon="fa fa-bank" Text="分配部门" OnClickCallback="users => OnAssignmentGroups(users.First())" />
<TableToolbarButton TItem="DataAccess.Models.User" IsEnableWhenSelectedOneRow="true" Color="Color.Info" Icon="fa fa-sitemap" Text="分配角色" OnClickCallback="users => OnAssignmentRoles(users.First())" />
@ -33,4 +36,4 @@
<TableCellButton Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa fa-bank" Text="分配部门" OnClick="() => OnAssignmentGroups(context)" />
<TableCellButton Size="Size.ExtraSmall" Color="Color.Info" Icon="fa fa-sitemap" Text="分配角色" OnClick="() => OnAssignmentRoles(context)" />
</RowButtonTemplate>
</AdminTable>
</Table>

View File

@ -66,4 +66,9 @@ public partial class Users
return Task.FromResult(ret);
}, ToastService);
}
private Task<bool> OnSaveAsync(User user, ItemChangedType itemChangedType)
{
return Task.FromResult(UserService.SaveUser(user.UserName, user.DisplayName, user.NewPassword));
}
}

View File

@ -1,4 +1,6 @@
var builder = WebApplication.CreateBuilder(args);
using BootstrapAdmin.Web.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();

View File

@ -14,6 +14,7 @@ public static class ExceptionsHelper
///
/// </summary>
/// <param name="provider"></param>
/// <param name="eventId"></param>
/// <param name="ex"></param>
/// <param name="additionalInfo"></param>
/// <returns></returns>
@ -50,8 +51,8 @@ public static class ExceptionsHelper
LogTime = DateTime.Now,
Category = category
};
var expceptionService = provider.GetRequiredService<IException>();
expceptionService.Log(exception);
//var expceptionService = provider.GetRequiredService<IException>();
//expceptionService.Log(exception);
}
}
}

View File

@ -3,8 +3,14 @@ using System.ComponentModel.DataAnnotations;
namespace BootstrapAdmin.Web.Validators;
/// <summary>
///
/// </summary>
public class UserNameValidator : IValidator
{
/// <summary>
///
/// </summary>
public string? ErrorMessage { get; set; }
private IUser UserService { get; }