feat: initialize manufacturing progress dashboard
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user