Compare commits
2 Commits
f4a24b840d
...
883837633a
| Author | SHA1 | Date | |
|---|---|---|---|
| 883837633a | |||
| aa0d2b1a73 |
@@ -33,22 +33,6 @@ public class MissionsController(IMissionService missionService) : ControllerBase
|
|||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получает текстовые данные миссии на определенном языке
|
|
||||||
/// </summary>
|
|
||||||
[HttpGet("{id}/texts/{language}")]
|
|
||||||
public async Task<IActionResult> GetMissionTexts([FromRoute] int id, [FromRoute] string language, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(language))
|
|
||||||
return BadRequest("Language parameter is required.");
|
|
||||||
|
|
||||||
var textData = await missionService.GetMissionTextAsync(id, language, cancellationToken);
|
|
||||||
if (textData == null)
|
|
||||||
return NotFound("Mission or language not found.");
|
|
||||||
|
|
||||||
return Ok(textData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получает подробную информацию о миссии
|
/// Получает подробную информацию о миссии
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ public class UploadMissionRequestValidator : AbstractValidator<UploadMissionRequ
|
|||||||
RuleFor(x => x.Difficulty)
|
RuleFor(x => x.Difficulty)
|
||||||
.GreaterThan(0)
|
.GreaterThan(0)
|
||||||
.WithMessage("Difficulty must be greater than 0")
|
.WithMessage("Difficulty must be greater than 0")
|
||||||
.LessThanOrEqualTo(5)
|
.LessThanOrEqualTo(10000)
|
||||||
.WithMessage("Difficulty must be between 1 and 5");
|
.WithMessage("Difficulty must be between 1 and 10000");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ public record MissionResponse(
|
|||||||
int AuthorId,
|
int AuthorId,
|
||||||
string Name,
|
string Name,
|
||||||
int Difficulty,
|
int Difficulty,
|
||||||
string S3ContentKey,
|
|
||||||
IReadOnlyList<string> Tags,
|
IReadOnlyList<string> Tags,
|
||||||
DateTime CreatedAt,
|
DateTime CreatedAt,
|
||||||
DateTime UpdatedAt
|
DateTime UpdatedAt
|
||||||
@@ -24,7 +23,6 @@ public record MissionResponse(
|
|||||||
entity.Author.Id,
|
entity.Author.Id,
|
||||||
entity.Name,
|
entity.Name,
|
||||||
entity.Difficulty,
|
entity.Difficulty,
|
||||||
entity.S3ContentKey,
|
|
||||||
entity.MissionTags.Select(mt => mt.Tag.Name).Distinct().OrderBy(name => name).ToList(),
|
entity.MissionTags.Select(mt => mt.Tag.Name).Distinct().OrderBy(name => name).ToList(),
|
||||||
entity.CreatedAt,
|
entity.CreatedAt,
|
||||||
entity.UpdatedAt
|
entity.UpdatedAt
|
||||||
|
|||||||
@@ -11,6 +11,4 @@ public record SubmitSolutionRequest(
|
|||||||
[Required] [StringLength(16)] string Language,
|
[Required] [StringLength(16)] string Language,
|
||||||
[Required] [StringLength(16)] string LanguageVersion,
|
[Required] [StringLength(16)] string LanguageVersion,
|
||||||
[Required] [StringLength(10000, MinimumLength = 1)] string SourceCode,
|
[Required] [StringLength(10000, MinimumLength = 1)] string SourceCode,
|
||||||
int? ContestId,
|
int? ContestId);
|
||||||
SubmissionSourceType SourceType = SubmissionSourceType.Direct
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using LiquidCode.Api.Submits.Requests;
|
using LiquidCode.Api.Submits.Requests;
|
||||||
using LiquidCode.Api.Submits.Responses;
|
using LiquidCode.Api.Submits.Responses;
|
||||||
|
using LiquidCode.Domain.Services.Contests;
|
||||||
using LiquidCode.Domain.Services.Submits;
|
using LiquidCode.Domain.Services.Submits;
|
||||||
|
using LiquidCode.Infrastructure.Database.Entities;
|
||||||
using LiquidCode.Infrastructure.External.TestingModule;
|
using LiquidCode.Infrastructure.External.TestingModule;
|
||||||
using LiquidCode.Shared.Constants;
|
using LiquidCode.Shared.Constants;
|
||||||
using LiquidCode.Shared.Extensions;
|
using LiquidCode.Shared.Extensions;
|
||||||
@@ -44,6 +46,9 @@ public class SubmitController(
|
|||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
return BadRequest(ModelState);
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
var contestId = request.ContestId;
|
||||||
|
var contest = contestId == null ? null : await _contestService.GetAsync(contestId.Value, cancellationToken);
|
||||||
|
|
||||||
var solution = await _submitService.SubmitSolutionAsync(
|
var solution = await _submitService.SubmitSolutionAsync(
|
||||||
request.MissionId,
|
request.MissionId,
|
||||||
userId,
|
userId,
|
||||||
@@ -51,7 +56,6 @@ public class SubmitController(
|
|||||||
request.Language,
|
request.Language,
|
||||||
request.LanguageVersion,
|
request.LanguageVersion,
|
||||||
request.ContestId,
|
request.ContestId,
|
||||||
request.SourceType,
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
if (solution == null)
|
if (solution == null)
|
||||||
|
|||||||
@@ -32,26 +32,6 @@ public interface IMissionRepository : IRepository<DbMission>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Task SyncTagsAsync(DbMission mission, IEnumerable<int> tagIds, CancellationToken cancellationToken = default);
|
Task SyncTagsAsync(DbMission mission, IEnumerable<int> tagIds, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получает текстовые данные миссии на определенном языке
|
|
||||||
/// </summary>
|
|
||||||
Task<DbMissionPublicTextData?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получает все доступные языки для миссии
|
|
||||||
/// </summary>
|
|
||||||
Task<IEnumerable<string>> GetMissionLanguagesAsync(int missionId, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавляет текстовые данные миссии
|
|
||||||
/// </summary>
|
|
||||||
Task CreateMissionTextAsync(DbMissionPublicTextData textData, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавляет несколько записей текстовых данных миссии
|
|
||||||
/// </summary>
|
|
||||||
Task CreateMissionTextsAsync(IEnumerable<DbMissionPublicTextData> textData, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Подсчитывает общее количество миссий
|
/// Подсчитывает общее количество миссий
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -17,16 +17,7 @@ public interface IMissionService
|
|||||||
/// <param name="cancellationToken">Токен отмены</param>
|
/// <param name="cancellationToken">Токен отмены</param>
|
||||||
/// <returns>Созданная модель миссии или null, если загрузка не удалась</returns>
|
/// <returns>Созданная модель миссии или null, если загрузка не удалась</returns>
|
||||||
Task<MissionResponse?> UploadMissionAsync(UploadMissionRequest form, int userId, CancellationToken cancellationToken = default);
|
Task<MissionResponse?> UploadMissionAsync(UploadMissionRequest form, int userId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Получает текстовые данные миссии на определенном языке
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="missionId">ID миссии</param>
|
|
||||||
/// <param name="language">Код языка</param>
|
|
||||||
/// <param name="cancellationToken">Токен отмены</param>
|
|
||||||
/// <returns>Текстовые данные миссии в виде строки JSON или null, если не найдено</returns>
|
|
||||||
Task<string?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Получает постраничный список миссий
|
/// Получает постраничный список миссий
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -49,39 +49,10 @@ public class MissionService : IMissionService
|
|||||||
var tempDir = Path.GetTempPath();
|
var tempDir = Path.GetTempPath();
|
||||||
var unpackFolder = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
|
var unpackFolder = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
|
||||||
var packageZipPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".zip");
|
var packageZipPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".zip");
|
||||||
var statementsZipPath = Path.Combine(tempDir, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".zip");
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Сохранить загруженный файл
|
// Получить юзера
|
||||||
_logger.LogInformation("Saving mission file: {FileName}", form.MissionFile.Name);
|
|
||||||
using (var fileStream = System.IO.File.Open(packageZipPath, FileMode.OpenOrCreate))
|
|
||||||
{
|
|
||||||
await form.MissionFile.CopyToAsync(fileStream, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Распаковать ZIP файл
|
|
||||||
_logger.LogInformation("Extracting mission ZIP to: {UnpackFolder}", unpackFolder);
|
|
||||||
ZipFile.ExtractToDirectory(packageZipPath, unpackFolder);
|
|
||||||
|
|
||||||
// Проверить, существует ли папка statement-sections
|
|
||||||
var statementSectionsPath = Path.Combine(unpackFolder, MissionStatementPaths.StatementSectionsFolder);
|
|
||||||
if (!Directory.Exists(statementSectionsPath))
|
|
||||||
{
|
|
||||||
_logger.LogError("statement-sections folder not found in mission ZIP");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Упаковать разделы утверждений
|
|
||||||
_logger.LogInformation("Creating statements ZIP: {StatementsZipPath}", statementsZipPath);
|
|
||||||
ZipFile.CreateFromDirectory(statementSectionsPath, statementsZipPath, CompressionLevel.SmallestSize, false);
|
|
||||||
|
|
||||||
// Загрузить на S3
|
|
||||||
_logger.LogInformation("Uploading mission files to S3");
|
|
||||||
var privateKey = await _s3Client.UploadFileWithRandomKey(S3BucketKeys.PrivateProblems, packageZipPath);
|
|
||||||
var contentKey = await _s3Client.UploadFileWithRandomKey(S3BucketKeys.PublicContent, statementsZipPath);
|
|
||||||
|
|
||||||
// Создать миссию в базе данных
|
|
||||||
var existingUser = await _userRepository.FindByIdAsync(userId, cancellationToken);
|
var existingUser = await _userRepository.FindByIdAsync(userId, cancellationToken);
|
||||||
if (existingUser == null)
|
if (existingUser == null)
|
||||||
{
|
{
|
||||||
@@ -89,12 +60,23 @@ public class MissionService : IMissionService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Сохранить загруженный файл
|
||||||
|
_logger.LogInformation("Saving mission file: {FileName}", form.MissionFile.Name);
|
||||||
|
using (var fileStream = System.IO.File.Open(packageZipPath, FileMode.OpenOrCreate))
|
||||||
|
{
|
||||||
|
await form.MissionFile.CopyToAsync(fileStream, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузить на S3
|
||||||
|
_logger.LogInformation("Uploading mission files to S3");
|
||||||
|
var privateKey = await _s3Client.UploadFileWithRandomKey(S3BucketKeys.PrivateProblems, packageZipPath);
|
||||||
|
|
||||||
|
// Создать миссию в базе данных
|
||||||
var dbMission = new DbMission
|
var dbMission = new DbMission
|
||||||
{
|
{
|
||||||
Author = existingUser,
|
Author = existingUser,
|
||||||
Name = form.Name,
|
Name = form.Name,
|
||||||
S3PrivateKey = privateKey,
|
S3PrivateKey = privateKey,
|
||||||
S3ContentKey = contentKey,
|
|
||||||
Difficulty = form.Difficulty,
|
Difficulty = form.Difficulty,
|
||||||
CreatedAt = DateTime.UtcNow,
|
CreatedAt = DateTime.UtcNow,
|
||||||
UpdatedAt = DateTime.UtcNow
|
UpdatedAt = DateTime.UtcNow
|
||||||
@@ -102,27 +84,6 @@ public class MissionService : IMissionService
|
|||||||
|
|
||||||
await _missionRepository.CreateAsync(dbMission, cancellationToken);
|
await _missionRepository.CreateAsync(dbMission, cancellationToken);
|
||||||
|
|
||||||
// Распарсить и сохранить текстовые данные миссии
|
|
||||||
var missionTexts = ExtractMissionTexts(statementSectionsPath, dbMission.Id);
|
|
||||||
|
|
||||||
// Обновить имя миссии из русского языка, если доступно, иначе из первого доступного языка
|
|
||||||
var russianText = missionTexts.FirstOrDefault(t => t.Language == "russian");
|
|
||||||
if (russianText != null)
|
|
||||||
{
|
|
||||||
var russianData = JsonSerializer.Deserialize<JsonMissionData>(russianText.Data, JsonSerializerOptions);
|
|
||||||
if (russianData?.Name != null)
|
|
||||||
dbMission.Name = russianData.Name;
|
|
||||||
}
|
|
||||||
else if (missionTexts.Count > 0)
|
|
||||||
{
|
|
||||||
var firstData = JsonSerializer.Deserialize<JsonMissionData>(missionTexts[0].Data, JsonSerializerOptions);
|
|
||||||
if (firstData?.Name != null)
|
|
||||||
dbMission.Name = firstData.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Добавить текстовые данные миссии в базу данных
|
|
||||||
await _missionRepository.CreateMissionTextsAsync(missionTexts, cancellationToken);
|
|
||||||
|
|
||||||
// Обработать теги
|
// Обработать теги
|
||||||
await SyncMissionTagsAsync(dbMission, form.Tags, cancellationToken);
|
await SyncMissionTagsAsync(dbMission, form.Tags, cancellationToken);
|
||||||
|
|
||||||
@@ -140,34 +101,7 @@ public class MissionService : IMissionService
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// Очистить временные файлы
|
// Очистить временные файлы
|
||||||
CleanupTemporaryFiles(unpackFolder, packageZipPath, statementsZipPath);
|
CleanupTemporaryFiles(unpackFolder, packageZipPath);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var mission = await _missionRepository.FindByIdAsync(missionId, cancellationToken);
|
|
||||||
if (mission == null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Mission not found: {MissionId}", missionId);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var textData = await _missionRepository.GetMissionTextAsync(missionId, language, cancellationToken);
|
|
||||||
if (textData == null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Mission text not found: {MissionId}, {Language}", missionId, language);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return textData.Data;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error getting mission text: {MissionId}, {Language}", missionId, language);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,79 +197,17 @@ public class MissionService : IMissionService
|
|||||||
await _missionRepository.SyncTagsAsync(mission, allTags.Values.Select(t => t.Id), cancellationToken);
|
await _missionRepository.SyncTagsAsync(mission, allTags.Values.Select(t => t.Id), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DbMissionPublicTextData> ExtractMissionTexts(string statementSectionsPath, int missionId)
|
private void CleanupTemporaryFiles(params string[] paths)
|
||||||
{
|
|
||||||
var missionTexts = new List<DbMissionPublicTextData>();
|
|
||||||
var directoryInfo = new DirectoryInfo(statementSectionsPath);
|
|
||||||
|
|
||||||
foreach (var languageDir in directoryInfo.GetDirectories())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var data = GetDataFromStatementSections(languageDir);
|
|
||||||
var json = JsonSerializer.Serialize(data, JsonSerializerOptions);
|
|
||||||
|
|
||||||
missionTexts.Add(new DbMissionPublicTextData
|
|
||||||
{
|
|
||||||
MissionId = missionId,
|
|
||||||
Language = languageDir.Name,
|
|
||||||
Data = json
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Error extracting mission text for language: {Language}", languageDir.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return missionTexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonMissionData GetDataFromStatementSections(DirectoryInfo dir)
|
|
||||||
{
|
|
||||||
var files = dir.GetFiles();
|
|
||||||
var data = new JsonMissionData
|
|
||||||
{
|
|
||||||
Name = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.NameFile).FullName),
|
|
||||||
Input = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.InputFile).FullName),
|
|
||||||
Output = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.OutputFile).FullName),
|
|
||||||
Legend = System.IO.File.ReadAllText(files.Single(f => f.Name == MissionStatementPaths.LegendFile).FullName),
|
|
||||||
Examples = [],
|
|
||||||
ExampleAnswers = []
|
|
||||||
};
|
|
||||||
|
|
||||||
var exampleFiles = dir.GetFiles()
|
|
||||||
.Where(f => f.Name.StartsWith(MissionStatementPaths.ExampleFilePrefix))
|
|
||||||
.OrderBy(f =>
|
|
||||||
{
|
|
||||||
var numberPart = f.Name[MissionStatementPaths.ExampleFilePrefix.Length..];
|
|
||||||
if (numberPart.Contains('.'))
|
|
||||||
numberPart = numberPart[..numberPart.IndexOf(".", StringComparison.Ordinal)];
|
|
||||||
return int.TryParse(numberPart, out var num) ? num : int.MaxValue;
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach (var exampleFile in exampleFiles)
|
|
||||||
{
|
|
||||||
var content = System.IO.File.ReadAllText(exampleFile.FullName);
|
|
||||||
if (exampleFile.Name.EndsWith("a"))
|
|
||||||
data.ExampleAnswers.Add(content);
|
|
||||||
else
|
|
||||||
data.Examples.Add(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CleanupTemporaryFiles(string unpackFolder, string packageZipPath, string statementsZipPath)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Directory.Exists(unpackFolder))
|
foreach (var path in paths)
|
||||||
Directory.Delete(unpackFolder, true);
|
{
|
||||||
if (System.IO.File.Exists(packageZipPath))
|
if (Directory.Exists(path))
|
||||||
System.IO.File.Delete(packageZipPath);
|
Directory.Delete(path, true);
|
||||||
if (System.IO.File.Exists(statementsZipPath))
|
else if (System.IO.File.Exists(path))
|
||||||
System.IO.File.Delete(statementsZipPath);
|
System.IO.File.Delete(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -343,16 +215,3 @@ public class MissionService : IMissionService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Внутренняя модель для структуры данных описания миссии
|
|
||||||
/// </summary>
|
|
||||||
internal class JsonMissionData
|
|
||||||
{
|
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string Input { get; set; } = "";
|
|
||||||
public string Output { get; set; } = "";
|
|
||||||
public string Legend { get; set; } = "";
|
|
||||||
public List<string> Examples { get; set; } = [];
|
|
||||||
public List<string> ExampleAnswers { get; set; } = [];
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ public class SubmitService : ISubmitService
|
|||||||
string language,
|
string language,
|
||||||
string languageVersion,
|
string languageVersion,
|
||||||
int? contestId,
|
int? contestId,
|
||||||
SubmissionSourceType sourceType,
|
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -70,6 +69,7 @@ public class SubmitService : ISubmitService
|
|||||||
}
|
}
|
||||||
|
|
||||||
DbContest? contest = null;
|
DbContest? contest = null;
|
||||||
|
|
||||||
var finalSourceType = sourceType;
|
var finalSourceType = sourceType;
|
||||||
if (contestId.HasValue)
|
if (contestId.HasValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,9 +22,6 @@ public class DbMission : ISoftDeletable, ITimestamped
|
|||||||
[StringLength(256)]
|
[StringLength(256)]
|
||||||
public string S3PrivateKey { get; init; } = "";
|
public string S3PrivateKey { get; init; } = "";
|
||||||
|
|
||||||
[StringLength(256)]
|
|
||||||
public string S3ContentKey { get; set; } = "";
|
|
||||||
|
|
||||||
public int Difficulty { get; init; }
|
public int Difficulty { get; init; }
|
||||||
|
|
||||||
public ICollection<DbMissionTag> MissionTags { get; init; } = new HashSet<DbMissionTag>();
|
public ICollection<DbMissionTag> MissionTags { get; init; } = new HashSet<DbMissionTag>();
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace LiquidCode.Infrastructure.Database.Entities;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Сущность текстовых данных миссии с составным индексом
|
|
||||||
/// </summary>
|
|
||||||
[Index(nameof(MissionId), nameof(Language), IsUnique = true)]
|
|
||||||
[Index(nameof(Language))]
|
|
||||||
public class DbMissionPublicTextData : ITimestamped
|
|
||||||
{
|
|
||||||
public int Id { get; init; }
|
|
||||||
|
|
||||||
[Required]
|
|
||||||
public int? MissionId { get; init; }
|
|
||||||
|
|
||||||
[StringLength(64)]
|
|
||||||
public string Language { get; init; } = "";
|
|
||||||
|
|
||||||
[StringLength(30000)]
|
|
||||||
public string Data { get; init; } = "";
|
|
||||||
|
|
||||||
// Timestamps
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ public class LiquidDbContext : DbContext
|
|||||||
public DbSet<DbUser> Users { get; set; } = null!;
|
public DbSet<DbUser> Users { get; set; } = null!;
|
||||||
public DbSet<DbRefreshToken> RefreshTokens { get; set; } = null!;
|
public DbSet<DbRefreshToken> RefreshTokens { get; set; } = null!;
|
||||||
public DbSet<DbMission> Missions { get; set; } = null!;
|
public DbSet<DbMission> Missions { get; set; } = null!;
|
||||||
public DbSet<DbMissionPublicTextData> MissionsTextData { get; set; } = null!;
|
|
||||||
public DbSet<DbSolution> Solutions { get; set; } = null!;
|
public DbSet<DbSolution> Solutions { get; set; } = null!;
|
||||||
public DbSet<DbUserSubmission> UserSubmits { get; set; } = null!;
|
public DbSet<DbUserSubmission> UserSubmits { get; set; } = null!;
|
||||||
public DbSet<DbArticle> Articles { get; set; } = null!;
|
public DbSet<DbArticle> Articles { get; set; } = null!;
|
||||||
|
|||||||
@@ -124,22 +124,6 @@ public class MissionRepository : IMissionRepository
|
|||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DbMissionPublicTextData?> GetMissionTextAsync(int missionId, string language, CancellationToken cancellationToken = default) =>
|
|
||||||
await _dbContext.MissionsTextData
|
|
||||||
.FirstOrDefaultAsync(m => m.MissionId == missionId && m.Language == language, cancellationToken);
|
|
||||||
|
|
||||||
public async Task<IEnumerable<string>> GetMissionLanguagesAsync(int missionId, CancellationToken cancellationToken = default) =>
|
|
||||||
await _dbContext.MissionsTextData
|
|
||||||
.Where(m => m.MissionId == missionId)
|
|
||||||
.Select(m => m.Language)
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
public async Task CreateMissionTextAsync(DbMissionPublicTextData textData, CancellationToken cancellationToken = default) =>
|
|
||||||
await _dbContext.MissionsTextData.AddAsync(textData, cancellationToken);
|
|
||||||
|
|
||||||
public async Task CreateMissionTextsAsync(IEnumerable<DbMissionPublicTextData> textData, CancellationToken cancellationToken = default) =>
|
|
||||||
await _dbContext.MissionsTextData.AddRangeAsync(textData, cancellationToken);
|
|
||||||
|
|
||||||
public async Task<int> CountMissionsAsync(CancellationToken cancellationToken = default) =>
|
public async Task<int> CountMissionsAsync(CancellationToken cancellationToken = default) =>
|
||||||
await _dbContext.Set<DbMission>().CountAsync(cancellationToken);
|
await _dbContext.Set<DbMission>().CountAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public static class AppConstants
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Время истечения JWT токена в минутах
|
/// Время истечения JWT токена в минутах
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int JwtExpirationMinutes = 10;
|
public const int JwtExpirationMinutes = 1440; // TODO: убавить, день для удобства
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Время истечения токена обновления в днях
|
/// Время истечения токена обновления в днях
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Script to update namespaces in the LiquidCode project
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/LiquidCode"
|
|
||||||
|
|
||||||
echo "Updating namespaces..."
|
|
||||||
|
|
||||||
# Update Infrastructure/Database/Entities
|
|
||||||
find Infrastructure/Database/Entities -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Models\.Database/namespace LiquidCode.Infrastructure.Database.Entities/g' {} \;
|
|
||||||
|
|
||||||
# Update Infrastructure/Database
|
|
||||||
sed -i 's/namespace LiquidCode\.Db/namespace LiquidCode.Infrastructure.Database/g' Infrastructure/Database/LiquidDbContext.cs
|
|
||||||
sed -i 's/namespace LiquidCode\.Db/namespace LiquidCode.Infrastructure.Database/g' Infrastructure/Database/ConnectionStringParser.cs
|
|
||||||
sed -i 's/using LiquidCode\.Models\.Database/using LiquidCode.Infrastructure.Database.Entities/g' Infrastructure/Database/LiquidDbContext.cs
|
|
||||||
|
|
||||||
# Update Domain/Services
|
|
||||||
find Domain/Services -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.AuthService/namespace LiquidCode.Domain.Services.Authentication/g' {} \;
|
|
||||||
find Domain/Services -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.MissionService/namespace LiquidCode.Domain.Services.Missions/g' {} \;
|
|
||||||
find Domain/Services -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.SubmitService/namespace LiquidCode.Domain.Services.Submits/g' {} \;
|
|
||||||
|
|
||||||
# Update Domain/Repositories
|
|
||||||
find Domain/Repositories -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Repositories/namespace LiquidCode.Domain.Repositories/g' {} \;
|
|
||||||
|
|
||||||
# Update Infrastructure/External
|
|
||||||
find Infrastructure/External -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.S3ClientService/namespace LiquidCode.Infrastructure.External.S3/g' {} \;
|
|
||||||
find Infrastructure/External -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services\.TestingModuleHttpClient/namespace LiquidCode.Infrastructure.External.TestingModule/g' {} \;
|
|
||||||
find Infrastructure/External -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Services/namespace LiquidCode.Infrastructure.External.S3/g' {} \;
|
|
||||||
|
|
||||||
# Update Shared
|
|
||||||
find Shared -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Models\.Constants/namespace LiquidCode.Shared.Constants/g' {} \;
|
|
||||||
find Shared -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Extensions/namespace LiquidCode.Shared.Extensions/g' {} \;
|
|
||||||
find Shared -name "*.cs" -type f -exec sed -i 's/namespace LiquidCode\.Middleware/namespace LiquidCode.Shared.Middleware/g' {} \;
|
|
||||||
|
|
||||||
# Update using statements in all new files
|
|
||||||
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Database/using LiquidCode.Infrastructure.Database.Entities/g' {} \;
|
|
||||||
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Constants/using LiquidCode.Shared.Constants/g' {} \;
|
|
||||||
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Repositories/using LiquidCode.Domain.Repositories/g' {} \;
|
|
||||||
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Extensions/using LiquidCode.Shared.Extensions/g' {} \;
|
|
||||||
find Domain -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Tools/using LiquidCode.Shared.Tools/g' {} \;
|
|
||||||
|
|
||||||
find Infrastructure -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Constants/using LiquidCode.Shared.Constants/g' {} \;
|
|
||||||
find Infrastructure -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Database/using LiquidCode.Infrastructure.Database.Entities/g' {} \;
|
|
||||||
|
|
||||||
find Shared -name "*.cs" -type f -exec sed -i 's/using LiquidCode\.Models\.Constants/using LiquidCode.Shared.Constants/g' {} \;
|
|
||||||
|
|
||||||
echo "Namespaces updated successfully!"
|
|
||||||
Reference in New Issue
Block a user