feat: 框架代码同步 (#173)

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-08-29 17:21:06 +08:00 committed by GitHub
parent b9b228c9e1
commit c088492cfa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 3055 additions and 2140 deletions

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 20.x ]
node-version: [ 22.x ]
steps:
- uses: actions/checkout@v3
with:

View File

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 20.x ]
node-version: [ 22.x ]
steps:
- uses: actions/checkout@v3
with:

View File

@ -25,7 +25,7 @@
<Title>$(AssemblyName)</Title>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MinVer" Version="5.0.0">
<PackageReference Include="MinVer" Version="6.0.0-rc.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0-preview.6 AS base
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0-preview.7 AS base
WORKDIR /app
EXPOSE 8080
RUN apt update

View File

@ -39,6 +39,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BB0B
gen.id.linq = scripts/gen.id.linq
gen.ln.cmd = scripts/gen.ln.cmd
gen.resx.tt = scripts/gen.resx.tt
git.del.obsolete.tags.ps1 = scripts/git.del.obsolete.tags.ps1
image.optimize.csx = scripts/image.optimize.csx
install.as.tpl.ps1 = scripts/install.as.tpl.ps1
rename.csx = scripts/rename.csx
@ -48,6 +49,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{BB0B
switcher.nsext.json = scripts/switcher.nsext.json
switcher.ps1 = scripts/switcher.ps1
sync.sln.files.csx = scripts/sync.sln.files.csx
wait.server.stop.sh = scripts/wait.server.stop.sh
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{1129FE25-466B-4F4F-85FC-3752664245E1}"

View File

@ -49,6 +49,7 @@ XML注释文件不存在
未获取到待执行任务
模块名称不能为空
模块说明不能为空
此节点已下线
民族不正确
消息主题不能为空
消息内容不能为空
@ -65,6 +66,7 @@ XML注释文件不存在
站内信不存在
站内信状态不正确
站内信类型不正确
缓存键不能为空
网络地址不正确
菜单名称不能为空
菜单标题不能为空

View File

@ -15,7 +15,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.19-preview">
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@ -23,7 +23,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.31.0.96804">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -0,0 +1,2 @@
git push origin :refs/tags/$(git tag -l "*-*")
git tag -d $(git tag -l "*-*")

View File

@ -10,11 +10,11 @@
"packages": [
{
"packageName": "FreeSql.NS",
"version": "3.2.833-preview20260627-ns1"
"version": "3.2.833-ns4"
},
{
"packageName": "FreeSql.DbContext.NS",
"version": "3.2.833-preview20260627-ns1"
"version": "3.2.833-ns4"
}
]
}

View File

@ -0,0 +1,28 @@
#!/bin/bash
# 检查是否提供了 URL 参数
if [ -z "$1" ]; then
echo "Usage: $0 <url>"
exit 1
fi
# 获取外部传入的 URL 参数
URL="$1"
# 初始化返回值
response=""
# 循环检查 API 返回值
while [ "$response" != "1" ]; do
# 等待一段时间再进行下一次检查,避免频繁请求
sleep 1
# 使用 curl 请求 URL并捕获返回值忽略 SSL 证书错误
response=$(curl -sk "$URL")
# 打印返回值 (可选)
echo "$1: $response"
done
# 当返回值为 "1" 时,继续执行后续脚本
echo "API returned 1. Continuing with the script..."

View File

@ -24,7 +24,9 @@ namespace NetAdmin.AdmServer.Host
/// <summary>
/// 配置应用程序中间件
/// </summary>
#pragma warning disable S2325
public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifeTime)
#pragma warning restore S2325
{
_ = app //
.UseMiddleware<SafetyShopHostMiddleware>() // 安全停机中间件
@ -52,7 +54,9 @@ namespace NetAdmin.AdmServer.Host
/// <summary>
/// 配置服务容器
/// </summary>
#pragma warning disable S2325
public void ConfigureServices(IServiceCollection services)
#pragma warning restore S2325
{
_ = services.AddConsoleFormatter() // 添加控制台日志模板
.AddAllOptions() // 添加配置项
@ -75,12 +79,18 @@ namespace NetAdmin.AdmServer.Host
/// <inheritdoc />
#pragma warning disable ASA001
public Task<int> Execute(CommandContext context, CommandLineArgs settings)
public async Task<int> Execute(CommandContext context, CommandLineArgs settings)
#pragma warning restore ASA001
{
Args = settings;
_ = Serve.Run(RunOptions.Default.WithArgs(context.Remaining.Raw.ToArray()));
return Task.FromResult(0);
var webOpt = new WebApplicationOptions //
{
EnvironmentName = Environment.GetEnvironmentVariable("TEST_ENVIRONMENT").NullOrEmpty(null)
, Args = context.Remaining.Raw.ToArray()
};
Serve.BuildApplication(RunOptions.Default.ConfigureOptions(webOpt), null, out var startUrl, out var app);
await app.RunAsync(startUrl).ConfigureAwait(false);
return 0;
}
/// <inheritdoc />

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,6 @@
<ProjectReference Include="../NetAdmin.AdmServer.Host/NetAdmin.AdmServer.Host.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0-release-24373-02"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0"/>
</ItemGroup>
</Project>

View File

@ -33,7 +33,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
/// <summary>
/// 导出实体
/// </summary>
protected async Task<IActionResult> ExportAsync<TQuery, TExport>( //
protected static async Task<IActionResult> ExportAsync<TQuery, TExport>( //
Func<QueryReq<TQuery>, ISelectGrouping<TEntity, TEntity>> selector, QueryReq<TQuery> query, string fileName
, Expression<Func<ISelectGroupingAggregate<TEntity, TEntity>, object>> listExp = null)
where TQuery : DataAbstraction, new()
@ -45,7 +45,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
/// <summary>
/// 导出实体
/// </summary>
protected async Task<IActionResult> ExportAsync<TQuery, TExport>( //
protected static async Task<IActionResult> ExportAsync<TQuery, TExport>( //
Func<QueryReq<TQuery>, ISelect<TEntity>> selector, QueryReq<TQuery> query, string fileName
, Expression<Func<TEntity, object>> listExp = null)
where TQuery : DataAbstraction, new()

View File

@ -13,9 +13,21 @@ public abstract record MutableEntity : MutableEntity<long>
/// <summary>
/// 可变实体
/// </summary>
public abstract record MutableEntity<T> : LiteMutableEntity<T>, IFieldModifiedUser
public abstract record MutableEntity<T> : LiteMutableEntity<T>, IFieldCreatedUser, IFieldModifiedUser
where T : IEquatable<T>
{
/// <inheritdoc />
[Column(CanUpdate = false, Position = -1)]
[CsvIgnore]
[JsonIgnore]
public virtual long? CreatedUserId { get; init; }
/// <inheritdoc />
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanUpdate = false, Position = -1)]
[CsvIgnore]
[JsonIgnore]
public virtual string CreatedUserName { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[Column(IsIdentity = false, IsPrimary = true, Position = 1)]
[CsvIgnore]

View File

@ -0,0 +1,13 @@
namespace NetAdmin.Domain.Dto.Sys.Cache;
/// <summary>
/// 请求:删除缓存项
/// </summary>
public sealed record DelEntryReq : DataAbstraction
{
/// <summary>
/// 缓存键
/// </summary>
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.缓存键不能为空))]
public string Key { get; init; }
}

View File

@ -0,0 +1,17 @@
namespace NetAdmin.Domain.Dto.Sys.Tool;
/// <summary>
/// 请求执行SQL
/// </summary>
public record ExecuteSqlReq : DataAbstraction
{
/// <summary>
/// SQL 语句
/// </summary>
public string Sql { get; init; }
/// <summary>
/// 超时时间(秒)
/// </summary>
public int TimeoutSecs { get; init; }
}

View File

@ -10,6 +10,23 @@ namespace NetAdmin.Host.Controllers;
[ApiDescriptionSettings("Probe")]
public sealed class ProbeController : ControllerBase<ICache<IDistributedCache, IService>, IService>
{
/// <summary>
/// 退出程序
/// </summary>
[AllowAnonymous]
[HttpGet]
#pragma warning disable CA1822
public ActionResult Exit(string token)
#pragma warning restore CA1822
{
if (token != GlobalStatic.SecretKey) {
return new UnauthorizedResult();
}
Environment.Exit(0);
return new OkResult();
}
/// <summary>
/// 健康检查
/// </summary>
@ -23,6 +40,46 @@ public sealed class ProbeController : ControllerBase<ICache<IDistributedCache, I
HostName = Environment.MachineName
, CurrentConnections = SafetyShopHostMiddleware.Connections
, GlobalStatic.ProductVersion
, ThreadCounts = GlobalStatic.CurrentProcess.Threads.Count
, GlobalStatic.LatestLogTime
};
}
/// <summary>
/// 系统是否已经安全停止
/// </summary>
[AllowAnonymous]
[HttpGet]
[NonUnify]
#pragma warning disable CA1822, S3400
public IActionResult IsSystemSafetyStopped(int logTimeoutSeconds = 15)
#pragma warning restore S3400, CA1822
{
return new ContentResult {
Content = (DateTime.Now - GlobalStatic.LatestLogTime).TotalSeconds >
logTimeoutSeconds
? "1"
: "0"
};
}
/// <summary>
/// 实例下线
/// </summary>
/// <remarks>
/// 流量只出不进
/// </remarks>
[AllowAnonymous]
[HttpGet]
#pragma warning disable CA1822
public ActionResult Offline(string token)
#pragma warning restore CA1822
{
if (token != GlobalStatic.SecretKey) {
return new UnauthorizedResult();
}
SafetyShopHostMiddleware.Stop();
return new OkResult();
}
}

View File

@ -0,0 +1,19 @@
namespace NetAdmin.Host.Extensions;
/// <summary>
/// Type 扩展方法
/// </summary>
public static class MethodInfoExtensions
{
/// <summary>
/// 获取路由路径
/// </summary>
public static string GetRoutePath(this MethodInfo me, IServiceProvider serviceProvider)
{
return serviceProvider.GetService<IActionDescriptorCollectionProvider>()
.ActionDescriptors.Items.FirstOrDefault(x => x.DisplayName!.StartsWith( //
$"{me.DeclaringType}.{me.Name}"
, StringComparison.Ordinal))
?.AttributeRouteInfo?.Template;
}
}

View File

@ -108,22 +108,20 @@ public static class ServiceCollectionExtensions
return me.AddConsoleFormatter(options => {
var logLevels = Enum.GetValues<LogLevels>().ToDictionary(x => x, x => x.GetDisplay());
#if DEBUG
options.WriteHandler = (message, _, _, _, _) => {
#if DEBUG
MarkupLine(message.Message.EscapeMarkup(), message, logLevels);
if (message.Exception != null) {
MarkupLine(message.Exception.ToString().EscapeMarkup(), message, logLevels);
}
};
#else
options.WriteHandler = (message, _, _, _, _) => {
#else
var msg = message.Message.ReplaceLineEndings(string.Empty);
var (date, logName, logFormat) = ParseMessage(message, false);
Console.WriteLine( //
logFormat, date, logLevels[(LogLevels)message.LogLevel].ShortName, logName, message.ThreadId, msg);
#endif
GlobalStatic.IncrementLogCounter();
};
#endif
});
}

View File

@ -11,6 +11,7 @@ public sealed class DefaultApiResultHandler : ApiResultHandler<RestfulInfo<objec
/// <inheritdoc />
public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata)
{
throw new NotImplementedException();
LogHelper.Get<DefaultApiResultHandler>().Error(metadata.Exception);
throw metadata.Exception;
}
}

View File

@ -9,36 +9,64 @@ namespace NetAdmin.Host.Middlewares;
public sealed class SafetyShopHostMiddleware(RequestDelegate next)
{
private static long _connections;
private static bool _hostStopping;
private static bool _trafficOff;
/// <summary>
/// 当前连接数
/// </summary>
public static long Connections => Interlocked.Read(ref _connections);
/// <summary>
/// 是否已停机
/// </summary>
public static bool IsShutdown => Volatile.Read(ref _trafficOff);
/// <summary>
/// 停机处理
/// </summary>
public static void OnStopping()
{
Volatile.Write(ref _hostStopping, true);
Stop();
while (Interlocked.Read(ref _connections) > 0) {
Thread.Sleep(10);
}
}
/// <summary>
/// 系统启机
/// </summary>
public static void Start()
{
Volatile.Write(ref _trafficOff, false);
}
/// <summary>
/// 系统停机
/// </summary>
public static void Stop()
{
Volatile.Write(ref _trafficOff, true);
}
/// <summary>
/// 主函数
/// </summary>
public async Task InvokeAsync(HttpContext context)
{
if (Volatile.Read(ref _hostStopping)) {
if (Volatile.Read(ref _trafficOff) &&
!context.Request.Path.StartsWithSegments($"/{Chars.FLG_PATH_API_RPOBE}")) {
context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
return;
}
_ = Interlocked.Increment(ref _connections);
await next(context).ConfigureAwait(false);
_ = Interlocked.Decrement(ref _connections);
// webSocket链接不参与计算
if (context.Request.Path.StartsWithSegments($"/{Chars.FLG_PATH_WEBSOCKET_PREFIX}")) {
await next(context).ConfigureAwait(false);
}
else {
_ = Interlocked.Increment(ref _connections);
await next(context).ConfigureAwait(false);
_ = Interlocked.Decrement(ref _connections);
}
}
}

View File

@ -73,12 +73,15 @@ public static class Chars
public const string FLG_HTTP_METHOD_POST = "POST";
public const string FLG_HTTP_METHOD_PUT = "PUT";
public const string FLG_HTTP_METHOD_TRACE = "TRACE";
public const string FLG_PATH_API_SYS_USER_LOGIN_BY_PWD = "api/sys/user/login.by.pwd";
public const string FLG_PATH_PREFIX_HEALTH_CHECK = "probe/health.check";
public const string FLG_RANDOM_UNAME_PWD = "VcXlp7WY";
public const string FLG_REDIS_INSTANCE_DATA_CACHE = "DataCache";
public const string FLG_SNOWFLAKE_WORK_ID = "SNOWFLAKE_WORK_ID";
public const string FLG_SYSTEM_PREFIX = "sc_";
public const string FLG_PATH_API_RPOBE = "api/probe";
public const string FLG_PATH_API_SYS_USER_LOGIN_BY_PWD = "api/sys/user/login.by.pwd";
public const string FLG_PATH_PREFIX_HEALTH_CHECK = "probe/health.check";
public const string FLG_PATH_WEBSOCKET_PREFIX = "ws";
public const string FLG_RANDOM_UNAME_PWD = "VcXlp7WY";
public const string FLG_REDIS_INSTANCE_DATA_CACHE = "DataCache";
public const string FLG_SNOWFLAKE_WORK_ID = "SNOWFLAKE_WORK_ID";
public const string FLG_SYSTEM_PREFIX = "sc_";
public const string FLGL_HTTP_HEADER_VALUE_UA_MOBILE
= "Mozilla/5.0 (Linux; Android 9; Redmi Note 8 Pro Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.96 Mobile Safari/537.36";

View File

@ -7,12 +7,19 @@ namespace NetAdmin.Infrastructure;
/// </summary>
public static class GlobalStatic
{
/// <summary>
/// 当前进程
/// </summary>
public static readonly Process CurrentProcess = Process.GetCurrentProcess();
/// <summary>
/// 产品版本
/// </summary>
public static readonly string ProductVersion
= FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly()!.Location).ProductVersion;
private static long _latestLogTime;
/// <summary>
/// 调试模式
/// </summary>
@ -24,10 +31,17 @@ public static class GlobalStatic
#endif
;
/// <summary>
/// 最后一次日志时间
/// </summary>
public static DateTime LatestLogTime => Volatile.Read(ref _latestLogTime).Time();
/// <summary>
/// 日志记录器忽略的API编号
/// </summary>
public static string[] LoggerIgnoreApiIds => [];
public static string[] LoggerIgnoreApiIds => [
"api/adm/tools/query.es.log", "api/probe/health.check", "api/probe/is.system.safety.stopped"
];
/// <summary>
/// 系统内部密钥
@ -73,4 +87,12 @@ public static class GlobalStatic
/// Json序列化选项
/// </summary>
public static JsonSerializerOptions JsonSerializerOptions { get; set; }
/// <summary>
/// 增加日志计数器
/// </summary>
public static void IncrementLogCounter()
{
Volatile.Write(ref _latestLogTime, DateTime.Now.TimeUnixUtcMs());
}
}

View File

@ -6,13 +6,13 @@
<Import Project="$(SolutionDir)/build/copy.pkg.xml.comment.files.targets"/>
<Import Project="$(SolutionDir)/build/prebuild.targets"/>
<ItemGroup>
<PackageReference Include="FreeSql.DbContext.NS" Version="3.2.833-preview20260627-ns1"/>
<PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.833-preview20260627-ns1"/>
<PackageReference Include="FreeSql.DbContext.NS" Version="3.2.833-ns4"/>
<PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.833-ns4"/>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.5.2"/>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.5.2-ns1"/>
<PackageReference Include="Furion.Pure.NS" Version="4.9.5.2-ns1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.10.0"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.6.24328.4"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.7.24406.2"/>
<PackageReference Include="Minio" Version="6.0.3"/>
<PackageReference Include="NSExt" Version="2.2.0"/>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.4"/>

View File

@ -18,7 +18,6 @@ public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
.UseAutoSyncStructure(
initMethods.HasFlag(FreeSqlInitMethods.SyncStructure))
.Build();
_ = InitDbAsync(freeSql, initMethods); // 初始化数据库 ,异步
return freeSql;
}

View File

@ -1,3 +1,4 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
@ -7,11 +8,21 @@ namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// </summary>
public interface ICacheModule
{
/// <summary>
/// 批量删除缓存项
/// </summary>
Task<int> BulkDeleteEntryAsync(BulkReq<DelEntryReq> req);
/// <summary>
/// 缓存统计
/// </summary>
Task<CacheStatisticsRsp> CacheStatisticsAsync();
/// <summary>
/// 删除缓存项
/// </summary>
Task<int> DeleteEntryAsync(DelEntryReq req);
/// <summary>
/// 获取所有缓存项
/// </summary>

View File

@ -7,6 +7,11 @@ namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// </summary>
public interface IToolsModule
{
/// <summary>
/// 执行SQL语句
/// </summary>
Task<object[][]> ExecuteSqlAsync(ExecuteSqlReq req);
/// <summary>
/// 获取更新日志
/// </summary>

View File

@ -1,5 +1,6 @@
using System.Collections.Concurrent;
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;
@ -21,6 +22,20 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
_redisInstance = redisOptions.Value.Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE);
}
/// <inheritdoc />
public async Task<int> BulkDeleteEntryAsync(BulkReq<DelEntryReq> req)
{
req.ThrowIfInvalid();
var ret = 0;
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var item in req.Items) {
ret += await DeleteEntryAsync(item).ConfigureAwait(false);
}
return ret;
}
/// <inheritdoc />
public async Task<CacheStatisticsRsp> CacheStatisticsAsync()
{
@ -30,6 +45,16 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
};
}
/// <inheritdoc />
public async Task<int> DeleteEntryAsync(DelEntryReq req)
{
req.ThrowIfInvalid();
#pragma warning disable VSTHRD103
var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
var delSuccess = await database.KeyDeleteAsync(req.Key).ConfigureAwait(false);
return delSuccess ? 1 : 0;
}
/// <inheritdoc />
public async Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{

View File

@ -84,7 +84,9 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
public async Task<QueryConfigRsp> GetAsync(QueryConfigReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryConfigReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryConfigReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryConfigRsp>();
}

View File

@ -102,7 +102,9 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
public async Task<QueryDeptRsp> GetAsync(QueryDeptReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryDeptReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryDeptReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryDeptRsp>();
}

View File

@ -89,7 +89,7 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
public async Task<QueryDicCatalogRsp> GetAsync(QueryDicCatalogReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryDicCatalogReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryDicCatalogReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryDicCatalogRsp>();

View File

@ -104,7 +104,7 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
public async Task<QueryDicContentRsp> GetAsync(QueryDicContentReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryDicContentReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryDicContentReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryDicContentRsp>();
@ -145,6 +145,9 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
public async Task<List<QueryDicContentRsp>> QueryByCatalogCodeAsync(string catalogCode)
{
var ret = await Rpo.Orm.Select<Sys_DicContent>()
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Include(a => a.Catalog)
.Where(a => a.Catalog.Code == catalogCode)
.ToListAsync()

View File

@ -73,7 +73,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
public async Task<QueryJobRecordRsp> GetAsync(QueryJobRecordReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryJobRecordReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryJobRecordReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryJobRecordRsp>();

View File

@ -112,9 +112,11 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
}
]
};
var job = await QueryInternal(new QueryReq<QueryJobReq> { Count = 1, Filter = req, DynamicFilter = df })
.ToOneAsync()
.ConfigureAwait(false) ?? throw new NetAdminInvalidOperationException(Ln.);
var job
= await QueryInternal(
new QueryReq<QueryJobReq> { Count = 1, Filter = req, DynamicFilter = df, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false) ?? throw new NetAdminInvalidOperationException(Ln.);
var nextExecTime = GetNextExecTime(Chars.FLG_CRON_PER_SECS);
try {
@ -175,7 +177,9 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
public async Task<QueryJobRsp> GetAsync(QueryJobReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryJobReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryJobReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryJobRsp>();
}
@ -201,13 +205,26 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
}
]
};
var job = await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Order = Orders.Random })
.Take(1)
var job = await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Order = Orders.Random }, false)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Where(a => !Rpo.Orm.Select<Sys_JobRecord>()
.As("b")
.Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId)
.Any())
.ToOneAsync()
.ToOneAsync(a => new {
a.RequestUrl
, a.HttpMethod
, a.RequestHeader
, a.RequestBody
, a.RandomDelayBegin
, a.RandomDelayEnd
, a.UserId
, a.Id
, a.NextTimeId
, a.Version
})
.ConfigureAwait(false);
if (job == null) {
return null;
@ -215,15 +232,15 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
#if DBTYPE_SQLSERVER
var ret = await UpdateReturnListAsync( //
job with { Status = JobStatues.Running, LastExecTime = DateTime.Now }
, [nameof(job.Status), nameof(job.LastExecTime)])
job.Adapt<Sys_Job>() with { Status = JobStatues.Running, LastExecTime = DateTime.Now }
, [nameof(Sys_Job.Status), nameof(Sys_Job.LastExecTime)])
.ConfigureAwait(false);
return ret.FirstOrDefault()?.Adapt<QueryJobRsp>();
#else
return await UpdateAsync( //
job with { Status = JobStatues.Running, LastExecTime = DateTime.Now }
, [nameof(job.Status), nameof(job.LastExecTime)])
job.Adapt<Sys_Job>() with { Status = JobStatues.Running, LastExecTime = DateTime.Now }
, [nameof(Sys_Job.Status), nameof(Sys_Job.LastExecTime)])
.ConfigureAwait(false) > 0
? await GetAsync(new QueryJobReq { Id = job.Id }).ConfigureAwait(false)
: null;
@ -330,12 +347,21 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req)
{
var ret = Rpo.Select.Include(a => a.User)
.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords));
return QueryInternal(req, true);
}
private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req, bool includeUser)
{
var ret = Rpo.Select;
if (includeUser) {
ret = ret.Include(a => a.User);
}
ret = ret.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords));
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {

View File

@ -72,7 +72,7 @@ public sealed class LoginLogService(BasicRepository<Sys_LoginLog, long> rpo) //
public async Task<QueryLoginLogRsp> GetAsync(QueryLoginLogReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryLoginLogReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryLoginLogReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryLoginLogRsp>();

View File

@ -88,7 +88,9 @@ public sealed class MenuService(BasicRepository<Sys_Menu, long> rpo, IUserServic
public async Task<QueryMenuRsp> GetAsync(QueryMenuReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryMenuReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryMenuReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryMenuRsp>();
}

View File

@ -71,7 +71,7 @@ public sealed class RequestLogDetailService(BasicRepository<Sys_RequestLogDetail
public async Task<QueryRequestLogDetailRsp> GetAsync(QueryRequestLogDetailReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRequestLogDetailReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryRequestLogDetailReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryRequestLogDetailRsp>();

View File

@ -99,7 +99,8 @@ public sealed class RequestLogService(BasicRepository<Sys_RequestLog, long> rpo,
}.Json()
.Object<JsonElement>()
};
var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req, DynamicFilter = df })
var ret = await QueryInternal(
new QueryReq<QueryRequestLogReq> { Filter = req, DynamicFilter = df, Order = Orders.None })
.Include(a => a.Detail)
.ToOneAsync()
.ConfigureAwait(false);

View File

@ -96,7 +96,9 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
public async Task<QueryRoleRsp> GetAsync(QueryRoleReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRoleReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryRoleReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryRoleRsp>();
}

View File

@ -71,7 +71,7 @@ public sealed class SiteMsgDeptService(BasicRepository<Sys_SiteMsgDept, long> rp
public async Task<QuerySiteMsgDeptRsp> GetAsync(QuerySiteMsgDeptReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgDeptReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QuerySiteMsgDeptReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgDeptRsp>();

View File

@ -71,7 +71,7 @@ public sealed class SiteMsgFlagService(BasicRepository<Sys_SiteMsgFlag, long> rp
public async Task<QuerySiteMsgFlagRsp> GetAsync(QuerySiteMsgFlagReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgFlagReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QuerySiteMsgFlagReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgFlagRsp>();

View File

@ -71,7 +71,7 @@ public sealed class SiteMsgRoleService(BasicRepository<Sys_SiteMsgRole, long> rp
public async Task<QuerySiteMsgRoleRsp> GetAsync(QuerySiteMsgRoleReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgRoleReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QuerySiteMsgRoleReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgRoleRsp>();

View File

@ -120,7 +120,7 @@ public sealed class SiteMsgService(
public async Task<QuerySiteMsgRsp> GetAsync(QuerySiteMsgReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QuerySiteMsgReq> { Filter = req, Order = Orders.None })
.IncludeMany(a => a.Roles)
.IncludeMany(a => a.Users)
.IncludeMany(a => a.Depts)

View File

@ -71,7 +71,7 @@ public sealed class SiteMsgUserService(BasicRepository<Sys_SiteMsgUser, long> rp
public async Task<QuerySiteMsgUserRsp> GetAsync(QuerySiteMsgUserReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgUserReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QuerySiteMsgUserReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgUserRsp>();

View File

@ -7,6 +7,15 @@ namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IToolsService" />
public sealed class ToolsService : ServiceBase<IToolsService>, IToolsService
{
/// <inheritdoc />
public Task<object[][]> ExecuteSqlAsync(ExecuteSqlReq req)
{
return App.GetService<IFreeSql>()
.Ado.CommandFluent(req.Sql)
.CommandTimeout(req.TimeoutSecs)
.ExecuteArrayAsync();
}
/// <inheritdoc />
public async Task<string> GetChangeLogAsync()
{

View File

@ -100,7 +100,7 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
public async Task<QueryUserProfileRsp> GetAsync(QueryUserProfileReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryUserProfileReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryUserProfileReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryUserProfileRsp>();

View File

@ -172,9 +172,9 @@ public sealed class UserService(
public async Task<QueryUserRsp> GetAsync(QueryUserReq req)
{
req.ThrowIfInvalid();
var ret = await (await QueryInternalAsync(new QueryReq<QueryUserReq> { Filter = req }).ConfigureAwait(false))
.ToOneAsync()
.ConfigureAwait(false);
var ret = await (await QueryInternalAsync(new QueryReq<QueryUserReq> { Filter = req, Order = Orders.None })
.ConfigureAwait(false)).ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryUserRsp>();
}
@ -433,14 +433,51 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<UserInfoRsp> UserInfoAsync()
{
static void OtherIncludes(ISelect<Sys_Role> select)
{
select.Where(a => a.Enabled)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.IncludeMany( //
a => a.Menus
#if DBTYPE_SQLSERVER
#pragma warning disable SA1115
, then => then.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#pragma warning restore SA1115
#endif
#pragma warning disable SA1009, SA1111
)
#pragma warning restore SA1111, SA1009
.IncludeMany( //
a => a.Depts
#if DBTYPE_SQLSERVER
#pragma warning disable SA1115
, then => then.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#pragma warning restore SA1115
#endif
#pragma warning disable SA1009, SA1111
)
#pragma warning restore SA1111, SA1009
.IncludeMany( //
a => a.Apis
#if DBTYPE_SQLSERVER
#pragma warning disable SA1115
, then => then.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#pragma warning restore SA1115
#endif
#pragma warning disable SA1009, SA1111
)
#pragma warning restore SA1111, SA1009
;
}
var dbUser = await Rpo.Where(a => a.Token == UserToken.Token && a.Enabled)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.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))
.IncludeMany(a => a.Roles, OtherIncludes)
.ToOneAsync()
.ConfigureAwait(false);
return dbUser.Adapt<UserInfoRsp>();

View File

@ -81,7 +81,7 @@ public sealed class VerifyCodeService(BasicRepository<Sys_VerifyCode, long> rpo,
public async Task<QueryVerifyCodeRsp> GetAsync(QueryVerifyCodeReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryVerifyCodeReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryVerifyCodeReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryVerifyCodeRsp>();
@ -188,11 +188,10 @@ public sealed class VerifyCodeService(BasicRepository<Sys_VerifyCode, long> rpo,
, Value = destDevice
}
})
#if DBTYPE_SQLSERVER
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.Take(1)
.ToOneAsync();
#endif
.ToOneAsync();
}
private ISelect<Sys_VerifyCode> QueryInternal(QueryReq<QueryVerifyCodeReq> req)

View File

@ -73,7 +73,7 @@ public sealed class ExampleService(BasicRepository<Tpl_Example, long> rpo) //
public async Task<QueryExampleRsp> GetAsync(QueryExampleReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryExampleReq> { Filter = req })
var ret = await QueryInternal(new QueryReq<QueryExampleReq> { Filter = req, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryExampleRsp>();

View File

@ -1,4 +1,5 @@
using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
@ -9,6 +10,12 @@ namespace NetAdmin.SysComponent.Cache.Sys;
public sealed class CacheCache(IDistributedCache cache, ICacheService service) //
: DistributedCache<ICacheService>(cache, service), IScoped, ICacheCache
{
/// <inheritdoc />
public Task<int> BulkDeleteEntryAsync(BulkReq<DelEntryReq> req)
{
return Service.BulkDeleteEntryAsync(req);
}
/// <inheritdoc />
public Task<CacheStatisticsRsp> CacheStatisticsAsync()
{
@ -20,6 +27,12 @@ public sealed class CacheCache(IDistributedCache cache, ICacheService service) /
#endif
}
/// <inheritdoc />
public Task<int> DeleteEntryAsync(DelEntryReq req)
{
return Service.DeleteEntryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{

View File

@ -9,6 +9,12 @@ namespace NetAdmin.SysComponent.Cache.Sys;
public sealed class ToolsCache(IDistributedCache cache, IToolsService service) //
: DistributedCache<IToolsService>(cache, service), IScoped, IToolsCache
{
/// <inheritdoc />
public Task<object[][]> ExecuteSqlAsync(ExecuteSqlReq req)
{
return Service.ExecuteSqlAsync(req);
}
/// <inheritdoc />
public Task<string> GetChangeLogAsync()
{

View File

@ -1,3 +1,4 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.Host.Controllers;
using NetAdmin.SysComponent.Application.Modules.Sys;
@ -12,6 +13,14 @@ namespace NetAdmin.SysComponent.Host.Controllers.Sys;
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class CacheController(ICacheCache cache) : ControllerBase<ICacheCache, ICacheService>(cache), ICacheModule
{
/// <summary>
/// 批量删除缓存项
/// </summary>
public Task<int> BulkDeleteEntryAsync(BulkReq<DelEntryReq> req)
{
return Cache.BulkDeleteEntryAsync(req);
}
/// <summary>
/// 缓存统计
/// </summary>
@ -20,6 +29,14 @@ public sealed class CacheController(ICacheCache cache) : ControllerBase<ICacheCa
return Cache.CacheStatisticsAsync();
}
/// <summary>
/// 删除缓存项
/// </summary>
public Task<int> DeleteEntryAsync(DelEntryReq req)
{
return Cache.DeleteEntryAsync(req);
}
/// <summary>
/// 获取所有缓存项
/// </summary>

View File

@ -12,6 +12,14 @@ namespace NetAdmin.SysComponent.Host.Controllers.Sys;
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class ToolsController(IToolsCache cache) : ControllerBase<IToolsCache, IToolsService>(cache), IToolsModule
{
/// <summary>
/// 执行SQL语句
/// </summary>
public Task<object[][]> ExecuteSqlAsync(ExecuteSqlReq req)
{
return Cache.ExecuteSqlAsync(req);
}
/// <summary>
/// 获取更新日志
/// </summary>

View File

@ -1,5 +1,6 @@
using Furion.Schedule;
using NetAdmin.Host.BackgroundRunning;
using NetAdmin.Host.Middlewares;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Host.Jobs;
@ -27,6 +28,11 @@ public sealed class FreeScheduledJob : WorkBase<FreeScheduledJob>, IJob
/// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
if (SafetyShopHostMiddleware.IsShutdown) {
Console.WriteLine(Ln.线);
return;
}
await WorkflowAsync(true, stoppingToken).ConfigureAwait(false);
}

View File

@ -6,6 +6,7 @@ using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.Domain.Dto.Sys.JobRecord;
using NetAdmin.Host.BackgroundRunning;
using NetAdmin.Host.Extensions;
using NetAdmin.Host.Middlewares;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Host.Jobs;
@ -42,6 +43,11 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
/// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
if (SafetyShopHostMiddleware.IsShutdown) {
Console.WriteLine(Ln.线);
return;
}
await WorkflowAsync(stoppingToken).ConfigureAwait(false);
}

View File

@ -1,5 +1,11 @@
using NetAdmin.Domain.Events.Sys;
#if !DEBUG
using System.Collections.Concurrent;
using NetAdmin.Domain.Dto.Sys.RequestLog;
#else
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
#endif
using NetAdmin.Domain.Events.Sys;
namespace NetAdmin.SysComponent.Host.Subscribers;
@ -8,6 +14,10 @@ namespace NetAdmin.SysComponent.Host.Subscribers;
/// </summary>
public sealed class OperationLogger : IEventSubscriber
{
#if !DEBUG
private static readonly ConcurrentQueue<CreateRequestLogReq> _requestLogs = new();
#endif
/// <summary>
/// 保存请求日志到数据库
/// </summary>
@ -18,7 +28,47 @@ public sealed class OperationLogger : IEventSubscriber
return;
}
operationEvent.Data.TruncateStrings();
#if DEBUG
_ = await App.GetService<IRequestLogService>().CreateAsync(operationEvent.Data).ConfigureAwait(false);
#else
if (_requestLogs.Count > Numbers.REQUEST_LOG_BUFF_SIZE) {
await WriteToDbAsync().ConfigureAwait(false);
}
else {
_requestLogs.Enqueue(operationEvent.Data);
}
#endif
}
#if !DEBUG
private static async Task WriteToDbAsync()
{
var inserts = new List<CreateRequestLogReq>(Numbers.REQUEST_LOG_BUFF_SIZE);
// 批量入库
for (var i = 0; i != Numbers.REQUEST_LOG_BUFF_SIZE; ++i) {
if (!_requestLogs.TryDequeue(out var log)) {
continue;
}
inserts.Add(log);
}
// 如果首尾日期不一致,要分别插入不同的日期分表
if (inserts[0].CreatedTime.Date != inserts[^1].CreatedTime.Date) {
foreach (var dayInserts in inserts.GroupBy(x => x.CreatedTime.Date)) {
await App.GetService<IFreeSql>()
.Insert<Sys_RequestLog>(dayInserts.Select(x => x))
.ExecuteSqlBulkCopyAsync(tableName: $"{nameof(Sys_RequestLog)}_{dayInserts.Key:yyyyMMdd}")
.ConfigureAwait(false);
}
}
else {
await App.GetService<IFreeSql>()
.Insert<Sys_RequestLog>(inserts)
.ExecuteSqlBulkCopyAsync(tableName: $"{nameof(Sys_RequestLog)}_{inserts[0].CreatedTime:yyyyMMdd}")
.ConfigureAwait(false);
}
}
#endif
}

View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="../NetAdmin.AdmServer.Tests/NetAdmin.AdmServer.Tests.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,118 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Api;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 接口测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class ApiTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IApiModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryApiReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryApiRsp> CreateAsync(CreateApiReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryApiReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryApiReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryApiRsp> GetAsync(QueryApiReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryApiRsp>> PagedQueryAsync(PagedQueryReq<QueryApiReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryApiRsp>> QueryAsync(QueryReq<QueryApiReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task SyncAsync()
{
var rsp = await PostJsonAsync(typeof(DeptController));
Assert.True(rsp.IsSuccessStatusCode);
}
}

View File

@ -0,0 +1,69 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Cache;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 缓存测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class CacheTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), ICacheModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteEntryAsync(BulkReq<DelEntryReq> req)
{
var rsp = await PostJsonAsync(typeof(CacheController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<CacheStatisticsRsp> CacheStatisticsAsync()
{
var rsp = await PostJsonAsync(typeof(CacheController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteEntryAsync(DelEntryReq req)
{
var rsp = await PostJsonAsync(typeof(CacheController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetEntryRsp>> GetAllEntriesAsync(GetAllEntriesReq req)
{
var rsp = await PostJsonAsync(typeof(CacheController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<GetEntryRsp> GetEntryAsync(GetEntriesReq req)
{
var rsp = await PostJsonAsync(typeof(CacheController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,38 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Sys.Captcha;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 人机验证测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class CaptchaTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), ICaptchaModule
{
/// <inheritdoc />
[Fact]
public async Task<GetCaptchaRsp> GetCaptchaImageAsync()
{
var rsp = await PostJsonAsync(typeof(CaptchaController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> VerifyCaptchaAsync(VerifyCaptchaReq req)
{
var rsp = await PostJsonAsync(typeof(CaptchaController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,139 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Config;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 配置测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class ConfigTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IConfigModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryConfigReq> req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryConfigRsp> EditAsync(EditConfigReq req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryConfigReq> req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryConfigReq> req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryConfigRsp> GetAsync(QueryConfigReq req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<QueryConfigRsp> GetLatestConfigAsync()
{
var rsp = await PostJsonAsync(typeof(ConfigController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryConfigRsp>> QueryAsync(QueryReq<QueryConfigReq> req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> SetEnabledAsync(SetConfigEnabledReq req)
{
var rsp = await PostJsonAsync(typeof(ConfigController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,55 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 常量测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
[SuppressMessage("Usage", "xUnit1031:Do not use blocking task operations in test method")]
public class ConstantTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IConstantModule
{
/// <inheritdoc />
[Fact]
public IDictionary<string, string> GetCharsDic()
{
var rsp = PostJsonAsync(typeof(ConstantController)).GetAwaiter().GetResult();
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public IDictionary<string, Dictionary<string, string[]>> GetEnums()
{
var rsp = PostJsonAsync(typeof(ConstantController)).GetAwaiter().GetResult();
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public IDictionary<string, string> GetLocalizedStrings()
{
var rsp = PostJsonAsync(typeof(ConstantController)).GetAwaiter().GetResult();
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public IDictionary<string, long> GetNumbersDic()
{
var rsp = PostJsonAsync(typeof(ConstantController)).GetAwaiter().GetResult();
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,130 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dept;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 部门测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class DeptTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IDeptModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryDeptReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDeptRsp> CreateAsync(CreateDeptReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDeptRsp> EditAsync(EditDeptReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryDeptReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryDeptReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDeptRsp> GetAsync(QueryDeptReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryDeptRsp>> PagedQueryAsync(PagedQueryReq<QueryDeptReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryDeptRsp>> QueryAsync(QueryReq<QueryDeptReq> req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> SetEnabledAsync(SetDeptEnabledReq req)
{
var rsp = await PostJsonAsync(typeof(DeptController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,45 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Sys.Dev;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 开发测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class DevTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IDevModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task GenerateCsCodeAsync(GenerateCsCodeReq req)
{
var rsp = await PostJsonAsync(typeof(DevController), req);
Assert.True(rsp.IsSuccessStatusCode);
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task GenerateIconCodeAsync(GenerateIconCodeReq req)
{
var rsp = await PostJsonAsync(typeof(DevController), req);
Assert.True(rsp.IsSuccessStatusCode);
}
/// <inheritdoc />
[Fact]
public async Task GenerateJsCodeAsync()
{
var rsp = await PostJsonAsync(typeof(DevController));
Assert.True(rsp.IsSuccessStatusCode);
}
}

View File

@ -0,0 +1,181 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Dic.Catalog;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 字典测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class DicTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IDicModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteCatalogAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteContentAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDicCatalogRsp> CreateCatalogAsync(CreateDicCatalogReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDicContentRsp> CreateContentAsync(CreateDicContentReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteCatalogAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteContentAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> EditCatalogAsync(EditDicCatalogReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDicContentRsp> EditContentAsync(EditDicContentReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportContentAsync(QueryReq<QueryDicContentReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDicCatalogRsp> GetCatalogAsync(QueryDicCatalogReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryDicContentRsp> GetContentAsync(QueryDicContentReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<string> GetDicValueAsync(GetDicValueReq req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryDicCatalogRsp>> PagedQueryCatalogAsync(PagedQueryReq<QueryDicCatalogReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryDicContentRsp>> PagedQueryContentAsync(PagedQueryReq<QueryDicContentReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryDicCatalogRsp>> QueryCatalogAsync(QueryReq<QueryDicCatalogReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryDicContentRsp>> QueryContentAsync(QueryReq<QueryDicContentReq> req)
{
var rsp = await PostJsonAsync(typeof(DicController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,28 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 文件测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class FileTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IFileModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<string> UploadAsync(IFormFile file)
{
var rsp = await PostJsonAsync(typeof(FileController), file);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,212 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys;
using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.Domain.Dto.Sys.JobRecord;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 计划作业测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class JobTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IJobModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryJobReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountRecordAsync(QueryReq<QueryJobRecordReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryJobRsp> CreateAsync(CreateJobReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryJobRsp> EditAsync(EditJobReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task ExecuteAsync(QueryJobReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryJobReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryJobReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportRecordAsync(QueryReq<QueryJobRecordReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryJobRsp> GetAsync(QueryJobReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryJobRecordRsp> GetRecordAsync(QueryJobRecordReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetBarChartRsp>> GetRecordBarChartAsync(QueryReq<QueryJobRecordReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByHttpStatusCodeAsync(
QueryReq<QueryJobRecordReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetPieChartRsp>> GetRecordPieChartByNameAsync(QueryReq<QueryJobRecordReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryJobRsp>> PagedQueryAsync(PagedQueryReq<QueryJobReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryJobRecordRsp>> PagedQueryRecordAsync(PagedQueryReq<QueryJobRecordReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryJobRsp>> QueryAsync(QueryReq<QueryJobReq> req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> SetEnabledAsync(SetJobEnabledReq req)
{
var rsp = await PostJsonAsync(typeof(JobController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,110 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.LoginLog;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 登录日志测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class LoginLogTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), ILoginLogModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryLoginLogReq> req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryLoginLogRsp> CreateAsync(CreateLoginLogReq req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryLoginLogReq> req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryLoginLogReq> req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryLoginLogRsp> GetAsync(QueryLoginLogReq req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryLoginLogRsp>> PagedQueryAsync(PagedQueryReq<QueryLoginLogReq> req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryLoginLogRsp>> QueryAsync(QueryReq<QueryLoginLogReq> req)
{
var rsp = await PostJsonAsync(typeof(LoginLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,128 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Menu;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 菜单测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class MenuTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IMenuModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryMenuReq> req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryMenuRsp> CreateAsync(CreateMenuReq req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryMenuRsp> EditAsync(EditMenuReq req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryMenuReq> req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryMenuReq> req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryMenuRsp> GetAsync(QueryMenuReq req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryMenuRsp>> PagedQueryAsync(PagedQueryReq<QueryMenuReq> req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryMenuRsp>> QueryAsync(QueryReq<QueryMenuReq> req)
{
var rsp = await PostJsonAsync(typeof(MenuTests), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<IEnumerable<QueryMenuRsp>> UserMenusAsync()
{
var rsp = await PostJsonAsync(typeof(MenuTests));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,141 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys;
using NetAdmin.Domain.Dto.Sys.RequestLog;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 请求日志测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class RequestLogTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IRequestLogModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryRequestLogRsp> CreateAsync(CreateRequestLogReq req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryRequestLogRsp> GetAsync(QueryRequestLogReq req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetBarChartRsp>> GetBarChartAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByApiSummaryAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<GetPieChartRsp>> GetPieChartByHttpStatusCodeAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryRequestLogRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryRequestLogRsp>> QueryAsync(QueryReq<QueryRequestLogReq> req)
{
var rsp = await PostJsonAsync(typeof(RequestLogController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,150 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Role;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 角色测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class RoleTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IRoleModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryRoleReq> req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryRoleRsp> CreateAsync(CreateRoleReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryRoleRsp> EditAsync(EditRoleReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryRoleReq> req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryRoleReq> req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryRoleRsp> GetAsync(QueryRoleReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryRoleRsp>> PagedQueryAsync(PagedQueryReq<QueryRoleReq> req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryRoleRsp>> QueryAsync(QueryReq<QueryRoleReq> req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> SetDisplayDashboardAsync(SetDisplayDashboardReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> SetEnabledAsync(SetRoleEnabledReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> SetIgnorePermissionControlAsync(SetIgnorePermissionControlReq req)
{
var rsp = await PostJsonAsync(typeof(RoleController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,159 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.SiteMsg;
using NetAdmin.Domain.Dto.Sys.SiteMsgFlag;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 站内信测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class SiteMsgTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), ISiteMsgModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QuerySiteMsgReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QuerySiteMsgRsp> CreateAsync(CreateSiteMsgReq req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QuerySiteMsgRsp> EditAsync(EditSiteMsgReq req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QuerySiteMsgReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QuerySiteMsgReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QuerySiteMsgRsp> GetAsync(QuerySiteMsgReq req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QuerySiteMsgRsp> GetMineAsync(QuerySiteMsgReq req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QuerySiteMsgRsp>> PagedQueryAsync(PagedQueryReq<QuerySiteMsgReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QuerySiteMsgRsp>> PagedQueryMineAsync(PagedQueryReq<QuerySiteMsgReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QuerySiteMsgRsp>> QueryAsync(QueryReq<QuerySiteMsgReq> req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task SetSiteMsgStatusAsync(SetUserSiteMsgStatusReq req)
{
var rsp = await PostJsonAsync(typeof(SiteMsgController), req);
Assert.True(rsp.IsSuccessStatusCode);
}
/// <inheritdoc />
[Fact]
public async Task<long> UnreadCountAsync()
{
var rsp = await PostJsonAsync(typeof(SiteMsgController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,65 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Sys.Tool;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 工具测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class ToolsTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IToolsModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<object[][]> ExecuteSqlAsync(ExecuteSqlReq req)
{
var rsp = await PostJsonAsync(typeof(ToolsController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<string> GetChangeLogAsync()
{
var rsp = await PostJsonAsync(typeof(ToolsController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<IEnumerable<GetModulesRsp>> GetModulesAsync()
{
var rsp = await PostJsonAsync(typeof(ToolsController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<DateTime> GetServerUtcTimeAsync()
{
var rsp = await PostJsonAsync(typeof(ToolsController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
public async Task<string> GetVersionAsync()
{
var rsp = await PostJsonAsync(typeof(ToolsController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,309 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Dto.Sys.UserProfile;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using NetAdmin.Tests.Attributes;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 用户测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
[TestCaseOrderer("NetAdmin.Tests.PriorityOrderer", "NetAdmin.Tests")]
public class UserTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IUserModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100100)]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.Equal(HttpStatusCode.NotFound, rsp.StatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100200)]
public async Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), new CheckMobileAvailableReq { Mobile = "13838381438" });
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100300)]
public async Task<bool> CheckUserNameAvailableAsync(CheckUserNameAvailableReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), new CheckUserNameAvailableReq { UserName = "test" });
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100400)]
public async Task<long> CountAsync(QueryReq<QueryUserReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100000)]
public async Task<QueryUserRsp> CreateAsync(CreateUserReq req)
{
var rsp = await PostJsonAsync(typeof(UserController)
, new CreateUserReq {
UserName = "test"
, PasswordText = "1234qwer"
, RoleIds = [371729946431493]
, DeptId = 372119301627909
});
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100600)]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100700)]
public async Task<QueryUserRsp> EditAsync(EditUserReq req)
{
var rsp = await PostJsonAsync(typeof(UserController)
, new EditUserReq {
UserName = "test"
, RoleIds = [371729946431493]
, DeptId = 372119301627909
});
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100800)]
public async Task<bool> ExistAsync(QueryReq<QueryUserReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), new QueryReq<QueryUserReq>());
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(100900)]
public async Task<IActionResult> ExportAsync(QueryReq<QueryUserReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101000)]
public async Task<QueryUserRsp> GetAsync(QueryUserReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
[TestPriority(101100)]
public async Task<GetSessionUserAppConfigRsp> GetSessionUserAppConfigAsync()
{
var rsp = await PostJsonAsync(typeof(UserController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101200)]
public async Task<LoginRsp> LoginByPwdAsync(LoginByPwdReq req)
{
var rsp = await PostJsonAsync(typeof(UserController)
, new LoginByPwdReq { Account = "root", Password = "1234qwer" });
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101300)]
public async Task<LoginRsp> LoginBySmsAsync(LoginBySmsReq req)
{
var rsp = await PostJsonAsync(typeof(UserController)
, new LoginBySmsReq { Code = "1234", DestDevice = "13838381438" });
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101400)]
public async Task<PagedQueryRsp<QueryUserRsp>> PagedQueryAsync(PagedQueryReq<QueryUserReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101500)]
public async Task<IEnumerable<QueryUserRsp>> QueryAsync(QueryReq<QueryUserReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101600)]
public async Task<IEnumerable<QueryUserProfileRsp>> QueryProfileAsync(QueryReq<QueryUserProfileReq> req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101700)]
public async Task<UserInfoRsp> RegisterAsync(RegisterUserReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101800)]
public async Task<int> ResetPasswordAsync(ResetPasswordReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(101900)]
public async Task<UserInfoRsp> SetAvatarAsync(SetAvatarReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(102000)]
public async Task<UserInfoRsp> SetEmailAsync(SetEmailReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(102100)]
public async Task<int> SetEnabledAsync(SetUserEnabledReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(102200)]
public async Task<UserInfoRsp> SetMobileAsync(SetMobileReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(102300)]
public async Task<int> SetPasswordAsync(SetPasswordReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
[TestPriority(102400)]
public async Task<int> SetSessionUserAppConfigAsync(SetSessionUserAppConfigReq req)
{
var rsp = await PostJsonAsync(typeof(UserController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[Fact]
[TestPriority(102500)]
public async Task<UserInfoRsp> UserInfoAsync()
{
var rsp = await PostJsonAsync(typeof(UserController));
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,130 @@
using System.Diagnostics.CodeAnalysis;
using NetAdmin.AdmServer.Host;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.VerifyCode;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Host.Controllers.Sys;
using NetAdmin.Tests;
using Xunit;
using Xunit.Abstractions;
namespace NetAdmin.SysComponent.Tests.Sys;
/// <summary>
/// 验证码测试
/// </summary>
[SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters")]
[SuppressMessage("Usage", "xUnit1028:Test method must have valid return type")]
public class VerifyCodeTests(WebTestApplicationFactory<Startup> factory, ITestOutputHelper testOutputHelper)
: WebApiTestBase<Startup>(factory, testOutputHelper), IVerifyCodeModule
{
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<long> CountAsync(QueryReq<QueryVerifyCodeReq> req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryVerifyCodeRsp> CreateAsync(CreateVerifyCodeReq req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<int> DeleteAsync(DelReq req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> ExistAsync(QueryReq<QueryVerifyCodeReq> req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IActionResult> ExportAsync(QueryReq<QueryVerifyCodeReq> req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<QueryVerifyCodeRsp> GetAsync(QueryVerifyCodeReq req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<PagedQueryRsp<QueryVerifyCodeRsp>> PagedQueryAsync(PagedQueryReq<QueryVerifyCodeReq> req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<IEnumerable<QueryVerifyCodeRsp>> QueryAsync(QueryReq<QueryVerifyCodeReq> req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<SendVerifyCodeRsp> SendVerifyCodeAsync(SendVerifyCodeReq req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public async Task<bool> VerifyAsync(VerifyCodeReq req)
{
var rsp = await PostJsonAsync(typeof(VerifyCodeController), req);
Assert.True(rsp.IsSuccessStatusCode);
return default;
}
}

View File

@ -0,0 +1,13 @@
namespace NetAdmin.Tests.Attributes;
/// <summary>
/// 测试用例优先级
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class TestPriorityAttribute(int priority) : Attribute
{
/// <summary>
/// 优先级
/// </summary>
public int Priority { get; private set; } = priority;
}

View File

@ -3,8 +3,8 @@
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0-preview.7.24406.2"/>
<PackageReference Include="xunit" Version="2.9.0"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0-preview.6.24328.4"/>
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0-pre.24">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>

View File

@ -0,0 +1,52 @@
using NetAdmin.Tests.Attributes;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace NetAdmin.Tests;
/// <summary>
/// 优先级排序
/// </summary>
public class PriorityOrderer : ITestCaseOrderer
{
/// <summary>
/// 排序测试用例
/// </summary>
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases)
where TTestCase : ITestCase
{
var sortedMethods = new SortedDictionary<int, List<TTestCase>>();
foreach (var testCase in testCases) {
var priority = 0;
foreach (var attr in testCase.TestMethod.Method.GetCustomAttributes(
typeof(TestPriorityAttribute).AssemblyQualifiedName)) {
priority = attr.GetNamedArgument<int>("Priority");
}
GetOrCreate(sortedMethods, priority).Add(testCase);
}
foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority])) {
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(
x.TestMethod.Method.Name, y.TestMethod.Method.Name));
foreach (var testCase in list) {
yield return testCase;
}
}
}
private static TValue GetOrCreate<TKey, TValue>(SortedDictionary<TKey, TValue> dictionary, TKey key)
where TValue : new()
{
if (dictionary.TryGetValue(key, out var result)) {
return result;
}
result = new TValue();
dictionary[key] = result;
return result;
}
}

View File

@ -1,8 +1,9 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using System.Runtime.CompilerServices;
using NetAdmin.Domain.Dto;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Host.Extensions;
using Xunit;
using Xunit.Abstractions;
@ -11,34 +12,68 @@ namespace NetAdmin.Tests;
/// <summary>
/// WebApi 测试用例基类
/// </summary>
public abstract class WebApiTestBase<T>(WebApplicationFactory<T> factory, ITestOutputHelper testOutputHelper)
: IClassFixture<WebApplicationFactory<T>>
public abstract class WebApiTestBase<T>(WebTestApplicationFactory<T> factory, ITestOutputHelper testOutputHelper)
: IClassFixture<WebTestApplicationFactory<T>>
where T : AppStartup
{
private const string _ACCOUNT = "root";
private const string _PASSWORD = "1234qwer";
private string _accessToken;
// ReSharper disable once StaticMemberInGenericType
private static string _accessToken;
/// <summary>
/// Post请求
/// </summary>
protected async Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
protected async Task<HttpResponseMessage> PostAsync(Type type, HttpContent content
, [CallerMemberName] string memberName = null)
{
var client = factory.CreateClient();
await Authorization(client);
var ret = await client.PostAsync(type.GetMethod(memberName!).GetRoutePath(factory.Services), content)
.ConfigureAwait(false);
testOutputHelper.WriteLine(await ret.Content.ReadAsStringAsync().ConfigureAwait(false));
return ret;
}
/// <summary>
/// Post请求
/// </summary>
protected async Task<HttpResponseMessage> PostJsonAsync<TRequest>(Type type, TRequest req = default
, [CallerMemberName] string memberName = null)
{
return await PostAsync(type, req == null ? JsonContent.Create(new { }) : JsonContent.Create(req), memberName);
}
/// <summary>
/// Post请求
/// </summary>
protected async Task<HttpResponseMessage> PostJsonAsync(Type type, [CallerMemberName] string memberName = null)
{
return await PostJsonAsync<object>(type, null, memberName);
}
private static async Task Authorization(HttpClient client)
{
if (_accessToken == null) {
var loginRsp = await client.PostAsync(Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD
, JsonContent.Create(
new LoginByPwdReq { Password = _PASSWORD, Account = _ACCOUNT }))
.ConfigureAwait(false);
var loginRspObj = (await loginRsp.Content.ReadAsStringAsync().ConfigureAwait(false))
var req = new LoginByPwdReq //
{
Password
= Environment.GetEnvironmentVariable(
nameof(WebTestApplicationFactory<T>.TEST_PASSWORD)) ??
WebTestApplicationFactory<T>.TEST_PASSWORD
, Account
= Environment.GetEnvironmentVariable(nameof(WebTestApplicationFactory<T>.TEST_ACCOUNT)) ??
WebTestApplicationFactory<T>.TEST_ACCOUNT
};
var loginAccount = JsonContent.Create(req);
var rspMsg = await client.PostAsync( //
Chars.FLG_PATH_API_SYS_USER_LOGIN_BY_PWD, loginAccount)
.ConfigureAwait(false);
var rspObj = (await rspMsg.Content.ReadAsStringAsync().ConfigureAwait(false))
.ToObject<RestfulInfo<LoginRsp>>();
_accessToken = loginRspObj.Data.AccessToken;
_accessToken = rspObj.Data.AccessToken;
}
client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA, _accessToken);
var ret = await client.PostAsync(url, content).ConfigureAwait(false);
testOutputHelper.WriteLine(await ret.Content.ReadAsStringAsync().ConfigureAwait(false));
return ret;
}
}

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc.Testing;
namespace NetAdmin.Tests;
/// <summary>
/// Web 测试应用程序工厂
/// </summary>
/// <typeparam name="T"></typeparam>
public class WebTestApplicationFactory<T> : WebApplicationFactory<T>
where T : class
{
/// <summary>
/// 测试用户
/// </summary>
public const string TEST_ACCOUNT = "root";
/// <summary>
/// 测试密码
/// </summary>
public const string TEST_PASSWORD = "1234qwer";
}

View File

@ -10,15 +10,15 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"ace-builds": "^1.35.4",
"aieditor": "^1.0.13",
"axios": "^1.7.3",
"ace-builds": "^1.36.0",
"aieditor": "^1.0.14",
"axios": "^1.7.5",
"clipboard": "^2.0.11",
"core-js": "^3.38.0",
"core-js": "^3.38.1",
"cropperjs": "^1.6.2",
"crypto-js": "^4.2.0",
"echarts": "^5.5.1",
"element-plus": "^2.8.0",
"element-plus": "^2.8.1",
"json-bigint": "^1.0.0",
"json5-to-table": "^0.1.8",
"markdown-it": "^14.1.0",
@ -28,8 +28,8 @@
"qrcodejs2": "^0.0.2",
"sortablejs": "^1.15.2",
"vkbeautify": "^0.99.3",
"vue": "^3.4.37",
"vue-i18n": "^9.13.1",
"vue": "^3.4.38",
"vue-i18n": "^9.14.0",
"vue-router": "^4.4.3",
"vue3-ace-editor": "^2.2.4",
"vue3-json-viewer": "^2.2.2",
@ -41,8 +41,8 @@
"prettier": "^3.3.3",
"prettier-plugin-organize-attributes": "^1.0.0",
"sass": "^1.77.8",
"terser": "^5.31.5",
"vite": "^5.4.0"
"terser": "^5.31.6",
"vite": "^5.4.2"
},
"browserslist": [
"> 1%",

View File

@ -15,4 +15,15 @@ export default {
return await http.get(this.url, data, config)
},
},
/**
* 系统是否已经安全停止
*/
isSystemSafetyStopped: {
url: `${config.API_URL}/api/probe/is.system.safety.stopped`,
name: `系统是否已经安全停止`,
get: async function (data = {}, config = {}) {
return await http.get(this.url, data, config)
},
},
}

View File

@ -5,6 +5,17 @@
import config from '@/config'
import http from '@/utils/request'
export default {
/**
* 批量删除缓存项
*/
bulkDeleteEntry: {
url: `${config.API_URL}/api/sys/cache/bulk.delete.entry`,
name: `批量删除缓存项`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 缓存统计
*/
@ -16,6 +27,17 @@ export default {
},
},
/**
* 删除缓存项
*/
deleteEntry: {
url: `${config.API_URL}/api/sys/cache/delete.entry`,
name: `删除缓存项`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取所有缓存项
*/

View File

@ -5,6 +5,17 @@
import config from '@/config'
import http from '@/utils/request'
export default {
/**
* 执行SQL语句
*/
executeSql: {
url: `${config.API_URL}/api/sys/tools/execute.sql`,
name: `执行SQL语句`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取更新日志
*/
@ -48,4 +59,15 @@ export default {
return await http.post(this.url, data, config)
},
},
/**
* 系统停机
*/
shutdown: {
url: `${config.API_URL}/api/sys/tools/shutdown`,
name: `系统停机`,
get: async function (data = {}, config = {}) {
return await http.get(this.url, data, config)
},
},
}

View File

@ -7,6 +7,29 @@ const T = {
top: '40',
containLabel: true,
},
pie: {
label: {
color: '#999',
},
},
gauge: {
axisTick: {
lineStyle: {
color: '#999',
},
},
splitLine: {
lineStyle: {
color: '#999',
},
},
axisLabel: {
color: '#999',
},
title: {
color: '#999',
},
},
legend: {
textStyle: {
color: '#999',

View File

@ -167,7 +167,7 @@ export default {
}
[data-theme='dark'] .sc-filter-my-list li {
color: #d0d0d0;
color: #c0c0c0;
}
[data-theme='dark'] .sc-filter-my-list li:hover {

View File

@ -88,6 +88,6 @@ export default {
}
[data-theme='dark'] .sc-page-header__title h2 {
color: #d0d0d0;
color: #c0c0c0;
}
</style>

View File

@ -100,6 +100,6 @@ export default {
}
.dark .sc-statistic-content {
color: #d0d0d0;
color: #c0c0c0;
}
</style>

View File

@ -490,7 +490,7 @@ export default {
this.loading = true
try {
await this.exportApi.post(this.getQueryParams())
this.$message.success(`数据已导出(上限1万条)`)
this.$message.success(`数据已导出(上限2万条)`)
} catch {}
this.loading = false
},

View File

@ -571,4 +571,20 @@ textarea {
color: var(--el-color-info-light-3);
}
}
}
.sc-statistic-error a {
font-size: inherit;
font-weight: inherit;
color: var(--el-color-danger) !important;
&:after {
border-bottom: 1px solid var(--el-color-danger) !important;
bottom: 0;
content: '';
height: 0;
left: 0;
position: absolute;
right: 0;
}
}

View File

@ -108,4 +108,8 @@ html.dark {
.ace_gutter {
background: var(--el-bg-color-overlay);
}
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell {
--el-table-row-hover-bg-color: unset;
}
}

View File

@ -302,4 +302,8 @@
.el-descriptions__body .el-descriptions__table .el-descriptions__cell {
font-size: inherit;
}
.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell {
--el-table-row-hover-bg-color: #fffaf0;
}

View File

@ -62,10 +62,24 @@
</el-button-group>
</form>
</div>
<div class="right-panel"></div>
<div class="right-panel">
<el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button>
</div>
</el-header>
<el-main v-loading="loading" class="nopadding">
<sc-table v-if="tableData.length > 0" :data="tableData" :page-size="100" hide-refresh pagination-layout="total" stripe>
<sc-table
v-if="tableData.length > 0"
:data="tableData"
:page-size="100"
@selection-change="
(items) => {
selection = items
}
"
hide-refresh
pagination-layout="total"
stripe>
<el-table-column type="selection" />
<el-table-column :label="$t('键名')" prop="key" />
<el-table-column :label="$t('数据类型')" align="center" prop="type" width="100" />
<el-table-column :label="$t('过期时间')" align="right" prop="expireTime" width="200" />
@ -73,7 +87,14 @@
:buttons="[
{
icon: 'el-icon-view',
click: rowClick,
click: viewClick,
},
{
icon: 'el-icon-delete',
type: 'danger',
confirm: true,
title: '删除缓存',
click: delClick,
},
]"
:vue="this"
@ -94,6 +115,7 @@ export default {
},
data() {
return {
selection: [],
loading: true,
dialog: {
info: false,
@ -113,6 +135,24 @@ export default {
}
},
methods: {
//
async batchDel() {
let loading
try {
await this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning',
})
loading = this.$loading()
const res = await this.$API.sys_cache.bulkDeleteEntry.post({
items: this.selection,
})
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
await this.getData()
loading?.close()
},
reset() {
this.query.keywords = ''
this.search()
@ -120,10 +160,19 @@ export default {
search() {
this.getData()
},
async rowClick(row) {
async delClick(row) {
try {
const res = await this.$API.sys_cache.deleteEntry.post({ key: row.key })
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
await this.getData()
},
async viewClick(row) {
this.dialog.info = true
await this.$nextTick()
this.$refs.info.open(
await this.$refs.info.open(
() => this.$t('缓存详情'),
() => this.$API.sys_cache.getEntry.post({ key: row.key }),
)

View File

@ -227,7 +227,7 @@ export default {
const apiCrcs = data.data.rows?.map((x) => x.apiPathCrc32)
const ips = data.data.rows?.map((x) => x.createdClientIp) ?? []
const res = await Promise.all([
ownerIds && ownerIds.length > 0
ownerIds && ownerIds.length > 0 && this.$GLOBAL.permissions.some((x) => x === '*/*/*' || x === 'sys/log/operation/user')
? this.$API.sys_user.query.post({
dynamicFilter: {
field: 'id',