Skip to content
Closed
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
4 changes: 4 additions & 0 deletions Client.Wasm/Client.Wasm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
<None Remove="Components\StudentCard.razor~RF1bb17a4.TMP" />
</ItemGroup>

<ItemGroup>
<None Include="Client.Wasm.csproj.user" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Blazorise" Version="1.8.8" />
<PackageReference Include="Blazorise.Bootstrap" Version="1.8.8" />
Expand Down
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>№ 52</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Томашайтисом Павлом 6513</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/Tomashaytis/cloud-development">Ссылка на форк</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:7170/course-management"
}
25 changes: 0 additions & 25 deletions CloudDevelopment.sln

This file was deleted.

43 changes: 43 additions & 0 deletions CourseManagement.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.4.11506.43
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}") = "CourseManagement.AppHost", "CourseManagement\CourseManagement.AppHost\CourseManagement.AppHost.csproj", "{68D48F1B-1FC5-4FF4-9227-1EEB20CF4B96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CourseManagement.ServiceDefaults", "CourseManagement\CourseManagement.ServiceDefaults\CourseManagement.ServiceDefaults.csproj", "{39157B84-72E5-AB3C-22EB-59DEE8C02D29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CourseManagement.ApiService", "CourseManagement\CourseManagement.ApiService\CourseManagement.ApiService.csproj", "{15EBA239-9A26-5A00-427D-D957E19F128A}"
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
{68D48F1B-1FC5-4FF4-9227-1EEB20CF4B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68D48F1B-1FC5-4FF4-9227-1EEB20CF4B96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68D48F1B-1FC5-4FF4-9227-1EEB20CF4B96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68D48F1B-1FC5-4FF4-9227-1EEB20CF4B96}.Release|Any CPU.Build.0 = Release|Any CPU
{39157B84-72E5-AB3C-22EB-59DEE8C02D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{39157B84-72E5-AB3C-22EB-59DEE8C02D29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39157B84-72E5-AB3C-22EB-59DEE8C02D29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39157B84-72E5-AB3C-22EB-59DEE8C02D29}.Release|Any CPU.Build.0 = Release|Any CPU
{15EBA239-9A26-5A00-427D-D957E19F128A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15EBA239-9A26-5A00-427D-D957E19F128A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15EBA239-9A26-5A00-427D-D957E19F128A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15EBA239-9A26-5A00-427D-D957E19F128A}.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
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using CourseManagement.ApiService.Dto;
using CourseManagement.ApiService.Services;

namespace CourseManagement.ApiService.Controllers;

/// <summary>
/// Контроллер для сущности типа Курс
/// </summary>
/// <param name="logger">Логгер</param>
/// <param name="courseService">Сервис для сущности типа Курс</param>
[ApiController]
[Route("course-management")]
public class CourseController(ILogger<CourseController> logger, CourseService courseService) : ControllerBase
{
/// <summary>
/// Обработчик GET-запроса на генерацию курса
/// </summary>
/// <param name="id">Идентификатор курса</param>
/// <returns>Сгенерированный курс</returns>
[HttpGet]
public async Task<ActionResult<CourseDto>> GetCourse(int? id)
{
logger.LogInformation("Processing request for course {ResourceId}", id);

var course = await courseService.GetCourse(id ?? 0);

return course != null ? Ok(course) : Problem("Internal server error", statusCode: 500);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

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

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

<ItemGroup>
<PackageReference Include="Aspire.StackExchange.Redis" Version="13.1.1" />
<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="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.3" />
</ItemGroup>

</Project>
58 changes: 58 additions & 0 deletions CourseManagement/CourseManagement.ApiService/DTO/CourseDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace CourseManagement.ApiService.Dto;


/// <summary>
/// DTO для сущности типа курс
/// </summary>
public class CourseDto
{
/// <summary>
/// Идентификатор курса
/// </summary>
public int Id { get; set; }

/// <summary>
/// Название курса
/// </summary>
public string Title { get; set; } = string.Empty;

/// <summary>
/// Лектор
/// </summary>
public string Lector { get; set; } = string.Empty;

/// <summary>
/// Дата начала курса
/// </summary>
public DateOnly StartDate { get; set; }

/// <summary>
/// Дата окончания курса
/// </summary>
public DateOnly EndDate { get; set; }

/// <summary>
/// Максимальное число студентов для курса
/// </summary>
public int MaxStudents { get; set; }

/// <summary>
/// Текущее число студентов курса
/// </summary>
public int EnrolledStudents { get; set; }

/// <summary>
/// Выдача сертификата
/// </summary>
public bool HasSertificate { get; set; }

/// <summary>
/// Стоимость курса
/// </summary>
public decimal Price { get; set; }

/// <summary>
/// Рейтинг
/// </summary>
public int Rating { get; set; }
}
55 changes: 55 additions & 0 deletions CourseManagement/CourseManagement.ApiService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using CourseManagement.ApiService.Dto;
using CourseManagement.ApiService.Services;

var builder = WebApplication.CreateBuilder(args);

// Add service defaults
builder.AddServiceDefaults();

// Redis
builder.AddRedisDistributedCache("course-cache");

// Add services to the container
builder.Services.AddProblemDetails();
builder.Services.AddOpenApi();

// Контроллеры
builder.Services.AddControllers();

// CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowClient", policy =>
{
policy.AllowAnyOrigin()
.WithMethods("GET")
.WithHeaders("Content-Type");
});
});

// Генератор курсов
builder.Services.AddSingleton<CourseGenerator>();

// Сервис для взаимодействия с кэшем
builder.Services.AddSingleton<CacheService<CourseDto>>();

// Сервис для сущности типа Курс
builder.Services.AddSingleton<CourseService>();

var app = builder.Build();

// Configure the HTTP request pipeline
app.UseExceptionHandler();
app.UseCors("AllowClient");

// Mapping
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}

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:5329",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7170;http://localhost:5329",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Text.Json;
using Microsoft.Extensions.Caching.Distributed;

namespace CourseManagement.ApiService.Services;

/// <summary>
/// Сервис для взаимодействия с кэшем
/// </summary>
/// <param name="cache">Кэш</param>
/// <param name="logger">Логгер</param>
public class CacheService<T>(IDistributedCache cache, ILogger<CacheService<T>> logger)
{
/// <summary>
/// Время жизни данных в кэше
/// </summary>
private static readonly double _cacheDuration = 5;

/// <summary>
/// Асинхронный метод для извлечения данных из кэша
/// </summary>
/// <param name="key">Ключ для сущности</param>
/// <param name="id">Идентификатор объекта</param>
/// <returns>Объект или null при его отсутствии</returns>
public async Task<T?> FetchAsync(string key, int id)
{
var cacheKey = $"{key}:{id}";

try
{
var cached = await cache.GetStringAsync(cacheKey);
if (cached != null)
{
var obj = JsonSerializer.Deserialize<T>(cached);

logger.LogInformation("Cache hit for object {ResourceId}", id);

return obj;
}

logger.LogInformation("Cache miss for object {ResourceId}", id);
}
catch (Exception ex)
{
logger.LogWarning(ex, "Сache is unavailable");
}

return default;
}

/// <summary>
/// Асинхронный метод для внесения данных в кэш
/// </summary>
/// <param name="key">Ключ для сущности</param>
/// <param name="id">Идентификатор объекта</param>
/// <param name="obj">Объект</param>
public async Task StoreAsync(string key, int id, T obj)
{
var cacheKey = $"{key}:{id}";

var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_cacheDuration)
};

try
{
var serialized = JsonSerializer.Serialize(obj);
await cache.SetStringAsync(cacheKey, serialized, options);
logger.LogInformation("Object {ResourceId} cached", id);
}
catch (Exception ex)
{
logger.LogWarning(ex, "Сache is unavailable");
}

}
}
Loading