DbHelper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Threading.Tasks;
  6. using FreeSql;
  7. using FreeSql.Aop;
  8. using FreeSql.DataAnnotations;
  9. using Yitter.IdGenerator;
  10. using ZhonTai.Admin.Core.Configs;
  11. using ZhonTai.Admin.Core.Entities;
  12. using ZhonTai.Admin.Core.Attributes;
  13. using ZhonTai.Admin.Core.Auth;
  14. using System.IO;
  15. using ZhonTai.Common.Helpers;
  16. using ZhonTai.Admin.Core.Db.Data;
  17. using StackExchange.Profiling;
  18. using ZhonTai.Admin.Core.Consts;
  19. using ZhonTai.Admin.Core.Startup;
  20. using ZhonTai.Admin.Domain.Role;
  21. using ZhonTai.Admin.Domain.User;
  22. namespace ZhonTai.Admin.Core.Db;
  23. public class DbHelper
  24. {
  25. /// <summary>
  26. /// 偏移时间
  27. /// </summary>
  28. public static TimeSpan TimeOffset;
  29. /// <summary>
  30. /// 创建数据库
  31. /// </summary>
  32. /// <param name="dbConfig"></param>
  33. /// <returns></returns>
  34. public async static Task CreateDatabaseAsync(DbConfig dbConfig)
  35. {
  36. if (!dbConfig.CreateDb || dbConfig.Type == DataType.Sqlite)
  37. {
  38. return;
  39. }
  40. var db = new FreeSqlBuilder()
  41. .UseConnectionString(dbConfig.Type, dbConfig.CreateDbConnectionString)
  42. .Build();
  43. try
  44. {
  45. Console.WriteLine($"{Environment.NewLine} create database started");
  46. var filePath = Path.Combine(AppContext.BaseDirectory, "Configs/createdbsql.txt").ToPath();
  47. if (File.Exists(filePath))
  48. {
  49. var createDbSql = FileHelper.ReadFile(filePath);
  50. if (createDbSql.NotNull())
  51. {
  52. dbConfig.CreateDbSql = createDbSql;
  53. }
  54. }
  55. await db.Ado.ExecuteNonQueryAsync(dbConfig.CreateDbSql);
  56. Console.WriteLine(" create database succeed");
  57. }
  58. catch (Exception e)
  59. {
  60. Console.WriteLine($" create database failed.\n {e.Message}");
  61. }
  62. }
  63. /// <summary>
  64. /// 获得指定程序集表实体
  65. /// </summary>
  66. /// <param name="assemblyNames"></param>
  67. /// <returns></returns>
  68. public static Type[] GetEntityTypes(string[] assemblyNames)
  69. {
  70. if(!(assemblyNames?.Length > 0))
  71. {
  72. return null;
  73. }
  74. var entityTypes = new List<Type>();
  75. foreach (var assemblyName in assemblyNames)
  76. {
  77. var assembly = Assembly.Load(assemblyName);
  78. foreach (Type type in assembly.GetExportedTypes())
  79. {
  80. foreach (Attribute attribute in type.GetCustomAttributes())
  81. {
  82. if (attribute is TableAttribute tableAttribute)
  83. {
  84. if (tableAttribute.DisableSyncStructure == false)
  85. {
  86. entityTypes.Add(type);
  87. }
  88. }
  89. }
  90. }
  91. }
  92. return entityTypes.ToArray();
  93. }
  94. /// <summary>
  95. /// 配置实体
  96. /// </summary>
  97. /// <param name="db"></param>
  98. /// <param name="appConfig"></param>
  99. /// <param name="dbConfig"></param>
  100. public static void ConfigEntity(IFreeSql db, AppConfig appConfig = null, DbConfig dbConfig = null)
  101. {
  102. //租户生成和操作租户Id
  103. if (!appConfig.Tenant)
  104. {
  105. var iTenant = nameof(ITenant);
  106. var tenantId = nameof(ITenant.TenantId);
  107. //获得指定程序集表实体
  108. var entityTypes = GetEntityTypes(dbConfig.AssemblyNames);
  109. foreach (var entityType in entityTypes)
  110. {
  111. if (entityType.GetInterfaces().Any(a => a.Name == iTenant))
  112. {
  113. db.CodeFirst.Entity(entityType, a =>
  114. {
  115. a.Ignore(tenantId);
  116. });
  117. }
  118. }
  119. }
  120. }
  121. /// <summary>
  122. /// 审计数据
  123. /// </summary>
  124. /// <param name="e"></param>
  125. /// <param name="timeOffset"></param>
  126. /// <param name="user"></param>
  127. public static void AuditValue(AuditValueEventArgs e, TimeSpan timeOffset, IUser user)
  128. {
  129. //数据库时间
  130. if ((e.Column.CsType == typeof(DateTime) || e.Column.CsType == typeof(DateTime?))
  131. && e.Property.GetCustomAttribute<ServerTimeAttribute>(false) != null
  132. && (e.Value == null || (DateTime)e.Value == default || (DateTime?)e.Value == default))
  133. {
  134. e.Value = DateTime.Now.Subtract(timeOffset);
  135. }
  136. //雪花Id
  137. if (e.Column.CsType == typeof(long)
  138. && e.Property.GetCustomAttribute<SnowflakeAttribute>(false) is SnowflakeAttribute snowflakeAttribute
  139. && snowflakeAttribute.Enable && (e.Value == null || (long)e.Value == default || (long?)e.Value == default))
  140. {
  141. e.Value = YitIdHelper.NextId();
  142. }
  143. //有序Guid
  144. if (e.Column.CsType == typeof(Guid)
  145. && e.Property.GetCustomAttribute<OrderGuidAttribute>(false) is OrderGuidAttribute orderGuidAttribute
  146. && orderGuidAttribute.Enable && (e.Value == null || (Guid)e.Value == default || (Guid?)e.Value == default))
  147. {
  148. e.Value = FreeUtil.NewMongodbId();
  149. }
  150. if (user == null || user.Id <= 0)
  151. {
  152. return;
  153. }
  154. if (e.AuditValueType is AuditValueType.Insert or AuditValueType.InsertOrUpdate)
  155. {
  156. switch (e.Property.Name)
  157. {
  158. case "CreatedUserId":
  159. case "OwnerId":
  160. case "MemberId":
  161. if (e.Value == null || (long)e.Value == default || (long?)e.Value == default)
  162. {
  163. e.Value = user.Id;
  164. }
  165. break;
  166. case "CreatedUserName":
  167. if (e.Value == null || ((string)e.Value).IsNull())
  168. {
  169. e.Value = user.UserName;
  170. }
  171. break;
  172. case "OwnerOrgId":
  173. if (e.Value == null || (long)e.Value == default || (long?)e.Value == default)
  174. {
  175. e.Value = user.DataPermission?.OrgId;
  176. }
  177. break;
  178. case "TenantId":
  179. if (e.Value == null || (long)e.Value == default || (long?)e.Value == default)
  180. {
  181. e.Value = user.TenantId;
  182. }
  183. break;
  184. }
  185. }
  186. if (e.AuditValueType is AuditValueType.Update or AuditValueType.InsertOrUpdate)
  187. {
  188. switch (e.Property.Name)
  189. {
  190. case "ModifiedUserId":
  191. e.Value = user.Id;
  192. break;
  193. case "ModifiedUserName":
  194. e.Value = user.UserName;
  195. break;
  196. }
  197. }
  198. }
  199. private static void SyncStructureAfter(object? s, SyncStructureAfterEventArgs e)
  200. {
  201. if (e.Sql.NotNull())
  202. {
  203. Console.WriteLine(" sync structure sql:\n" + e.Sql);
  204. }
  205. }
  206. /// <summary>
  207. /// 同步结构
  208. /// </summary>
  209. public static void SyncStructure(IFreeSql db, string msg = null, DbConfig dbConfig = null, AppConfig appConfig = null)
  210. {
  211. //打印结构比对脚本
  212. //var dDL = db.CodeFirst.GetComparisonDDLStatements<PermissionEntity>();
  213. //Console.WriteLine($"{Environment.NewLine} " + dDL);
  214. //打印结构同步脚本
  215. if(dbConfig.SyncStructureSql)
  216. {
  217. db.Aop.SyncStructureAfter += SyncStructureAfter;
  218. }
  219. // 同步结构
  220. var dbType = dbConfig.Type.ToString();
  221. Console.WriteLine($"{Environment.NewLine} {(msg.NotNull() ? msg : $"sync {dbType} structure")} started");
  222. if (dbConfig.Type == DataType.Oracle)
  223. {
  224. db.CodeFirst.IsSyncStructureToUpper = true;
  225. }
  226. //获得指定程序集表实体
  227. var entityTypes = GetEntityTypes(dbConfig.AssemblyNames);
  228. db.CodeFirst.SyncStructure(entityTypes);
  229. if (dbConfig.SyncStructureSql)
  230. {
  231. db.Aop.SyncStructureAfter -= SyncStructureAfter;
  232. }
  233. Console.WriteLine($" {(msg.NotNull() ? msg : $"sync {dbType} structure")} succeed");
  234. }
  235. private static void SyncDataCurdBefore(object? s, CurdBeforeEventArgs e)
  236. {
  237. if (e.Sql.NotNull())
  238. {
  239. Console.WriteLine($"{e.Sql}{Environment.NewLine}");
  240. }
  241. }
  242. /// <summary>
  243. /// 同步数据审计方法
  244. /// </summary>
  245. /// <param name="s"></param>
  246. /// <param name="e"></param>
  247. private static void SyncDataAuditValue(object s, AuditValueEventArgs e)
  248. {
  249. var user = new { Id = 161223411986501, Name = "admin", TenantId = 161223412138053 };
  250. if (e.Property.GetCustomAttribute<ServerTimeAttribute>(false) != null
  251. && (e.Column.CsType == typeof(DateTime) || e.Column.CsType == typeof(DateTime?))
  252. && (e.Value == null || (DateTime)e.Value == default || (DateTime?)e.Value == default))
  253. {
  254. e.Value = DateTime.Now.Subtract(TimeOffset);
  255. }
  256. if (e.Column.CsType == typeof(long)
  257. && e.Property.GetCustomAttribute<SnowflakeAttribute>(false) != null
  258. && (e.Value == null || (long)e.Value == default || (long?)e.Value == default))
  259. {
  260. e.Value = YitIdHelper.NextId();
  261. }
  262. if (user == null || user.Id <= 0)
  263. {
  264. return;
  265. }
  266. if (e.AuditValueType is AuditValueType.Insert or AuditValueType.InsertOrUpdate)
  267. {
  268. switch (e.Property.Name)
  269. {
  270. case "CreatedUserId":
  271. if (e.Value == null || (long)e.Value == default || (long?)e.Value == default)
  272. {
  273. e.Value = user.Id;
  274. }
  275. break;
  276. case "CreatedUserName":
  277. if (e.Value == null || ((string)e.Value).IsNull())
  278. {
  279. e.Value = user.Name;
  280. }
  281. break;
  282. case "TenantId":
  283. if (e.Value == null || (long)e.Value == default || (long?)e.Value == default)
  284. {
  285. e.Value = user.TenantId;
  286. }
  287. break;
  288. }
  289. }
  290. if (e.AuditValueType is AuditValueType.Update or AuditValueType.InsertOrUpdate)
  291. {
  292. switch (e.Property.Name)
  293. {
  294. case "ModifiedUserId":
  295. e.Value = user.Id;
  296. break;
  297. case "ModifiedUserName":
  298. e.Value = user.Name;
  299. break;
  300. }
  301. }
  302. }
  303. /// <summary>
  304. /// 同步数据
  305. /// </summary>
  306. /// <param name="db"></param>
  307. /// <param name="dbConfig"></param>
  308. /// <param name="appConfig"></param>
  309. /// <returns></returns>
  310. /// <exception cref="Exception"></exception>
  311. public static async Task SyncDataAsync(
  312. IFreeSql db,
  313. DbConfig dbConfig = null,
  314. AppConfig appConfig = null
  315. )
  316. {
  317. try
  318. {
  319. Console.WriteLine($"{Environment.NewLine} sync data started");
  320. if (dbConfig.AssemblyNames?.Length > 0)
  321. {
  322. db.Aop.AuditValue += SyncDataAuditValue;
  323. if (dbConfig.SyncDataCurd)
  324. {
  325. db.Aop.CurdBefore += SyncDataCurdBefore;
  326. }
  327. Assembly[] assemblies = AssemblyHelper.GetAssemblyList(dbConfig.AssemblyNames);
  328. List<ISyncData> syncDatas = assemblies.Select(assembly => assembly.GetTypes()
  329. .Where(x => typeof(ISyncData).GetTypeInfo().IsAssignableFrom(x.GetTypeInfo()) && x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract))
  330. .SelectMany(registerTypes => registerTypes.Select(registerType => (ISyncData)Activator.CreateInstance(registerType))).ToList();
  331. foreach (ISyncData syncData in syncDatas)
  332. {
  333. await syncData.SyncDataAsync(db, dbConfig, appConfig);
  334. }
  335. if (dbConfig.SyncDataCurd)
  336. {
  337. db.Aop.CurdBefore -= SyncDataCurdBefore;
  338. }
  339. db.Aop.AuditValue -= SyncDataAuditValue;
  340. }
  341. Console.WriteLine($" sync data succeed{Environment.NewLine}");
  342. }
  343. catch (Exception ex)
  344. {
  345. throw new Exception($" sync data failed.\n{ex.Message}");
  346. }
  347. }
  348. /// <summary>
  349. /// 生成数据
  350. /// </summary>
  351. /// <param name="db"></param>
  352. /// <param name="appConfig"></param>
  353. /// <param name="dbConfig"></param>
  354. /// <returns></returns>
  355. /// <exception cref="Exception"></exception>
  356. public static async Task GenerateDataAsync(IFreeSql db, AppConfig appConfig = null, DbConfig dbConfig = null)
  357. {
  358. try
  359. {
  360. Console.WriteLine($"{Environment.NewLine} generate data started");
  361. if (dbConfig.AssemblyNames?.Length > 0)
  362. {
  363. Assembly[] assemblies = AssemblyHelper.GetAssemblyList(dbConfig.AssemblyNames);
  364. List<IGenerateData> generateDatas = assemblies.Select(assembly => assembly.GetTypes()
  365. .Where(x => typeof(IGenerateData).GetTypeInfo().IsAssignableFrom(x.GetTypeInfo()) && x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract))
  366. .SelectMany(registerTypes => registerTypes.Select(registerType => (IGenerateData)Activator.CreateInstance(registerType))).ToList();
  367. foreach (IGenerateData generateData in generateDatas)
  368. {
  369. await generateData.GenerateDataAsync(db, appConfig);
  370. }
  371. }
  372. Console.WriteLine($" generate data succeed{Environment.NewLine}");
  373. }
  374. catch (Exception ex)
  375. {
  376. throw new Exception($" generate data failed。\n{ex.Message}{Environment.NewLine}");
  377. }
  378. }
  379. /// <summary>
  380. /// 注册数据库
  381. /// </summary>
  382. /// <param name="freeSqlCloud"></param>
  383. /// <param name="user"></param>
  384. /// <param name="dbConfig"></param>
  385. /// <param name="appConfig"></param>
  386. /// <param name="hostAppOptions"></param>
  387. public static void RegisterDb(
  388. FreeSqlCloud freeSqlCloud,
  389. IUser user,
  390. DbConfig dbConfig,
  391. AppConfig appConfig,
  392. HostAppOptions hostAppOptions
  393. )
  394. {
  395. //注册数据库
  396. var idelTime = dbConfig.IdleTime.HasValue && dbConfig.IdleTime.Value > 0 ? TimeSpan.FromMinutes(dbConfig.IdleTime.Value) : TimeSpan.MaxValue;
  397. freeSqlCloud.Register(dbConfig.Key, () =>
  398. {
  399. //创建数据库
  400. if (dbConfig.CreateDb)
  401. {
  402. CreateDatabaseAsync(dbConfig).Wait();
  403. }
  404. var providerType = dbConfig.ProviderType.NotNull() ? Type.GetType(dbConfig.ProviderType) : null;
  405. var freeSqlBuilder = new FreeSqlBuilder()
  406. .UseConnectionString(dbConfig.Type, dbConfig.ConnectionString, providerType)
  407. .UseAutoSyncStructure(false)
  408. .UseLazyLoading(false)
  409. .UseNoneCommandParameter(true);
  410. if (dbConfig.SlaveList?.Length > 0)
  411. {
  412. var slaveList = dbConfig.SlaveList.Select(a => a.ConnectionString).ToArray();
  413. var slaveWeightList = dbConfig.SlaveList.Select(a => a.Weight).ToArray();
  414. freeSqlBuilder.UseSlave(slaveList).UseSlaveWeight(slaveWeightList);
  415. }
  416. hostAppOptions?.ConfigureFreeSqlBuilder?.Invoke(freeSqlBuilder);
  417. #region 监听所有命令
  418. if (dbConfig.MonitorCommand)
  419. {
  420. freeSqlBuilder.UseMonitorCommand(cmd => { }, (cmd, traceLog) =>
  421. {
  422. //Console.WriteLine($"{cmd.CommandText}\n{traceLog}{Environment.NewLine}");
  423. Console.WriteLine($"{cmd.CommandText}{Environment.NewLine}");
  424. });
  425. }
  426. #endregion 监听所有命令
  427. var fsql = freeSqlBuilder.Build();
  428. //生成数据
  429. if (dbConfig.GenerateData && !dbConfig.CreateDb && !dbConfig.SyncData)
  430. {
  431. GenerateDataAsync(fsql, appConfig, dbConfig).Wait();
  432. }
  433. //软删除过滤器
  434. fsql.GlobalFilter.ApplyOnly<IDelete>(FilterNames.Delete, a => a.IsDeleted == false);
  435. //租户过滤器
  436. if (appConfig.Tenant)
  437. {
  438. fsql.GlobalFilter.ApplyOnly<ITenant>(FilterNames.Tenant, a => a.TenantId == user.TenantId);
  439. }
  440. //会员过滤器
  441. fsql.GlobalFilter.ApplyOnlyIf<IMember>(FilterNames.Member,
  442. () =>
  443. {
  444. if (user?.Id > 0 && user.Type != UserType.Member)
  445. return false;
  446. return true;
  447. },
  448. a => a.MemberId == user.Id
  449. );
  450. //数据权限过滤器
  451. fsql.GlobalFilter.ApplyOnlyIf<IData>(FilterNames.Self,
  452. () =>
  453. {
  454. if (!(user?.Id > 0))
  455. return false;
  456. var dataPermission = user.DataPermission;
  457. if (user.Type == UserType.DefaultUser && dataPermission != null)
  458. return dataPermission.DataScope != DataScope.All && dataPermission.OrgIds.Count == 0;
  459. return false;
  460. },
  461. a => a.OwnerId == user.Id
  462. );
  463. fsql.GlobalFilter.ApplyOnlyIf<IData>(FilterNames.Data,
  464. () =>
  465. {
  466. if (!(user?.Id > 0))
  467. return false;
  468. var dataPermission = user.DataPermission;
  469. if (user.Type == UserType.DefaultUser && dataPermission != null)
  470. return dataPermission.DataScope != DataScope.All && dataPermission.OrgIds.Count > 0;
  471. return false;
  472. },
  473. a => a.OwnerId == user.Id || user.DataPermission.OrgIds.Contains(a.OwnerOrgId.Value)
  474. );
  475. //配置实体
  476. ConfigEntity(fsql, appConfig, dbConfig);
  477. hostAppOptions?.ConfigureFreeSql?.Invoke(fsql);
  478. #region 初始化数据库
  479. //同步结构
  480. if (dbConfig.SyncStructure)
  481. {
  482. SyncStructure(fsql, dbConfig: dbConfig, appConfig: appConfig);
  483. }
  484. #region 审计数据
  485. //计算服务器时间
  486. var serverTime = fsql.Ado.QuerySingle(() => DateTime.UtcNow);
  487. var timeOffset = DateTime.UtcNow.Subtract(serverTime);
  488. TimeOffset = timeOffset;
  489. fsql.Aop.AuditValue += (s, e) =>
  490. {
  491. AuditValue(e, timeOffset, user);
  492. };
  493. #endregion 审计数据
  494. //同步数据
  495. if (dbConfig.SyncData)
  496. {
  497. SyncDataAsync(fsql, dbConfig, appConfig).Wait();
  498. }
  499. #endregion 初始化数据库
  500. #region 监听Curd操作
  501. if (dbConfig.Curd)
  502. {
  503. fsql.Aop.CurdBefore += (s, e) =>
  504. {
  505. if (appConfig.MiniProfiler)
  506. {
  507. MiniProfiler.Current.CustomTiming("CurdBefore", e.Sql);
  508. }
  509. Console.WriteLine($"{e.Sql}{Environment.NewLine}");
  510. };
  511. fsql.Aop.CurdAfter += (s, e) =>
  512. {
  513. if (appConfig.MiniProfiler)
  514. {
  515. MiniProfiler.Current.CustomTiming("CurdAfter", $"{e.ElapsedMilliseconds}");
  516. }
  517. };
  518. }
  519. #endregion 监听Curd操作
  520. return fsql;
  521. }, idelTime);
  522. //执行注册数据库
  523. var fsql = freeSqlCloud.Use(dbConfig.Key);
  524. if (dbConfig.SyncStructure)
  525. {
  526. var _ = fsql.CodeFirst;
  527. }
  528. }
  529. }