diff --git a/.gitignore b/.gitignore index ce89292..50ff4d8 100644 --- a/.gitignore +++ b/.gitignore @@ -416,3 +416,5 @@ FodyWeavers.xsd *.msix *.msm *.msp + +.DS_Store diff --git a/AspireApp.ApiService/AspireApp.ApiService.csproj b/AspireApp.ApiService/AspireApp.ApiService.csproj new file mode 100644 index 0000000..8a2a7ed --- /dev/null +++ b/AspireApp.ApiService/AspireApp.ApiService.csproj @@ -0,0 +1,19 @@ + + + + net10.0 + enable + enable + + + + + + + + + + + + + diff --git a/AspireApp.ApiService/Entities/Warehouse.cs b/AspireApp.ApiService/Entities/Warehouse.cs new file mode 100644 index 0000000..37609d0 --- /dev/null +++ b/AspireApp.ApiService/Entities/Warehouse.cs @@ -0,0 +1,69 @@ +using System.Text.Json.Serialization; + +namespace AspireApp.ApiService.Entities; + +/// +/// Товар на складе +/// +public class Warehouse +{ + /// + /// Идентификатор + /// + [JsonPropertyName("id")] + public int Id { get; set; } + + /// + /// Наименование товара + /// + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// + /// Категория товара + /// + [JsonPropertyName("category")] + public string? Category { get; set; } + + /// + /// Количество на складе + /// + [JsonPropertyName("stockQuantity")] + public int StockQuantity { get; set; } + + /// + /// Цена за единицу товара + /// + [JsonPropertyName("price")] + public decimal Price { get; set; } + + /// + /// Вес единицы товара + /// + [JsonPropertyName("weight")] + public double Weight { get; set; } + + /// + /// Габариты единицы товара + /// + [JsonPropertyName("dimensions")] + public string? Dimensions { get; set; } + + /// + /// Хрупкий ли товар + /// + [JsonPropertyName("isFragile")] + public bool IsFragile { get; set; } + + /// + /// Дата последней поставки + /// + [JsonPropertyName("lastDeliveryDate")] + public DateOnly LastDeliveryDate { get; set; } + + /// + /// Дата следующей планируемой поставки + /// + [JsonPropertyName("nextDeliveryDate")] + public DateOnly NextDeliveryDate { get; set; } +} \ No newline at end of file diff --git a/AspireApp.ApiService/Generator/IWarehouseCache.cs b/AspireApp.ApiService/Generator/IWarehouseCache.cs new file mode 100644 index 0000000..bc9c977 --- /dev/null +++ b/AspireApp.ApiService/Generator/IWarehouseCache.cs @@ -0,0 +1,19 @@ +using AspireApp.ApiService.Entities; + +namespace AspireApp.ApiService.Generator; + +/// +/// Интерфейс для работы с кэшем товаров +/// +public interface IWarehouseCache +{ + /// + /// Получить товар из кэша по идентификатору + /// + Task GetAsync(int id); + + /// + /// Сохранить товар в кэш + /// + Task SetAsync(Warehouse warehouse); +} \ No newline at end of file diff --git a/AspireApp.ApiService/Generator/IWarehouseGeneratorService.cs b/AspireApp.ApiService/Generator/IWarehouseGeneratorService.cs new file mode 100644 index 0000000..193ab01 --- /dev/null +++ b/AspireApp.ApiService/Generator/IWarehouseGeneratorService.cs @@ -0,0 +1,11 @@ +using AspireApp.ApiService.Entities; + +namespace AspireApp.ApiService.Generator; + +/// +/// Сервис обработки товаров на складе +/// +public interface IWarehouseGeneratorService +{ + Task ProcessWarehouse(int id); +} \ No newline at end of file diff --git a/AspireApp.ApiService/Generator/WarehouseCache.cs b/AspireApp.ApiService/Generator/WarehouseCache.cs new file mode 100644 index 0000000..6e542f4 --- /dev/null +++ b/AspireApp.ApiService/Generator/WarehouseCache.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.Caching.Distributed; +using System.Text.Json; +using AspireApp.ApiService.Entities; + +namespace AspireApp.ApiService.Generator; + +/// +/// Кэширование товаров +/// +public class WarehouseCache( + IDistributedCache cache, + ILogger logger, + IConfiguration configuration) : IWarehouseCache +{ + private readonly TimeSpan _defaultExpiration = int.TryParse(configuration["CacheExpiration"], out var seconds) + ? TimeSpan.FromSeconds(seconds) + : TimeSpan.FromSeconds(3600); + + public async Task GetAsync(int id) + { + var key = $"warehouse_{id}"; + var cached = await cache.GetStringAsync(key); + if (cached == null) + return null; + + try + { + return JsonSerializer.Deserialize(cached); + } + catch (Exception ex) + { + logger.LogError(ex, "Ошибка десериализации товара {Id} из кэша", id); + return null; + } + } + + public async Task SetAsync(Warehouse warehouse) + { + var key = $"warehouse_{warehouse.Id}"; + var options = new DistributedCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = _defaultExpiration + }; + var serialized = JsonSerializer.Serialize(warehouse); + await cache.SetStringAsync(key, serialized, options); + } +} \ No newline at end of file diff --git a/AspireApp.ApiService/Generator/WarehouseGenerator.cs b/AspireApp.ApiService/Generator/WarehouseGenerator.cs new file mode 100644 index 0000000..dbb6893 --- /dev/null +++ b/AspireApp.ApiService/Generator/WarehouseGenerator.cs @@ -0,0 +1,32 @@ +using Bogus; +using AspireApp.ApiService.Entities; + +namespace AspireApp.ApiService.Generator; + +/// +/// Генератор случайных товаров с использованием Bogus +/// +public class WarehouseGenerator +{ + private readonly Faker _faker; + + public WarehouseGenerator() + { + _faker = new Faker() + .RuleFor(w => w.Id, f => f.IndexFaker + 1) // будет перезаписан позже + .RuleFor(w => w.Name, f => f.Commerce.ProductName()) + .RuleFor(w => w.Category, f => f.Commerce.Categories(1)[0]) + .RuleFor(w => w.StockQuantity, f => f.Random.Int(0, 1000)) + .RuleFor(w => w.Price, f => decimal.Parse(f.Commerce.Price())) + .RuleFor(w => w.Weight, f => f.Random.Double(0.1, 50.0)) + .RuleFor(w => w.Dimensions, f => $"{f.Random.Int(10, 100)}x{f.Random.Int(10, 100)}x{f.Random.Int(10, 100)}") + .RuleFor(w => w.IsFragile, f => f.Random.Bool(0.3f)) + .RuleFor(w => w.LastDeliveryDate, f => DateOnly.FromDateTime(f.Date.Past(30))) + .RuleFor(w => w.NextDeliveryDate, f => DateOnly.FromDateTime(f.Date.Future(30))); + } + + /// + /// Генерирует один случайный товар (Id будет перезаписан вызывающим кодом) + /// + public Warehouse Generate() => _faker.Generate(); +} \ No newline at end of file diff --git a/AspireApp.ApiService/Generator/WarehouseGeneratorService.cs b/AspireApp.ApiService/Generator/WarehouseGeneratorService.cs new file mode 100644 index 0000000..cc832d0 --- /dev/null +++ b/AspireApp.ApiService/Generator/WarehouseGeneratorService.cs @@ -0,0 +1,51 @@ +using AspireApp.ApiService.Entities; + +namespace AspireApp.ApiService.Generator; + +/// +/// Служба для запуска юзкейса по обработке товаров на складе +/// +public class WarehouseGeneratorService( + IWarehouseCache warehouseCache, + ILogger logger, + WarehouseGenerator generator) : IWarehouseGeneratorService +{ + public async Task ProcessWarehouse(int id) + { + logger.LogInformation("Обработка товара с Id = {Id} начата", id); + + // Получаем товар из кэша + Warehouse? warehouse = null; + try + { + warehouse = await warehouseCache.GetAsync(id); + if (warehouse != null) + { + logger.LogInformation("Товар {Id} получен из кэша", id); + return warehouse; + } + } + catch (Exception ex) + { + logger.LogWarning(ex, "Не удалось получить товар {Id} из кэша (ошибка игнорируется)", id); + } + + // Если в кэше нет или ошибка — генерируем новый товар + logger.LogInformation("Товар {Id} в кэше не найден или кэш недоступен, запуск генерации", id); + warehouse = generator.Generate(); + warehouse.Id = id; + + // Попытка сохранить в кэш + try + { + logger.LogInformation("Сохранение товара {Id} в кэш", id); + await warehouseCache.SetAsync(warehouse); + } + catch (Exception ex) + { + logger.LogWarning(ex, "Не удалось сохранить товар {Id} в кэш (ошибка игнорируется)", id); + } + + return warehouse; + } +} \ No newline at end of file diff --git a/AspireApp.ApiService/Program.cs b/AspireApp.ApiService/Program.cs new file mode 100644 index 0000000..c900388 --- /dev/null +++ b/AspireApp.ApiService/Program.cs @@ -0,0 +1,35 @@ +using AspireApp.ApiService.Generator; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddServiceDefaults(); + +builder.AddRedisDistributedCache("RedisCache"); + +builder.Services.AddScoped(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); + +builder.Services.AddCors(options => +{ + options.AddDefaultPolicy(policy => + { + policy.WithOrigins("http://localhost:5127") + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); + +var app = builder.Build(); + +app.MapDefaultEndpoints(); + +app.MapGet("/warehouse", async (IWarehouseGeneratorService service, int id) => +{ + var warehouse = await service.ProcessWarehouse(id); + return Results.Ok(warehouse); +}); + +app.UseCors(); + +app.Run(); \ No newline at end of file diff --git a/AspireApp.ApiService/Properties/launchSettings.json b/AspireApp.ApiService/Properties/launchSettings.json new file mode 100644 index 0000000..b78d61e --- /dev/null +++ b/AspireApp.ApiService/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/AspireApp.ApiService/appsettings.Development.json b/AspireApp.ApiService/appsettings.Development.json new file mode 100644 index 0000000..0b120f3 --- /dev/null +++ b/AspireApp.ApiService/appsettings.Development.json @@ -0,0 +1,9 @@ +{"BaseAddress": "http://localhost:53677", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} + diff --git a/AspireApp.ApiService/appsettings.json b/AspireApp.ApiService/appsettings.json new file mode 100644 index 0000000..4d56694 --- /dev/null +++ b/AspireApp.ApiService/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/AspireApp.AppHost/AppHost.cs b/AspireApp.AppHost/AppHost.cs new file mode 100644 index 0000000..c72cd63 --- /dev/null +++ b/AspireApp.AppHost/AppHost.cs @@ -0,0 +1,13 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var cache = builder.AddRedis("RedisCache").WithRedisInsight(containerName: "insight"); + +var service = builder.AddProject("service-api") + .WithReference(cache) + .WaitFor(cache); + +builder.AddProject("client-wasm", "../Client.Wasm/Client.Wasm.csproj") + .WithReference(service) + .WaitFor(service); + +builder.Build().Run(); \ No newline at end of file diff --git a/AspireApp.AppHost/AspireApp.AppHost.csproj b/AspireApp.AppHost/AspireApp.AppHost.csproj new file mode 100644 index 0000000..51f9f6d --- /dev/null +++ b/AspireApp.AppHost/AspireApp.AppHost.csproj @@ -0,0 +1,22 @@ + + + + + Exe + net10.0 + enable + enable + true + 59f7c075-68cd-4f31-89d0-9f0df5a69013 + + + + + + + + + + + + \ No newline at end of file diff --git a/AspireApp.AppHost/Properties/launchSettings.json b/AspireApp.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..266d9e9 --- /dev/null +++ b/AspireApp.AppHost/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17144;http://localhost:15057", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21296", + "DOTNET_DASHBOARD_MCP_ENDPOINT_URL": "https://localhost:23138", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22147" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15057", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19185", + "DOTNET_DASHBOARD_MCP_ENDPOINT_URL": "http://localhost:18108", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20262" + } + } + } +} diff --git a/AspireApp.AppHost/appsettings.Development.json b/AspireApp.AppHost/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/AspireApp.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/AspireApp.AppHost/appsettings.json b/AspireApp.AppHost/appsettings.json new file mode 100644 index 0000000..2185f95 --- /dev/null +++ b/AspireApp.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/AspireApp.ServiceDefaults/.DS_Store b/AspireApp.ServiceDefaults/.DS_Store new file mode 100644 index 0000000..c9f4528 Binary files /dev/null and b/AspireApp.ServiceDefaults/.DS_Store differ diff --git a/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj b/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj new file mode 100644 index 0000000..5e7c773 --- /dev/null +++ b/AspireApp.ServiceDefaults/AspireApp.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/AspireApp.ServiceDefaults/Extensions.cs b/AspireApp.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..e95afc3 --- /dev/null +++ b/AspireApp.ServiceDefaults/Extensions.cs @@ -0,0 +1,111 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder) + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + return builder; + } + + public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder) + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder) + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder) + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/AspireApp.sln b/AspireApp.sln new file mode 100644 index 0000000..180c571 --- /dev/null +++ b/AspireApp.sln @@ -0,0 +1,79 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36811.4 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.Wasm", "Client.Wasm\Client.Wasm.csproj", "{AE7EEA74-2FE0-136F-D797-854FD87E022A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.AppHost", "AspireApp.AppHost\AspireApp.AppHost.csproj", "{65F3885F-1D3D-497E-ADFA-2553D258B383}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.ApiService", "AspireApp.ApiService\AspireApp.ApiService.csproj", "{14A10063-0D85-45A5-A1B8-413A9C98F82A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspireApp.ServiceDefaults", "AspireApp.ServiceDefaults\AspireApp.ServiceDefaults.csproj", "{8384C807-5F88-4228-8803-3A7F440AAA40}" +EndProject + +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|x64.ActiveCfg = Debug|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|x64.Build.0 = Debug|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|x86.ActiveCfg = Debug|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|x86.Build.0 = Debug|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.Build.0 = Release|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|x64.ActiveCfg = Release|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|x64.Build.0 = Release|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|x86.ActiveCfg = Release|Any CPU + {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|x86.Build.0 = Release|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Debug|x64.ActiveCfg = Debug|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Debug|x64.Build.0 = Debug|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Debug|x86.ActiveCfg = Debug|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Debug|x86.Build.0 = Debug|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Release|Any CPU.Build.0 = Release|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Release|x64.ActiveCfg = Release|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Release|x64.Build.0 = Release|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Release|x86.ActiveCfg = Release|Any CPU + {65F3885F-1D3D-497E-ADFA-2553D258B383}.Release|x86.Build.0 = Release|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Debug|x64.ActiveCfg = Debug|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Debug|x64.Build.0 = Debug|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Debug|x86.ActiveCfg = Debug|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Debug|x86.Build.0 = Debug|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Release|Any CPU.Build.0 = Release|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Release|x64.ActiveCfg = Release|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Release|x64.Build.0 = Release|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Release|x86.ActiveCfg = Release|Any CPU + {14A10063-0D85-45A5-A1B8-413A9C98F82A}.Release|x86.Build.0 = Release|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Debug|x64.ActiveCfg = Debug|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Debug|x64.Build.0 = Debug|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Debug|x86.ActiveCfg = Debug|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Debug|x86.Build.0 = Debug|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Release|Any CPU.Build.0 = Release|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Release|x64.ActiveCfg = Release|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Release|x64.Build.0 = Release|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Release|x86.ActiveCfg = Release|Any CPU + {8384C807-5F88-4228-8803-3A7F440AAA40}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {90FE6B04-8381-437E-893A-FEBA1DA10AEE} + EndGlobalSection +EndGlobal \ No newline at end of file diff --git a/Client.Wasm/.DS_Store b/Client.Wasm/.DS_Store new file mode 100644 index 0000000..7444364 Binary files /dev/null and b/Client.Wasm/.DS_Store differ diff --git a/Client.Wasm/Client.Wasm.csproj b/Client.Wasm/Client.Wasm.csproj index 0ba9f90..bacb191 100644 --- a/Client.Wasm/Client.Wasm.csproj +++ b/Client.Wasm/Client.Wasm.csproj @@ -1,21 +1,23 @@  - net8.0 + net10.0 enable enable + + - - + - - + + + diff --git a/Client.Wasm/Components/DataCard.razor b/Client.Wasm/Components/DataCard.razor index c646a83..abdf839 100644 --- a/Client.Wasm/Components/DataCard.razor +++ b/Client.Wasm/Components/DataCard.razor @@ -1,4 +1,4 @@ -@inject IConfiguration Configuration +@inject IConfiguration Configuration @inject HttpClient Client @@ -7,16 +7,16 @@ Характеристики текущего объекта - +
- + # Характеристика Значение - + - @if(Value is null) + @if (Value is null) { 1 @@ -40,7 +40,7 @@
- + Запросить новый объект @@ -51,7 +51,7 @@ Идентификатор нового объекта: - + @@ -71,4 +71,4 @@ Value = await Client.GetFromJsonAsync($"{baseAddress}?id={Id}", new JsonSerializerOptions { }); StateHasChanged(); } -} +} \ No newline at end of file diff --git a/Client.Wasm/Components/StudentCard.razor b/Client.Wasm/Components/StudentCard.razor index 661f118..cce9a0c 100644 --- a/Client.Wasm/Components/StudentCard.razor +++ b/Client.Wasm/Components/StudentCard.razor @@ -4,10 +4,10 @@ - Номер №X "Название лабораторной" - Вариант №Х "Название варианта" - Выполнена Фамилией Именем 65ХХ - Ссылка на форк + Номер №1 "Кэширование" + Вариант №22 "Товары на складе" + Выполнена Карпачевой Полиной 6512 + Ссылка на форк diff --git a/Client.Wasm/Program.cs b/Client.Wasm/Program.cs index a182a92..7e2e1c2 100644 --- a/Client.Wasm/Program.cs +++ b/Client.Wasm/Program.cs @@ -1,5 +1,5 @@ using Blazorise; -using Blazorise.Bootstrap; +using Blazorise.Bootstrap5; using Blazorise.Icons.FontAwesome; using Client.Wasm; using Microsoft.AspNetCore.Components.Web; @@ -10,8 +10,9 @@ builder.RootComponents.Add("head::after"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); -builder.Services.AddBlazorise(options => { options.Immediate = true; }) - .AddBootstrapProviders() +builder.Services + .AddBlazorise(options => { options.Immediate = true; }) + .AddBootstrap5Providers() .AddFontAwesomeIcons(); -await builder.Build().RunAsync(); +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/Client.Wasm/Properties/launchSettings.json b/Client.Wasm/Properties/launchSettings.json index 0d824ea..5bda7b5 100644 --- a/Client.Wasm/Properties/launchSettings.json +++ b/Client.Wasm/Properties/launchSettings.json @@ -11,7 +11,6 @@ "profiles": { "http": { "commandName": "Project", - "dotnetRunMessages": true, "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", "applicationUrl": "http://localhost:5127", @@ -19,16 +18,6 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7282;http://localhost:5127", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, @@ -38,4 +27,4 @@ } } } -} +} \ No newline at end of file diff --git a/Client.Wasm/_Imports.razor b/Client.Wasm/_Imports.razor index 31e16a8..48f9eab 100644 --- a/Client.Wasm/_Imports.razor +++ b/Client.Wasm/_Imports.razor @@ -11,4 +11,5 @@ @using Blazorise @using Blazorise.Components @using System.Text.Json -@using System.Text.Json.Nodes \ No newline at end of file +@using System.Text.Json.Nodes +@using Microsoft.Extensions.DependencyInjection \ No newline at end of file diff --git a/Client.Wasm/wwwroot/Client.styles.css b/Client.Wasm/wwwroot/Client.styles.css new file mode 100644 index 0000000..e69de29 diff --git a/Client.Wasm/wwwroot/appsettings.json b/Client.Wasm/wwwroot/appsettings.json index d1fe7ab..264b374 100644 --- a/Client.Wasm/wwwroot/appsettings.json +++ b/Client.Wasm/wwwroot/appsettings.json @@ -1,10 +1,9 @@ { + "BaseAddress": "http://localhost:5000/warehouse", "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } - }, - "AllowedHosts": "*", - "BaseAddress": "" -} + } +} \ No newline at end of file diff --git a/CloudDevelopment.sln b/CloudDevelopment.sln deleted file mode 100644 index cb48241..0000000 --- a/CloudDevelopment.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.14.36811.4 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client.Wasm", "Client.Wasm\Client.Wasm.csproj", "{AE7EEA74-2FE0-136F-D797-854FD87E022A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE7EEA74-2FE0-136F-D797-854FD87E022A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {90FE6B04-8381-437E-893A-FEBA1DA10AEE} - EndGlobalSection -EndGlobal