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,69 @@
// ReSharper disable RedundantUsingDirective.Global
global using System;
global using System.Collections;
global using System.Collections.Generic;
global using System.Collections.Immutable;
global using System.ComponentModel;
global using System.ComponentModel.DataAnnotations;
global using System.Data;
global using System.Diagnostics;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Linq.Expressions;
global using System.Net;
global using System.Reflection;
global using System.Text;
global using System.Text.Encodings.Web;
global using System.Text.Json;
global using System.Text.Json.Serialization;
global using System.Text.Json.Serialization.Metadata;
global using System.Text.RegularExpressions;
global using System.Threading.Tasks;
global using FreeSql;
global using FreeSql.Aop;
global using FreeSql.DataAnnotations;
global using FreeSql.Internal.Model;
global using Furion;
global using Furion.Authorization;
global using Furion.ConfigurableOptions;
global using Furion.DataEncryption;
global using Furion.DataValidation;
global using Furion.DependencyInjection;
global using Furion.DynamicApiController;
global using Furion.EventBus;
global using Furion.SpecificationDocument;
global using Furion.UnifyResult;
global using Mapster;
global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Diagnostics;
global using Microsoft.AspNetCore.Http;
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.AspNetCore.Mvc.ActionConstraints;
global using Microsoft.AspNetCore.Mvc.Controllers;
global using Microsoft.AspNetCore.Mvc.Filters;
global using Microsoft.AspNetCore.Mvc.Infrastructure;
global using Microsoft.Extensions.Caching.Distributed;
global using Microsoft.Extensions.Caching.Memory;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options;
global using NetAdmin.Infrastructure;
global using NetAdmin.Infrastructure.Attributes;
global using NetAdmin.Infrastructure.Configuration.Options;
global using NetAdmin.Infrastructure.Configuration.Options.SubNodes.Redis;
global using NetAdmin.Infrastructure.Configuration.Options.SubNodes.Upload;
global using NetAdmin.Infrastructure.Constant;
global using NetAdmin.Infrastructure.Enums;
global using NetAdmin.Infrastructure.Exceptions;
global using NetAdmin.Infrastructure.Exceptions.InvalidInput;
global using NetAdmin.Infrastructure.Exceptions.InvalidOperation;
global using NetAdmin.Infrastructure.Exceptions.Unexpected;
global using NetAdmin.Infrastructure.Extensions;
global using NetAdmin.Infrastructure.Languages;
global using NetAdmin.Infrastructure.Utils;
global using NSExt.Attributes;
global using NSExt.Extensions;

View File

@ -0,0 +1,64 @@
using NetAdmin.Domain;
using NetAdmin.Domain.Dto.Dependency;
namespace NetAdmin.Application.Modules;
/// <summary>
/// 增删改查模块接口
/// </summary>
/// <typeparam name="TCreateReq">创建请求类型</typeparam>
/// <typeparam name="TCreateRsp">创建响应类型</typeparam>
/// <typeparam name="TQueryReq">查询请求类型</typeparam>
/// <typeparam name="TQueryRsp">查询响应类型</typeparam>
/// <typeparam name="TUpdateReq">修改请求类型</typeparam>
/// <typeparam name="TUpdateRsp">修改响应类型</typeparam>
/// <typeparam name="TDelReq">删除请求类型</typeparam>
public interface ICrudModule<in TCreateReq, TCreateRsp, TQueryReq, TQueryRsp, in TUpdateReq, TUpdateRsp, TDelReq>
where TCreateReq : DataAbstraction, new()
where TCreateRsp : DataAbstraction
where TQueryReq : DataAbstraction, new()
where TQueryRsp : DataAbstraction
where TUpdateReq : DataAbstraction, new()
where TUpdateRsp : DataAbstraction
where TDelReq : DataAbstraction, new()
{
/// <summary>
/// 批量删除实体
/// </summary>
Task<int> BulkDeleteAsync(BulkReq<TDelReq> req);
/// <summary>
/// 创建实体
/// </summary>
Task<TCreateRsp> CreateAsync(TCreateReq req);
/// <summary>
/// 删除实体
/// </summary>
Task<int> DeleteAsync(TDelReq req);
/// <summary>
/// 判断实体是否存在
/// </summary>
Task<bool> ExistAsync(QueryReq<TQueryReq> req);
/// <summary>
/// 获取单个实体
/// </summary>
Task<TQueryRsp> GetAsync(TQueryReq req);
/// <summary>
/// 分页查询实体
/// </summary>
Task<PagedQueryRsp<TQueryRsp>> PagedQueryAsync(PagedQueryReq<TQueryReq> req);
/// <summary>
/// 查询实体
/// </summary>
Task<IEnumerable<TQueryRsp>> QueryAsync(QueryReq<TQueryReq> req);
/// <summary>
/// 更新实体
/// </summary>
Task<TUpdateRsp> UpdateAsync(TUpdateReq req);
}

View File

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

View File

@ -0,0 +1,39 @@
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Application.Repositories;
/// <summary>
/// 基础仓储接口
/// </summary>
public interface IRepository<TEntity> : IBaseRepository<TEntity>
where TEntity : EntityBase
{
/// <summary>
/// 当前上下文关联的用户
/// </summary>
ContextUserToken UserToken { get; }
/// <summary>
/// 递归删除
/// </summary>
/// <param name="exp">exp</param>
/// <param name="disableGlobalFilterNames">禁用全局过滤器名</param>
Task<bool> DeleteRecursiveAsync(Expression<Func<TEntity, bool>> exp, params string[] disableGlobalFilterNames);
/// <summary>
/// 获得Dto
/// </summary>
/// <param name="id">主键</param>
Task<TDto> GetAsync<TDto>(long id);
/// <summary>
/// 根据条件获取Dto
/// </summary>
Task<TDto> GetAsync<TDto>(Expression<Func<TEntity, bool>> exp);
/// <summary>
/// 根据条件获取实体
/// </summary>
Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> exp);
}

View File

@ -0,0 +1,82 @@
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Application.Repositories;
/// <inheritdoc cref="IRepository{TEntity}" />
public sealed class Repository<TEntity> : DefaultRepository<TEntity, long>, IRepository<TEntity>
where TEntity : EntityBase
{
/// <summary>
/// Initializes a new instance of the <see cref="Repository{TEntity}" /> class.
/// </summary>
public Repository(IFreeSql fSql, UnitOfWorkManager uowManger, ContextUserToken userToken) //
: base(fSql, uowManger)
{
UserToken = userToken;
}
/// <summary>
/// 当前上下文关联的用户
/// </summary>
public ContextUserToken UserToken { get; }
/// <summary>
/// 递归删除
/// </summary>
/// <param name="exp">exp</param>
/// <param name="disableGlobalFilterNames">禁用全局过滤器名</param>
public async Task<bool> DeleteRecursiveAsync( //
Expression<Func<TEntity, bool>> exp, params string[] disableGlobalFilterNames)
{
_ = await Select.Where(exp)
.DisableGlobalFilter(disableGlobalFilterNames)
.AsTreeCte()
.ToDelete()
.ExecuteAffrowsAsync();
return true;
}
/// <summary>
/// 获得Dto
/// </summary>
/// <param name="id">主键</param>
public Task<TDto> GetAsync<TDto>(long id)
{
return Select.WhereDynamic(id).ToOneAsync<TDto>();
}
/// <summary>
/// 根据条件获取Dto
/// </summary>
public Task<TDto> GetAsync<TDto>(Expression<Func<TEntity, bool>> exp)
{
return Select.Where(exp).ToOneAsync<TDto>();
}
/// <summary>
/// 根据条件获取实体
/// </summary>
public Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> exp)
{
return Select.Where(exp).ToOneAsync();
}
/// <summary>
/// 获取分页列表
/// </summary>
/// <param name="dynamicFilterInfo">动态过滤器</param>
/// <param name="page">页码</param>
/// <param name="pageSize">页容量</param>
/// <returns>分页列表和总条数</returns>
public async Task<(IEnumerable<TEntity> List, long Total)> GetPagedListAsync(
DynamicFilterInfo dynamicFilterInfo, int page, int pageSize)
{
var list = await Select.WhereDynamicFilter(dynamicFilterInfo)
.Count(out var total)
.Page(page, pageSize)
.ToListAsync();
return (list, total);
}
}

View File

@ -0,0 +1,19 @@
using NetAdmin.Domain.Contexts;
namespace NetAdmin.Application.Services;
/// <summary>
/// 服务接口
/// </summary>
public interface IService
{
/// <summary>
/// 服务编号
/// </summary>
Guid ServiceId { get; set; }
/// <summary>
/// 上下文用户
/// </summary>
ContextUserToken UserToken { get; set; }
}

View File

@ -0,0 +1,34 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Application.Services;
/// <summary>
/// 仓储服务基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TLogger">日志类型</typeparam>
public abstract class RepositoryService<TEntity, TLogger> : ServiceBase<TLogger>
where TEntity : EntityBase
{
/// <summary>
/// Initializes a new instance of the <see cref="RepositoryService{TEntity, TLogger}" /> class.
/// </summary>
protected RepositoryService(Repository<TEntity> rpo) //
{
Rpo = rpo;
}
/// <summary>
/// 默认仓储
/// </summary>
protected Repository<TEntity> Rpo { get; }
/// <summary>
/// 启用级联保存
/// </summary>
protected bool EnableCascadeSave {
get => Rpo.DbContextOptions.EnableCascadeSave;
set => Rpo.DbContextOptions.EnableCascadeSave = value;
}
}

View File

@ -0,0 +1,45 @@
using NetAdmin.Domain.Contexts;
namespace NetAdmin.Application.Services;
/// <inheritdoc />
public abstract class ServiceBase<TLogger> : ServiceBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBase{TLogger}" /> class.
/// </summary>
protected ServiceBase() //
{
Logger = App.GetService<ILogger<TLogger>>();
}
/// <summary>
/// 日志记录器
/// </summary>
protected ILogger<TLogger> Logger { get; }
}
/// <summary>
/// 服务基类
/// </summary>
public abstract class ServiceBase : IScoped, IService
{
/// <summary>
/// Initializes a new instance of the <see cref="ServiceBase" /> class.
/// </summary>
protected ServiceBase()
{
UserToken = App.GetService<ContextUserToken>();
ServiceId = Guid.NewGuid();
}
/// <summary>
/// 服务编号
/// </summary>
public Guid ServiceId { get; set; }
/// <summary>
/// 上下文用户
/// </summary>
public ContextUserToken UserToken { get; set; }
}

View File

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

View File

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

View File

@ -0,0 +1,39 @@
using NetAdmin.BizServer.Host.Filters;
using NetAdmin.Domain.Contexts;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.Enums.Sys;
using NetAdmin.Host.Extensions;
namespace NetAdmin.BizServer.Host.Extensions;
/// <summary>
/// ServiceCollection 扩展方法
/// </summary>
[SuppressSniffer]
public static class ServiceCollectionExtensions
{
/// <summary>
/// 注册FreeSql
/// </summary>
public static IServiceCollection AddFreeSql(this IServiceCollection me)
{
_ = me.AddFreeSql( //
FreeSqlInitOptions.SyncStructure | FreeSqlInitOptions.InsertSeedData, freeSql => {
// 数据权限过滤器
_ = freeSql.GlobalFilter.ApplyOnlyIf<IFieldOwner>( //
Chars.FLG_GLOBAL_FILTER_DATA
, () => ContextUserInfo.Create()?.Roles.All(x => x.DataScope == DataScopes.Self) ?? false
, a => a.OwnerId == ContextUserInfo.Create().Id);
});
return me;
}
/// <summary>
/// jwt授权处理器
/// </summary>
public static IServiceCollection AddJwt(this IServiceCollection me)
{
_ = me.AddJwt<JwtHandler>(enableGlobalAuthorize: true);
return me;
}
}

View File

@ -0,0 +1,48 @@
using NetAdmin.Domain.Contexts;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.BizServer.Host.Filters;
/// <inheritdoc />
[SuppressSniffer]
public sealed class JwtHandler : AppAuthorizeHandler
{
/// <summary>
/// Initializes a new instance of the <see cref="JwtHandler" /> class.
/// </summary>
public JwtHandler() { }
/// <summary>
/// 验证管道
/// </summary>
public override async Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
{
// 无法从token中获取context user拒绝访问
if (App.GetService<ContextUserToken>() == null) {
return false;
}
// 数据库不存在context user或用户已被禁用拒绝访问
var userInfo = await App.GetService<IUserCache>().UserInfoAsync();
if (userInfo?.Roles == null) {
return false;
}
httpContext.Items.Add(Chars.FLG_CONTEXT_USER_INFO, userInfo);
var path = httpContext.Request.Path.Value!.TrimStart('/');
foreach (var role in userInfo.Roles) {
// 忽略权限控制,直接通过
if (role.IgnorePermissionControl) {
return true;
}
// 获取所属角色接口权限 进行核对
if (role.ApiIds?.Contains(path) ?? false) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="$(SolutionDir)/CodeQuality.props"/>
<ItemGroup>
<EmbeddedResource Include="$(SolutionDir)/assets/captcha/**" LinkBase="Assets/Captcha"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../NetAdmin.SysComponent.Host/NetAdmin.SysComponent.Host.csproj"/>
<ProjectReference Include="../NetAdmin.BizServer.Cache/NetAdmin.BizServer.Cache.csproj"/>
</ItemGroup>
<ItemGroup>
<None Update="*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"NetAdmin.BizServer.Host": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "http://localhost:65010",
"applicationUrl": "http://[::]:65010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,67 @@
using NetAdmin.BizServer.Host.Extensions;
using NetAdmin.Host.Extensions;
using NetAdmin.Host.Middlewares;
using Prometheus;
namespace NetAdmin.BizServer.Host;
/// <summary>
/// 启动类
/// </summary>
public sealed class Startup : NetAdmin.Host.Startup
{
/// <summary>
/// 程序入口
/// </summary>
public static void Main(string[] args)
{
ShowBanner();
_ = Serve.Run(RunOptions.Default.WithArgs(args));
}
/// <summary>
/// 配置应用程序中间件
/// </summary>
public void Configure(IApplicationBuilder app)
{
_ = app //
.UseRealIp() // 获取真实IP
.EnableBuffering() // 启用 Body 重读
.UseMiddleware<RequestAuditMiddleware>() // 请求审计
#if DEBUG
.UseOpenApiSkin() // Swagger皮肤
#endif
.UseInject(string.Empty) // / Furion脚手架
.UseUnifyResultStatusCodes() // 状态码拦截
.UseCorsAccessor() // 跨域访问
.UseRouting() // 路由映射
.UseHttpMetrics() // 性能监控
.UseAuthentication() // / 认证
.UseAuthorization() // 授权
.UseMiddleware<RemoveNullNodeMiddleware>() // 删除json空节点
.UseEndpoints(); // 执行匹配的端点
}
/// <summary>
/// 配置服务容器
/// </summary>
public void ConfigureServices(IServiceCollection services)
{
_ = services.AddConsoleFormatter() // 控制台日志模板
.AddAllOptions() // 注册配置项
.AddJwt() // Jwt 授权处理器
.AddSnowflake() // 雪花编号生成器
.AddEventBus() // 事件总线
.AddFreeSql() // freeSql
.AddRemoteRequest() // 注册远程请求
.AddCorsAccessor() // 支持跨域访问
.AddContextUser() // 上下文用户
.AddRedisCache() // Redis缓存
// IMvcBuilder
.AddControllers() // 注册控制器
.AddJsonSerializer(true) // json序列化配置
.AddDefaultApiResultHandler() // Api结果处理器
;
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,47 @@
{
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
"SpecificationDocumentSettings": {
"GroupOpenApiInfos": [
{
"Group": "Sys",
"Title": "系统组件",
"Description": "NetAdmin - 系统组件",
},
{
"Group": "Biz",
"Title": "业务服务",
"Description": "NetAdmin - 业务服务",
},
{
"Group": "Tpl",
"Title": "示例服务",
"Description": "NetAdmin - 示例服务",
"Visible": false
},
{
"Group": "Health",
"Visible": false
}
],
"SecurityDefinitions": [
{
"Id": "Bearer",
"Type": "ApiKey",
"Name": "Authorization",
"Description": "JWT Authorization header using the Bearer scheme.",
"BearerFormat": "JWT",
"Scheme": "bearer",
"In": "Header",
"Requirement": {
"Scheme": {
"Reference": {
"Id": "Bearer",
"Type": "SecurityScheme"
},
"Accesses": []
}
}
}
]
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
<ProjectReference Include="..\NetAdmin.BizServer.Host\NetAdmin.BizServer.Host.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0-preview-23371-04"/>
<PackageReference Include="xunit" Version="2.5.1-pre.20"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1-pre.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
using Xunit;
namespace NetAdmin.BizServer.Tests;
/// <summary>
/// UsedNumberTests
/// </summary>
public class UsedNumberTests
{
/// <summary>
/// Test1
/// </summary>
[Fact]
public void Test1()
{
// Assert.True("1".Int32() == 2);
}
}

View File

@ -0,0 +1,29 @@
using NetAdmin.Application.Services;
namespace NetAdmin.Cache;
/// <summary>
/// 缓存基类
/// </summary>
public abstract class CacheBase<TCacheContainer, TService> : ICache<TCacheContainer, TService>
where TService : IService
{
/// <summary>
/// Initializes a new instance of the <see cref="CacheBase{TCacheLoad, TService}" /> class.
/// </summary>
protected CacheBase(TCacheContainer cache, TService service)
{
Cache = cache;
Service = service;
}
/// <summary>
/// 缓存对象
/// </summary>
public TCacheContainer Cache { get; }
/// <summary>
/// 关联的服务
/// </summary>
public TService Service { get; }
}

View File

@ -0,0 +1,95 @@
using System.Runtime.CompilerServices;
using NetAdmin.Application.Services;
namespace NetAdmin.Cache;
/// <summary>
/// 分布式缓存
/// </summary>
public abstract class DistributedCache<TService> : CacheBase<IDistributedCache, TService>
where TService : IService
{
/// <summary>
/// Initializes a new instance of the <see cref="DistributedCache{TService}" /> class.
/// </summary>
protected DistributedCache(IDistributedCache cache, TService service) //
: base(cache, service) { }
/// <summary>
/// 创建缓存
/// </summary>
/// <param name="key">缓存键</param>
/// <param name="createObj">创建对象</param>
/// <param name="absLifeTime">绝对过期时间</param>
/// <param name="slideLifeTime">滑动过期时间</param>
/// <typeparam name="T">缓存对象类型</typeparam>
/// <returns>缓存对象</returns>
protected Task CreateAsync<T>(string key, T createObj, TimeSpan? absLifeTime = null, TimeSpan? slideLifeTime = null)
{
var cacheWrite = createObj.ToJson();
var options = new DistributedCacheEntryOptions();
if (absLifeTime != null) {
_ = options.SetAbsoluteExpiration(absLifeTime.Value);
}
if (slideLifeTime != null) {
_ = options.SetSlidingExpiration(slideLifeTime.Value);
}
return Cache.SetAsync(key, cacheWrite.Hex(), options);
}
/// <summary>
/// 获取缓存
/// </summary>
protected async Task<T> GetAsync<T>(string key)
{
var cacheRead = await Cache.GetStringAsync(key);
return cacheRead != null ? cacheRead.ToObject<T>() : default;
}
/// <summary>
/// 获取缓存键
/// </summary>
protected virtual string GetCacheKey(string id = "0", [CallerMemberName] string memberName = null)
{
return $"{GetType().FullName}.{memberName}.{id}";
}
/// <summary>
/// 获取或创建缓存
/// </summary>
/// <param name="key">缓存键</param>
/// <param name="createProc">创建函数</param>
/// <param name="absLifeTime">绝对过期时间</param>
/// <param name="slideLifeTime">滑动过期时间</param>
/// <typeparam name="T">缓存对象类型</typeparam>
/// <returns>缓存对象</returns>
protected async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> createProc, TimeSpan? absLifeTime = null
, TimeSpan? slideLifeTime = null)
{
var cacheRead = await GetAsync<T>(key);
if (cacheRead is not null) {
return cacheRead;
}
var obj = await createProc.Invoke();
var cacheWrite = obj?.ToJson();
if (cacheWrite == null) {
return obj;
}
await CreateAsync(key, obj, absLifeTime, slideLifeTime);
return obj;
}
/// <summary>
/// 删除缓存
/// </summary>
protected Task RemoveAsync(string key)
{
return Cache.RemoveAsync(key);
}
}

View File

@ -0,0 +1,20 @@
using NetAdmin.Application.Services;
namespace NetAdmin.Cache;
/// <summary>
/// 缓存接口
/// </summary>
public interface ICache<out TCacheLoad, out TService>
where TService : IService
{
/// <summary>
/// 缓存对象
/// </summary>
TCacheLoad Cache { get; }
/// <summary>
/// 关联的服务
/// </summary>
public TService Service { get; }
}

View File

@ -0,0 +1,16 @@
using NetAdmin.Application.Services;
namespace NetAdmin.Cache;
/// <summary>
/// 内存缓存
/// </summary>
public abstract class MemoryCache<TService> : CacheBase<IMemoryCache, TService>
where TService : IService
{
/// <summary>
/// Initializes a new instance of the <see cref="MemoryCache{TService}" /> class.
/// </summary>
protected MemoryCache(IMemoryCache cache, TService service) //
: base(cache, service) { }
}

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,23 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 支付宝验证器(手机或邮箱)
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class AlipayAttribute : ValidationAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="AlipayAttribute" /> class.
/// </summary>
public AlipayAttribute()
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
/// <inheritdoc />
public override bool IsValid(object value)
{
return new MobileAttribute().IsValid(value) || new EmailAddressAttribute().IsValid(value);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 证件号码
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class CertificateAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="CertificateAttribute" /> class.
/// </summary>
public CertificateAttribute() //
: base(Chars.RGX_CERTIFICATE)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 中文姓名
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class ChineseNameAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ChineseNameAttribute" /> class.
/// </summary>
public ChineseNameAttribute() //
: base(Chars.RGX_CHINESE_NAME)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,23 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 电话验证器(手机或固话)
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class CulturePhoneAttribute : ValidationAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="CulturePhoneAttribute" /> class.
/// </summary>
public CulturePhoneAttribute()
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
/// <inheritdoc />
public override bool IsValid(object value)
{
return new MobileAttribute().IsValid(value) || new TelephoneAttribute().IsValid(value);
}
}

View File

@ -0,0 +1,26 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 区间验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class CultureRangeAttribute : RangeAttribute
{
/// <inheritdoc cref="RangeAttribute" />
public CultureRangeAttribute(double minimum, double maximum) //
: base(minimum, maximum) { }
/// <inheritdoc cref="RangeAttribute" />
public CultureRangeAttribute(int minimum, int maximum) //
: base(minimum, maximum) { }
/// <inheritdoc cref="RangeAttribute" />
public CultureRangeAttribute(Type type, string minimum, string maximum) //
: base(type, minimum, maximum) { }
/// <inheritdoc />
public override string FormatErrorMessage(string name)
{
return $"{ErrorMessageString} {Ln.必须介于} {Minimum} - {Maximum}";
}
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 非空验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class CultureRequiredAttribute : RequiredAttribute
{
/// <summary>Applies formatting to an error message, based on the data field where the error occurred.</summary>
/// <param name="name">The name to include in the formatted message.</param>
/// <exception cref="T:System.InvalidOperationException">The current attribute is malformed.</exception>
/// <returns>An instance of the formatted error message.</returns>
public override string FormatErrorMessage(string name)
{
return $"{ErrorMessageString.NullOrEmpty(name)} {Ln.不能为空}";
}
}

View File

@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// Url验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class CultureUrlAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="CultureUrlAttribute" /> class.
/// </summary>
public CultureUrlAttribute() //
: base(Chars.RGX_URL)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 邮箱验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class EmailAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="EmailAttribute" /> class.
/// </summary>
public EmailAttribute() //
: base(Chars.RGX_EMAIL)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 邀请码验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class InviteCodeAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="InviteCodeAttribute" /> class.
/// </summary>
public InviteCodeAttribute() //
: base(Chars.RGX_INVITE_CODE)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 手机号验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class MobileAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="MobileAttribute" /> class.
/// </summary>
public MobileAttribute() //
: base(Chars.RGX_MOBILE)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 密码验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class PasswordAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PasswordAttribute" /> class.
/// </summary>
public PasswordAttribute() //
: base(Chars.RGX_PASSWORD)
{
ErrorMessageResourceName = nameof(Ln._8位以上数字字母组合);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 交易密码验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class PayPasswordAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="PayPasswordAttribute" /> class.
/// </summary>
public PayPasswordAttribute() //
: base(Chars.RGX_PAY_PASSWORD)
{
ErrorMessageResourceName = nameof(Ln._6位数字);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,16 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 正则表达式验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
#pragma warning disable DesignedForInheritance
public class RegexAttribute : RegularExpressionAttribute
#pragma warning restore DesignedForInheritance
{
/// <summary>
/// Initializes a new instance of the <see cref="RegexAttribute" /> class.
/// </summary>
protected RegexAttribute(string pattern) //
: base(pattern) { }
}

View File

@ -0,0 +1,27 @@
using NetAdmin.Domain.Dto.Sys.Role;
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 数据范围为特定部门的验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class SpecificDeptAttribute : ValidationAttribute
{
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (validationContext.ObjectInstance is not CreateRoleReq { DataScope: DataScopes.SpecificDept }) {
return ValidationResult.Success;
}
#pragma warning disable IDE0046
if ((value as IEnumerable<long>)?.Any() ?? false) {
#pragma warning restore IDE0046
return ValidationResult.Success;
}
return new ValidationResult(Ln.);
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 固定电话验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class TelephoneAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="TelephoneAttribute" /> class.
/// </summary>
public TelephoneAttribute() //
: base(Chars.RGX_TELEPHONE)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,34 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 用户名验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class UserNameAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UserNameAttribute" /> class.
/// </summary>
public UserNameAttribute() //
: base(Chars.RGX_USERNAME)
{
ErrorMessageResourceType = typeof(Ln);
}
/// <inheritdoc />
public override bool IsValid(object value)
{
if (!base.IsValid(value)) {
ErrorMessageResourceName = nameof(Ln.4);
return false;
}
if (!new MobileAttribute().IsValid(value)) {
return true;
}
// 不能是手机号
ErrorMessageResourceName = nameof(Ln.);
return false;
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 验证码验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class VerifyCodeAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="VerifyCodeAttribute" /> class.
/// </summary>
public VerifyCodeAttribute() //
: base(Chars.RGX_VERIFY_CODE)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,7 @@
namespace NetAdmin.Domain.Attributes;
/// <summary>
/// 标记一个字段启用服务器时间
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class ServerTimeAttribute : Attribute { }

View File

@ -0,0 +1,12 @@
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
namespace NetAdmin.Domain.Attributes;
/// <summary>
/// 标记一个字段启用雪花编号生成
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class SnowflakeAttribute : Attribute
{
//
}

View File

@ -0,0 +1,113 @@
namespace NetAdmin.Domain.Contexts;
/// <summary>
/// 上下文应用信息
/// </summary>
/// <remarks>
/// 签名算法: $"${appId}{appSecret.ToLowerInvariant()}{timestamp}{reqBody}".Md5(Encoding.UTF8);
/// reqBody 需去除\r、\n、whitespace
/// </remarks>
public sealed record ContextApp : DataAbstraction, IValidatableObject
{
private const int _TS_OFFSET_SCOPE_SEC = 30;
/// <summary>
/// Initializes a new instance of the <see cref="ContextApp" /> class.
/// </summary>
public ContextApp(long appId, string appSecret, long timestamp)
{
AppId = appId;
AppSecret = appSecret;
Timestamp = timestamp;
}
/// <summary>
/// Initializes a new instance of the <see cref="ContextApp" /> class.
/// </summary>
private ContextApp()
{
AppId = App.HttpContext.Request.Headers[nameof(AppId)].FirstOrDefault().Int64Try(0);
AppSecret = App.HttpContext.Request.Headers[nameof(AppSecret)].FirstOrDefault();
Sign = App.HttpContext.Request.Headers[nameof(Sign)].FirstOrDefault();
Timestamp = App.HttpContext.Request.Headers[nameof(Timestamp)].FirstOrDefault().Int64Try(0);
}
/// <summary>
/// AppId
/// </summary>
[Range(1, long.MaxValue)]
public long AppId { get; init; }
/// <summary>
/// AppSecret
/// </summary>
public string AppSecret { get; init; }
/// <summary>
/// 签名
/// </summary>
public string Sign { get; set; }
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
/// <summary>
/// 从HttpContext 创建上下文应用
/// </summary>
public static async Task<ContextApp> CreateAsync()
{
var ret = new ContextApp();
if (!ret.TryValidate().IsValid) {
return null;
}
// 具有secret的情况下自动生成时间戳+sign方便调试
if (!ret.AppSecret.NullOrEmpty()) {
ret.Timestamp = DateTime.Now.TimeUnixUtc();
ret.Sign = await ret.BuildSignFromHttpContextAsync();
}
return ret;
}
/// <summary>
/// 构建签名
/// </summary>
public string BuildSign(string reqBody)
{
// 去除\r\n和空格再计算签名规避风格样式问题
reqBody = reqBody.Replace("\r", string.Empty).Replace("\n", string.Empty).Replace(" ", string.Empty);
return $"{AppId}{AppSecret.ToLowerInvariant()}{Timestamp}{reqBody}".Md5(Encoding.UTF8);
}
/// <summary>
/// 构建签名从http上下文
/// </summary>
public async Task<string> BuildSignFromHttpContextAsync()
{
var sr = new StreamReader(App.HttpContext.Request.Body);
var reqBody = await sr.ReadToEndAsync();
_ = App.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
return BuildSign(reqBody);
}
/// <inheritdoc />
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!AppSecret.NullOrEmpty()) {
yield break;
}
// 没有密码, 就要签名+时间戳
if (Sign.NullOrEmpty()) {
yield return new ValidationResult(Ln., new[] { nameof(Sign) });
}
if (Math.Abs(DateTime.Now.TimeUnixUtc() - Timestamp) > _TS_OFFSET_SCOPE_SEC) {
yield return new ValidationResult(Ln., new[] { nameof(Timestamp) });
}
}
}

View File

@ -0,0 +1,26 @@
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Contexts;
/// <summary>
/// 上下文用户信息
/// </summary>
public sealed record ContextUserInfo : QueryUserRsp
{
/// <summary>
/// 从HttpContext 创建上下文用户信息
/// </summary>
public static ContextUserInfo Create()
{
var ret = App.HttpContext?.Items[nameof(Chars.FLG_CONTEXT_USER_INFO)] as QueryUserRsp;
return ret?.Adapt<ContextUserInfo>();
}
/// <summary>
/// 是否存在于 HttpContext
/// </summary>
public static bool HasInContext()
{
return App.HttpContext?.Items.ContainsKey(Chars.FLG_CONTEXT_USER_INFO) ?? false;
}
}

View File

@ -0,0 +1,41 @@
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Contexts;
/// <summary>
/// 上下文用户凭据
/// </summary>
public sealed record ContextUserToken : DataAbstraction
{
/// <summary>
/// 用户编号
/// </summary>
public long Id { get; set; }
/// <summary>
/// 做授权验证的Token全局唯一可以随时重置强制下线
/// </summary>
public Guid Token { get; init; }
/// <summary>
/// 用户名
/// </summary>
public string UserName { get; init; }
/// <summary>
/// 从HttpContext 创建上下文用户
/// </summary>
public static ContextUserToken Create()
{
var claim = App.User?.FindFirst(nameof(ContextUserToken));
return claim?.Value.ToObject<ContextUserToken>();
}
/// <summary>
/// 从 QueryUserRsp 创建上下文用户
/// </summary>
public static ContextUserToken Create(QueryUserRsp user)
{
return new ContextUserToken { Id = user.Id, Token = user.Token, UserName = user.UserName };
}
}

View File

@ -0,0 +1,36 @@
namespace NetAdmin.Domain;
/// <summary>
/// 数据基类
/// </summary>
public abstract record DataAbstraction
{
/// <inheritdoc />
public override string ToString()
{
return this.ToJson();
}
/// <summary>
/// 截断所有字符串属性 以符合[MaxLength(x)]特性
/// </summary>
public void TruncateStrings()
{
foreach (var property in GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof(string))) {
var maxLen = property.GetCustomAttribute<MaxLengthAttribute>(true)?.Length;
if (maxLen is null or 0) {
continue;
}
var value = property.GetValue(this);
if (value is not string s || s.Length < maxLen) {
continue;
}
s = s.Sub(0, maxLen.Value);
property.SetValue(this, s);
}
}
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 数据库实体基类
/// </summary>
public abstract record EntityBase : DataAbstraction;

View File

@ -0,0 +1,22 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 创建者客户端字段接口
/// </summary>
public interface IFieldCreatedClient
{
/// <summary>
/// 创建者客户端IP
/// </summary>
int? CreatedClientIp { get; init; }
/// <summary>
/// 创建者来源地址
/// </summary>
string CreatedReferer { get; init; }
/// <summary>
/// 创建者客户端用户代理
/// </summary>
string CreatedUserAgent { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 创建时间字段接口
/// </summary>
public interface IFieldCreatedTime
{
/// <summary>
/// 创建时间
/// </summary>
DateTime CreatedTime { get; init; }
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 创建用户字段接口
/// </summary>
public interface IFieldCreatedUser
{
/// <summary>
/// 创建者编号
/// </summary>
long? CreatedUserId { get; init; }
/// <summary>
/// 创建者用户名
/// </summary>
string CreatedUserName { get; set; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 启用字段接口
/// </summary>
public interface IFieldEnabled
{
/// <summary>
/// 是否启用
/// </summary>
bool Enabled { get; init; }
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 修改客户端字段接口
/// </summary>
public interface IFieldModifiedClient
{
/// <summary>
/// 客户端IP
/// </summary>
int ModifiedClientIp { get; init; }
/// <summary>
/// 客户端用户代理
/// </summary>
string ModifiedUserAgent { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 修改时间字段接口
/// </summary>
public interface IFieldModifiedTime
{
/// <summary>
/// 修改时间
/// </summary>
DateTime? ModifiedTime { get; init; }
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 更新用户字段接口
/// </summary>
public interface IFieldModifiedUser
{
/// <summary>
/// 修改者编号
/// </summary>
long? ModifiedUserId { get; init; }
/// <summary>
/// 修改者用户名
/// </summary>
string ModifiedUserName { get; init; }
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 拥有者字段接口
/// </summary>
public interface IFieldOwner
{
/// <summary>
/// 拥有者部门编号
/// </summary>
long? OwnerDeptId { get; init; }
/// <summary>
/// 拥有者用户编号
/// </summary>
long? OwnerId { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 主键字段接口
/// </summary>
public interface IFieldPrimary<T>
{
/// <summary>
/// 唯一编码
/// </summary>
T Id { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 排序字段接口
/// </summary>
public interface IFieldSort
{
/// <summary>
/// 排序值,越大越前
/// </summary>
long Sort { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 描述字段接口
/// </summary>
public interface IFieldSummary
{
/// <summary>
/// 描述
/// </summary>
string Summary { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.DbMaps.Dependency.Fields;
/// <summary>
/// 版本字段接口
/// </summary>
public interface IFieldVersion
{
/// <summary>
/// 数据版本
/// </summary>
long Version { get; init; }
}

View File

@ -0,0 +1,44 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 不可变实体
/// </summary>
public abstract record ImmutableEntity : ImmutableEntity<long>
{
/// <summary>
/// 唯一编码
/// </summary>
[Snowflake]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override long Id { get; init; }
}
/// <summary>
/// 不可变实体
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract record ImmutableEntity<T> : LiteImmutableEntity<T>, IFieldCreatedUser
{
/// <summary>
/// 创建者编号
/// </summary>
[JsonIgnore]
[Column(CanUpdate = false, Position = -1)]
public long? CreatedUserId { get; init; }
/// <summary>
/// 创建者用户名
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanUpdate = false, Position = -1)]
public virtual string CreatedUserName { get; set; }
/// <summary>
/// 唯一编码
/// </summary>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override T Id { get; init; }
}

View File

@ -0,0 +1,38 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 轻型不可变实体
/// </summary>
public abstract record LiteImmutableEntity : LiteImmutableEntity<long>
{
/// <summary>
/// 唯一编码
/// </summary>
[Snowflake]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override long Id { get; init; }
}
/// <summary>
/// 轻型不可变实体
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract record LiteImmutableEntity<T> : EntityBase, IFieldPrimary<T>, IFieldCreatedTime
{
/// <summary>
/// 创建时间
/// </summary>
[JsonIgnore]
[Column(ServerTime = DateTimeKind.Utc, CanUpdate = false, Position = -1)]
public virtual DateTime CreatedTime { get; init; }
/// <summary>
/// 唯一编码
/// </summary>
[JsonIgnore]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public virtual T Id { get; init; }
}

View File

@ -0,0 +1,36 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 轻型可变实体
/// </summary>
public abstract record LiteMutableEntity : LiteMutableEntity<long>
{
/// <summary>
/// 唯一编码
/// </summary>
[Snowflake]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override long Id { get; init; }
}
/// <summary>
/// 轻型可变实体
/// </summary>
public abstract record LiteMutableEntity<T> : LiteImmutableEntity<T>, IFieldModifiedTime
{
/// <summary>
/// 唯一编码
/// </summary>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override T Id { get; init; }
/// <summary>
/// 修改时间
/// </summary>
[JsonIgnore]
[Column(ServerTime = DateTimeKind.Utc, CanInsert = false, Position = -1)]
public virtual DateTime? ModifiedTime { get; init; }
}

View File

@ -0,0 +1,36 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 乐观锁轻型可变实体
/// </summary>
public abstract record LiteVersionEntity : LiteVersionEntity<long>
{
/// <summary>
/// 唯一编码
/// </summary>
[Snowflake]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override long Id { get; init; }
}
/// <summary>
/// 乐观锁轻型可变实体
/// </summary>
public abstract record LiteVersionEntity<T> : LiteMutableEntity<T>, IFieldVersion
{
/// <summary>
/// 唯一编码
/// </summary>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override T Id { get; init; }
/// <summary>
/// 数据版本
/// </summary>
[JsonIgnore]
[Column(IsVersion = true, Position = -1)]
public virtual long Version { get; init; }
}

View File

@ -0,0 +1,43 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 可变实体
/// </summary>
public abstract record MutableEntity : MutableEntity<long>
{
/// <summary>
/// 唯一编码
/// </summary>
[Snowflake]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override long Id { get; init; }
}
/// <summary>
/// 可变实体
/// </summary>
public abstract record MutableEntity<T> : LiteMutableEntity<T>, IFieldModifiedUser
{
/// <summary>
/// 唯一编码
/// </summary>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override T Id { get; init; }
/// <summary>
/// 修改者编号
/// </summary>
[JsonIgnore]
[Column(CanInsert = false, Position = -1)]
public long? ModifiedUserId { get; init; }
/// <summary>
/// 修改者用户名
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanInsert = false, Position = -1)]
public string ModifiedUserName { get; init; }
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 简单实体
/// </summary>
public abstract record SimpleEntity : EntityBase;

View File

@ -0,0 +1,43 @@
using NetAdmin.Domain.Attributes;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Dependency;
/// <summary>
/// 乐观锁可变实体
/// </summary>
public abstract record VersionEntity : VersionEntity<long>
{
/// <summary>
/// 唯一编码
/// </summary>
[Snowflake]
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override long Id { get; init; }
}
/// <summary>
/// 乐观锁可变实体
/// </summary>
public abstract record VersionEntity<T> : LiteVersionEntity<T>, IFieldModifiedUser
{
/// <summary>
/// 唯一编码
/// </summary>
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
public override T Id { get; init; }
/// <summary>
/// 修改者编号
/// </summary>
[JsonIgnore]
[Column(CanInsert = false, Position = -1)]
public long? ModifiedUserId { get; init; }
/// <summary>
/// 修改者用户名
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanInsert = false, Position = -1)]
public string ModifiedUserName { get; init; }
}

View File

@ -0,0 +1,67 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// Api接口表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Api))]
public record Sys_Api : ImmutableEntity<string>, IFieldSummary
{
/// <summary>
/// 子节点
/// </summary>
[JsonIgnore]
[Navigate(nameof(ParentId))]
public IEnumerable<Sys_Api> Children { get; init; }
/// <summary>
/// 唯一编码
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127, IsIdentity = false, IsPrimary = true, Position = 1)]
public override string Id { get; init; }
/// <summary>
/// 请求方式
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
public virtual string Method { get; init; }
/// <summary>
/// 服务名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Name { get; init; }
/// <summary>
/// 命名空间
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Namespace { get; init; }
/// <summary>
/// 父编号
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string ParentId { get; init; }
/// <summary>
/// 角色集合
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_RoleApi))]
public ICollection<Sys_Role> Roles { get; init; }
/// <summary>
/// 服务描述
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Summary { get; init; }
}

View File

@ -0,0 +1,53 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 配置表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Config))]
public record Sys_Config : VersionEntity, IFieldEnabled
{
/// <summary>
/// 是否启用
/// </summary>
[JsonIgnore]
[Column]
public virtual bool Enabled { get; init; }
/// <summary>
/// 用户注册是否需要人工确认
/// </summary>
[JsonIgnore]
[Column]
public virtual bool UserRegisterConfirm { get; set; }
/// <summary>
/// 用户注册默认部门
/// </summary>
[JsonIgnore]
[Navigate(nameof(UserRegisterDeptId))]
public Sys_Dept UserRegisterDept { get; init; }
/// <summary>
/// 用户注册默认部门编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long UserRegisterDeptId { get; init; }
/// <summary>
/// 用户注册默认角色
/// </summary>
[JsonIgnore]
[Navigate(nameof(UserRegisterRoleId))]
public Sys_Role UserRegisterRole { get; init; }
/// <summary>
/// 用户注册默认角色编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long UserRegisterRoleId { get; init; }
}

View File

@ -0,0 +1,60 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 部门表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Dept))]
public record Sys_Dept : VersionEntity, IFieldEnabled, IFieldSummary, IFieldSort
{
/// <summary>
/// 子节点
/// </summary>
[JsonIgnore]
[Navigate(nameof(ParentId))]
public IEnumerable<Sys_Dept> Children { get; init; }
/// <summary>
/// 是否启用
/// </summary>
[JsonIgnore]
[Column]
public virtual bool Enabled { get; init; }
/// <summary>
/// 部门名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Name { get; init; }
/// <summary>
/// 父编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long ParentId { get; init; }
/// <summary>
/// 角色集合
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_RoleDept))]
public ICollection<Sys_Role> Roles { get; init; }
/// <summary>
/// 排序值,越大越前
/// </summary>
[JsonIgnore]
[Column]
public virtual long Sort { get; init; }
/// <summary>
/// 部门描述
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string Summary { get; init; }
}

View File

@ -0,0 +1,46 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 字典目录表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_DicCatalog))]
[Index($"idx_{{tablename}}_{nameof(Code)}", nameof(Code), true)]
public record Sys_DicCatalog : VersionEntity
{
/// <summary>
/// 子节点
/// </summary>
[JsonIgnore]
[Navigate(nameof(ParentId))]
public IEnumerable<Sys_DicCatalog> Children { get; init; }
/// <summary>
/// 字典编码
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Code { get; init; }
/// <summary>
/// 字典内容集合
/// </summary>
[JsonIgnore]
[Navigate(nameof(Sys_DicContent.CatalogId))]
public ICollection<Sys_DicContent> Contents { get; init; }
/// <summary>
/// 字典名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Name { get; init; }
/// <summary>
/// 父编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long ParentId { get; init; }
}

View File

@ -0,0 +1,40 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 字典内容表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_DicContent))]
[Index($"idx_{{tablename}}_{nameof(CatalogId)}_{nameof(Key)}", $"{nameof(CatalogId)},{nameof(Key)}", true)]
[Index($"idx_{{tablename}}_{nameof(CatalogId)}_{nameof(Value)}", $"{nameof(CatalogId)},{nameof(Value)}", true)]
public record Sys_DicContent : VersionEntity
{
/// <summary>
/// 字典目录
/// </summary>
[JsonIgnore]
[Navigate(nameof(CatalogId))]
public Sys_DicCatalog Catalog { get; init; }
/// <summary>
/// 字典目录编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long CatalogId { get; init; }
/// <summary>
/// 键名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Key { get; init; }
/// <summary>
/// 键值
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string Value { get; init; }
}

View File

@ -0,0 +1,132 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 菜单表
/// </summary>
[Table(Name = "Sys_Menu")]
[Index($"idx_{{tablename}}_{nameof(Name)}", nameof(Name), true)]
public record Sys_Menu : VersionEntity, IFieldSort
{
/// <summary>
/// 子节点或详情页需要高亮的上级菜单路由地址
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string Active { get; init; }
/// <summary>
/// 子节点
/// </summary>
[JsonIgnore]
[Navigate(nameof(ParentId))]
public IEnumerable<Sys_Menu> Children { get; init; }
/// <summary>
/// 背景颜色
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_7)]
public virtual string Color { get; init; }
/// <summary>
/// 组件
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Component { get; init; }
/// <summary>
/// 是否整页路由
/// </summary>
[JsonIgnore]
[Column]
public virtual bool FullPageRouting { get; init; }
/// <summary>
/// 是否隐藏菜单
/// </summary>
[JsonIgnore]
[Column]
public virtual bool Hidden { get; init; }
/// <summary>
/// 是否隐藏面包屑
/// </summary>
[JsonIgnore]
[Column]
public virtual bool HiddenBreadCrumb { get; init; }
/// <summary>
/// 图标
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Icon { get; init; }
/// <summary>
/// 菜单名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Name { get; init; }
/// <summary>
/// 父编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long ParentId { get; init; }
/// <summary>
/// 菜单路径
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string Path { get; init; }
/// <summary>
/// 重定向地址
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string Redirect { get; init; }
/// <summary>
/// 拥有此菜单的角色集合
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_RoleMenu))]
public ICollection<Sys_Role> Roles { get; init; }
/// <summary>
/// 排序值,越大越前
/// </summary>
[JsonIgnore]
[Column]
public virtual long Sort { get; init; }
/// <summary>
/// 标签
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Tag { get; init; }
/// <summary>
/// 菜单标题
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Title { get; init; }
/// <summary>
/// 菜单类型
/// </summary>
[JsonIgnore]
[Column]
public virtual MenuTypes Type { get; init; }
}

View File

@ -0,0 +1,151 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 请求日志表
/// </summary>
[Table(Name = "Sys_RequestLog")]
public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
{
/// <summary>
/// 接口
/// </summary>
[JsonIgnore]
[Navigate(nameof(ApiId))]
public Sys_Api Api { get; init; }
/// <summary>
/// 接口编号
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[JsonIgnore]
public virtual string ApiId { get; init; }
/// <summary>
/// 创建者客户端IP
/// </summary>
[Column(Position = -1)]
[JsonIgnore]
public virtual int? CreatedClientIp { get; init; }
/// <summary>
/// 创建者来源地址
/// </summary>
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public string CreatedReferer { get; init; }
/// <summary>
/// 创建者客户端用户代理
/// </summary>
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string CreatedUserAgent { get; init; }
/// <summary>
/// 执行耗时(微秒)
/// </summary>
[Column]
[JsonIgnore]
public virtual long Duration { get; init; }
/// <summary>
/// 程序响应码
/// </summary>
[Column]
[JsonIgnore]
public virtual ErrorCodes ErrorCode { get; init; }
/// <summary>
/// 异常信息
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string Exception { get; init; }
/// <summary>
/// 附加数据
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ExtraData { get; init; }
/// <summary>
/// HTTP状态码
/// </summary>
[Column]
[JsonIgnore]
public virtual int HttpStatusCode { get; init; }
/// <summary>
/// 请求方法
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
[JsonIgnore]
public virtual string Method { get; init; }
/// <summary>
/// 来源地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ReferUrl { get; init; }
/// <summary>
/// 请求内容
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string RequestBody { get; init; }
/// <summary>
/// 请求content-type
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[JsonIgnore]
public virtual string RequestContentType { get; init; }
/// <summary>
/// 请求头信息
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string RequestHeaders { get; init; }
/// <summary>
/// 请求地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[JsonIgnore]
public virtual string RequestUrl { get; init; }
/// <summary>
/// 响应内容
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ResponseBody { get; init; }
/// <summary>
/// 响应content-type
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
[JsonIgnore]
public virtual string ResponseContentType { get; init; }
/// <summary>
/// 响应头
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ResponseHeaders { get; init; }
/// <summary>
/// 服务器IP
/// </summary>
[Column]
[JsonIgnore]
public virtual int? ServerIp { get; init; }
}

View File

@ -0,0 +1,115 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.Dto.Sys.Role;
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 角色表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Role))]
[Index("idx_{tablename}_01", nameof(Name), true)]
public record Sys_Role : VersionEntity, IFieldSort, IFieldEnabled, IFieldSummary, IRegister
{
/// <summary>
/// 角色-接口映射
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_RoleApi))]
public ICollection<Sys_Api> Apis { get; init; }
/// <summary>
/// 数据范围
/// </summary>
[JsonIgnore]
[Column]
public virtual DataScopes DataScope { get; init; }
/// <summary>
/// 角色-部门映射
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_RoleDept))]
public ICollection<Sys_Dept> Depts { get; init; }
/// <summary>
/// 是否显示仪表板
/// </summary>
[JsonIgnore]
[Column]
public virtual bool DisplayDashboard { get; init; }
/// <summary>
/// 是否启用
/// </summary>
[JsonIgnore]
[Column]
public virtual bool Enabled { get; init; }
/// <summary>
/// 是否忽略权限控制
/// </summary>
[JsonIgnore]
[Column]
public virtual bool IgnorePermissionControl { get; init; }
/// <summary>
/// 角色-菜单映射
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_RoleMenu))]
public ICollection<Sys_Menu> Menus { get; init; }
/// <summary>
/// 角色名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Name { get; init; }
/// <summary>
/// 排序值,越大越前
/// </summary>
[JsonIgnore]
[Column]
public virtual long Sort { get; init; }
/// <summary>
/// 备注
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string Summary { get; init; }
/// <summary>
/// 此角色下的用户集合
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_UserRole))]
public ICollection<Sys_User> Users { get; init; }
/// <inheritdoc />
public void Register(TypeAdapterConfig config)
{
_ = config.ForType<CreateRoleReq, Sys_Role>()
.Map( //
d => d.Depts
, s => s.DeptIds.NullOrEmpty()
? Array.Empty<Sys_Dept>()
: s.DeptIds.Select(x => new Sys_Dept { Id = x }))
.Map( //
d => d.Menus
, s => s.MenuIds.NullOrEmpty()
? Array.Empty<Sys_Menu>()
: s.MenuIds.Select(x => new Sys_Menu { Id = x }))
.Map( //
d => d.Apis
, s => s.ApiIds.NullOrEmpty()
? Array.Empty<Sys_Api>()
: s.ApiIds.Select(x => new Sys_Api { Id = x }))
//
;
}
}

View File

@ -0,0 +1,36 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 角色-接口映射表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleApi))]
public sealed record Sys_RoleApi : ImmutableEntity
{
/// <summary>
/// 关联的接口
/// </summary>
[JsonIgnore]
public Sys_Api Api { get; init; }
/// <summary>
/// 接口编号
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public string ApiId { get; init; }
/// <summary>
/// 关联的角色
/// </summary>
[JsonIgnore]
public Sys_Role Role { get; init; }
/// <summary>
/// 角色编号
/// </summary>
[JsonIgnore]
[Column]
public long RoleId { get; init; }
}

View File

@ -0,0 +1,37 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 角色-部门映射表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleDept))]
[Index($"idx_{{tablename}}_{nameof(RoleId)}_{nameof(DeptId)}", $"{nameof(RoleId)},{nameof(DeptId)}", true)]
public sealed record Sys_RoleDept : ImmutableEntity
{
/// <summary>
/// 关联的部门
/// </summary>
[JsonIgnore]
public Sys_Dept Dept { get; init; }
/// <summary>
/// 可访问的部门编号
/// </summary>
[JsonIgnore]
[Column]
public long DeptId { get; init; }
/// <summary>
/// 关联的角色
/// </summary>
[JsonIgnore]
public Sys_Role Role { get; init; }
/// <summary>
/// 角色编号
/// </summary>
[JsonIgnore]
[Column]
public long RoleId { get; init; }
}

View File

@ -0,0 +1,37 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 角色-菜单映射表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleMenu))]
[Index($"idx_{{tablename}}_{nameof(RoleId)}_{nameof(MenuId)}", $"{nameof(RoleId)},{nameof(MenuId)}", true)]
public record Sys_RoleMenu : ImmutableEntity
{
/// <summary>
/// 关联的菜单
/// </summary>
[JsonIgnore]
public Sys_Menu Menu { get; init; }
/// <summary>
/// 菜单编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long MenuId { get; init; }
/// <summary>
/// 关联的角色
/// </summary>
[JsonIgnore]
public Sys_Role Role { get; init; }
/// <summary>
/// 角色编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long RoleId { get; init; }
}

View File

@ -0,0 +1,120 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 用户基本信息表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_User))]
[Index($"idx_{{tablename}}_{nameof(UserName)}", nameof(UserName), true)]
[Index($"idx_{{tablename}}_{nameof(Mobile)}", nameof(Mobile), true)]
[Index($"idx_{{tablename}}_{nameof(Email)}", nameof(Email), true)]
public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
{
/// <summary>
/// 头像链接
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string Avatar { get; init; }
/// <summary>
/// 所属部门
/// </summary>
[JsonIgnore]
[Navigate(nameof(DeptId))]
public Sys_Dept Dept { get; init; }
/// <summary>
/// 部门编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long DeptId { get; init; }
/// <summary>
/// 邮箱
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string Email { get; init; }
/// <summary>
/// 是否启用
/// </summary>
[JsonIgnore]
[Column]
public virtual bool Enabled { get; init; }
/// <summary>
/// 手机号
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
public virtual string Mobile { get; init; }
/// <summary>
/// 密码
/// </summary>
[JsonIgnore]
[Column]
public Guid Password { get; set; }
/// <summary>
/// 用户档案
/// </summary>
[JsonIgnore]
public Sys_UserProfile Profile { get; init; }
/// <summary>
/// 所属角色
/// </summary>
[JsonIgnore]
[Navigate(ManyToMany = typeof(Sys_UserRole))]
public ICollection<Sys_Role> Roles { get; init; }
/// <summary>
/// 描述
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string Summary { get; init; }
/// <summary>
/// 授权验证Token全局唯一可以随时重置强制下线
/// </summary>
[JsonIgnore]
[Column]
public Guid Token { get; init; }
/// <summary>
/// 用户名
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string UserName { get; init; }
/// <inheritdoc />
public void Register(TypeAdapterConfig config)
{
_ = config.ForType<CreateUserReq, Sys_User>()
.Map(d => d.Password, s => s.PasswordText.Pwd().Guid())
.Map(d => d.Token, _ => Guid.NewGuid())
.Map( //
d => d.Roles
, s => s.RoleIds.NullOrEmpty()
? Array.Empty<Sys_Role>()
: s.RoleIds.Select(x => new Sys_Role { Id = x }));
_ = config.ForType<UpdateUserReq, Sys_User>()
.Map( //
d => d.Password, s => s.PasswordText.NullOrEmpty() ? Guid.Empty : s.PasswordText.Pwd().Guid())
.Map( //
d => d.Roles
, s => s.RoleIds.NullOrEmpty()
? Array.Empty<Sys_Role>()
: s.RoleIds.Select(x => new Sys_Role { Id = x }));
}
}

View File

@ -0,0 +1,198 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.Dto.Sys.UserProfile;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 用户档案表
/// </summary>
[Table(Name = "Sys_UserProfile")]
public record Sys_UserProfile : VersionEntity, IRegister
{
/// <summary>
/// 出生日期
/// </summary>
[JsonIgnore]
[Column]
public virtual DateTime? BornDate { get; init; }
/// <summary>
/// 证件号码
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string CertificateNumber { get; init; }
/// <summary>
/// 证件类型
/// </summary>
[JsonIgnore]
[Column]
public virtual CertificateTypes? CertificateType { get; init; }
/// <summary>
/// 工作地址
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string CompanyAddress { get; init; }
/// <summary>
/// 工作地区
/// </summary>
[JsonIgnore]
[Column]
public int? CompanyArea { get; init; }
/// <summary>
/// 工作单位
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string CompanyName { get; init; }
/// <summary>
/// 工作电话
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string CompanyTelephone { get; init; }
/// <summary>
/// 文化程度
/// </summary>
[JsonIgnore]
[Column]
public virtual Educations? Education { get; init; }
/// <summary>
/// 紧急联系地址
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string EmergencyContactAddress { get; init; }
/// <summary>
/// 紧急联系地区
/// </summary>
[JsonIgnore]
[Column]
public int? EmergencyContactArea { get; init; }
/// <summary>
/// 紧急联系人手机号
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
public virtual string EmergencyContactMobile { get; init; }
/// <summary>
/// 紧急联系人
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string EmergencyContactName { get; init; }
/// <summary>
/// 毕业学校
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string GraduateSchool { get; init; }
/// <summary>
/// 身高
/// </summary>
[JsonIgnore]
[Column]
public virtual int? Height { get; init; }
/// <summary>
/// 住宅地址
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
public virtual string HomeAddress { get; init; }
/// <summary>
/// 住宅地区
/// </summary>
[JsonIgnore]
[Column]
public int? HomeArea { get; init; }
/// <summary>
/// 住宅电话
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string HomeTelephone { get; init; }
/// <summary>
/// 婚姻状况
/// </summary>
[JsonIgnore]
[Column]
public virtual MarriageStatues? MarriageStatus { get; init; }
/// <summary>
/// 民族
/// </summary>
/// 7
[JsonIgnore]
[Column]
public virtual Nations? Nation { get; init; }
/// <summary>
/// 籍贯
/// </summary>
[JsonIgnore]
[Column]
public int? NationArea { get; init; }
/// <summary>
/// 政治面貌
/// </summary>
[JsonIgnore]
[Column]
public virtual PoliticalStatues? PoliticalStatus { get; init; }
/// <summary>
/// 职业
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string Profession { get; init; }
/// <summary>
/// 真实姓名
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string RealName { get; init; }
/// <summary>
/// 性别
/// </summary>
[JsonIgnore]
[Column]
public virtual Sexes? Sex { get; init; }
/// <summary>
/// 用户基本信息
/// </summary>
[JsonIgnore]
public Sys_User User { get; init; }
/// <inheritdoc />
public void Register(TypeAdapterConfig config)
{
_ = config.ForType<CreateUserProfileReq, Sys_UserProfile>()
.Map(d => d.NationArea, s => s.NationArea == null ? null : s.NationArea.Value)
.Map(d => d.CompanyArea, s => s.CompanyArea == null ? null : s.CompanyArea.Value)
.Map(d => d.HomeArea, s => s.HomeArea == null ? null : s.HomeArea.Value)
.Map( //
d => d.EmergencyContactArea
, s => s.EmergencyContactArea == null ? null : s.EmergencyContactArea.Value);
}
}

View File

@ -0,0 +1,36 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 用户-角色映射表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_UserRole))]
public sealed record Sys_UserRole : VersionEntity
{
/// <summary>
/// 关联的角色
/// </summary>
[JsonIgnore]
public Sys_Role Role { get; init; }
/// <summary>
/// 角色编号
/// </summary>
[JsonIgnore]
[Column]
public long RoleId { get; init; }
/// <summary>
/// 关联的用户
/// </summary>
[JsonIgnore]
public Sys_User User { get; init; }
/// <summary>
/// 用户编号
/// </summary>
[JsonIgnore]
[Column]
public long UserId { get; init; }
}

View File

@ -0,0 +1,53 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.Enums.Sys;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 验证码表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_VerifyCode))]
public record Sys_VerifyCode : VersionEntity
{
/// <summary>
/// 验证码
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_7)]
public virtual string Code { get; init; }
/// <summary>
/// 目标设备
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string DestDevice { get; init; }
/// <summary>
/// 设备类型
/// </summary>
[JsonIgnore]
[Column]
public virtual VerifyCodeDeviceTypes DeviceType { get; init; }
/// <summary>
/// 发送报告
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public string Report { get; init; }
/// <summary>
/// 验证码状态
/// </summary>
[JsonIgnore]
[Column]
public virtual VerifyCodeStatues Status { get; init; }
/// <summary>
/// 验证码类型
/// </summary>
[JsonIgnore]
[Column]
public virtual VerifyCodeTypes Type { get; init; }
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Domain.DbMaps.Dependency;
namespace NetAdmin.Domain.DbMaps.Tpl;
/// <summary>
/// 示例表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Tpl_Example))]
public record Tpl_Example : VersionEntity;

View File

@ -0,0 +1,18 @@
using NetAdmin.Domain.Attributes.DataValidation;
namespace NetAdmin.Domain.Dto.Dependency;
/// <summary>
/// 批量请求
/// </summary>
public sealed record BulkReq<T> : DataAbstraction
where T : DataAbstraction, new()
{
/// <summary>
/// 请求对象
/// </summary>
[CultureRequired(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.请求对象))]
[MinLength(1)]
[MaxLength(Numbers.BULK_REQ_LIMIT)]
public IEnumerable<T> Items { get; init; }
}

View File

@ -0,0 +1,16 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.Dto.Dependency;
/// <inheritdoc cref="DelReq{T}" />
public sealed record DelReq : DelReq<long>;
/// <summary>
/// 请求:通过编号删除
/// </summary>
/// <typeparam name="T"></typeparam>
public record DelReq<T> : DataAbstraction, IFieldPrimary<T>
{
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
public T Id { get; init; }
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.Dto.Dependency;
/// <summary>
/// 信息:分页
/// </summary>
public interface IPagedInfo
{
/// <summary>
/// 当前页码
/// </summary>
int Page { get; init; }
/// <summary>
/// 页容量
/// </summary>
int PageSize { get; init; }
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.Domain.Dto.Dependency;
/// <summary>
/// 空请求
/// </summary>
public sealed record NopReq : DataAbstraction;

View File

@ -0,0 +1,16 @@
namespace NetAdmin.Domain.Dto.Dependency;
/// <summary>
/// 请求:分页查询
/// </summary>
public sealed record PagedQueryReq<T> : QueryReq<T>, IPagedInfo
where T : DataAbstraction, new()
{
/// <inheritdoc cref="IPagedInfo.Page" />
[Range(1, Numbers.QUERY_MAX_PAGE_NO)]
public int Page { get; init; } = 1;
/// <inheritdoc cref="IPagedInfo.PageSize" />
[Range(1, Numbers.QUERY_MAX_PAGE_SIZE)]
public int PageSize { get; init; } = Numbers.QUERY_DEF_PAGE_SIZE;
}

View File

@ -0,0 +1,24 @@
namespace NetAdmin.Domain.Dto.Dependency;
/// <summary>
/// 响应:分页查询
/// </summary>
public sealed record PagedQueryRsp<T>(int Page, int PageSize, long Total, IEnumerable<T> Rows) : IPagedInfo
where T : DataAbstraction
{
/// <inheritdoc cref="IPagedInfo.Page" />
public int Page { get; init; } = Page;
/// <inheritdoc cref="IPagedInfo.PageSize" />
public int PageSize { get; init; } = PageSize;
/// <summary>
/// 数据行
/// </summary>
public IEnumerable<T> Rows { get; init; } = Rows;
/// <summary>
/// 数据总条
/// </summary>
public long Total { get; init; } = Total;
}

View File

@ -0,0 +1,39 @@
namespace NetAdmin.Domain.Dto.Dependency;
/// <summary>
/// 请求:查询
/// </summary>
public record QueryReq<T> : DataAbstraction
where T : DataAbstraction, new()
{
/// <summary>
/// 取前n条
/// </summary>
[Range(1, Numbers.QUERY_LIMIT)]
public int Count { get; init; } = Numbers.QUERY_LIMIT;
/// <summary>
/// 动态查询条件
/// </summary>
public DynamicFilterInfo DynamicFilter { get; init; }
/// <summary>
/// 查询条件
/// </summary>
public T Filter { get; init; }
/// <summary>
/// 查询关键字
/// </summary>
public string Keywords { get; set; }
/// <summary>
/// 排序方式
/// </summary>
public Orders? Order { get; init; }
/// <summary>
/// 排序字段
/// </summary>
public string Prop { get; init; }
}

View File

@ -0,0 +1,23 @@
namespace NetAdmin.Domain.Dto;
/// <summary>
/// 信息RESTful 风格结果集
/// </summary>
public record RestfulInfo<T> : DataAbstraction
{
/// <summary>
/// 代码
/// </summary>
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public ErrorCodes Code { get; init; }
/// <summary>
/// 数据
/// </summary>
public T Data { get; init; }
/// <summary>
/// 消息
/// </summary>
public object Msg { get; init; }
}

View File

@ -0,0 +1,8 @@
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Api;
/// <summary>
/// 请求:创建接口
/// </summary>
public sealed record CreateApiReq : Sys_Api;

View File

@ -0,0 +1,8 @@
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Api;
/// <summary>
/// 请求:查询接口
/// </summary>
public sealed record QueryApiReq : Sys_Api;

View File

@ -0,0 +1,38 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Api;
/// <summary>
/// 响应:查询接口
/// </summary>
public sealed record QueryApiRsp : Sys_Api
{
/// <summary>
/// 子节点
/// </summary>
public new IEnumerable<QueryApiRsp> Children { get; init; }
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
public override string Id { get; init; }
/// <inheritdoc cref="Sys_Api.Method" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Method { get; init; }
/// <inheritdoc cref="Sys_Api.Name" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Name { get; init; }
/// <inheritdoc cref="Sys_Api.Namespace" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Namespace { get; init; }
/// <inheritdoc cref="Sys_Api.ParentId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ParentId { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
}

View File

@ -0,0 +1,71 @@
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 响应:缓存统计
/// </summary>
public sealed record CacheStatisticsRsp : DataAbstraction
{
private static readonly Regex[] _regexes = {
new(@"keyspace_hits:(\d+)", RegexOptions.Compiled)
, new(@"keyspace_misses:(\d+)", RegexOptions.Compiled)
, new(@"uptime_in_seconds:(\d+)", RegexOptions.Compiled)
, new(@"used_cpu_sys:([\d\\.]+)", RegexOptions.Compiled)
, new(@"used_cpu_user:([\d\\.]+)", RegexOptions.Compiled)
, new(@"used_memory:(\d+)", RegexOptions.Compiled)
, new("redis_version:(.+)", RegexOptions.Compiled)
};
/// <summary>
/// Initializes a new instance of the <see cref="CacheStatisticsRsp" /> class.
/// </summary>
public CacheStatisticsRsp() { }
/// <summary>
/// Initializes a new instance of the <see cref="CacheStatisticsRsp" /> class.
/// </summary>
public CacheStatisticsRsp(string redisResult)
{
KeyspaceHits = _regexes[0].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
KeyspaceMisses = _regexes[1].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
UpTime = _regexes[2].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
UsedCpu = _regexes[3].Match(redisResult).Groups[1].Value.Trim().DecTry(0) +
_regexes[4].Match(redisResult).Groups[1].Value.Trim().DecTry(0);
UsedMemory = _regexes[5].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
Version = _regexes[6].Match(redisResult).Groups[1].Value.Trim();
}
/// <summary>
/// 键总数
/// </summary>
public long DbSize { get; init; }
/// <summary>
/// 命中键的数量
/// </summary>
public long KeyspaceHits { get; init; }
/// <summary>
/// 未命中键的数量
/// </summary>
public long KeyspaceMisses { get; init; }
/// <summary>
/// Redis运行时间
/// </summary>
public long UpTime { get; init; }
/// <summary>
/// 使用的CPU时间
/// </summary>
public decimal UsedCpu { get; init; }
/// <summary>
/// 使用的内存量(字节)
/// </summary>
public long UsedMemory { get; init; }
/// <summary>
/// Redis版本号
/// </summary>
public string Version { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 请求:获取所有缓存项
/// </summary>
public sealed record GetAllEntriesReq : DataAbstraction
{
/// <summary>
/// 数据库索引号
/// </summary>
public uint DbIndex { get; init; }
}

View File

@ -0,0 +1,43 @@
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 响应:获取所有缓存项
/// </summary>
public sealed record GetAllEntriesRsp : DataAbstraction
{
/// <summary>
/// Initializes a new instance of the <see cref="GetAllEntriesRsp" /> class.
/// </summary>
public GetAllEntriesRsp() { }
/// <summary>
/// Initializes a new instance of the <see cref="GetAllEntriesRsp" /> class.
/// </summary>
public GetAllEntriesRsp(long absExp, string key, long sldExp, string data)
{
AbsExp = absExp;
Key = key;
SldExp = sldExp;
Data = data;
}
/// <summary>
/// 绝对过期时间
/// </summary>
public long AbsExp { get; init; }
/// <summary>
/// 缓存值
/// </summary>
public string Data { get; init; }
/// <summary>
/// 缓存键
/// </summary>
public string Key { get; init; }
/// <summary>
/// 滑动过期时间
/// </summary>
public long SldExp { get; init; }
}

View File

@ -0,0 +1,28 @@
namespace NetAdmin.Domain.Dto.Sys.Captcha;
/// <summary>
/// 响应:获取人机校验图
/// </summary>
public sealed record GetCaptchaRsp : DataAbstraction
{
/// <summary>
/// 背景图base64
/// </summary>
public string BackgroundImage { get; init; }
/// <summary>
/// 唯一编码
/// </summary>
public string Id { get; init; }
/// <summary>
/// 缺口x坐标
/// </summary>
[JsonIgnore]
public int SawOffsetX { get; init; }
/// <summary>
/// 滑块图base64
/// </summary>
public string SliderImage { get; init; }
}

View File

@ -0,0 +1,27 @@
using NetAdmin.Domain.Attributes.DataValidation;
namespace NetAdmin.Domain.Dto.Sys.Captcha;
/// <summary>
/// 请求:完成人机验证
/// </summary>
public sealed record VerifyCaptchaReq : DataAbstraction
{
/// <summary>
/// 唯一编码
/// </summary>
[CultureRequired(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.唯一编码))]
public string Id { get; init; }
/// <summary>
/// 缺口x坐标
/// </summary>
[JsonIgnore]
public int? SawOffsetX { get; init; }
/// <summary>
/// 验证数据
/// </summary>
[CultureRequired(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.验证数据))]
public string VerifyData { get; init; }
}

Some files were not shown because too many files have changed in this diff Show More