wip: 🧠 初步的框架

This commit is contained in:
tk
2023-08-25 15:33:42 +08:00
parent 57c1ba2002
commit 18b4d7547a
1014 changed files with 122380 additions and 2 deletions

View File

@ -0,0 +1,20 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Api;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 接口模块
/// </summary>
public interface IApiModule : ICrudModule<CreateApiReq, QueryApiRsp // 创建类型
, QueryApiReq, QueryApiRsp // 查询类型
, NopReq, NopReq // 修改类型
, DelReq // 删除类型
>
{
/// <summary>
/// 同步接口
/// </summary>
Task SyncAsync();
}

View File

@ -0,0 +1,20 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 缓存模块
/// </summary>
public interface ICacheModule
{
/// <summary>
/// 缓存统计
/// </summary>
Task<CacheStatisticsRsp> CacheStatisticsAsync();
/// <summary>
/// 获取所有缓存项
/// </summary>
PagedQueryRsp<GetAllEntriesRsp> GetAllEntries(PagedQueryReq<GetAllEntriesReq> req);
}

View File

@ -0,0 +1,19 @@
using NetAdmin.Domain.Dto.Sys.Captcha;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 人机验证模块
/// </summary>
public interface ICaptchaModule
{
/// <summary>
/// 获取人机校验图
/// </summary>
Task<GetCaptchaRsp> GetCaptchaImageAsync();
/// <summary>
/// 完成人机校验
/// </summary>
Task<bool> VerifyCaptchaAsync(VerifyCaptchaReq req);
}

View File

@ -0,0 +1,20 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Config;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 配置模块
/// </summary>
public interface IConfigModule : ICrudModule<CreateConfigReq, QueryConfigRsp // 创建类型
, QueryConfigReq, QueryConfigRsp // 查询类型
, UpdateConfigReq, QueryConfigRsp // 修改类型
, DelReq // 删除类型
>
{
/// <summary>
/// 获取最新有效配置
/// </summary>
Task<QueryConfigRsp> GetLatestConfigAsync();
}

View File

@ -0,0 +1,27 @@
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 常量模块
/// </summary>
public interface IConstantModule
{
/// <summary>
/// 获得常量字符串
/// </summary>
IDictionary<string, string> GetCharsDic();
/// <summary>
/// 获得公共枚举值
/// </summary>
IDictionary<string, Dictionary<string, string[]>> GetEnums();
/// <summary>
/// 获得本地化字符串
/// </summary>
IDictionary<string, string> GetLocalizedStrings();
/// <summary>
/// 获得数字常量表
/// </summary>
IDictionary<string, long> GetNumbersDic();
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dept;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 部门模块
/// </summary>
public interface IDeptModule : ICrudModule<CreateDeptReq, QueryDeptRsp // 创建类型
, QueryDeptReq, QueryDeptRsp // 查询类型
, UpdateDeptReq, QueryDeptRsp // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,24 @@
using NetAdmin.Domain.Dto.Sys.Dev;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 开发模块
/// </summary>
public interface IDevModule
{
/// <summary>
/// 生成后端代码
/// </summary>
Task GenerateCsCodeAsync(GenerateCsCodeReq req);
/// <summary>
/// 生成图标代码
/// </summary>
Task GenerateIconCodeAsync(GenerateIconCodeReq req);
/// <summary>
/// 生成接口代码
/// </summary>
Task GenerateJsCodeAsync();
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Catalog;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 字典目录模块
/// </summary>
public interface IDicCatalogModule : ICrudModule<CreateDicCatalogReq, QueryDicCatalogRsp // 创建类型
, QueryDicCatalogReq, QueryDicCatalogRsp // 查询类型
, UpdateDicCatalogReq, QueryDicCatalogRsp // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 字典内容模块
/// </summary>
public interface IDicContentModule : ICrudModule<CreateDicContentReq, QueryDicContentRsp // 创建类型
, QueryDicContentReq, QueryDicContentRsp // 查询类型
, UpdateDicContentReq, QueryDicContentRsp // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,71 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Catalog;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 字典模块
/// </summary>
public interface IDicModule
{
/// <summary>
/// 批量删除字典目录
/// </summary>
Task<int> BulkDeleteCatalogAsync(BulkReq<DelReq> req);
/// <summary>
/// 批量删除字典内容
/// </summary>
Task<int> BulkDeleteContentAsync(BulkReq<DelReq> req);
/// <summary>
/// 创建字典目录
/// </summary>
Task<QueryDicCatalogRsp> CreateCatalogAsync(CreateDicCatalogReq req);
/// <summary>
/// 创建字典内容
/// </summary>
Task<QueryDicContentRsp> CreateContentAsync(CreateDicContentReq req);
/// <summary>
/// 删除字典目录
/// </summary>
Task<int> DeleteCatalogAsync(DelReq req);
/// <summary>
/// 删除字典内容
/// </summary>
Task<int> DeleteContentAsync(DelReq req);
/// <summary>
/// 分页查询字典目录
/// </summary>
Task<PagedQueryRsp<QueryDicCatalogRsp>> PagedQueryCatalogAsync(PagedQueryReq<QueryDicCatalogReq> req);
/// <summary>
/// 分页查询字典内容
/// </summary>
Task<PagedQueryRsp<QueryDicContentRsp>> PagedQueryContentAsync(PagedQueryReq<QueryDicContentReq> req);
/// <summary>
/// 查询字典目录
/// </summary>
Task<IEnumerable<QueryDicCatalogRsp>> QueryCatalogAsync(QueryReq<QueryDicCatalogReq> req);
/// <summary>
/// 查询字典内容
/// </summary>
Task<IEnumerable<QueryDicContentRsp>> QueryContentAsync(QueryReq<QueryDicContentReq> req);
/// <summary>
/// 更新字典目录
/// </summary>
Task<QueryDicCatalogRsp> UpdateCatalogAsync(UpdateDicCatalogReq req);
/// <summary>
/// 更新字典内容
/// </summary>
Task<QueryDicContentRsp> UpdateContentAsync(UpdateDicContentReq req);
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 文件模块
/// </summary>
public interface IFileModule
{
/// <summary>
/// 文件上传
/// </summary>
Task<string> UploadAsync(IFormFile file);
}

View File

@ -0,0 +1,20 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Menu;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 菜单模块
/// </summary>
public interface IMenuModule : ICrudModule<CreateMenuReq, QueryMenuRsp // 创建类型
, QueryMenuReq, QueryMenuRsp // 查询类型
, UpdateMenuReq, QueryMenuRsp // 修改类型
, DelReq // 删除类型
>
{
/// <summary>
/// 当前用户菜单
/// </summary>
Task<IEnumerable<QueryMenuRsp>> UserMenusAsync();
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.RequestLog;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 请求日志模块
/// </summary>
public interface IRequestLogModule : ICrudModule<CreateRequestLogReq, QueryRequestLogRsp // 创建类型
, QueryRequestLogReq, QueryRequestLogRsp // 查询类型
, NopReq, NopReq // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Role;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 角色模块
/// </summary>
public interface IRoleModule : ICrudModule<CreateRoleReq, QueryRoleRsp // 创建类型
, QueryRoleReq, QueryRoleRsp // 查询类型
, UpdateRoleReq, QueryRoleRsp // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,17 @@
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 工具模块
/// </summary>
public interface IToolsModule
{
/// <summary>
/// 服务器时间
/// </summary>
DateTime GetServerUtcTime();
/// <summary>
/// 版本信息
/// </summary>
string Version();
}

View File

@ -0,0 +1,76 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Dto.Sys.UserProfile;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 用户模块
/// </summary>
public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建类型
, QueryUserReq, QueryUserRsp // 查询类型
, UpdateUserReq, QueryUserRsp // 修改类型
, DelReq // 删除类型
>
{
/// <summary>
/// 检查手机号是否可用
/// </summary>
Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req);
/// <summary>
/// 检查用户名是否可用
/// </summary>
Task<bool> CheckUserNameAvailableAsync(CheckUserNameAvailableReq req);
/// <summary>
/// 密码登录
/// </summary>
Task<LoginRsp> LoginByPwdAsync(LoginByPwdReq req);
/// <summary>
/// 短信登录
/// </summary>
Task<LoginRsp> LoginBySmsAsync(LoginBySmsReq req);
/// <summary>
/// 查询用户档案
/// </summary>
Task<IEnumerable<QueryUserProfileRsp>> QueryProfileAsync(QueryReq<QueryUserProfileReq> req);
/// <summary>
/// 注册用户
/// </summary>
Task<UserInfoRsp> RegisterAsync(RegisterUserReq req);
/// <summary>
/// 重设密码
/// </summary>
Task<uint> ResetPasswordAsync(ResetPasswordReq req);
/// <summary>
/// 设置用户头像
/// </summary>
Task<UserInfoRsp> SetAvatarAsync(SetAvatarReq req);
/// <summary>
/// 设置邮箱
/// </summary>
Task<UserInfoRsp> SetEmailAsync(SetEmailReq req);
/// <summary>
/// 设置手机号
/// </summary>
Task<UserInfoRsp> SetMobileAsync(SetMobileReq req);
/// <summary>
/// 设置密码
/// </summary>
Task<uint> SetPasswordAsync(SetPasswordReq req);
/// <summary>
/// 当前用户信息
/// </summary>
Task<UserInfoRsp> UserInfoAsync();
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.UserProfile;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 用户档案模块
/// </summary>
public interface IUserProfileModule : ICrudModule<CreateUserProfileReq, QueryUserProfileRsp // 创建类型
, QueryUserProfileReq, QueryUserProfileRsp // 查询类型
, UpdateUserProfileReq, QueryUserProfileRsp // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,28 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.VerifyCode;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 验证码模块
/// </summary>
public interface IVerifyCodeModule : ICrudModule<CreateVerifyCodeReq, QueryVerifyCodeRsp // 创建类型
, QueryVerifyCodeReq, QueryVerifyCodeRsp // 查询类型
, UpdateVerifyCodeReq, QueryVerifyCodeRsp // 修改类型
, DelReq // 删除类型
>
{
/// <summary>
/// 发送验证码
/// </summary>
Task<SendVerifyCodeRsp> SendVerifyCodeAsync(SendVerifyCodeReq req);
/// <summary>
/// 完成验证
/// </summary>
/// <remarks>
/// 对于验证失败的,不主动删除缓存,通过防火墙来应对暴力破解
/// </remarks>
Task<bool> VerifyAsync(VerifyCodeReq req);
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Tpl.Example;
namespace NetAdmin.SysComponent.Application.Modules.Tpl;
/// <summary>
/// 示例模块
/// </summary>
public interface IExampleModule : ICrudModule<CreateExampleReq, QueryExampleRsp // 创建类型
, QueryExampleReq, QueryExampleRsp // 查询类型
, UpdateExampleReq, QueryExampleRsp // 修改类型
, DelReq // 删除类型
> { }

View File

@ -0,0 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)/CodeQuality.props"/>
<ItemGroup>
<ProjectReference Include="../NetAdmin.Application/NetAdmin.Application.csproj"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,160 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Api;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IApiService" />
public sealed class ApiService : RepositoryService<Sys_Api, IApiService>, IApiService
{
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
private readonly XmlCommentReader _xmlCommentReader;
/// <summary>
/// Initializes a new instance of the <see cref="ApiService" /> class.
/// </summary>
public ApiService(Repository<Sys_Api> rpo, XmlCommentReader xmlCommentReader
, IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) //
: base(rpo)
{
_xmlCommentReader = xmlCommentReader;
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
}
/// <summary>
/// 批量删除接口
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 创建接口
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryApiRsp> CreateAsync(CreateApiReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 删除接口
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<int> DeleteAsync(DelReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 判断接口是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryApiReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个接口
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryApiRsp> GetAsync(QueryApiReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询接口
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<PagedQueryRsp<QueryApiRsp>> PagedQueryAsync(PagedQueryReq<QueryApiReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 查询接口
/// </summary>
public async Task<IEnumerable<QueryApiRsp>> QueryAsync(QueryReq<QueryApiReq> req)
{
var ret = await Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter).ToTreeListAsync();
return ret.Adapt<IEnumerable<QueryApiRsp>>();
}
/// <summary>
/// 反射接口列表
/// </summary>
public IEnumerable<QueryApiRsp> ReflectionList(bool excludeAnonymous = true)
{
var regex = new Regex(@"\.(\w+)$", RegexOptions.Compiled);
QueryApiRsp SelectQueryApiRsp(IGrouping<TypeInfo, ControllerActionDescriptor> group)
{
var first = group.First()!;
return new QueryApiRsp {
Summary = _xmlCommentReader.GetComments(group.Key)
, Name = first.ControllerName
, Id = Regex.Replace( //
first.AttributeRouteInfo!.Template!, $"/{first.ActionName}$", string.Empty)
, Children = GetChildren(group)
, Namespace = regex.Match(group.Key.Namespace!).Groups[1].Value.ToLowerInvariant()
};
}
var actionDescriptors //
= _actionDescriptorCollectionProvider.ActionDescriptors.Items.Cast<ControllerActionDescriptor>();
if (excludeAnonymous) {
actionDescriptors = actionDescriptors.Where(x => x.EndpointMetadata.All(y => y is AllowAnonymousAttribute));
}
var actionGroup //
= actionDescriptors.GroupBy(x => x.ControllerTypeInfo);
return actionGroup.Select(SelectQueryApiRsp);
}
/// <summary>
/// 同步接口
/// </summary>
public async Task SyncAsync()
{
_ = await Rpo.DeleteAsync(_ => true);
var list = ReflectionList(false);
EnableCascadeSave = true;
foreach (var item in list) {
var entity = item.Adapt<Sys_Api>();
_ = await Rpo.InsertAsync(entity);
}
}
/// <summary>
/// 更新接口
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<NopReq> UpdateAsync(NopReq req)
{
throw new NotImplementedException();
}
private IEnumerable<QueryApiRsp> GetChildren(IEnumerable<ControllerActionDescriptor> actionDescriptors)
{
return actionDescriptors //
.Select(x => new QueryApiRsp {
Summary = _xmlCommentReader.GetComments(x.MethodInfo)
, Name = x.ActionName
, Id = x.AttributeRouteInfo!.Template
, Method = x.ActionConstraints?.OfType<HttpMethodActionConstraint>()
.FirstOrDefault()
?.HttpMethods.First()
});
}
}

View File

@ -0,0 +1,49 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using StackExchange.Redis;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="ICacheService" />
public sealed class CacheService : ServiceBase<ICacheService>, ICacheService
{
private readonly IConnectionMultiplexer _connectionMultiplexer;
/// <summary>
/// Initializes a new instance of the <see cref="CacheService" /> class.
/// </summary>
public CacheService(IConnectionMultiplexer connectionMultiplexer)
{
_connectionMultiplexer = connectionMultiplexer;
}
/// <summary>
/// 缓存统计
/// </summary>
public Task<CacheStatisticsRsp> CacheStatisticsAsync()
{
var database = _connectionMultiplexer.GetDatabase();
return Task.FromResult(
new CacheStatisticsRsp((string)database.Execute("info")) { DbSize = (long)database.Execute("dbSize") });
}
/// <inheritdoc />
public PagedQueryRsp<GetAllEntriesRsp> GetAllEntries(PagedQueryReq<GetAllEntriesReq> req)
{
var database = _connectionMultiplexer.GetDatabase((int?)req.Filter?.DbIndex ?? 0);
var redisResults
= (RedisResult[])database.Execute("scan", (req.Page - 1) * req.PageSize, "count", req.PageSize);
var list = ((string[])redisResults![1])!.Where(x => database.KeyType(x) == RedisType.Hash)
.Select(x => database.HashGetAll(x)
.Append(new HashEntry("key", x))
.ToArray()
.ToStringDictionary())
.ToList()
.ConvertAll(x => x.Adapt<GetAllEntriesRsp>());
return new PagedQueryRsp<GetAllEntriesRsp>(req.Page, req.PageSize, 10000, list);
}
}

View File

@ -0,0 +1,58 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Sys.Captcha;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using SixLabors.ImageSharp;
using Yitter.IdGenerator;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="ICaptchaService" />
public sealed class CaptchaService : ServiceBase<ICaptchaService>, ICaptchaService
{
private static readonly Assembly _entryAsm = Assembly.GetEntryAssembly();
private static readonly string _entryAsmName = _entryAsm.FullName![.._entryAsm.FullName.IndexOf(',')];
/// <summary>
/// Initializes a new instance of the <see cref="CaptchaService" /> class.
/// </summary>
public CaptchaService() { }
/// <summary>
/// 获取人机校验图
/// </summary>
public async Task<GetCaptchaRsp> GetCaptchaImageAsync()
{
var (backgroundImage, sliderImage, offsetSaw) = await CaptchaImageHelper.CreateSawSliderImageAsync(
_entryAsm, $"{_entryAsmName}.Assets.Captcha.background", $"{_entryAsmName}.Assets.Captcha.template"
, (1, 101), (1, 7), new Size(50, 50));
var id = $"{nameof(GetCaptchaImageAsync)}_{YitIdHelper.NextId()}";
return new GetCaptchaRsp {
Id = id
, BackgroundImage = backgroundImage
, SliderImage = sliderImage
, SawOffsetX = offsetSaw.X
};
}
/// <summary>
/// 完成人机校验
/// </summary>
public Task<bool> VerifyCaptchaAsync(VerifyCaptchaReq req)
{
if (req.SawOffsetX == null) {
return Task.FromResult(false);
}
bool ret;
try {
var aesKey = req.Id.Aes(CaptchaOptions.SecretKey)[..32];
ret = Math.Abs(req.SawOffsetX.Value - req.VerifyData.AesDe(aesKey).Float()) < 5f;
}
catch {
ret = false;
}
return Task.FromResult(ret);
}
}

View File

@ -0,0 +1,132 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Config;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using DataType = FreeSql.DataType;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IConfigService" />
public sealed class ConfigService : RepositoryService<Sys_Config, IConfigService>, IConfigService
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigService" /> class.
/// </summary>
public ConfigService(Repository<Sys_Config> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除配置
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建配置
/// </summary>
public async Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
{
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryConfigRsp>();
}
/// <summary>
/// 删除配置
/// </summary>
public Task<int> DeleteAsync(DelReq req)
{
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断配置是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryConfigReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个配置
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public async Task<QueryConfigRsp> GetAsync(QueryConfigReq req)
{
var ret = await QueryInternal(new QueryReq<QueryConfigReq> { Filter = req }).ToOneAsync();
return ret.Adapt<QueryConfigRsp>();
}
/// <summary>
/// 获取最新有效配置
/// </summary>
public async Task<QueryConfigRsp> GetLatestConfigAsync()
{
var ret = await QueryAsync(
new QueryReq<QueryConfigReq> { Count = 1, Filter = new QueryConfigReq { Enabled = true } });
return ret.FirstOrDefault();
}
/// <summary>
/// 分页查询配置
/// </summary>
public async Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
{
var list = await QueryInternal(req).Page(req.Page, req.PageSize).Count(out var total).ToListAsync();
return new PagedQueryRsp<QueryConfigRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryConfigRsp>>());
}
/// <summary>
/// 查询配置
/// </summary>
public async Task<IEnumerable<QueryConfigRsp>> QueryAsync(QueryReq<QueryConfigReq> req)
{
var ret = await QueryInternal(req).Take(req.Count).ToListAsync();
return ret.Adapt<IEnumerable<QueryConfigRsp>>();
}
/// <summary>
/// 更新配置
/// </summary>
public async Task<QueryConfigRsp> UpdateAsync(UpdateConfigReq req)
{
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req);
}
var ret = await Rpo.UpdateDiy.SetSource(req).ExecuteUpdatedAsync();
return ret.FirstOrDefault()?.Adapt<QueryConfigRsp>();
}
private ISelect<Sys_Config> QueryInternal(QueryReq<QueryConfigReq> req)
{
return Rpo.Select.Include(a => a.UserRegisterDept)
.Include(a => a.UserRegisterRole)
.WhereDynamicFilter(req.DynamicFilter)
.WhereIf( //
req.Filter?.Enabled.HasValue ?? false, a => a.Enabled == req.Filter.Enabled.Value)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending(a => a.Id);
}
/// <summary>
/// 非sqlite数据库请删掉
/// </summary>
private async Task<QueryConfigRsp> UpdateForSqliteAsync(Sys_Config req)
{
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0
? null
: await GetAsync(new QueryConfigReq { Id = req.Id });
}
}

View File

@ -0,0 +1,58 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IConstantService" />
public sealed class ConstantService : ServiceBase<IConstantService>, IConstantService
{
/// <summary>
/// 获得常量字符串
/// </summary>
public IDictionary<string, string> GetCharsDic()
{
return typeof(Chars).GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.FieldType == typeof(string))
.ToImmutableSortedDictionary( //
x => x.Name, x => x.GetValue(null)?.ToString());
}
/// <summary>
/// 获得公共枚举值
/// </summary>
public IDictionary<string, Dictionary<string, string[]>> GetEnums()
{
return App.EffectiveTypes.Where(x => x.IsEnum && x.GetCustomAttribute<ExportAttribute>(false) != null)
.ToDictionary(x => x.Name, x => //
x.GetEnumValues()
.Cast<Enum>()
.ToDictionary( //
y => y.ToString()
, y => new[] {
Convert.ToInt64(y, CultureInfo.InvariantCulture)
.ToString(CultureInfo.InvariantCulture)
, y.ResDesc<Ln>()
}));
}
/// <summary>
/// 获得本地化字符串
/// </summary>
public IDictionary<string, string> GetLocalizedStrings()
{
return typeof(Ln).GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.PropertyType == typeof(string))
.ToImmutableSortedDictionary(x => x.Name, x => x.GetValue(null)?.ToString());
}
/// <summary>
/// 获得数字常量表
/// </summary>
public IDictionary<string, long> GetNumbersDic()
{
return typeof(Numbers).GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.FieldType == typeof(int) || x.FieldType == typeof(long))
.ToImmutableSortedDictionary( //
x => x.Name, x => Convert.ToInt64(x.GetValue(null), CultureInfo.InvariantCulture));
}
}

View File

@ -0,0 +1,16 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Sys.Api;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 接口服务
/// </summary>
public interface IApiService : IService, IApiModule
{
/// <summary>
/// 反射接口列表
/// </summary>
IEnumerable<QueryApiRsp> ReflectionList(bool excludeAnonymous = true);
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 缓存服务
/// </summary>
public interface ICacheService : IService, ICacheModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 人机验证服务
/// </summary>
public interface ICaptchaService : IService, ICaptchaModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 配置服务
/// </summary>
public interface IConfigService : IService, IConfigModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 常量服务
/// </summary>
public interface IConstantService : IService, IConstantModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 部门服务
/// </summary>
public interface IDeptService : IService, IDeptModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 开发服务
/// </summary>
public interface IDevService : IService, IDevModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 字典目录服务
/// </summary>
public interface IDicCatalogService : IService, IDicCatalogModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 字典内容服务
/// </summary>
public interface IDicContentService : IService, IDicContentModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 字典服务
/// </summary>
public interface IDicService : IService, IDicModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 文件服务
/// </summary>
public interface IFileService : IService, IFileModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 菜单服务
/// </summary>
public interface IMenuService : IService, IMenuModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 请求日志服务
/// </summary>
public interface IRequestLogService : IService, IRequestLogModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 角色服务
/// </summary>
public interface IRoleService : IService, IRoleModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 工具服务
/// </summary>
public interface IToolsService : IService, IToolsModule { }

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 用户档案服务
/// </summary>
public interface IUserProfileService : IService, IUserProfileModule { }

View File

@ -0,0 +1,21 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 用户服务
/// </summary>
public interface IUserService : IService, IUserModule
{
/// <summary>
/// 获取单个用户(带更新锁)
/// </summary>
Task<QueryUserRsp> GetForUpdateAsync(QueryUserReq req);
/// <summary>
/// 单体更新
/// </summary>
Task UpdateSingleAsync(UpdateUserReq req);
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 验证码服务
/// </summary>
public interface IVerifyCodeService : IService, IVerifyCodeModule { }

View File

@ -0,0 +1,139 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dept;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IDeptService" />
public sealed class DeptService : RepositoryService<Sys_Dept, IDeptService>, IDeptService
{
/// <summary>
/// Initializes a new instance of the <see cref="DeptService" /> class.
/// </summary>
public DeptService(Repository<Sys_Dept> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除部门
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建部门
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">Parent_department_does_not_exist</exception>
public async Task<QueryDeptRsp> CreateAsync(CreateDeptReq req)
{
if (req.ParentId != 0 && !await Rpo.Select.AnyAsync(a => a.Id == req.ParentId)) {
throw new NetAdminInvalidOperationException(Ln.);
}
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryDeptRsp>();
}
/// <summary>
/// 删除部门
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">该部门下存在用户</exception>
/// <exception cref="NetAdminInvalidOperationException">该部门下存在子部门</exception>
public async Task<int> DeleteAsync(DelReq req)
{
if (await Rpo.Orm.Select<Sys_User>().AnyAsync(a => a.DeptId == req.Id)) {
throw new NetAdminInvalidOperationException(Ln.);
}
#pragma warning disable IDE0046
if (await Rpo.Select.AnyAsync(a => a.ParentId == req.Id)) {
#pragma warning restore IDE0046
throw new NetAdminInvalidOperationException(Ln.);
}
return await Rpo.DeleteAsync(x => x.Id == req.Id);
}
/// <summary>
/// 判断部门是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryDeptReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个部门
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryDeptRsp> GetAsync(QueryDeptReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询部门
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<PagedQueryRsp<QueryDeptRsp>> PagedQueryAsync(PagedQueryReq<QueryDeptReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 查询部门
/// </summary>
public async Task<IEnumerable<QueryDeptRsp>> QueryAsync(QueryReq<QueryDeptReq> req)
{
return (await QueryInternal(req).ToTreeListAsync()).Adapt<IEnumerable<QueryDeptRsp>>();
}
/// <summary>
/// 更新部门
/// </summary>
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryDeptRsp> UpdateAsync(UpdateDeptReq req)
{
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0
? throw new NetAdminUnexpectedException()
: (await QueryInternal(new QueryReq<QueryDeptReq> { Filter = new QueryDeptReq { Id = req.Id } }, true)
.ToTreeListAsync())[0]
.Adapt<QueryDeptRsp>();
}
private ISelect<Sys_Dept> QueryInternal(QueryReq<QueryDeptReq> req, bool asTreeCte = false)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Name.Contains(req.Keywords) || a.Summary.Contains(req.Keywords) ||
a.Id == req.Keywords.Int64Try(0));
if (asTreeCte) {
ret = ret.AsTreeCte();
}
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.Sort), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.Sort);
}
if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
}
}

View File

@ -0,0 +1,215 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Sys.Api;
using NetAdmin.Domain.Dto.Sys.Dev;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IDevService" />
public sealed class DevService : ServiceBase<DevService>, IDevService
{
private const string _REPLACE_TO_EMPTY = "//~";
private static readonly string _clientProjectPath = Path.Combine( //
Environment.CurrentDirectory, "../../frontend/admin");
private static readonly string[] _projectDirs
= Directory.GetDirectories(Path.Combine(Environment.CurrentDirectory, "../"));
private readonly IApiService _apiService;
/// <summary>
/// Initializes a new instance of the <see cref="DevService" /> class.
/// </summary>
public DevService(IApiService apiService)
{
_apiService = apiService;
}
/// <summary>
/// 生成后端代码
/// </summary>
public async Task GenerateCsCodeAsync(GenerateCsCodeReq req)
{
// 模块类型Sys、Biz、等
var moduleType = Enum.GetName(req.Type)!;
// 模板层目录
var tplHostDir = GetDir("SysComponent.Host");
var tplCacheDir = GetDir("SysComponent.Cache");
var tplAppDir = GetDir("SysComponent.Application");
// 主机层目录
var hostControllerDir = Path.Combine(GetDir($"{moduleType}.Host"), "Controllers", moduleType[..3]);
// 缓存层目录
var cacheDir = Path.Combine(GetDir($"{moduleType}.Cache"), moduleType[..3]);
var cacheDependencyDir = Path.Combine(cacheDir, "Dependency");
// 业务逻辑层目录
var appDir = GetDir($"{moduleType}.Application");
var appModulesDir = Path.Combine(appDir, "Modules", moduleType[..3]);
var appServicesDir = Path.Combine(appDir, "Services", moduleType[..3]);
var appServicesDependencyDir = Path.Combine(appServicesDir, "Dependency");
// 数据契约层目录
var dataDir = GetDir("NetAdmin.Domain");
var dtoDir = Path.Combine(dataDir, "Dto", moduleType[..3], req.ModuleName);
var entityDir = Path.Combine(dataDir, "DbMaps", moduleType[..3]);
// 创建缺少的目录
CreateDir(hostControllerDir, cacheDir, cacheDependencyDir, appDir, appModulesDir, appServicesDir
, appServicesDependencyDir, dataDir, dtoDir, entityDir);
// Controller
await WriteCodeFileAsync(req, Path.Combine(tplHostDir, "Controllers", "Tpl", "ExampleController.cs")
, Path.Combine(hostControllerDir, $"{req.ModuleName}Controller.cs"));
// CreateReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "CreateExampleReq.cs")
, Path.Combine(dtoDir, $"Create{req.ModuleName}Req.cs"));
// UpdateReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "UpdateExampleReq.cs")
, Path.Combine(dtoDir, $"Update{req.ModuleName}Req.cs"));
// QueryReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "QueryExampleReq.cs")
, Path.Combine(dtoDir, $"Query{req.ModuleName}Req.cs"));
// QueryRsp
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "QueryExampleRsp.cs")
, Path.Combine(dtoDir, $"Query{req.ModuleName}Rsp.cs"));
// ICache
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "Dependency", "IExampleCache.cs")
, Path.Combine(cacheDependencyDir, $"I{req.ModuleName}Cache.cs"));
// Cache
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "ExampleCache.cs")
, Path.Combine(cacheDir, $"{req.ModuleName}Cache.cs"));
// IModule
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Modules", "Tpl", "IExampleModule.cs")
, Path.Combine(appModulesDir, $"I{req.ModuleName}Module.cs"));
// IService
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "Dependency", "IExampleService.cs")
, Path.Combine(appServicesDependencyDir, $"I{req.ModuleName}Service.cs"));
// Service
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "ExampleService.cs")
, Path.Combine(appServicesDir, $"{req.ModuleName}Service.cs"));
// Entity
await WriteCodeFileAsync(req, Path.Combine(dataDir, "DbMaps", "Tpl", "Tpl_Example.cs")
, Path.Combine(entityDir, $"{moduleType[..3]}_{req.ModuleName}.cs"));
}
/// <summary>
/// 生成图标代码
/// </summary>
public async Task GenerateIconCodeAsync(GenerateIconCodeReq req)
{
var tplSvg = await File.ReadAllTextAsync(
Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "Svg.vue"));
var tplExport
= await File.ReadAllTextAsync(
Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "export.js"));
var vueContent = tplSvg.Replace("$svgCode$", req.SvgCode).Replace(_REPLACE_TO_EMPTY, string.Empty);
var dir = Path.Combine(_clientProjectPath, "src", "assets", "icons");
if (!Directory.Exists(dir)) {
_ = Directory.CreateDirectory(dir);
}
var vueFile = Path.Combine(dir, $"{req.IconName}.vue");
await File.WriteAllTextAsync(vueFile, vueContent);
var indexJsFile = Path.Combine(dir, "index.js");
await File.AppendAllTextAsync(
indexJsFile
, Environment.NewLine +
tplExport.Replace("$iconName$", req.IconName).Replace(_REPLACE_TO_EMPTY, string.Empty));
// 修改iconSelect.js
var iconSelectFile = Path.Combine(_clientProjectPath, "src", "config", "iconSelect.js");
var iconSelectContent = await File.ReadAllTextAsync(iconSelectFile);
iconSelectContent = iconSelectContent.Replace("export default", "exportDefault:").Replace("'", "\"");
iconSelectContent = Regex.Replace(iconSelectContent, "([a-zA-Z]+):", "\"$1\":");
iconSelectContent = "{" + iconSelectContent + "}";
var iconExportJsInfo = iconSelectContent.ToObject<IconExportJsInfo>();
iconExportJsInfo.ExportDefault.Icons.Last().Icons.Add($"sc-icon-{req.IconName.ToLowerInvariant()}");
var newContent = iconExportJsInfo.ToJson().TrimStart('{')[..^1].Replace("\"exportDefault\":", "export default");
await File.WriteAllTextAsync(iconSelectFile, newContent);
}
/// <summary>
/// 生成接口代码
/// </summary>
public async Task GenerateJsCodeAsync()
{
// 模板文件
var tplOuter = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "outer.js"));
var tplInner = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "inner.js"));
IEnumerable<string> Select(QueryApiRsp item)
{
return item.Children.Select(x => tplInner.Replace("$actionDesc$", x.Summary)
.Replace( //
"$actionName$"
, Regex.Replace(x.Name, @"\.(\w)"
, y => y.Groups[1].Value.ToUpperInvariant()))
.Replace("$actionPath$", x.Id)
.Replace( //
"$actionMethod$", x.Method?.ToLowerInvariant())
.Replace(_REPLACE_TO_EMPTY, string.Empty)); //
}
foreach (var item in _apiService.ReflectionList(false)) {
var dir = Path.Combine(_clientProjectPath, "src", "api", item.Namespace);
if (!Directory.Exists(dir)) {
_ = Directory.CreateDirectory(dir);
}
var file = Path.Combine(dir, $"{item.Name.Replace(".", string.Empty)}.js");
var content = tplOuter.Replace("$controllerDesc$", item.Summary)
.Replace("$controllerPath$", item.Id)
.Replace( //
"$inner$"
, string.Join(Environment.NewLine + Environment.NewLine, Select(item)))
.Replace(_REPLACE_TO_EMPTY, string.Empty);
await File.WriteAllTextAsync(file, content);
}
}
private static void CreateDir(params string[] dirs)
{
foreach (var dir in dirs) {
if (!Directory.Exists(dir)) {
_ = Directory.CreateDirectory(dir);
}
}
}
private static string GetDir(string key)
{
return _projectDirs.First(x => x.EndsWith(key, true, CultureInfo.InvariantCulture));
}
private static async Task WriteCodeFileAsync(GenerateCsCodeReq req, string tplFile, string writeFile)
{
var tplContent = await File.ReadAllTextAsync(tplFile);
tplContent = tplContent.Replace("Tpl", Enum.GetName(req.Type)![..3])
.Replace("示例", req.ModuleRemark)
.Replace("Example", req.ModuleName)
.Replace("NetAdmin.SysComponent", "SysComponent");
await File.WriteAllTextAsync(writeFile, tplContent);
}
}

View File

@ -0,0 +1,119 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Catalog;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IDicCatalogService" />
public sealed class DicCatalogService : RepositoryService<Sys_DicCatalog, IDicCatalogService>, IDicCatalogService
{
/// <summary>
/// Initializes a new instance of the <see cref="DicCatalogService" /> class.
/// </summary>
public DicCatalogService(Repository<Sys_DicCatalog> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除字典目录
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建字典目录
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">The_parent_node_does_not_exist</exception>
public async Task<QueryDicCatalogRsp> CreateAsync(CreateDicCatalogReq req)
{
if (req.ParentId != 0 && !await Rpo.Select.Where(a => a.Id == req.ParentId).ForUpdate().AnyAsync()) {
throw new NetAdminInvalidOperationException(Ln.);
}
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryDicCatalogRsp>();
}
/// <summary>
/// 删除字典目录
/// </summary>
public async Task<int> DeleteAsync(DelReq req)
{
var ret = await Rpo.DeleteCascadeByDatabaseAsync(a => a.Id == req.Id);
return ret.Count;
}
/// <summary>
/// 判断字典目录是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryDicCatalogReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个字典目录
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryDicCatalogRsp> GetAsync(QueryDicCatalogReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询字典目录
/// </summary>
public async Task<PagedQueryRsp<QueryDicCatalogRsp>> PagedQueryAsync(PagedQueryReq<QueryDicCatalogReq> req)
{
var list = await QueryInternal(req).Page(req.Page, req.PageSize).Count(out var total).ToListAsync();
return new PagedQueryRsp<QueryDicCatalogRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryDicCatalogRsp>>());
}
/// <summary>
/// 查询字典目录
/// </summary>
public async Task<IEnumerable<QueryDicCatalogRsp>> QueryAsync(QueryReq<QueryDicCatalogReq> req)
{
var ret = await QueryInternal(req).ToTreeListAsync();
return ret.Adapt<IEnumerable<QueryDicCatalogRsp>>();
}
/// <summary>
/// 更新字典目录
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">The_parent_node_does_not_exist</exception>
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryDicCatalogRsp> UpdateAsync(UpdateDicCatalogReq req)
{
if (req.ParentId != 0 && !await Rpo.Select.Where(a => a.Id == req.ParentId).ForUpdate().AnyAsync()) {
throw new NetAdminInvalidOperationException(Ln.);
}
if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0) {
throw new NetAdminUnexpectedException();
}
var ret = await Rpo.Select.Where(a => a.Id == req.Id).ToOneAsync();
return ret.Adapt<QueryDicCatalogRsp>();
}
private ISelect<Sys_DicCatalog> QueryInternal(QueryReq<QueryDicCatalogReq> req)
{
return Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending(a => a.Id);
}
}

View File

@ -0,0 +1,122 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IDicContentService" />
public sealed class DicContentService : RepositoryService<Sys_DicContent, IDicContentService>, IDicContentService
{
/// <summary>
/// Initializes a new instance of the <see cref="DicContentService" /> class.
/// </summary>
public DicContentService(Repository<Sys_DicContent> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除字典内容
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建字典内容
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">Dictionary_directory_does_not_exist</exception>
public async Task<QueryDicContentRsp> CreateAsync(CreateDicContentReq req)
{
if (!await Rpo.Orm.Select<Sys_DicCatalog>().Where(a => a.Id == req.CatalogId).ForUpdate().AnyAsync()) {
throw new NetAdminInvalidOperationException(Ln.);
}
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryDicContentRsp>();
}
/// <summary>
/// 删除字典内容
/// </summary>
public Task<int> DeleteAsync(DelReq req)
{
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断字典是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryDicContentReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个字典
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryDicContentRsp> GetAsync(QueryDicContentReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询字典内容
/// </summary>
public async Task<PagedQueryRsp<QueryDicContentRsp>> PagedQueryAsync(PagedQueryReq<QueryDicContentReq> req)
{
var list = await QueryInternal(req).Page(req.Page, req.PageSize).Count(out var total).ToListAsync();
return new PagedQueryRsp<QueryDicContentRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryDicContentRsp>>());
}
/// <summary>
/// 查询字典内容
/// </summary>
public async Task<IEnumerable<QueryDicContentRsp>> QueryAsync(QueryReq<QueryDicContentReq> req)
{
var ret = await QueryInternal(req).Take(req.Count).ToListAsync();
return ret.Adapt<IEnumerable<QueryDicContentRsp>>();
}
/// <summary>
/// 更新字典内容
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">Dictionary_directory_does_not_exist</exception>
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryDicContentRsp> UpdateAsync(UpdateDicContentReq req)
{
if (!await Rpo.Orm.Select<Sys_DicCatalog>().Where(a => a.Id == req.CatalogId).ForUpdate().AnyAsync()) {
throw new NetAdminInvalidOperationException(Ln.);
}
if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0) {
throw new NetAdminUnexpectedException();
}
var ret = await Rpo.Select.Where(a => a.Id == req.Id).ToOneAsync();
return ret.Adapt<QueryDicContentRsp>();
}
private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.Id);
}
return ret;
}
}

View File

@ -0,0 +1,119 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Catalog;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IDicService" />
public sealed class DicService : ServiceBase<IDicService>, IDicService
{
private readonly IDicCatalogService _catalogService;
private readonly IDicContentService _contentService;
/// <summary>
/// Initializes a new instance of the <see cref="DicService" /> class.
/// </summary>
public DicService(IDicCatalogService catalogService, IDicContentService contentService)
{
_catalogService = catalogService;
_contentService = contentService;
}
/// <summary>
/// 批量删除字典目录
/// </summary>
public Task<int> BulkDeleteCatalogAsync(BulkReq<DelReq> req)
{
return _catalogService.BulkDeleteAsync(req);
}
/// <summary>
/// 批量删除字典内容
/// </summary>
public Task<int> BulkDeleteContentAsync(BulkReq<DelReq> req)
{
return _contentService.BulkDeleteAsync(req);
}
/// <summary>
/// 创建字典目录
/// </summary>
public Task<QueryDicCatalogRsp> CreateCatalogAsync(CreateDicCatalogReq req)
{
return _catalogService.CreateAsync(req);
}
/// <summary>
/// 创建字典内容
/// </summary>
public Task<QueryDicContentRsp> CreateContentAsync(CreateDicContentReq req)
{
return _contentService.CreateAsync(req);
}
/// <summary>
/// 删除字典目录
/// </summary>
public Task<int> DeleteCatalogAsync(DelReq req)
{
return _catalogService.DeleteAsync(req);
}
/// <summary>
/// 删除字典内容
/// </summary>
public Task<int> DeleteContentAsync(DelReq req)
{
return _contentService.DeleteAsync(req);
}
/// <summary>
/// 分页查询字典目录
/// </summary>
public Task<PagedQueryRsp<QueryDicCatalogRsp>> PagedQueryCatalogAsync(PagedQueryReq<QueryDicCatalogReq> req)
{
return _catalogService.PagedQueryAsync(req);
}
/// <summary>
/// 分页查询字典内容
/// </summary>
public Task<PagedQueryRsp<QueryDicContentRsp>> PagedQueryContentAsync(PagedQueryReq<QueryDicContentReq> req)
{
return _contentService.PagedQueryAsync(req);
}
/// <summary>
/// 查询字典目录
/// </summary>
public Task<IEnumerable<QueryDicCatalogRsp>> QueryCatalogAsync(QueryReq<QueryDicCatalogReq> req)
{
return _catalogService.QueryAsync(req);
}
/// <summary>
/// 查询字典内容
/// </summary>
public Task<IEnumerable<QueryDicContentRsp>> QueryContentAsync(QueryReq<QueryDicContentReq> req)
{
return _contentService.QueryAsync(req);
}
/// <summary>
/// 更新字典目录
/// </summary>
public Task<QueryDicCatalogRsp> UpdateCatalogAsync(UpdateDicCatalogReq req)
{
return _catalogService.UpdateAsync(req);
}
/// <summary>
/// 更新字典内容
/// </summary>
public Task<QueryDicContentRsp> UpdateContentAsync(UpdateDicContentReq req)
{
return _contentService.UpdateAsync(req);
}
}

View File

@ -0,0 +1,47 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IFileService" />
public sealed class FileService : ServiceBase<IFileService>, IFileService
{
private readonly MinioHelper _minioHelper;
private readonly UploadOptions _uploadOptions;
/// <summary>
/// Initializes a new instance of the <see cref="FileService" /> class.
/// </summary>
public FileService(IOptions<UploadOptions> uploadOptions, MinioHelper minioHelper) //
{
_minioHelper = minioHelper;
_uploadOptions = uploadOptions.Value;
}
/// <summary>
/// 文件上传
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">文件不能为空</exception>
/// <exception cref="NetAdminInvalidOperationException">允许上传的文件格式</exception>
/// <exception cref="NetAdminInvalidOperationException">允许的文件大小</exception>
public async Task<string> UploadAsync(IFormFile file)
{
if (file == null || file.Length < 1) {
throw new NetAdminInvalidOperationException(Ln.);
}
if (!_uploadOptions.ContentTypes.Contains(file.ContentType)) {
throw new NetAdminInvalidOperationException(
$"{Ln.允许的文件格式} {string.Join(",", _uploadOptions.ContentTypes)}");
}
if (file.Length > _uploadOptions.MaxSize) {
throw new NetAdminInvalidOperationException($"{Ln.允许的文件大小} {_uploadOptions.MaxSize}");
}
var fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}";
var objectName = $"{UserToken.Id}/{fileName}";
await using var fs = file.OpenReadStream();
return await _minioHelper.UploadAsync(objectName, fs, file.ContentType, file.Length);
}
}

View File

@ -0,0 +1,144 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Menu;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IMenuService" />
public sealed class MenuService : RepositoryService<Sys_Menu, IMenuService>, IMenuService
{
private readonly IUserService _userService;
/// <summary>
/// Initializes a new instance of the <see cref="MenuService" /> class.
/// </summary>
public MenuService(Repository<Sys_Menu> rpo, IUserService userService) //
: base(rpo)
{
_userService = userService;
}
/// <summary>
/// 批量删除菜单
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建菜单
/// </summary>
public async Task<QueryMenuRsp> CreateAsync(CreateMenuReq req)
{
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryMenuRsp>();
}
/// <summary>
/// 删除菜单
/// </summary>
public Task<int> DeleteAsync(DelReq req)
{
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断菜单是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryMenuReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个菜单
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryMenuRsp> GetAsync(QueryMenuReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询菜单
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<PagedQueryRsp<QueryMenuRsp>> PagedQueryAsync(PagedQueryReq<QueryMenuReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 查询菜单
/// </summary>
public async Task<IEnumerable<QueryMenuRsp>> QueryAsync(QueryReq<QueryMenuReq> req)
{
var ret = await QueryInternal(req).ToTreeListAsync();
return ret.Adapt<IEnumerable<QueryMenuRsp>>();
}
/// <summary>
/// 更新菜单
/// </summary>
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryMenuRsp> UpdateAsync(UpdateMenuReq req)
{
if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0) {
throw new NetAdminUnexpectedException();
}
var ret = await Rpo.Select.Where(a => a.Id == req.Id).ToOneAsync();
return ret.Adapt<QueryMenuRsp>();
}
/// <summary>
/// 当前用户菜单
/// </summary>
public async Task<IEnumerable<QueryMenuRsp>> UserMenusAsync()
{
var userInfo = await _userService.UserInfoAsync();
Task<IEnumerable<QueryMenuRsp>> ret;
var req = new QueryReq<QueryMenuReq>();
if (userInfo.Roles.Any(x => x.IgnorePermissionControl)) {
// 忽略权限控制
ret = QueryAsync(req);
}
else {
var ownedMenuIds = userInfo.Roles.SelectMany(x => x.MenuIds);
if (ownedMenuIds.NullOrEmpty()) {
ownedMenuIds = new[] { 0L };
}
ret = QueryAsync(req with {
DynamicFilter = new DynamicFilterInfo {
Field = nameof(QueryMenuReq.Id)
, Operator = DynamicFilterOperator.Any
, Value = ownedMenuIds
}
});
}
return await ret;
}
private ISelect<Sys_Menu> QueryInternal(QueryReq<QueryMenuReq> req)
{
return Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending(a => a.Sort)
.OrderBy(a => a.Name)
.OrderBy(a => a.Id);
}
}

View File

@ -0,0 +1,125 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.RequestLog;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IRequestLogService" />
public sealed class RequestLogService : RepositoryService<Sys_RequestLog, IRequestLogService>, IRequestLogService
{
/// <summary>
/// Initializes a new instance of the <see cref="RequestLogService" /> class.
/// </summary>
public RequestLogService(Repository<Sys_RequestLog> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除请求日志
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建请求日志
/// </summary>
public async Task<QueryRequestLogRsp> CreateAsync(CreateRequestLogReq req)
{
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryRequestLogRsp>();
}
/// <summary>
/// 删除请求日志
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<int> DeleteAsync(DelReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 判断请求日志是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryRequestLogReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个请求日志
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryRequestLogRsp> GetAsync(QueryRequestLogReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询请求日志
/// </summary>
public async Task<PagedQueryRsp<QueryRequestLogRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogReq> req)
{
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
.ToListAsync(a => new {
a.ApiId
, ApiSummary = a.Api.Summary
, a.ExtraData
, a.CreatedClientIp
, a.CreatedTime
, a.CreatedUserName
, a.Duration
, a.Method
, a.CreatedUserAgent
, a.HttpStatusCode
, a.Id
});
return new PagedQueryRsp<QueryRequestLogRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryRequestLogRsp>>());
}
/// <summary>
/// 查询请求日志
/// </summary>
public async Task<IEnumerable<QueryRequestLogRsp>> QueryAsync(QueryReq<QueryRequestLogReq> req)
{
var ret = await QueryInternal(req).Take(req.Count).ToListAsync();
return ret.Adapt<IEnumerable<QueryRequestLogRsp>>();
}
/// <summary>
/// 更新请求日志
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<NopReq> UpdateAsync(NopReq req)
{
throw new NotImplementedException();
}
private ISelect<Sys_RequestLog> QueryInternal(QueryReq<QueryRequestLogReq> req)
{
var ret = Rpo.Select.Include(a => a.Api)
.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
}
}

View File

@ -0,0 +1,133 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Role;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IRoleService" />
public sealed class RoleService : RepositoryService<Sys_Role, IRoleService>, IRoleService
{
/// <summary>
/// Initializes a new instance of the <see cref="RoleService" /> class.
/// </summary>
public RoleService(Repository<Sys_Role> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除角色
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建角色
/// </summary>
public async Task<QueryRoleRsp> CreateAsync(CreateRoleReq req)
{
var entity = req.Adapt<Sys_Role>();
var ret = await Rpo.InsertAsync(entity);
await Rpo.SaveManyAsync(entity, nameof(entity.Depts));
await Rpo.SaveManyAsync(entity, nameof(entity.Menus));
await Rpo.SaveManyAsync(entity, nameof(entity.Apis));
entity = entity with { Id = ret.Id };
return entity.Adapt<QueryRoleRsp>();
}
/// <summary>
/// 删除角色
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">Users_exist_under_this_role_and_deletion_is_not_allowed</exception>
public async Task<int> DeleteAsync(DelReq req)
{
return await Rpo.Orm.Select<Sys_UserRole>().ForUpdate().AnyAsync(a => a.RoleId == req.Id)
? throw new NetAdminInvalidOperationException(Ln.)
: await Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断角色是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryRoleReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个角色
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryRoleRsp> GetAsync(QueryRoleReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 分页查询角色
/// </summary>
public async Task<PagedQueryRsp<QueryRoleRsp>> PagedQueryAsync(PagedQueryReq<QueryRoleReq> req)
{
var list = await QueryInternal(req).Page(req.Page, req.PageSize).Count(out var total).ToListAsync();
return new PagedQueryRsp<QueryRoleRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryRoleRsp>>());
}
/// <summary>
/// 查询角色
/// </summary>
public async Task<IEnumerable<QueryRoleRsp>> QueryAsync(QueryReq<QueryRoleReq> req)
{
var ret = await QueryInternal(req).ToListAsync();
return ret.Adapt<IEnumerable<QueryRoleRsp>>();
}
/// <summary>
/// 更新角色
/// </summary>
public async Task<QueryRoleRsp> UpdateAsync(UpdateRoleReq req)
{
var entity = req.Adapt<Sys_Role>();
_ = await Rpo.UpdateAsync(entity);
await Rpo.SaveManyAsync(entity, nameof(entity.Depts));
await Rpo.SaveManyAsync(entity, nameof(entity.Menus));
await Rpo.SaveManyAsync(entity, nameof(entity.Apis));
return (await QueryAsync(new QueryReq<QueryRoleReq> { Filter = new QueryRoleReq { Id = req.Id } })).First();
}
private ISelect<Sys_Role> QueryInternal(QueryReq<QueryRoleReq> req)
{
var ret = Rpo.Select.IncludeMany(a => a.Depts.Select(b => new Sys_Dept { Id = b.Id }))
.IncludeMany(a => a.Menus.Select(b => new Sys_Menu { Id = b.Id }))
.IncludeMany(a => a.Apis.Select(b => new Sys_Api { Id = b.Id }))
.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Name.Contains(req.Keywords) || a.Summary.Contains(req.Keywords) ||
a.Id == req.Keywords.Int64Try(0))
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.Sort), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.Sort);
}
if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
}
}

View File

@ -0,0 +1,24 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IToolsService" />
public sealed class ToolsService : ServiceBase<IToolsService>, IToolsService
{
/// <summary>
/// 服务器时间
/// </summary>
public DateTime GetServerUtcTime()
{
return DateTime.UtcNow;
}
/// <summary>
/// 版本信息
/// </summary>
public string Version()
{
return Global.ProductVersion;
}
}

View File

@ -0,0 +1,171 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
using NetAdmin.Domain.Dto.Sys.UserProfile;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using DataType = FreeSql.DataType;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IUserProfileService" />
public sealed class UserProfileService : RepositoryService<Sys_UserProfile, IUserProfileService>, IUserProfileService
{
/// <summary>
/// Initializes a new instance of the <see cref="UserProfileService" /> class.
/// </summary>
public UserProfileService(Repository<Sys_UserProfile> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除用户档案
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建用户档案
/// </summary>
public async Task<QueryUserProfileRsp> CreateAsync(CreateUserProfileReq req)
{
var entity = req.Adapt<Sys_UserProfile>();
var ret = await Rpo.InsertAsync(entity);
return ret.Adapt<QueryUserProfileRsp>();
}
/// <summary>
/// 删除用户档案
/// </summary>
public Task<int> DeleteAsync(DelReq req)
{
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断用户档案是否存在
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<bool> ExistAsync(QueryReq<QueryUserProfileReq> req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个用户档案
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public async Task<QueryUserProfileRsp> GetAsync(QueryUserProfileReq req)
{
var ret = await QueryInternal(new QueryReq<QueryUserProfileReq> { Filter = req }).ToOneAsync();
return ret.Adapt<QueryUserProfileRsp>();
}
/// <summary>
/// 分页查询用户档案
/// </summary>
public async Task<PagedQueryRsp<QueryUserProfileRsp>> PagedQueryAsync(PagedQueryReq<QueryUserProfileReq> req)
{
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
.ToListAsync((a, b, c, d, e) => new {
a
, b = new { b.Key, b.Value }
, c = new { c.Key, c.Value }
, d = new { d.Key, d.Value }
, e = new { e.Key, e.Value }
});
return new PagedQueryRsp<QueryUserProfileRsp>(req.Page, req.PageSize, total
, list.ConvertAll(
x => x.a.Adapt<QueryUserProfileRsp>() with {
NationArea = x.b.Adapt<QueryDicContentRsp>()
, CompanyArea = x.c.Adapt<QueryDicContentRsp>()
, HomeArea = x.d.Adapt<QueryDicContentRsp>()
, EmergencyContactArea
= x.e.Adapt<QueryDicContentRsp>()
}));
}
/// <summary>
/// 查询用户档案
/// </summary>
public async Task<IEnumerable<QueryUserProfileRsp>> QueryAsync(QueryReq<QueryUserProfileReq> req)
{
var ret = await QueryInternal(req)
.Take(req.Count)
.ToListAsync((a, b, c, d, e) => new {
a
, b = new { b.Key, b.Value }
, c = new { c.Key, c.Value }
, d = new { d.Key, d.Value }
, e = new { e.Key, e.Value }
});
return ret.ConvertAll(x => x.a.Adapt<QueryUserProfileRsp>() with {
NationArea
= x.b.Key == null
? null
: x.b.Adapt<QueryDicContentRsp>()
, CompanyArea
= x.c.Key == null
? null
: x.c.Adapt<QueryDicContentRsp>()
, HomeArea
= x.d.Key == null
? null
: x.d.Adapt<QueryDicContentRsp>()
, EmergencyContactArea = x.e.Key == null
? null
: x.e.Adapt<QueryDicContentRsp>()
});
}
/// <summary>
/// 更新用户档案
/// </summary>
public async Task<QueryUserProfileRsp> UpdateAsync(UpdateUserProfileReq req)
{
var entity = req.Adapt<Sys_UserProfile>();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(entity);
}
var ret = await Rpo.UpdateDiy.SetSource(entity).ExecuteUpdatedAsync();
return ret.FirstOrDefault()?.Adapt<QueryUserProfileRsp>();
}
private ISelect<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent> QueryInternal(
QueryReq<QueryUserProfileReq> req)
{
return Rpo.Orm.Select<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent>()
.LeftJoin((a, b, _, __, ___) =>
a.NationArea.ToString() == b.Value && b.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
.LeftJoin((a, _, c, __, ___) =>
a.CompanyArea.ToString() == c.Value && c.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
.LeftJoin((a, _, __, d, ___) =>
a.HomeArea.ToString() == d.Value && d.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
.LeftJoin((a, _, __, ___, e) => a.EmergencyContactArea.ToString() == e.Value &&
e.CatalogId == Numbers.DIC_CATALOG_ID_GEO_AREA)
.WhereDynamicFilter(req.DynamicFilter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending((a, _, __, ___, ____) => a.Id);
}
/// <summary>
/// 非sqlite数据库请删掉
/// </summary>
private async Task<QueryUserProfileRsp> UpdateForSqliteAsync(Sys_UserProfile req)
{
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0
? null
: await GetAsync(new QueryUserProfileReq { Id = req.Id });
}
}

View File

@ -0,0 +1,487 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.Attributes.DataValidation;
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Dto.Sys.UserProfile;
using NetAdmin.Domain.Dto.Sys.VerifyCode;
using NetAdmin.Domain.Events.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IUserService" />
public sealed class UserService : RepositoryService<Sys_User, IUserService>, IUserService
{
private readonly IEventPublisher _eventPublisher;
private readonly Expression<Func<Sys_User, Sys_User>> _selectUserFields = a => new Sys_User {
Id = a.Id
, Avatar = a.Avatar
, Email = a.Email
, Mobile = a.Mobile
, Enabled = a.Enabled
, UserName = a.UserName
, Summary = a.Summary
, Version = a.Version
, CreatedTime = a.CreatedTime
, Dept = new Sys_Dept { Id = a.Dept.Id, Name = a.Dept.Name }
, Roles = a.Roles
};
private readonly IUserProfileService _userProfileService;
private readonly IVerifyCodeService _verifyCodeService;
/// <summary>
/// Initializes a new instance of the <see cref="UserService" /> class.
/// </summary>
public UserService(Repository<Sys_User> rpo, IUserProfileService userProfileService
, IVerifyCodeService verifyCodeService, IEventPublisher eventPublisher) //
: base(rpo)
{
_userProfileService = userProfileService;
_verifyCodeService = verifyCodeService;
_eventPublisher = eventPublisher;
}
/// <summary>
/// 批量删除用户
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 检查手机号是否可用
/// </summary>
public async Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
{
return !await Rpo.Select.Where(a => a.Mobile == req.Mobile && a.Id != req.Id).AnyAsync();
}
/// <summary>
/// 检查用户名是否可用
/// </summary>
public async Task<bool> CheckUserNameAvailableAsync(CheckUserNameAvailableReq req)
{
return !await Rpo.Select.Where(a => a.UserName == req.UserName && a.Id != req.Id).AnyAsync();
}
/// <summary>
/// 创建用户
/// </summary>
public async Task<QueryUserRsp> CreateAsync(CreateUserReq req)
{
await CreateUpdateCheckAsync(req);
// 主表
var entity = req.Adapt<Sys_User>();
var dbUser = await Rpo.InsertAsync(entity);
// 分表
await Rpo.SaveManyAsync(entity, nameof(entity.Roles));
// 档案表
_ = await _userProfileService.CreateAsync(req.Profile with { Id = dbUser.Id });
var ret = await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = dbUser.Id } });
return ret.First();
}
/// <summary>
/// 删除用户
/// </summary>
public async Task<int> DeleteAsync(DelReq req)
{
var effect = 0;
// 删除主表
effect += await Rpo.DeleteAsync(req.Id);
// 删除分表
effect += await Rpo.Orm.Delete<Sys_UserRole>(new { UserId = req.Id }).ExecuteAffrowsAsync();
// 删除档案表
effect += await _userProfileService.DeleteAsync(req);
return effect;
}
/// <summary>
/// 判断用户是否存在
/// </summary>
public async Task<bool> ExistAsync(QueryReq<QueryUserReq> req)
{
return await (await QueryInternalAsync(req)).AnyAsync();
}
/// <summary>
/// 获取单个用户
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public Task<QueryUserRsp> GetAsync(QueryUserReq req)
{
throw new NotImplementedException();
}
/// <summary>
/// 获取单个用户(带更新锁)
/// </summary>
public async Task<QueryUserRsp> GetForUpdateAsync(QueryUserReq req)
{
// ReSharper disable once MethodHasAsyncOverload
return (await QueryInternal(new QueryReq<QueryUserReq> { Filter = req }).ForUpdate().ToOneAsync())
.Adapt<QueryUserRsp>();
}
/// <summary>
/// 密码登录
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">用户名或密码错误</exception>
public async Task<LoginRsp> LoginByPwdAsync(LoginByPwdReq req)
{
var pwd = req.Password.Pwd().Guid();
Sys_User dbUser;
#pragma warning disable IDE0045
if (new MobileAttribute().IsValid(req.Account)) {
#pragma warning restore IDE0045
dbUser = await Rpo.GetAsync(a => a.Mobile == req.Account && a.Password == pwd);
}
else {
dbUser = new EmailAddressAttribute().IsValid(req.Account)
? await Rpo.GetAsync(a => a.Email == req.Account && a.Password == pwd)
: await Rpo.GetAsync(a => a.UserName == req.Account && a.Password == pwd);
}
return dbUser == null ? throw new NetAdminInvalidOperationException(Ln.) : LoginInternal(dbUser);
}
/// <summary>
/// 短信登录
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">验证码不正确</exception>
/// <exception cref="NetAdminInvalidOperationException">用户不存在</exception>
public async Task<LoginRsp> LoginBySmsAsync(LoginBySmsReq req)
{
if (!await _verifyCodeService.VerifyAsync(req.Adapt<VerifySmsCodeReq>())) {
throw new NetAdminInvalidOperationException(Ln.);
}
var dbUser = await Rpo.GetAsync(a => a.Mobile == req.DestDevice);
return dbUser == null ? throw new NetAdminInvalidOperationException(Ln.) : LoginInternal(dbUser);
}
/// <summary>
/// 分页查询用户
/// </summary>
public async Task<PagedQueryRsp<QueryUserRsp>> PagedQueryAsync(PagedQueryReq<QueryUserReq> req)
{
var list = await (await QueryInternalAsync(req)).Page(req.Page, req.PageSize)
.Count(out var total)
.ToListAsync(_selectUserFields);
return new PagedQueryRsp<QueryUserRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryUserRsp>>());
}
/// <summary>
/// 查询用户
/// </summary>
public async Task<IEnumerable<QueryUserRsp>> QueryAsync(QueryReq<QueryUserReq> req)
{
var list = await (await QueryInternalAsync(req)).Take(req.Count).ToListAsync(_selectUserFields);
return list.Adapt<IEnumerable<QueryUserRsp>>();
}
/// <summary>
/// 查询用户档案
/// </summary>
public Task<IEnumerable<QueryUserProfileRsp>> QueryProfileAsync(QueryReq<QueryUserProfileReq> req)
{
return _userProfileService.QueryAsync(req);
}
/// <summary>
/// 注册用户
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">验证码不正确</exception>
public async Task<UserInfoRsp> RegisterAsync(RegisterUserReq req)
{
if (!await _verifyCodeService.VerifyAsync(req.VerifySmsCodeReq)) {
throw new NetAdminInvalidOperationException(Ln.);
}
var createReq = req.Adapt<CreateUserReq>() with { Profile = new CreateUserProfileReq() };
return (await CreateAsync(createReq)).Adapt<UserInfoRsp>();
}
/// <summary>
/// 重设密码
/// </summary>
/// <exception cref="NetAdminInvalidOperationException">验证码不正确</exception>
/// <exception cref="NetAdminInvalidOperationException">用户不存在</exception>
public async Task<uint> ResetPasswordAsync(ResetPasswordReq req)
{
return !await _verifyCodeService.VerifyAsync(req.VerifySmsCodeReq)
? throw new NetAdminInvalidOperationException(Ln.)
: (uint)await Rpo.UpdateDiy
.SetSource((await Rpo.Where(a => a.Mobile == req.VerifySmsCodeReq.DestDevice)
.ToOneAsync(a => new { a.Version, a.Id })).Adapt<Sys_User>() with {
Password = req.PasswordText.Pwd().Guid()
})
.UpdateColumns(a => a.Password)
.ExecuteAffrowsAsync();
}
/// <inheritdoc />
public async Task<UserInfoRsp> SetAvatarAsync(SetAvatarReq req)
{
if (await Rpo.UpdateDiy
.SetSource(req with {
Id = UserToken.Id
, Version = Rpo.Where(a => a.Id == UserToken.Id).ToOne(a => a.Version)
})
.UpdateColumns(a => a.Avatar)
.ExecuteAffrowsAsync() <= 0) {
throw new NetAdminUnexpectedException();
}
var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = UserToken.Id } }))
.First()
.Adapt<UserInfoRsp>();
// 发布用户更新事件
await _eventPublisher.PublishAsync(new UserUpdatedEvent(ret));
return ret;
}
/// <inheritdoc />
public async Task<UserInfoRsp> SetEmailAsync(SetEmailReq req)
{
var user = Rpo.Where(a => a.Id == UserToken.Id).ToOne(a => new { a.Mobile, a.Version, a.Email });
// 如果已绑定手机号、需要手机安全验证
if (!user.Mobile.NullOrEmpty()) {
if (!await _verifyCodeService.VerifyAsync(req.VerifySmsCodeReq)) {
throw new NetAdminInvalidOperationException(Ln.);
}
if (user.Mobile != req.VerifySmsCodeReq.DestDevice) {
throw new NetAdminInvalidOperationException($"{Ln.手机号码} {Ln.不正确}");
}
}
if (await Rpo.UpdateDiy
.SetSource(new Sys_User { Email = req.DestDevice, Id = UserToken.Id, Version = user.Version })
.UpdateColumns(a => a.Email)
.ExecuteAffrowsAsync() <= 0) {
throw new NetAdminUnexpectedException();
}
var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = UserToken.Id } }))
.First()
.Adapt<UserInfoRsp>();
// 发布用户更新事件
await _eventPublisher.PublishAsync(new UserUpdatedEvent(ret));
return ret;
}
/// <inheritdoc />
public async Task<UserInfoRsp> SetMobileAsync(SetMobileReq req)
{
var user = await Rpo.Where(a => a.Id == UserToken.Id).ToOneAsync(a => new { a.Version, a.Mobile });
if (!user.Mobile.NullOrEmpty()) {
// 已有手机号,需验证旧手机
if (!await _verifyCodeService.VerifyAsync(req.OriginVerifySmsCodeReq)) {
throw new NetAdminInvalidOperationException($"{Ln.旧手机号码} {Ln.验证码不正确}");
}
if (user.Mobile != req.OriginVerifySmsCodeReq.DestDevice) {
throw new NetAdminInvalidOperationException($"{Ln.旧手机号码} {Ln.不正确}");
}
}
// 验证新手机号
if (!await _verifyCodeService.VerifyAsync(req.NewVerifySmsCodeReq)) {
throw new NetAdminInvalidOperationException($"{Ln.新手机号码} {Ln.验证码不正确}");
}
if (await Rpo.UpdateDiy
.SetSource(new Sys_User {
Version = user.Version
, Id = UserToken.Id
, Mobile = req.NewVerifySmsCodeReq.DestDevice
})
.UpdateColumns(a => a.Mobile)
.ExecuteAffrowsAsync() <= 0) {
throw new NetAdminUnexpectedException();
}
var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = UserToken.Id } }))
.First()
.Adapt<UserInfoRsp>();
// 发布用户更新事件
await _eventPublisher.PublishAsync(new UserUpdatedEvent(ret));
return ret;
}
/// <inheritdoc />
public async Task<uint> SetPasswordAsync(SetPasswordReq req)
{
var version = await Rpo.Where(a => a.Id == UserToken.Id && a.Password == req.OldPassword.Pwd().Guid())
.ToOneAsync(a => new long?(a.Version));
if (version == null) {
throw new NetAdminInvalidInputException($"{Ln.旧密码} {Ln.不正确}");
}
var ret = await Rpo.UpdateDiy
.SetSource(new Sys_User {
Id = UserToken.Id
, Password = req.NewPassword.Pwd().Guid()
, Version = version.Value
})
.UpdateColumns(a => a.Password)
.ExecuteAffrowsAsync();
return ret <= 0 ? throw new NetAdminUnexpectedException() : (uint)ret;
}
/// <summary>
/// 更新用户
/// </summary>
public async Task<QueryUserRsp> UpdateAsync(UpdateUserReq req)
{
await CreateUpdateCheckAsync(req);
// 主表
var entity = req.Adapt<Sys_User>();
var ignoreCols = new List<string> { nameof(Sys_User.Token) };
if (entity.Password == Guid.Empty) {
ignoreCols.Add(nameof(Sys_User.Password));
}
_ = await Rpo.UpdateDiy.SetSource(entity).IgnoreColumns(ignoreCols.ToArray()).ExecuteAffrowsAsync();
// 档案表
_ = await _userProfileService.UpdateAsync(req.Profile);
// 分表
await Rpo.SaveManyAsync(entity, nameof(entity.Roles));
var ret = (await QueryAsync(new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = req.Id } })).First();
// 发布用户更新事件
await _eventPublisher.PublishAsync(new UserUpdatedEvent(ret.Adapt<UserInfoRsp>()));
return ret;
}
/// <summary>
/// 单体更新
/// </summary>
public Task UpdateSingleAsync(UpdateUserReq req)
{
return Rpo.UpdateAsync(req);
}
/// <summary>
/// 当前用户信息
/// </summary>
public async Task<UserInfoRsp> UserInfoAsync()
{
var dbUser = await Rpo.Where(a => a.Token == UserToken.Token && a.Enabled)
.Include(a => a.Dept)
.IncludeMany( //
a => a.Roles
, then => then.Where(a => a.Enabled)
.IncludeMany(a => a.Menus)
.IncludeMany(a => a.Depts)
.IncludeMany(a => a.Apis))
.ToOneAsync();
return dbUser.Adapt<UserInfoRsp>();
}
private static LoginRsp LoginInternal(IFieldEnabled dbUser)
{
if (!dbUser.Enabled) {
throw new NetAdminInvalidOperationException(Ln.);
}
var tokenPayload
= new Dictionary<string, object> { { nameof(ContextUserToken), dbUser.Adapt<ContextUserToken>() } };
var accessToken = JWTEncryption.Encrypt(tokenPayload);
return new LoginRsp {
AccessToken = accessToken
, RefreshToken = JWTEncryption.GenerateRefreshToken(accessToken)
};
}
private async Task CreateUpdateCheckAsync(CreateUpdateUserReq req)
{
// 检查角色是否存在
var roles = await Rpo.Orm.Select<Sys_Role>()
.ForUpdate()
.Where(a => req.RoleIds.Contains(a.Id))
.ToListAsync(a => a.Id);
if (roles.Count != req.RoleIds.Count) {
throw new NetAdminInvalidOperationException(Ln.);
}
// 检查部门是否存在
var dept = await Rpo.Orm.Select<Sys_Dept>().ForUpdate().Where(a => req.DeptId == a.Id).ToListAsync(a => a.Id);
if (dept.Count != 1) {
throw new NetAdminInvalidOperationException(Ln.);
}
}
private ISelect<Sys_User> QueryInternal(QueryReq<QueryUserReq> req)
{
return Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending(a => a.Id);
}
private async Task<ISelect<Sys_User>> QueryInternalAsync(QueryReq<QueryUserReq> req)
{
IEnumerable<long> deptIds = null;
if (req.Filter?.DeptId > 0) {
deptIds = await Rpo.Orm.Select<Sys_Dept>()
.Where(a => a.Id == req.Filter.DeptId)
.AsTreeCte()
.ToListAsync(a => a.Id);
}
var ret = Rpo.Select.Include(a => a.Dept)
.IncludeMany(a => a.Roles.Select(b => new Sys_Role { Id = b.Id, Name = b.Name }))
.WhereDynamicFilter(req.DynamicFilter)
.WhereIf(deptIds != null, a => deptIds.Contains(a.DeptId))
.WhereIf( //
req.Filter?.Id > 0, a => a.Id == req.Filter.Id)
.WhereIf( //
req.Filter?.RoleId > 0, a => a.Roles.Any(b => b.Id == req.Filter.RoleId))
.WhereIf( //
req.Keywords?.Length > 0
, a => a.UserName.Contains(req.Keywords) || a.Id == req.Keywords.Int64Try(0) ||
a.Mobile.Contains(req.Keywords) || a.Email.Contains(req.Keywords) ||
a.Summary.Contains(req.Keywords))
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
}
}

View File

@ -0,0 +1,194 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.VerifyCode;
using NetAdmin.Domain.Enums.Sys;
using NetAdmin.Domain.Events.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using DataType = FreeSql.DataType;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IVerifyCodeService" />
public sealed class VerifyCodeService : RepositoryService<Sys_VerifyCode, IVerifyCodeService>, IVerifyCodeService
{
private readonly IEventPublisher _eventPublisher;
/// <summary>
/// Initializes a new instance of the <see cref="VerifyCodeService" /> class.
/// </summary>
public VerifyCodeService(Repository<Sys_VerifyCode> rpo, IEventPublisher eventPublisher) //
: base(rpo)
{
_eventPublisher = eventPublisher;
}
/// <summary>
/// 批量删除验证码
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建验证码
/// </summary>
public async Task<QueryVerifyCodeRsp> CreateAsync(CreateVerifyCodeReq req)
{
var entity = await Rpo.InsertAsync(req);
var ret = entity.Adapt<QueryVerifyCodeRsp>();
// 发布验证码创建事件
await _eventPublisher.PublishAsync(new VerifyCodeCreatedEvent(ret));
return ret;
}
/// <summary>
/// 删除验证码
/// </summary>
public Task<int> DeleteAsync(DelReq req)
{
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断验证码是否存在
/// </summary>
public Task<bool> ExistAsync(QueryReq<QueryVerifyCodeReq> req)
{
return QueryInternal(req).AnyAsync();
}
/// <summary>
/// 获取单个验证码
/// </summary>
public async Task<QueryVerifyCodeRsp> GetAsync(QueryVerifyCodeReq req)
{
var ret = await QueryInternal(new QueryReq<QueryVerifyCodeReq> { Filter = req }).ToOneAsync();
return ret.Adapt<QueryVerifyCodeRsp>();
}
/// <summary>
/// 分页查询验证码
/// </summary>
public async Task<PagedQueryRsp<QueryVerifyCodeRsp>> PagedQueryAsync(PagedQueryReq<QueryVerifyCodeReq> req)
{
var list = await QueryInternal(req).Page(req.Page, req.PageSize).Count(out var total).ToListAsync();
return new PagedQueryRsp<QueryVerifyCodeRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryVerifyCodeRsp>>());
}
/// <summary>
/// 查询验证码
/// </summary>
public async Task<IEnumerable<QueryVerifyCodeRsp>> QueryAsync(QueryReq<QueryVerifyCodeReq> req)
{
var ret = await QueryInternal(req).Take(req.Count).ToListAsync();
return ret.Adapt<IEnumerable<QueryVerifyCodeRsp>>();
}
/// <inheritdoc />
public async Task<SendVerifyCodeRsp> SendVerifyCodeAsync(SendVerifyCodeReq req)
{
var lastSent = await GetLastSentAsync(req.DestDevice);
QueryVerifyCodeRsp ret;
#if !DEBUG
// 有发送记录且小于1分钟不允许
if (lastSent != null && (DateTime.UtcNow - lastSent.CreatedTime).TotalMinutes < 1) {
throw new NetAdminInvalidOperationException(Ln._1分钟内只能发送1次);
}
#endif
if (lastSent != null && lastSent.Status != VerifyCodeStatues.Verified) { // 上次发送未验证生成相同code
ret = await CreateAsync(req.Adapt<CreateVerifyCodeReq>() with { Code = lastSent.Code });
}
else { // 生成新的code
var code = new[] { 0, 10000 }.Rand().ToString(CultureInfo.InvariantCulture).PadLeft(4, '0');
ret = await CreateAsync(req.Adapt<CreateVerifyCodeReq>() with { Code = code });
}
return ret.Adapt<SendVerifyCodeRsp>();
}
/// <summary>
/// 更新验证码
/// </summary>
public async Task<QueryVerifyCodeRsp> UpdateAsync(UpdateVerifyCodeReq req)
{
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req);
}
var ret = await Rpo.UpdateDiy.SetSource(req).ExecuteUpdatedAsync();
return ret.FirstOrDefault()?.Adapt<QueryVerifyCodeRsp>();
}
/// <inheritdoc />
public async Task<bool> VerifyAsync(VerifyCodeReq req)
{
#if DEBUG
if (req.Code == "8888") {
return true;
}
#endif
if (req.Code == Global.SecretKey) {
return true;
}
var lastSent = await GetLastSentAsync(req.DestDevice);
if (lastSent is not { Status: VerifyCodeStatues.Sent } || req.Code != lastSent.Code ||
(DateTime.UtcNow - lastSent.CreatedTime).TotalMinutes > 10) {
return false;
}
_ = await UpdateAsync((lastSent with { Status = VerifyCodeStatues.Verified }).Adapt<UpdateVerifyCodeReq>());
return true;
}
private Task<Sys_VerifyCode> GetLastSentAsync(string destDevice)
{
return QueryInternal(new QueryReq<QueryVerifyCodeReq> {
Count = 1
, DynamicFilter
= new DynamicFilterInfo {
Field = nameof(
Sys_VerifyCode.DestDevice)
, Operator = DynamicFilterOperator.Eq
, Value = destDevice
}
})
.ToOneAsync();
}
private ISelect<Sys_VerifyCode> QueryInternal(QueryReq<QueryVerifyCodeReq> req)
{
return Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending(a => a.Id);
}
/// <summary>
/// 非sqlite数据库请删掉
/// </summary>
private async Task<QueryVerifyCodeRsp> UpdateForSqliteAsync(Sys_VerifyCode req)
{
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0
? null
: await GetAsync(new QueryVerifyCodeReq { Id = req.Id });
}
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Tpl;
namespace NetAdmin.SysComponent.Application.Services.Tpl.Dependency;
/// <summary>
/// 示例服务
/// </summary>
public interface IExampleService : IService, IExampleModule { }

View File

@ -0,0 +1,117 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Tpl;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Tpl.Example;
using NetAdmin.SysComponent.Application.Services.Tpl.Dependency;
using DataType = FreeSql.DataType;
namespace NetAdmin.SysComponent.Application.Services.Tpl;
/// <inheritdoc cref="IExampleService" />
public sealed class ExampleService : RepositoryService<Tpl_Example, IExampleService>, IExampleService
{
/// <summary>
/// Initializes a new instance of the <see cref="ExampleService" /> class.
/// </summary>
public ExampleService(Repository<Tpl_Example> rpo) //
: base(rpo) { }
/// <summary>
/// 批量删除示例
/// </summary>
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item);
}
return sum;
}
/// <summary>
/// 创建示例
/// </summary>
public async Task<QueryExampleRsp> CreateAsync(CreateExampleReq req)
{
var ret = await Rpo.InsertAsync(req);
return ret.Adapt<QueryExampleRsp>();
}
/// <summary>
/// 删除示例
/// </summary>
public Task<int> DeleteAsync(DelReq req)
{
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <summary>
/// 判断示例是否存在
/// </summary>
public Task<bool> ExistAsync(QueryReq<QueryExampleReq> req)
{
return QueryInternal(req).AnyAsync();
}
/// <summary>
/// 获取单个示例
/// </summary>
public async Task<QueryExampleRsp> GetAsync(QueryExampleReq req)
{
var ret = await QueryInternal(new QueryReq<QueryExampleReq> { Filter = req }).ToOneAsync();
return ret.Adapt<QueryExampleRsp>();
}
/// <summary>
/// 分页查询示例
/// </summary>
public async Task<PagedQueryRsp<QueryExampleRsp>> PagedQueryAsync(PagedQueryReq<QueryExampleReq> req)
{
var list = await QueryInternal(req).Page(req.Page, req.PageSize).Count(out var total).ToListAsync();
return new PagedQueryRsp<QueryExampleRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryExampleRsp>>());
}
/// <summary>
/// 查询示例
/// </summary>
public async Task<IEnumerable<QueryExampleRsp>> QueryAsync(QueryReq<QueryExampleReq> req)
{
var ret = await QueryInternal(req).Take(req.Count).ToListAsync();
return ret.Adapt<IEnumerable<QueryExampleRsp>>();
}
/// <summary>
/// 更新示例
/// </summary>
public async Task<QueryExampleRsp> UpdateAsync(UpdateExampleReq req)
{
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req);
}
var ret = await Rpo.UpdateDiy.SetSource(req).ExecuteUpdatedAsync();
return ret.FirstOrDefault()?.Adapt<QueryExampleRsp>();
}
private ISelect<Tpl_Example> QueryInternal(QueryReq<QueryExampleReq> req)
{
return Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
.OrderByDescending(a => a.Id);
}
/// <summary>
/// 非sqlite数据库请删掉
/// </summary>
private async Task<QueryExampleRsp> UpdateForSqliteAsync(Tpl_Example req)
{
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync() <= 0
? null
: await GetAsync(new QueryExampleReq { Id = req.Id });
}
}