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

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-10-14 13:55:53 +08:00 committed by GitHub
parent dfe6b03b21
commit 58e4572723
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
185 changed files with 4732 additions and 1086 deletions

View File

@ -10,6 +10,7 @@ ij_xml_text_wrap = off # IntelliJ IDEA 中 XML 文本不换行
indent_size = 4 # 缩进大小为 4 个空格
indent_style = space # 使用空格进行缩进
insert_final_newline = false # 不在文件末尾插入空行
max_line_length = 150 # 行长度限制为 150 个字符
trim_trailing_whitespace = true # 删除行尾的空格
[{*.json,*.yml}]

View File

@ -25,7 +25,7 @@
<Title>$(AssemblyName)</Title>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MinVer" Version="6.0.0-rc.1">
<PackageReference Include="MinVer" Version="6.0.0">
<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.7 AS base
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 8080
RUN apt update

View File

@ -78,6 +78,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002EMemberReordering_002EMigrations_002ECSharpFileLayoutPatternRemoveIsAttributeUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.12.4">
<PackageReference Include="Roslynator.Analyzers" Version="4.12.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,8 +1,8 @@
{
"version": "1.6.0",
"devDependencies": {
"cz-git": "^1.9.4",
"commitizen": "^4.3.0",
"cz-git": "^1.10.1",
"commitizen": "^4.3.1",
"prettier": "^3.3.3",
"standard-version": "^9.5.0"
},

View File

@ -9,7 +9,7 @@
"packages": [
{
"packageName": "Furion.Pure.NS",
"version": "4.9.5.2-ns1"
"version": "4.9.5.8-ns1"
}
]
}

View File

@ -9,8 +9,8 @@ namespace NetAdmin.AdmServer.Host.Extensions;
public static class IApplicationBuilderExtensions
{
private const string _INDEX_HTML_PATH = ".index.html";
private const string _RES_PFX = $"{nameof(NetAdmin)}.{nameof(AdmServer)}.{nameof(Host)}.UI";
private static readonly Regex _regex = new(@"\.(\w+)$", RegexOptions.Compiled);
private const string _RES_PFX = $"{nameof(NetAdmin)}.{nameof(AdmServer)}.{nameof(Host)}.UI";
private static readonly Regex _regex = new(@"\.(\w+)$", RegexOptions.Compiled);
private static IEnumerable<string> _allResNames;
/// <summary>

View File

@ -21,8 +21,7 @@ public static class ServiceCollectionExtensions
(Startup.Args.InsertSeedData ? FreeSqlInitMethods.InsertSeedData : FreeSqlInitMethods.None), freeSql => {
// 数据权限过滤器
_ = freeSql.GlobalFilter.ApplyOnlyIf<IFieldOwner>( //
Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA
, () => ContextUserInfo.Create()?.Roles.All(x => x.DataScope == DataScopes.Self) ?? false
Chars.FLG_FREE_SQL_GLOBAL_FILTER_DATA, () => ContextUserInfo.Create()?.Roles.All(x => x.DataScope == DataScopes.Self) ?? false
, a => a.OwnerId == ContextUserInfo.Create().Id);
});
}

View File

@ -1,3 +1,26 @@
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
BUG
*/
using NetAdmin.AdmServer.Host;
using NetAdmin.AdmServer.Host.Extensions;
using NetAdmin.Host.Extensions;

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"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
</ItemGroup>
</Project>

View File

@ -5,10 +5,8 @@ namespace NetAdmin.Application.Repositories;
/// <summary>
/// 基础仓储
/// </summary>
public sealed class BasicRepository<TEntity, TPrimary>(
IFreeSql fSql
, UnitOfWorkManager uowManger
, ContextUserToken userToken) : DefaultRepository<TEntity, TPrimary>(fSql, uowManger)
public sealed class BasicRepository<TEntity, TPrimary>(IFreeSql fSql, UnitOfWorkManager uowManger, ContextUserToken userToken)
: DefaultRepository<TEntity, TPrimary>(fSql, uowManger)
where TEntity : EntityBase<TPrimary> //
where TPrimary : IEquatable<TPrimary>
{

View File

@ -20,19 +20,15 @@ public abstract class RedisService<TEntity, TPrimary, TLogger>(BasicRepository<T
/// </summary>
protected IDatabase RedisDatabase { get; } //
= App.GetService<IConnectionMultiplexer>()
.GetDatabase(App.GetOptions<RedisOptions>()
.Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE)
.Database);
.GetDatabase(App.GetOptions<RedisOptions>().Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE).Database);
/// <summary>
/// 获取锁
/// </summary>
protected Task<RedisLocker> GetLockerAsync(string lockerName)
{
return RedisLocker.GetLockerAsync(RedisDatabase, lockerName
, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_EXPIRY)
, Numbers.MAX_LIMIT_RETRY_CNT_REDIS_LOCK
, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_RETRY_DELAY));
return RedisLocker.GetLockerAsync(RedisDatabase, lockerName, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_EXPIRY)
, Numbers.MAX_LIMIT_RETRY_CNT_REDIS_LOCK, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_RETRY_DELAY));
}
/// <summary>
@ -40,8 +36,7 @@ public abstract class RedisService<TEntity, TPrimary, TLogger>(BasicRepository<T
/// </summary>
protected Task<RedisLocker> GetLockerOnceAsync(string lockerName)
{
return RedisLocker.GetLockerAsync(RedisDatabase, lockerName
, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_EXPIRY), 1
return RedisLocker.GetLockerAsync(RedisDatabase, lockerName, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_EXPIRY), 1
, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_RETRY_DELAY));
}
}

View File

@ -12,8 +12,7 @@ namespace NetAdmin.Application.Services;
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TPrimary">主键类型</typeparam>
/// <typeparam name="TLogger">日志类型</typeparam>
public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicRepository<TEntity, TPrimary> rpo)
: ServiceBase<TLogger>
public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicRepository<TEntity, TPrimary> rpo) : ServiceBase<TLogger>
where TEntity : EntityBase<TPrimary> //
where TPrimary : IEquatable<TPrimary>
{
@ -34,7 +33,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
/// 导出实体
/// </summary>
protected static async Task<IActionResult> ExportAsync<TQuery, TExport>( //
Func<QueryReq<TQuery>, ISelectGrouping<TEntity, TEntity>> selector, QueryReq<TQuery> query, string fileName
Func<QueryReq<TQuery>, ISelectGrouping<TEntity, TEntity>> selector, QueryReq<TQuery> query, string fileName
, Expression<Func<ISelectGroupingAggregate<TEntity, TEntity>, object>> listExp = null)
where TQuery : DataAbstraction, new()
{
@ -46,8 +45,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
/// 导出实体
/// </summary>
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)
Func<QueryReq<TQuery>, ISelect<TEntity>> selector, QueryReq<TQuery> query, string fileName, Expression<Func<TEntity, object>> listExp = null)
where TQuery : DataAbstraction, new()
{
var select = selector(query)
@ -56,9 +54,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
#endif
.Take(Numbers.MAX_LIMIT_EXPORT);
object list = listExp == null
? await select.ToListAsync().ConfigureAwait(false)
: await select.ToListAsync(listExp).ConfigureAwait(false);
object list = listExp == null ? await select.ToListAsync().ConfigureAwait(false) : await select.ToListAsync(listExp).ConfigureAwait(false);
return await GetExportFileStreamAsync<TExport>(fileName, list).ConfigureAwait(false);
}
@ -108,10 +104,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
{
// 默认匹配主键
whereExp ??= a => a.Id.Equals(newValue.Id);
return BuildUpdate(newValue, includeFields, excludeFields, ignoreVersion)
.Where(whereExp)
.Where(whereSql)
.ExecuteUpdatedAsync();
return BuildUpdate(newValue, includeFields, excludeFields, ignoreVersion).Where(whereExp).Where(whereSql).ExecuteUpdatedAsync();
}
#endif
@ -134,8 +127,9 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
App.HttpContext.Response.Headers.ContentDisposition
= new ContentDispositionHeaderValue(Chars.FLG_HTTP_HEADER_VALUE_ATTACHMENT) {
FileNameStar = $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv"
}.ToString();
FileNameStar
= $"{fileName}_{DateTime.Now:yyyy.MM.dd-HH.mm.ss}.csv"
}.ToString();
return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM);
}
@ -149,9 +143,7 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
var updateExp = includeFields == null
? Rpo.UpdateDiy.SetSource(entity)
: Rpo.UpdateDiy.SetDto(includeFields!.ToDictionary(
x => x
, x => typeof(TEntity).GetProperty(x, BindingFlags.Public | BindingFlags.Instance)!
.GetValue(entity)));
x => x, x => typeof(TEntity).GetProperty(x, BindingFlags.Public | BindingFlags.Instance)!.GetValue(entity)));
if (excludeFields != null) {
updateExp = updateExp.IgnoreColumns(excludeFields);
}

View File

@ -5,8 +5,7 @@ namespace NetAdmin.Cache;
/// <summary>
/// 缓存基类
/// </summary>
public abstract class CacheBase<TCacheContainer, TService>(TCacheContainer cache, TService service)
: ICache<TCacheContainer, TService>
public abstract class CacheBase<TCacheContainer, TService>(TCacheContainer cache, TService service) : ICache<TCacheContainer, TService>
where TService : IService
{
/// <inheritdoc />

View File

@ -6,8 +6,7 @@ namespace NetAdmin.Cache;
/// <summary>
/// 分布式缓存
/// </summary>
public abstract class DistributedCache<TService>(IDistributedCache cache, TService service)
: CacheBase<IDistributedCache, TService>(cache, service)
public abstract class DistributedCache<TService>(IDistributedCache cache, TService service) : CacheBase<IDistributedCache, TService>(cache, service)
where TService : IService
{
/// <summary>
@ -66,12 +65,10 @@ public abstract class DistributedCache<TService>(IDistributedCache cache, TServi
/// <param name="slideLifeTime">滑动过期时间</param>
/// <typeparam name="T">缓存对象类型</typeparam>
/// <returns>缓存对象</returns>
protected async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> createProc, TimeSpan? absLifeTime = null
, TimeSpan? slideLifeTime = null)
protected async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T>> createProc, TimeSpan? absLifeTime = null, TimeSpan? slideLifeTime = null)
{
var cacheRead = await GetAsync<T>(key).ConfigureAwait(false);
if (cacheRead is not null && App.HttpContext?.Request.Headers.CacheControl.FirstOrDefault() !=
Chars.FLG_HTTP_HEADER_VALUE_NO_CACHE) {
if (cacheRead is not null && App.HttpContext?.Request.Headers.CacheControl.FirstOrDefault() != Chars.FLG_HTTP_HEADER_VALUE_NO_CACHE) {
return cacheRead;
}

View File

@ -5,6 +5,5 @@ namespace NetAdmin.Cache;
/// <summary>
/// 内存缓存
/// </summary>
public abstract class MemoryCache<TService>(IMemoryCache cache, TService service)
: CacheBase<IMemoryCache, TService>(cache, service)
public abstract class MemoryCache<TService>(IMemoryCache cache, TService service) : CacheBase<IMemoryCache, TService>(cache, service)
where TService : IService;

View File

@ -12,8 +12,7 @@ public sealed class ApiIdAttribute : ValidationAttribute
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var service = App.GetService(App.EffectiveTypes.Single(
x => x.FullName == "NetAdmin.SysComponent.Cache.Sys.Dependency.IApiCache"));
var service = App.GetService(App.EffectiveTypes.Single(x => x.FullName == "NetAdmin.SysComponent.Cache.Sys.Dependency.IApiCache"));
var req = new QueryReq<QueryApiReq> { Filter = new QueryApiReq { Id = value as string } };

View File

@ -9,8 +9,6 @@ public sealed class JsonStringAttribute : ValidationAttribute
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
return (value as string).IsJsonString()
? ValidationResult.Success
: new ValidationResult(Ln.JSON字符串, new[] { validationContext.MemberName });
return (value as string).IsJsonString() ? ValidationResult.Success : new ValidationResult(Ln.JSON字符串, [validationContext.MemberName]);
}
}

View File

@ -12,8 +12,7 @@ public sealed class UserIdAttribute : ValidationAttribute
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var service = App.GetService(App.EffectiveTypes.Single(
x => x.FullName == "NetAdmin.SysComponent.Cache.Sys.Dependency.IUserCache"));
var service = App.GetService(App.EffectiveTypes.Single(x => x.FullName == "NetAdmin.SysComponent.Cache.Sys.Dependency.IUserCache"));
var req = new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = (long)value! } };

View File

@ -45,9 +45,7 @@ public sealed record ContextUserToken : DataAbstraction
/// </summary>
public static ContextUserToken Create(QueryUserRsp user)
{
return new ContextUserToken {
Id = user.Id, Token = user.Token, UserName = user.UserName, DeptId = user.DeptId
};
return new ContextUserToken { Id = user.Id, Token = user.Token, UserName = user.UserName, DeptId = user.DeptId };
}
/// <summary>

View File

@ -30,9 +30,7 @@ public abstract record DataAbstraction
/// </summary>
public void TruncateStrings()
{
foreach (var property in GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof(string))) {
foreach (var property in GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.PropertyType == typeof(string))) {
var maxLen = property.GetCustomAttribute<MaxLengthAttribute>(true)?.Length;
if (maxLen is null or 0) {
continue;

View File

@ -5,7 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// </summary>
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(CatalogId)}_{nameof(Key)}", $"{nameof(CatalogId)},{nameof(Key)}", true)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_DicContent))]
public record Sys_DicContent : VersionEntity
public record Sys_DicContent : VersionEntity, IFieldEnabled, IFieldSummary
{
/// <summary>
/// 字典目录
@ -23,6 +23,12 @@ public record Sys_DicContent : VersionEntity
[JsonIgnore]
public virtual long CatalogId { get; init; }
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[Column]
[CsvIgnore]
[JsonIgnore]
public virtual bool Enabled { get; init; }
/// <summary>
/// 键名称
/// </summary>
@ -31,6 +37,14 @@ public record Sys_DicContent : VersionEntity
[JsonIgnore]
public virtual string Key { get; init; }
/// <summary>
/// 描述
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[CsvIgnore]
[JsonIgnore]
public virtual string Summary { get; init; }
/// <summary>
/// 键值
/// </summary>

View File

@ -7,8 +7,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(OwnerId), nameof(OwnerId), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_LoginLog))]
public record Sys_LoginLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFieldCreatedClientIp
, IFieldCreatedClientUserAgent
public record Sys_LoginLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFieldCreatedClientIp, IFieldCreatedClientUserAgent
{
/// <inheritdoc />
[Column]

View File

@ -10,8 +10,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(OwnerId), nameof(OwnerId), false)]
[SqlIndex(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table( //
Name = $"{Chars.FLG_DB_TABLE_NAME_PREFIX}{nameof(Sys_RequestLog)}_{{yyyyMMdd}}"
, AsTable = $"{nameof(CreatedTime)}=2024-1-1(1 day)")]
Name = $"{Chars.FLG_DB_TABLE_NAME_PREFIX}{nameof(Sys_RequestLog)}_{{yyyyMMdd}}", AsTable = $"{nameof(CreatedTime)}=2024-5-1(1 day)")]
public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldOwner, IFieldCreatedClientIp
{
/// <summary>

View File

@ -123,20 +123,11 @@ public record Sys_Role : VersionEntity, IFieldSort, IFieldEnabled, IFieldSummary
{
_ = config.ForType<CreateRoleReq, Sys_Role>()
.Map( //
d => d.Depts
, s => s.DeptIds.NullOrEmpty()
? Array.Empty<Sys_Dept>()
: s.DeptIds.Select(x => new Sys_Dept { Id = x }))
d => d.Depts, s => s.DeptIds.NullOrEmpty() ? Array.Empty<Sys_Dept>() : s.DeptIds.Select(x => new Sys_Dept { Id = x }))
.Map( //
d => d.Menus
, s => s.MenuIds.NullOrEmpty()
? Array.Empty<Sys_Menu>()
: s.MenuIds.Select(x => new Sys_Menu { Id = x }))
d => d.Menus, s => s.MenuIds.NullOrEmpty() ? Array.Empty<Sys_Menu>() : s.MenuIds.Select(x => new Sys_Menu { Id = x }))
.Map( //
d => d.Apis
, s => s.ApiIds.NullOrEmpty()
? Array.Empty<Sys_Api>()
: s.ApiIds.Select(x => new Sys_Api { Id = x }))
d => d.Apis, s => s.ApiIds.NullOrEmpty() ? Array.Empty<Sys_Api>() : s.ApiIds.Select(x => new Sys_Api { Id = x }))
//
;

View File

@ -91,20 +91,11 @@ public record Sys_SiteMsg : VersionEntity, IRegister, IFieldSummary
.Map( //
d => d.Summary, s => s.Content.RemoveHtmlTag().HtmlDe().Sub(0, 100))
.Map( //
d => d.Roles
, s => s.RoleIds.NullOrEmpty()
? Array.Empty<Sys_Role>()
: s.RoleIds.Select(x => new Sys_Role { Id = x }))
d => d.Roles, s => s.RoleIds.NullOrEmpty() ? Array.Empty<Sys_Role>() : s.RoleIds.Select(x => new Sys_Role { Id = x }))
.Map( //
d => d.Users
, s => s.UserIds.NullOrEmpty()
? Array.Empty<Sys_User>()
: s.UserIds.Select(x => new Sys_User { Id = x }))
d => d.Users, s => s.UserIds.NullOrEmpty() ? Array.Empty<Sys_User>() : s.UserIds.Select(x => new Sys_User { Id = x }))
.Map( //
d => d.Depts
, s => s.DeptIds.NullOrEmpty()
? Array.Empty<Sys_Dept>()
: s.DeptIds.Select(x => new Sys_Dept { Id = x }))
d => d.Depts, s => s.DeptIds.NullOrEmpty() ? Array.Empty<Sys_Dept>() : s.DeptIds.Select(x => new Sys_Dept { Id = x }))
//
;

View File

@ -3,8 +3,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 站内信-部门映射表
/// </summary>
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(DeptId)}_{nameof(SiteMsgId)}", $"{nameof(DeptId)},{nameof(SiteMsgId)}"
, true)]
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(DeptId)}_{nameof(SiteMsgId)}", $"{nameof(DeptId)},{nameof(SiteMsgId)}", true)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgDept))]
public record Sys_SiteMsgDept : ImmutableEntity
{

View File

@ -5,8 +5,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 站内信标记表
/// </summary>
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(SiteMsgId)}_{nameof(UserId)}", $"{nameof(SiteMsgId)},{nameof(UserId)}"
, true)]
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(SiteMsgId)}_{nameof(UserId)}", $"{nameof(SiteMsgId)},{nameof(UserId)}", true)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgFlag))]
public record Sys_SiteMsgFlag : MutableEntity
{

View File

@ -3,8 +3,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 站内信-角色映射表
/// </summary>
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(RoleId)}_{nameof(SiteMsgId)}", $"{nameof(RoleId)},{nameof(SiteMsgId)}"
, true)]
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(RoleId)}_{nameof(SiteMsgId)}", $"{nameof(RoleId)},{nameof(SiteMsgId)}", true)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgRole))]
public record Sys_SiteMsgRole : ImmutableEntity
{

View File

@ -3,8 +3,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 站内信-用户映射表
/// </summary>
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(UserId)}_{nameof(SiteMsgId)}", $"{nameof(UserId)},{nameof(SiteMsgId)}"
, true)]
[SqlIndex($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(UserId)}_{nameof(SiteMsgId)}", $"{nameof(UserId)},{nameof(SiteMsgId)}", true)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_SiteMsgUser))]
public record Sys_SiteMsgUser : ImmutableEntity
{

View File

@ -130,18 +130,12 @@ public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
.Map(d => d.Password, s => s.PasswordText.Pwd().Guid())
.Map(d => d.Token, _ => Guid.NewGuid())
.Map( //
d => d.Roles
, s => s.RoleIds.NullOrEmpty()
? Array.Empty<Sys_Role>()
: s.RoleIds.Select(x => new Sys_Role { Id = x }));
d => d.Roles, s => s.RoleIds.NullOrEmpty() ? Array.Empty<Sys_Role>() : s.RoleIds.Select(x => new Sys_Role { Id = x }));
_ = config.ForType<EditUserReq, Sys_User>()
.Map( //
d => d.Password, s => s.PasswordText.NullOrEmpty() ? Guid.Empty : s.PasswordText.Pwd().Guid())
.Map( //
d => d.Roles
, s => s.RoleIds.NullOrEmpty()
? Array.Empty<Sys_Role>()
: s.RoleIds.Select(x => new Sys_Role { Id = x }));
d => d.Roles, s => s.RoleIds.NullOrEmpty() ? Array.Empty<Sys_Role>() : s.RoleIds.Select(x => new Sys_Role { Id = x }));
}
}

View File

@ -228,7 +228,6 @@ public record Sys_UserProfile : VersionEntity, IRegister
.Map(d => d.CompanyArea, s => s.CompanyArea == null ? null : s.CompanyArea.Value)
.Map(d => d.HomeArea, s => s.HomeArea == null ? null : s.HomeArea.Value)
.Map( //
d => d.EmergencyContactArea
, s => s.EmergencyContactArea == null ? null : s.EmergencyContactArea.Value);
d => d.EmergencyContactArea, s => s.EmergencyContactArea == null ? null : s.EmergencyContactArea.Value);
}
}

View File

@ -74,8 +74,8 @@ public sealed record DynamicFilterInfo : DataAbstraction
}
}
if (new[] { nameof(IFieldCreatedClientIp.CreatedClientIp), nameof(IFieldModifiedClientIp.ModifiedClientIp) }
.Contains(d?.Field, StringComparer.OrdinalIgnoreCase)) {
if (new[] { nameof(IFieldCreatedClientIp.CreatedClientIp), nameof(IFieldModifiedClientIp.ModifiedClientIp) }.Contains(
d?.Field, StringComparer.OrdinalIgnoreCase)) {
var val = d!.Value?.ToString();
if (val?.IsIpV4() == true) {
d.Value = val.IpV4ToInt32();

View File

@ -6,13 +6,13 @@ namespace NetAdmin.Domain.Dto.Sys.Cache;
public sealed record CacheStatisticsRsp : DataAbstraction
{
private static readonly Regex[] _regexes = [
new Regex(@"keyspace_hits:(\d+)", RegexOptions.Compiled) //
, new Regex(@"keyspace_misses:(\d+)", RegexOptions.Compiled) //
, new Regex(@"uptime_in_seconds:(\d+)", RegexOptions.Compiled) //
, new Regex(@"used_cpu_sys:([\d\\.]+)", RegexOptions.Compiled) //
, new Regex(@"used_cpu_user:([\d\\.]+)", RegexOptions.Compiled) //
, new Regex(@"used_memory:(\d+)", RegexOptions.Compiled) //
, new Regex("redis_version:(.+)", RegexOptions.Compiled) //
new(@"keyspace_hits:(\d+)", RegexOptions.Compiled) //
, new(@"keyspace_misses:(\d+)", RegexOptions.Compiled) //
, new(@"uptime_in_seconds:(\d+)", RegexOptions.Compiled) //
, new(@"used_cpu_sys:([\d\\.]+)", RegexOptions.Compiled) //
, new(@"used_cpu_user:([\d\\.]+)", RegexOptions.Compiled) //
, new(@"used_memory:(\d+)", RegexOptions.Compiled) //
, new("redis_version:(.+)", RegexOptions.Compiled) //
];
/// <summary>
@ -25,13 +25,12 @@ public sealed record CacheStatisticsRsp : DataAbstraction
/// </summary>
public CacheStatisticsRsp(string redisResult)
{
KeyspaceHits = _regexes[0].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
KeyspaceHits = _regexes[0].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
KeyspaceMisses = _regexes[1].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
UpTime = _regexes[2].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
UsedCpu = _regexes[3].Match(redisResult).Groups[1].Value.Trim().DecTry(0) +
_regexes[4].Match(redisResult).Groups[1].Value.Trim().DecTry(0);
UpTime = _regexes[2].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
UsedCpu = _regexes[3].Match(redisResult).Groups[1].Value.Trim().DecTry(0) + _regexes[4].Match(redisResult).Groups[1].Value.Trim().DecTry(0);
UsedMemory = _regexes[5].Match(redisResult).Groups[1].Value.Trim().Int64Try(0);
Version = _regexes[6].Match(redisResult).Groups[1].Value.Trim();
Version = _regexes[6].Match(redisResult).Groups[1].Value.Trim();
}
/// <summary>

View File

@ -10,13 +10,21 @@ public record CreateDicContentReq : Sys_DicContent
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.字典目录编号不能为空))]
public override long CatalogId { get; init; }
/// <inheritdoc cref="Sys_DicContent.Key" />
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; } = true;
/// <inheritdoc cref="Sys_DicContent.Key" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.键名称不能为空))]
public override string Key { get; init; }
/// <inheritdoc cref="Sys_DicContent.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
/// <inheritdoc cref="Sys_DicContent.Value" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.键值不能为空))]
public override string Value { get; init; }
}

View File

@ -11,12 +11,24 @@ public sealed record ExportDicContentRsp : QueryDicContentRsp
[CsvName(nameof(Ln.创建时间))]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc />
[CsvIndex(3)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.是否启用))]
public override bool Enabled { get; init; }
/// <inheritdoc />
[CsvIndex(0)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.项名))]
public override string Key { get; init; }
/// <inheritdoc />
[CsvIndex(4)]
[CsvIgnore(false)]
[CsvName(nameof(Ln.备注))]
public override string Summary { get; init; }
/// <inheritdoc />
[CsvIndex(1)]
[CsvIgnore(false)]

View File

@ -13,10 +13,18 @@ public record QueryDicContentRsp : Sys_DicContent
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="Sys_DicContent.Key" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Key { get; init; }
/// <inheritdoc cref="Sys_DicContent.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
/// <inheritdoc cref="Sys_DicContent.Value" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Value { get; init; }

View File

@ -0,0 +1,15 @@
namespace NetAdmin.Domain.Dto.Sys.Dic.Content;
/// <summary>
/// 请求:设置字典内容启用状态
/// </summary>
public sealed record SetDicContentEnabledReq : Sys_DicContent
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -19,8 +19,7 @@ public record CreateJobReq : Sys_Job
public override string ExecutionCron { get; init; }
/// <inheritdoc cref="Sys_Job.HttpMethod" />
[EnumDataType(typeof(HttpMethods), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(HttpMethods), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.请求方法不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override HttpMethods HttpMethod { get; init; }

View File

@ -15,8 +15,7 @@ public record QueryJobRsp : Sys_Job
/// Cron 表达式描述
/// </summary>
[JsonInclude]
public string CronDescription =>
ExpressionDescriptor.GetDescription(ExecutionCron, new Options { Locale = "zh-CN" });
public string CronDescription => ExpressionDescriptor.GetDescription(ExecutionCron, new Options { Locale = "zh-CN" });
/// <inheritdoc cref="Sys_Job.LastStatusCode" />
[JsonInclude]

View File

@ -15,8 +15,7 @@ public sealed record MetaInfo : DataAbstraction
/// <summary>
/// Initializes a new instance of the <see cref="MetaInfo" /> class.
/// </summary>
public MetaInfo(string color, bool fullPage, bool hidden, bool hiddenBreadCrumb, string icon, string tag
, string title, MenuTypes type)
public MetaInfo(string color, bool fullPage, bool hidden, bool hiddenBreadCrumb, string icon, string tag, string title, MenuTypes type)
{
Color = color;
FullPage = fullPage;
@ -73,8 +72,7 @@ public sealed record MetaInfo : DataAbstraction
/// <summary>
/// 类型
/// </summary>
[EnumDataType(typeof(MenuTypes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(MenuTypes), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.菜单类型不正确))]
[JsonInclude]
public MenuTypes Type { get; init; }
}

View File

@ -18,8 +18,7 @@ public record CreateRoleReq : Sys_Role, IValidatableObject
public override string DashboardLayout { get; set; }
/// <inheritdoc cref="Sys_Role.DataScope" />
[EnumDataType(typeof(DataScopes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(DataScopes), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.角色数据范围不正确))]
public override DataScopes DataScope { get; init; } = DataScopes.All;
/// <summary>

View File

@ -20,8 +20,7 @@ public record CreateSiteMsgReq : Sys_SiteMsg
public IReadOnlyCollection<long> DeptIds { get; init; }
/// <inheritdoc cref="Sys_SiteMsg.MsgType" />
[EnumDataType(typeof(SiteMsgTypes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(SiteMsgTypes), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.站内信类型不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override SiteMsgTypes MsgType { get; init; }

View File

@ -12,8 +12,7 @@ public record CreateSiteMsgFlagReq : Sys_SiteMsgFlag
public override long SiteMsgId { get; init; }
/// <inheritdoc cref="Sys_SiteMsgFlag.UserSiteMsgStatus" />
[EnumDataType(typeof(UserSiteMsgStatues), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(UserSiteMsgStatues), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.站内信状态不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override UserSiteMsgStatues UserSiteMsgStatus { get; init; }
}

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Domain.Dto.Sys.Tool;
/// <summary>
/// 请求Aes解密
/// </summary>
public record AesDecodeReq : DataAbstraction
{
/// <summary>
/// 密文
/// </summary>
public string CipherText { get; init; }
}

View File

@ -16,6 +16,14 @@ public record QueryUserRsp : Sys_User
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? CreatedUserId { get; init; }
/// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string CreatedUserName { get; init; }
/// <inheritdoc cref="Sys_User.Dept" />
public new virtual QueryDeptRsp Dept { get; init; }

View File

@ -17,8 +17,7 @@ public record CreateUserProfileReq : Sys_UserProfile
public override string CertificateNumber { get; init; }
/// <inheritdoc cref="Sys_UserProfile.CertificateType" />
[EnumDataType(typeof(CertificateTypes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(CertificateTypes), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.证件类型不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override CertificateTypes? CertificateType { get; init; }
@ -39,8 +38,7 @@ public record CreateUserProfileReq : Sys_UserProfile
public override string CompanyTelephone { get; init; }
/// <inheritdoc cref="Sys_UserProfile.Education" />
[EnumDataType(typeof(Educations), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(Educations), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.学历不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override Educations? Education { get; init; }
@ -82,8 +80,7 @@ public record CreateUserProfileReq : Sys_UserProfile
public override string HomeTelephone { get; init; }
/// <inheritdoc cref="Sys_UserProfile.MarriageStatus" />
[EnumDataType(typeof(MarriageStatues), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(MarriageStatues), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.婚姻状况不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override MarriageStatues? MarriageStatus { get; init; }
@ -96,8 +93,7 @@ public record CreateUserProfileReq : Sys_UserProfile
public new CreateDicContentReq NationArea { get; init; }
/// <inheritdoc cref="Sys_UserProfile.PoliticalStatus" />
[EnumDataType(typeof(PoliticalStatues), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(PoliticalStatues), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.政治面貌不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override PoliticalStatues? PoliticalStatus { get; init; }

View File

@ -14,8 +14,7 @@ public sealed record SendVerifyCodeReq : Sys_VerifyCode, IValidatableObject
public override string DestDevice { get; init; }
/// <inheritdoc cref="Sys_VerifyCode.DeviceType" />
[EnumDataType(typeof(VerifyCodeDeviceTypes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(VerifyCodeDeviceTypes), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.验证码目标设备类型不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.设备类型不能为空))]
public override VerifyCodeDeviceTypes DeviceType { get; init; }
@ -24,8 +23,7 @@ public sealed record SendVerifyCodeReq : Sys_VerifyCode, IValidatableObject
public override VerifyCodeStatues Status => VerifyCodeStatues.Waiting;
/// <inheritdoc cref="Sys_VerifyCode.Type" />
[EnumDataType(typeof(VerifyCodeTypes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
[EnumDataType(typeof(VerifyCodeTypes), ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.验证码类型不正确))]
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.验证码类型不能为空))]
public override VerifyCodeTypes Type { get; init; }
@ -43,22 +41,20 @@ public sealed record SendVerifyCodeReq : Sys_VerifyCode, IValidatableObject
switch (DeviceType) {
case VerifyCodeDeviceTypes.Email:
validationResult = new EmailAttribute().GetValidationResult(DestDevice, validationContext);
if (validationResult == null) {
yield break;
}
yield return new ValidationResult(validationResult.ErrorMessage, new[] { nameof(DestDevice) });
break;
case VerifyCodeDeviceTypes.Mobile:
validationResult = new MobileAttribute().GetValidationResult(DestDevice, validationContext);
if (validationResult == null) {
yield break;
}
yield return new ValidationResult(validationResult.ErrorMessage, new[] { nameof(DestDevice) });
break;
default:
yield break;
}
if (validationResult == null) {
yield break;
}
yield return new ValidationResult(validationResult.ErrorMessage, [nameof(DestDevice)]);
}
}

View File

@ -17,7 +17,6 @@ public sealed record SyncStructureAfterEvent : SyncStructureBeforeEvent
/// <inheritdoc />
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}: {1}: {2}", Id, Ln.
, string.Join(',', EntityTypes.Select(x => x.Name)));
return string.Format(CultureInfo.InvariantCulture, "{0}: {1}: {2}", Id, Ln., string.Join(',', EntityTypes.Select(x => x.Name)));
}
}

View File

@ -10,8 +10,8 @@ public sealed record RequestLogEvent : DataAbstraction, IEventSourceGeneric<Crea
/// <summary>
/// Initializes a new instance of the <see cref="RequestLogEvent" /> class.
/// </summary>
public RequestLogEvent(CreateRequestLogReq data, bool isConsumOnce = false, object payload = default
, DateTime createdTime = default, CancellationToken cancellationToken = default)
public RequestLogEvent(CreateRequestLogReq data, bool isConsumOnce = false, object payload = default, DateTime createdTime = default
, CancellationToken cancellationToken = default)
{
Data = data;
IsConsumOnce = isConsumOnce;

View File

@ -10,8 +10,8 @@ public sealed record UserUpdatedEvent : DataAbstraction, IEventSourceGeneric<Use
/// <summary>
/// Initializes a new instance of the <see cref="UserUpdatedEvent" /> class.
/// </summary>
public UserUpdatedEvent(UserInfoRsp data, DateTime createdTime = default, bool isConsumOnce = false
, object payload = default, CancellationToken cancellationToken = default)
public UserUpdatedEvent(UserInfoRsp data, DateTime createdTime = default, bool isConsumOnce = false, object payload = default
, CancellationToken cancellationToken = default)
{
Data = data;
CancellationToken = cancellationToken;

View File

@ -10,8 +10,8 @@ public sealed record VerifyCodeCreatedEvent : DataAbstraction, IEventSourceGener
/// <summary>
/// Initializes a new instance of the <see cref="VerifyCodeCreatedEvent" /> class.
/// </summary>
public VerifyCodeCreatedEvent(QueryVerifyCodeRsp data, DateTime createdTime = default, bool isConsumOnce = false
, object payload = default, CancellationToken cancellationToken = default)
public VerifyCodeCreatedEvent(QueryVerifyCodeRsp data, DateTime createdTime = default, bool isConsumOnce = false, object payload = default
, CancellationToken cancellationToken = default)
{
Data = data;
CancellationToken = cancellationToken;

View File

@ -67,11 +67,9 @@ public abstract class WorkBase<TLogger>
{
var db = ServiceProvider.GetService<IConnectionMultiplexer>()
.GetDatabase(ServiceProvider.GetService<IOptions<RedisOptions>>()
.Value.Instances
.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE)
.Value.Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE)
.Database);
return RedisLocker.GetLockerAsync(db, lockId, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_EXPIRY)
, Numbers.MAX_LIMIT_RETRY_CNT_REDIS_LOCK
return RedisLocker.GetLockerAsync(db, lockId, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_EXPIRY), Numbers.MAX_LIMIT_RETRY_CNT_REDIS_LOCK
, TimeSpan.FromSeconds(Numbers.SECS_REDIS_LOCK_RETRY_DELAY));
}
}

View File

@ -8,6 +8,7 @@ namespace NetAdmin.Host.Controllers;
/// 探针组件
/// </summary>
[ApiDescriptionSettings("Probe")]
[Produces(Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_JSON)]
public sealed class ProbeController : ControllerBase<ICache<IDistributedCache, IService>, IService>
{
/// <summary>
@ -55,12 +56,7 @@ public sealed class ProbeController : ControllerBase<ICache<IDistributedCache, I
public IActionResult IsSystemSafetyStopped(int logTimeoutSeconds = 15)
#pragma warning restore S3400, CA1822
{
return new ContentResult {
Content = (DateTime.Now - GlobalStatic.LatestLogTime).TotalSeconds >
logTimeoutSeconds
? "1"
: "0"
};
return new ContentResult { Content = (DateTime.Now - GlobalStatic.LatestLogTime).TotalSeconds > logTimeoutSeconds ? "1" : "0" };
}
/// <summary>
@ -82,4 +78,21 @@ public sealed class ProbeController : ControllerBase<ICache<IDistributedCache, I
SafetyShopHostMiddleware.Stop();
return new OkResult();
}
/// <summary>
/// 停止日志计数器
/// </summary>
[AllowAnonymous]
[HttpGet]
#pragma warning disable CA1822
public ActionResult StopLogCounter(string token)
#pragma warning restore CA1822
{
if (token != GlobalStatic.SecretKey) {
return new UnauthorizedResult();
}
GlobalStatic.LogCounterOff = true;
return new OkResult();
}
}

View File

@ -17,26 +17,20 @@ public static class IMvcBuilderExtensions
public static IMvcBuilder AddDefaultApiResultHandler(this IMvcBuilder me)
{
return me.AddInjectWithUnifyResult<DefaultApiResultHandler>(injectOptions => {
injectOptions.ConfigureSwaggerGen(
genOptions => {
// 替换自定义的EnumSchemaFilter支持多语言Resx资源 需将SpecificationDocumentSettings.EnableEnumSchemaFilter配置为false)
genOptions
.SchemaFilter<
SwaggerEnumSchemaFixer>();
injectOptions.ConfigureSwaggerGen(genOptions => {
// 替换自定义的EnumSchemaFilter支持多语言Resx资源 需将SpecificationDocumentSettings.EnableEnumSchemaFilter配置为false)
genOptions.SchemaFilter<SwaggerEnumSchemaFixer>();
// 将程序集版本号与OpenApi版本号同步
foreach (var doc in genOptions
.SwaggerGeneratorOptions
.SwaggerDocs) {
doc.Value.Version
= FileVersionInfo
.GetVersionInfo(
Assembly
.GetEntryAssembly()!
.Location)
.ProductVersion;
}
});
// 枚举显示自身xml comment 而不是$ref原型引用
genOptions.UseInlineDefinitionsForEnums();
// 将程序集版本号与OpenApi版本号同步
foreach (var doc in genOptions.SwaggerGeneratorOptions.SwaggerDocs) {
doc.Value.Version = FileVersionInfo
.GetVersionInfo(Assembly.GetEntryAssembly()!.Location)
.ProductVersion;
}
});
});
}

View File

@ -12,8 +12,7 @@ public static class MethodInfoExtensions
{
return serviceProvider.GetService<IActionDescriptorCollectionProvider>()
.ActionDescriptors.Items.FirstOrDefault(x => x.DisplayName!.StartsWith( //
$"{me.DeclaringType}.{me.Name}"
, StringComparison.Ordinal))
$"{me.DeclaringType}.{me.Name}", StringComparison.Ordinal))
?.AttributeRouteInfo?.Template;
}
}

View File

@ -90,8 +90,8 @@ public static class ServiceCollectionExtensions
var sbLog = new StringBuilder();
foreach (var type in optionsTypes) {
var configureMethod = typeof(ConfigurableOptionsServiceCollectionExtensions).GetMethod(
nameof(ConfigurableOptionsServiceCollectionExtensions.AddConfigurableOptions)
, BindingFlags.Public | BindingFlags.Static, [typeof(IServiceCollection)]);
nameof(ConfigurableOptionsServiceCollectionExtensions.AddConfigurableOptions), BindingFlags.Public | BindingFlags.Static
, [typeof(IServiceCollection)]);
_ = configureMethod!.MakeGenericMethod(type).Invoke(me, [me]);
_ = sbLog.Append(CultureInfo.InvariantCulture, $" {type.Name}");
}
@ -146,36 +146,32 @@ public static class ServiceCollectionExtensions
/// 添加 freeSql orm工具
/// </summary>
public static IServiceCollection AddFreeSql( //
this IServiceCollection me, FreeSqlInitMethods initMethods = FreeSqlInitMethods.None
, Action<IFreeSql> freeSqlConfig = null)
this IServiceCollection me, FreeSqlInitMethods initMethods = FreeSqlInitMethods.None, Action<IFreeSql> freeSqlConfig = null)
{
// // 非调试模式下禁止同步数据库
// #if !DEBUG
// initOptions = FreeSqlInitOptions.None;
// #endif
var freeSql = new FreeSqlBuilder(App.GetOptions<DatabaseOptions>()).Build(initMethods);
_ = me.AddSingleton(freeSql);
var dbOptions = App.GetOptions<DatabaseOptions>();
var fSql = new FreeSqlBuilder(dbOptions).Build(initMethods);
_ = me.AddSingleton(fSql);
freeSql.Aop.AuditValue += SqlAuditor.DataAuditHandler; // Insert/Update自动值处理
fSql.Aop.AuditValue += SqlAuditor.DataAuditHandler; // Insert/Update自动值处理
var eventPublisher = App.GetService<IEventPublisher>();
#pragma warning disable VSTHRD110
// AOP事件发布异步
freeSql.Aop.CommandBefore
+= (_, e) => eventPublisher.PublishAsync(new SqlCommandBeforeEvent(e)); // 增删查改,执行命令之前触发
freeSql.Aop.CommandAfter
+= (_, e) => eventPublisher.PublishAsync(new SqlCommandAfterEvent(e)); // 增删查改,执行命令完成后触发
fSql.Aop.CommandBefore += (_, e) => eventPublisher.PublishAsync(new SqlCommandBeforeEvent(e)); // 增删查改,执行命令之前触发
fSql.Aop.CommandAfter += (_, e) => eventPublisher.PublishAsync(new SqlCommandAfterEvent(e)); // 增删查改,执行命令完成后触发
freeSql.Aop.SyncStructureBefore += (_, e) =>
eventPublisher.PublishAsync(new SyncStructureBeforeEvent(e)); // CodeFirst迁移执行之前触发
fSql.Aop.SyncStructureBefore += (_, e) => eventPublisher.PublishAsync(new SyncStructureBeforeEvent(e)); // CodeFirst迁移执行之前触发
freeSql.Aop.SyncStructureAfter += (_, e) =>
eventPublisher.PublishAsync(new SyncStructureAfterEvent(e)); // CodeFirst迁移执行完成触发
fSql.Aop.SyncStructureAfter += (_, e) => eventPublisher.PublishAsync(new SyncStructureAfterEvent(e)); // CodeFirst迁移执行完成触发
#pragma warning restore VSTHRD110
// 全局过滤器设置
freeSqlConfig?.Invoke(freeSql);
freeSqlConfig?.Invoke(fSql);
return me.AddScoped<UnitOfWorkManager>() // 注入工作单元管理器
.AddFreeRepository(null, App.Assemblies.ToArray()) // 批量注入 Repository
@ -209,8 +205,7 @@ public static class ServiceCollectionExtensions
/// </summary>
public static IServiceCollection AddRedisCache(this IServiceCollection me)
{
var redisOptions = App.GetOptions<RedisOptions>()
.Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE);
var redisOptions = App.GetOptions<RedisOptions>().Instances.First(x => x.Name == Chars.FLG_REDIS_INSTANCE_DATA_CACHE);
// IDistributedCache 分布式缓存通用接口
_ = me.AddStackExchangeRedisCache(options => {

View File

@ -64,8 +64,7 @@ public abstract class ApiResultHandler<T>
{
SetErrorCodeToHeader(context.HttpContext, ErrorCodes.InvalidInput);
return new JsonResult(RestfulResult(ErrorCodes.InvalidInput, metadata.Data
, GetValidationResult(metadata.ValidationResult))) {
return new JsonResult(RestfulResult(ErrorCodes.InvalidInput, metadata.Data, GetValidationResult(metadata.ValidationResult))) {
StatusCode = Numbers.HTTP_STATUS_BIZ_FAIL
};
}

View File

@ -16,8 +16,7 @@ public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logge
}
// 将异常设置到HttpContext.Features中 以便中间件能获取到他
context.HttpContext.Features.Set<IExceptionHandlerFeature>(
new ExceptionHandlerFeature { Error = context.Exception });
context.HttpContext.Features.Set<IExceptionHandlerFeature>(new ExceptionHandlerFeature { Error = context.Exception });
return Task.CompletedTask;
}

View File

@ -13,8 +13,7 @@ public sealed class TransactionInterceptor(UnitOfWorkManager uowManager) : IAsyn
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 跳过没有事务特性标记的方法
if (context.HttpContext.GetControllerActionDescriptor().MethodInfo.GetCustomAttribute<TransactionAttribute>() ==
null) {
if (context.HttpContext.GetControllerActionDescriptor().MethodInfo.GetCustomAttribute<TransactionAttribute>() == null) {
_ = await next().ConfigureAwait(false);
return;
}

View File

@ -10,11 +10,9 @@ public sealed class RequestAuditMiddleware(
, IOptions<DynamicApiControllerSettingsOptions> dynamicApiControllerSettingsOptions
, RequestLogger requestLogger)
{
private readonly PathString _defaultRoutePrefix
= new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}");
private readonly PathString _defaultRoutePrefix = new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}");
private readonly PathString _healthCheckRoutePrefix
= new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}/probe/health.check");
private readonly PathString _healthCheckRoutePrefix = new($"/{dynamicApiControllerSettingsOptions.Value.DefaultRoutePrefix}/probe/health.check");
/// <summary>
/// 主函数
@ -22,11 +20,10 @@ public sealed class RequestAuditMiddleware(
public async Task InvokeAsync(HttpContext context)
{
// 跳过处理的情况:
if (!context.Request.Path.StartsWithSegments(_defaultRoutePrefix) // 非api请求
|| context.Request.Path.StartsWithSegments(_healthCheckRoutePrefix) // 健康检查
|| context.Request.Method == Chars.FLG_HTTP_METHOD_OPTIONS // is options 请求
|| (context.Request.ContentType?.StartsWith("multipart/form-data", true, CultureInfo.InvariantCulture) ??
false) // 文件上传
if (!context.Request.Path.StartsWithSegments(_defaultRoutePrefix) // 非api请求
|| context.Request.Path.StartsWithSegments(_healthCheckRoutePrefix) // 健康检查
|| context.Request.Method == Chars.FLG_HTTP_METHOD_OPTIONS // is options 请求
|| (context.Request.ContentType?.StartsWith("multipart/form-data", true, CultureInfo.InvariantCulture) ?? false) // 文件上传
#pragma warning disable SA1009
) {
#pragma warning restore SA1009
@ -58,8 +55,6 @@ public sealed class RequestAuditMiddleware(
.FirstOrDefault()
?.Enum<ErrorCodes>() ?? 0;
_ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMilliseconds, responseBody, errorCode
, exception)
.ConfigureAwait(false);
_ = await requestLogger.LogAsync(context, (long)sw.Elapsed.TotalMilliseconds, responseBody, errorCode, exception).ConfigureAwait(false);
}
}

View File

@ -53,8 +53,8 @@ public sealed class SafetyShopHostMiddleware(RequestDelegate next)
/// </summary>
public async Task InvokeAsync(HttpContext context)
{
if (Volatile.Read(ref _trafficOff) &&
!context.Request.Path.StartsWithSegments($"/{Chars.FLG_PATH_API_RPOBE}")) {
if (Volatile.Read(ref _trafficOff) && !context.Request.Path.StartsWithSegments($"/{Chars.FLG_PATH_API_RPOBE}") &&
!context.Request.Path.StartsWithSegments($"/{Chars.FLG_PATH_API_METRICS}")) {
context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
return;
}

View File

@ -4,7 +4,7 @@
<ProjectReference Include="../NetAdmin.Cache/NetAdmin.Cache.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="IGeekFan.AspNetCore.Knife4jUI.NS" Condition="'$(Configuration)' == 'Debug'" Version="0.0.15-ns2"/>
<PackageReference Include="IGeekFan.AspNetCore.Knife4jUI.NS" Condition="'$(Configuration)' == 'Debug'" Version="0.0.15-ns3"/>
<PackageReference Include="Spectre.Console.Cli.NS" Version="0.45.1-preview.0.179"/>
<PackageReference Include="prometheus-net.AspNetCore" Condition="'$(Configuration)' != 'Debug'" Version="8.2.1"/>
</ItemGroup>

View File

@ -29,9 +29,7 @@ public abstract class Startup : AppStartup
private static void ShowBanner()
{
AnsiConsole.WriteLine();
var gridInfo = new Grid().AddColumn(new GridColumn().NoWrap().Width(50).PadRight(10))
.AddColumn(new GridColumn().NoWrap())
.Expand();
var gridInfo = new Grid().AddColumn(new GridColumn().NoWrap().Width(50).PadRight(10)).AddColumn(new GridColumn().NoWrap()).Expand();
foreach (var kv in ApplicationHelper.GetEnvironmentInfo().OrderBy(x => x.Key)) {
_ = gridInfo.AddRow(kv.Key, kv.Value.ToString()!.EscapeMarkup());
}

View File

@ -38,10 +38,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver
private static string GetMemberName(JsonPropertyInfo property)
{
return property.GetType()
.GetRuntimeProperties()
.First(x => x.Name == "MemberName")
.GetValue(property) as string;
return property.GetType().GetRuntimeProperties().First(x => x.Name == "MemberName").GetValue(property) as string;
}
/// <summary>
@ -49,10 +46,7 @@ public sealed class CollectionJsonTypeInfoResolver : DefaultJsonTypeInfoResolver
/// </summary>
private static PropertyInfo GetNewProperty(string memberName, object obj)
{
return obj.GetType()
.GetProperties()
.Where(x => x.Name == memberName)
.First(x => x.DeclaringType == x.ReflectedType);
return obj.GetType().GetProperties().Where(x => x.Name == memberName).First(x => x.DeclaringType == x.ReflectedType);
}
/// <summary>

View File

@ -17,15 +17,14 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
/// <summary>
/// 生成审计数据
/// </summary>
public async Task<CreateRequestLogReq> LogAsync(HttpContext context, long duration, string responseBody
, ErrorCodes errorCode, IExceptionHandlerFeature exception)
public async Task<CreateRequestLogReq> LogAsync(HttpContext context, long duration, string responseBody, ErrorCodes errorCode
, IExceptionHandlerFeature exception)
{
// 从请求头中读取用户信息
var associatedUser = GetAssociatedUser(context);
var id = YitIdHelper.NextId();
var requestBody = Array.Exists( //
_textContentTypes
, x => context.Request.ContentType?.Contains(x, StringComparison.OrdinalIgnoreCase) ?? false)
_textContentTypes, x => context.Request.ContentType?.Contains(x, StringComparison.OrdinalIgnoreCase) ?? false)
? await context.ReadBodyContentAsync().ConfigureAwait(false)
: string.Empty;
var apiId = context.Request.Path.Value!.TrimStart('/');

View File

@ -29,10 +29,14 @@ public sealed class SwaggerEnumSchemaFixer : ISchemaFilter
foreach (var e in Enum.GetValues(context.Type).Cast<Enum>()) {
var value = Convert.ToInt64(e, CultureInfo.InvariantCulture);
schema.Enum.Add(new OpenApiLong(value));
var enumName = Enum.GetName(context.Type, e).ToLowerCamelCase();
if (enumName.Length <= 3) {
enumName = enumName.ToLowerInvariant();
}
_ = sb.Append(wrap)
.Append( //
CultureInfo.InvariantCulture
, $"{Enum.GetName(context.Type, e).ToLowerCamelCase()} = {value} ({e.ResDesc<Ln>()})");
CultureInfo.InvariantCulture, $"{enumName} = {value} ({e.ResDesc<Ln>()})");
}
schema.Description = sb.ToString();

View File

@ -13,8 +13,7 @@ public sealed record CaptchaOptions : OptionAbstraction
{
var rtn = new char[32];
for (var i = 0; i != 32; i++) {
rtn[i] = Chars.FLGL_VISIBLE_ASCIIS[
(int)(Math.Abs(Math.Sin(_seed * (i + 1))) * Chars.FLGL_VISIBLE_ASCIIS.Length)];
rtn[i] = Chars.FLGL_VISIBLE_ASCIIS[(int)(Math.Abs(Math.Sin(_seed * (i + 1))) * Chars.FLGL_VISIBLE_ASCIIS.Length)];
}
SecretKey = new string(rtn);

View File

@ -73,6 +73,7 @@ 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_METRICS = "metrics";
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";
@ -89,8 +90,7 @@ public static class Chars
public const string FLGL_HTTP_HEADER_VALUE_UA_PC
= "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
public const string FLGL_VISIBLE_ASCIIS
= """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""";
public const string FLGL_VISIBLE_ASCIIS = """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""";
public const string RGX_CERTIFICATE = "^[a-zA-Z0-9-_]+$";
public const string RGX_INVITE_CODE = """^\d{8}$""";
@ -112,6 +112,5 @@ public static class Chars
public const string RGXL_EMAIL
= """^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$""";
public const string RGXL_IP_V4
= @"^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$";
public const string RGXL_IP_V4 = @"^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$";
}

View File

@ -23,12 +23,12 @@ public static class Numbers
public const int MAX_LIMIT_QUERY_PAGE_SIZE = 100; // 最大限制:分页查询页容量
public const int MAX_LIMIT_RETRY_CNT_REDIS_LOCK = 10; // 最大限制Redis锁重试次数
public const int REQUEST_LOG_BUFF_SIZE = 10; // 请求日志缓冲区大小
public const int SECS_CACHE_CHART = 300; // 秒:缓存时间-仪表
public const int SECS_CACHE_DEFAULT = 60; // 秒:缓存时间-默认
public const int SECS_CACHE_DIC_CATALOG_CODE = 300; // 秒:缓存时间-字典配置-目录代码
public const int SECS_REDIS_LOCK_EXPIRY = 60; // 秒Redis锁过期时间
public const int SECS_REDIS_LOCK_RETRY_DELAY = 1; // 秒Redis锁重试间隔
public const int SECS_TIMEOUT_HTTP_CLIENT = 15; // 秒:超时时间-默认HTTP客户端
public const int SECS_TIMEOUT_JOB = 600; // 秒:超时时间-作业
public const int SCHEDULED_JOB_PARALLEL_NUM = 5; // 计划作业并发数
public const int SECS_CACHE_CHART = 300; // 秒:缓存时间-仪表
public const int SECS_CACHE_DEFAULT = 60; // 秒:缓存时间-默认
public const int SECS_CACHE_DIC_CATALOG_CODE = 300; // 秒:缓存时间-字典配置-目录代码
public const int SECS_REDIS_LOCK_EXPIRY = 60; // 秒Redis锁过期时间
public const int SECS_REDIS_LOCK_RETRY_DELAY = 1; // 秒Redis锁重试间隔
public const int SECS_TIMEOUT_HTTP_CLIENT = 15; // 秒:超时时间-默认HTTP客户端
public const int SECS_TIMEOUT_JOB = 600; // 秒:超时时间-作业
}

View File

@ -10,8 +10,7 @@ public static class HttpRequestPartExtensions
/// <summary>
/// 设置日志
/// </summary>
public static HttpRequestPart SetLog<T>(this HttpRequestPart me, ILogger<T> logger
, Func<string, string> bodyHandle = null)
public static HttpRequestPart SetLog<T>(this HttpRequestPart me, ILogger<T> logger, Func<string, string> bodyHandle = null)
{
return me.OnRequesting(RequestHandleAsync).OnResponsing(ResponseHandleAsync).OnException(ExceptionHandleAsync);

View File

@ -8,8 +8,7 @@ public static class HttpResponseMessageExtensions
/// <summary>
/// 记录日志
/// </summary>
public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger
, Func<string, string> bodyPreHandle = null)
public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger, Func<string, string> bodyPreHandle = null)
{
logger.Info($"HTTP Response {await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false)}");
}
@ -30,10 +29,7 @@ public static class HttpResponseMessageExtensions
this HttpResponseMessage me, Func<string, string> bodyHandle = null)
{
var body = me?.Content is null ? null : await me.Content!.ReadAsStringAsync().ConfigureAwait(false);
return new {
Header = me?.ToString()
, RequestHeader = me?.RequestMessage?.Headers
, Body = bodyHandle is null ? body : bodyHandle(body)
}.Json();
return new { Header = me?.ToString(), RequestHeader = me?.RequestMessage?.Headers, Body = bodyHandle is null ? body : bodyHandle(body) }
.Json();
}
}

View File

@ -1,3 +1,4 @@
using System.Numerics;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
@ -8,7 +9,79 @@ namespace NetAdmin.Infrastructure.Extensions;
/// </summary>
public static class StringExtensions
{
private static readonly Regex _regexIpV4 = new(Chars.RGXL_IP_V4);
private const string _CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static readonly Regex _regexIpV4 = new(Chars.RGXL_IP_V4);
/// <summary>
/// 将指定的输入字符串进行Base62解码
/// </summary>
/// <exception cref="ArgumentException">ArgumentException</exception>
public static string Base62Decode(this string me)
{
BigInteger result = 0;
foreach (var index in me.Select(c => _CHARACTERS.IndexOf(c))) {
if (index < 0) {
throw new ArgumentException("Invalid character in Base62 string.");
}
#pragma warning disable IDE0048, RCS1123, SA1407
result = result * 62 + index;
#pragma warning restore SA1407, RCS1123, IDE0048
}
// Convert BigInteger back to byte array and then to string
var bytes = result.ToByteArray();
// Handle the sign bit
if (bytes[^1] == 0) {
Array.Resize(ref bytes, bytes.Length - 1);
}
return Encoding.UTF8.GetString(bytes);
}
/// <summary>
/// 将指定的输入字符串进行Base62编码
/// </summary>
public static string Base62Encode(this string me)
{
// Convert string to byte array
var bytes = Encoding.UTF8.GetBytes(me);
// Convert byte array to BigInteger for easier processing
var bigInteger = new BigInteger(bytes);
if (bigInteger == 0) {
return _CHARACTERS[0].ToString();
}
var result = new StringBuilder();
while (bigInteger > 0) {
var remainder = (int)(bigInteger % 62);
bigInteger /= 62;
_ = result.Insert(0, _CHARACTERS[remainder]);
}
return result.ToString();
}
/// <summary>
/// 解码避免转义的Base64
/// </summary>
public static string Base64InUrlDecode(this string me)
{
return me.Replace("-", "+").Replace("_", "/");
}
/// <summary>
/// 编码避免转义的Base64
/// </summary>
public static string Base64InUrlEncode(this string me)
{
return me.Replace("+", "-").Replace("/", "_");
}
/// <summary>
/// 计算Crc32
@ -21,12 +94,10 @@ public static class StringExtensions
/// <summary>
/// 执行C#代码
/// </summary>
public static Task<T> ExecuteCSharpCodeAsync<T>(this string me, Assembly[] assemblies
, params string[] importNamespaces)
public static Task<T> ExecuteCSharpCodeAsync<T>(this string me, Assembly[] assemblies, params string[] importNamespaces)
{
// 使用 Roslyn 编译并执行代码
return CSharpScript.EvaluateAsync<T>(
me, ScriptOptions.Default.WithReferences(assemblies).WithImports(importNamespaces));
return CSharpScript.EvaluateAsync<T>(me, ScriptOptions.Default.WithReferences(assemblies).WithImports(importNamespaces));
}
/// <summary>

View File

@ -15,8 +15,7 @@ public static class GlobalStatic
/// <summary>
/// 产品版本
/// </summary>
public static readonly string ProductVersion
= FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly()!.Location).ProductVersion;
public static readonly string ProductVersion = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly()!.Location).ProductVersion;
private static long _latestLogTime;
@ -34,14 +33,12 @@ public static class GlobalStatic
/// <summary>
/// 最后一次日志时间
/// </summary>
public static DateTime LatestLogTime => Volatile.Read(ref _latestLogTime).Time();
public static DateTime LatestLogTime => LogCounterOff ? DateTime.MinValue : Volatile.Read(ref _latestLogTime).Time();
/// <summary>
/// 日志记录器忽略的API编号
/// </summary>
public static string[] LoggerIgnoreApiIds => [
"api/adm/tools/query.es.log", "api/probe/health.check", "api/probe/is.system.safety.stopped"
];
public static string[] LoggerIgnoreApiIds => ["api/adm/tools/query.es.log", "api/probe/health.check", "api/probe/is.system.safety.stopped"];
/// <summary>
/// 系统内部密钥
@ -88,6 +85,11 @@ public static class GlobalStatic
/// </summary>
public static JsonSerializerOptions JsonSerializerOptions { get; set; }
/// <summary>
/// 停止更新日志时间
/// </summary>
public static bool LogCounterOff { get; set; }
/// <summary>
/// 增加日志计数器
/// </summary>

View File

@ -8,11 +8,11 @@
<ItemGroup>
<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.7.24406.2"/>
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.5.8"/>
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.5.8-ns1"/>
<PackageReference Include="Furion.Pure.NS" Version="4.9.5.8-ns1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.11.0"/>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-rc.2.24474.3"/>
<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

@ -11,8 +11,7 @@ public static class ApplicationHelper
public static Dictionary<string, object> GetEnvironmentInfo()
{
var ret = typeof(Environment).GetProperties(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.Name is not (nameof(Environment.StackTrace)
or nameof(Environment.NewLine)))
.Where(x => x.Name is not (nameof(Environment.StackTrace) or nameof(Environment.NewLine)))
.ToDictionary(x => x.Name, x => x.GetValue(null));
var vars = Environment.GetEnvironmentVariables();

View File

@ -26,15 +26,13 @@ public static class CaptchaImageHelper
/// <returns> 背景图base64滑块图base64缺口坐标 </returns>
#pragma warning disable SA1414
public static async Task<(string BackgroundImage, string SliderImage, Point OffsetSaw)> CreateSawSliderImageAsync(
Assembly resAsm, string bgPath, string tempPath, (int, int) bgIndexScope, (int, int) tempIndexScope
, Size sliderSize)
Assembly resAsm, string bgPath, string tempPath, (int, int) bgIndexScope, (int, int) tempIndexScope, Size sliderSize)
#pragma warning restore SA1414
{
// 深色模板图
var templateIndex = new[] { tempIndexScope.Item1, tempIndexScope.Item2 }.Rand();
await using var bgStream = resAsm.GetManifestResourceStream(
$"{bgPath}.{new[] { bgIndexScope.Item1, bgIndexScope.Item2 }.Rand()}.jpg");
await using var bgStream = resAsm.GetManifestResourceStream($"{bgPath}.{new[] { bgIndexScope.Item1, bgIndexScope.Item2 }.Rand()}.jpg");
await using var darkStream = resAsm.GetManifestResourceStream($"{tempPath}._{templateIndex}.dark.png");
await using var tranStream = resAsm.GetManifestResourceStream($"{tempPath}._{templateIndex}.transparent.png");
@ -57,8 +55,7 @@ public static class CaptchaImageHelper
using var sliderBlockImage = new Image<Rgba32>(sliderSize.Width, backgroundImage.Height);
// 随机生成拼图坐标
var offsetRand = GeneratePoint(backgroundImage.Width, backgroundImage.Height, sliderSize.Width
, sliderSize.Height);
var offsetRand = GeneratePoint(backgroundImage.Width, backgroundImage.Height, sliderSize.Width, sliderSize.Height);
// 根据深色模板图计算轮廓形状
var blockShape = CalcBlockShape(darkTemplateImage);
@ -84,16 +81,13 @@ public static class CaptchaImageHelper
backgroundImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(offsetRand.X, offsetRand.Y), opacity));
// 生成干扰图坐标
var interferencePoint = GenerateInterferencePoint(backgroundImage.Width, backgroundImage.Height
, sliderSize.Width, sliderSize.Height, offsetRand.X
, offsetRand.Y);
var interferencePoint = GenerateInterferencePoint(backgroundImage.Width, backgroundImage.Height, sliderSize.Width, sliderSize.Height
, offsetRand.X, offsetRand.Y);
// 底图叠加深色干扰模板图
// ReSharper disable once AccessToDisposedClosure
backgroundImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(interferencePoint.X, interferencePoint.Y)
, opacity));
return (backgroundImage.ToBase64String(PngFormat.Instance), sliderBlockImage.ToBase64String(PngFormat.Instance)
, offsetRand);
backgroundImage.Mutate(x => x.DrawImage(darkTemplateImage, new Point(interferencePoint.X, interferencePoint.Y), opacity));
return (backgroundImage.ToBase64String(PngFormat.Instance), sliderBlockImage.ToBase64String(PngFormat.Instance), offsetRand);
}
private static int BuildPathList(Span<Rgba32> rowSpan, int temp, List<IPath> pathList, int y)
@ -133,8 +127,8 @@ public static class CaptchaImageHelper
/// <summary>
/// 随机生成干扰图坐标
/// </summary>
private static Point GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth
, int templateHeight, int blockX, int blockY)
private static Point GenerateInterferencePoint(int originalWidth, int originalHeight, int templateWidth, int templateHeight, int blockX
, int blockY)
{
var x =
@ -166,9 +160,7 @@ public static class CaptchaImageHelper
{
var widthDifference = originalWidth - templateWidth;
var heightDifference = originalHeight - templateHeight;
var x = widthDifference switch {
<= 0 => 5, _ => new[] { 0, originalWidth - templateWidth - 100 }.Rand() + 100
};
var x = widthDifference switch { <= 0 => 5, _ => new[] { 0, originalWidth - templateWidth - 100 }.Rand() + 100 };
var y = heightDifference switch { <= 0 => 5, _ => new[] { 0, originalHeight - templateHeight - 5 }.Rand() + 5 };

View File

@ -15,8 +15,7 @@ public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
{
var freeSql = new FreeSql.FreeSqlBuilder().UseConnectionString(databaseOptions.DbType, databaseOptions.ConnStr)
.UseGenerateCommandParameterWithLambda(true)
.UseAutoSyncStructure(
initMethods.HasFlag(FreeSqlInitMethods.SyncStructure))
.UseAutoSyncStructure(initMethods.HasFlag(FreeSqlInitMethods.SyncStructure))
.Build();
_ = InitDbAsync(freeSql, initMethods); // 初始化数据库 ,异步
return freeSql;
@ -90,8 +89,7 @@ public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
private void InsertSeedData(IFreeSql freeSql, IEnumerable<Type> entityTypes)
{
foreach (var entityType in entityTypes) {
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, databaseOptions.SeedDataRelativePath
, $"{entityType.Name}.json");
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, databaseOptions.SeedDataRelativePath, $"{entityType.Name}.json");
if (!File.Exists(file)) {
continue;
}
@ -102,12 +100,8 @@ public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
fileContent, typeof(IEnumerable<>).MakeGenericType(entityType));
// 如果表存在数据,跳过
var select = typeof(IFreeSql).GetMethod(nameof(freeSql.Select), 1, Type.EmptyTypes)
?.MakeGenericMethod(entityType)
.Invoke(freeSql, null);
if (select?.GetType()
.GetMethod(nameof(ISelect<dynamic>.Any), 0, Type.EmptyTypes)
?.Invoke(select, null) as bool? ?? true) {
var select = typeof(IFreeSql).GetMethod(nameof(freeSql.Select), 1, Type.EmptyTypes)?.MakeGenericMethod(entityType).Invoke(freeSql, null);
if (select?.GetType().GetMethod(nameof(ISelect<dynamic>.Any), 0, Type.EmptyTypes)?.Invoke(select, null) as bool? ?? true) {
continue;
}

View File

@ -1018,12 +1018,10 @@ public static class MimeTypeHelper
zmm application/vnd.handheld-entertainment+xml
""";
private static readonly Dictionary<string, string> _mimeTypeDic = _MIME_TYPES_RAW_STRING
.Split(
'\n', StringSplitOptions.RemoveEmptyEntries)
.ToDictionary(
x => x.Split(' ')[0].Trim()
, x => x.Split(' ')[1].Trim());
private static readonly Dictionary<string, string> _mimeTypeDic = _MIME_TYPES_RAW_STRING.Split('\n', StringSplitOptions.RemoveEmptyEntries)
.ToDictionary( //
x => x.Split(' ')[0].Trim()
, x => x.Split(' ')[1].Trim());
/// <summary>
/// 通过扩展名获取MIME类型

View File

@ -30,16 +30,15 @@ public sealed class RedisLocker : IAsyncDisposable
/// 获取锁
/// </summary>
/// <exception cref="NetAdminGetLockerException">NetAdminGetLockerException</exception>
public static async Task<RedisLocker> GetLockerAsync(IDatabase redisDatabase, string lockerName
, TimeSpan lockerExpire, int retryCount, TimeSpan retryDelay)
public static async Task<RedisLocker> GetLockerAsync(IDatabase redisDatabase, string lockerName, TimeSpan lockerExpire, int retryCount
, TimeSpan retryDelay)
{
lockerName = $"{nameof(RedisLocker)}.{lockerName}";
var setOk = false;
for (var i = 0; i != retryCount; ++i) {
try {
setOk = await redisDatabase
.StringSetAsync(lockerName, RedisValue.EmptyString, lockerExpire, When.NotExists
, CommandFlags.DemandMaster)
.StringSetAsync(lockerName, RedisValue.EmptyString, lockerExpire, When.NotExists, CommandFlags.DemandMaster)
.ConfigureAwait(false);
}
catch (Exception ex) {
@ -60,8 +59,7 @@ public sealed class RedisLocker : IAsyncDisposable
public async ValueTask DisposeAsync()
{
try {
_ = await _redisDatabase.KeyDeleteAsync(_redisKey, CommandFlags.DemandMaster | CommandFlags.FireAndForget)
.ConfigureAwait(false);
_ = await _redisDatabase.KeyDeleteAsync(_redisKey, CommandFlags.DemandMaster | CommandFlags.FireAndForget).ConfigureAwait(false);
}
catch (Exception ex) {
LogHelper.Get<RedisLocker>().Error(ex.Message);

View File

@ -15,10 +15,7 @@ public sealed class UserAgentParser
, { "Opera.*?Version", "Opera" }
, { "Opera", "Opera" }
, { "MSIE", "Internet Explorer" }
, {
"Internet Explorer"
, "Internet Explorer"
}
, { "Internet Explorer", "Internet Explorer" }
, { "Trident.* rv", "Internet Explorer" }
, { "Shiira", "Shiira" }
, { "Firefox", "Firefox" }
@ -73,10 +70,7 @@ public sealed class UserAgentParser
, { "benq", "BenQ" }
, { "ipaq", "HP iPaq" }
, { "mot-", "Motorola" }
, {
"playstation portable"
, "PlayStation Portable"
}
, { "playstation portable", "PlayStation Portable" }
, { "playstation 3", "PlayStation 3" }
, { "playstation vita", "PlayStation Vita" }
, { "hiptop", "Danger Hiptop" }
@ -195,16 +189,10 @@ public sealed class UserAgentParser
, { "infoseek", "InfoSeek Robot 1.0" }
, { "lycos", "Lycos" }
, { "yandex", "YandexBot" }
, {
"mediapartners-google"
, "MediaPartners Google"
}
, { "mediapartners-google", "MediaPartners Google" }
, { "CRAZYWEBCRAWLER", "Crazy Webcrawler" }
, { "adsbot-google", "AdsBot Google" }
, {
"feedfetcher-google"
, "Feedfetcher Google"
}
, { "feedfetcher-google", "Feedfetcher Google" }
, { "curious george", "Curious George" }
, { "ia_archiver", "Alexa Crawler" }
, { "MJ12bot", "Majestic-12" }
@ -316,8 +304,7 @@ public sealed class UserAgentParser
private bool SetPlatform()
{
var kv = _platforms.FirstOrDefault(x => //
Regex.IsMatch(_agent, $"{Regex.Escape(x.Key)}"
, RegexOptions.IgnoreCase));
Regex.IsMatch(_agent, $"{Regex.Escape(x.Key)}", RegexOptions.IgnoreCase));
if (kv.Key == null) {
Platform = "Unknown Platform";

View File

@ -17,8 +17,7 @@ public sealed class XmlCommentReader : ISingleton
public XmlCommentReader(IOptions<SpecificationDocumentSettingsOptions> specificationDocumentSettings)
{
var xmlComments = specificationDocumentSettings.Value.XmlComments //
?? App.GetConfig<SpecificationDocumentSettingsOptions>(
nameof(SpecificationDocumentSettingsOptions).TrimSuffixOptions())
?? App.GetConfig<SpecificationDocumentSettingsOptions>(nameof(SpecificationDocumentSettingsOptions).TrimSuffixOptions())
.XmlComments;
foreach (var commentFile in xmlComments.Where(x => x.Contains(nameof(NetAdmin)))) {
var xmlDoc = new XmlDocument();
@ -40,9 +39,7 @@ public sealed class XmlCommentReader : ISingleton
public string GetComments(MemberInfo memberInfo)
{
var node = memberInfo switch {
MethodInfo method => GetNodeByMethod(method)
, Type type => GetNodeByType(type)
, _ => throw new InvalidCastException()
MethodInfo method => GetNodeByMethod(method), Type type => GetNodeByType(type), _ => throw new InvalidCastException()
};
if (node?.FirstChild?.Name != "inheritdoc") {
@ -60,9 +57,7 @@ public sealed class XmlCommentReader : ISingleton
return GetComments(methodFromBaseType);
}
var methodFromInterface = memberInfo.DeclaringType?.GetInterfaces()
.Select(x => x.GetMethod(memberInfo.Name))
.FirstOrDefault(x => x != null);
var methodFromInterface = memberInfo.DeclaringType?.GetInterfaces().Select(x => x.GetMethod(memberInfo.Name)).FirstOrDefault(x => x != null);
return methodFromInterface == null ? null : GetComments(methodFromInterface);
}
@ -82,9 +77,7 @@ public sealed class XmlCommentReader : ISingleton
static string Replace(ParameterInfo parameterInfo)
{
return _regex.Replace(parameterInfo.ParameterType.ToString(), string.Empty)
.Replace("[", "{")
.Replace("]", "}");
return _regex.Replace(parameterInfo.ParameterType.ToString(), string.Empty).Replace("[", "{").Replace("]", "}");
}
}

View File

@ -10,4 +10,10 @@ namespace NetAdmin.SysComponent.Application.Modules.Sys;
public interface IDicContentModule : ICrudModule<CreateDicContentReq, QueryDicContentRsp // 创建类型
, QueryDicContentReq, QueryDicContentRsp // 查询类型
, DelReq // 删除类型
>;
>
{
/// <summary>
/// 启用/禁用字典内容
/// </summary>
Task<int> SetEnabledAsync(SetDicContentEnabledReq req);
}

View File

@ -67,7 +67,7 @@ public interface IDicModule
/// <summary>
/// 获取字典值
/// </summary>
public Task<string> GetDicValueAsync(GetDicValueReq req);
Task<string> GetDicValueAsync(GetDicValueReq req);
/// <summary>
/// 分页查询字典目录
@ -88,4 +88,9 @@ public interface IDicModule
/// 查询字典内容
/// </summary>
Task<IEnumerable<QueryDicContentRsp>> QueryContentAsync(QueryReq<QueryDicContentReq> req);
/// <summary>
/// 启用/禁用字典内容
/// </summary>
Task<int> SetEnabledAsync(SetDicContentEnabledReq req);
}

View File

@ -7,6 +7,11 @@ namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// </summary>
public interface IToolsModule
{
/// <summary>
/// Aes解密
/// </summary>
string AesDecode(AesDecodeReq req);
/// <summary>
/// 执行SQL语句
/// </summary>

View File

@ -81,10 +81,7 @@ public sealed class ApiService(
public async Task<IEnumerable<QueryApiRsp>> QueryAsync(QueryReq<QueryApiReq> req)
{
req.ThrowIfInvalid();
var ret = await Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.ToTreeListAsync()
.ConfigureAwait(false);
var ret = await Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter).ToTreeListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryApiRsp>>();
}
@ -143,12 +140,10 @@ public sealed class ApiService(
.Select(x => {
var id = x.AttributeRouteInfo!.Template;
return new QueryApiRsp {
Summary = xmlCommentReader.GetComments(x.MethodInfo)
, Name = x.ActionName
, Id = id
, Method = x.ActionConstraints?.OfType<HttpMethodActionConstraint>()
.FirstOrDefault()
?.HttpMethods.First()
Summary = xmlCommentReader.GetComments(x.MethodInfo)
, Name = x.ActionName
, Id = id
, Method = x.ActionConstraints?.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods.First()
, PathCrc32 = id.Crc32()
};
});

View File

@ -63,22 +63,19 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
var server = connectionMultiplexer.GetServers()[0];
var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
var keys = server.Keys(_redisInstance.Database, $"*{req.Keywords}*", Numbers.MAX_LIMIT_BULK_REQ)
.Take(Numbers.MAX_LIMIT_BULK_REQ)
.ToList();
var keys = server.Keys(_redisInstance.Database, $"*{req.Keywords}*", Numbers.MAX_LIMIT_BULK_REQ).Take(Numbers.MAX_LIMIT_BULK_REQ).ToList();
#pragma warning restore VSTHRD103
var dic = new ConcurrentDictionary<string, (DateTime?, RedisType)>();
await Parallel
.ForEachAsync(
keys
, async (key, _) =>
dic.TryAdd(
key
, (DateTime.Now + await database.KeyTimeToLiveAsync(key).ConfigureAwait(false)
, await database.KeyTypeAsync(key).ConfigureAwait(false))))
.ConfigureAwait(false);
await Parallel.ForEachAsync(
keys
, async (key, _) =>
dic.TryAdd(
key
, (DateTime.Now + await database.KeyTimeToLiveAsync(key).ConfigureAwait(false)
, await database.KeyTypeAsync(key).ConfigureAwait(false))))
.ConfigureAwait(false);
return dic.Select(x => new GetEntryRsp { Key = x.Key, ExpireTime = x.Value.Item1, Type = x.Value.Item2 });
}
@ -88,24 +85,21 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
var database = connectionMultiplexer.GetDatabase(_redisInstance.Database);
var ret = new GetEntryRsp {
Type = await database.KeyTypeAsync(req.Key).ConfigureAwait(false)
, Key = req.Key
, ExpireTime = DateTime.Now +
await database.KeyTimeToLiveAsync(req.Key).ConfigureAwait(false)
Type = await database.KeyTypeAsync(req.Key).ConfigureAwait(false)
, Key = req.Key
, ExpireTime = DateTime.Now + await database.KeyTimeToLiveAsync(req.Key).ConfigureAwait(false)
};
#pragma warning disable IDE0072
ret.Data = ret.Type switch
#pragma warning restore IDE0072
{
RedisType.String => await database.StringGetAsync(req.Key).ConfigureAwait(false)
, RedisType.List => string.Join(", ", await database.ListRangeAsync(req.Key).ConfigureAwait(false))
, RedisType.Set => string.Join(", ", await database.SetMembersAsync(req.Key).ConfigureAwait(false))
, RedisType.SortedSet =>
string.Join(", ", await database.SortedSetRangeByRankAsync(req.Key).ConfigureAwait(false))
, RedisType.Hash => string.Join(
", ", await database.HashGetAllAsync(req.Key).ConfigureAwait(false))
, _ => "Unsupported key type"
RedisType.String => await database.StringGetAsync(req.Key).ConfigureAwait(false)
, RedisType.List => string.Join(", ", await database.ListRangeAsync(req.Key).ConfigureAwait(false))
, RedisType.Set => string.Join(", ", await database.SetMembersAsync(req.Key).ConfigureAwait(false))
, RedisType.SortedSet => string.Join(", ", await database.SortedSetRangeByRankAsync(req.Key).ConfigureAwait(false))
, RedisType.Hash => string.Join(", ", await database.HashGetAllAsync(req.Key).ConfigureAwait(false))
, _ => "Unsupported key type"
};
return ret;

View File

@ -21,17 +21,13 @@ public sealed class CaptchaService : ServiceBase<ICaptchaService>, ICaptchaServi
public async Task<GetCaptchaRsp> GetCaptchaImageAsync()
{
var (backgroundImage, sliderImage, offsetSaw) = await CaptchaImageHelper.CreateSawSliderImageAsync(
_entryAsm, $"{_entryAsmName}.Assets.Captcha.background", $"{_entryAsmName}.Assets.Captcha.template"
, (1, 101), (1, 7), new Size(50, 50))
.ConfigureAwait(false);
_entryAsm, $"{_entryAsmName}.Assets.Captcha.background"
, $"{_entryAsmName}.Assets.Captcha.template", (1, 101), (1, 7)
, new Size(50, 50))
.ConfigureAwait(false);
var id = $"{nameof(GetCaptchaImageAsync)}_{YitIdHelper.NextId()}";
return new GetCaptchaRsp {
Id = id
, BackgroundImage = backgroundImage
, SliderImage = sliderImage
, SawOffsetX = offsetSaw.X
};
return new GetCaptchaRsp { Id = id, BackgroundImage = backgroundImage, SliderImage = sliderImage, SawOffsetX = offsetSaw.X };
}
/// <inheritdoc />

View File

@ -84,18 +84,14 @@ 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, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryConfigReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryConfigRsp>();
}
/// <inheritdoc />
public async Task<QueryConfigRsp> GetLatestConfigAsync()
{
var ret = await QueryAsync(
new QueryReq<QueryConfigReq> { Count = 1, Filter = new QueryConfigReq { Enabled = true } })
.ConfigureAwait(false);
var ret = await QueryAsync(new QueryReq<QueryConfigReq> { Count = 1, Filter = new QueryConfigReq { Enabled = true } }).ConfigureAwait(false);
return ret.FirstOrDefault();
}
@ -112,8 +108,7 @@ public sealed class ConfigService(BasicRepository<Sys_Config, long> rpo) //
.ToListAsync()
.ConfigureAwait(false);
return new PagedQueryRsp<QueryConfigRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryConfigRsp>>());
return new PagedQueryRsp<QueryConfigRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryConfigRsp>>());
}
/// <inheritdoc />

View File

@ -26,19 +26,13 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
var httpStatusCodes = Enum.GetNames<HttpStatusCode>().ToDictionary(x => x, GetHttpStatusCodeDicValue);
httpStatusCodes.Add( //
nameof(ErrorCodes.Unhandled)
, [
Numbers.HTTP_STATUS_BIZ_FAIL.ToInvString(), nameof(ErrorCodes.Unhandled)
, nameof(Indicates.Danger).ToLowerInvariant()
]);
, [Numbers.HTTP_STATUS_BIZ_FAIL.ToInvString(), nameof(ErrorCodes.Unhandled), nameof(Indicates.Danger).ToLowerInvariant()]);
ret.Add($"{nameof(HttpStatusCode)}s", httpStatusCodes);
return ret;
static string[] GetDicValue(Enum y)
{
var ret = new[] {
Convert.ToInt64(y, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture)
, y.ResDesc<Ln>()
};
var ret = new[] { Convert.ToInt64(y, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture), y.ResDesc<Ln>() };
var indicate = y.GetAttributeOfType<IndicatorAttribute>()?.Indicate.ToLowerInvariant();
return indicate.NullOrEmpty() ? ret : [..ret, indicate];
}
@ -48,11 +42,8 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
var codeInt = Convert.ToInt64(Enum.Parse<HttpStatusCode>(name), CultureInfo.InvariantCulture);
return [
codeInt.ToString(CultureInfo.InvariantCulture), name
, (codeInt switch {
>= 200 and < 300 => nameof(Indicates.Success)
, < 400 => nameof(Indicates.Warning)
, _ => nameof(Indicates.Danger)
}).ToLowerInvariant()
, (codeInt switch { >= 200 and < 300 => nameof(Indicates.Success), < 400 => nameof(Indicates.Warning), _ => nameof(Indicates.Danger) })
.ToLowerInvariant()
];
}
}

View File

@ -74,9 +74,7 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
#if DBTYPE_SQLSERVER
return (await UpdateReturnListAsync(req, null).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryDeptRsp>();
#else
return await UpdateAsync(req, null).ConfigureAwait(false) > 0
? await GetAsync(new QueryDeptReq { Id = req.Id }).ConfigureAwait(false)
: null;
return await UpdateAsync(req, null).ConfigureAwait(false) > 0 ? await GetAsync(new QueryDeptReq { Id = req.Id }).ConfigureAwait(false) : null;
#endif
}
@ -102,9 +100,7 @@ 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, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryDeptReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryDeptRsp>();
}
@ -145,8 +141,7 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) ||
a.Summary.Contains(req.Keywords));
, a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) || a.Summary.Contains(req.Keywords));
if (asTreeCte) {
ret = ret.AsTreeCte();
}

View File

@ -13,8 +13,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
private static readonly string _clientProjectPath = Path.Combine( //
Environment.CurrentDirectory, "../../frontend/admin");
private static readonly string[] _projectDirs
= Directory.GetDirectories(Path.Combine(Environment.CurrentDirectory, "../"));
private static readonly string[] _projectDirs = Directory.GetDirectories(Path.Combine(Environment.CurrentDirectory, "../"));
private static readonly Regex _regex = new(@"\.(\w)");
private static readonly Regex _regex2 = new("([a-zA-Z]+):");
@ -26,6 +25,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
// 模块类型Sys、Adm、等
var moduleType = Enum.GetName(req.Type)!;
var @namespace = req.Type.ToString();
// 模板层目录
var tplHostDir = GetDir("SysComponent.Host");
@ -51,57 +51,57 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
var entityDir = Path.Combine(dataDir, "DbMaps", moduleType[..3]);
// 创建缺少的目录
CreateDir(hostControllerDir, cacheDir, cacheDependencyDir, appDir, appModulesDir, appServicesDir
, appServicesDependencyDir, dataDir, dtoDir, entityDir);
CreateDir(hostControllerDir, cacheDir, cacheDependencyDir, appDir, appModulesDir, appServicesDir, appServicesDependencyDir, dataDir, dtoDir
, entityDir);
// Controller
await WriteCodeFileAsync(req, Path.Combine(tplHostDir, "Controllers", "Tpl", "ExampleController.cs")
, Path.Combine(hostControllerDir, $"{req.ModuleName}Controller.cs"))
, Path.Combine(hostControllerDir, $"{req.ModuleName}Controller.cs"), @namespace)
.ConfigureAwait(false);
// CreateReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "CreateExampleReq.cs")
, Path.Combine(dtoDir, $"Create{req.ModuleName}Req.cs"))
, Path.Combine(dtoDir, $"Create{req.ModuleName}Req.cs"), @namespace)
.ConfigureAwait(false);
// QueryReq
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "QueryExampleReq.cs")
, Path.Combine(dtoDir, $"Query{req.ModuleName}Req.cs"))
, Path.Combine(dtoDir, $"Query{req.ModuleName}Req.cs"), @namespace)
.ConfigureAwait(false);
// QueryRsp
await WriteCodeFileAsync(req, Path.Combine(dataDir, "Dto", "Tpl", "Example", "QueryExampleRsp.cs")
, Path.Combine(dtoDir, $"Query{req.ModuleName}Rsp.cs"))
, Path.Combine(dtoDir, $"Query{req.ModuleName}Rsp.cs"), @namespace)
.ConfigureAwait(false);
// ICache
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "Dependency", "IExampleCache.cs")
, Path.Combine(cacheDependencyDir, $"I{req.ModuleName}Cache.cs"))
, Path.Combine(cacheDependencyDir, $"I{req.ModuleName}Cache.cs"), @namespace)
.ConfigureAwait(false);
// Cache
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "ExampleCache.cs")
, Path.Combine(cacheDir, $"{req.ModuleName}Cache.cs"))
await WriteCodeFileAsync(req, Path.Combine(tplCacheDir, "Tpl", "ExampleCache.cs"), Path.Combine(cacheDir, $"{req.ModuleName}Cache.cs")
, @namespace)
.ConfigureAwait(false);
// IModule
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Modules", "Tpl", "IExampleModule.cs")
, Path.Combine(appModulesDir, $"I{req.ModuleName}Module.cs"))
, Path.Combine(appModulesDir, $"I{req.ModuleName}Module.cs"), @namespace)
.ConfigureAwait(false);
// IService
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "Dependency", "IExampleService.cs")
, Path.Combine(appServicesDependencyDir, $"I{req.ModuleName}Service.cs"))
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "Dependency", "IExampleService.cs")
, Path.Combine(appServicesDependencyDir, $"I{req.ModuleName}Service.cs"), @namespace)
.ConfigureAwait(false);
// Service
await WriteCodeFileAsync(req, Path.Combine(tplAppDir, "Services", "Tpl", "ExampleService.cs")
, Path.Combine(appServicesDir, $"{req.ModuleName}Service.cs"))
, Path.Combine(appServicesDir, $"{req.ModuleName}Service.cs"), @namespace)
.ConfigureAwait(false);
// Entity
await WriteCodeFileAsync(req, Path.Combine(dataDir, "DbMaps", "Tpl", "Tpl_Example.cs")
, Path.Combine(entityDir, $"{moduleType[..3]}_{req.ModuleName}.cs"))
, Path.Combine(entityDir, $"{moduleType[..3]}_{req.ModuleName}.cs"), @namespace)
.ConfigureAwait(false);
}
@ -109,13 +109,9 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
public async Task GenerateIconCodeAsync(GenerateIconCodeReq req)
{
req.ThrowIfInvalid();
var tplSvg = await File.ReadAllTextAsync(
Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "Svg.vue"))
.ConfigureAwait(false);
var tplExport = await File
.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl"
, "export.js"))
.ConfigureAwait(false);
var tplSvg = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "Svg.vue")).ConfigureAwait(false);
var tplExport = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "export.js"))
.ConfigureAwait(false);
var vueContent = tplSvg.Replace("$svgCode$", req.SvgCode).Replace(_REPLACE_TO_EMPTY, string.Empty);
@ -130,9 +126,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
var indexJsFile = Path.Combine(dir, "index.js");
await File.AppendAllTextAsync(
indexJsFile
, Environment.NewLine +
tplExport.Replace("$iconName$", req.IconName).Replace(_REPLACE_TO_EMPTY, string.Empty))
indexJsFile, Environment.NewLine + tplExport.Replace("$iconName$", req.IconName).Replace(_REPLACE_TO_EMPTY, string.Empty))
.ConfigureAwait(false);
// 修改iconSelect.js
@ -152,10 +146,8 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
public async Task GenerateJsCodeAsync()
{
// 模板文件
var tplOuter = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "outer.js"))
.ConfigureAwait(false);
var tplInner = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "inner.js"))
.ConfigureAwait(false);
var tplOuter = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "outer.js")).ConfigureAwait(false);
var tplInner = await File.ReadAllTextAsync(Path.Combine(_clientProjectPath, "src", "api", "tpl", "inner.js")).ConfigureAwait(false);
foreach (var item in apiService.ReflectionList(false)) {
var dir = Path.Combine(_clientProjectPath, "src", "api", item.Namespace);
@ -168,8 +160,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
var content = tplOuter.Replace("$controllerDesc$", item.Summary)
.Replace("$controllerPath$", item.Id)
.Replace( //
"$inner$"
, string.Join(Environment.NewLine + Environment.NewLine, Select(item)))
"$inner$", string.Join(Environment.NewLine + Environment.NewLine, Select(item)))
.Replace(_REPLACE_TO_EMPTY, string.Empty);
await File.WriteAllTextAsync(file, content).ConfigureAwait(false);
@ -180,9 +171,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
{
return item.Children.Select(x => tplInner.Replace("$actionDesc$", x.Summary)
.Replace( //
"$actionName$"
, _regex.Replace(
x.Name, y => y.Groups[1].Value.ToUpperInvariant()))
"$actionName$", _regex.Replace(x.Name, y => y.Groups[1].Value.ToUpperInvariant()))
.Replace("$actionPath$", x.Id)
.Replace( //
"$actionMethod$", x.Method?.ToLowerInvariant())
@ -204,13 +193,13 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
return _projectDirs.First(x => x.EndsWith(key, true, CultureInfo.InvariantCulture));
}
private static async Task WriteCodeFileAsync(GenerateCsCodeReq req, string tplFile, string writeFile)
private static async Task WriteCodeFileAsync(GenerateCsCodeReq req, string tplFile, string writeFile, string @namespace = "SysComponent")
{
var tplContent = await File.ReadAllTextAsync(tplFile).ConfigureAwait(false);
tplContent = tplContent.Replace("Tpl", Enum.GetName(req.Type)![..3])
.Replace("示例", req.ModuleRemark)
.Replace("Example", req.ModuleName)
.Replace("NetAdmin.SysComponent", "SysComponent");
.Replace("NetAdmin.SysComponent", $"NetAdmin.{@namespace}");
await File.WriteAllTextAsync(writeFile, tplContent).ConfigureAwait(false);
}

View File

@ -40,8 +40,12 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
public async Task<QueryDicCatalogRsp> CreateAsync(CreateDicCatalogReq req)
{
req.ThrowIfInvalid();
if (req.ParentId != 0 &&
!await Rpo.Where(a => a.Id == req.ParentId).ForUpdate().AnyAsync().ConfigureAwait(false)) {
if (req.ParentId != 0 && !await Rpo.Where(a => a.Id == req.ParentId)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.AnyAsync()
.ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
@ -62,8 +66,12 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
public async Task<int> EditAsync(EditDicCatalogReq req)
{
req.ThrowIfInvalid();
return req.ParentId == 0 ||
await Rpo.Where(a => a.Id == req.ParentId).ForUpdate().AnyAsync().ConfigureAwait(false)
return req.ParentId == 0 || await Rpo.Where(a => a.Id == req.ParentId)
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.AnyAsync()
.ConfigureAwait(false)
? await UpdateAsync(req, null).ConfigureAwait(false)
: throw new NetAdminInvalidOperationException(Ln.);
}
@ -89,9 +97,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, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryDicCatalogReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryDicCatalogRsp>();
}
@ -108,8 +114,7 @@ public sealed class DicCatalogService(BasicRepository<Sys_DicCatalog, long> rpo)
.ToListAsync()
.ConfigureAwait(false);
return new PagedQueryRsp<QueryDicCatalogRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryDicCatalogRsp>>());
return new PagedQueryRsp<QueryDicCatalogRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryDicCatalogRsp>>());
}
/// <inheritdoc />

View File

@ -42,7 +42,9 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
req.ThrowIfInvalid();
if (!await Rpo.Orm.Select<Sys_DicCatalog>()
.Where(a => a.Id == req.CatalogId)
.ForUpdate()
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.AnyAsync()
.ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
@ -66,15 +68,16 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
req.ThrowIfInvalid();
if (!await Rpo.Orm.Select<Sys_DicCatalog>()
.Where(a => a.Id == req.CatalogId)
.ForUpdate()
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.AnyAsync()
.ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
#if DBTYPE_SQLSERVER
return (await UpdateReturnListAsync(req, null).ConfigureAwait(false)).FirstOrDefault()
?.Adapt<QueryDicContentRsp>();
return (await UpdateReturnListAsync(req, null).ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryDicContentRsp>();
#else
return await UpdateAsync(req, null).ConfigureAwait(false) > 0
? await GetAsync(new QueryDicContentReq { Id = req.Id }).ConfigureAwait(false)
@ -104,9 +107,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, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryDicContentReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryDicContentRsp>();
}
@ -123,8 +124,7 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
.ToListAsync()
.ConfigureAwait(false);
return new PagedQueryRsp<QueryDicContentRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryDicContentRsp>>());
return new PagedQueryRsp<QueryDicContentRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryDicContentRsp>>());
}
/// <inheritdoc />
@ -150,11 +150,19 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
#endif
.Include(a => a.Catalog)
.Where(a => a.Catalog.Code == catalogCode)
.Where(a => a.Enabled)
.ToListAsync()
.ConfigureAwait(false);
return ret.Adapt<List<QueryDicContentRsp>>();
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetDicContentEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(Sys_DicContent.Enabled)]);
}
private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);

View File

@ -136,4 +136,11 @@ public sealed class DicService(IDicCatalogService catalogService, IDicContentSer
req.ThrowIfInvalid();
return contentService.QueryAsync(req);
}
/// <inheritdoc />
public Task<int> SetEnabledAsync(SetDicContentEnabledReq req)
{
req.ThrowIfInvalid();
return contentService.SetEnabledAsync(req);
}
}

View File

@ -18,8 +18,7 @@ public sealed class FileService(IOptions<UploadOptions> uploadOptions, MinioHelp
}
if (!uploadOptions.Value.ContentTypes.Contains(file.ContentType)) {
throw new NetAdminInvalidOperationException(
$"{Ln.允许的文件格式} {string.Join(",", uploadOptions.Value.ContentTypes)}");
throw new NetAdminInvalidOperationException($"{Ln.允许的文件格式} {string.Join(",", uploadOptions.Value.ContentTypes)}");
}
if (file.Length > uploadOptions.Value.MaxSize) {

View File

@ -73,9 +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, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryJobRecordReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryJobRecordRsp>();
}
@ -88,16 +86,10 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
#if DBTYPE_SQLSERVER
.WithLock(SqlServerLock.NoLock | SqlServerLock.NoWait)
#endif
.GroupBy(a => new {
a.CreatedTime.Year
, a.CreatedTime.Month
, a.CreatedTime.Day
, a.CreatedTime.Hour
})
.GroupBy(a => new { a.CreatedTime.Year, a.CreatedTime.Month, a.CreatedTime.Day, a.CreatedTime.Hour })
.ToListAsync(a => new GetBarChartRsp {
Timestamp = new DateTime(
a.Key.Year, a.Key.Month, a.Key.Day, a.Key.Hour, 0
, 0, DateTimeKind.Unspecified)
Timestamp = new DateTime(a.Key.Year, a.Key.Month, a.Key.Day, a.Key.Hour, 0, 0
, DateTimeKind.Unspecified)
, Value = a.Count()
})
.ConfigureAwait(false);
@ -118,8 +110,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
.ToListAsync(a => new GetPieChartRsp { Value = a.Count(), Key = a.Key.ToString() })
#pragma warning restore CA1305
.ConfigureAwait(false);
return ret.Select(x => x with { Key = Enum.Parse<HttpStatusCode>(x.Key).ToString() })
.OrderByDescending(x => x.Value);
return ret.Select(x => x with { Key = Enum.Parse<HttpStatusCode>(x.Key).ToString() }).OrderByDescending(x => x.Value);
}
/// <inheritdoc />
@ -150,8 +141,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
.ToListAsync()
.ConfigureAwait(false);
return new PagedQueryRsp<QueryJobRecordRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryJobRecordRsp>>());
return new PagedQueryRsp<QueryJobRecordRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryJobRecordRsp>>());
}
/// <inheritdoc />
@ -175,8 +165,7 @@ public sealed class JobRecordService(BasicRepository<Sys_JobRecord, long> rpo) /
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0) ||
a.Job.JobName == req.Keywords);
, a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0) || a.Job.JobName == req.Keywords);
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {

View File

@ -112,11 +112,9 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
}
]
};
var job
= await QueryInternal(
new QueryReq<QueryJobReq> { Count = 1, Filter = req, DynamicFilter = df, Order = Orders.None })
.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 {
@ -160,16 +158,9 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
{
req.ThrowIfInvalid();
var nextExecTime = GetNextExecTime(req.ExecutionCron);
_ = await UpdateAsync(
req with {
Status = JobStatues.Idle
, NextExecTime = nextExecTime
, NextTimeId = nextExecTime?.TimeUnixUtc()
}
, [
nameof(req.Status), nameof(req.NextExecTime), nameof(req.NextTimeId), nameof(req.LastDuration)
, nameof(req.LastStatusCode)
])
_ = await UpdateAsync( //
req with { Status = JobStatues.Idle, NextExecTime = nextExecTime, NextTimeId = nextExecTime?.TimeUnixUtc() }
, [nameof(req.Status), nameof(req.NextExecTime), nameof(req.NextTimeId), nameof(req.LastDuration), nameof(req.LastStatusCode)])
.ConfigureAwait(false);
}
@ -177,9 +168,7 @@ 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, Order = Orders.None })
.ToOneAsync()
.ConfigureAwait(false);
var ret = await QueryInternal(new QueryReq<QueryJobReq> { Filter = req, Order = Orders.None }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryJobRsp>();
}
@ -209,10 +198,7 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
#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())
.Where(a => !Rpo.Orm.Select<Sys_JobRecord>().As("b").Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId).Any())
.ToOneAsync(a => new {
a.RequestUrl
, a.HttpMethod
@ -317,16 +303,13 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
{
var ret1 = await UpdateAsync( // 运行中,运行时间超过超时设定;置为空闲状态
new Sys_Job { Status = JobStatues.Idle }, [nameof(Sys_Job.Status)], null
, a => a.Status == JobStatues.Running &&
a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB)
, null, true)
, a => a.Status == JobStatues.Running && a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB), null, true)
.ConfigureAwait(false);
var ret2 = await UpdateAsync( // 空闲中,下次执行时间在当前时间减去超时时间以前;将下次执行时间调整到现在
new Sys_Job { NextExecTime = DateTime.Now, NextTimeId = DateTime.Now.TimeUnixUtc() }
, [nameof(Sys_Job.NextExecTime), nameof(Sys_Job.NextTimeId)], null
, a => a.Status == JobStatues.Idle && a.NextExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB)
, null, true)
, [nameof(Sys_Job.NextExecTime), nameof(Sys_Job.NextTimeId)], null
, a => a.Status == JobStatues.Idle && a.NextExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB), null, true)
.ConfigureAwait(false);
return ret1 + ret2;
}
@ -340,9 +323,7 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
private static DateTime? GetNextExecTime(string cron)
{
return CronExpression.Parse(cron, CronFormat.IncludeSeconds)
.GetNextOccurrence(DateTime.UtcNow, TimeZoneInfo.Local)
?.ToLocalTime();
return CronExpression.Parse(cron, CronFormat.IncludeSeconds).GetNextOccurrence(DateTime.UtcNow, TimeZoneInfo.Local)?.ToLocalTime();
}
private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req)
@ -360,8 +341,7 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
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));
req.Keywords?.Length > 0, a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords));
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
switch (req.Order) {

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