😎1、优化sqlsugar扩展相关及其他代码 2、升级依赖
This commit is contained in:
parent
23ab3f9c2c
commit
a95b29757d
@ -26,11 +26,11 @@
|
|||||||
<PackageReference Include="AngleSharp" Version="1.3.0" />
|
<PackageReference Include="AngleSharp" Version="1.3.0" />
|
||||||
<PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
|
<PackageReference Include="AspectCore.Extensions.Reflection" Version="2.4.0" />
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" Aliases="BouncyCastleV2" />
|
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.0" Aliases="BouncyCastleV2" />
|
||||||
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.0.3" />
|
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.0.3" />
|
||||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.62" />
|
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.7.68" />
|
||||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.62" />
|
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.9.7.68" />
|
||||||
<PackageReference Include="Furion.Pure" Version="4.9.7.62" />
|
<PackageReference Include="Furion.Pure" Version="4.9.7.68" />
|
||||||
<PackageReference Include="Hardware.Info" Version="101.0.1" />
|
<PackageReference Include="Hardware.Info" Version="101.0.1" />
|
||||||
<PackageReference Include="Hashids.net" Version="1.7.0" />
|
<PackageReference Include="Hashids.net" Version="1.7.0" />
|
||||||
<PackageReference Include="IPTools.China" Version="1.6.0" />
|
<PackageReference Include="IPTools.China" Version="1.6.0" />
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.2" />
|
<PackageReference Include="Magicodes.IE.Excel" Version="2.7.5.2" />
|
||||||
<PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.2" />
|
<PackageReference Include="Magicodes.IE.Pdf" Version="2.7.5.2" />
|
||||||
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.2" />
|
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.2" />
|
||||||
<PackageReference Include="MailKit" Version="4.12.0" />
|
<PackageReference Include="MailKit" Version="4.12.1" />
|
||||||
<PackageReference Include="MiniExcel" Version="1.41.1" />
|
<PackageReference Include="MiniExcel" Version="1.41.1" />
|
||||||
<PackageReference Include="MiniWord" Version="0.9.2" />
|
<PackageReference Include="MiniWord" Version="0.9.2" />
|
||||||
<PackageReference Include="MQTTnet.Server" Version="5.0.1.1416" />
|
<PackageReference Include="MQTTnet.Server" Version="5.0.1.1416" />
|
||||||
@ -56,7 +56,7 @@
|
|||||||
<PackageReference Include="SSH.NET" Version="2025.0.0" />
|
<PackageReference Include="SSH.NET" Version="2025.0.0" />
|
||||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.3" />
|
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.3" />
|
||||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1238" />
|
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1240" />
|
||||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 枚举拓展
|
/// 枚举拓展方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class EnumExtension
|
public static class EnumExtension
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,6 +8,9 @@ using Microsoft.AspNetCore.Authentication;
|
|||||||
|
|
||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HttpContext 扩展方法
|
||||||
|
/// </summary>
|
||||||
public static class HttpContextExtension
|
public static class HttpContextExtension
|
||||||
{
|
{
|
||||||
public static async Task<AuthenticationScheme[]> GetExternalProvidersAsync(this HttpContext context)
|
public static async Task<AuthenticationScheme[]> GetExternalProvidersAsync(this HttpContext context)
|
||||||
|
|||||||
@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
public static class ListExtensions
|
/// <summary>
|
||||||
|
/// List 扩展方法
|
||||||
|
/// </summary>
|
||||||
|
public static class ListExtension
|
||||||
{
|
{
|
||||||
public static async Task ForEachAsync<T>(this List<T> list, Func<T, Task> func)
|
public static async Task ForEachAsync<T>(this List<T> list, Func<T, Task> func)
|
||||||
{
|
{
|
||||||
@ -7,7 +7,7 @@
|
|||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对象拓展
|
/// 对象拓展方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static partial class ObjectExtension
|
public static partial class ObjectExtension
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,6 +8,9 @@ using MapsterMapper;
|
|||||||
|
|
||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 仓储扩展方法
|
||||||
|
/// </summary>
|
||||||
public static class RepositoryExtension
|
public static class RepositoryExtension
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -444,146 +447,4 @@ public static class RepositoryExtension
|
|||||||
throw Oops.Oh(error);
|
throw Oops.Oh(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化表实体
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dbProvider"></param>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static void InitTable<T>(this SqlSugarScopeProvider dbProvider) where T : class, new()
|
|
||||||
{
|
|
||||||
InitTable(dbProvider, typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化表实体
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="entityType"></param>
|
|
||||||
/// <param name="dbProvider"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static void InitTable(this SqlSugarScopeProvider dbProvider, Type entityType)
|
|
||||||
{
|
|
||||||
// 初始化表实体,如果存在分表特性,需要额外处理
|
|
||||||
if (entityType.GetCustomAttribute<SplitTableAttribute>() == null)
|
|
||||||
dbProvider.CodeFirst.InitTables(entityType);
|
|
||||||
else
|
|
||||||
dbProvider.CodeFirst.SplitTables().InitTables(entityType);
|
|
||||||
|
|
||||||
// 将不存在实体中的字段改为可空
|
|
||||||
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
|
|
||||||
var dbColumnInfos = dbProvider.DbMaintenance.GetColumnInfosByTableName(entityInfo.DbTableName) ?? [];
|
|
||||||
foreach (var dbColumnInfo in dbColumnInfos.Where(dbColumnInfo => !dbColumnInfo.IsPrimarykey && entityInfo.Columns.All(u => u.DbColumnName != dbColumnInfo.DbColumnName)))
|
|
||||||
{
|
|
||||||
dbColumnInfo.IsNullable = true;
|
|
||||||
dbProvider.DbMaintenance.UpdateColumn(entityInfo.DbTableName, dbColumnInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化表种子数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="db"></param>
|
|
||||||
/// <param name="handleBefore"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static (int, int, int)? InitTableSeedData<T>(this SqlSugarScope db, Action<object> handleBefore = null)
|
|
||||||
{
|
|
||||||
return InitTableSeedData(db, typeof(T), handleBefore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化表种子数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="db"></param>
|
|
||||||
/// <param name="seedType"></param>
|
|
||||||
/// <param name="handleBefore"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static (int, int, int)? InitTableSeedData(this SqlSugarScope db, Type seedType, Action<object> handleBefore = null)
|
|
||||||
{
|
|
||||||
var config = db.CurrentConnectionConfig;
|
|
||||||
var dbProvider = db.GetConnectionScope(config.ConfigId);
|
|
||||||
|
|
||||||
// 获取表实体类型
|
|
||||||
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
|
||||||
|
|
||||||
if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
|
|
||||||
{
|
|
||||||
if (entityType.GetCustomAttribute<SysTableAttribute>() == null &&
|
|
||||||
(entityType.GetCustomAttribute<LogTableAttribute>() != null ||
|
|
||||||
entityType.GetCustomAttribute<TenantAttribute>() != null))
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
|
|
||||||
{
|
|
||||||
if (entityType.GetCustomAttribute<LogTableAttribute>() == null) return default;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
|
|
||||||
if (att == null || att.configId.ToString() != config.ConfigId.ToString()) return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
var instance = Activator.CreateInstance(seedType);
|
|
||||||
var hasDataMethod = seedType.GetMethod("HasData");
|
|
||||||
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>().ToArray() ?? [];
|
|
||||||
if (!seedData.Any()) return default;
|
|
||||||
|
|
||||||
// 若实体包含Id字段,则设置为当前租户Id递增1
|
|
||||||
var idProp = entityType.GetProperty(nameof(EntityBaseId.Id));
|
|
||||||
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
|
|
||||||
if (idProp != null && entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityBaseId.Id)))
|
|
||||||
{
|
|
||||||
var seedId = config.ConfigId.ToLong();
|
|
||||||
foreach (var sd in seedData)
|
|
||||||
{
|
|
||||||
var id = idProp!.GetValue(sd, null);
|
|
||||||
if (id == null || id.ToString() == "0" || string.IsNullOrWhiteSpace(id.ToString()))
|
|
||||||
idProp.SetValue(sd, ++seedId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行前处理种子数据
|
|
||||||
if (handleBefore != null) foreach (var sd in seedData) handleBefore(sd);
|
|
||||||
|
|
||||||
int total, insertCount = 0, updateCount = 0;
|
|
||||||
if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
|
|
||||||
{
|
|
||||||
// 拆分表的操作需要实体类型,而通过反射很难实现
|
|
||||||
// 所以,这里将Init方法写在“种子数据类”内部,再传入 db 反射调用
|
|
||||||
var hasInitMethod = seedType.GetMethod("Init");
|
|
||||||
var parameters = new object[] { db };
|
|
||||||
var result = hasInitMethod?.Invoke(instance, parameters) as (int, int, int)?;
|
|
||||||
total = result?.Item1 ?? 0;
|
|
||||||
insertCount = result?.Item2 ?? 0;
|
|
||||||
updateCount = result?.Item3 ?? 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var seedDataList = seedData.ToList();
|
|
||||||
total = seedDataList.Count;
|
|
||||||
|
|
||||||
// 按主键进行批量增加和更新
|
|
||||||
if (entityInfo.Columns.Any(u => u.IsPrimarykey))
|
|
||||||
{
|
|
||||||
// 先修改再插入,否则会更新修改时间字段
|
|
||||||
var storage = dbProvider.StorageableByObject(seedDataList).ToStorage();
|
|
||||||
if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
|
|
||||||
{
|
|
||||||
updateCount = storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null)
|
|
||||||
.Select(u => u.PropertyName).ToArray()).ExecuteCommand();
|
|
||||||
}
|
|
||||||
insertCount = storage.AsInsertable.ExecuteCommand();
|
|
||||||
}
|
|
||||||
// 无主键则只进行插入
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!dbProvider.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
|
|
||||||
{
|
|
||||||
insertCount = seedDataList.Count;
|
|
||||||
dbProvider.InsertableByObject(seedDataList).ExecuteCommand();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (total, insertCount, updateCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,58 +0,0 @@
|
|||||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
|
||||||
//
|
|
||||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
|
||||||
//
|
|
||||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
|
||||||
|
|
||||||
namespace Admin.NET.Core;
|
|
||||||
|
|
||||||
public static class SqlSugarFilterExtension
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 根据指定Attribute获取属性
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static List<string> GetPropertyNames<T>(this Type type) where T : Attribute
|
|
||||||
{
|
|
||||||
return type.GetProperties()
|
|
||||||
.Where(p => p.CustomAttributes.Any(x => x.AttributeType == typeof(T)))
|
|
||||||
.Select(x => x.Name).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取过滤表达式
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="owners"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static LambdaExpression GetConditionExpression<T>(this Type type, List<long> owners) where T : Attribute
|
|
||||||
{
|
|
||||||
var fieldNames = type.GetPropertyNames<T>();
|
|
||||||
ParameterExpression parameter = Expression.Parameter(type, "c");
|
|
||||||
|
|
||||||
Expression right = Expression.Constant(false);
|
|
||||||
ConstantExpression ownersCollection = Expression.Constant(owners);
|
|
||||||
foreach (var fieldName in fieldNames)
|
|
||||||
{
|
|
||||||
var property = type.GetProperty(fieldName);
|
|
||||||
Expression memberExp = Expression.Property(parameter, property!);
|
|
||||||
|
|
||||||
// 如果属性是可为空的类型,则转换为其基础类型
|
|
||||||
var baseType = Nullable.GetUnderlyingType(property.PropertyType);
|
|
||||||
if (baseType != null) memberExp = Expression.Convert(memberExp, baseType);
|
|
||||||
|
|
||||||
// 调用ownersCollection.Contains方法,检查是否包含属性值
|
|
||||||
right = Expression.OrElse(Expression.Call(
|
|
||||||
typeof(Enumerable),
|
|
||||||
nameof(Enumerable.Contains),
|
|
||||||
new[] { memberExp.Type },
|
|
||||||
ownersCollection,
|
|
||||||
memberExp
|
|
||||||
), right);
|
|
||||||
}
|
|
||||||
return Expression.Lambda(right, parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -220,7 +220,7 @@ public class SysDbBackupService : IDynamicApiController, ITransient
|
|||||||
// 压缩成功后,将临时文件移动到目标路径
|
// 压缩成功后,将临时文件移动到目标路径
|
||||||
File.Move(tempPath, finalPath);
|
File.Move(tempPath, finalPath);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
// 清理临时文件
|
// 清理临时文件
|
||||||
if (File.Exists(tempPath))
|
if (File.Exists(tempPath))
|
||||||
|
|||||||
@ -170,7 +170,7 @@ public class GenerateQRImageInput
|
|||||||
public class GenerateQRImageUnLimitInput : GenerateQRImageInput
|
public class GenerateQRImageUnLimitInput : GenerateQRImageInput
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 二维码携带的参数 eg:a=1(最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~)
|
/// 二维码携带的参数 eg:a=1(最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:<!-- !#$&'()*+,/:;=?@-._~ -->)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Scene { get; set; }
|
public string Scene { get; set; }
|
||||||
}
|
}
|
||||||
@ -9,10 +9,72 @@ using System.Text.Json;
|
|||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sqlsugar 动态查询扩展方法
|
/// Sqlsugar 扩展方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SqlSugarExtension
|
public static class SqlSugarExtension
|
||||||
{
|
{
|
||||||
|
#region 切换数据库
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity"></typeparam>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ISqlSugarClient ForTenant<TEntity>(this ISqlSugarClient db)
|
||||||
|
{
|
||||||
|
var attr = typeof(TEntity).GetCustomAttribute<TenantAttribute>();
|
||||||
|
var tenantId = attr != null ? GetConfigIdFromAttribute(attr) : SqlSugarConst.DefaultTenantId;
|
||||||
|
return db.AsTenant().GetConnection(tenantId ?? SqlSugarConst.DefaultTenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetConfigIdFromAttribute(TenantAttribute attr)
|
||||||
|
{
|
||||||
|
const string targetKey = "configId";
|
||||||
|
var type = attr.GetType();
|
||||||
|
|
||||||
|
// 方式1:尝试通过属性获取
|
||||||
|
var prop = type.GetProperty(targetKey, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
if (prop != null) return prop.GetValue(attr);
|
||||||
|
|
||||||
|
// 方式2:尝试通过私有字段获取
|
||||||
|
var fieldNames = new[]
|
||||||
|
{
|
||||||
|
$"<{targetKey}>k__BackingField", // 自动属性字段
|
||||||
|
$"_{targetKey}", // 常规私有字段 (如_configId)
|
||||||
|
$"m_{targetKey}", // 匈牙利命名法
|
||||||
|
"configId" // 直接字段访问
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var name in fieldNames)
|
||||||
|
{
|
||||||
|
var field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
if (field != null) return field.GetValue(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方式3:通过Dynamic访问(备用方案)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dynamic d = attr;
|
||||||
|
return d.configId;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 切换数据库
|
||||||
|
|
||||||
|
#region 动态查询扩展方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sqlsugar 动态查询扩展方法
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="queryable"></param>
|
||||||
|
/// <param name="filter"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static ISugarQueryable<T> SearchBy<T>(this ISugarQueryable<T> queryable, BaseFilter filter)
|
public static ISugarQueryable<T> SearchBy<T>(this ISugarQueryable<T> queryable, BaseFilter filter)
|
||||||
{
|
{
|
||||||
return queryable.SearchByKeyword(filter.Keyword).AdvancedSearch(filter.Search).AdvancedFilter(filter.Filter);
|
return queryable.SearchByKeyword(filter.Keyword).AdvancedSearch(filter.Search).AdvancedFilter(filter.Filter);
|
||||||
@ -253,4 +315,152 @@ public static class SqlSugarExtension
|
|||||||
|
|
||||||
return Expression.Call(selectorExpr, method, constant);
|
return Expression.Call(selectorExpr, method, constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion 动态查询扩展方法
|
||||||
|
|
||||||
|
#region 切换数据库
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化表实体
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dbProvider"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static void InitTable<T>(this SqlSugarScopeProvider dbProvider) where T : class, new()
|
||||||
|
{
|
||||||
|
InitTable(dbProvider, typeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化表实体
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityType"></param>
|
||||||
|
/// <param name="dbProvider"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static void InitTable(this SqlSugarScopeProvider dbProvider, Type entityType)
|
||||||
|
{
|
||||||
|
// 初始化表实体,如果存在分表特性,需要额外处理
|
||||||
|
if (entityType.GetCustomAttribute<SplitTableAttribute>() == null)
|
||||||
|
dbProvider.CodeFirst.InitTables(entityType);
|
||||||
|
else
|
||||||
|
dbProvider.CodeFirst.SplitTables().InitTables(entityType);
|
||||||
|
|
||||||
|
// 将不存在实体中的字段改为可空
|
||||||
|
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
|
||||||
|
var dbColumnInfos = dbProvider.DbMaintenance.GetColumnInfosByTableName(entityInfo.DbTableName) ?? [];
|
||||||
|
foreach (var dbColumnInfo in dbColumnInfos.Where(dbColumnInfo => !dbColumnInfo.IsPrimarykey && entityInfo.Columns.All(u => u.DbColumnName != dbColumnInfo.DbColumnName)))
|
||||||
|
{
|
||||||
|
dbColumnInfo.IsNullable = true;
|
||||||
|
dbProvider.DbMaintenance.UpdateColumn(entityInfo.DbTableName, dbColumnInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化表种子数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
/// <param name="handleBefore"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static (int, int, int)? InitTableSeedData<T>(this SqlSugarScope db, Action<object> handleBefore = null)
|
||||||
|
{
|
||||||
|
return InitTableSeedData(db, typeof(T), handleBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化表种子数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db"></param>
|
||||||
|
/// <param name="seedType"></param>
|
||||||
|
/// <param name="handleBefore"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static (int, int, int)? InitTableSeedData(this SqlSugarScope db, Type seedType, Action<object> handleBefore = null)
|
||||||
|
{
|
||||||
|
var config = db.CurrentConnectionConfig;
|
||||||
|
var dbProvider = db.GetConnectionScope(config.ConfigId);
|
||||||
|
|
||||||
|
// 获取表实体类型
|
||||||
|
var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
|
||||||
|
|
||||||
|
if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
|
||||||
|
{
|
||||||
|
if (entityType.GetCustomAttribute<SysTableAttribute>() == null &&
|
||||||
|
(entityType.GetCustomAttribute<LogTableAttribute>() != null ||
|
||||||
|
entityType.GetCustomAttribute<TenantAttribute>() != null))
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
|
||||||
|
{
|
||||||
|
if (entityType.GetCustomAttribute<LogTableAttribute>() == null) return default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
|
||||||
|
if (att == null || att.configId.ToString() != config.ConfigId.ToString()) return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = Activator.CreateInstance(seedType);
|
||||||
|
var hasDataMethod = seedType.GetMethod("HasData");
|
||||||
|
var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>().ToArray() ?? [];
|
||||||
|
if (!seedData.Any()) return default;
|
||||||
|
|
||||||
|
// 若实体包含Id字段,则设置为当前租户Id递增1
|
||||||
|
var idProp = entityType.GetProperty(nameof(EntityBaseId.Id));
|
||||||
|
var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);
|
||||||
|
if (idProp != null && entityInfo.Columns.Any(u => u.PropertyName == nameof(EntityBaseId.Id)))
|
||||||
|
{
|
||||||
|
var seedId = config.ConfigId.ToLong();
|
||||||
|
foreach (var sd in seedData)
|
||||||
|
{
|
||||||
|
var id = idProp!.GetValue(sd, null);
|
||||||
|
if (id == null || id.ToString() == "0" || string.IsNullOrWhiteSpace(id.ToString()))
|
||||||
|
idProp.SetValue(sd, ++seedId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行前处理种子数据
|
||||||
|
if (handleBefore != null) foreach (var sd in seedData) handleBefore(sd);
|
||||||
|
|
||||||
|
int total, insertCount = 0, updateCount = 0;
|
||||||
|
if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
|
||||||
|
{
|
||||||
|
// 拆分表的操作需要实体类型,而通过反射很难实现
|
||||||
|
// 所以,这里将Init方法写在“种子数据类”内部,再传入 db 反射调用
|
||||||
|
var hasInitMethod = seedType.GetMethod("Init");
|
||||||
|
var parameters = new object[] { db };
|
||||||
|
var result = hasInitMethod?.Invoke(instance, parameters) as (int, int, int)?;
|
||||||
|
total = result?.Item1 ?? 0;
|
||||||
|
insertCount = result?.Item2 ?? 0;
|
||||||
|
updateCount = result?.Item3 ?? 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var seedDataList = seedData.ToList();
|
||||||
|
total = seedDataList.Count;
|
||||||
|
|
||||||
|
// 按主键进行批量增加和更新
|
||||||
|
if (entityInfo.Columns.Any(u => u.IsPrimarykey))
|
||||||
|
{
|
||||||
|
// 先修改再插入,否则会更新修改时间字段
|
||||||
|
var storage = dbProvider.StorageableByObject(seedDataList).ToStorage();
|
||||||
|
if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
|
||||||
|
{
|
||||||
|
updateCount = storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null)
|
||||||
|
.Select(u => u.PropertyName).ToArray()).ExecuteCommand();
|
||||||
|
}
|
||||||
|
insertCount = storage.AsInsertable.ExecuteCommand();
|
||||||
|
}
|
||||||
|
// 无主键则只进行插入
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!dbProvider.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
|
||||||
|
{
|
||||||
|
insertCount = seedDataList.Count;
|
||||||
|
dbProvider.InsertableByObject(seedDataList).ExecuteCommand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (total, insertCount, updateCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion 切换数据库
|
||||||
}
|
}
|
||||||
@ -1,56 +0,0 @@
|
|||||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
|
||||||
//
|
|
||||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
|
||||||
//
|
|
||||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
|
||||||
|
|
||||||
namespace Admin.NET.Core;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 切换数据库
|
|
||||||
/// </summary>
|
|
||||||
public static class SqlSugarExtensions
|
|
||||||
{
|
|
||||||
public static ISqlSugarClient ForTenant<TEntity>(this ISqlSugarClient db)
|
|
||||||
{
|
|
||||||
var attr = typeof(TEntity).GetCustomAttribute<TenantAttribute>();
|
|
||||||
var tenantId = attr != null ? GetConfigIdFromAttribute(attr) : SqlSugarConst.DefaultTenantId;
|
|
||||||
return db.AsTenant().GetConnection(tenantId ?? SqlSugarConst.DefaultTenantId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object GetConfigIdFromAttribute(TenantAttribute attr)
|
|
||||||
{
|
|
||||||
const string targetKey = "configId";
|
|
||||||
var type = attr.GetType();
|
|
||||||
|
|
||||||
// 方式1:尝试通过属性获取
|
|
||||||
var prop = type.GetProperty(targetKey, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
||||||
if (prop != null) return prop.GetValue(attr);
|
|
||||||
|
|
||||||
// 方式2:尝试通过私有字段获取
|
|
||||||
var fieldNames = new[]
|
|
||||||
{
|
|
||||||
$"<{targetKey}>k__BackingField", // 自动属性字段
|
|
||||||
$"_{targetKey}", // 常规私有字段 (如_configId)
|
|
||||||
$"m_{targetKey}", // 匈牙利命名法
|
|
||||||
"configId" // 直接字段访问
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var name in fieldNames)
|
|
||||||
{
|
|
||||||
var field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Instance);
|
|
||||||
if (field != null) return field.GetValue(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方式3:通过Dynamic访问(备用方案)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dynamic d = attr;
|
|
||||||
return d.configId;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -207,6 +207,54 @@ public static class SqlSugarFilter
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取过滤表达式
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="owners"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static LambdaExpression GetConditionExpression<T>(this Type type, List<long> owners) where T : Attribute
|
||||||
|
{
|
||||||
|
var fieldNames = type.GetPropertyNames<T>();
|
||||||
|
ParameterExpression parameter = Expression.Parameter(type, "c");
|
||||||
|
|
||||||
|
Expression right = Expression.Constant(false);
|
||||||
|
ConstantExpression ownersCollection = Expression.Constant(owners);
|
||||||
|
foreach (var fieldName in fieldNames)
|
||||||
|
{
|
||||||
|
var property = type.GetProperty(fieldName);
|
||||||
|
Expression memberExp = Expression.Property(parameter, property!);
|
||||||
|
|
||||||
|
// 如果属性是可为空的类型,则转换为其基础类型
|
||||||
|
var baseType = Nullable.GetUnderlyingType(property.PropertyType);
|
||||||
|
if (baseType != null) memberExp = Expression.Convert(memberExp, baseType);
|
||||||
|
|
||||||
|
// 调用ownersCollection.Contains方法,检查是否包含属性值
|
||||||
|
right = Expression.OrElse(Expression.Call(
|
||||||
|
typeof(Enumerable),
|
||||||
|
nameof(Enumerable.Contains),
|
||||||
|
new[] { memberExp.Type },
|
||||||
|
ownersCollection,
|
||||||
|
memberExp
|
||||||
|
), right);
|
||||||
|
}
|
||||||
|
return Expression.Lambda(right, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据指定Attribute获取属性
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static List<string> GetPropertyNames<T>(this Type type) where T : Attribute
|
||||||
|
{
|
||||||
|
return type.GetProperties()
|
||||||
|
.Where(p => p.CustomAttributes.Any(x => x.AttributeType == typeof(T)))
|
||||||
|
.Select(x => x.Name).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -525,7 +525,6 @@ public static class SqlSugarSetup
|
|||||||
.WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
|
.WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
|
||||||
.OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();
|
.OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();
|
||||||
|
|
||||||
|
|
||||||
var semaphore = new SemaphoreSlim(CommonConst.MaxConcurrent); // 并发限制数量
|
var semaphore = new SemaphoreSlim(CommonConst.MaxConcurrent); // 并发限制数量
|
||||||
int taskIndex = 0, size = seedDataTypes.Count;
|
int taskIndex = 0, size = seedDataTypes.Count;
|
||||||
var taskList = seedDataTypes.Select(seedType => Task.Run(() =>
|
var taskList = seedDataTypes.Select(seedType => Task.Run(() =>
|
||||||
|
|||||||
@ -24,14 +24,9 @@
|
|||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="OcrModel\**" />
|
|
||||||
<EmbeddedResource Remove="OcrModel\**" />
|
|
||||||
<None Remove="OcrModel\**" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="PaddleOCRSharp" Version="4.5.0.1" />
|
<PackageReference Include="PaddleOCRSharp" Version="4.5.0.1" />
|
||||||
|
<PackageReference Include="Paddle.Runtime.win_x64" Version="3.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
|
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.13.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
|
||||||
<PackageReference Include="Rezero.Api" Version="1.8.16" />
|
<PackageReference Include="Rezero.Api" Version="1.8.16" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "admin.net.pro",
|
"name": "admin.net.pro",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.4.33",
|
"version": "2.4.33",
|
||||||
"lastBuildTime": "2025.05.15",
|
"lastBuildTime": "2025.05.18",
|
||||||
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
||||||
"author": "zuohuaijun",
|
"author": "zuohuaijun",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -20,7 +20,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@logicflow/core": "^2.0.13",
|
"@logicflow/core": "^2.0.13",
|
||||||
"@logicflow/extension": "^2.0.17",
|
"@logicflow/extension": "^2.0.18",
|
||||||
"@microsoft/signalr": "^8.0.7",
|
"@microsoft/signalr": "^8.0.7",
|
||||||
"@vue-office/docx": "^1.6.2",
|
"@vue-office/docx": "^1.6.2",
|
||||||
"@vue-office/excel": "^1.7.14",
|
"@vue-office/excel": "^1.7.14",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"splitpanes": "^4.0.3",
|
"splitpanes": "^4.0.3",
|
||||||
"vcrontab-3": "^3.3.22",
|
"vcrontab-3": "^3.3.22",
|
||||||
"vform3-builds": "^3.0.10",
|
"vform3-builds": "^3.0.10",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.14",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
"vue-demi": "0.14.10",
|
"vue-demi": "0.14.10",
|
||||||
"vue-draggable-plus": "^0.6.0",
|
"vue-draggable-plus": "^0.6.0",
|
||||||
@ -80,8 +80,8 @@
|
|||||||
"vue-signature-pad": "^3.0.2",
|
"vue-signature-pad": "^3.0.2",
|
||||||
"vue3-flag-icons": "^0.0.3",
|
"vue3-flag-icons": "^0.0.3",
|
||||||
"vue3-tree-org": "^4.2.2",
|
"vue3-tree-org": "^4.2.2",
|
||||||
"vxe-pc-ui": "^4.6.8",
|
"vxe-pc-ui": "^4.6.11",
|
||||||
"vxe-table": "^4.13.28",
|
"vxe-table": "^4.13.30",
|
||||||
"xe-utils": "^3.7.4",
|
"xe-utils": "^3.7.4",
|
||||||
"xlsx-js-style": "^1.2.0"
|
"xlsx-js-style": "^1.2.0"
|
||||||
},
|
},
|
||||||
@ -96,17 +96,17 @@
|
|||||||
"@typescript-eslint/parser": "^8.32.1",
|
"@typescript-eslint/parser": "^8.32.1",
|
||||||
"@vitejs/plugin-vue": "^5.2.4",
|
"@vitejs/plugin-vue": "^5.2.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
||||||
"@vue/compiler-sfc": "^3.5.13",
|
"@vue/compiler-sfc": "^3.5.14",
|
||||||
"code-inspector-plugin": "^0.20.10",
|
"code-inspector-plugin": "^0.20.10",
|
||||||
"eslint": "^9.26.0",
|
"eslint": "^9.27.0",
|
||||||
"eslint-plugin-vue": "^10.1.0",
|
"eslint-plugin-vue": "^10.1.0",
|
||||||
"globals": "^16.1.0",
|
"globals": "^16.1.0",
|
||||||
"less": "^4.3.0",
|
"less": "^4.3.0",
|
||||||
"openapi-ts-request": "^1.4.0",
|
"openapi-ts-request": "^1.5.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"rollup-plugin-visualizer": "^5.14.0",
|
"rollup-plugin-visualizer": "^5.14.0",
|
||||||
"sass": "^1.88.0",
|
"sass": "^1.89.0",
|
||||||
"terser": "^5.39.1",
|
"terser": "^5.39.2",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^6.3.5",
|
"vite": "^6.3.5",
|
||||||
"vite-plugin-cdn-import": "^1.0.1",
|
"vite-plugin-cdn-import": "^1.0.1",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user