mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-12-21 22:05:47 +08:00
feat: ✨ 基础模块
注册登录 用户管理 角色管理 部门管理 消息管理 接口管理 菜单管理 字典管理 缓存管理 请求日志 系统设置 版本信息 代码生成
This commit is contained in:
@@ -11,10 +11,19 @@ public static class ApplicationHelper
|
||||
public static Dictionary<string, object> GetEnvironmentInfo()
|
||||
{
|
||||
var ret = typeof(Environment).GetProperties(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(x => x.Name != nameof(Environment.StackTrace))
|
||||
.Where(x => x.Name is not (nameof(Environment.StackTrace)
|
||||
or nameof(Environment.NewLine)))
|
||||
.ToDictionary(x => x.Name, x => x.GetValue(null));
|
||||
_ = ret.TryAdd( //
|
||||
"Environment", Environment.GetEnvironmentVariables().ToJson());
|
||||
|
||||
var vars = Environment.GetEnvironmentVariables();
|
||||
var keys = new ArrayList(vars.Keys);
|
||||
keys.Sort();
|
||||
var sb = new StringBuilder(vars.Count);
|
||||
foreach (var key in keys) {
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"{key}: {vars[key]}");
|
||||
}
|
||||
|
||||
_ = ret.TryAdd("EnvironmentVars", sb.ToString().Trim());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace NetAdmin.Infrastructure.Utils;
|
||||
/// </summary>
|
||||
public static class CaptchaImageHelper
|
||||
{
|
||||
private static readonly int[] _randRange = { 70, 100 };
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个缺口滑块验证码图片
|
||||
/// </summary>
|
||||
@@ -75,7 +77,7 @@ public static class CaptchaImageHelper
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
sliderBlockImage.Mutate(x => x.DrawImage(blockImage, new Point(0, offsetRand.Y), 1));
|
||||
|
||||
var opacity = (float)(new[] { 70, 100 }.Rand() * 0.01);
|
||||
var opacity = (float)(_randRange.Rand() * 0.01);
|
||||
|
||||
// 底图叠加深色模板图
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
|
||||
@@ -6,27 +6,16 @@ namespace NetAdmin.Infrastructure.Utils;
|
||||
/// <summary>
|
||||
/// FreeSqlBuilder
|
||||
/// </summary>
|
||||
public sealed class FreeSqlBuilder
|
||||
public sealed class FreeSqlBuilder(DatabaseOptions databaseOptions)
|
||||
{
|
||||
private readonly DatabaseOptions _databaseOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FreeSqlBuilder" /> class.
|
||||
/// </summary>
|
||||
public FreeSqlBuilder(DatabaseOptions databaseOptions)
|
||||
{
|
||||
_databaseOptions = databaseOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建freeSql对象
|
||||
/// </summary>
|
||||
public IFreeSql Build(FreeSqlInitOptions initOptions)
|
||||
{
|
||||
var freeSql = new FreeSql.FreeSqlBuilder()
|
||||
.UseConnectionString(_databaseOptions.DbType, _databaseOptions.ConnStr)
|
||||
.UseAutoSyncStructure(false)
|
||||
.Build();
|
||||
var freeSql = new FreeSql.FreeSqlBuilder().UseConnectionString(databaseOptions.DbType, databaseOptions.ConnStr)
|
||||
.UseAutoSyncStructure(false)
|
||||
.Build();
|
||||
|
||||
_ = InitDbAsync(freeSql, initOptions); // 初始化数据库 ,异步
|
||||
return freeSql;
|
||||
@@ -98,7 +87,7 @@ public sealed class FreeSqlBuilder
|
||||
private void InsertSeedData(IFreeSql freeSql, IEnumerable<Type> entityTypes)
|
||||
{
|
||||
foreach (var entityType in entityTypes) {
|
||||
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _databaseOptions.SeedDataRelativePath
|
||||
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, databaseOptions.SeedDataRelativePath
|
||||
, $"{entityType.Name}.json");
|
||||
if (!File.Exists(file)) {
|
||||
continue;
|
||||
@@ -136,7 +125,7 @@ public sealed class FreeSqlBuilder
|
||||
/// </summary>
|
||||
private void SyncStructure(IFreeSql freeSql, Type[] entityTypes)
|
||||
{
|
||||
if (_databaseOptions.DbType == DataType.Oracle) {
|
||||
if (databaseOptions.DbType == DataType.Oracle) {
|
||||
freeSql.CodeFirst.IsSyncStructureToUpper = true;
|
||||
}
|
||||
|
||||
|
||||
1036
src/backend/NetAdmin.Infrastructure/Utils/MimeTypeHelper.cs
Normal file
1036
src/backend/NetAdmin.Infrastructure/Utils/MimeTypeHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,13 @@
|
||||
using Minio;
|
||||
using Minio.DataModel.Args;
|
||||
|
||||
namespace NetAdmin.Infrastructure.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// MinioHelper
|
||||
/// </summary>
|
||||
public sealed class MinioHelper : IScoped
|
||||
public sealed class MinioHelper(IOptions<UploadOptions> uploadOptions) : IScoped
|
||||
{
|
||||
private readonly UploadOptions _uploadOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MinioHelper" /> class.
|
||||
/// </summary>
|
||||
public MinioHelper(IOptions<UploadOptions> uploadOptions)
|
||||
{
|
||||
_uploadOptions = uploadOptions.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传文件
|
||||
/// </summary>
|
||||
@@ -27,26 +18,26 @@ public sealed class MinioHelper : IScoped
|
||||
/// <returns>可访问的url地址</returns>
|
||||
public async Task<string> UploadAsync(string objectName, Stream fileStream, string contentType, long fileSize)
|
||||
{
|
||||
using var minio = new MinioClient().WithEndpoint(_uploadOptions.Minio.ServerAddress)
|
||||
using var minio = new MinioClient().WithEndpoint(uploadOptions.Value.Minio.ServerAddress)
|
||||
.WithCredentials( //
|
||||
_uploadOptions.Minio.AccessKey, _uploadOptions.Minio.SecretKey)
|
||||
.WithSSL(_uploadOptions.Minio.Secure)
|
||||
uploadOptions.Value.Minio.AccessKey, uploadOptions.Value.Minio.SecretKey)
|
||||
.WithSSL(uploadOptions.Value.Minio.Secure)
|
||||
.Build();
|
||||
|
||||
var beArgs = new BucketExistsArgs().WithBucket(_uploadOptions.Minio.BucketName);
|
||||
var beArgs = new BucketExistsArgs().WithBucket(uploadOptions.Value.Minio.BucketName);
|
||||
|
||||
if (!await minio.BucketExistsAsync(beArgs)) {
|
||||
var mbArgs = new MakeBucketArgs().WithBucket(_uploadOptions.Minio.BucketName);
|
||||
var mbArgs = new MakeBucketArgs().WithBucket(uploadOptions.Value.Minio.BucketName);
|
||||
await minio.MakeBucketAsync(mbArgs);
|
||||
}
|
||||
|
||||
var putArgs = new PutObjectArgs().WithBucket(_uploadOptions.Minio.BucketName)
|
||||
var putArgs = new PutObjectArgs().WithBucket(uploadOptions.Value.Minio.BucketName)
|
||||
.WithObject(objectName)
|
||||
.WithStreamData(fileStream)
|
||||
.WithObjectSize(fileSize)
|
||||
.WithContentType(contentType);
|
||||
_ = await minio.PutObjectAsync(putArgs);
|
||||
|
||||
return $"{_uploadOptions.Minio.AccessUrl}/{_uploadOptions.Minio.BucketName}/{objectName}";
|
||||
return $"{uploadOptions.Value.Minio.AccessUrl}/{uploadOptions.Value.Minio.BucketName}/{objectName}";
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using StackExchange.Redis;
|
||||
namespace NetAdmin.Infrastructure.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Redis锁
|
||||
/// Redis 分布锁
|
||||
/// </summary>
|
||||
#pragma warning disable DesignedForInheritance
|
||||
public class RedLocker : IDisposable, ISingleton
|
||||
@@ -19,7 +19,7 @@ public class RedLocker : IDisposable, ISingleton
|
||||
/// </summary>
|
||||
public RedLocker(IOptions<RedisOptions> redisOptions)
|
||||
{
|
||||
RedlockFactory = RedLockFactory.Create( //
|
||||
RedLockFactory = RedLockFactory.Create( //
|
||||
new List<RedLockMultiplexer> //
|
||||
{
|
||||
ConnectionMultiplexer.Connect( //
|
||||
@@ -44,9 +44,9 @@ public class RedLocker : IDisposable, ISingleton
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RedlockFactory
|
||||
/// RedLockFactory
|
||||
/// </summary>
|
||||
public RedLockFactory RedlockFactory { get; }
|
||||
public RedLockFactory RedLockFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Implement IDisposable.
|
||||
@@ -84,7 +84,7 @@ public class RedLocker : IDisposable, ISingleton
|
||||
// If disposing equals true, dispose all managed
|
||||
// and unmanaged resources.
|
||||
if (disposing) {
|
||||
RedlockFactory.Dispose();
|
||||
RedLockFactory.Dispose();
|
||||
}
|
||||
|
||||
// Call the appropriate methods to clean up
|
||||
|
||||
@@ -301,8 +301,10 @@ public sealed class UserAgentParser
|
||||
|
||||
private bool SetMobile()
|
||||
{
|
||||
#pragma warning disable S3267
|
||||
foreach (var item in _mobiles) {
|
||||
if (_agent.IndexOf(item.Key, StringComparison.OrdinalIgnoreCase) != -1) {
|
||||
#pragma warning restore S3267
|
||||
if (_agent.Contains(item.Key, StringComparison.OrdinalIgnoreCase)) {
|
||||
IsMobile = true;
|
||||
Mobile = item.Value;
|
||||
return true;
|
||||
|
||||
@@ -7,8 +7,9 @@ namespace NetAdmin.Infrastructure.Utils;
|
||||
/// </summary>
|
||||
public sealed class XmlCommentReader : ISingleton
|
||||
{
|
||||
private const string _XPATH = "//doc/members/member[@name=\"{0}\"]";
|
||||
private readonly List<XmlDocument> _xmlDocuments = new();
|
||||
private const string _XPATH = "//doc/members/member[@name=\"{0}\"]";
|
||||
private static readonly Regex _regex = new(@"`\d+");
|
||||
private readonly List<XmlDocument> _xmlDocuments = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="XmlCommentReader" /> class.
|
||||
@@ -20,14 +21,15 @@ public sealed class XmlCommentReader : ISingleton
|
||||
nameof(SpecificationDocumentSettingsOptions).TrimEndOptions())
|
||||
.XmlComments;
|
||||
foreach (var commentFile in xmlComments.Where(x => x.Contains(nameof(NetAdmin)))) {
|
||||
var xmlDoc = new XmlDocument();
|
||||
try {
|
||||
xmlDoc.Load(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, commentFile));
|
||||
_xmlDocuments.Add(xmlDoc);
|
||||
}
|
||||
catch (FileNotFoundException) {
|
||||
LogHelper.Get<XmlCommentReader>().Warn(Ln.Xml注释文件不存在);
|
||||
var xmlDoc = new XmlDocument();
|
||||
var xmlFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, commentFile);
|
||||
if (!File.Exists(xmlFilePath)) {
|
||||
LogHelper.Get<XmlCommentReader>().Warn($"{Ln.XML注释文件不存在}: {xmlFilePath}");
|
||||
continue;
|
||||
}
|
||||
|
||||
xmlDoc.Load(xmlFilePath);
|
||||
_xmlDocuments.Add(xmlDoc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,30 +68,32 @@ public sealed class XmlCommentReader : ISingleton
|
||||
|
||||
private XmlNode GetNodeByMethod(MethodInfo method)
|
||||
{
|
||||
static string Replace(ParameterInfo parameterInfo)
|
||||
{
|
||||
return Regex.Replace(parameterInfo.ParameterType.ToString(), @"`\d+", string.Empty)
|
||||
.Replace("[", "{")
|
||||
.Replace("]", "}");
|
||||
}
|
||||
|
||||
var nodeName = $"M:{method.DeclaringType}.{method.Name}";
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 0) {
|
||||
nodeName += $"({string.Join(',', parameters.Select(Replace))})";
|
||||
}
|
||||
|
||||
return _xmlDocuments
|
||||
.Select(xmlDoc => xmlDoc.SelectSingleNode(
|
||||
string.Format(NumberFormatInfo.InvariantInfo, _XPATH, nodeName)))
|
||||
.FirstOrDefault(ret => ret != null);
|
||||
return _xmlDocuments.Select(xmlDoc => xmlDoc.SelectSingleNode(
|
||||
#pragma warning disable CA1863
|
||||
string.Format(NumberFormatInfo.InvariantInfo, _XPATH, nodeName)))
|
||||
#pragma warning restore CA1863
|
||||
.FirstOrDefault(ret => ret != null);
|
||||
|
||||
static string Replace(ParameterInfo parameterInfo)
|
||||
{
|
||||
return _regex.Replace(parameterInfo.ParameterType.ToString(), string.Empty)
|
||||
.Replace("[", "{")
|
||||
.Replace("]", "}");
|
||||
}
|
||||
}
|
||||
|
||||
private XmlNode GetNodeByType(Type type)
|
||||
{
|
||||
return _xmlDocuments
|
||||
.Select(xmlDoc => xmlDoc.SelectSingleNode(
|
||||
string.Format(NumberFormatInfo.InvariantInfo, _XPATH, $"T:{type.FullName}")))
|
||||
.FirstOrDefault(ret => ret != null);
|
||||
return _xmlDocuments.Select(xmlDoc => xmlDoc.SelectSingleNode(
|
||||
#pragma warning disable CA1863
|
||||
string.Format(NumberFormatInfo.InvariantInfo, _XPATH, $"T:{type.FullName}")))
|
||||
#pragma warning restore CA1863
|
||||
.FirstOrDefault(ret => ret != null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user