1.什么是SaaS
SaaS系统,软件即服务,租户系统,相对于传统软件,SaaS不需要租户购买服务器,不需要安装,也不需要花费大量的时间和成本去维护,只需要购买使用权,租用即可
2.首先要有租户表
/// <summary>
/// 租户
/// </summary>
public class SysTenant
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <summary>
/// 名字
/// </summary>
[MaxLength(50)]
public string Name { get; set; }
/// <summary>
/// 是否删除
/// </summary>
[DefaultValue(false)]
public bool IsDeleted { get; set; }
/// <summary>
/// 添加时间
/// </summary>
public DateTime CreateDate { get; set; } = DateTime.Now;
/// <summary>
/// 编码
/// </summary>
[Required]
[MaxLength(20)]
public string Code { get; set; }
/// <summary>
/// 电话
/// </summary>
[Required]
[MaxLength(20)]
public string Phone { get; set; }
/// <summary>
/// 租户类型(共享租户、独立租户)
/// </summary>
[Required]
public ETenantType TenantType { get; set; }
/// <summary>
/// 所属区域
/// </summary>
[MaxLength(20)]
public string Area { get; set; }
/// <summary>
/// 租户状态(待审核、已审核、启用、禁用、取消)
/// </summary>
[Required]
public ETenantState TenantState { get; set; }
/// <summary>
/// Host
/// </summary>
[MaxLength(200)]
public string Host { get; set; }
/// <summary>
/// 备注
/// </summary>
[MaxLength(200)]
public string Remark { get; set; }
}
租户类型(共享租户、独立租户)
/// <summary>
/// 租户类型(共享租户、独立租户)
/// </summary>
public enum ETenantType
{
/// <summary>
/// 共享租户
/// </summary>
[Description("共享租户")]
Share =1,
/// <summary>
/// 独立租户
/// </summary>
[Description("独立租户")]
Independent =2
}
租户状态(待审核、已审核、启用、禁用、取消)
/// <summary>
/// 租户状态(待审核、已审核、启用、禁用、取消)
/// </summary>
public enum ETenantState
{
/// <summary>
/// 待审核
/// </summary>
[Description("待审核")]
WaitExamine = 1,
/// <summary>
/// 已审核
/// </summary>
[Description("已审核")]
ExamineFinish = 2,
/// <summary>
/// 启用
/// </summary>
[Description("启用")]
Enable = 3,
/// <summary>
/// 禁用
/// </summary>
[Description("禁用")]
Disable = 4,
/// <summary>
/// 取消
/// </summary>
[Description("取消")]
Cancel = 5
}
3.获取请求的租户Id,Host
public class TenantProvider : ITenantProvider
{
private readonly IHttpContextAccessor _accessor;
public TenantProvider(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public string GetTenantId()
{
if (_accessor.HttpContext.User.FindFirst("TenantId") != null)
{
return _accessor.HttpContext.User.FindFirst("TenantId").Value;
}
else
{
return "";
}
//return "localhost:5001";
}
public string GetHost()
{
return _accessor.HttpContext.Request.Host.ToString();
//return "localhost:5001";
}
}
4.DBContext调用,并进行租户过滤
public class JKCRMDBContext : IdentityDbContext<Admins>
{
private readonly ITenantProvider TenantProvider;
internal string TenantId => TenantProvider.GetTenantId();
public JKCRMDBContext(DbContextOptions<JKCRMDBContext> options, ITenantProvider tenantProvider) : base(options)
{
TenantProvider = tenantProvider;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region 租户过滤器
string tenantId = "******";
//根据登录Token查询租户Id
if (!string.IsNullOrEmpty(TenantProvider.GetTenantId()))
{
tenantId = TenantProvider.GetTenantId();
}
else
{
#region 查询租户信息
//根据Host查询租户Id
MySqlParameter parameter = new MySqlParameter("@Host", MySqlDbType.String);
var originalhost = TenantProvider.GetHost();//localhost:5001
var host = originalhost;
if (originalhost == "localhost:8080")
{
host = "localhost:5001";
}
parameter.Value = host;
var MySqlDataReader = DbHelperMySQL.ExecuteReader("select * from SysTenant where Host=@Host", parameter);
while (MySqlDataReader.Read()) //开始读数据
{
//对数据进行处理
tenantId = MySqlDataReader["Id"].ToString();
}
MySqlDataReader.Close();
#region 写入一条记录
//根据Host查询租户Id
MySqlParameter[] mySqlParameters = new MySqlParameter[]
{
new MySqlParameter("@Name", "用户登录记录Host"),
new MySqlParameter("@CreateDate", DateTime.Now),
new MySqlParameter("@OperationType", 1007),
new MySqlParameter("@OperationParameters", "OriginalHost:"+originalhost+",Host:"+host),
new MySqlParameter("@OperationInfo", "TenantId:"+tenantId)
};
var MySqlInsert = DbHelperMySQL.ExecuteSql("insert into OperationRecords (Name,CreateDate,OperationType,OperationParameters,OperationInfo) values (@Name,@CreateDate,@OperationType,@OperationParameters,@OperationInfo)", mySqlParameters);
#endregion
if (string.IsNullOrEmpty(tenantId) || tenantId == "******")
{
throw new Exception("所属机构不存在,Host:"+ host);
}
#endregion
}
//开始过滤
modelBuilder.Entity<Customers>().HasQueryFilter(x => x.SysTenantId == Convert.ToInt32(tenantId));
modelBuilder.Entity<ManageMenu>().HasQueryFilter(x => x.SysTenantId == Convert.ToInt32(tenantId));
modelBuilder.Entity<ManageMenuButton>().HasQueryFilter(x => x.SysTenantId == Convert.ToInt32(tenantId));
#endregion
}
}
5.Startup的ConfigureServices
services.AddScoped<ITenantProvider, TenantProvider>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
6.每个涉及到业务的表都需要跟租户表加主外键关系
#region 关系
/// <summary>
/// 租户Id
/// </summary>
[Description(@"租户Id")]
public int? SysTenantId { get; set; }
[ForeignKey("SysTenantId")]
public virtual SysTenant SysTenant { get; set; }
#endregion