Files
BI/server/DongfangHydro.Dashboard.Api/Program.cs
T

166 lines
5.4 KiB
C#

using DongfangHydro.Dashboard.Api.Contracts;
using DongfangHydro.Dashboard.Api.Data;
using DongfangHydro.Dashboard.Api.Importing;
using DongfangHydro.Dashboard.Api.Realtime;
using DongfangHydro.Dashboard.Api.Services;
using Microsoft.EntityFrameworkCore;
using System.Text.Json;
var importCommand = BomImportCommand.Parse(args);
var builder = WebApplication.CreateBuilder(importCommand is null ? args : []);
if (importCommand is not null)
{
builder.Configuration.AddUserSecrets<Program>(optional: true);
}
var connectionString = builder.Configuration.GetConnectionString("DashboardDb")
?? throw new InvalidOperationException("Connection string 'DashboardDb' is not configured.");
builder.Services.AddDbContext<DashboardDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddScoped<ProgressAggregationService>();
builder.Services.AddScoped<DashboardSeeder>();
builder.Services.AddScoped<DashboardQueryService>();
builder.Services.AddScoped<ProgressUpdateService>();
builder.Services.AddScoped<BomImportService>();
builder.Services.AddSignalR();
builder.Services.AddSingleton<IDashboardNotifier, SignalRDashboardNotifier>();
builder.Services.AddSingleton<DashboardUpdateQueue>();
builder.Services.AddSingleton<IDashboardUpdateQueue>(services =>
services.GetRequiredService<DashboardUpdateQueue>());
builder.Services.AddHostedService(services => services.GetRequiredService<DashboardUpdateQueue>());
builder.Services.AddHealthChecks();
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
var origins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>() ?? [];
if (origins.Length == 0)
{
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
return;
}
policy.WithOrigins(origins).AllowAnyHeader().AllowAnyMethod().AllowCredentials();
});
});
var app = builder.Build();
if (importCommand is not null)
{
var report = await BomImportCommandRunner.RunAsync(app.Services, importCommand, CancellationToken.None);
Console.WriteLine(JsonSerializer.Serialize(report, new JsonSerializerOptions { WriteIndented = true }));
return;
}
app.UseCors();
app.MapHealthChecks("/health");
app.MapGet("/api/dashboard/overview", async (
DashboardQueryService service,
CancellationToken cancellationToken) =>
Results.Ok(await service.GetOverviewAsync(cancellationToken)));
app.MapGet("/api/orders", async (
string? keyword,
string? status,
Guid? lineId,
int? page,
int? pageSize,
DashboardQueryService service,
CancellationToken cancellationToken) =>
Results.Ok(await service.GetOrdersAsync(
keyword,
status,
lineId,
page ?? 1,
pageSize ?? 20,
cancellationToken)));
app.MapGet("/api/orders/{orderId:guid}", async (
Guid orderId,
DashboardQueryService service,
CancellationToken cancellationToken) =>
{
var order = await service.GetOrderAsync(orderId, cancellationToken);
return order is null ? Results.NotFound() : Results.Ok(order);
});
app.MapGet("/api/orders/{orderId:guid}/tree", async (
Guid orderId,
DashboardQueryService service,
CancellationToken cancellationToken) =>
{
var tree = await service.GetTreeAsync(orderId, cancellationToken);
return tree is null ? Results.NotFound() : Results.Ok(tree);
});
app.MapGet("/api/orders/{orderId:guid}/trend", async (
Guid orderId,
int? limit,
DashboardQueryService service,
CancellationToken cancellationToken) =>
Results.Ok(await service.GetTrendAsync(orderId, limit ?? 12, cancellationToken)));
app.MapGet("/api/orders/{orderId:guid}/material-quotas", async (
Guid orderId,
string? category,
string? keyword,
int? page,
int? pageSize,
DashboardQueryService service,
CancellationToken cancellationToken) =>
Results.Ok(await service.GetMaterialQuotasAsync(
orderId,
category,
keyword,
page ?? 1,
pageSize ?? 50,
cancellationToken)));
app.MapGet("/api/risk-events", async (
Guid? orderId,
int? limit,
DashboardQueryService service,
CancellationToken cancellationToken) =>
Results.Ok(await service.GetRiskEventsAsync(orderId, limit ?? 20, cancellationToken)));
app.MapGet("/api/delay-top", async (
int? limit,
DashboardQueryService service,
CancellationToken cancellationToken) =>
Results.Ok(await service.GetDelayTopAsync(limit ?? 5, cancellationToken)));
app.MapPatch("/api/nodes/{nodeId:guid}/progress", async (
Guid nodeId,
UpdateNodeProgressRequest request,
ProgressUpdateService service,
CancellationToken cancellationToken) =>
{
try
{
var result = await service.UpdateAsync(nodeId, request, cancellationToken);
return result is null ? Results.NotFound() : Results.Ok(result);
}
catch (ArgumentException exception)
{
return Results.ValidationProblem(new Dictionary<string, string[]>
{
["progress"] = [exception.Message],
});
}
catch (DbUpdateConcurrencyException)
{
return Results.Conflict(new
{
code = "progress_conflict",
message = "The production node changed after it was loaded. Reload the node and retry.",
});
}
});
app.MapHub<DashboardHub>("/hubs/dashboard");
await DatabaseInitializer.InitializeAsync(app);
app.Run();
public partial class Program;