using System;
using Rugrid.Sdaid.Notation.Documents;
namespace Rugrid.Sdaid.Epas.Ntr.Infrastructure.AisOiaPeo
{
/// <summary>
/// Контракт интеграции с АИС ОИА ПЭО. Перенесено дословно из rugrid-ntr/docs/ais_data_import_spec.md.
/// Каждый заголовок источника — отдельная секция.
/// </summary>
public class DOC_AisDataImport : IInfrastructureDocument, IHasStructuralLinks
{
public SdaidLink[] Links => new[]
{
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_EnergyEfficiencyClasses>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_GlazingKind>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_GatesKind>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_InsulationMaterial>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_Material>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_ClimateRegion>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_BuildingPurpose>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.Reference.DOC_RoofType>(),
};
public static string _refName = "Импорт данных из АИС";
public string Name => "Импорт данных из АИС";
public string Description => @"";
public string Version => "1.0";
public string Status => "accepted";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisNaznachenie : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(object);
public int Order => 10;
public string Number => "";
public string Name => "Назначение";
public string Body => """"
Два отдельных сервиса выполняют импорт данных из внешней системы АИС (read-only БД `AisReadDbContext`) в соответствующие целевые БД:
| Сервис | Целевая БД | Точка входа | Результат |
|---|---|---|---|
| `AisToNtrImportService` | `NtrDbContext` | `ImportAsync` | `AisToNtrImportResult` |
| `AisToMverImportService` | `MverDbContext` | `ImportAsync` | `AisToMverImportResult` |
---
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisObschieArhitekturnyeResheniya : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(object);
public int Order => 20;
public string Number => "";
public string Name => "Общие архитектурные решения";
public string Body => """"
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisUpravleniePamyatyuEfCoreChangeTracker : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisObschieArhitekturnyeResheniya);
public int Order => 30;
public string Number => "";
public string Name => "Управление памятью EF Core (Change Tracker)";
public string Body => """"
Главная проблема: при импорте «в лоб» EF съедает ~1 ГБ памяти из-за Change Tracker.
**Решение:** оба сервиса принимают `IServiceScopeFactory` (не `DbContext` напрямую) и создают отдельный DI-scope на каждый логический блок импорта. После завершения scope DbContext и его Change Tracker освобождаются.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisTranzakcii : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisObschieArhitekturnyeResheniya);
public int Order => 40;
public string Number => "";
public string Name => "Транзакции";
public string Body => """"
Каждый блок/шаг импорта оборачивается в явную транзакцию (`BeginTransactionAsync` → `CommitAsync` / `RollbackAsync`). При ошибке откатывается только текущий блок; ранее сохранённые данные остаются в БД.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisPeredachaDannyhMezhduBlokami : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisObschieArhitekturnyeResheniya);
public int Order => 50;
public string Number => "";
public string Name => "Передача данных между блоками";
public string Body => """"
Между блоками данные передаются через словари маппинга `Dictionary<Guid, Guid>` (AisId → NtrId/MverId). Это необходимо, т.к. каждый блок работает в своём scope и не имеет доступа к tracked-сущностям предыдущего блока.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisIdempotentnostUpsertInsertOnly : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisObschieArhitekturnyeResheniya);
public int Order => 60;
public string Number => "";
public string Name => "Идемпотентность (upsert / insert-only)";
public string Body => """"
Большинство методов импорта реализуют паттерн **upsert**:
1. Загрузить исходные данные из АИС
2. Загрузить существующие записи из целевой БД по `AisId`
3. Если запись с таким `AisId` есть — обновить поля через Set-методы
4. Если нет — создать новую сущность и добавить в DbContext
**Исключения (insert-only):** некоторые модели не обновляют существующие записи, а только создают новые при отсутствии. Это осознанные решения — такие сущности являются корневыми или не содержат данных из АИС.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisStrategiyaObrabotkiOshibok : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisObschieArhitekturnyeResheniya);
public int Order => 70;
public string Number => "";
public string Name => "Стратегия обработки ошибок";
public string Body => """"
Оба сервиса используют стратегию **fail-fast**: если при импорте невозможно разрешить FK-зависимость, выбрасывается `NtrEntityNotFoundException` или `NtrApplicationException`. Текущая транзакция откатывается, ранее сохранённые блоки остаются в БД. Повторный запуск безопасен благодаря upsert.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisKonfiguraciya : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisObschieArhitekturnyeResheniya);
public int Order => 80;
public string Number => "";
public string Name => "Конфигурация";
public string Body => """"
`ImportAisDataConfiguration` содержит `AllowedCompanyNames` — список LIKE-паттернов для фильтрации компаний. Фильтр применяется в обоих сервисах при загрузке орг. структуры, зданий и связанных данных.
---
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisAistontrimportservice : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(object);
public int Order => 90;
public string Number => "";
public string Name => "AisToNtrImportService";
public string Body => """"
Импортирует данные НТР (нормативное теплопотребление). Работает с `NtrDbContext`.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlokiImporta : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisAistontrimportservice);
public int Order => 100;
public string Number => "";
public string Name => "Блоки импорта";
public string Body => """"
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok1SpravochnikiOdnaTranzakciyaOdinScope : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta);
public int Order => 110;
public string Number => "";
public string Name => "Блок 1: Справочники (одна транзакция, один scope)";
public string Body => """"
Записей мало, грузим все разом.
| Метод | Источник АИС | Целевая сущность | Возвращает |
|---|---|---|---|
| `ImportClimateRegionsAsync` | `RefDClimateData` | `RefClimateRegionEntity` | `Dict<AisId, NtrId>` |
| `ImportBasementAtticHeatingTypesAsync` | `RefDBasementAtticHeatingTypes` | `RefBasementAtticHeatingTypeEntity` | `Dict<AisId, NtrId>` |
| `ImportBuildingPurposesAsync` | `RefDBuildingTypes` | `RefBuildingPurposeEntity` | `Dict<AisId, NtrId>` |
| `ImportBuildingShapesAsync` | `RefDBuildingShapes` | `RefBuildingShapeEntity` | `Dict<AisId, NtrId>` |
| `ImportElectricityConsumerTypesAsync` | `RefDConsumerDeviceKinds` | `RefElectricityConsumerTypeEntity` | `Dict<AisId, NtrId>` |
| `ImportGateKindsAsync` | `RefDGateKinds` | `RefGateKindEntity` | `Dict<AisId, NtrId>` |
| `ImportGlazingKindsAsync` | `RefDGlazingKinds` | `RefGlazingKindEntity` | `Dict<AisId, NtrId>` |
| `ImportInsulationMaterialsAsync` | `RefDHeaterMaterials` | `RefInsulationMaterialEntity` | `Dict<AisId, NtrId>` |
| `ImportMaterialsAsync` | `RefDConstructionMaterials` | `RefMaterialEntity` | `Dict<AisId, NtrId>` |
| `ImportRoofTypesAsync` | `RefDRoofTypes` | `RefRoofTypeEntity` | `Dict<AisId, NtrId>` |
| `ImportEnergyEfficiencyClassesAsync` | `RefDEnergyEfficiencyClasses` | `RefEnergyEfficiencyClassEntity` | `int` (count) |
| `ImportSolarRadiationByDirectionsAsync` | `RefDSsrs` | `RefSolarRadiationByDirectionEntity` | `int` (count) |
| `ImportSpecificHeatConsumptionNormsAsync` | — | `RefSpecificHeatConsumptionNormEntity` | `int` (count) |
**Нюанс:** `ImportSolarRadiationByDirectionsAsync` выполняет маппинг сокращённых месяцев (`янв`, `фев`...) в полные названия и номера.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok2OrgStrukturaOdnaTranzakciyaOdinScope : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta);
public int Order => 120;
public string Number => "";
public string Name => "Блок 2: Орг. структура (одна транзакция, один scope)";
public string Body => """"
~1240 записей. Порядок импорта строго иерархический (FK-зависимости):
```
NavCompanies → NavIa (зависит от Companies)
→ NavMes (зависит от Companies)
→ NavPmes (зависит от Mes)
→ NavSubstations (зависит от Pmes, ClimateRegions)
→ Navs (зависит от всех вышеперечисленных)
```
| Метод | Целевая сущность |
|---|---|
| `ImportNavCompaniesAsync` | `NavCompanyEntity` |
| `ImportNavIaAsync` | `NavIaEntity` |
| `ImportMesAsync` | `NavMesEntity` |
| `ImportPmesAsync` | `NavPmesEntity` |
| `ImportSubstationsAsync` | `NavSubstationEntity` |
| `ImportNavsAsync` | `NavEntity` |
**Нюанс `ImportNavsAsync`:** ParentId устанавливается в два прохода — сначала создаются все Nav, затем второй проход устанавливает ParentId через словарь result.
**Отличие от Mver:** `ImportSubstationsAsync` дополнительно принимает `climateRegionsDictByAisId` и устанавливает `ClimateRegionId` подстанции.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok3ZdaniyaOdnaTranzakciyaOdinScope : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta);
public int Order => 130;
public string Number => "";
public string Name => "Блок 3: Здания (одна транзакция, один scope)";
public string Body => """"
~6450 записей.
| Метод | Описание |
|---|---|
| `FetchBuildingSourceDataAsync` | Загружает сырые данные зданий из АИС (включая параметры стен, крыши, подвала). Не создаёт сущности — только DTO. |
| `ImportBuildingsAsync` | Создаёт/обновляет `RegBuildingEntity`. Возвращает `Dict<AisId, BuildingDto>`. |
**Нюанс:** `BuildingSourceDataDto` содержит данные, которые позже распределяются по разным моделям (стены, крыша, контакт с грунтом, форма, условия эксплуатации). Этот DTO живёт в памяти до конца импорта.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok4ModeliPotrebleniyaKazhdayaOtdelnayaTranzakciyaCher : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta);
public int Order => 140;
public string Number => "";
public string Name => "Блок 4: Модели потребления (каждая — отдельная транзакция через `ExecuteInSeparateScopeAsync`)";
public string Body => """"
6450 зданий × 11 типов моделей = ~70950 потенциальных insert/update. Каждый тип создаётся в отдельной транзакции.
| # | Метод | Целевая сущность | Возвращает |
|---|---|---|---|
| 1 | `ImportConsumptionModels` | `CmConsumptionModelEntity` | `Dict<BuildingId, CmId>` (insert-only) |
| 2 | `ImportWindowsModelsAsync` | `CmWindowsModelEntity` | `Dict<CmId, WindowsModelId>` |
| 3 | `ImportGateModelsAsync` | `CmGateModelEntity` | `Dict<CmId, GateModelId>` |
| 4 | `ImportHeatingPeriodModelsAsync` | `CmHeatingPeriodModelEntity` | `Dict<CmId, HeatingPeriodModelId>` (insert-only) |
| 5 | `ImportRoofModelsAsync` | `CmRoofModelEntity` | `int` |
| 6 | `ImportGroundContactsAsync` | `CmGroundContactEntity` | `int` |
| 7 | `ImportVentilationsAsync` | `CmVentilationEntity` | `int` (insert-only) |
| 8 | `ImportLightingModelsAsync` | `CmLightingModelEntity` | `Dict<CmId, LightingModelId>` |
| 9 | `ImportEeConsumerModelAsync` | `CmEeConsumerModelEntity` | `Dict<CmId, EeConsumerModelId>` |
| 10 | `ImportOperatingConditions` | `CmOperatingConditionsEntity` | `int` |
| 11 | `ImportWallsModelsAsync` | `CmWallsModelEntity` | `int` |
| 12 | `ImportShapeAndDimensionsAsync` | `CmShapeAndDimensionsEntity` | `int` |
**Нюансы:**
- `ImportConsumptionModels` создаёт базовую модель через `CmConsumptionModelEntity.CreateBaseModel(buildingId, CmCalcKind.Custom.Id, buildingName)`. Insert-only.
- `ImportVentilationsAsync` создаёт заглушки (isMechVentExist=false, isRecuperatorExist=false) — данных в АИС нет. Insert-only.
- `ImportHeatingPeriodModelsAsync` создаёт модель с `CmHeatingPeriodConditionsSrc.ReferenceClimateData`.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok5SuschnostiModeleyKazhdayaOtdelnayaTranzakciyaChere : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta);
public int Order => 150;
public string Number => "";
public string Name => "Блок 5: Сущности моделей (каждая — отдельная транзакция через `ExecuteInSeparateScopeAsync`)";
public string Body => """"
Дочерние записи моделей из блока 4. Могут быть множественными на одно здание. Используют 3-параметровый `ExecuteInSeparateScopeAsync` (NtrDbContext + AisReadDbContext + CancellationToken).
| Метод | Целевая сущность | Примерное кол-во |
|---|---|---|
| `ImportWindowsEntitiesAsync` | `CmWindowsEntity` | N на здание |
| `ImportGateEntitiesAsync` | `CmGateEntity` | N на здание |
| `ImportLightingEntitiesAsync` | `CmLightingEntity` | N на здание |
| `ImportEeConsumerKindEntitiesAsync` | `CmEeConsumerKindEntity` | ~22641 |
| `ImportHeatingPeriodsAsync` | `CmHeatingPeriodEntity` | N на здание |
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisExecuteinseparatescopeasyncTriPeregruzki : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisAistontrimportservice);
public int Order => 160;
public string Number => "";
public string Name => "ExecuteInSeparateScopeAsync — три перегрузки";
public string Body => """"
```
// 1. С результатом, без AisReadDbContext (блок 4)
Task<TResult> ExecuteInSeparateScopeAsync<TResult>(Func<NtrDbContext, CancellationToken, Task<TResult>>)
// 2. Без результата, с AisReadDbContext (блок 5)
Task ExecuteInSeparateScopeAsync(Func<NtrDbContext, AisReadDbContext, CancellationToken, Task>)
// 3. Без результата, без AisReadDbContext
Task ExecuteInSeparateScopeAsync(Func<NtrDbContext, CancellationToken, Task>)
```
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisGrafZavisimostey : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisAistontrimportservice);
public int Order => 170;
public string Number => "";
public string Name => "Граф зависимостей";
public string Body => """"
```
Блок 1 (справочники)
├─→ climateRegionsDictByAisId ──────────→ Блок 2 (Substations), Блок 3 (Buildings)
├─→ basementAtticHeatingTypesDictByAisId → Блок 4 (RoofModels, GroundContacts)
├─→ buildingPurposesDictByAisId ────────→ Блок 4 (OperatingConditions)
├─→ buildingShapesDictByAisId ──────────→ Блок 4 (ShapeAndDimensions)
├─→ electricityConsumerTypesDictByAisId → Блок 5 (EeConsumerKindEntities)
├─→ gateKindsDictByAisId ──────────────→ Блок 5 (GateEntities)
├─→ glazingKindsDictByAisId ───────────→ Блок 5 (WindowsEntities)
├─→ insulationMaterialsDictByAisId ────→ Блок 4 (RoofModels, WallsModels)
├─→ materialsDictByAisId ─────────────→ Блок 4 (RoofModels, WallsModels)
└─→ roofTypesDictByAisId ─────────────→ Блок 4 (RoofModels)
Блок 2 (орг. структура)
└─→ navsDictByAisId ──→ Блок 3 (Buildings)
Блок 3 (здания)
├─→ buildingSourceDataDtosDictByAisId ──→ Блок 4 (Roof, GroundContact, Walls, Shape, OperatingConditions)
└─→ buildingsDictByAisId ──────────────→ Блок 4, Блок 5
Блок 4 (модели)
├─→ cmDictByBuildingId ───────→ Блок 4 (все модели), Блок 5
├─→ windowsModelsDictByCmId ─→ Блок 5 (WindowsEntities)
├─→ gateModelsDictByCmId ────→ Блок 5 (GateEntities)
├─→ heatingPeriodModelsDictByCmId → Блок 5 (HeatingPeriods)
├─→ lightingModelDictByCmId ─→ Блок 5 (LightingEntities)
└─→ eeConsumerModelDictByCmId → Блок 5 (EeConsumerKindEntities)
```
---
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisAistomverimportservice : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(object);
public int Order => 180;
public string Number => "";
public string Name => "AisToMverImportService";
public string Body => """"
Импортирует данные МВЭР (матрица выбора энергоэффективных решений). Работает с `MverDbContext`.
"""";
public string Version => "";
public string Status => "to-move";
public string[] Comments => new[] { "Раздел описывает импорт в МВЭР — кандидат на переезд в отдельный проект спеки Rugrid.Sdaid.Mver." };
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlokiImporta2 : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisAistomverimportservice);
public int Order => 190;
public string Number => "";
public string Name => "Блоки импорта";
public string Body => """"
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok1SpravochnikiMverOdnaTranzakciyaOdinScope : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta2);
public int Order => 200;
public string Number => "";
public string Name => "Блок 1: Справочники МВЭР (одна транзакция, один scope)";
public string Body => """"
Записей мало, грузим все разом.
| Метод | Источник АИС | Целевая сущность | Возвращает |
|---|---|---|---|
| `ImportHeatingTypesAsync` | `RefDHeatingTypes` | `RefHeatingTypeEntity` | `Dict<AisId, MverId>` |
| `ImportElectricDeviceTypesAsync` | `RefDConsumerDeviceKinds` | `RefElectricDeviceTypeEntity` | `Dict<AisId, MverId>` |
| `ImportHeatAdjustMethodsAsync` | `RefDHeatAdjustMethods` | `RefHeatAdjustMethodEntity` | `Dict<AisId, MverId>` |
| `ImportPeriodsAsync` | `RefPeriods` | `RefPeriodEntity` | `Dict<AisId, MverId>` |
| `ImportTransformerCoolTypesAsync` | `RefDTransformerCoolingTypes` | `RefTransformerCoolTypeEntity` | `Dict<AisId, MverId>` |
| `ImportTransformerTypesAsync` | `RefDTransformerTypes` | `RefTransformerTypeEntity` | `Dict<AisId, MverId>` |
"""";
public string Version => "";
public string Status => "to-move";
public string[] Comments => new[] { "Раздел описывает импорт в МВЭР — кандидат на переезд в отдельный проект спеки Rugrid.Sdaid.Mver." };
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok2OrgStrukturaOdnaTranzakciyaOdinScope2 : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta2);
public int Order => 210;
public string Number => "";
public string Name => "Блок 2: Орг. структура (одна транзакция, один scope)";
public string Body => """"
Идентична орг. структуре Ntr-импорта по логике, но работает с `MverDbContext`.
```
NavCompanies → NavIa → NavMes → NavPmes → NavSubstations → Navs
```
**Отличие от Ntr:** `ImportSubstationsAsync` не принимает `climateRegionsDictByAisId` — подстанции в МВЭР не привязаны к климатическому региону.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok3ZdaniyaOdnaTranzakciyaOdinScope2 : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta2);
public int Order => 220;
public string Number => "";
public string Name => "Блок 3: Здания (одна транзакция, один scope)";
public string Body => """"
~6450 записей. Создаёт/обновляет `RegBuildingEntity`.
- `FetchBuildingSourceDataAsync` — загружает сырые данные зданий из АИС
- `ImportBuildingsAsync` — upsert `RegBuildingEntity`. Возвращает `Dict<AisId, Guid>`.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisBlok4MverDannyeKazhdayaOtdelnayaTranzakciyaCherezExecut : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisBlokiImporta2);
public int Order => 230;
public string Number => "";
public string Name => "Блок 4: Mver данные (каждая — отдельная транзакция через `ExecuteInSeparateScopeAsync`)";
public string Body => """"
| Метод | Целевая сущность | Источник АИС | Стратегия |
|---|---|---|---|
| `ImportMverBuildingEeConsumersAsync` | `MverBuildingEeConsumerEntity` | `DocBuildingConsumersEes` | upsert |
| `ImportMverHeatedEquipmentsAsync` | `MverHeatedEquipmentEntity` | `DocHeatedEquipments` | upsert |
| `ImportMverTransformersAsync` | `MverTransformerEntity` | `RefETransformers` | upsert |
| `ImportMverTransformerWorkInfosAsync` | `MverTransformerWorkInfoEntity` | `DocTrWorks` + `DocTrWorksValues` | upsert, батчи по 10К |
| `ImportMverTsnsAsync` | `MverTsnEntity` | `RefETsns` | upsert |
**Нюансы:**
- **Фильтр по типу прибора:** `ImportMverBuildingEeConsumersAsync` — только `RefDConsumerDeviceKind.Code == "conditioner"`.
- **Name** для EeConsumer формируется как `"{DeviceKindName} ({Amount} шт., {Power} кВт)"`.
- **`ImportMverTransformerWorkInfosAsync`** фильтрует по `Year >= 2020`. Обходит данные постранично батчами по 10К с вызовом `SaveChanges + ChangeTracker.Clear()` на каждый батч. Фильтр по типу периода задаётся через константу `MonthPeriodType` в сервисе.
"""";
public string Version => "";
public string Status => "to-move";
public string[] Comments => new[] { "Раздел описывает импорт в МВЭР — кандидат на переезд в отдельный проект спеки Rugrid.Sdaid.Mver." };
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisExecuteinseparatescopeasyncOdnaPeregruzka : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisAistomverimportservice);
public int Order => 240;
public string Number => "";
public string Name => "ExecuteInSeparateScopeAsync — одна перегрузка";
public string Body => """"
```
// С результатом, с AisReadDbContext
Task<TResult> ExecuteInSeparateScopeAsync<TResult>(Func<MverDbContext, AisReadDbContext, CancellationToken, Task<TResult>>)
```
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisGrafZavisimostey2 : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisAistomverimportservice);
public int Order => 250;
public string Number => "";
public string Name => "Граф зависимостей";
public string Body => """"
```
Блок 1 (справочники)
├─→ heatingTypesDictByAisId ──────────→ Блок 3 (Buildings)
├─→ electricDeviceTypesDictByAisId ───→ Блок 4 (MverBuildingEeConsumers)
├─→ heatAdjustMethodsDictByAisId ────→ Блок 4 (MverHeatedEquipments)
├─→ periodsDictByAisId ──────────────→ Блок 4 (MverTransformerWorkInfos)
├─→ transformerCoolTypesDictByAisId ─→ Блок 4 (MverTransformers)
└─→ transformerTypesDictByAisId ─────→ Блок 4 (MverTransformers)
Блок 2 (орг. структура)
└─→ navsDictByAisId ──→ Блок 3 (Buildings), Блок 4 (все)
Блок 3 (здания)
└─→ buildingsDictByAisId ────────────→ Блок 4 (MverBuildingEeConsumers)
Блок 4 (Mver данные)
└─→ transformersDictByAisId ─────────→ Блок 4 (MverTransformerWorkInfos)
```
---
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisMappingPoleySpravochnikovAisNtr : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(object);
public int Order => 260;
public string Number => "";
public string Name => "Маппинг полей справочников (АИС → НТР)";
public string Body => """"
Большинство справочников маппят поля 1:1 (Id → AisId, Name → Name, Code → Code). Ниже перечислены только нетривиальные маппинги:
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisRefglazingkindentityVidOstekleniyaTolkoNtr : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisMappingPoleySpravochnikovAisNtr);
public int Order => 270;
public string Number => "";
public string Name => "RefGlazingKindEntity (вид остекления) — только NTR";
public string Body => """"
| АИС (`RefDGlazingKinds`) | НТР (`RefGlazingKindEntity`) |
|---|---|
| `CoefRf0` | `ThermalResistance` |
| `CoefTf` | `ShadingCoefficient` |
| `CoefKf` | `SolarHeatGainCoefficient` |
| `BreathabilityKgPerM2h` | `AirPermeability` |
| `Emissivity` | `EmissionCoefficient` |
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisRefgatekindentityVidVorotTolkoNtr : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisMappingPoleySpravochnikovAisNtr);
public int Order => 280;
public string Number => "";
public string Name => "RefGateKindEntity (вид ворот) — только NTR";
public string Body => """"
| АИС (`RefDGateKinds`) | НТР (`RefGateKindEntity`) |
|---|---|
| `CoefRv` | `ThermalResistance` |
| `BreathabilityKgPerM2h` | `AirPermeability` |
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisRefinsulationmaterialentityRefmaterialentityTolkoNtr : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisMappingPoleySpravochnikovAisNtr);
public int Order => 290;
public string Number => "";
public string Name => "RefInsulationMaterialEntity / RefMaterialEntity — только NTR";
public string Body => """"
| АИС | НТР |
|---|---|
| `LVtMK` | `ThermalConductivity` |
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisRefclimateregionentityKlimaticheskiyRayonTolkoNtr : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisMappingPoleySpravochnikovAisNtr);
public int Order => 300;
public string Number => "";
public string Name => "RefClimateRegionEntity (климатический район) — только NTR";
public string Body => """"
| АИС (`RefDClimateData`) | НТР (`RefClimateRegionEntity`) |
|---|---|
| `LatitudeSr` | `Latitude` |
| `LongitudeSr` | `Longitude` |
| `DaysAvgTempLe8` | `DaysAvgTempLe8` |
| `AvgTempLe8` | `AvgTempLe8` |
| `MaxAvgWindSpeedLe8` | `MaxAvgWindSpeedLe8` |
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisRefbuildingpurposeentityNaznachenieZdaniyTolkoNtr : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(AisMappingPoleySpravochnikovAisNtr);
public int Order => 310;
public string Number => "";
public string Name => "RefBuildingPurposeEntity (назначение зданий) — только NTR";
public string Body => """"
| АИС (`RefDBuildingTypes`) | НТР (`RefBuildingPurposeEntity`) |
|---|---|
| `AirM3PerHumanhour` | `SpecificAirConsumptionM3PerHumanHour` |
---
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
/// <summary>Раздел спецификации импорта данных из АИС ОИА ПЭО.</summary>
public class AisOgranicheniyaIOsobennosti : ISdaidDocumentSection
{
public Type Document => typeof(DOC_AisDataImport);
public Type Parent => typeof(object);
public int Order => 320;
public string Number => "";
public string Name => "Ограничения и особенности";
public string Body => """"
1. **Последовательное выполнение** — все блоки внутри каждого сервиса выполняются строго последовательно из-за зависимостей по данным.
2. **Нет частичного повторного запуска** — при падении на блоке N блоки 1..N-1 уже сохранены. Повторный запуск безопасен благодаря upsert.
3. **Фильтрация по компаниям** — весь импорт ограничен компаниями из `AllowedCompanyNames` (LIKE-паттерны).
4. **Оба сервиса независимы** — импортируют в разные схемы БД, могут запускаться независимо.
5. **Орг. структура дублируется** — `NavEntity`, `NavCompanyEntity` и др. импортируются в обе БД отдельно.
"""";
public string Version => "";
public string Status => "";
public string[] Comments => new string[0];
}
}