Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Client.Wasm/Components/StudentCard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
</CardHeader>
<CardBody>
<UnorderedList Unstyled>
<UnorderedListItem>Номер <Strong>№X "Название лабораторной"</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№Х "Название варианта"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Фамилией Именем 65ХХ</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://puginarug.com/">Ссылка на форк</Link></UnorderedListItem>
<UnorderedListItem>Номер <Strong>№1 "Кэширование"</Strong></UnorderedListItem>
<UnorderedListItem>Вариант <Strong>№2 "Кредитная заявка"</Strong></UnorderedListItem>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вариант 15

<UnorderedListItem>Выполнил <Strong>Сукач Данил 6511</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/DanilSukach/cloud-development.git">Ссылка на форк</Link></UnorderedListItem>
</UnorderedList>
</CardBody>
</Card>
2 changes: 1 addition & 1 deletion Client.Wasm/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
}
},
"AllowedHosts": "*",
"BaseAddress": "https://localhost:7170/land-plot"
"BaseAddress": "https://localhost:7084/api/Credit"
}
40 changes: 38 additions & 2 deletions CloudDevelopment.sln
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36811.4
# Visual Studio Version 18
VisualStudioVersion = 18.3.11505.172 d18.3
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}") = "CreditApp.AppHost", "CreditApp\CreditApp.AppHost\CreditApp.AppHost.csproj", "{595C6305-ACA3-4E87-8CBF-27A4C92BAE97}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.ServiceDefaults", "CreditApp\CreditApp.ServiceDefaults\CreditApp.ServiceDefaults.csproj", "{A7B14B5D-B738-B77E-404F-CF5182E30A41}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.Api", "CreditApp.Api\CreditApp.Api.csproj", "{E7D4CA8B-53EA-9676-D96D-BE2F0CB11054}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.Domain", "CreditApp.Domain\CreditApp.Domain.csproj", "{2B736CD0-1260-4F6F-854F-5F35E0CBC260}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.Application", "CreditApp.Application\CreditApp.Application.csproj", "{69FCFAEE-907C-4365-897A-EF120A644131}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.Infrastructure", "CreditApp.Infrastructure\CreditApp.Infrastructure.csproj", "{C5A26B57-58ED-45E9-9371-AAF64F30C3A8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +27,30 @@ Global
{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
{595C6305-ACA3-4E87-8CBF-27A4C92BAE97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{595C6305-ACA3-4E87-8CBF-27A4C92BAE97}.Debug|Any CPU.Build.0 = Debug|Any CPU
{595C6305-ACA3-4E87-8CBF-27A4C92BAE97}.Release|Any CPU.ActiveCfg = Release|Any CPU
{595C6305-ACA3-4E87-8CBF-27A4C92BAE97}.Release|Any CPU.Build.0 = Release|Any CPU
{A7B14B5D-B738-B77E-404F-CF5182E30A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7B14B5D-B738-B77E-404F-CF5182E30A41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7B14B5D-B738-B77E-404F-CF5182E30A41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7B14B5D-B738-B77E-404F-CF5182E30A41}.Release|Any CPU.Build.0 = Release|Any CPU
{E7D4CA8B-53EA-9676-D96D-BE2F0CB11054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7D4CA8B-53EA-9676-D96D-BE2F0CB11054}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7D4CA8B-53EA-9676-D96D-BE2F0CB11054}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7D4CA8B-53EA-9676-D96D-BE2F0CB11054}.Release|Any CPU.Build.0 = Release|Any CPU
{2B736CD0-1260-4F6F-854F-5F35E0CBC260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B736CD0-1260-4F6F-854F-5F35E0CBC260}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B736CD0-1260-4F6F-854F-5F35E0CBC260}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B736CD0-1260-4F6F-854F-5F35E0CBC260}.Release|Any CPU.Build.0 = Release|Any CPU
{69FCFAEE-907C-4365-897A-EF120A644131}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69FCFAEE-907C-4365-897A-EF120A644131}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69FCFAEE-907C-4365-897A-EF120A644131}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69FCFAEE-907C-4365-897A-EF120A644131}.Release|Any CPU.Build.0 = Release|Any CPU
{C5A26B57-58ED-45E9-9371-AAF64F30C3A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C5A26B57-58ED-45E9-9371-AAF64F30C3A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C5A26B57-58ED-45E9-9371-AAF64F30C3A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C5A26B57-58ED-45E9-9371-AAF64F30C3A8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
36 changes: 36 additions & 0 deletions CreditApp.Api/Controllers/CreditController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using CreditApp.Application.Interfaces;
using Microsoft.AspNetCore.Mvc;

namespace CreditApp.Api.Controllers;

/// <summary>
/// Контроллер для работы с кредитными заявками через HTTP API.
/// Реализует конечную точку получения заявки по идентификатору.
/// <param name="creditService">Сервис для получения данных кредитных заявок.</param>
/// <param name="logger">Логгер для записи событий и ошибок.</param>
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class CreditController(
ICreditService creditService,
ILogger<CreditController> logger
) : ControllerBase
{
/// <summary>
/// Получает кредитную заявку по идентификатору.
/// </summary>
/// <param name="id">Идентификатор запрашиваемой заявки (передаётся в строке запроса).</param>
/// <param name="ct">Токен отмены для асинхронной операции.</param>
/// <returns>HTTP 200 с объектом заявки при успешном получении.</returns>
[HttpGet]
public async Task<IActionResult> Get([FromQuery] int id, CancellationToken ct)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если использовать сваггер, то нужно нормально настроить генерацию OpenApi:

Task<ActionResult<CreditApplication>>

Только после этого нужно будет добавить еще xml файл из CreditApp.Domain, чтобы у CreditApplication в OpenApi было описание

Можно так же накинуть атрибуты, по типу ProduceResponseType

{
logger.LogInformation("Request for credit {CreditId} started", id);

var result = await creditService.GetAsync(id, ct);

logger.LogInformation("Request for credit {CreditId} completed", id);

return Ok(result);
}
}
24 changes: 24 additions & 0 deletions CreditApp.Api/CreditApp.Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Aspire.StackExchange.Redis" Version="13.1.1" />
<PackageReference Include="Aspire.StackExchange.Redis.DistributedCaching" Version="13.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CreditApp.Application\CreditApp.Application.csproj" />
<ProjectReference Include="..\CreditApp.Domain\CreditApp.Domain.csproj" />
<ProjectReference Include="..\CreditApp.Infrastructure\CreditApp.Infrastructure.csproj" />
<ProjectReference Include="..\CreditApp\CreditApp.ServiceDefaults\CreditApp.ServiceDefaults.csproj" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Референс на проект есть, но код из него нигде не используется

</ItemGroup>

</Project>
51 changes: 51 additions & 0 deletions CreditApp.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using CreditApp.Application.Interfaces;
using CreditApp.Application.Services;
using CreditApp.Infrastructure.Generators;
using Microsoft.OpenApi;
using System.Reflection;

var builder = WebApplication.CreateBuilder(args);

builder.AddRedisDistributedCache("credit-cache");

var centralBankRate = builder.Configuration.GetValue<double>("CreditGenerator:CentralBankRate", 16.0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<double> тут не нужен, тип по дефолтному значение подтягивается


builder.Services.AddSingleton<ICreditApplicationGenerator>(
new CreditApplicationGenerator(centralBankRate));
Comment on lines +13 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А вообще лучше так не делать, если в сервисе появятся зависимости от других сервисов, то придется вручную их тут прописывать


builder.Services.AddScoped<ICreditService, CreditService>();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});

builder.Services.AddCors(options =>
{
options.AddPolicy("DefaultCors",
policy => policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
});
Comment on lines +27 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше cors настроить нормально:
Брать из appsettings.json допустимые origins

Для app.Environment.IsDevelopment() можно оставить доступ всем


var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "V1");
c.RoutePrefix = string.Empty;
});
}

app.UseCors("DefaultCors");

app.MapControllers();

app.Run();
38 changes: 38 additions & 0 deletions CreditApp.Api/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:53917",
"sslPort": 44347
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5022",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7084;http://localhost:5022",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
8 changes: 8 additions & 0 deletions CreditApp.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
16 changes: 16 additions & 0 deletions CreditApp.Api/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"redis": "localhost:6379"
}
,
"CreditGenerator": {
"CentralBankRate": 16.0
}
}
18 changes: 18 additions & 0 deletions CreditApp.Application/CreditApp.Application.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CreditApp.Domain\CreditApp.Domain.csproj" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions CreditApp.Application/Interfaces/ICreditApplicationGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using CreditApp.Domain.Entities;

namespace CreditApp.Application.Interfaces;

/// <summary>
/// Генерирует объекты заявок на кредит для тестирования, заполнения демонстрационных данных или инициализации.
/// Реализации должны создавать и возвращать готовый к использованию экземпляр <see cref="CreditApplication"/>.
/// </summary>
public interface ICreditApplicationGenerator
{
/// <summary>
/// Асинхронно генерирует заявку на кредит с указанным идентификатором.
/// </summary>
/// <param name="id">Идентификатор создаваемой заявки.</param>
/// <param name="ct">Токен отмены для асинхронной операции.</param>
/// <returns>Сгенерированный экземпляр <see cref="CreditApplication"/>.</returns>
public Task<CreditApplication> GenerateAsync(int id, CancellationToken ct);
}
19 changes: 19 additions & 0 deletions CreditApp.Application/Interfaces/ICreditService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using CreditApp.Domain.Entities;

namespace CreditApp.Application.Interfaces;

/// <summary>
/// Сервис для работы с объектами кредитных заявок.
/// Предоставляет операции получения и управления заявками в приложении.
/// </summary>
public interface ICreditService
{
/// <summary>
/// Асинхронно получает заявку на кредит по её идентификатору.
/// </summary>
/// <param name="id">Идентификатор заявки.</param>
/// <param name="ct">Токен отмены для асинхронной операции.</param>
/// <returns>Экземпляр <see cref="CreditApplication"/>, соответствующий запрошенному идентификатору.</returns>
public Task<CreditApplication> GetAsync(int id, CancellationToken ct);

}
43 changes: 43 additions & 0 deletions CreditApp.Application/Services/CreditService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System.Text.Json;
using CreditApp.Application.Interfaces;
using CreditApp.Domain.Entities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace CreditApp.Application.Services;

public class CreditService(IDistributedCache distributedCache,
ICreditApplicationGenerator generator,
ILogger<CreditService> logger)
: ICreditService
{
public async Task<CreditApplication> GetAsync(int id, CancellationToken ct)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Все тело метода стоит в try catch обернуть

{
var cacheKey = $"Credit_{id}";

var cached = await distributedCache.GetStringAsync(cacheKey, ct);

if (!string.IsNullOrEmpty(cached))
{
logger.LogInformation("Cache HIT {CacheKey}", cacheKey);
return JsonSerializer.Deserialize<CreditApplication>(cached)!;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Теоретически возможно ситуация, что в кэше лежит мусор, который не сможет быть десериализован
Так что тут лучше не использовать null-forgiving operator, а проверить на null и вернуть если все нормально, а если не нормально, то код ниже сгенерирует новый объект по заданному ключу

}

logger.LogInformation("Cache MISS {CacheKey}", cacheKey);

var credit = await generator.GenerateAsync(id, ct);

var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10 лучше вынести в appsettings.json

};

await distributedCache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(credit),
options,
ct);

return credit;
}
}
9 changes: 9 additions & 0 deletions CreditApp.Domain/CreditApp.Domain.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Loading