2026-03-22 17:45:48 +08:00
|
|
|
|
using System.Collections.Generic;
|
2025-12-17 10:32:52 +08:00
|
|
|
|
using System.Data;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Reflection;
|
2025-12-17 11:12:13 +08:00
|
|
|
|
using YD_Event.Core.Const;
|
|
|
|
|
|
using YD_Event.Core.Entities;
|
|
|
|
|
|
using YD_Event.Core.Enum;
|
|
|
|
|
|
using YD_Event.Core.Options;
|
2025-12-17 10:32:52 +08:00
|
|
|
|
using Mapster;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using Yitter.IdGenerator;
|
|
|
|
|
|
|
|
|
|
|
|
namespace SqlSugar;
|
|
|
|
|
|
|
|
|
|
|
|
public static class SqlSugarExtensions
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 添加 SqlSugar 拓展(适用于单个数据库)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="services"></param>
|
|
|
|
|
|
/// <param name="config">数据库连接对象</param>
|
|
|
|
|
|
/// <param name="buildAction"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public static IServiceCollection AddSqlSugar(this IServiceCollection services, ConnectionConfigExt config, Action<ISqlSugarClient> buildAction = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
return services.AddSqlSugar(new List<ConnectionConfigExt>()
|
|
|
|
|
|
{
|
|
|
|
|
|
config
|
|
|
|
|
|
}, buildAction);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 添加 SqlSugar 拓展(适用于多数据库)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="services"></param>
|
|
|
|
|
|
/// <param name="configs">数据库连接对象集合</param>
|
|
|
|
|
|
/// <param name="buildAction"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public static IServiceCollection AddSqlSugar(this IServiceCollection services, List<ConnectionConfigExt> configs, Action<SqlSugarClient> buildAction = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
SqlSugarScope sqlSugarScope = new SqlSugarScope(configs.Adapt<List<ConnectionConfig>>(), buildAction ?? Aop);
|
|
|
|
|
|
configs.ForEach(x =>
|
|
|
|
|
|
{
|
|
|
|
|
|
Init(sqlSugarScope, x);
|
|
|
|
|
|
});
|
|
|
|
|
|
services.AddSingleton<ISqlSugarClient>(sqlSugarScope);//使用单例注入
|
|
|
|
|
|
services.AddSingleton<ITenant>(sqlSugarScope);
|
|
|
|
|
|
services.AddUnitOfWork<SqlSugarUnitOfWork>();//事务
|
|
|
|
|
|
services.AddScoped(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));//注入泛型仓储
|
|
|
|
|
|
|
|
|
|
|
|
return services;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化数据库
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="client"></param>
|
|
|
|
|
|
/// <param name="config"></param>
|
|
|
|
|
|
static void Init(SqlSugarScope client, ConnectionConfigExt config)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!config.EnableInitDb)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
SqlSugarScopeProvider provider = client.GetConnectionScope(config.ConfigId);
|
|
|
|
|
|
//创建数据库
|
|
|
|
|
|
provider.DbMaintenance.CreateDatabase();
|
|
|
|
|
|
IEnumerable<Type> types = App.EffectiveTypes.Where(x => !x.IsInterface && x.IsClass && !x.IsAbstract && typeof(IEntity).IsAssignableFrom(x));
|
|
|
|
|
|
provider.CodeFirst.InitTables(types.ToArray());
|
|
|
|
|
|
|
|
|
|
|
|
//初始化数据
|
|
|
|
|
|
InitData(client);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 添加 SqlSugar 服务
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="services"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public static IServiceCollection AddSqlSugar(this IServiceCollection services)
|
|
|
|
|
|
{
|
|
|
|
|
|
var options = App.GetOptions<DbConnectionOptions>();
|
|
|
|
|
|
ICacheService ormCache = new SqlSugarCache();
|
|
|
|
|
|
options.Connections.ForEach(x =>
|
|
|
|
|
|
{
|
|
|
|
|
|
x.MoreSettings = new ConnMoreSettings()
|
|
|
|
|
|
{
|
|
|
|
|
|
//所有 增、删 、改 会自动调用.RemoveDataCache()
|
|
|
|
|
|
IsAutoRemoveDataCache = true
|
|
|
|
|
|
};
|
|
|
|
|
|
//配置缓存
|
|
|
|
|
|
x.ConfigureExternalServices = new ConfigureExternalServices()
|
|
|
|
|
|
{
|
|
|
|
|
|
DataInfoCacheService = ormCache,
|
|
|
|
|
|
EntityService = ((prop, column) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
//如果实体不是主键,并且是可空类型,表列设置为可空(支持string?和string)
|
|
|
|
|
|
if (column.IsPrimarykey == false && new NullabilityInfoContext()
|
|
|
|
|
|
.Create(prop).WriteState is NullabilityState.Nullable)
|
|
|
|
|
|
{
|
|
|
|
|
|
column.IsNullable = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
return AddSqlSugar(services, options.Connections, Aop);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 拦截器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static readonly Action<SqlSugarClient> Aop = (db) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
db.Aop.DataExecuting = (_, entityInfo) =>
|
|
|
|
|
|
{
|
2026-03-12 11:56:17 +08:00
|
|
|
|
try
|
2025-12-17 10:32:52 +08:00
|
|
|
|
{
|
2026-03-12 11:56:17 +08:00
|
|
|
|
switch (entityInfo.OperationType)
|
|
|
|
|
|
{
|
|
|
|
|
|
//执行insert时
|
|
|
|
|
|
case DataFilterType.InsertByObject:
|
|
|
|
|
|
//自动设置主键
|
|
|
|
|
|
if (entityInfo.EntityColumnInfo.IsPrimarykey &&
|
|
|
|
|
|
entityInfo.EntityValue is Entity<long> { Id: 0 } entity)
|
|
|
|
|
|
{
|
|
|
|
|
|
entity.Id = YitIdHelper.NextId();
|
|
|
|
|
|
}
|
2026-08-30 21:46:05 +08:00
|
|
|
|
// 创建审计字段只在调用方未赋值时补齐,保留数据迁移等场景的显式历史值。
|
|
|
|
|
|
if (entityInfo.PropertyName == nameof(ICreatedTime.CreatedTime) &&
|
|
|
|
|
|
entityInfo.EntityValue is ICreatedTime { CreatedTime: var createdAt } &&
|
|
|
|
|
|
createdAt == DateTime.MinValue)
|
2026-03-12 11:56:17 +08:00
|
|
|
|
{
|
2026-08-30 21:46:05 +08:00
|
|
|
|
entityInfo.SetValue(DateTime.Now);
|
2026-03-12 11:56:17 +08:00
|
|
|
|
}
|
2026-08-30 21:46:05 +08:00
|
|
|
|
if (entityInfo.PropertyName == nameof(ICreatedUserId.CreatedUserId) &&
|
|
|
|
|
|
entityInfo.EntityValue is ICreatedUserId { CreatedUserId: 0 } &&
|
|
|
|
|
|
TryGetCurrentUserId(out var createdUserId))
|
2026-03-12 11:56:17 +08:00
|
|
|
|
{
|
2026-08-30 21:46:05 +08:00
|
|
|
|
entityInfo.SetValue(createdUserId);
|
2026-03-12 11:56:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DataFilterType.UpdateByObject:
|
2026-08-30 21:46:05 +08:00
|
|
|
|
// 修改时间每次更新都刷新;有登录上下文时,修改人统一取当前操作者。
|
|
|
|
|
|
if (entityInfo.PropertyName == nameof(IUpdatedTime.UpdatedTime) &&
|
|
|
|
|
|
entityInfo.EntityValue is IUpdatedTime)
|
2026-03-12 11:56:17 +08:00
|
|
|
|
{
|
2026-08-30 21:46:05 +08:00
|
|
|
|
entityInfo.SetValue(DateTime.Now);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (entityInfo.PropertyName == nameof(IUpdatedUserId.UpdatedUserId) &&
|
|
|
|
|
|
entityInfo.EntityValue is IUpdatedUserId updatedUser)
|
|
|
|
|
|
{
|
|
|
|
|
|
var claimValue = App.User?.FindFirst(AuthClaimsConst.AuthIdKey)?.Value;
|
|
|
|
|
|
var updatedUserId = ResolveUpdatedUserId(claimValue, updatedUser.UpdatedUserId);
|
|
|
|
|
|
if (updatedUserId.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 存在有效登录用户时始终覆盖显式值,确保记录最后一次实际操作人。
|
|
|
|
|
|
entityInfo.SetValue(updatedUserId.Value);
|
|
|
|
|
|
}
|
2026-03-12 11:56:17 +08:00
|
|
|
|
}
|
2025-12-17 10:32:52 +08:00
|
|
|
|
|
2026-03-12 11:56:17 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case DataFilterType.DeleteByObject:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (TargetInvocationException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"[Error] TargetInvocationException in DataExecuting: {ex.InnerException?.ToString() ?? ex.Message}");
|
|
|
|
|
|
throw ex.InnerException ?? ex;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"[Error] Exception in DataExecuting: {ex.ToString()}");
|
|
|
|
|
|
throw;
|
2025-12-17 10:32:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 文档地址:https://www.donet5.com/Home/Doc?typeId=1204
|
|
|
|
|
|
db.Aop.OnLogExecuting = (sql, parameters) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var originColor = Console.ForegroundColor;
|
|
|
|
|
|
if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.Green;
|
|
|
|
|
|
if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
|
|
|
|
if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.Red;
|
|
|
|
|
|
Console.WriteLine("【" + DateTime.Now + "——执行SQL】\r\n" + UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, sql, parameters) + "\r\n");
|
|
|
|
|
|
Console.ForegroundColor = originColor;
|
|
|
|
|
|
//App.PrintToMiniProfiler("SqlSugar", "Info", sql + "\r\n" + db.Utilities.SerializeObject(parameters.ToDictionary(it => it.ParameterName, it => it.Value)));
|
|
|
|
|
|
};
|
|
|
|
|
|
db.Aop.OnError = ex =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ex.Parametres == null) return;
|
|
|
|
|
|
var originColor = Console.ForegroundColor;
|
|
|
|
|
|
Console.ForegroundColor = ConsoleColor.DarkRed;
|
|
|
|
|
|
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
|
|
|
|
|
|
Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" + UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, ex.Sql, (SugarParameter[])ex.Parametres) + "\r\n");
|
|
|
|
|
|
Console.ForegroundColor = originColor;
|
|
|
|
|
|
// App.PrintToMiniProfiler("SqlSugar", "Error", $"{ex.Message}{Environment.NewLine}{ex.Sql}{pars}{Environment.NewLine}");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//配置逻辑删除过滤器(查询数据时过滤掉已被标记删除的数据)
|
|
|
|
|
|
db.QueryFilter.AddTableFilter<ISoftDelete>(it => it.DeleteMark == false);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-08-30 21:46:05 +08:00
|
|
|
|
private static bool TryGetCurrentUserId(out long userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var claimValue = App.User?.FindFirst(AuthClaimsConst.AuthIdKey)?.Value;
|
|
|
|
|
|
return long.TryParse(claimValue, out userId) && userId > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 有有效登录用户时以当前用户为准;无用户上下文时保留后台任务显式传入的操作者。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal static long? ResolveUpdatedUserId(string? currentUserClaim, long? originalUserId)
|
|
|
|
|
|
{
|
|
|
|
|
|
return long.TryParse(currentUserClaim, out var currentUserId) && currentUserId > 0
|
|
|
|
|
|
? currentUserId
|
|
|
|
|
|
: originalUserId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-17 10:32:52 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化基础数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static void InitData(SqlSugarScope client)
|
|
|
|
|
|
{
|
|
|
|
|
|
string path = Path.Combine(AppContext.BaseDirectory, "InitData");
|
|
|
|
|
|
var dir = new DirectoryInfo(path);
|
|
|
|
|
|
var files = dir.GetFiles("*.txt");
|
|
|
|
|
|
foreach (var file in files)
|
|
|
|
|
|
{
|
|
|
|
|
|
using var reader = file.OpenText();
|
|
|
|
|
|
string s = reader.ReadToEnd();
|
|
|
|
|
|
var table = JsonConvert.DeserializeObject<DataTable>(s);
|
|
|
|
|
|
if (table.Rows.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
table.TableName = file.Name.Replace(".txt", "");
|
|
|
|
|
|
|
|
|
|
|
|
client.Storageable(table).WhereColumns("Id").ToStorage().AsInsertable.ExecuteCommand();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-08-30 21:46:05 +08:00
|
|
|
|
}
|