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>№43 "Сотрудник компании"</Strong></UnorderedListItem>
<UnorderedListItem>Выполнена <Strong>Казаковым Андреем 6513</Strong> </UnorderedListItem>
<UnorderedListItem><Link To="https://github.com/Gironape/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": ""
"BaseAddress": "https://localhost:7106/api/employee"
}
24 changes: 24 additions & 0 deletions CloudDevelopment.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ 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}") = "CompanyEmployee.AppHost", "CompanyEmployee.AppHost\CompanyEmployee.AppHost.csproj", "{069756DA-EFFA-4835-B69C-0849C48BE473}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompanyEmployee.ServiceDefaults", "CompanyEmployee.ServiceDefaults\CompanyEmployee.ServiceDefaults.csproj", "{60C547C0-C951-4270-1D2E-4BB68A5739B6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompanyEmployee.Domain", "CompanyEmployee.Domain\CompanyEmployee.Domain.csproj", "{FD5B46C8-0F5C-493A-B5FF-708AEA44AD3D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompanyEmployee.Api", "CompanyEmployee.Api\CompanyEmployee.Api.csproj", "{EEC6E8C9-9951-4CE6-DBC8-FEDF498A759B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +23,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
{069756DA-EFFA-4835-B69C-0849C48BE473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{069756DA-EFFA-4835-B69C-0849C48BE473}.Debug|Any CPU.Build.0 = Debug|Any CPU
{069756DA-EFFA-4835-B69C-0849C48BE473}.Release|Any CPU.ActiveCfg = Release|Any CPU
{069756DA-EFFA-4835-B69C-0849C48BE473}.Release|Any CPU.Build.0 = Release|Any CPU
{60C547C0-C951-4270-1D2E-4BB68A5739B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60C547C0-C951-4270-1D2E-4BB68A5739B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60C547C0-C951-4270-1D2E-4BB68A5739B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60C547C0-C951-4270-1D2E-4BB68A5739B6}.Release|Any CPU.Build.0 = Release|Any CPU
{FD5B46C8-0F5C-493A-B5FF-708AEA44AD3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD5B46C8-0F5C-493A-B5FF-708AEA44AD3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD5B46C8-0F5C-493A-B5FF-708AEA44AD3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD5B46C8-0F5C-493A-B5FF-708AEA44AD3D}.Release|Any CPU.Build.0 = Release|Any CPU
{EEC6E8C9-9951-4CE6-DBC8-FEDF498A759B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EEC6E8C9-9951-4CE6-DBC8-FEDF498A759B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEC6E8C9-9951-4CE6-DBC8-FEDF498A759B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEC6E8C9-9951-4CE6-DBC8-FEDF498A759B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
25 changes: 25 additions & 0 deletions CompanyEmployee.Api/CompanyEmployee.Api.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

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

<ItemGroup>
<PackageReference Include="Aspire.StackExchange.Redis.DistributedCaching" Version="13.1.1" />
<PackageReference Include="Bogus" Version="35.6.5" />
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.3" />
Comment on lines +12 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

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

Зачем тебе майкрософтовские пакеты, когда ты уже пользуешься аспаеровским?

<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Client.Wasm\Client.Wasm.csproj" />
<ProjectReference Include="..\CompanyEmployee.Domain\CompanyEmployee.Domain.csproj" />
<ProjectReference Include="..\CompanyEmployee.ServiceDefaults\CompanyEmployee.ServiceDefaults.csproj" />
</ItemGroup>

<ProjectExtensions><VisualStudio><UserProperties properties_4launchsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>

</Project>
6 changes: 6 additions & 0 deletions CompanyEmployee.Api/CompanyEmployee.Api.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@CompanyEmployee.Api_HostAddress = http://localhost:5121

GET {{CompanyEmployee.Api_HostAddress}}/weatherforecast/
Copy link
Collaborator

Choose a reason for hiding this comment

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

Очень интересный документ с маршрутом к эндпоинту, которого у тебя в софте нет

Accept: application/json

###
34 changes: 34 additions & 0 deletions CompanyEmployee.Api/Controllers/EmployeeController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using CompanyEmployee.Api.Services;
using CompanyEmployee.Domain.Entity;
using Microsoft.AspNetCore.Mvc;

namespace CompanyEmployee.Api.Controllers;

/// <summary>
/// Контроллер для работы с сотрудниками.
/// </summary>
[ApiController]
Comment on lines +9 to +10
Copy link
Collaborator

Choose a reason for hiding this comment

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

Добавить параметры праймари конструктора в саммари

[Route("api/[controller]")]
public class EmployeeController : ControllerBase
{
private readonly IEmployeeService _employeeService;
private readonly ILogger<EmployeeController> _logger;

public EmployeeController(IEmployeeService employeeService, ILogger<EmployeeController> logger)
{
_employeeService = employeeService;
_logger = logger;
}
Comment on lines +14 to +21
Copy link
Collaborator

Choose a reason for hiding this comment

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

Использовать праймари конструктор


/// <summary>
/// Получить сгенерированного сотрудника.
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(Employee), StatusCodes.Status200OK)]
public async Task<ActionResult<Employee>> GetEmployee(int? seed, CancellationToken cancellationToken)
{
_logger.LogInformation("Запрос на получение сотрудника с seed: {Seed}", seed);
var employee = await _employeeService.GetEmployeeAsync(seed, cancellationToken);
return Ok(employee);
Comment on lines +30 to +32
Copy link
Collaborator

Choose a reason for hiding this comment

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

Точно не надо какую-нибудь обработку исключений соорудить?

}
}
39 changes: 39 additions & 0 deletions CompanyEmployee.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using CompanyEmployee.Api.Services;

var builder = WebApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddRedisDistributedCache("redis");

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

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

builder.Services.AddSingleton<IEmployeeGenerator, EmployeeGenerator>();
builder.Services.AddScoped<IEmployeeService, EmployeeService>();

var app = builder.Build();

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

app.MapDefaultEndpoints();
app.UseHttpsRedirection();
app.UseCors("wasm");
app.UseAuthorization();
app.MapControllers();

app.Run();
41 changes: 41 additions & 0 deletions CompanyEmployee.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:56739",
"sslPort": 44378
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5121",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7106;http://localhost:5121",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
83 changes: 83 additions & 0 deletions CompanyEmployee.Api/Services/EmployeeGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Bogus;
using Bogus.DataSets;
using CompanyEmployee.Domain.Entity;
using System.Xml.Linq;

namespace CompanyEmployee.Api.Services;

public class EmployeeGenerator : IEmployeeGenerator
{
private readonly ILogger<EmployeeGenerator> _logger;
private readonly string[] _professions = { "Developer", "Manager", "Analyst", "Designer", "QA" };
private readonly string[] _suffixes = { "Junior", "Middle", "Senior" };

public EmployeeGenerator(ILogger<EmployeeGenerator> logger)
{
_logger = logger;
}
Comment on lines +14 to +17
Copy link
Collaborator

Choose a reason for hiding this comment

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

6 дотнет вышел в 2021 году, в какой временной петле ты существуешь без праймари конструкторов?


/// <summary>
/// Генератор сотрудника
/// </summary>
public Employee Generate(int? seed = null)
{
if (seed.HasValue)
Randomizer.Seed = new Random(seed.Value);

var faker = new Faker("ru");

var gender = faker.PickRandom<Bogus.DataSets.Name.Gender>();
var firstName = faker.Name.FirstName(gender);
var lastName = faker.Name.LastName(gender);
var patronymicBase = faker.Name.FirstName(Name.Gender.Male);
var patronymic = gender == Name.Gender.Male
? patronymicBase + "ович"
: patronymicBase + "овна";
var fullName = $"{lastName} {firstName} {patronymic}";

var profession = faker.PickRandom(_professions);
var suffix = faker.PickRandom(_suffixes);
var position = $"{profession} {suffix}".Trim();

var department = faker.Commerce.Department();

var hireDate = DateOnly.FromDateTime(faker.Date.Past(10).ToUniversalTime());

var salary = suffix switch
{
"Junior" => faker.Random.Decimal(30000, 60000),
"Middle" => faker.Random.Decimal(60000, 100000),
"Senior" => faker.Random.Decimal(100000, 180000),
_ => faker.Random.Decimal(40000, 80000)
};
salary = Math.Round(salary, 2);

var email = faker.Internet.Email(firstName, lastName);
var phone = faker.Phone.PhoneNumber("+7(###)###-##-##");
var isTerminated = faker.Random.Bool(0.1f);

DateOnly? terminationDate = null;
if (isTerminated)
{
var termDate = faker.Date.Between(hireDate.ToDateTime(TimeOnly.MinValue), DateTime.Now);
terminationDate = DateOnly.FromDateTime(termDate);
}

var employee = new Employee
{
Id = faker.Random.Int(1, 100000),
FullName = fullName,
Position = position,
Department = department,
HireDate = hireDate,
Salary = salary,
Email = email,
Phone = phone,
IsTerminated = isTerminated,
TerminationDate = terminationDate
};
Comment on lines +29 to +78
Copy link
Collaborator

Choose a reason for hiding this comment

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

Переписать все через .RuleFor()
Выйдет раза в три меньше кода, кстати


_logger.LogInformation("Сгенерирован новый сотрудник: {@Employee}", employee);
return employee;
}
}
54 changes: 54 additions & 0 deletions CompanyEmployee.Api/Services/EmployeeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using CompanyEmployee.Domain.Entity;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

namespace CompanyEmployee.Api.Services;

/// <summary>
/// Реализация сервиса сотрудников с кэшированием в Redis через IDistributedCache.
/// </summary>
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeGenerator _generator;
private readonly IDistributedCache _cache;
private readonly ILogger<EmployeeService> _logger;
private readonly DistributedCacheEntryOptions _cacheOptions;
Comment on lines +10 to +15
Copy link
Collaborator

Choose a reason for hiding this comment

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

И службу эту, по-хорошему, нужно отрефакторить, чтобы srp не нарушался


public EmployeeService(IEmployeeGenerator generator, IDistributedCache cache, ILogger<EmployeeService> logger)
{
_generator = generator;
_cache = cache;
_logger = logger;
_cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
};
}
Comment on lines +17 to +26
Copy link
Collaborator

Choose a reason for hiding this comment

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

Image


/// <summary>
/// Получает сотрудника.
/// </summary>
Comment on lines +28 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. Лаконично пиздец
  2. Существует <inheritdoc/>

public async Task<Employee> GetEmployeeAsync(int? seed, CancellationToken cancellationToken = default)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ставить суффикс Async имеет смысл только при наличии в том же классе синхронного метода с тем же названием. Синхронного метода я тут не наблюдаю

Copy link
Collaborator

Choose a reason for hiding this comment

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

Вообще, мне не нравится этот метод, в нем намешано как минимум три вектора изменений код, отрефактори его

{
var cacheKey = seed.HasValue ? $"employee:seed:{seed}" : $"employee:random:{Guid.NewGuid()}";
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. Зачем тебе nullability в параметре этого метода в принципе?
  2. Зачем тебе обрабатывать null в нем таким извращенским образом?


_logger.LogDebug("Попытка получения сотрудника из кэша по ключу {CacheKey}", cacheKey);

var cachedJson = await _cache.GetStringAsync(cacheKey, cancellationToken);
if (cachedJson != null)
{
_logger.LogInformation("Сотрудник найден в кэше по ключу {CacheKey}", cacheKey);
var employee = JsonSerializer.Deserialize<Employee>(cachedJson);
return employee!;
}
Comment on lines +37 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

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

В текущем виде ошибка работы кэша положит тебе приложение, а так быть не должно


_logger.LogInformation("Сотрудник не найден в кэше, генерация нового. Seed: {Seed}", seed);
var newEmployee = _generator.Generate(seed);

var serialized = JsonSerializer.Serialize(newEmployee);
await _cache.SetStringAsync(cacheKey, serialized, _cacheOptions, cancellationToken);
_logger.LogDebug("Сгенерированный сотрудник сохранён в кэш по ключу {CacheKey}", cacheKey);
Comment on lines +46 to +50
Copy link
Collaborator

Choose a reason for hiding this comment

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

Аналогично


return newEmployee;
}
}
11 changes: 11 additions & 0 deletions CompanyEmployee.Api/Services/IEmployeeGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using CompanyEmployee.Domain.Entity;

namespace CompanyEmployee.Api.Services;

public interface IEmployeeGenerator
Copy link
Collaborator

Choose a reason for hiding this comment

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

Нет саммари

{
/// <summary>
/// Генерирует нового сотрудника.
/// </summary>
public Employee Generate(int? seed = null);
}
11 changes: 11 additions & 0 deletions CompanyEmployee.Api/Services/IEmployeeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using CompanyEmployee.Domain.Entity;

namespace CompanyEmployee.Api.Services;

public interface IEmployeeService
Copy link
Collaborator

Choose a reason for hiding this comment

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

Тоже нет саммари

{
/// <summary>
/// Получает сотрудника.
/// </summary>
public Task<Employee> GetEmployeeAsync(int? seed, CancellationToken cancellationToken = default);
}
8 changes: 8 additions & 0 deletions CompanyEmployee.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"
}
}
}
9 changes: 9 additions & 0 deletions CompanyEmployee.Api/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Loading