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>№9 "Кредитная заявка"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Куненковым Иваном 6511</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/razzzenya/cloud-development">Ссылка на форк</Link></UnorderedListItem>
</UnorderedList>
</CardBody>
</Card>
6 changes: 3 additions & 3 deletions Client.Wasm/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5127",
"environmentVariables": {
Expand All @@ -22,7 +22,7 @@
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7282;http://localhost:5127",
"environmentVariables": {
Expand All @@ -31,7 +31,7 @@
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchBrowser": false,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
Expand Down
4 changes: 2 additions & 2 deletions Client.Wasm/wwwroot/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
}
},
"AllowedHosts": "*",
"BaseAddress": ""
}
"BaseAddress": "https://localhost:7170/api/credit"
}
27 changes: 25 additions & 2 deletions CloudDevelopment.sln
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36811.4
VisualStudioVersion = 17.13.35931.197
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.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", "{CC5A9873-4CC3-4B71-83AF-E4FD09F7B1AD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.ServiceDefaults", "CreditApp.ServiceDefaults\CreditApp.ServiceDefaults.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreditApp.AppHost", "CreditApp.AppHost\CreditApp.AppHost.csproj", "{2A5FB573-9376-4FEB-9289-A8387F435C13}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +22,22 @@ 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
{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
{CC5A9873-4CC3-4B71-83AF-E4FD09F7B1AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC5A9873-4CC3-4B71-83AF-E4FD09F7B1AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC5A9873-4CC3-4B71-83AF-E4FD09F7B1AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC5A9873-4CC3-4B71-83AF-E4FD09F7B1AD}.Release|Any CPU.Build.0 = Release|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU
{2A5FB573-9376-4FEB-9289-A8387F435C13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A5FB573-9376-4FEB-9289-A8387F435C13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A5FB573-9376-4FEB-9289-A8387F435C13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A5FB573-9376-4FEB-9289-A8387F435C13}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
26 changes: 26 additions & 0 deletions CreditApp.Api/Controllers/CreditController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using CreditApp.Api.Services.CreditGeneratorService;
using CreditApp.Domain.Entities;
using Microsoft.AspNetCore.Mvc;

namespace CreditApp.Api.Controllers;

[Route("api/[controller]")]
[ApiController]
public class CreditController(CreditApplicationGeneratorService _generatorService, ILogger<CreditController> _logger) : ControllerBase
{
/// <summary>
/// Получить кредитную заявку по ID, если не найдена в кэше генерируем новую
/// </summary>
/// <param name="id">ID кредитной заявки</param>
/// <param name="cancellationToken">Токен отмены операции</param>
/// <returns>Кредитная заявка</returns>
[HttpGet]
public async Task<ActionResult<CreditApplication>> GetById([FromQuery] int id, CancellationToken cancellationToken)
{
_logger.LogInformation("Получен запрос на получение/генерацию заявки {Id}", id);

var application = await _generatorService.GetByIdAsync(id, cancellationToken);

return Ok(application);
}
}
23 changes: 23 additions & 0 deletions CreditApp.Api/CreditApp.Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<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.DistributedCaching" Version="9.5.2" />
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.24" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.4" />
</ItemGroup>

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

</Project>
58 changes: 58 additions & 0 deletions CreditApp.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using CreditApp.Api.Services.CreditGeneratorService;
using CreditApp.ServiceDefaults;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();

builder.AddRedisDistributedCache("cache");

builder.Services.AddCors(options =>
{
options.AddPolicy("AllowBlazorWasm", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});

builder.Services.AddScoped<CreditApplicationGeneratorService>();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.OpenApiInfo
{
Title = "CreditApp API"
});

var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFilename);
if (File.Exists(xmlPath))
{
options.IncludeXmlComments(xmlPath);
}

var domainXmlPath = Path.Combine(AppContext.BaseDirectory, "CreditApp.Domain.xml");
if (File.Exists(domainXmlPath))
{
options.IncludeXmlComments(domainXmlPath);
}
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseCors("AllowBlazorWasm");
app.MapControllers();
app.MapDefaultEndpoints();

app.Run();
41 changes: 41 additions & 0 deletions CreditApp.Api/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:46825",
"sslPort": 44333
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5179",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7170;http://localhost:5179",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using Bogus;
using CreditApp.Domain.Entities;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

namespace CreditApp.Api.Services.CreditGeneratorService;

public class CreditApplicationGeneratorService(IDistributedCache _cache, IConfiguration _configuration, ILogger<CreditApplicationGeneratorService> _logger)
{
private static readonly string[] _creditTypes =
[
"Потребительский",
"Ипотека",
"Автокредит",
"Бизнес-кредит",
"Образовательный"
];

private static readonly string[] _statuses =
[
"Новая",
"В обработке",
"Одобрена",
"Отклонена"
];

private static readonly string[] _terminalStatuses = ["Одобрена", "Отклонена"];

private readonly int _expirationMinutes = _configuration.GetValue("CacheSettings:ExpirationMinutes", 10);

public async Task<CreditApplication> GetByIdAsync(int id, CancellationToken cancellationToken = default)
Copy link

Choose a reason for hiding this comment

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

Я бы этот метод в try catch обернул

{
try
{
var cacheKey = $"credit-application-{id}";

_logger.LogInformation("Попытка получить заявку {Id} из кэша", id);

var cachedData = await _cache.GetStringAsync(cacheKey, cancellationToken);

if (!string.IsNullOrEmpty(cachedData))
{
var deserializedApplication = JsonSerializer.Deserialize<CreditApplication>(cachedData);

if (deserializedApplication != null)
{
_logger.LogInformation("Заявка {Id} найдена в кэше", id);
return deserializedApplication;
}

_logger.LogWarning("Заявка {Id} найдена в кэше, но не удалось десериализовать. Генерируем новую", id);
}

_logger.LogInformation("Заявка {Id} не найдена в кэше, генерируем новую", id);

var application = GenerateApplication(id);

var cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_expirationMinutes)
};

await _cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(application),
cacheOptions,
cancellationToken);

_logger.LogInformation(
"Кредитная заявка сгенерирована и закэширована: Id={Id}, Тип={Type}, Сумма={Amount}, Статус={Status}",
application.Id,
application.Type,
application.Amount,
application.Status);

return application;
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка при получении/генерации заявки {Id}", id);
throw;
}
}

/// <summary>
/// Генерация кредитной заявки с указанным ID
/// </summary>
private static CreditApplication GenerateApplication(int id)
{
var faker = new Faker<CreditApplication>("ru")
.RuleFor(c => c.Id, f => id)
.RuleFor(c => c.Type, f => f.PickRandom(_creditTypes))
.RuleFor(c => c.Amount, f => Math.Round(f.Finance.Amount(10000, 10000000), 2))
.RuleFor(c => c.Term, f => f.Random.Int(6, 360))
.RuleFor(c => c.InterestRate, f => Math.Round(f.Random.Double(16.0, 25.0), 2))
.RuleFor(c => c.SubmissionDate, f => f.Date.PastDateOnly(2))
.RuleFor(c => c.RequiresInsurance, f => f.Random.Bool())
.RuleFor(c => c.Status, f => f.PickRandom(_statuses))
.RuleFor(c => c.ApprovalDate, (f, c) =>
{
if (!_terminalStatuses.Contains(c.Status))
return null;

return f.Date.BetweenDateOnly(c.SubmissionDate, DateOnly.FromDateTime(DateTime.Today));
})
.RuleFor(c => c.ApprovedAmount, (f, c) =>
{
if (c.Status != "Одобрена")
return null;

return Math.Round(c.Amount * f.Random.Decimal(0.7m, 1.0m), 2);
});

return faker.Generate();
}
}
12 changes: 12 additions & 0 deletions CreditApp.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"CacheSettings": {
"ExpirationMinutes": 10
}
}
12 changes: 12 additions & 0 deletions CreditApp.Api/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"CacheSettings": {
"ExpirationMinutes": 10
}
}
Loading