diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs
new file mode 100644
index 00000000..0f41cdce
--- /dev/null
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs
@@ -0,0 +1,25 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Contracts.Admin.Infrastructure.Password;
+
+///
+/// Resolves the effective password rule for a given client and validates passwords against it.
+/// A client-level rule (stored on ClientConfig) takes precedence; when absent it falls back to
+/// the global DCC configuration.
+///
+public interface IPasswordRuleProvider
+{
+ ///
+ /// Validate a password against the client's effective rule.
+ /// Returns the localized failure prompt, or null when the password is valid.
+ /// Designed to be called from a FluentValidation rule (see )
+ /// so client-aware password validation stays inside the single, framework-invoked validation pipeline.
+ ///
+ Task GetFailureAsync(string? password, string? clientId);
+
+ ///
+ /// Generate a new password satisfying the global DCC password rule.
+ ///
+ Task GenerateNewPasswordAsync();
+}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordHelper.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordHelper.cs
deleted file mode 100644
index 29d5292c..00000000
--- a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordHelper.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (c) MASA Stack All rights reserved.
-// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
-
-namespace Masa.Auth.Contracts.Admin.Infrastructure.Password;
-
-public class PasswordHelper : ISingletonDependency
-{
- public const string PASSWORDRULECONFIGNAME = "$public.AppSettings:PasswordRule";
- public const string PASSWORDPROMPTCONFIGNAME = "$public.AppSettings:PasswordPrompt";
- public const string DEFAULTPASSWORDRULE = "^\\S*(?=\\S{6,})(?=\\S*\\d)(?=\\S*[A-Za-z])\\S*$";
- public const string DEFAULTPASSWORDPROMPT = "PasswordValidateFailed";
-
- private readonly IMasaConfiguration _masaConfiguration;
- private readonly ILogger _logger;
-
- public PasswordHelper(IMasaConfiguration masaConfiguration, ILogger logger)
- {
- _masaConfiguration = masaConfiguration;
- _logger = logger;
- }
-
- ///
- /// 生成新的密码
- ///
- /// 生成的密码
- public string GenerateNewPassword()
- {
- try
- {
- var (passwordRule, _) = GetPasswordRule();
- var xeger = new Xeger(passwordRule);
- return xeger.Generate();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "生成新密码时发生错误");
- // 如果生成失败,返回默认密码规则生成的密码
- try
- {
- var defaultXeger = new Xeger(DEFAULTPASSWORDRULE);
- return defaultXeger.Generate();
- }
- catch (Exception fallbackEx)
- {
- _logger.LogError(fallbackEx, "使用默认密码规则生成密码时也发生错误");
- throw new InvalidOperationException("无法生成密码", fallbackEx);
- }
- }
- }
-
- ///
- /// 获取密码规则和提示信息
- ///
- /// 密码规则和提示信息的元组
- public (string rule, string prompt) GetPasswordRule()
- {
- var passwordRule = DEFAULTPASSWORDRULE;
- var passwordPrompt = DEFAULTPASSWORDPROMPT;
- if (_masaConfiguration != null)
- {
- try
- {
- passwordRule = _masaConfiguration.ConfigurationApi.GetPublic().GetValue(PASSWORDRULECONFIGNAME, DEFAULTPASSWORDRULE);
- passwordPrompt = _masaConfiguration.ConfigurationApi.GetPublic().GetValue(PASSWORDPROMPTCONFIGNAME, DEFAULTPASSWORDPROMPT);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "获取密码规则配置时发生错误,使用默认配置。配置名称: {PasswordRuleConfigName}, {PasswordPromptConfigName}",
- PASSWORDRULECONFIGNAME, PASSWORDPROMPTCONFIGNAME);
- }
- }
- else
- {
- _logger.LogWarning("MasaConfiguration 为空,使用默认密码规则配置");
- }
- return (passwordRule, passwordPrompt);
- }
-
- ///
- /// 验证密码格式
- ///
- /// 待验证的密码
- /// 验证上下文
- public void ValidatePassword(string? password, ValidationContext context)
- {
- if (password == null)
- {
- password = string.Empty;
- }
-
- try
- {
- var (passwordRule, passwordPrompt) = GetPasswordRule();
- if (!Regex.IsMatch(password, passwordRule))
- {
- _logger.LogWarning("密码验证失败,密码不符合规则。规则: {PasswordRule}", passwordRule);
- var message = DEFAULTPASSWORDRULE.Equals(passwordRule) ? "PasswordFormatVerificationPrompt" : passwordPrompt;
- try
- {
- message = I18n.T(message);
- }
- catch (Exception i18nEx)
- {
- _logger.LogWarning(i18nEx, "获取密码验证失败提示的本地化文本时发生错误,消息键: {MessageKey}", message);
- }
- finally
- {
- context.AddFailure(message);
- }
- }
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "密码验证过程中发生异常");
- context.AddFailure("密码验证过程中发生错误");
- }
- }
-}
\ No newline at end of file
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs
new file mode 100644
index 00000000..e3da97f2
--- /dev/null
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs
@@ -0,0 +1,27 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Contracts.Admin.Infrastructure.Password;
+
+///
+/// Wires into a FluentValidation rule chain. The configured
+/// rule (global DCC default, or a client override) is the single source of truth for what a valid
+/// password looks like - no hardcoded required/length checks are layered on top of it here.
+///
+public static class PasswordRuleValidatorExtensions
+{
+ public static IRuleBuilderOptionsConditions PasswordRule(
+ this IRuleBuilder ruleBuilder,
+ IPasswordRuleProvider passwordRuleProvider,
+ Func clientId)
+ {
+ return ruleBuilder.CustomAsync(async (password, context, cancellation) =>
+ {
+ var failure = await passwordRuleProvider.GetFailureAsync(password, clientId(context.InstanceToValidate));
+ if (failure is not null)
+ {
+ context.AddFailure(failure);
+ }
+ });
+ }
+}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientConfigDto.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientConfigDto.cs
new file mode 100644
index 00000000..1eb5b4ac
--- /dev/null
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientConfigDto.cs
@@ -0,0 +1,30 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Contracts.Admin.Sso;
+
+public class ClientConfigDto
+{
+ ///
+ /// OIDC client_id
+ ///
+ public string ClientId { get; set; } = "";
+
+ ///
+ /// 密码规则(正则表达式);为空表示回退到全局 DCC 配置
+ ///
+ public ClientPasswordRuleDto PasswordRule { get; set; } = new();
+
+ ///
+ /// 密码验证失败提示(i18n key);为空表示回退到全局 DCC 配置
+ ///
+ ///
+ /// 短信场景 × 渠道 × 模板配置
+ ///
+ public List SmsTemplates { get; set; } = new();
+
+ ///
+ /// 邮件场景 × 渠道 × 模板配置
+ ///
+ public List EmailTemplates { get; set; } = new();
+}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientDetailDto.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientDetailDto.cs
index 9eb1c9bf..2dc02026 100644
--- a/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientDetailDto.cs
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientDetailDto.cs
@@ -82,4 +82,24 @@ public class ClientDetailDto : AddClientDto
#region Properties
public List Properties { get; set; } = new();
#endregion
+
+ #region Password & Message Templates
+ ///
+ /// 密码规则(正则表达式);为空表示回退到全局 DCC 配置
+ ///
+ public ClientPasswordRuleDto PasswordRule { get; set; } = new();
+
+ ///
+ /// 密码验证失败提示(i18n key);为空表示回退到全局 DCC 配置
+ ///
+ ///
+ /// 短信场景 × 渠道 × 模板配置
+ ///
+ public List SmsTemplates { get; set; } = new();
+
+ ///
+ /// 邮件场景 × 渠道 × 模板配置
+ ///
+ public List EmailTemplates { get; set; } = new();
+ #endregion
}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientEmailTemplateDto.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientEmailTemplateDto.cs
new file mode 100644
index 00000000..4d2604d6
--- /dev/null
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientEmailTemplateDto.cs
@@ -0,0 +1,22 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Contracts.Admin.Sso;
+
+///
+/// Client 级邮件模板配置:一个发送场景对应一个渠道 + 模板
+///
+public class ClientEmailTemplateDto
+{
+ public SendEmailTypes Scene { get; set; }
+
+ ///
+ /// MC 渠道编码
+ ///
+ public string ChannelCode { get; set; } = "";
+
+ ///
+ /// MC 模板编码
+ ///
+ public string TemplateCode { get; set; } = "";
+}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientPasswordRuleDto.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientPasswordRuleDto.cs
new file mode 100644
index 00000000..ac29715e
--- /dev/null
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientPasswordRuleDto.cs
@@ -0,0 +1,25 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Contracts.Admin.Sso;
+
+public class ClientPasswordRuleDto
+{
+ public bool UseRegexPattern { get; set; }
+
+ public int MinLength { get; set; } = 6;
+
+ public int MaxLength { get; set; } = 20;
+
+ public bool RequireUppercase { get; set; }
+
+ public bool RequireLowercase { get; set; }
+
+ public bool RequireDigit { get; set; }
+
+ public bool RequireSpecialCharacter { get; set; }
+
+ public string RegexPattern { get; set; } = string.Empty;
+
+ public string? PasswordPrompt { get; set; }
+}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientSmsTemplateDto.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientSmsTemplateDto.cs
new file mode 100644
index 00000000..be6576be
--- /dev/null
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Sso/ClientSmsTemplateDto.cs
@@ -0,0 +1,22 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Contracts.Admin.Sso;
+
+///
+/// Client 级短信模板配置:一个发送场景对应一个渠道 + 模板
+///
+public class ClientSmsTemplateDto
+{
+ public SendMsgCodeTypes Scene { get; set; }
+
+ ///
+ /// MC 渠道编码
+ ///
+ public string ChannelCode { get; set; } = "";
+
+ ///
+ /// MC 模板编码
+ ///
+ public string TemplateCode { get; set; } = "";
+}
diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Subjects/Validator/PasswordValidator.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Subjects/Validator/PasswordValidator.cs
index 643dab0f..4e429518 100644
--- a/src/Contracts/Masa.Auth.Contracts.Admin/Subjects/Validator/PasswordValidator.cs
+++ b/src/Contracts/Masa.Auth.Contracts.Admin/Subjects/Validator/PasswordValidator.cs
@@ -5,8 +5,16 @@ namespace Masa.Auth.Contracts.Admin.Subjects.Validator;
public class PasswordValidator : MasaAbstractValidator
{
- public PasswordValidator(PasswordHelper passwordHelper)
+ public PasswordValidator(IPasswordRuleProvider passwordRuleProvider)
{
- RuleFor(password => password).Required().MaximumLength(50).Custom(passwordHelper.ValidatePassword);
+ RuleFor(password => password).Required()
+ .CustomAsync(async (password, context, cancellation) =>
+ {
+ var failure = await passwordRuleProvider.GetFailureAsync(password, null);
+ if (failure is not null)
+ {
+ context.AddFailure(failure);
+ }
+ });
}
}
diff --git a/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientConfig.cs b/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientConfig.cs
new file mode 100644
index 00000000..1db4c775
--- /dev/null
+++ b/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientConfig.cs
@@ -0,0 +1,46 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Domain.Sso.Aggregates;
+
+///
+/// Per-client business configuration: password rule and the message template
+/// matrix (scene x channel x template). Keyed by the OIDC client_id (string).
+/// A missing value means "fall back to the global DCC configuration".
+///
+public class ClientConfig : FullAggregateRoot
+{
+ private List _messageTemplates = new();
+
+ public string ClientId { get; private set; } = "";
+
+ public string? PasswordRule { get; private set; }
+
+ public string? PasswordPrompt { get; private set; }
+
+ public ClientPasswordRuleConfig? PasswordRuleConfig { get; private set; }
+
+ public IReadOnlyCollection MessageTemplates => _messageTemplates;
+
+ private ClientConfig() { }
+
+ public ClientConfig(string clientId)
+ {
+ ClientId = clientId;
+ }
+
+ public void UpdatePasswordRule(string? passwordRule, string? passwordPrompt, ClientPasswordRuleConfig? passwordRuleConfig)
+ {
+ PasswordRule = passwordRule;
+ PasswordPrompt = passwordPrompt;
+ PasswordRuleConfig = passwordRuleConfig;
+ }
+
+ ///
+ /// Replace the whole message template matrix.
+ ///
+ public void SetMessageTemplates(IEnumerable messageTemplates)
+ {
+ _messageTemplates = messageTemplates.ToList();
+ }
+}
\ No newline at end of file
diff --git a/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientMessageTemplate.cs b/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientMessageTemplate.cs
new file mode 100644
index 00000000..7d724537
--- /dev/null
+++ b/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientMessageTemplate.cs
@@ -0,0 +1,32 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+using Masa.BuildingBlocks.StackSdks.Mc.Enum;
+
+namespace Masa.Auth.Domain.Sso.Aggregates;
+
+///
+/// Client-level message template binding for a single (channel type, scene).
+/// Scene stores the value of SendMsgCodeTypes (when ChannelType is Sms) or
+/// SendEmailTypes (when ChannelType is Email); ChannelType disambiguates the two.
+///
+public class ClientMessageTemplate : Entity
+{
+ public int ClientConfigId { get; private set; }
+
+ public ChannelTypes ChannelType { get; private set; }
+
+ public int Scene { get; private set; }
+
+ public string ChannelCode { get; private set; } = "";
+
+ public string TemplateCode { get; private set; } = "";
+
+ public ClientMessageTemplate(ChannelTypes channelType, int scene, string channelCode, string templateCode)
+ {
+ ChannelType = channelType;
+ Scene = scene;
+ ChannelCode = channelCode;
+ TemplateCode = templateCode;
+ }
+}
diff --git a/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientPasswordRuleConfig.cs b/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientPasswordRuleConfig.cs
new file mode 100644
index 00000000..b996bc52
--- /dev/null
+++ b/src/Domain/Masa.Auth.Domain/Sso/Aggregates/ClientPasswordRuleConfig.cs
@@ -0,0 +1,41 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Domain.Sso.Aggregates;
+
+public class ClientPasswordRuleConfig
+{
+ public int MinLength { get; set; } = 6;
+
+ public int MaxLength { get; set; } = 32;
+
+ public bool RequireUppercase { get; set; }
+
+ public bool RequireLowercase { get; set; }
+
+ public bool RequireDigit { get; set; }
+
+ public bool RequireSpecialCharacter { get; set; }
+
+ public bool UseRegexPattern { get; set; }
+
+ public string RegexPattern { get; set; } = string.Empty;
+
+ public string? PasswordPrompt { get; set; }
+
+ public ClientPasswordRuleConfig Clone()
+ {
+ return new ClientPasswordRuleConfig
+ {
+ MinLength = MinLength,
+ MaxLength = MaxLength,
+ RequireUppercase = RequireUppercase,
+ RequireLowercase = RequireLowercase,
+ RequireDigit = RequireDigit,
+ RequireSpecialCharacter = RequireSpecialCharacter,
+ UseRegexPattern = UseRegexPattern,
+ RegexPattern = RegexPattern,
+ PasswordPrompt = PasswordPrompt
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Domain/Masa.Auth.Domain/Sso/Repositories/IClientConfigRepository.cs b/src/Domain/Masa.Auth.Domain/Sso/Repositories/IClientConfigRepository.cs
new file mode 100644
index 00000000..bac37056
--- /dev/null
+++ b/src/Domain/Masa.Auth.Domain/Sso/Repositories/IClientConfigRepository.cs
@@ -0,0 +1,9 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+namespace Masa.Auth.Domain.Sso.Repositories;
+
+public interface IClientConfigRepository : IRepository
+{
+ Task FindByClientIdAsync(string clientId);
+}
diff --git a/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/EntityConfigurations/Sso/ClientConfigEntityTypeConfiguration.cs b/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/EntityConfigurations/Sso/ClientConfigEntityTypeConfiguration.cs
new file mode 100644
index 00000000..e4b187f3
--- /dev/null
+++ b/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/EntityConfigurations/Sso/ClientConfigEntityTypeConfiguration.cs
@@ -0,0 +1,45 @@
+// Copyright (c) MASA Stack All rights reserved.
+// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
+
+using System.Text.Json;
+using Masa.BuildingBlocks.StackSdks.Mc.Enum;
+using Microsoft.EntityFrameworkCore.ChangeTracking;
+
+namespace Masa.Auth.EntityFrameworkCore.PostgreSql.EntityConfigurations.Sso;
+
+public class ClientConfigEntityTypeConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasKey(x => x.Id);
+ builder.Property(x => x.ClientId).HasMaxLength(200).IsRequired();
+ // Soft delete (IsDeleted) leaves the row in place, so the unique index must exclude
+ // deleted rows - otherwise a client can never be reconfigured after its config is removed.
+ builder.HasIndex(x => x.ClientId).IsUnique().HasFilter("NOT \"IsDeleted\"");
+ builder.Property(x => x.PasswordRule).HasMaxLength(500);
+ builder.Property(x => x.PasswordPrompt).HasMaxLength(500);
+ builder.Property(x => x.PasswordRuleConfig)
+ .HasConversion(
+ v => v == null ? null : JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
+ v => string.IsNullOrWhiteSpace(v)
+ ? null
+ : JsonSerializer.Deserialize(v, (JsonSerializerOptions?)null))
+ .Metadata.SetValueComparer(new ValueComparer(
+ (l, r) => JsonSerializer.Serialize(l, (JsonSerializerOptions?)null) == JsonSerializer.Serialize(r, (JsonSerializerOptions?)null),
+ v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null).GetHashCode(),
+ v => v == null ? null : v.Clone()));
+ builder.Property(x => x.PasswordRuleConfig).HasMaxLength(2000);
+ builder.HasMany(x => x.MessageTemplates).WithOne().HasForeignKey(x => x.ClientConfigId);
+ }
+}
+
+public class ClientMessageTemplateEntityTypeConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasKey(x => x.Id);
+ builder.Property(x => x.ChannelCode).HasMaxLength(200).IsRequired();
+ builder.Property(x => x.TemplateCode).HasMaxLength(200).IsRequired();
+ builder.HasIndex(x => new { x.ClientConfigId, x.ChannelType, x.Scene }).IsUnique();
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Masa.Auth.EntityFrameworkCore.PostgreSql.csproj b/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Masa.Auth.EntityFrameworkCore.PostgreSql.csproj
index 6f861aaa..2f76bfb1 100644
--- a/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Masa.Auth.EntityFrameworkCore.PostgreSql.csproj
+++ b/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Masa.Auth.EntityFrameworkCore.PostgreSql.csproj
@@ -10,7 +10,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Migrations/20260703082647_AddClientConfig.Designer.cs b/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Migrations/20260703082647_AddClientConfig.Designer.cs
new file mode 100644
index 00000000..df279a3e
--- /dev/null
+++ b/src/Infrastructure/Masa.Auth.EntityFrameworkCore.PostgreSql/Migrations/20260703082647_AddClientConfig.Designer.cs
@@ -0,0 +1,3474 @@
+//
+using System;
+using Masa.Auth.Domain.Subjects.Aggregates;
+using Masa.Auth.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Masa.Auth.EntityFrameworkCore.PostgreSql.Migrations
+{
+ [DbContext(typeof(AuthDbContext))]
+ [Migration("20260703082647_AddClientConfig")]
+ partial class AddClientConfig
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("auth")
+ .HasAnnotation("ProductVersion", "10.0.9")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("DeviceFlowCodes", b =>
+ {
+ b.Property("UserCode")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Data")
+ .IsRequired()
+ .HasMaxLength(50000)
+ .HasColumnType("character varying(50000)");
+
+ b.Property("Description")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("DeviceCode")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("Expiration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("SessionId")
+ .HasMaxLength(100)
+ .HasColumnType("character varying(100)");
+
+ b.Property("SubjectId")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.HasKey("UserCode");
+
+ b.HasIndex("DeviceCode")
+ .IsUnique()
+ .HasFilter("NOT \"IsDeleted\"");
+
+ b.HasIndex("Expiration");
+
+ b.ToTable("DeviceFlowCodes", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.DynamicRoles.Aggregates.DynamicRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("character varying(512)");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DynamicRole", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.GlobalNavs.Aggregates.GlobalNavVisible", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AppId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Visible")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AppId");
+
+ b.HasIndex("ClientId");
+
+ b.ToTable("GlobalNavVisible", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Logs.Aggregates.OperationLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ClientId")
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("OperationDescription")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OperationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("OperationType")
+ .HasColumnType("integer");
+
+ b.Property("Operator")
+ .HasColumnType("uuid");
+
+ b.Property("OperatorName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId");
+
+ b.HasIndex("OperationDescription");
+
+ b.HasIndex("OperationTime");
+
+ b.HasIndex("OperationType");
+
+ b.HasIndex("Operator");
+
+ b.ToTable("OperationLog", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Organizations.Aggregates.Department", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Level")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasDefaultValue(1);
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("ParentId")
+ .HasColumnType("uuid");
+
+ b.Property("Sort")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Level");
+
+ b.HasIndex("Name", "ParentId");
+
+ b.ToTable("Department", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Organizations.Aggregates.DepartmentStaff", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("DepartmentId")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("StaffId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DepartmentId");
+
+ b.HasIndex("StaffId");
+
+ b.ToTable("DepartmentStaff", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Organizations.Aggregates.Position", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name");
+
+ b.ToTable("Position", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Permissions.Aggregates.Permission", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AppId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Description")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("Icon")
+ .HasColumnType("text");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Legend")
+ .HasColumnType("text");
+
+ b.Property("MatchPattern")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(40)
+ .HasColumnType("character varying(40)");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("ParentId")
+ .HasColumnType("uuid");
+
+ b.Property("SystemId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Url")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentId");
+
+ b.HasIndex("SystemId", "AppId", "Code");
+
+ b.ToTable("Permission", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Permissions.Aggregates.PermissionRelation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AffiliationPermissionId")
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("LeadingPermissionId")
+ .HasColumnType("uuid");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AffiliationPermissionId");
+
+ b.HasIndex("LeadingPermissionId", "AffiliationPermissionId");
+
+ b.ToTable("PermissionRelation", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Permissions.Aggregates.Role", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AvailableQuantity")
+ .HasColumnType("integer");
+
+ b.Property("Code")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("Limit")
+ .HasColumnType("integer");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Type")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasDefaultValue(2);
+
+ b.HasKey("Id");
+
+ b.ToTable("Role", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Permissions.Aggregates.RoleClient", b =>
+ {
+ b.Property("RoleId")
+ .HasColumnType("uuid");
+
+ b.Property("ClientId")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.HasKey("RoleId", "ClientId");
+
+ b.ToTable("RoleClient", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Permissions.Aggregates.RoleRelation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("ParentId")
+ .HasColumnType("uuid");
+
+ b.Property("RoleId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("RoleRelation", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Permissions.Aggregates.SubjectPermissionRelation", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Effect")
+ .HasColumnType("boolean");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("PermissionId")
+ .HasColumnType("uuid");
+
+ b.Property("_businessType")
+ .IsRequired()
+ .HasMaxLength(34)
+ .HasColumnType("character varying(34)");
+
+ b.HasKey("Id");
+
+ b.ToTable("SubjectPermissionRelation", "auth");
+
+ b.HasDiscriminator("_businessType").HasValue("SubjectPermissionRelation");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Sso.Aggregates.ClientConfig", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("PasswordPrompt")
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property("PasswordRule")
+ .HasMaxLength(500)
+ .HasColumnType("character varying(500)");
+
+ b.Property("PasswordRuleConfig")
+ .HasMaxLength(2000)
+ .HasColumnType("character varying(2000)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientId")
+ .IsUnique()
+ .HasFilter("NOT \"IsDeleted\"");
+
+ b.ToTable("ClientConfig", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Sso.Aggregates.ClientMessageTemplate", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChannelCode")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("ChannelType")
+ .HasColumnType("integer");
+
+ b.Property("ClientConfigId")
+ .HasColumnType("integer");
+
+ b.Property("Scene")
+ .HasColumnType("integer");
+
+ b.Property("TemplateCode")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClientConfigId", "ChannelType", "Scene")
+ .IsUnique();
+
+ b.ToTable("ClientMessageTemplate", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Sso.Aggregates.CustomLogin", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("character varying(200)");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Creator");
+
+ b.HasIndex("Modifier");
+
+ b.HasIndex("Name");
+
+ b.ToTable("CustomLogin", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Sso.Aggregates.CustomLoginThirdPartyIdp", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CustomLoginId")
+ .HasColumnType("integer");
+
+ b.Property("Sort")
+ .HasColumnType("integer");
+
+ b.Property("ThirdPartyIdpId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CustomLoginId");
+
+ b.HasIndex("ThirdPartyIdpId");
+
+ b.ToTable("CustomLoginThirdPartyIdp", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Sso.Aggregates.RegisterField", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CustomLoginId")
+ .HasColumnType("integer");
+
+ b.Property("RegisterFieldType")
+ .HasColumnType("integer");
+
+ b.Property("Required")
+ .HasColumnType("boolean");
+
+ b.Property("Sort")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CustomLoginId");
+
+ b.ToTable("RegisterField", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Sso.Aggregates.UserClaimExtend", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("DataSourceType")
+ .HasColumnType("integer");
+
+ b.Property("DataSourceValue")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ItemText")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ItemValue")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UserClaimId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.ToTable("UserClaimExtend", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Subjects.Aggregates.IdentityProvider", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasMaxLength(21)
+ .HasColumnType("character varying(21)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("Icon")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("ThirdPartyIdpType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name");
+
+ b.ToTable("IdentityProvider", "auth");
+
+ b.HasDiscriminator("Discriminator").HasValue("IdentityProvider");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Subjects.Aggregates.Staff", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("Avatar")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CompanyName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("CurrentTeamId")
+ .HasColumnType("uuid");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Enabled")
+ .HasColumnType("boolean");
+
+ b.Property("Gender")
+ .HasColumnType("integer");
+
+ b.Property("IdCard")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("JobNumber")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PhoneNumber")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PositionId")
+ .HasColumnType("uuid");
+
+ b.Property("StaffType")
+ .HasColumnType("integer");
+
+ b.Property("UserId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CurrentTeamId");
+
+ b.HasIndex("JobNumber");
+
+ b.HasIndex("PositionId");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("Staff", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Subjects.Aggregates.Team", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("character varying(20)");
+
+ b.Property("TeamType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name");
+
+ b.ToTable("Team", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Subjects.Aggregates.TeamRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Modifier")
+ .HasColumnType("uuid");
+
+ b.Property("RoleId")
+ .HasColumnType("uuid");
+
+ b.Property("TeamId")
+ .HasColumnType("uuid");
+
+ b.Property("TeamMemberType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.HasIndex("TeamId");
+
+ b.ToTable("TeamRole", "auth");
+ });
+
+ modelBuilder.Entity("Masa.Auth.Domain.Subjects.Aggregates.TeamStaff", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Creator")
+ .HasColumnType("uuid");
+
+ b.Property("IsDeleted")
+ .HasColumnType("boolean");
+
+ b.Property("ModificationTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property