using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Keyapis.EF.DataAnnotations; using Keyapis.EF.Extensions; using Keyapis.EF.ValueConverters; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; // ReSharper disable once CheckNamespace namespace Keyapis.EF.Banner.V1.Data.BannerDb; public interface IBannerDbContext : IAsyncDisposable, IDisposable { public DbSet Banners { get; set; } public Task SaveChangesAsync(CancellationToken cancellationToken = default); } public abstract class BannerDbContextBase : DbContext, IBannerDbContext { protected BannerDbContextBase(DbContextOptions options) : base(options) { } public DbSet Banners { get; set; } = null!; protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.HasPostgresExtension("pg_trgm"); foreach(var entity in builder.Model.GetEntityTypes()) { foreach(var property in entity.GetProperties()) { var memberInfo = property.PropertyInfo ?? (MemberInfo ?)property.FieldInfo; if (memberInfo == null) continue; var customRangeAttribute = memberInfo.GetCustomAttribute(); if (customRangeAttribute != null) { var actualType = Nullable.GetUnderlyingType(property.ClrType) ?? property.ClrType; if (actualType == typeof(string) || typeof(IEnumerable).IsAssignableFrom(actualType)) { property.SetMaxLength(customRangeAttribute.Maximum > int.MaxValue ? int.MaxValue : (int)customRangeAttribute.Maximum); } } var customRequiredAttribute = memberInfo.GetCustomAttribute(); if (customRequiredAttribute != null) { property.IsNullable = false; } } } builder.Entity(e => { // Для поля id дополнительные конфигурации свойства не нужны // Для поля id сопоствалений с фильтром и сортировкой не найденно e.Property(p => p.Type) .HasMaxLength(32) .HasConversion(new EnumToStringConverter()); // Найдено сопоставление: model.type ~ filter.types e.HasIndex(p => p.Type) .IsDescending(); // Для поля priority дополнительные конфигурации свойства не нужны // Найдено сопоставление: model.priority ~ order.PRIORITY_THEN_SHOW_START_AT e.HasIndex(p => new { p.Priority, p.ShowStartAt }) .HasMethod("btree") .IsDescending(); // Поле priority_type вычисляется в коде // Для поля title дополнительные конфигурации свойства не нужны // Для поля title сопоствалений с фильтром и сортировкой не найденно // Для поля header дополнительные конфигурации свойства не нужны // Для поля header сопоствалений с фильтром и сортировкой не найденно // Для поля header_color дополнительные конфигурации свойства не нужны // Для поля header_color сопоствалений с фильтром и сортировкой не найденно // Для поля subtitle дополнительные конфигурации свойства не нужны // Для поля subtitle сопоствалений с фильтром и сортировкой не найденно // Для поля subtitle_color дополнительные конфигурации свойства не нужны // Для поля subtitle_color сопоствалений с фильтром и сортировкой не найденно // Для поля background_color дополнительные конфигурации свойства не нужны // Для поля background_color сопоствалений с фильтром и сортировкой не найденно e.Property(p => p.BackgroundGradient) .HasColumnType("jsonb") .HasConversion(new CustomJsonConverter()); e.HasIndex(p => p.BackgroundGradient) .HasMethod("gin") .HasOperators("jsonb_path_ops"); // Для поля repeat_after_days дополнительные конфигурации свойства не нужны // Для поля repeat_after_days сопоствалений с фильтром и сортировкой не найденно // Для поля show_start_at дополнительные конфигурации свойства не нужны // Найдено сопоставление: model.show_start_at ~ order.SHOW_START_AT e.HasIndex(p => p.ShowStartAt) .HasMethod("btree") .IsDescending(); // Для поля show_ended_at дополнительные конфигурации свойства не нужны // Найдено сопоставление: model.show_ended_at ~ order.SHOW_ENDED_AT e.HasIndex(p => p.ShowEndedAt) .HasMethod("btree") .IsDescending(); // Поле status_type вычисляется в коде e.Property(p => p.Images) .HasColumnType("jsonb") .HasConversion(new CustomJsonConverter>(), new ValueComparer>( (c1, c2) => (c1 != null && c2 != null && c1.SequenceEqual(c2)), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => c.ToList() )) .HasDefaultValue(new List()) .HasColumnType("jsonb"); e.HasIndex(p => p.Images) .HasMethod("gin") .HasOperators("jsonb_ops"); e.Property(p => p.Links) .HasColumnType("jsonb") .HasConversion(new CustomJsonConverter>(), new ValueComparer>( (c1, c2) => (c1 != null && c2 != null && c1.SequenceEqual(c2)), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => c.ToList() )) .HasDefaultValue(new List()) .HasColumnType("jsonb"); e.HasIndex(p => p.Links) .HasMethod("gin") .HasOperators("jsonb_ops"); e.Property(p => p.UserTags) .HasDefaultValue(new List()); // Найдено сопоставление: model.user_tags ~ filter.user_tags e.HasIndex(p => p.UserTags) .HasMethod("gin"); e.Property(p => p.AppTags) .HasDefaultValue(new List()); // Найдено сопоставление: model.app_tags ~ filter.app_tags e.HasIndex(p => p.AppTags) .HasMethod("gin"); e.Property(p => p.CreatedAt) .HasColumnType("timestamp without time zone") .HasConversion(DateTimeUtcConverter.Instance); // Найдено сопоставление: model.created_at ~ order.CREATED_AT e.HasIndex(p => p.CreatedAt) .HasMethod("btree") .IsDescending(); e.Property(p => p.ChangedAt) .HasColumnType("timestamp without time zone") .HasConversion(DateTimeUtcConverter.Instance) .IsConcurrencyToken(); // Найдено сопоставление: model.changed_at ~ order.CHANGED_AT e.HasIndex(p => p.ChangedAt) .HasMethod("btree") .IsDescending(); // Дополнительные индексы: // По полю BannerFilter.(title,header,subtitle) e.HasIndex(p => new { p.Title, p.Header, p.Subtitle }) .HasMethod("gin") .IsTsVectorExpressionIndex("russian"); }); builder.HasDefaultSchema("banner"); } } /// /// Баннер. /// # Описание модели /// [ Display(Name = @"Баннер"), Description(@"Описание модели") ] public partial class BannerModel { /// /// Идентификатор. /// Если не передан создаётся сервером. /// # Тип: Guid /// [ Display(Name = @"Идентификатор"), Description(@"Если не передан создаётся сервером. Тип: Guid"), CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid Id { get; set; } /// /// Тип баннера. /// Обязательное /// [ Display(Name = @"Тип баннера"), Description(@"Обязательное"), CustomRequired ] public BannerModel.BannerType Type { get; set; } /// /// Приоритет. /// Обязательное. /// # Диапазон: 1..255 /// [ Display(Name = @"Приоритет"), Description(@"Обязательное. Диапазон: 1..255"), CustomRequired, CustomRange(1, 255) ] public int Priority { get; set; } // Поле priority_type вычисляется в коде /// /// Название. /// Обязательное. /// Обычно это название рекламной компании. /// Не отображается пользователям. /// # Диапазон: 3..256 /// [ Display(Name = @"Название"), Description(@"Обязательное. Обычно это название рекламной компании. Не отображается пользователям. Диапазон: 3..256"), CustomRequired, CustomRange(3, 256) ] public string Title { get; set; } = null!; /// /// Заголовок. /// # Диапазон: 3..64 /// [ Display(Name = @"Заголовок"), Description(@"Диапазон: 3..64"), CustomRange(3, 64) ] public string? Header { get; set; } /// /// Цвет текста заголовка. /// Все цвета должны быть в формате hex #RRGGBBAA. /// Пример: #7e00c380. /// # Диапазон: 9..9. /// # Паттерн: /^#[0-9a-fA-F]{8}$/ /// [ Display(Name = @"Цвет текста заголовка"), Description(@"Все цвета должны быть в формате hex #RRGGBBAA. Пример: #7e00c380. Диапазон: 9..9. Паттерн: /^#[0-9a-fA-F]{8}$/"), CustomRange(9, 9), CustomPattern(@"^#[0-9a-fA-F]{8}$") ] public string? HeaderColor { get; set; } /// /// Подзаголовок. /// # Диапазон: 3..256 /// [ Display(Name = @"Подзаголовок"), Description(@"Диапазон: 3..256"), CustomRange(3, 256) ] public string? Subtitle { get; set; } /// /// Цвет текста подзаголовка. /// Все цвета должны быть в формате hex #RRGGBBAA. /// Пример: #7e00c380. /// # Диапазон: 9..9. /// # Паттерн: /^#[0-9a-fA-F]{8}$/ /// [ Display(Name = @"Цвет текста подзаголовка"), Description(@"Все цвета должны быть в формате hex #RRGGBBAA. Пример: #7e00c380. Диапазон: 9..9. Паттерн: /^#[0-9a-fA-F]{8}$/"), CustomRange(9, 9), CustomPattern(@"^#[0-9a-fA-F]{8}$") ] public string? SubtitleColor { get; set; } /// /// Цвет заднего плана(подложки). /// Отрисовывается до загрузки картинки и градиента. /// Все цвета должны быть в формате hex #RRGGBBAA. /// Пример: #7e00c380. /// # Диапазон: 9..9. /// # Паттерн: /^#[0-9a-fA-F]{8}$/ /// [ Display(Name = @"Цвет заднего плана"), Description(@"Отрисовывается до загрузки картинки и градиента. Все цвета должны быть в формате hex #RRGGBBAA. Пример: #7e00c380. Диапазон: 9..9. Паттерн: /^#[0-9a-fA-F]{8}$/"), CustomRange(9, 9), CustomPattern(@"^#[0-9a-fA-F]{8}$") ] public string? BackgroundColor { get; set; } /// /// Градиент /// [ Display(Name = @"Градиент") ] public BannerModel.GradientModel? BackgroundGradient { get; set; } /// /// Количество дней через которое можно повторно показать баннер. /// Если null, то закрыть нельзя, баннер повторно выводится каждый раз. /// # Диапазон: 0..366 /// [ Display(Name = @"Количество дней через которое можно повторно показать баннер"), Description(@"Если null, то закрыть нельзя, баннер повторно выводится каждый раз. Диапазон: 0..366"), CustomRange(0, 366) ] public int? RepeatAfterDays { get; set; } /// /// Дата начала показа. /// Обязательное. /// Бессрочно если значение 2000-01-01. /// # Тип: DateOnly. /// # Диапазон: 730119..1095362 /// [ Display(Name = @"Дата начала показа"), Description(@"Обязательное. Бессрочно если значение 2000-01-01. Тип: DateOnly. Диапазон: 730119..1095362"), CustomRequired, CustomRange(730119, 1095362) ] public DateOnly ShowStartAt { get; set; } /// /// Дата конца показа. /// Обязательное. /// Бессрочно если значение 3000-01-01. /// # Тип: DateOnly. /// # Диапазон: 730119..1095362 /// [ Display(Name = @"Дата конца показа"), Description(@"Обязательное. Бессрочно если значение 3000-01-01. Тип: DateOnly. Диапазон: 730119..1095362"), CustomRequired, CustomRange(730119, 1095362) ] public DateOnly ShowEndedAt { get; set; } // Поле status_type вычисляется в коде /// /// Картинки. /// # Диапазон: 0..20 /// [ Display(Name = @"Картинки"), Description(@"Диапазон: 0..20"), CustomRange(0, 20) ] public List Images { get; set; } = new(0); /// /// Ссылки для переходов. /// # Диапазон: 0..100 /// [ Display(Name = @"Ссылки для переходов"), Description(@"Диапазон: 0..100"), CustomRange(0, 100) ] public List Links { get; set; } = new(0); /// /// Список тегов пользователей. /// Обязательное. /// Описывает кто может видеть баннер. /// Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. /// # Диапазон: 1..100. /// # Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/ /// [ Display(Name = @"Список тегов пользователей"), Description(@"Обязательное. Описывает кто может видеть баннер. Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. Диапазон: 1..100. Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/"), CustomRequired, CustomRange(1, 100), CustomPattern(@"^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$") ] public List UserTags { get; set; } = new(0); /// /// Список тегов приложений. /// Обязательное. /// Описывает какие клиенты могут видеть баннер. /// Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. /// # Диапазон: 1..20. /// # Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/ /// [ Display(Name = @"Список тегов приложений"), Description(@"Обязательное. Описывает какие клиенты могут видеть баннер. Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. Диапазон: 1..20. Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/"), CustomRequired, CustomRange(1, 20), CustomPattern(@"^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$") ] public List AppTags { get; set; } = new(0); /// /// Дата создания. /// # Тип: DateTime /// [ Display(Name = @"Дата создания"), Description(@"Тип: DateTime"), Editable(false) ] public DateTime CreatedAt { get; set; } /// /// Дата последнего изменения. /// Заполняется и обновляется сервером. /// Заполняется при создании и изменении. /// Является версией объекта. /// # Тип: DateTime /// [ Display(Name = @"Дата последнего изменения"), Description(@"Заполняется и обновляется сервером. Заполняется при создании и изменении. Является версией объекта. Тип: DateTime"), Editable(false) ] public DateTime ChangedAt { get; set; } /// /// Градиент /// [ Display(Name = @"Градиент") ] public partial class GradientModel { /// /// Линейный градиент /// [ Display(Name = @"Линейный градиент") ] public BannerModel.GradientModel.LinearModel? Linear { get; set; } /// /// Линейный градиент /// [ Display(Name = @"Линейный градиент") ] public partial class LinearModel { /// /// Угол направления линейного градиента в градусах. /// # Диапазон: 0..360 /// [ Display(Name = @"Угол направления линейного градиента в градусах"), Description(@"Диапазон: 0..360"), CustomRange(0, 360) ] public float Angle { get; set; } /// /// Точки градиента. /// Обязательное. /// # Диапазон: 2..20 /// [ Display(Name = @"Точки градиента"), Description(@"Обязательное. Диапазон: 2..20"), CustomRequired, CustomRange(2, 20) ] public List Points { get; set; } = new(0); /// /// Точка /// [ Display(Name = @"Точка") ] public partial class PointModel { /// /// Цвет линейного градиента. /// Обязательное. /// Отрисовывается до загрузки картинки и градиента. /// Все цвета должны быть в формате hex #RRGGBBAA. /// Пример: #7e00c380. /// # Диапазон: 9..9. /// # Паттерн: /^#[0-9a-fA-F]{8}$/ /// [ Display(Name = @"Цвет линейного градиента"), Description(@"Обязательное. Отрисовывается до загрузки картинки и градиента. Все цвета должны быть в формате hex #RRGGBBAA. Пример: #7e00c380. Диапазон: 9..9. Паттерн: /^#[0-9a-fA-F]{8}$/"), CustomRequired, CustomRange(9, 9), CustomPattern(@"^#[0-9a-fA-F]{8}$") ] public string Color { get; set; } = null!; /// /// Стоп линейного градиента. /// Записывается в процентах. /// # Диапазон: 0..100 /// [ Display(Name = @"Стоп линейного градиента"), Description(@"Записывается в процентах. Диапазон: 0..100"), CustomRange(0, 100) ] public float Stop { get; set; } } } } /// /// Объект картинки /// [ Display(Name = @"Объект картинки") ] public partial class ImageModel { /// /// Тип картинки. /// Обязательное /// [ Display(Name = @"Тип картинки"), Description(@"Обязательное"), CustomRequired ] public BannerModel.ImageModel.BannerImageType Type { get; set; } /// /// Значение ссылки на картинку. /// Обязательное. /// # Диапазон: 12..512 /// [ Display(Name = @"Значение ссылки на картинку"), Description(@"Обязательное. Диапазон: 12..512"), CustomRequired, CustomRange(12, 512) ] public string Url { get; set; } = null!; /// /// Справочник типов картинок. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerImageType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("TYPE_UNKNOWN") ] Unknown = 0, /// /// Пререндеренный баннер в портретном режиме. /// В портретном режиме ширина меньше или равна высоте. /// Представление характерно для телефонов. /// Может содержать дополнительные параметры запроса в ссылке(https://docs.imgproxy.net/generating_the_url?id=processing-options). /// После загрузки изображения отрисовывается поверх цвета и градиента. /// Если ссылки нет то нужно использовать ссылку из ландшафтного режима /// [ Display(Name = @"Пререндеренный баннер в портретном режиме"), Description(@"В портретном режиме ширина меньше или равна высоте. Представление характерно для телефонов. Может содержать дополнительные параметры запроса в ссылке(https://docs.imgproxy.net/generating_the_url?id=processing-options). После загрузки изображения отрисовывается поверх цвета и градиента. Если ссылки нет то нужно использовать ссылку из ландшафтного режима"), JsonPropertyName("PORTRAIT") ] Portrait = 1, /// /// Пререндеренный баннер в ландшафтном режиме. /// В ландшафтном режиме ширина больше высоты. /// Представление характерно для планшетов и декстопа. /// Может содержать дополнительные параметры запроса в ссылке(https://docs.imgproxy.net/generating_the_url?id=processing-options). /// После загрузки изображения отрисовывается поверх цвета и градиента. /// Если ссылки нет то нужно использовать ссылку из портретного режима /// [ Display(Name = @"Пререндеренный баннер в ландшафтном режиме"), Description(@"В ландшафтном режиме ширина больше высоты. Представление характерно для планшетов и декстопа. Может содержать дополнительные параметры запроса в ссылке(https://docs.imgproxy.net/generating_the_url?id=processing-options). После загрузки изображения отрисовывается поверх цвета и градиента. Если ссылки нет то нужно использовать ссылку из портретного режима"), JsonPropertyName("LANDSCAPE") ] Landscape = 2, /// /// Иконка. /// Может содержать дополнительные параметры запроса в ссылке(https://docs.imgproxy.net/generating_the_url?id=processing-options). /// После загрузки изображения отрисовывается поверх цвета и градиента /// [ Display(Name = @"Иконка"), Description(@"Может содержать дополнительные параметры запроса в ссылке(https://docs.imgproxy.net/generating_the_url?id=processing-options). После загрузки изображения отрисовывается поверх цвета и градиента"), JsonPropertyName("ICON") ] Icon = 3, } } /// /// Ссылка /// [ Display(Name = @"Ссылка") ] public partial class LinkModel { /// /// Тип платформы. /// Обязательное /// [ Display(Name = @"Тип платформы"), Description(@"Обязательное"), CustomRequired ] public BannerModel.BannerPlatformType PlatformType { get; set; } /// /// Тип ссылки. /// Обязательное /// [ Display(Name = @"Тип ссылки"), Description(@"Обязательное"), CustomRequired ] public BannerModel.LinkModel.BannerLinkType Type { get; set; } /// /// Значение ссылки для перехода. /// Обязательное. /// # Диапазон: 12..512 /// [ Display(Name = @"Значение ссылки для перехода"), Description(@"Обязательное. Диапазон: 12..512"), CustomRequired, CustomRange(12, 512) ] public string Url { get; set; } = null!; /// /// Список пакетов. /// Если пусто попытка перехода осуществляется безусловно. /// Если заполнено, то переход осуществляется только в случае если хоть один пакет есть на устройстве, иначе обработка переходит к следующей ссылке. /// # Диапазон: 0..20 /// [ Display(Name = @"Список пакетов"), Description(@"Если пусто попытка перехода осуществляется безусловно. Если заполнено, то переход осуществляется только в случае если хоть один пакет есть на устройстве, иначе обработка переходит к следующей ссылке. Диапазон: 0..20"), CustomRange(0, 20) ] public List Packages { get; set; } = new(0); /// /// Справочник типов ссылок. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerLinkType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("TYPE_UNKNOWN") ] Unknown = 0, /// /// Ссылка для перехода на раздел текущего приложения /// [ Display(Name = @"Ссылка для перехода на раздел текущего приложения"), JsonPropertyName("INTERNAL_APP") ] InternalApp = 1, /// /// Ссылка для перехода на стороннее приложение /// [ Display(Name = @"Ссылка для перехода на стороннее приложение"), JsonPropertyName("EXTERNAL_APP") ] ExternalApp = 2, /// /// Ссылка для перехода на веб-вью внутренних (своих) приложений. /// В окне отсутствует адресаная строка. /// Для iOS используется WKWebView, для Android используется WebView /// [ Display(Name = @"Ссылка для перехода на веб-вью внутренних"), Description(@"В окне отсутствует адресаная строка. Для iOS используется WKWebView, для Android используется WebView"), JsonPropertyName("WEB_VIEW") ] WebView = 3, /// /// Ссылка для перехода на веб-браузер /// [ Display(Name = @"Ссылка для перехода на веб-браузер"), JsonPropertyName("WEB_BROWSER") ] WebBrowser = 4, /// /// Ссылка для перехода на веб-вью внешних (партнёрских) приложений. /// В окне присутствует адресаная строка. /// Для iOS используется SFSafariViewController, для Android используется ChromeCustomTabs /// [ Display(Name = @"Ссылка для перехода на веб-вью внешних"), Description(@"В окне присутствует адресаная строка. Для iOS используется SFSafariViewController, для Android используется ChromeCustomTabs"), JsonPropertyName("EXTERNAL_WEB_VIEW") ] ExternalWebView = 5, } } /// /// Ошибка сохранения. /// Эти проверки выполняются при работе с базой данных и сторонними сервисами /// [ Display(Name = @"Ошибка сохранения"), Description(@"Эти проверки выполняются при работе с базой данных и сторонними сервисами") ] public partial class SavingErrorModel { /// /// Конфликт версий /// [ Display(Name = @"Конфликт версий") ] public BannerModel.SavingErrorModel.ConflictModel? Conflict { get; set; } /// /// Конфликт версий. /// Причины: /// - В базе хранится другая версия строки, значения changed_at отличаются /// [ Display(Name = @"Конфликт версий"), Description(@"Причины: - В базе хранится другая версия строки, значения changed_at отличаются") ] public partial class ConflictModel { } } /// /// Справочник типов баннеров. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("TYPE_UNKNOWN") ] Unknown = 0, /// /// Стандартный баннер /// [ Display(Name = @"Стандартный баннер"), JsonPropertyName("STANDARD") ] Standard = 1, } /// /// Справочник типов приоритетов. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerPriorityType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("PRIORITY_TYPE_UNKNOWN") ] Unknown = 0, /// /// Обычный. /// Приоритет 31. /// Вычисляется в логике приложения как priority меньше 64. /// Извлекаются из базы по фильтру end_priority = 64 /// [ Display(Name = @"Обычный"), Description(@"Приоритет 31. Вычисляется в логике приложения как priority меньше 64. Извлекаются из базы по фильтру end_priority = 64"), JsonPropertyName("NORMAL") ] Normal = 1, /// /// Вайт-лейбл. /// Приоритет 127. /// Вычисляется в логике приложения как priority больше или равно 64 и priority меньше 160. /// Извлекаются из базы по фильтру begin_priority = 64 и end_priority = 160 /// [ Display(Name = @"Вайт-лейбл"), Description(@"Приоритет 127. Вычисляется в логике приложения как priority больше или равно 64 и priority меньше 160. Извлекаются из базы по фильтру begin_priority = 64 и end_priority = 160"), JsonPropertyName("WHITE_LABEL") ] WhiteLabel = 2, /// /// Критикл. /// Приоритет 191. /// Вычисляется в логике приложения как priority больше или равно 160. /// Извлекаются из базы по фильтру begin_priority = 160 /// [ Display(Name = @"Критикл"), Description(@"Приоритет 191. Вычисляется в логике приложения как priority больше или равно 160. Извлекаются из базы по фильтру begin_priority = 160"), JsonPropertyName("CRITICAL") ] Critical = 3, } /// /// Справочник статусов баннеров. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerStatusType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("STATUS_TYPE_UNKNOWN") ] Unknown = 0, /// /// Отложен. /// Вычисляется в логике приложения как сurr_date меньше show_start_at. /// Извлекаются из базы по фильтру end_show_start_at = сurr_date /// [ Display(Name = @"Отложен"), Description(@"Вычисляется в логике приложения как сurr_date меньше show_start_at. Извлекаются из базы по фильтру end_show_start_at = сurr_date"), JsonPropertyName("DEFERRED") ] Deferred = 1, /// /// Активен. /// Вычисляется в логике приложения как сurr_date больше или равно show_start_at и сurr_date меньше show_ended_at. /// Извлекаются из базы по фильтру begin_show_start_at = сurr_date и end_show_ended_at = сurr_date /// [ Display(Name = @"Активен"), Description(@"Вычисляется в логике приложения как сurr_date больше или равно show_start_at и сurr_date меньше show_ended_at. Извлекаются из базы по фильтру begin_show_start_at = сurr_date и end_show_ended_at = сurr_date"), JsonPropertyName("ACTIVE") ] Active = 2, /// /// Истёк. /// Вычисляется в логике приложения как сurr_date больше или равно show_ended_at. /// Извлекаются из базы по фильтру begin_show_ended_at = сurr_date /// [ Display(Name = @"Истёк"), Description(@"Вычисляется в логике приложения как сurr_date больше или равно show_ended_at. Извлекаются из базы по фильтру begin_show_ended_at = сurr_date"), JsonPropertyName("EXPIRED") ] Expired = 3, } /// /// Справочник платформ. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerPlatformType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("PLATFORM_TYPE_UNKNOWN") ] Unknown = 0, /// /// Платформа iOS. /// Приложение размещается в App Store /// [ Display(Name = @"Платформа iOS"), Description(@"Приложение размещается в App Store"), JsonPropertyName("IOS") ] Ios = 1, /// /// Платформа Android. /// Приложение размещается в Google Play, App Gallery или прочих сторах /// [ Display(Name = @"Платформа Android"), Description(@"Приложение размещается в Google Play, App Gallery или прочих сторах"), JsonPropertyName("ANDROID") ] Android = 2, /// /// Платформа Web. /// Приложение размещается по интернет адресу /// [ Display(Name = @"Платформа Web"), Description(@"Приложение размещается по интернет адресу"), JsonPropertyName("WEB") ] Web = 3, } } /// /// Облегчённый баннер /// [ Display(Name = @"Облегчённый баннер") ] public partial class BannerLiteModel { /// /// Идентификатор. /// # Тип: Guid /// [ Display(Name = @"Идентификатор"), Description(@"Тип: Guid"), CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid Id { get; set; } /// /// Количество дней через которое можно повторно показать баннер. /// Если Значение не указано то повторного показа баннера не будет /// [ Display(Name = @"Количество дней через которое можно повторно показать баннер"), Description(@"Если Значение не указано то повторного показа баннера не будет") ] public int? RepeatAfterDays { get; set; } /// /// Картинки /// [ Display(Name = @"Картинки") ] public List Images { get; set; } = new(0); /// /// Ссылки для переходов. /// Возвращаются отфильтрованные по платформе /// [ Display(Name = @"Ссылки для переходов"), Description(@"Возвращаются отфильтрованные по платформе") ] public List Links { get; set; } = new(0); /// /// Упрощённый объект картинки /// [ Display(Name = @"Упрощённый объект картинки") ] public partial class ImageLiteModel { /// /// Тип картинки. /// Обязательное /// [ Display(Name = @"Тип картинки"), Description(@"Обязательное"), CustomRequired ] public BannerModel.ImageModel.BannerImageType Type { get; set; } /// /// Значение ссылки на картинку. /// Обязательное /// [ Display(Name = @"Значение ссылки на картинку"), Description(@"Обязательное"), CustomRequired ] public string Url { get; set; } = null!; } /// /// Упрощённая ссылка /// [ Display(Name = @"Упрощённая ссылка") ] public partial class LinkLiteModel { /// /// Тип ссылки. /// Обязательное /// [ Display(Name = @"Тип ссылки"), Description(@"Обязательное"), CustomRequired ] public BannerModel.LinkModel.BannerLinkType Type { get; set; } /// /// Значение ссылки. /// Обязательное /// [ Display(Name = @"Значение ссылки"), Description(@"Обязательное"), CustomRequired ] public string Url { get; set; } = null!; /// /// Список пакетов. /// Если пусто попытка перехода осуществляется безусловно. /// Если заполнено, то переход осуществляется только в случае если хоть один пакет есть на устройстве иначе обработка переходит к следующей ссылке /// [ Display(Name = @"Список пакетов"), Description(@"Если пусто попытка перехода осуществляется безусловно. Если заполнено, то переход осуществляется только в случае если хоть один пакет есть на устройстве иначе обработка переходит к следующей ссылке") ] public List Packages { get; set; } = new(0); /// /// Тип платформы. /// Обязательное /// [ Display(Name = @"Тип платформы"), Description(@"Обязательное"), CustomRequired ] public BannerModel.BannerPlatformType PlatformType { get; set; } } } /// /// Фильтр баннеров /// [ Display(Name = @"Фильтр баннеров") ] public partial class BannerFilterModel { /// /// По тексту. /// Если значение не передано то поиск по нему не производится. /// # Диапазон: 3..64. /// # Поиск производится по полям: /// # - Название; /// # - Заголовок; /// # - Подзаголовок; /// # - Список тегов пользователей; /// # - Список тегов приложений /// [ Display(Name = @"По тексту"), Description(@"Если значение не передано то поиск по нему не производится. Диапазон: 3..64. Поиск производится по полям: - Название; - Заголовок; - Подзаголовок; - Список тегов пользователей; - Список тегов приложений"), CustomRange(3, 64) ] public string? Text { get; set; } /// /// По типам баннеров /// [ Display(Name = @"По типам баннеров") ] public List Types { get; set; } = new(0); /// /// От приоритета включительно (больше или равно) /// [ Display(Name = @"От приоритета включительно") ] public int? BeginPriority { get; set; } /// /// До приоритета (меньше) /// [ Display(Name = @"До приоритета") ] public int? EndPriority { get; set; } /// /// По типам приоритетов. /// Переписывает значения фильтров по приоритету. /// Если переданы все типы, то фильтр игнорируется(так как нужно вернуть всё) /// [ Display(Name = @"По типам приоритетов"), Description(@"Переписывает значения фильтров по приоритету. Если переданы все типы, то фильтр игнорируется(так как нужно вернуть всё)") ] public List PriorityTypes { get; set; } = new(0); /// /// От даты начала показа включительно (больше или равно) /// [ Display(Name = @"От даты начала показа включительно") ] public DateTime? BeginShowStartAt { get; set; } /// /// До даты начала показа (меньше) /// [ Display(Name = @"До даты начала показа") ] public DateTime? EndShowStartAt { get; set; } /// /// От даты конца показа включительно (больше или равно) /// [ Display(Name = @"От даты конца показа включительно") ] public DateTime? BeginShowEndedAt { get; set; } /// /// До даты конца показа (меньше) /// [ Display(Name = @"До даты конца показа") ] public DateTime? EndShowEndedAt { get; set; } /// /// По статусам баннеров. /// Переписывает значения фильтров по датам. /// Если переданы все типы, то фильтр игнорируется(так как нужно вернуть всё) /// [ Display(Name = @"По статусам баннеров"), Description(@"Переписывает значения фильтров по датам. Если переданы все типы, то фильтр игнорируется(так как нужно вернуть всё)") ] public List StatusTypes { get; set; } = new(0); /// /// По пользовательским тегам. /// Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. /// # Диапазон: 0..20. /// # Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/ /// [ Display(Name = @"По пользовательским тегам"), Description(@"Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. Диапазон: 0..20. Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/"), CustomRange(0, 20), CustomPattern(@"^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$") ] public List UserTags { get; set; } = new(0); /// /// По тегам приложений. /// Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. /// # Диапазон: 0..20. /// # Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/ /// [ Display(Name = @"По тегам приложений"), Description(@"Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. Диапазон: 0..20. Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/"), CustomRange(0, 20), CustomPattern(@"^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$") ] public List AppTags { get; set; } = new(0); } /// /// Упрощённый фильтр по баннерам /// [ Display(Name = @"Упрощённый фильтр по баннерам") ] public partial class BannerLiteFilterModel { /// /// По типам баннеров /// [ Display(Name = @"По типам баннеров") ] public List Types { get; set; } = new(0); /// /// Название приложения. /// # Диапазон: 1..19. /// # Паттерн: /^[0-9A-Z-]{1,19}$/ /// [ Display(Name = @"Название приложения"), Description(@"Диапазон: 1..19. Паттерн: /^[0-9A-Z-]{1,19}$/"), CustomRange(1, 19), CustomPattern(@"^[0-9A-Z-]{1,19}$") ] public string? AppName { get; set; } /// /// Тип платформы /// [ Display(Name = @"Тип платформы") ] public BannerModel.BannerPlatformType PlatformType { get; set; } /// /// Версия приложения. /// # Диапазон: 1..19. /// # Паттерн: /^[0-9A-Z-]{1,19}$/ /// [ Display(Name = @"Версия приложения"), Description(@"Диапазон: 1..19. Паттерн: /^[0-9A-Z-]{1,19}$/"), CustomRange(1, 19), CustomPattern(@"^[0-9A-Z-]{1,19}$") ] public string? Version { get; set; } /// /// По тегам пользователя. /// Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. /// # Диапазон: 1..20. /// # Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/ /// [ Display(Name = @"По тегам пользователя"), Description(@"Тэг - максимум 5 символов, значение - максимум 19 символов, количество сегментов - максимум 10. Диапазон: 1..20. Паттерн: /^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$/"), CustomRange(1, 20), CustomPattern(@"^[A-Z-]{1,5}_[0-9A-Z-]{1,19}(?:\.[A-Z-]{1,5}_[0-9A-Z-]{1,19}){0,9}$") ] public List UserTags { get; set; } = new(0); } /// /// Пагинация баннеров /// [ Display(Name = @"Пагинация баннеров") ] public partial class BannerPagingModel { /// /// Тип значения сортировки. /// Если значение не передано, то будет взято значение по умолчанию. /// # По умолчанию: CHANGED_AT /// [ Display(Name = @"Тип значения сортировки"), Description(@"Если значение не передано, то будет взято значение по умолчанию. По умолчанию: CHANGED_AT") ] public BannerPagingModel.BannerPagingOrderByType OrderByType { get; set; } = BannerPagingOrderByType.ChangedAt; /// /// Тип направления сортировки. /// # По умолчанию: DESC /// [ Display(Name = @"Тип направления сортировки"), Description(@"По умолчанию: DESC") ] public BannerPagingModel.BannerPagingDirectionType DirectionType { get; set; } = BannerPagingDirectionType.Desc; /// /// Количество записей на страницу. /// Если значение 0 (не передано), то будет взято значение по умолчанию. /// # Диапазон: 0..100. /// # По умолчанию: 20 /// [ Display(Name = @"Количество записей на страницу"), Description(@"Если значение 0 (не передано), то будет взято значение по умолчанию. Диапазон: 0..100. По умолчанию: 20"), CustomRange(0, 100) ] public int Limit { get; set; } = 20; /// /// Сдвиг. /// # Диапазон: 0..2147483647 /// [ Display(Name = @"Сдвиг"), Description(@"Диапазон: 0..2147483647"), CustomRange(0, 2147483647) ] public int Offset { get; set; } /// /// Справочник типов значений сортировки. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerPagingOrderByType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("ORDER_BY_TYPE_UNKNOWN") ] Unknown = 0, /// /// Дата последнего изменения /// [ Display(Name = @"Дата последнего изменения"), JsonPropertyName("CHANGED_AT") ] ChangedAt = 1, /// /// По приоритету, затем по дате начала показа /// [ Display(Name = @"По приоритету, затем по дате начала показа"), JsonPropertyName("PRIORITY_THEN_SHOW_START_AT") ] PriorityThenShowStartAt = 2, /// /// Дата создания /// [ Display(Name = @"Дата создания"), JsonPropertyName("CREATED_AT") ] CreatedAt = 3, /// /// Дата начала показа /// [ Display(Name = @"Дата начала показа"), JsonPropertyName("SHOW_START_AT") ] ShowStartAt = 4, /// /// Дата конца показа /// [ Display(Name = @"Дата конца показа"), JsonPropertyName("SHOW_ENDED_AT") ] ShowEndedAt = 5, /// /// По рангу для поиска по тексту. /// Применяется когда передано поле для поиска по тексту. /// В случае если текстовое поле не передано, применяется значение по умолчанию /// [ Display(Name = @"По рангу для поиска по тексту"), Description(@"Применяется когда передано поле для поиска по тексту. В случае если текстовое поле не передано, применяется значение по умолчанию"), JsonPropertyName("RANK") ] Rank = 6, } /// /// Справочник типов направлений сортировки. /// # Тип: byte /// [ Description("Тип: byte") ] public enum BannerPagingDirectionType : byte { /// /// Значение не указано /// [ Display(Name = @"Значение не указано"), JsonPropertyName("DIRECTION_TYPE_UNKNOWN") ] Unknown = 0, /// /// От большего к меньшему /// [ Display(Name = @"От большего к меньшему"), JsonPropertyName("DESC") ] Desc = 1, /// /// От меньшего к большему /// [ Display(Name = @"От меньшего к большему"), JsonPropertyName("ASC") ] Asc = 2, } } /// /// Запрос получения количества баннеров /// [ Display(Name = @"Запрос получения количества баннеров") ] public partial class GetBannerCountRequestModel { /// /// Фильтр /// [ Display(Name = @"Фильтр") ] public BannerFilterModel? Filter { get; set; } } /// /// Ответ на запрос получения количества баннеров /// [ Display(Name = @"Ответ на запрос получения количества баннеров") ] public partial class GetBannerCountResponseModel { /// /// Всего баннеров /// [ Display(Name = @"Всего баннеров") ] public int Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public GetBannerCountResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса получения количества баннеров /// [ Display(Name = @"Ошибка запроса получения количества баннеров") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } } } /// /// Запрос получения списка баннеров /// [ Display(Name = @"Запрос получения списка баннеров") ] public partial class GetBannerListRequestModel { /// /// Фильтр /// [ Display(Name = @"Фильтр") ] public BannerFilterModel? Filter { get; set; } /// /// Пагинация /// [ Display(Name = @"Пагинация") ] public BannerPagingModel? Paging { get; set; } } /// /// Ответ на запрос получения списка баннеров /// [ Display(Name = @"Ответ на запрос получения списка баннеров") ] public partial class GetBannerListResponseModel { /// /// Баннер /// [ Display(Name = @"Баннер") ] public BannerModel? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public GetBannerListResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса получения списка баннеров /// [ Display(Name = @"Ошибка запроса получения списка баннеров") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } } } /// /// Запрос получения списка упрощённого баннеров /// [ Display(Name = @"Запрос получения списка упрощённого баннеров") ] public partial class GetBannerLiteListRequestModel { /// /// Упрощённый фильтр /// [ Display(Name = @"Упрощённый фильтр") ] public BannerLiteFilterModel? Filter { get; set; } /// /// Признак отключения фильтрации по типу платформы /// [ Display(Name = @"Признак отключения фильтрации по типу платформы") ] public bool IsLinkFilterDisabled { get; set; } } /// /// Ответ на запрос получения списка упрощённых баннеров /// [ Display(Name = @"Ответ на запрос получения списка упрощённых баннеров") ] public partial class GetBannerLiteListResponseModel { /// /// Упрощённый баннер /// [ Display(Name = @"Упрощённый баннер") ] public BannerLiteModel? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public GetBannerLiteListResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса получения списка баннеров /// [ Display(Name = @"Ошибка запроса получения списка баннеров") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } } } /// /// Запрос получения упрощённого баннера /// [ Display(Name = @"Запрос получения упрощённого баннера") ] public partial class GetBannerLiteRequestModel { /// /// Идентификатор баннера. /// # Тип: Guid /// [ Display(Name = @"Идентификатор баннера"), Description(@"Тип: Guid"), CustomRequired, CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid Id { get; set; } } /// /// Ответ на запрос получения упрощённого баннера /// [ Display(Name = @"Ответ на запрос получения упрощённого баннера") ] public partial class GetBannerLiteResponseModel { /// /// Упрощённый баннер /// [ Display(Name = @"Упрощённый баннер") ] public BannerLiteModel? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public GetBannerLiteResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса получения упрощённого баннера /// [ Display(Name = @"Ошибка запроса получения упрощённого баннера") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } } } /// /// Запрос получения баннера /// [ Display(Name = @"Запрос получения баннера") ] public partial class GetBannerRequestModel { /// /// Идентификатор баннера. /// # Тип: Guid /// [ Display(Name = @"Идентификатор баннера"), Description(@"Тип: Guid"), CustomRequired, CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid Id { get; set; } } /// /// Ответ на запрос получения баннера /// [ Display(Name = @"Ответ на запрос получения баннера") ] public partial class GetBannerResponseModel { /// /// Баннер /// [ Display(Name = @"Баннер") ] public BannerModel? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public GetBannerResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса получения баннера /// [ Display(Name = @"Ошибка запроса получения баннера") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } } } /// /// Запрос сохранения баннера /// [ Display(Name = @"Запрос сохранения баннера") ] public partial class PostBannerRequestModel { /// /// Баннер /// [ Display(Name = @"Баннер"), CustomRequired ] public BannerModel Data { get; set; } = null!; } /// /// Ответ на запрос сохранения баннера /// [ Display(Name = @"Ответ на запрос сохранения баннера") ] public partial class PostBannerResponseModel { /// /// Баннер /// [ Display(Name = @"Баннер") ] public BannerModel? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public PostBannerResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса сохранения баннера /// [ Display(Name = @"Ошибка запроса сохранения баннера") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } /// /// Ошибка сохранения /// [ Display(Name = @"Ошибка сохранения") ] public BannerModel.SavingErrorModel? Saving { get; set; } } } /// /// Запрос загрузки картинки баннера /// [ Display(Name = @"Запрос загрузки картинки баннера") ] public partial class PostBannerUploadImageRequestModel { /// /// Идентификатор баннера. /// Используется для складывания картинок баннера в папку баннера в бакете. /// Название картинки в папке создаётся из случайного Guid. /// # Тип: Guid /// [ Display(Name = @"Идентификатор баннера"), Description(@"Используется для складывания картинок баннера в папку баннера в бакете. Название картинки в папке создаётся из случайного Guid. Тип: Guid"), CustomRequired, CustomPattern(@"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$") ] public Guid BannerId { get; set; } /// /// Свойство навигации /// [ JsonIgnore ] public BannerModel Banner { get; set; } = null!; /// /// Загружаемая картинка /// [ Display(Name = @"Загружаемая картинка"), CustomRequired ] public UploadImageModel UploadImage { get; set; } = null!; } /// /// Ответ на запрос загрузки картинки баннера /// [ Display(Name = @"Ответ на запрос загрузки картинки баннера") ] public partial class PostBannerUploadImageResponseModel { /// /// Ссылка на баннер на imageproxy cdn сервер. /// Общедоступна в интернете /// [ Display(Name = @"Ссылка на баннер на imageproxy cdn сервер"), Description(@"Общедоступна в интернете") ] public string? Data { get; set; } /// /// Ошибка /// [ Display(Name = @"Ошибка") ] public PostBannerUploadImageResponseModel.ErrorModel? Error { get; set; } /// /// Ошибка запроса загрузки картинки баннера /// [ Display(Name = @"Ошибка запроса загрузки картинки баннера") ] public partial class ErrorModel { /// /// Ошибка валидации /// [ Display(Name = @"Ошибка валидации") ] public ValidationErrorModel? Validation { get; set; } } } /// /// Объект загружаемой картинки /// [ Display(Name = @"Объект загружаемой картинки") ] public partial class UploadImageModel { /// /// Картинка в формате инлайн base64. /// Обязательное. /// Пример: \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA...\". /// # Паттерн: /^data:\s*image\/(png|jpeg|jpg);\s*base64/ /// [ Display(Name = @"Картинка в формате инлайн base64"), Description(@"Обязательное. Пример: ""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..."". Паттерн: /^data:\s*image\/(png|jpeg|jpg);\s*base64/"), CustomRequired, CustomPattern(@"^data:\s*image\/(png|jpeg|jpg);\s*base64") ] public string Base64 { get; set; } = null!; } /// /// Ошибки валидации. /// Эти проверки выполняются до обращения в базу данных /// [ Display(Name = @"Ошибки валидации"), Description(@"Эти проверки выполняются до обращения в базу данных") ] public partial class ValidationErrorModel { /// /// Путь к полю в формате наименования прото /// [ Display(Name = @"Путь к полю в формате наименования прото"), CustomRequired ] public string Path { get; set; } = null!; /// /// Валидационное сообщение /// [ Display(Name = @"Валидационное сообщение"), CustomRequired ] public string Message { get; set; } = null!; } /// /// Запрос проверки доступности сервиса /// [ Display(Name = @"Запрос проверки доступности сервиса") ] public partial class GetSystemStatusRequestModel { } /// /// Ответ на запрос проверки доступности сервиса /// [ Display(Name = @"Ответ на запрос проверки доступности сервиса") ] public partial class GetSystemStatusResponseModel { }