Blame view

泰额版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling/FoodLabeling.Th.Application/MultiTenancy/TenantBusinessDatabaseAccessor.cs 3.7 KB
e41dfa09   李曜臣   泰鄂版菜单;租户列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  using FoodLabeling.Th.Application.Contracts.Options;
  using SqlSugar;
  using Volo.Abp;
  using Yi.Framework.TenantManagement.Domain;
  
  namespace FoodLabeling.Th.Application.MultiTenancy;
  
  /// <summary>
  /// 跨租户操作业务库:解析/回填连接串并用独立 SqlSugarClient 直连,避免 CurrentTenant.Change 解析空连接串。
  /// </summary>
  public static class TenantBusinessDatabaseAccessor
  {
      private const string InitializeTenantDatabaseHintTemplate =
          "请先调用 POST /api/app/th-tenant-provisioning/initialize-tenant-database/{0} 初始化业务库";
  
      /// <summary>
      /// 解析租户业务库连接串;为空时按配置回填并写回主库 YiTenant
      /// </summary>
      public static async Task<(string ConnectionString, DbType DbType)> ResolveConnectionAsync(
          TenantAggregateRoot tenant,
          FoodLabelingThTenantDatabaseOptions dbOptions,
          Func<TenantAggregateRoot, Task> persistTenantAsync)
      {
          var connectionString = tenant.TenantConnectionString?.Trim();
          if (!string.IsNullOrWhiteSpace(connectionString))
          {
              return (connectionString, ResolveDbType(tenant.DbType));
          }
  
          connectionString = TenantDatabaseConnectionStringBuilder.BuildMySqlConnectionString(
              dbOptions,
              tenant.Name);
  
          if (string.IsNullOrWhiteSpace(connectionString))
          {
              throw BuildMissingConnectionStringException(tenant.Name);
          }
  
          var dbType = DbType.MySql;
          tenant.SetConnectionString(dbType, connectionString);
          await persistTenantAsync(tenant);
  
          return (connectionString, dbType);
      }
  
      /// <summary>
      /// 创建直连租户业务库的 SqlSugar 客户端(调用方 using 释放)。
      /// </summary>
      public static SqlSugarClient CreateClient(string connectionString, DbType dbType)
      {
          if (string.IsNullOrWhiteSpace(connectionString))
          {
              throw new UserFriendlyException("租户业务库连接串不能为空");
          }
  
          return new SqlSugarClient(new ConnectionConfig
          {
              ConfigId = $"th-tenant-direct-{Guid.NewGuid():N}",
              DbType = dbType,
              ConnectionString = connectionString,
              IsAutoCloseConnection = true
          });
      }
  
      /// <summary>
      /// 探测业务库是否可连;库不存在时给出初始化引导。
      /// </summary>
      public static void EnsureDatabaseReachable(ISqlSugarClient db, Guid tenantId)
      {
          try
          {
              db.Ado.ExecuteCommand("SELECT 1");
          }
          catch (Exception ex) when (IsDatabaseNotExistError(ex))
          {
              throw new UserFriendlyException(
                  string.Format(InitializeTenantDatabaseHintTemplate, tenantId));
          }
      }
  
      public static UserFriendlyException BuildMissingConnectionStringException(string tenantName)
      {
          return new UserFriendlyException(
              $"租户「{tenantName}」未配置业务库连接串,请先在租户管理完善连接串或调用开通/初始化接口");
      }
  
      private static DbType ResolveDbType(DbType tenantDbType)
      {
          return tenantDbType == default ? DbType.MySql : tenantDbType;
      }
  
      private static bool IsDatabaseNotExistError(Exception ex)
      {
          var message = ex.Message;
          var current = ex;
          while (current.InnerException != null)
          {
              current = current.InnerException;
              message += " " + current.Message;
          }
  
          return message.Contains("Unknown database", StringComparison.OrdinalIgnoreCase)
                 || message.Contains("doesn't exist", StringComparison.OrdinalIgnoreCase)
                 || message.Contains("1049", StringComparison.Ordinal);
      }
  }