84 changed files with 2027 additions and 209 deletions
@ -0,0 +1,21 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EntityFrameworkCore |
|||
{ |
|||
public class MessageServiceHostMigrationsDbContext : AbpDbContext<MessageServiceHostMigrationsDbContext> |
|||
{ |
|||
public MessageServiceHostMigrationsDbContext(DbContextOptions<MessageServiceHostMigrationsDbContext> options) |
|||
: base(options) |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder modelBuilder) |
|||
{ |
|||
base.OnModelCreating(modelBuilder); |
|||
|
|||
modelBuilder.ConfigureMessageService(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System.IO; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Design; |
|||
using Microsoft.Extensions.Configuration; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EntityFrameworkCore |
|||
{ |
|||
public class MessageServiceHostMigrationsDbContextFactory : IDesignTimeDbContextFactory<MessageServiceHostMigrationsDbContext> |
|||
{ |
|||
public MessageServiceHostMigrationsDbContext CreateDbContext(string[] args) |
|||
{ |
|||
var configuration = BuildConfiguration(); |
|||
|
|||
var builder = new DbContextOptionsBuilder<MessageServiceHostMigrationsDbContext>() |
|||
.UseMySql(configuration.GetConnectionString("Default")); |
|||
|
|||
return new MessageServiceHostMigrationsDbContext(builder.Options); |
|||
} |
|||
|
|||
private static IConfigurationRoot BuildConfiguration() |
|||
{ |
|||
var builder = new ConfigurationBuilder() |
|||
.SetBasePath(Directory.GetCurrentDirectory()) |
|||
.AddJsonFile("appsettings.Development.json", optional: false); |
|||
|
|||
return builder.Build(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace>LINGYUN.Abp.MessageService</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3"> |
|||
<PrivateAssets>all</PrivateAssets> |
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> |
|||
</PackageReference> |
|||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.3" /> |
|||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="3.1.3" /> |
|||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> |
|||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" /> |
|||
<PackageReference Include="Serilog.Enrichers.Assembly" Version="2.0.0" /> |
|||
<PackageReference Include="Serilog.Enrichers.Process" Version="2.0.1" /> |
|||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" /> |
|||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" /> |
|||
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> |
|||
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.4.1" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.Autofac" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.EntityFrameworkCore.MySQL" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.TenantManagement.EntityFrameworkCore" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.SettingManagement.EntityFrameworkCore" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.PermissionManagement.EntityFrameworkCore" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\modules\common\LINGYUN.Abp.IM.SignalR\LINGYUN.Abp.IM.SignalR.csproj" /> |
|||
<ProjectReference Include="..\modules\common\LINGYUN.Abp.Notifications.SignalR\LINGYUN.Abp.Notifications.SignalR.csproj" /> |
|||
<ProjectReference Include="..\modules\message\LINGYUN.Abp.MessageService.EntityFrameworkCore\LINGYUN.Abp.MessageService.EntityFrameworkCore.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,143 @@ |
|||
using IdentityModel; |
|||
using LINGYUN.Abp.IM.SignalR; |
|||
using LINGYUN.Abp.MessageService.EntityFrameworkCore; |
|||
using LINGYUN.Abp.MessageService.MultiTenancy; |
|||
using LINGYUN.Abp.Notifications.SignalR; |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.AspNetCore.DataProtection; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Microsoft.OpenApi.Models; |
|||
using StackExchange.Redis; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Authentication.JwtBearer; |
|||
using Volo.Abp.AspNetCore.MultiTenancy; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.PermissionManagement.EntityFrameworkCore; |
|||
using Volo.Abp.Security.Claims; |
|||
using Volo.Abp.SettingManagement.EntityFrameworkCore; |
|||
using Volo.Abp.TenantManagement.EntityFrameworkCore; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpAspNetCoreMultiTenancyModule), |
|||
typeof(AbpMessageServiceEntityFrameworkCoreModule), |
|||
typeof(AbpTenantManagementEntityFrameworkCoreModule), |
|||
typeof(AbpSettingManagementEntityFrameworkCoreModule), |
|||
typeof(AbpPermissionManagementEntityFrameworkCoreModule), |
|||
typeof(AbpAspNetCoreAuthenticationJwtBearerModule), |
|||
typeof(AbpIMSignalRModule), |
|||
typeof(AbpNotificationsSignalRModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class AbpMessageServiceHttpApiHostModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var hostingEnvironment = context.Services.GetHostingEnvironment(); |
|||
var configuration = hostingEnvironment.BuildConfiguration(); |
|||
// 配置Ef
|
|||
Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.UseMySQL(); |
|||
}); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpMessageServiceHttpApiHostModule>("LINGYUN.Abp.MessageService"); |
|||
}); |
|||
|
|||
// 多租户
|
|||
Configure<AbpMultiTenancyOptions>(options => |
|||
{ |
|||
options.IsEnabled = true; |
|||
}); |
|||
|
|||
Configure<AbpTenantResolveOptions>(options => |
|||
{ |
|||
options.TenantResolvers.Insert(0, new AuthorizationTenantResolveContributor()); |
|||
}); |
|||
|
|||
// Swagger
|
|||
context.Services.AddSwaggerGen( |
|||
options => |
|||
{ |
|||
options.SwaggerDoc("v1", new OpenApiInfo { Title = "MessageService API", Version = "v1" }); |
|||
options.DocInclusionPredicate((docName, description) => true); |
|||
options.CustomSchemaIds(type => type.FullName); |
|||
}); |
|||
|
|||
// 支持本地化语言类型
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Languages.Add(new LanguageInfo("en", "en", "English")); |
|||
options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); |
|||
}); |
|||
|
|||
context.Services.AddAuthentication("Bearer") |
|||
.AddIdentityServerAuthentication(options => |
|||
{ |
|||
options.Authority = configuration["AuthServer:Authority"]; |
|||
options.RequireHttpsMetadata = false; |
|||
options.ApiName = configuration["AuthServer:ApiName"]; |
|||
AbpClaimTypes.UserId = JwtClaimTypes.Subject; |
|||
AbpClaimTypes.UserName = JwtClaimTypes.Name; |
|||
AbpClaimTypes.Role = JwtClaimTypes.Role; |
|||
AbpClaimTypes.Email = JwtClaimTypes.Email; |
|||
}); |
|||
|
|||
context.Services.AddStackExchangeRedisCache(options => |
|||
{ |
|||
options.Configuration = configuration["RedisCache:ConnectString"]; |
|||
var instanceName = configuration["RedisCache:RedisPrefix"]; |
|||
options.InstanceName = instanceName.IsNullOrEmpty() ? "Platform_Cache" : instanceName; |
|||
}); |
|||
|
|||
if (!hostingEnvironment.IsDevelopment()) |
|||
{ |
|||
var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]); |
|||
context.Services |
|||
.AddDataProtection() |
|||
.PersistKeysToStackExchangeRedis(redis, "MessageService-Protection-Keys"); |
|||
} |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
// http调用链
|
|||
app.UseCorrelationId(); |
|||
// 虚拟文件系统
|
|||
app.UseVirtualFiles(); |
|||
// 本地化
|
|||
app.UseAbpRequestLocalization(); |
|||
//路由
|
|||
app.UseRouting(); |
|||
// 认证
|
|||
app.UseAuthentication(); |
|||
// jwt
|
|||
app.UseJwtTokenMiddleware(); |
|||
// 多租户
|
|||
app.UseMultiTenancy(); |
|||
// Swagger
|
|||
app.UseSwagger(); |
|||
// Swagger可视化界面
|
|||
app.UseSwaggerUI(options => |
|||
{ |
|||
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support MessageService API"); |
|||
}); |
|||
// 审计日志
|
|||
app.UseAuditing(); |
|||
// 路由
|
|||
app.UseConfiguredEndpoints(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using LINGYUN.Abp.MessageService.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Migrations |
|||
{ |
|||
[DbContext(typeof(MessageServiceHostMigrationsDbContext))] |
|||
[Migration("20200601060701_Add-Abp-Message-Service-Module")] |
|||
partial class AddAbpMessageServiceModule |
|||
{ |
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "3.1.3") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 64); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.MessageService.Notifications.Notification", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<DateTime?>("ExpirationTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("NotificationData") |
|||
.IsRequired() |
|||
.HasColumnType("longtext CHARACTER SET utf8mb4") |
|||
.HasMaxLength(1048576); |
|||
|
|||
b.Property<long>("NotificationId") |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<string>("NotificationName") |
|||
.IsRequired() |
|||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4") |
|||
.HasMaxLength(100); |
|||
|
|||
b.Property<string>("NotificationTypeName") |
|||
.IsRequired() |
|||
.HasColumnType("varchar(512) CHARACTER SET utf8mb4") |
|||
.HasMaxLength(512); |
|||
|
|||
b.Property<sbyte>("Severity") |
|||
.HasColumnType("tinyint"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<int>("Type") |
|||
.HasColumnType("int"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("NotificationName"); |
|||
|
|||
b.ToTable("AppNotifications"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.MessageService.Notifications.UserNotification", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<long>("NotificationId") |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<sbyte>("ReadStatus") |
|||
.HasColumnType("tinyint"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<Guid>("UserId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("TenantId", "UserId", "NotificationId") |
|||
.HasName("IX_Tenant_User_Notification_Id"); |
|||
|
|||
b.ToTable("AppUserNotifications"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.MessageService.Subscriptions.UserSubscribe", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("NotificationName") |
|||
.IsRequired() |
|||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4") |
|||
.HasMaxLength(100); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<Guid>("UserId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("TenantId", "UserId", "NotificationName") |
|||
.IsUnique() |
|||
.HasName("IX_Tenant_User_Notification_Name"); |
|||
|
|||
b.ToTable("AppUserSubscribes"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore.Metadata; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Migrations |
|||
{ |
|||
public partial class AddAbpMessageServiceModule : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.CreateTable( |
|||
name: "AppNotifications", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<long>(nullable: false) |
|||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), |
|||
TenantId = table.Column<Guid>(nullable: true), |
|||
Severity = table.Column<sbyte>(nullable: false), |
|||
Type = table.Column<int>(nullable: false), |
|||
NotificationId = table.Column<long>(nullable: false), |
|||
NotificationName = table.Column<string>(maxLength: 100, nullable: false), |
|||
NotificationData = table.Column<string>(maxLength: 1048576, nullable: false), |
|||
NotificationTypeName = table.Column<string>(maxLength: 512, nullable: false), |
|||
ExpirationTime = table.Column<DateTime>(nullable: true), |
|||
CreationTime = table.Column<DateTime>(nullable: false) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AppNotifications", x => x.Id); |
|||
}); |
|||
|
|||
migrationBuilder.CreateTable( |
|||
name: "AppUserNotifications", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<long>(nullable: false) |
|||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), |
|||
TenantId = table.Column<Guid>(nullable: true), |
|||
UserId = table.Column<Guid>(nullable: false), |
|||
NotificationId = table.Column<long>(nullable: false), |
|||
ReadStatus = table.Column<sbyte>(nullable: false) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AppUserNotifications", x => x.Id); |
|||
}); |
|||
|
|||
migrationBuilder.CreateTable( |
|||
name: "AppUserSubscribes", |
|||
columns: table => new |
|||
{ |
|||
Id = table.Column<long>(nullable: false) |
|||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), |
|||
TenantId = table.Column<Guid>(nullable: true), |
|||
CreationTime = table.Column<DateTime>(nullable: false), |
|||
NotificationName = table.Column<string>(maxLength: 100, nullable: false), |
|||
UserId = table.Column<Guid>(nullable: false) |
|||
}, |
|||
constraints: table => |
|||
{ |
|||
table.PrimaryKey("PK_AppUserSubscribes", x => x.Id); |
|||
}); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_AppNotifications_NotificationName", |
|||
table: "AppNotifications", |
|||
column: "NotificationName"); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_Tenant_User_Notification_Id", |
|||
table: "AppUserNotifications", |
|||
columns: new[] { "TenantId", "UserId", "NotificationId" }); |
|||
|
|||
migrationBuilder.CreateIndex( |
|||
name: "IX_Tenant_User_Notification_Name", |
|||
table: "AppUserSubscribes", |
|||
columns: new[] { "TenantId", "UserId", "NotificationName" }, |
|||
unique: true); |
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropTable( |
|||
name: "AppNotifications"); |
|||
|
|||
migrationBuilder.DropTable( |
|||
name: "AppUserNotifications"); |
|||
|
|||
migrationBuilder.DropTable( |
|||
name: "AppUserSubscribes"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using LINGYUN.Abp.MessageService.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Migrations |
|||
{ |
|||
[DbContext(typeof(MessageServiceHostMigrationsDbContext))] |
|||
partial class MessageServiceHostMigrationsDbContextModelSnapshot : ModelSnapshot |
|||
{ |
|||
protected override void BuildModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "3.1.3") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 64); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.MessageService.Notifications.Notification", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<DateTime?>("ExpirationTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("NotificationData") |
|||
.IsRequired() |
|||
.HasColumnType("longtext CHARACTER SET utf8mb4") |
|||
.HasMaxLength(1048576); |
|||
|
|||
b.Property<long>("NotificationId") |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<string>("NotificationName") |
|||
.IsRequired() |
|||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4") |
|||
.HasMaxLength(100); |
|||
|
|||
b.Property<string>("NotificationTypeName") |
|||
.IsRequired() |
|||
.HasColumnType("varchar(512) CHARACTER SET utf8mb4") |
|||
.HasMaxLength(512); |
|||
|
|||
b.Property<sbyte>("Severity") |
|||
.HasColumnType("tinyint"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<int>("Type") |
|||
.HasColumnType("int"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("NotificationName"); |
|||
|
|||
b.ToTable("AppNotifications"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.MessageService.Notifications.UserNotification", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<long>("NotificationId") |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<sbyte>("ReadStatus") |
|||
.HasColumnType("tinyint"); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<Guid>("UserId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("TenantId", "UserId", "NotificationId") |
|||
.HasName("IX_Tenant_User_Notification_Id"); |
|||
|
|||
b.ToTable("AppUserNotifications"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("LINGYUN.Abp.MessageService.Subscriptions.UserSubscribe", b => |
|||
{ |
|||
b.Property<long>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnType("bigint"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime") |
|||
.HasColumnType("datetime(6)"); |
|||
|
|||
b.Property<string>("NotificationName") |
|||
.IsRequired() |
|||
.HasColumnType("varchar(100) CHARACTER SET utf8mb4") |
|||
.HasMaxLength(100); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.Property<Guid>("UserId") |
|||
.HasColumnType("char(36)"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("TenantId", "UserId", "NotificationName") |
|||
.IsUnique() |
|||
.HasName("IX_Tenant_User_Notification_Name"); |
|||
|
|||
b.ToTable("AppUserSubscribes"); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using Microsoft.AspNetCore.Http; |
|||
using System.Linq; |
|||
using Volo.Abp.AspNetCore.MultiTenancy; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Security.Claims; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.MultiTenancy |
|||
{ |
|||
public class AuthorizationTenantResolveContributor : HttpTenantResolveContributorBase |
|||
{ |
|||
public override string Name => "Authorization"; |
|||
|
|||
protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext) |
|||
{ |
|||
if (httpContext.User?.Identity == null) |
|||
{ |
|||
return null; |
|||
} |
|||
if (!httpContext.User.Identity.IsAuthenticated) |
|||
{ |
|||
return null; |
|||
} |
|||
var tenantIdKey = context.GetAbpAspNetCoreMultiTenancyOptions().TenantKey; |
|||
|
|||
var tenantClaim = httpContext.User.Claims.FirstOrDefault(x => x.Type.Equals(AbpClaimTypes.TenantId)); |
|||
|
|||
if (tenantClaim == null) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return tenantClaim.Value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Serilog; |
|||
using System; |
|||
using System.IO; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
public class Program |
|||
{ |
|||
public static int Main(string[] args) |
|||
{ |
|||
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"; |
|||
var configuration = new ConfigurationBuilder() |
|||
.SetBasePath(Directory.GetCurrentDirectory()) |
|||
.AddJsonFile($"appsettings.{env}.json", optional: false, reloadOnChange: true) |
|||
.AddEnvironmentVariables() |
|||
.Build(); |
|||
Log.Logger = new LoggerConfiguration() |
|||
.ReadFrom.Configuration(configuration) |
|||
.CreateLogger(); |
|||
try |
|||
{ |
|||
Log.Information("Starting web host."); |
|||
CreateHostBuilder(args).Build().Run(); |
|||
return 0; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Log.Fatal(ex, "Host terminated unexpectedly!"); |
|||
return 1; |
|||
} |
|||
finally |
|||
{ |
|||
Log.CloseAndFlush(); |
|||
} |
|||
} |
|||
|
|||
internal static IHostBuilder CreateHostBuilder(string[] args) => |
|||
Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) |
|||
.ConfigureWebHostDefaults(webBuilder => |
|||
{ |
|||
webBuilder.UseStartup<Startup>(); |
|||
}) |
|||
.UseSerilog() |
|||
.UseAutofac(); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
{ |
|||
"iisSettings": { |
|||
"windowsAuthentication": false, |
|||
"anonymousAuthentication": true, |
|||
"iisExpress": { |
|||
"applicationUrl": "http://localhost:63963", |
|||
"sslPort": 0 |
|||
} |
|||
}, |
|||
"profiles": { |
|||
"LINGYUN.Abp.MessageService.HttpApi.Host": { |
|||
"commandName": "Project", |
|||
"launchBrowser": false, |
|||
"applicationUrl": "http://localhost:30020", |
|||
"environmentVariables": { |
|||
"ASPNETCORE_ENVIRONMENT": "Development" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
public class Startup |
|||
{ |
|||
public void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddApplication<AbpMessageServiceHttpApiHostModule>(); |
|||
} |
|||
|
|||
public void Configure(IApplicationBuilder app) |
|||
{ |
|||
app.InitializeApplication(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using LINGYUN.Abp.IM.Messages; |
|||
using LINGYUN.Abp.RealTime.Client; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.AspNetCore.SignalR; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.SignalR; |
|||
|
|||
namespace LINGYUN.Abp.IM.SignalR.Hubs |
|||
{ |
|||
[Authorize] |
|||
[HubRoute("messages")] |
|||
public class AbpMessageHub : OnlineClientHubBase |
|||
{ |
|||
private readonly IMessageStore _messageStore; |
|||
|
|||
public AbpMessageHub( |
|||
IMessageStore messageStore) |
|||
{ |
|||
_messageStore = messageStore; |
|||
} |
|||
/// <summary>
|
|||
/// 客户端调用发送消息方法
|
|||
/// </summary>
|
|||
/// <param name="chatMessage"></param>
|
|||
/// <returns></returns>
|
|||
[HubMethodName("SendMessage")] |
|||
public virtual async Task SendMessageAsync(ChatMessage chatMessage) |
|||
{ |
|||
// 持久化
|
|||
await _messageStore.StoreMessageAsync(chatMessage); |
|||
|
|||
if (!chatMessage.GroupName.IsNullOrWhiteSpace()) |
|||
{ |
|||
try |
|||
{ |
|||
var signalRClient = Clients.Group(chatMessage.GroupName); |
|||
if (signalRClient == null) |
|||
{ |
|||
Logger.LogDebug("Can not get group " + chatMessage.GroupName + " from SignalR hub!"); |
|||
return; |
|||
} |
|||
|
|||
await signalRClient.SendAsync("getChatMessage", chatMessage); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning("Could not send message to group: {0}", chatMessage.GroupName); |
|||
Logger.LogWarning("Send group message error: {0}", ex.Message); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var onlineClientContext = new OnlineClientContext(chatMessage.TenantId, chatMessage.ToUserId); |
|||
var onlineClients = OnlineClientManager.GetAllByContext(onlineClientContext); |
|||
foreach (var onlineClient in onlineClients) |
|||
{ |
|||
try |
|||
{ |
|||
var signalRClient = Clients.Client(onlineClient.ConnectionId); |
|||
if (signalRClient == null) |
|||
{ |
|||
Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " with connectionId " + onlineClient.ConnectionId + " from SignalR hub!"); |
|||
continue; |
|||
} |
|||
await signalRClient.SendAsync("getChatMessage", chatMessage); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning("Could not send message to user: {0}", chatMessage.ToUserId); |
|||
Logger.LogWarning("Send to user message error: {0}", ex.Message); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
using LINGYUN.Abp.IM.Messages; |
|||
using LINGYUN.Abp.IM.SignalR.Hubs; |
|||
using LINGYUN.Abp.RealTime.Client; |
|||
using Microsoft.AspNetCore.SignalR; |
|||
using Microsoft.Extensions.Logging; |
|||
using Microsoft.Extensions.Logging.Abstractions; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.IM.SignalR.Messages |
|||
{ |
|||
public class SignalRMessageSender : IMessageSender, ITransientDependency |
|||
{ |
|||
public ILogger<SignalRMessageSender> Logger { protected get; set; } |
|||
|
|||
private readonly IOnlineClientManager _onlineClientManager; |
|||
|
|||
private readonly IHubContext<AbpMessageHub> _hubContext; |
|||
|
|||
private readonly IMessageStore _messageStore; |
|||
|
|||
public SignalRMessageSender( |
|||
IOnlineClientManager onlineClientManager, |
|||
IHubContext<AbpMessageHub> hubContext, |
|||
IMessageStore messageStore) |
|||
{ |
|||
_hubContext = hubContext; |
|||
_messageStore = messageStore; |
|||
_onlineClientManager = onlineClientManager; |
|||
|
|||
Logger = NullLogger<SignalRMessageSender>.Instance; |
|||
} |
|||
/// <summary>
|
|||
/// 服务端调用发送消息方法
|
|||
/// </summary>
|
|||
/// <param name="chatMessage"></param>
|
|||
/// <returns></returns>
|
|||
public async Task SendMessageAsync(ChatMessage chatMessage) |
|||
{ |
|||
// 持久化
|
|||
await _messageStore.StoreMessageAsync(chatMessage); |
|||
|
|||
if (!chatMessage.GroupName.IsNullOrWhiteSpace()) |
|||
{ |
|||
try |
|||
{ |
|||
var signalRClient = _hubContext.Clients.Group(chatMessage.GroupName); |
|||
if (signalRClient == null) |
|||
{ |
|||
Logger.LogDebug("Can not get group " + chatMessage.GroupName + " from SignalR hub!"); |
|||
return; |
|||
} |
|||
|
|||
await signalRClient.SendAsync("getChatMessage", chatMessage); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning("Could not send message to group: {0}", chatMessage.GroupName); |
|||
Logger.LogWarning("Send group message error: {0}", ex.Message); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
var onlineClientContext = new OnlineClientContext(chatMessage.TenantId, chatMessage.ToUserId); |
|||
var onlineClients = _onlineClientManager.GetAllByContext(onlineClientContext); |
|||
foreach (var onlineClient in onlineClients) |
|||
{ |
|||
try |
|||
{ |
|||
var signalRClient = _hubContext.Clients.Client(onlineClient.ConnectionId); |
|||
if (signalRClient == null) |
|||
{ |
|||
Logger.LogDebug("Can not get user " + onlineClientContext.UserId + " with connectionId " + onlineClient.ConnectionId + " from SignalR hub!"); |
|||
continue; |
|||
} |
|||
|
|||
await signalRClient.SendAsync("getChatMessage", chatMessage); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning("Could not send message to user: {0}", chatMessage.ToUserId); |
|||
Logger.LogWarning("Send to user message error: {0}", ex.Message); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.IM.Group |
|||
{ |
|||
public class GroupChat |
|||
{ |
|||
public string Name { get; set; } |
|||
public int MaxUserLength { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.IM.Group |
|||
{ |
|||
public interface IUserGroupStore |
|||
{ |
|||
/// <summary>
|
|||
/// 获取用户所在通讯组列表
|
|||
/// </summary>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="userId"></param>
|
|||
/// <returns></returns>
|
|||
Task<IEnumerable<GroupChat>> GetUserGroupsAsync(Guid? tenantId, Guid userId); |
|||
/// <summary>
|
|||
/// 获取通讯组所有用户
|
|||
/// </summary>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="groupId"></param>
|
|||
/// <returns></returns>
|
|||
Task<IEnumerable<UserGroup>> GetGroupUsersAsync(Guid? tenantId, long groupId); |
|||
/// <summary>
|
|||
/// 用户加入通讯组
|
|||
/// </summary>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="userId"></param>
|
|||
/// <param name="groupId"></param>
|
|||
/// <returns></returns>
|
|||
Task AddUserToGroupAsync(Guid? tenantId, Guid userId, long groupId); |
|||
/// <summary>
|
|||
/// 用户退出通讯组
|
|||
/// </summary>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="userId"></param>
|
|||
/// <param name="groupId"></param>
|
|||
/// <returns></returns>
|
|||
Task RemoveUserFormGroupAsync(Guid? tenantId, Guid userId, long groupId); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.IM.Group |
|||
{ |
|||
public class UserGroup |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
public Guid UserId { get; set; } |
|||
public long GroupId { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public class ChatMessage |
|||
{ |
|||
public Guid? TenantId { get; set; } |
|||
public string GroupName { get; set; } |
|||
public Guid FormUserId { get; set; } |
|||
public Guid ToUserId { get; set; } |
|||
public string Content { get; set; } |
|||
public DateTime SendTime { get; set; } |
|||
public MessageType MessageType { get; set; } = MessageType.Text; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public interface IMessageSender |
|||
{ |
|||
Task SendMessageAsync(ChatMessage chatMessage); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public interface IMessageStore |
|||
{ |
|||
/// <summary>
|
|||
/// 存储聊天记录
|
|||
/// </summary>
|
|||
/// <param name="chatMessage"></param>
|
|||
/// <param name="formUserId"></param>
|
|||
/// <param name="toUserId"></param>
|
|||
/// <returns></returns>
|
|||
Task StoreMessageAsync(ChatMessage chatMessage); |
|||
/// <summary>
|
|||
/// 获取群组聊天记录
|
|||
/// </summary>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="groupName"></param>
|
|||
/// <param name="maxResultCount"></param>
|
|||
/// <returns></returns>
|
|||
Task<List<ChatMessage>> GetGroupMessageAsync(Guid tenantId, string groupName, int maxResultCount = 10); |
|||
/// <summary>
|
|||
/// 获取与某个用户的聊天记录
|
|||
/// </summary>
|
|||
/// <param name="tenantId"></param>
|
|||
/// <param name="userId"></param>
|
|||
/// <param name="maxResultCount"></param>
|
|||
/// <returns></returns>
|
|||
Task<List<ChatMessage>> GetChatMessageAsync(Guid tenantId, Guid userId, int maxResultCount = 10); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
namespace LINGYUN.Abp.IM.Messages |
|||
{ |
|||
public enum MessageType |
|||
{ |
|||
Text = 0, |
|||
Image = 10, |
|||
Link = 20, |
|||
Video = 30 |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.SignalR" Version="2.8.0" /> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.RealTime\LINGYUN.Abp.RealTime.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,15 @@ |
|||
using LINGYUN.Abp.RealTime; |
|||
using Volo.Abp.AspNetCore.SignalR; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.SignalR |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpRealTimeModule), |
|||
typeof(AbpNotificationModule), |
|||
typeof(AbpAspNetCoreSignalRModule))] |
|||
public class AbpNotificationsSignalRModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.AspNetCore.SignalR; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
using Volo.Abp.AspNetCore.SignalR; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.SignalR.Hubs |
|||
{ |
|||
[Authorize] |
|||
[HubRoute("notifications")] |
|||
public class AbpNotificationsHub : OnlineClientHubBase |
|||
{ |
|||
private INotificationStore _notificationStore; |
|||
protected INotificationStore NotificationStore => LazyGetRequiredService(ref _notificationStore); |
|||
|
|||
[HubMethodName("GetNotification")] |
|||
public virtual async Task<ListResultDto<NotificationInfo>> GetNotificationAsync(NotificationReadState readState = NotificationReadState.UnRead) |
|||
{ |
|||
var userNotifications = await NotificationStore.GetUserNotificationsAsync(CurrentTenant.Id, CurrentUser.GetId(), readState); |
|||
|
|||
return new ListResultDto<NotificationInfo>(userNotifications); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
using LINGYUN.Abp.RealTime.Client; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Logging; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AspNetCore.SignalR; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.SignalR |
|||
{ |
|||
public abstract class OnlineClientHubBase : AbpHub |
|||
{ |
|||
private IOnlineClientManager _onlineClientManager; |
|||
protected IOnlineClientManager OnlineClientManager => LazyGetRequiredService(ref _onlineClientManager); |
|||
|
|||
private IHttpContextAccessor _httpContextAccessor; |
|||
protected IHttpContextAccessor HttpContextAccessor => LazyGetRequiredService(ref _httpContextAccessor); |
|||
|
|||
public override async Task OnConnectedAsync() |
|||
{ |
|||
await base.OnConnectedAsync(); |
|||
IOnlineClient onlineClient = CreateClientForCurrentConnection(); |
|||
Logger.LogDebug("A client is connected: " + onlineClient.ToString()); |
|||
OnlineClientManager.Add(onlineClient); |
|||
} |
|||
|
|||
public override async Task OnDisconnectedAsync(Exception exception) |
|||
{ |
|||
await base.OnDisconnectedAsync(exception); |
|||
Logger.LogDebug("A client is disconnected: " + Context.ConnectionId); |
|||
try |
|||
{ |
|||
// 从通讯组移除
|
|||
var onlineClient = OnlineClientManager.GetByConnectionIdOrNull(Context.ConnectionId); |
|||
if(onlineClient != null) |
|||
{ |
|||
// 移除在线客户端
|
|||
OnlineClientManager.Remove(Context.ConnectionId); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogWarning(ex.ToString(), ex); |
|||
} |
|||
} |
|||
|
|||
protected virtual IOnlineClient CreateClientForCurrentConnection() |
|||
{ |
|||
return new OnlineClient(Context.ConnectionId, GetClientIpAddress(), |
|||
CurrentTenant.Id, CurrentUser.Id) |
|||
{ |
|||
ConnectTime = Clock.Now |
|||
}; |
|||
} |
|||
|
|||
protected virtual string GetClientIpAddress() |
|||
{ |
|||
try |
|||
{ |
|||
return HttpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Logger.LogException(ex, LogLevel.Warning); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using LINGYUN.Abp.Notifications.Internal; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public class AbpNotificationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddTransient<INotificationDispatcher, DefaultNotificationDispatcher>(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications |
|||
{ |
|||
public interface INotificationSender |
|||
{ |
|||
Task SendAsync(NotificationData data, Guid userId, Guid? tenantId); |
|||
Task SendAsync(NotificationData data, IEnumerable<Guid> userIds, Guid? tenantId); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Notifications.Internal |
|||
{ |
|||
internal class DefaultNotificationDispatcher : INotificationDispatcher |
|||
{ |
|||
private readonly INotificationStore _notificationStore; |
|||
private readonly INotificationPublisher _notificationPublisher; |
|||
public DefaultNotificationDispatcher( |
|||
INotificationStore notificationStore, |
|||
INotificationPublisher notificationPublisher) |
|||
{ |
|||
_notificationStore = notificationStore; |
|||
_notificationPublisher = notificationPublisher; |
|||
} |
|||
|
|||
public async Task DispatcheAsync(NotificationInfo notification) |
|||
{ |
|||
// 持久化通知
|
|||
await _notificationStore.InsertNotificationAsync(notification); |
|||
|
|||
// 获取用户订阅列表
|
|||
var userSubscriptions = await _notificationStore.GetSubscriptionsAsync(notification.TenantId, notification.Name); |
|||
|
|||
// 持久化用户订阅通知
|
|||
var subscriptionUserIds = userSubscriptions.Select(us => us.UserId); |
|||
await _notificationStore.InsertUserSubscriptionAsync(notification.TenantId, subscriptionUserIds, notification.Name); |
|||
|
|||
// 发布用户通知
|
|||
await _notificationPublisher.PublishAsync(notification, subscriptionUserIds); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.RealTime |
|||
{ |
|||
public class AbpRealTimeModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.IM |
|||
namespace LINGYUN.Abp.RealTime.Client |
|||
{ |
|||
public interface IOnlineClientStore |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.IM |
|||
namespace LINGYUN.Abp.RealTime.Client |
|||
{ |
|||
public class OnlineClientEventArgs : EventArgs |
|||
{ |
|||
@ -1,4 +1,4 @@ |
|||
namespace LINGYUN.Abp.IM |
|||
namespace LINGYUN.Abp.RealTime.Client |
|||
{ |
|||
public class OnlineUserEventArgs : OnlineClientEventArgs |
|||
{ |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Localization |
|||
{ |
|||
[LocalizationResourceName("MessageService")] |
|||
public class MessageServiceResource |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
public class MessageServiceSettingNames |
|||
{ |
|||
public const string GroupName = "Abp.MessageService"; |
|||
|
|||
public class Notifications |
|||
{ |
|||
public const string Default = GroupName + ".Notifications"; |
|||
/// <summary>
|
|||
/// 清理过期消息批次
|
|||
/// </summary>
|
|||
public const string CleanupExpirationBatchCount = Default + ".CleanupExpirationBatchCount"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class CleanupNotificationJobArgs |
|||
{ |
|||
/// <summary>
|
|||
/// 清理大小
|
|||
/// </summary>
|
|||
public int Count { get; set; } |
|||
/// <summary>
|
|||
/// 清理租户
|
|||
/// </summary>
|
|||
public Guid? TenantId { get; set; } |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
/// <summary>
|
|||
/// 读取状态
|
|||
/// </summary>
|
|||
public enum ReadStatus : sbyte |
|||
{ |
|||
/// <summary>
|
|||
/// 已读
|
|||
/// </summary>
|
|||
Read = 0, |
|||
/// <summary>
|
|||
/// 未读
|
|||
/// </summary>
|
|||
UnRead = 10 |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace LINGYUN.Abp.MessageService |
|||
{ |
|||
public class AbpMessageServiceDbProperties |
|||
{ |
|||
public const string DefaultTablePrefix = "App"; |
|||
|
|||
public const string DefaultSchema = null; |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using LINGYUN.Abp.MessageService.Notifications; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.BackgroundJobs; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.BackgroundJobs |
|||
{ |
|||
public class NotificationExpritionCleanupJob : AsyncBackgroundJob<CleanupNotificationJobArgs> |
|||
{ |
|||
private readonly ICurrentTenant _currentTenant; |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
private readonly INotificationRepository _notificationRepository; |
|||
public NotificationExpritionCleanupJob( |
|||
ICurrentTenant currentTenant, |
|||
IUnitOfWorkManager unitOfWorkManager, |
|||
INotificationRepository notificationRepository) |
|||
{ |
|||
_currentTenant = currentTenant; |
|||
_unitOfWorkManager = unitOfWorkManager; |
|||
_notificationRepository = notificationRepository; |
|||
} |
|||
|
|||
public override async Task ExecuteAsync(CleanupNotificationJobArgs args) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (_currentTenant.Change(args.TenantId)) |
|||
{ |
|||
await _notificationRepository.DeleteExpritionAsync(args.Count); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
using AutoMapper; |
|||
using LINGYUN.Abp.MessageService.Notifications; |
|||
using LINGYUN.Abp.MessageService.Subscriptions; |
|||
using LINGYUN.Abp.Notifications; |
|||
using Newtonsoft.Json; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Mapper |
|||
{ |
|||
public class MessageServiceDomainAutoMapperProfile : Profile |
|||
{ |
|||
public MessageServiceDomainAutoMapperProfile() |
|||
{ |
|||
CreateMap<Notification, NotificationInfo>() |
|||
.ForMember(dto => dto.Id, map => map.MapFrom(src => src.NotificationId)) |
|||
.ForMember(dto => dto.Name, map => map.MapFrom(src => src.NotificationName)) |
|||
.ForMember(dto => dto.NotificationType, map => map.MapFrom(src => src.Type)) |
|||
.ForMember(dto => dto.NotificationSeverity, map => map.MapFrom(src => src.Severity)) |
|||
.ForMember(dto => dto.Data, map => map.MapFrom((src, nfi) => |
|||
{ |
|||
var notificationDataType = Type.GetType(src.NotificationTypeName); |
|||
var notificationData = JsonConvert.DeserializeObject(src.NotificationData, notificationDataType); |
|||
if(notificationData != null) |
|||
{ |
|||
return notificationData as NotificationData; |
|||
} |
|||
return new NotificationData(); |
|||
})); |
|||
|
|||
CreateMap<UserSubscribe, NotificationSubscriptionInfo>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public interface INotificationRepository : IBasicRepository<Notification, long> |
|||
{ |
|||
Task<Notification> GetByIdAsync(long notificationId); |
|||
|
|||
Task DeleteExpritionAsync(int batchCount); |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public interface INotificationStore |
|||
{ |
|||
Task InsertUserNotificationAsync(NotificationInfo notification, Guid userId); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public interface IUserNotificationRepository : IBasicRepository<UserNotification, long> |
|||
{ |
|||
Task<UserNotification> GetByIdAsync(Guid userId, long notificationId); |
|||
|
|||
Task<List<Notification>> GetNotificationsAsync(Guid userId, NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10); |
|||
|
|||
Task ChangeUserNotificationReadStateAsync(Guid userId, long notificationId, NotificationReadState readState); |
|||
} |
|||
} |
|||
@ -1,47 +1,40 @@ |
|||
using LINGYUN.Abp.MessageService.Subscriptions; |
|||
using LINGYUN.Abp.MessageService.Utils; |
|||
using LINGYUN.Abp.Notifications; |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class NotificationDispatcher : INotificationDispatcher, ITransientDependency |
|||
{ |
|||
protected IJsonSerializer JsonSerializer { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected ISubscribeStore SubscribeStore { get; } |
|||
protected INotificationStore NotificationStore { get; } |
|||
protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager.Current; |
|||
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
|||
protected INotificationPublisher NotificationPublisher { get; } |
|||
|
|||
protected ISnowflakeIdGenerator SnowflakeIdGenerator { get; } |
|||
public NotificationDispatcher( |
|||
INotificationStore notificationStore, |
|||
INotificationPublisher notificationPublisher) |
|||
{ |
|||
NotificationStore = notificationStore; |
|||
NotificationPublisher = notificationPublisher; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public virtual async Task DispatcheAsync(NotificationInfo notification) |
|||
{ |
|||
using (CurrentTenant.Change(notification.TenantId)) |
|||
var subscribes = await NotificationStore.GetSubscriptionsAsync(notification.TenantId, notification.Name); |
|||
foreach (var subscribe in subscribes) |
|||
{ |
|||
var subscribeUsers = await SubscribeStore.GetUserSubscribesAsync(notification.Name); |
|||
foreach(var userId in subscribeUsers) |
|||
{ |
|||
await NotificationStore.InsertUserNotificationAsync(notification, userId); |
|||
await NotificationStore.InsertUserNotificationAsync(notification, subscribe.UserId); |
|||
} |
|||
await CurrentUnitOfWork.SaveChangesAsync(); |
|||
|
|||
await NotifyAsync(notification.Data, notification.TenantId, subscribeUsers); |
|||
} |
|||
var subscribeUsers = subscribes.Select(s => s.UserId); |
|||
await NotifyAsync(notification, subscribeUsers); |
|||
} |
|||
|
|||
protected virtual async Task NotifyAsync(NotificationData data, Guid? tenantId, IEnumerable<Guid> userIds) |
|||
protected virtual async Task NotifyAsync(NotificationInfo notification, IEnumerable<Guid> userIds) |
|||
{ |
|||
await NotificationPublisher.PublishAsync(data, userIds, tenantId); |
|||
await NotificationPublisher.PublishAsync(notification, userIds); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,239 @@ |
|||
using LINGYUN.Abp.MessageService.Subscriptions; |
|||
using LINGYUN.Abp.MessageService.Utils; |
|||
using LINGYUN.Abp.Notifications; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.Json; |
|||
using Volo.Abp.ObjectMapping; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class NotificationStore : DomainService, INotificationStore |
|||
{ |
|||
private readonly IObjectMapper _objectMapper; |
|||
private readonly IUnitOfWorkManager _unitOfWorkManager; |
|||
|
|||
private IJsonSerializer _jsonSerializer; |
|||
protected IJsonSerializer JsonSerializer => LazyGetRequiredService(ref _jsonSerializer); |
|||
|
|||
private ISnowflakeIdGenerator _snowflakeIdGenerator; |
|||
protected ISnowflakeIdGenerator SnowflakeIdGenerator => LazyGetRequiredService(ref _snowflakeIdGenerator); |
|||
|
|||
private INotificationRepository _notificationRepository; |
|||
protected INotificationRepository NotificationRepository => LazyGetRequiredService(ref _notificationRepository); |
|||
|
|||
private IUserNotificationRepository _userNotificationRepository; |
|||
protected IUserNotificationRepository UserNotificationRepository => LazyGetRequiredService(ref _userNotificationRepository); |
|||
|
|||
private IUserSubscribeRepository _userSubscribeRepository; |
|||
protected IUserSubscribeRepository UserSubscribeRepository => LazyGetRequiredService(ref _userSubscribeRepository); |
|||
|
|||
public NotificationStore( |
|||
IObjectMapper objectMapper, |
|||
IUnitOfWorkManager unitOfWorkManager) |
|||
{ |
|||
_objectMapper = objectMapper; |
|||
_unitOfWorkManager = unitOfWorkManager; |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task ChangeUserNotificationReadStateAsync(Guid? tenantId, Guid userId, long notificationId, NotificationReadState readState) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
await UserNotificationRepository.ChangeUserNotificationReadStateAsync(userId, notificationId, readState); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task DeleteNotificationAsync(NotificationInfo notification) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(notification.TenantId)) |
|||
{ |
|||
var notify = await NotificationRepository.GetByIdAsync(notification.Id); |
|||
await NotificationRepository.DeleteAsync(notify.Id); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task DeleteUserNotificationAsync(Guid? tenantId, Guid userId, long notificationId) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var notify = await UserNotificationRepository.GetByIdAsync(userId, notificationId); |
|||
await UserNotificationRepository.DeleteAsync(notify.Id); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task DeleteUserSubscriptionAsync(Guid? tenantId, Guid userId, string notificationName) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var userSubscribe = await UserSubscribeRepository.GetUserSubscribeAsync(notificationName, userId); |
|||
await UserSubscribeRepository.DeleteAsync(userSubscribe.Id); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public async Task<NotificationInfo> GetNotificationOrNullAsync(Guid? tenantId, long notificationId) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var notification = await NotificationRepository.GetByIdAsync(notificationId); |
|||
|
|||
return _objectMapper.Map<Notification, NotificationInfo>(notification); |
|||
} |
|||
} |
|||
|
|||
public async Task<List<NotificationSubscriptionInfo>> GetSubscriptionsAsync(Guid? tenantId, string notificationName) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var userSubscriptions = await UserSubscribeRepository.GetSubscribesAsync(notificationName); |
|||
|
|||
return _objectMapper.Map<List<UserSubscribe>, List<NotificationSubscriptionInfo>>(userSubscriptions); |
|||
} |
|||
} |
|||
|
|||
public async Task<List<NotificationInfo>> GetUserNotificationsAsync(Guid? tenantId, Guid userId, NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var notifications = await UserNotificationRepository.GetNotificationsAsync(userId, readState, maxResultCount); |
|||
|
|||
return _objectMapper.Map<List<Notification>, List<NotificationInfo>>(notifications); |
|||
} |
|||
} |
|||
|
|||
public async Task<List<NotificationSubscriptionInfo>> GetUserSubscriptionsAsync(Guid? tenantId, Guid userId) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var userSubscriptionNames = await UserSubscribeRepository.GetUserSubscribesAsync(userId); |
|||
|
|||
var userSubscriptions = new List<NotificationSubscriptionInfo>(); |
|||
|
|||
userSubscriptionNames.ForEach(name => userSubscriptions.Add( |
|||
new NotificationSubscriptionInfo |
|||
{ |
|||
UserId = userId, |
|||
TenantId = tenantId, |
|||
NotificationName = name |
|||
})); |
|||
|
|||
return userSubscriptions; |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task InsertNotificationAsync(NotificationInfo notification) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(notification.TenantId)) |
|||
{ |
|||
var notify = new Notification(SnowflakeIdGenerator.Create(), notification.Name, |
|||
notification.Data.GetType().AssemblyQualifiedName, |
|||
JsonSerializer.Serialize(notification.Data), notification.NotificationSeverity) |
|||
{ |
|||
CreationTime = Clock.Now, |
|||
Type = notification.NotificationType, |
|||
ExpirationTime = Clock.Now.AddDays(60) |
|||
}; |
|||
notify.SetTenantId(notification.TenantId); |
|||
|
|||
await NotificationRepository.InsertAsync(notify); |
|||
|
|||
notification.Id = notify.NotificationId; |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task InsertUserNotificationAsync(NotificationInfo notification, Guid userId) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(notification.TenantId)) |
|||
{ |
|||
var userNotification = new UserNotification(notification.Id, userId); |
|||
await UserNotificationRepository.InsertAsync(userNotification); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task InsertUserSubscriptionAsync(Guid? tenantId, Guid userId, string notificationName) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
|
|||
var userSubscription = new UserSubscribe(notificationName, userId) |
|||
{ |
|||
CreationTime = Clock.Now |
|||
}; |
|||
|
|||
await UserSubscribeRepository.InsertAsync(userSubscription); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[UnitOfWork] |
|||
public async Task InsertUserSubscriptionAsync(Guid? tenantId, IEnumerable<Guid> userIds, string notificationName) |
|||
{ |
|||
using (var unitOfWork = _unitOfWorkManager.Begin()) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
{ |
|||
var userSubscribes = new List<UserSubscribe>(); |
|||
|
|||
foreach(var userId in userIds) |
|||
{ |
|||
userSubscribes.Add(new UserSubscribe(notificationName, userId)); |
|||
} |
|||
|
|||
await UserSubscribeRepository.InsertUserSubscriptionAsync(userSubscribes); |
|||
|
|||
await unitOfWork.SaveChangesAsync(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public async Task<bool> IsSubscribedAsync(Guid? tenantId, Guid userId, string notificationName) |
|||
{ |
|||
using (CurrentTenant.Change(tenantId)) |
|||
return await UserSubscribeRepository.UserSubscribeExistsAysnc(notificationName, userId); |
|||
} |
|||
} |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Subscriptions |
|||
{ |
|||
public interface ISubscribeStore |
|||
{ |
|||
Task<List<Guid>> GetUserSubscribesAsync(string notificationName); |
|||
Task UserSubscribeAsync(string notificationName, Guid userId); |
|||
Task UserUnSubscribeAsync(string notificationName, Guid userId); |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
using System; |
|||
using Volo.Abp.Auditing; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Subscriptions |
|||
{ |
|||
public class RoleSubscribe : Subscribe, IHasCreationTime |
|||
{ |
|||
public virtual string RoleName { get; set; } |
|||
public virtual DateTime CreationTime { get; set; } |
|||
protected RoleSubscribe() { } |
|||
public RoleSubscribe(string notificationName, string roleName) : base(notificationName) |
|||
{ |
|||
RoleName = roleName; |
|||
CreationTime = DateTime.Now; |
|||
} |
|||
} |
|||
} |
|||
@ -1,41 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Subscriptions |
|||
{ |
|||
public class SubscribeStore : ISubscribeStore, ITransientDependency |
|||
{ |
|||
protected ISubscribeRepository SubscribeRepository { get; } |
|||
|
|||
public SubscribeStore(ISubscribeRepository subscribeRepository) |
|||
{ |
|||
SubscribeRepository = subscribeRepository; |
|||
} |
|||
|
|||
public virtual async Task<List<Guid>> GetUserSubscribesAsync(string notificationName) |
|||
{ |
|||
return await SubscribeRepository.GetUserSubscribesAsync(notificationName); |
|||
} |
|||
|
|||
public virtual async Task UserSubscribeAsync(string notificationName, Guid userId) |
|||
{ |
|||
var userSubscribeExists = await SubscribeRepository.UserSubscribeExistsAysnc(notificationName, userId); |
|||
if (!userSubscribeExists) |
|||
{ |
|||
var userSbuscribe = new UserSubscribe(notificationName, userId); |
|||
await SubscribeRepository.InsertAsync(userSbuscribe); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task UserUnSubscribeAsync(string notificationName, Guid userId) |
|||
{ |
|||
var userSubscribe = await SubscribeRepository.GetUserSubscribeAsync(notificationName, userId); |
|||
if (userSubscribe != null) |
|||
{ |
|||
await SubscribeRepository.DeleteAsync(userSubscribe); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.EntityFrameworkCore" Version="2.8.0" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.MessageService.Domain\LINGYUN.Abp.MessageService.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,26 @@ |
|||
using LINGYUN.Abp.MessageService.Notifications; |
|||
using LINGYUN.Abp.MessageService.Subscriptions; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EntityFrameworkCore |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpMessageServiceDomainModule), |
|||
typeof(AbpEntityFrameworkCoreModule))] |
|||
public class AbpMessageServiceEntityFrameworkCoreModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddAbpDbContext<MessageServiceDbContext>(options => |
|||
{ |
|||
options.AddRepository<Notification, INotificationRepository>(); |
|||
options.AddRepository<UserNotification, IUserNotificationRepository>(); |
|||
options.AddRepository<UserSubscribe, IUserSubscribeRepository>(); |
|||
|
|||
options.AddDefaultRepositories(includeAllEntities: true); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EntityFrameworkCore |
|||
{ |
|||
[ConnectionStringName("MessageService")] |
|||
public class MessageServiceDbContext : AbpDbContext<MessageServiceDbContext> |
|||
{ |
|||
public MessageServiceDbContext(DbContextOptions<MessageServiceDbContext> options) |
|||
: base(options) |
|||
{ |
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder modelBuilder) |
|||
{ |
|||
modelBuilder.ConfigureMessageService(options => |
|||
{ |
|||
options.TablePrefix = AbpMessageServiceDbProperties.DefaultTablePrefix; |
|||
options.Schema = AbpMessageServiceDbProperties.DefaultSchema; |
|||
}); |
|||
|
|||
base.OnModelCreating(modelBuilder); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
using LINGYUN.Abp.MessageService.Notifications; |
|||
using LINGYUN.Abp.MessageService.Subscriptions; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EntityFrameworkCore |
|||
{ |
|||
public static class MessageServiceDbContextModelCreatingExtensions |
|||
{ |
|||
public static void ConfigureMessageService( |
|||
this ModelBuilder builder, |
|||
Action<MessageServiceModelBuilderConfigurationOptions> optionsAction = null) |
|||
{ |
|||
Check.NotNull(builder, nameof(builder)); |
|||
|
|||
var options = new MessageServiceModelBuilderConfigurationOptions(); |
|||
|
|||
optionsAction?.Invoke(options); |
|||
|
|||
builder.Entity<Notification>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "Notifications", options.Schema); |
|||
|
|||
b.Property(p => p.NotificationName).HasMaxLength(NotificationConsts.MaxNameLength).IsRequired(); |
|||
b.Property(p => p.NotificationTypeName).HasMaxLength(NotificationConsts.MaxTypeNameLength).IsRequired(); |
|||
b.Property(p => p.NotificationData).HasMaxLength(NotificationConsts.MaxDataLength).IsRequired(); |
|||
|
|||
b.ConfigureMultiTenant(); |
|||
b.ConfigureCreationTime(); |
|||
|
|||
b.HasIndex(p => p.NotificationName); |
|||
}); |
|||
|
|||
builder.Entity<UserNotification>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "UserNotifications", options.Schema); |
|||
|
|||
b.ConfigureMultiTenant(); |
|||
|
|||
b.HasIndex(p => new { p.TenantId, p.UserId, p.NotificationId }) |
|||
.HasName("IX_Tenant_User_Notification_Id"); |
|||
}); |
|||
|
|||
builder.Entity<UserSubscribe>(b => |
|||
{ |
|||
b.ToTable(options.TablePrefix + "UserSubscribes", options.Schema); |
|||
|
|||
b.Property(p => p.NotificationName).HasMaxLength(SubscribeConsts.MaxNotificationNameLength).IsRequired(); |
|||
|
|||
b.ConfigureCreationTime(); |
|||
b.ConfigureMultiTenant(); |
|||
|
|||
b.ConfigureMultiTenant(); |
|||
b.HasIndex(p => new { p.TenantId, p.UserId, p.NotificationName }) |
|||
.HasName("IX_Tenant_User_Notification_Name") |
|||
.IsUnique(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.EntityFrameworkCore.Modeling; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.EntityFrameworkCore |
|||
{ |
|||
public class MessageServiceModelBuilderConfigurationOptions : AbpModelBuilderConfigurationOptions |
|||
{ |
|||
public MessageServiceModelBuilderConfigurationOptions( |
|||
[NotNull] string tablePrefix = AbpMessageServiceDbProperties.DefaultTablePrefix, |
|||
[CanBeNull] string schema = AbpMessageServiceDbProperties.DefaultSchema) |
|||
: base( |
|||
tablePrefix, |
|||
schema) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using LINGYUN.Abp.MessageService.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class EfCoreNotificationRepository : EfCoreRepository<MessageServiceDbContext, Notification, long>, |
|||
INotificationRepository, ITransientDependency |
|||
{ |
|||
public EfCoreNotificationRepository( |
|||
IDbContextProvider<MessageServiceDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task DeleteExpritionAsync(int batchCount) |
|||
{ |
|||
var notifications = await DbSet |
|||
.Where(x => x.ExpirationTime <= DateTime.Now) |
|||
.Take(batchCount) |
|||
.ToArrayAsync(); |
|||
|
|||
DbSet.RemoveRange(notifications); |
|||
} |
|||
|
|||
public async Task<Notification> GetByIdAsync(long notificationId) |
|||
{ |
|||
return await DbSet.Where(x => x.NotificationId.Equals(notificationId)).FirstOrDefaultAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
using LINGYUN.Abp.MessageService.EntityFrameworkCore; |
|||
using LINGYUN.Abp.Notifications; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Notifications |
|||
{ |
|||
public class EfCoreUserNotificationRepository : EfCoreRepository<MessageServiceDbContext, UserNotification, long>, |
|||
IUserNotificationRepository, ITransientDependency |
|||
{ |
|||
public EfCoreUserNotificationRepository( |
|||
IDbContextProvider<MessageServiceDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task ChangeUserNotificationReadStateAsync(Guid userId, long notificationId, NotificationReadState readState) |
|||
{ |
|||
var userNofitication = await GetByIdAsync(userId, notificationId); |
|||
userNofitication.ChangeReadState(readState); |
|||
|
|||
DbSet.Update(userNofitication); |
|||
} |
|||
|
|||
public async Task<UserNotification> GetByIdAsync(Guid userId, long notificationId) |
|||
{ |
|||
var userNofitication = await DbSet |
|||
.Where(x => x.NotificationId.Equals(notificationId) && x.UserId.Equals(userId)) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return userNofitication; |
|||
} |
|||
|
|||
public async Task<List<Notification>> GetNotificationsAsync(Guid userId, NotificationReadState readState = NotificationReadState.UnRead, int maxResultCount = 10) |
|||
{ |
|||
|
|||
var userNofitications = await (from un in DbContext.Set<UserNotification>() |
|||
join n in DbContext.Set<Notification>() |
|||
on un.NotificationId equals n.NotificationId |
|||
where un.UserId.Equals(userId) && un.ReadStatus.Equals(readState) |
|||
select n) |
|||
.Take(maxResultCount) |
|||
.ToListAsync(); |
|||
return userNofitications; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
using LINGYUN.Abp.MessageService.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace LINGYUN.Abp.MessageService.Subscriptions |
|||
{ |
|||
public class EfCoreUserSubscribeRepository : EfCoreRepository<MessageServiceDbContext, UserSubscribe, long>, |
|||
IUserSubscribeRepository, ITransientDependency |
|||
{ |
|||
public EfCoreUserSubscribeRepository( |
|||
IDbContextProvider<MessageServiceDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<List<UserSubscribe>> GetSubscribesAsync(string notificationName) |
|||
{ |
|||
var userSubscribes = await DbSet.Where(x => x.NotificationName.Equals(notificationName)).ToListAsync(); |
|||
|
|||
return userSubscribes; |
|||
} |
|||
|
|||
public async Task<UserSubscribe> GetUserSubscribeAsync(string notificationName, Guid userId) |
|||
{ |
|||
var userSubscribe = await DbSet |
|||
.Where(x => x.UserId.Equals(userId) && x.NotificationName.Equals(notificationName)) |
|||
.FirstOrDefaultAsync(); |
|||
|
|||
return userSubscribe; |
|||
} |
|||
|
|||
public async Task<List<string>> GetUserSubscribesAsync(Guid userId) |
|||
{ |
|||
var userSubscribeNames = await DbSet |
|||
.Where(x => x.UserId.Equals(userId)) |
|||
.Select(x => x.NotificationName) |
|||
.ToListAsync(); |
|||
|
|||
return userSubscribeNames; |
|||
} |
|||
|
|||
public async Task<List<Guid>> GetUserSubscribesAsync(string notificationName) |
|||
{ |
|||
var subscribeUsers = await DbSet |
|||
.Where(x => x.NotificationName.Equals(notificationName)) |
|||
.Select(x => x.UserId) |
|||
.ToListAsync(); |
|||
|
|||
return subscribeUsers; |
|||
} |
|||
|
|||
public async Task InsertUserSubscriptionAsync(IEnumerable<UserSubscribe> userSubscribes) |
|||
{ |
|||
await DbSet.AddRangeAsync(userSubscribes); |
|||
} |
|||
|
|||
public async Task<bool> UserSubscribeExistsAysnc(string notificationName, Guid userId) |
|||
{ |
|||
return await DbSet.AnyAsync(x => x.UserId.Equals(userId) && x.NotificationName.Equals(notificationName)); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue