重构代码并抽取基础构建库

This commit is contained in:
administrator committed 2025-03-20 22:13:45 +08:00
1 parent 9e6b5be92c
commit ccf1e7672d
55 files changed
+398 -104

No files matched your search

@@ -0,0 +1,29 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
using Dapr;
using System.Text.Json.Serialization;
namespace HelloShop.EventBus.Dapr
{
public class DaprCloudEvent<TData>(TData data) : CloudEvent<TData>(data)
{
/// <summary>
/// CloudEvent 'pubsubname' attribute.
/// </summary>
[JsonPropertyName("pubsubname")]
public required string PubSubName { get; init; }
/// <summary>
/// CloudEvent 'topic' attribute.
/// </summary>
[JsonPropertyName("topic")]
public required string Topic { get; init; }
/// <summary>
/// CloudEvent 'time' attribute.
/// </summary>
[JsonPropertyName("time")]
public required DateTimeOffset Time { get; init; }
}
}
@@ -0,0 +1,25 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
using Dapr.Client;
using HelloShop.EventBus.Abstractions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace HelloShop.EventBus.Dapr
{
public class DaprEventBus(DaprClient daprClient, IOptions<DaprEventBusOptions> options, ILogger<DaprEventBus> logger) : IEventBus
{
public async Task PublishAsync(DistributedEvent @event, CancellationToken cancellationToken = default)
{
string pubSubName = options.Value.PubSubName;
string topicName = @event.GetType().Name;
logger.LogInformation("Publishing event {@Event} to {PubsubName}.{TopicName}", @event, pubSubName, topicName);
object? data = Convert.ChangeType(@event, @event.GetType());
await daprClient.PublishEventAsync(pubSubName, topicName, data, cancellationToken);
}
}
}
@@ -0,0 +1,92 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
using HelloShop.EventBus.Abstractions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Text.Json;
namespace HelloShop.EventBus.Dapr
{
public static class DaprEventBusExtensions
{
private const string DefaultSectionName = "DaprEventBus";
public static IEventBusBuilder AddDaprEventBus(this IHostApplicationBuilder builder, string sectionName = DefaultSectionName)
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.AddDaprClient();
DaprEventBusOptions daprOptions = new();
builder.Configuration.GetSection(sectionName).Bind(daprOptions);
builder.Services.AddSingleton(Options.Create(daprOptions));
builder.Services.AddSingleton<IEventBus, DaprEventBus>();
if (daprOptions.RequireAuthenticatedDaprApiToken)
{
builder.Services.AddAuthentication().AddDapr();
builder.Services.Configure<AuthorizationOptions>(options => options.AddDapr());
}
return new DistributedEventBusBuilder(builder.Services);
}
private class DistributedEventBusBuilder(IServiceCollection services) : IEventBusBuilder
{
public IServiceCollection Services => services;
}
public static WebApplication MapDaprEventBus(this WebApplication app)
{
ArgumentNullException.ThrowIfNull(app);
var eventBusOptions = app.Services.GetRequiredService<IOptions<EventBusOptions>>().Value;
var daprEventBusOptions = app.Services.GetRequiredService<IOptions<DaprEventBusOptions>>().Value;
RouteHandlerBuilder routeHandler = app.MapPost($"/api/DistributedEvents", async (DaprCloudEvent<JsonElement> cloudEvent, IHttpContextAccessor contextAccessor) =>
{
var httpContext = contextAccessor.HttpContext ?? throw new InvalidOperationException("HTTP context not available.");
if (string.IsNullOrWhiteSpace(cloudEvent.Topic) || !eventBusOptions.EventTypes.TryGetValue(cloudEvent.Topic, out Type? eventType) || eventType is null)
{
return Results.NotFound();
}
var jsonOptions = httpContext.RequestServices.GetRequiredService<IOptions<Microsoft.AspNetCore.Mvc.JsonOptions>>().Value;
if (JsonSerializer.Deserialize(cloudEvent.Data.GetRawText(), eventType, jsonOptions.JsonSerializerOptions) is not DistributedEvent @event)
{
return Results.BadRequest();
}
foreach (var handler in httpContext.RequestServices.GetKeyedServices<IDistributedEventHandler>(eventType))
{
await handler.HandleAsync(@event);
}
return Results.Ok();
}).WithTags(nameof(DistributedEvent));
app.MapSubscribeHandler();
foreach (var subscription in eventBusOptions.EventTypes)
{
routeHandler.WithTopic(daprEventBusOptions.PubSubName, subscription.Key, enableRawPayload: false);
}
return app;
}
}
}
@@ -0,0 +1,14 @@
// Copyright (c) HelloShop Corporation. All rights reserved.
// See the license file in the project root for more information.
namespace HelloShop.EventBus.Dapr
{
public class DaprEventBusOptions
{
public string PubSubName { get; set; } = "event-bus-pubsub";
public bool RequireAuthenticatedDaprApiToken { get; set; } = false;
public string? DaprApiToken { get; set; }
}
}
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapr.AspNetCore" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HelloShop.EventBus.Abstractions\HelloShop.EventBus.Abstractions.csproj" />
</ItemGroup>
</Project>