TenantContextGuard.cs
3.6 KB
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
using Volo.Abp;
using Volo.Abp.MultiTenancy;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Helpers;
/// <summary>
/// 独立库多租户下,业务 API 须已解析到租户上下文
/// </summary>
public static class TenantContextGuard
{
/// <summary>
/// 平台主库登录(JWT/__tenant 均无业务租户)时访问 fl_* 等业务表的友好提示。
/// </summary>
public const string PlatformCannotAccessBusinessDataMessage =
"当前为平台主库登录,无法访问公司业务数据(标签/产品/成员等)。请选择具体公司登录,或使用平台「公司管理」相关接口。";
public static void EnsureTenantResolved(ICurrentTenant currentTenant, string? operation = null)
{
if (currentTenant.Id.HasValue && currentTenant.Id.Value != Guid.Empty)
{
return;
}
var hint = string.IsNullOrWhiteSpace(operation)
? "未识别租户上下文"
: $"{operation}:未识别租户上下文";
throw new UserFriendlyException(
$"{hint}。请使用泰额登录接口(th-web-auth / th-app-auth)选择具体公司登录,或请求头 __tenant 携带租户 Id。");
}
/// <summary>
/// 泰额 SaaS 多租户开启时,业务表(fl_* / location 等)必须走租户库,禁止落到 host。
/// </summary>
public static void EnsureBusinessTenantIfSaas(
ICurrentTenant currentTenant,
DbConnOptions? dbConnOptions,
string? operation = null)
{
if (dbConnOptions is null || !dbConnOptions.EnabledSaasMultiTenancy)
{
return;
}
EnsureTenantResolved(currentTenant, operation);
}
/// <summary>
/// 判断当前 DbContext 是否连到平台主库(antis-foodlabeling-host)。
/// </summary>
public static bool IsConnectedToHostDatabase(ISqlSugarDbContext dbContext, DbConnOptions dbConnOptions)
{
var dbName = dbContext.SqlSugarClient.Ado.Connection.Database;
var hostDbName = TryExtractDatabaseName(dbConnOptions.Url);
return !string.IsNullOrWhiteSpace(hostDbName)
&& string.Equals(dbName, hostDbName, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// SaaS 模式下校验 DbContext 未落到 host 主库(双保险,避免缺表 500)。
/// </summary>
public static void EnsureNotHostDatabaseIfSaas(
ISqlSugarDbContext dbContext,
DbConnOptions dbConnOptions,
string? operation = null)
{
if (!dbConnOptions.EnabledSaasMultiTenancy)
{
return;
}
if (!IsConnectedToHostDatabase(dbContext, dbConnOptions))
{
return;
}
var hint = string.IsNullOrWhiteSpace(operation)
? PlatformCannotAccessBusinessDataMessage
: $"{operation}:{PlatformCannotAccessBusinessDataMessage}";
throw new UserFriendlyException(hint);
}
private static string? TryExtractDatabaseName(string? connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
return null;
}
foreach (var part in connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries))
{
var kv = part.Split('=', 2, StringSplitOptions.TrimEntries);
if (kv.Length == 2
&& (kv[0].Equals("database", StringComparison.OrdinalIgnoreCase)
|| kv[0].Equals("Database", StringComparison.OrdinalIgnoreCase)))
{
return kv[1].Trim();
}
}
return null;
}
}