-
Notifications
You must be signed in to change notification settings - Fork 25
Ле Хань Хоанг 6513 Лаб. 1 #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8d67c74
64130a5
69e71ee
c25c998
430191b
6b54ce1
02cf2eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,5 +6,5 @@ | |
| } | ||
| }, | ||
| "AllowedHosts": "*", | ||
| "BaseAddress": "" | ||
| "BaseAddress": "https://localhost:7266/api/inventory" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| using System.Text.Json; | ||
| using Inventory.ApiService.Entity; | ||
| using Inventory.ApiService.Generation; | ||
| using Microsoft.Extensions.Caching.Distributed; | ||
|
|
||
| namespace Inventory.ApiService.Cache; | ||
|
|
||
| public class InventoryCache | ||
| { | ||
| private static readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); | ||
| private readonly IDistributedCache _cache; | ||
| private readonly Generator _generator; | ||
| private readonly ILogger<InventoryCache> _logger; | ||
|
|
||
| public InventoryCache(IDistributedCache cache, Generator generator, ILogger<InventoryCache> logger) | ||
| { | ||
| _cache = cache; | ||
| _generator = generator; | ||
| _logger = logger; | ||
| } | ||
|
Comment on lines
+10
to
+20
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Здесь и далее по коду - использовать праймари конструктор |
||
|
|
||
| public async Task<IReadOnlyList<Product>> GetAsync(int count, int? seed, CancellationToken ct) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я хз, откуда в уже третьем pr, который я смотрю, используется seed. В описании задачи он не фигурирует. Необходимости передавать его в качестве параметра, да еще и в службу, которая запускает юз-кейс генерации данных, я тоже не вижу Также для меня загадка, зачем генерировать коллекцию объектов, если по заданию требуется создать всего один |
||
| { | ||
| var cacheKey = $"inventory:count={count}:seed={(seed?.ToString() ?? "null")}"; | ||
|
|
||
| var cached = await _cache.GetStringAsync(cacheKey, ct); | ||
|
|
||
| if (cached is not null) | ||
| { | ||
| _logger.LogInformation("Inventory cache HIT {CacheKey}", cacheKey); | ||
| return JsonSerializer.Deserialize<List<Product>>(cached, _jsonOptions) ?? new List<Product>(); | ||
| } | ||
|
|
||
|
Comment on lines
+26
to
+33
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Исключение в работе кэша сложит тебе весь софт. Такого быть не должно, ошибки в работе кэша или его недоступность не должны влиять на работоспособность основного приложения. |
||
| _logger.LogInformation("Inventory cache MISS {CacheKey}. Generating {Count} items.", cacheKey, count); | ||
|
|
||
| var sw = System.Diagnostics.Stopwatch.StartNew(); | ||
| var data = Generator.Generate(count, seed); | ||
|
|
||
| sw.Stop(); | ||
|
|
||
| _logger.LogInformation("Generated inventory {Count} items in {ElapsedMs}ms", data.Count, sw.ElapsedMilliseconds); | ||
|
|
||
| var json = JsonSerializer.Serialize(data, _jsonOptions); | ||
|
|
||
| await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) }, ct); | ||
|
Comment on lines
+43
to
+45
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Аналогично - тут исключение в работе кэша положит весь софт |
||
|
|
||
| return data; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| using Microsoft.AspNetCore.Mvc; | ||
| using Inventory.ApiService.Entity; | ||
| using Inventory.ApiService.Cache; | ||
|
|
||
| namespace Inventory.ApiService.Controllers; | ||
|
|
||
| [ApiController] | ||
| [Route("api/inventory")] | ||
| public class InventoryController(InventoryCache _cache) : ControllerBase | ||
| { | ||
| [HttpGet] | ||
| public async Task<ActionResult<Product>> Get(int? id) | ||
|
Comment on lines
+11
to
+12
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Нет атрибутов возвращаемых респонс кодов |
||
| { | ||
| var index = id ?? 0; | ||
|
|
||
| if (index < 0) | ||
| return BadRequest("id must be >= 0"); | ||
|
|
||
| var data = await _cache.GetAsync(100, seed: 1, CancellationToken.None); | ||
|
|
||
| var product = data.ElementAtOrDefault(index); | ||
|
Comment on lines
+14
to
+21
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| return product is not null? Ok(product): NotFound(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| namespace Inventory.ApiService.Entity; | ||
|
|
||
| /// <summary> | ||
| /// Класс, представляющий товар на складе | ||
| /// </summary> | ||
| public class Product | ||
| { | ||
| /// <summary> | ||
| /// Идентификатор в системе | ||
| /// </summary> | ||
| public int Id { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Наименование товара | ||
| /// </summary> | ||
| public string NameProduct { get; set; } = string.Empty; | ||
|
|
||
| /// <summary> | ||
| /// Категория товара | ||
| /// </summary> | ||
| public string Category { get; set; } = string.Empty; | ||
|
|
||
| /// <summary> | ||
| /// Количество на складе | ||
| /// </summary> | ||
| public int Quantity { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Цена за единицу товара | ||
| /// </summary> | ||
| public decimal Price { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Вес единицы товара | ||
| /// </summary> | ||
| public double Weight { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Габариты единицы товара | ||
| /// </summary> | ||
| public string Dimension { get; set; } = string.Empty; | ||
|
|
||
| /// <summary> | ||
| /// Товар хрупкий | ||
| /// </summary> | ||
| public bool IsFragile { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Дата последней поставки | ||
| /// </summary> | ||
| public DateOnly LastDeliveryDate { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Дата следующей поставки | ||
| /// </summary> | ||
| public DateOnly NextDeliveryDate { get; set; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| using Bogus; | ||
| using Inventory.ApiService.Entity; | ||
|
|
||
| namespace Inventory.ApiService.Generation; | ||
|
|
||
| public class Generator | ||
| { | ||
| public static List<Product> Generate(int count, int? seed = null) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Сид либо вынести в параметры софта, либо избавиться от него целиком
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Метод переделать под генерацию единственного значения |
||
| { | ||
| if (seed.HasValue) | ||
| Randomizer.Seed = new Random(seed.Value); | ||
|
|
||
| var faker = new Faker<Product>() | ||
| .RuleFor(x => x.Id, f => f.IndexFaker) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В качестве идентификатора генерируемой сущности нудно присваивать id, по которому пришел запрос |
||
| .RuleFor(x => x.NameProduct, f => f.Commerce.ProductName()) | ||
| .RuleFor(x => x.Category, f => f.Commerce.Categories(1)[0]) | ||
| .RuleFor(x => x.Quantity, f => f.Random.Int(0, 1000)) | ||
| .RuleFor(x => x.Price, f => Math.Round(f.Random.Decimal(1, 10000), 2)) | ||
| .RuleFor(x => x.Weight, f => Math.Round(f.Random.Double(0.1, 100), 2)) | ||
| .RuleFor(x => x.Dimension, f => | ||
| { | ||
| var a = f.Random.Int(1, 200); | ||
| var b = f.Random.Int(1, 200); | ||
| var c = f.Random.Int(1, 200); | ||
| return $"{a}×{b}×{c} cm"; | ||
| }) | ||
| .RuleFor(x => x.IsFragile, f => f.Random.Bool()) | ||
| .RuleFor(x => x.LastDeliveryDate, f => | ||
| { | ||
| var date = f.Date.Past(2); | ||
| return DateOnly.FromDateTime(date); | ||
| }) | ||
| .RuleFor(x => x.NextDeliveryDate, (f, item) => | ||
| { | ||
| var lastDate = item.LastDeliveryDate.ToDateTime(TimeOnly.MinValue); | ||
| var nextDate = f.Date.Between(lastDate, lastDate.AddMonths(6)); | ||
| return DateOnly.FromDateTime(nextDate); | ||
| }); | ||
|
Comment on lines
+13
to
+38
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Фейкер вынести как статическое ридонли поле |
||
|
|
||
| return faker.Generate(count); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Inventory.ServiceDefaults\Inventory.ServiceDefaults.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Aspire.StackExchange.Redis.DistributedCaching" Version="13.1.1" /> | ||
| <PackageReference Include="Bogus" Version="35.6.5" /> | ||
| <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.1" /> | ||
| <PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.4" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| using Inventory.ApiService.Cache; | ||
| using Inventory.ApiService.Generation; | ||
| using Inventory.ServiceDefaults; | ||
|
|
||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
||
| builder.AddServiceDefaults(); | ||
|
|
||
| // Cache | ||
| builder.AddRedisDistributedCache("cache"); | ||
|
|
||
| builder.Services.AddControllers(); | ||
| builder.Services.AddEndpointsApiExplorer(); | ||
| builder.Services.AddSwaggerGen(); | ||
|
|
||
| // CORS | ||
| builder.Services.AddCors(options => | ||
| { | ||
| options.AddPolicy("client", policy => | ||
| { | ||
| policy.AllowAnyOrigin() | ||
| .WithMethods("GET") | ||
| .WithHeaders("Content-Type"); | ||
| }); | ||
| }); | ||
|
|
||
| // DI | ||
| builder.Services.AddSingleton<Generator>(); | ||
| builder.Services.AddScoped<InventoryCache>(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Выделить интерфейс из службы и зарегистрировать в di по нему |
||
|
|
||
| var app = builder.Build(); | ||
|
|
||
| app.UseSwagger(); | ||
| app.UseSwaggerUI(); | ||
|
|
||
| app.UseCors("client"); | ||
|
|
||
| app.MapControllers(); | ||
| app.MapDefaultEndpoints(); | ||
|
|
||
| app.Run(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| { | ||
| "$schema": "https://json.schemastore.org/launchsettings.json", | ||
| "profiles": { | ||
| "http": { | ||
| "commandName": "Project", | ||
| "dotnetRunMessages": true, | ||
| "launchBrowser": false, | ||
| "applicationUrl": "http://localhost:5339", | ||
| "environmentVariables": { | ||
| "ASPNETCORE_ENVIRONMENT": "Development" | ||
| } | ||
| }, | ||
| "https": { | ||
| "commandName": "Project", | ||
| "dotnetRunMessages": true, | ||
| "launchBrowser": false, | ||
| "applicationUrl": "https://localhost:7266;http://localhost:5339", | ||
| "environmentVariables": { | ||
| "ASPNETCORE_ENVIRONMENT": "Development" | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "Logging": { | ||
| "LogLevel": { | ||
| "Default": "Information", | ||
| "Microsoft.AspNetCore": "Warning" | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "Logging": { | ||
| "LogLevel": { | ||
| "Default": "Information", | ||
| "Microsoft.AspNetCore": "Warning" | ||
| } | ||
| }, | ||
| "AllowedHosts": "*" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| var builder = DistributedApplication.CreateBuilder(args); | ||
|
|
||
| var cache = builder.AddRedis("cache"); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Нет запуска redis commander или redis insights |
||
|
|
||
| var api = builder.AddProject<Projects.Inventory_ApiService>("apiservice") | ||
| .WithReference(cache) | ||
| .WaitFor(cache); | ||
|
|
||
|
|
||
| var client = builder.AddProject<Projects.Client_Wasm>("client-wasm") | ||
| .WithExternalHttpEndpoints() | ||
| .WithHttpHealthCheck("/health") | ||
| .WaitFor(api); | ||
|
|
||
| builder.Build().Run(); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <Project Sdk="Aspire.AppHost.Sdk/13.1.1"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <UserSecretsId>c2ca5822-3bae-42fc-9a94-9efbb7235036</UserSecretsId> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\Client.Wasm\Client.Wasm.csproj" /> | ||
| <ProjectReference Include="..\Inventory.ApiService\Inventory.ApiService.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Aspire.Hosting.Redis" Version="13.1.1" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Здесь и далее по коду - добавить саммари для всех служб и методов