feat(cuod): spacecraft.mission — x-localized текст → FK на mission.code
mission у КА хранился свободным x-localized текстом (краткое описание), дублировался, рассинхронизировался, не был связан со справочником mission (где уже есть программы миссий + поле spacecraft_codes). Теперь mission — строка-код, x-references → mission.code. Референсная целостность + единый источник. Маппинг 14 КА → 9 миссий взят из mission.spacecraft_codes (обратная связь уже была в данных). Проверено: все 14 мапятся однозначно, 0 пропусков, 0 конфликтов. - 0027-spacecraft-mission-fk.xml — Liquibase-миграция существующих БД: схема + 14 записей одной транзакцией прямым SQL (смена типа поля — chicken-and-egg, как 0020 raw SQL минует Envers/outbox; forward-only). schema_version → 2.0.0. - cuod-bundle: spacecraft.schema.json mission → x-references, records/spacecraft.records.json — 14 mission → код, manifest 2.0.0. Свежие установки получают новую форму сразу. Caveat: миграция через raw SQL не пишет Envers _AUD и Kafka outbox — проекции read-api нужно пересобрать после деплоя (как при 0020).
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
"supportedLocales": ["ru-RU", "en-US"],
|
"supportedLocales": ["ru-RU", "en-US"],
|
||||||
"defaultLocale": "ru-RU",
|
"defaultLocale": "ru-RU",
|
||||||
"dictionaries": [
|
"dictionaries": [
|
||||||
{ "name": "spacecraft", "displayName": "Космические аппараты", "description": "Каталог КА (космических аппаратов) ДЗЗ-миссий", "scope": "PUBLIC", "schemaResource": "spacecraft.schema.json", "schemaVersion": "1.1.0", "approvalRequired": true },
|
{ "name": "spacecraft", "displayName": "Космические аппараты", "description": "Каталог КА (космических аппаратов) ДЗЗ-миссий", "scope": "PUBLIC", "schemaResource": "spacecraft.schema.json", "schemaVersion": "2.0.0", "approvalRequired": true },
|
||||||
{ "name": "satellite_type", "displayName": "Типы КА", "description": "Классификация типов космических аппаратов по миссиям и характеристикам", "scope": "PUBLIC", "schemaResource": "satellite_type.schema.json", "schemaVersion": "1.0.0" },
|
{ "name": "satellite_type", "displayName": "Типы КА", "description": "Классификация типов космических аппаратов по миссиям и характеристикам", "scope": "PUBLIC", "schemaResource": "satellite_type.schema.json", "schemaVersion": "1.0.0" },
|
||||||
{ "name": "spacecraft_status", "displayName": "Статусы КА", "description": "Жизненный цикл КА: planned → operational → decommissioned/lost.", "scope": "PUBLIC", "schemaResource": "spacecraft_status.schema.json", "schemaVersion": "1.0.0" },
|
{ "name": "spacecraft_status", "displayName": "Статусы КА", "description": "Жизненный цикл КА: planned → operational → decommissioned/lost.", "scope": "PUBLIC", "schemaResource": "spacecraft_status.schema.json", "schemaVersion": "1.0.0" },
|
||||||
{ "name": "ground_station", "displayName": "Наземные станции", "description": "Каталог наземных пунктов приёма и управления", "scope": "PUBLIC", "schemaResource": "ground_station.schema.json", "schemaVersion": "1.0.0", "approvalRequired": true },
|
{ "name": "ground_station", "displayName": "Наземные станции", "description": "Каталог наземных пунктов приёма и управления", "scope": "PUBLIC", "schemaResource": "ground_station.schema.json", "schemaVersion": "1.0.0", "approvalRequired": true },
|
||||||
|
|||||||
+14
-22
@@ -24,11 +24,7 @@
|
|||||||
"inclination_deg": 98.2
|
"inclination_deg": 98.2
|
||||||
},
|
},
|
||||||
"spectrum_bands": ["VIS", "NIR", "SWIR", "TIR"],
|
"spectrum_bands": ["VIS", "NIR", "SWIR", "TIR"],
|
||||||
"mission": {
|
"mission": "TERRA_AQUA"}
|
||||||
"ru-RU": "Глобальный мониторинг суши и атмосферы",
|
|
||||||
"en-US": "Global land and atmosphere monitoring"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"businessKey": "2014-016A",
|
"businessKey": "2014-016A",
|
||||||
@@ -55,11 +51,7 @@
|
|||||||
"inclination_deg": 98.18
|
"inclination_deg": 98.18
|
||||||
},
|
},
|
||||||
"spectrum_bands": ["C_BAND"],
|
"spectrum_bands": ["C_BAND"],
|
||||||
"mission": {
|
"mission": "SENTINEL_1"}
|
||||||
"ru-RU": "C-SAR радиолокация для Copernicus",
|
|
||||||
"en-US": "C-SAR radar for Copernicus"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"businessKey": "2015-028A",
|
"businessKey": "2015-028A",
|
||||||
@@ -85,11 +77,7 @@
|
|||||||
"inclination_deg": 98.62
|
"inclination_deg": 98.62
|
||||||
},
|
},
|
||||||
"spectrum_bands": ["VIS", "NIR", "SWIR"],
|
"spectrum_bands": ["VIS", "NIR", "SWIR"],
|
||||||
"mission": {
|
"mission": "SENTINEL_2"}
|
||||||
"ru-RU": "Multispectral imaging для Copernicus",
|
|
||||||
"en-US": "Multispectral imaging for Copernicus"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"businessKey": "2024-149E",
|
"businessKey": "2024-149E",
|
||||||
@@ -113,11 +101,7 @@
|
|||||||
"inclination_deg": 97.5
|
"inclination_deg": 97.5
|
||||||
},
|
},
|
||||||
"spectrum_bands": ["S_BAND"],
|
"spectrum_bands": ["S_BAND"],
|
||||||
"mission": {
|
"mission": "KONDOR_FKA"}
|
||||||
"ru-RU": "Радиолокационное наблюдение Земли",
|
|
||||||
"en-US": "Radar Earth observation"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"businessKey": "2013-030A",
|
"businessKey": "2013-030A",
|
||||||
@@ -134,13 +118,13 @@
|
|||||||
"status": "LOST",
|
"status": "LOST",
|
||||||
"orbit": { "type": "SSO", "inclination_deg": 97.27 },
|
"orbit": { "type": "SSO", "inclination_deg": 97.27 },
|
||||||
"spectrum_bands": ["VIS", "NIR"],
|
"spectrum_bands": ["VIS", "NIR"],
|
||||||
"mission": { "ru-RU": "Высокодетальная оптическая съёмка", "en-US": "High-resolution optical imaging" }
|
"mission": "RESURS_P"}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"businessKey": "2014-085A",
|
"businessKey": "2014-085A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2014-085A",
|
"designator": "2014-085A",
|
||||||
|
"mission": "RESURS_P",
|
||||||
"norad_id": 40360,
|
"norad_id": 40360,
|
||||||
"name": { "ru-RU": "Ресурс-П № 2", "en-US": "Resurs-P No.2" },
|
"name": { "ru-RU": "Ресурс-П № 2", "en-US": "Resurs-P No.2" },
|
||||||
"operator": { "ru-RU": "Роскосмос / РКЦ Прогресс", "en-US": "Roscosmos / RSC Progress" },
|
"operator": { "ru-RU": "Роскосмос / РКЦ Прогресс", "en-US": "Roscosmos / RSC Progress" },
|
||||||
@@ -157,6 +141,7 @@
|
|||||||
"businessKey": "2016-016A",
|
"businessKey": "2016-016A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2016-016A",
|
"designator": "2016-016A",
|
||||||
|
"mission": "RESURS_P",
|
||||||
"norad_id": 41386,
|
"norad_id": 41386,
|
||||||
"name": { "ru-RU": "Ресурс-П № 3", "en-US": "Resurs-P No.3" },
|
"name": { "ru-RU": "Ресурс-П № 3", "en-US": "Resurs-P No.3" },
|
||||||
"operator": { "ru-RU": "Роскосмос / РКЦ Прогресс", "en-US": "Roscosmos / RSC Progress" },
|
"operator": { "ru-RU": "Роскосмос / РКЦ Прогресс", "en-US": "Roscosmos / RSC Progress" },
|
||||||
@@ -173,6 +158,7 @@
|
|||||||
"businessKey": "2024-046A",
|
"businessKey": "2024-046A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2024-046A",
|
"designator": "2024-046A",
|
||||||
|
"mission": "RESURS_P",
|
||||||
"name": { "ru-RU": "Ресурс-П № 4", "en-US": "Resurs-P No.4" },
|
"name": { "ru-RU": "Ресурс-П № 4", "en-US": "Resurs-P No.4" },
|
||||||
"operator": { "ru-RU": "Роскосмос / РКЦ Прогресс", "en-US": "Roscosmos / RSC Progress" },
|
"operator": { "ru-RU": "Роскосмос / РКЦ Прогресс", "en-US": "Roscosmos / RSC Progress" },
|
||||||
"country": "RU",
|
"country": "RU",
|
||||||
@@ -188,6 +174,7 @@
|
|||||||
"businessKey": "2012-039A",
|
"businessKey": "2012-039A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2012-039A",
|
"designator": "2012-039A",
|
||||||
|
"mission": "KANOPUS_V",
|
||||||
"norad_id": 38707,
|
"norad_id": 38707,
|
||||||
"name": { "ru-RU": "Канопус-В № 1", "en-US": "Kanopus-V No.1" },
|
"name": { "ru-RU": "Канопус-В № 1", "en-US": "Kanopus-V No.1" },
|
||||||
"operator": { "ru-RU": "Роскосмос / ВНИИЭМ", "en-US": "Roscosmos / VNIIEM" },
|
"operator": { "ru-RU": "Роскосмос / ВНИИЭМ", "en-US": "Roscosmos / VNIIEM" },
|
||||||
@@ -204,6 +191,7 @@
|
|||||||
"businessKey": "2017-076A",
|
"businessKey": "2017-076A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2017-076A",
|
"designator": "2017-076A",
|
||||||
|
"mission": "KANOPUS_V",
|
||||||
"name": { "ru-RU": "Канопус-В № 5 (2-Гранд)", "en-US": "Kanopus-V No.5 (Grand)" },
|
"name": { "ru-RU": "Канопус-В № 5 (2-Гранд)", "en-US": "Kanopus-V No.5 (Grand)" },
|
||||||
"operator": { "ru-RU": "Роскосмос / ВНИИЭМ", "en-US": "Roscosmos / VNIIEM" },
|
"operator": { "ru-RU": "Роскосмос / ВНИИЭМ", "en-US": "Roscosmos / VNIIEM" },
|
||||||
"country": "RU",
|
"country": "RU",
|
||||||
@@ -218,6 +206,7 @@
|
|||||||
"businessKey": "2014-037A",
|
"businessKey": "2014-037A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2014-037A",
|
"designator": "2014-037A",
|
||||||
|
"mission": "METEOR_M",
|
||||||
"name": { "ru-RU": "Метеор-М № 2", "en-US": "Meteor-M No.2" },
|
"name": { "ru-RU": "Метеор-М № 2", "en-US": "Meteor-M No.2" },
|
||||||
"operator": { "ru-RU": "Росгидромет / ВНИИЭМ", "en-US": "Roshydromet / VNIIEM" },
|
"operator": { "ru-RU": "Росгидромет / ВНИИЭМ", "en-US": "Roshydromet / VNIIEM" },
|
||||||
"country": "RU",
|
"country": "RU",
|
||||||
@@ -233,6 +222,7 @@
|
|||||||
"businessKey": "2019-038A",
|
"businessKey": "2019-038A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2019-038A",
|
"designator": "2019-038A",
|
||||||
|
"mission": "METEOR_M",
|
||||||
"name": { "ru-RU": "Метеор-М № 2-2", "en-US": "Meteor-M No.2-2" },
|
"name": { "ru-RU": "Метеор-М № 2-2", "en-US": "Meteor-M No.2-2" },
|
||||||
"operator": { "ru-RU": "Росгидромет / ВНИИЭМ", "en-US": "Roshydromet / VNIIEM" },
|
"operator": { "ru-RU": "Росгидромет / ВНИИЭМ", "en-US": "Roshydromet / VNIIEM" },
|
||||||
"country": "RU",
|
"country": "RU",
|
||||||
@@ -247,6 +237,7 @@
|
|||||||
"businessKey": "2015-074A",
|
"businessKey": "2015-074A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2015-074A",
|
"designator": "2015-074A",
|
||||||
|
"mission": "ELEKTRO_L",
|
||||||
"name": { "ru-RU": "Электро-Л № 2", "en-US": "Elektro-L No.2" },
|
"name": { "ru-RU": "Электро-Л № 2", "en-US": "Elektro-L No.2" },
|
||||||
"operator": { "ru-RU": "Росгидромет / НПО Лавочкина", "en-US": "Roshydromet / Lavochkin Association" },
|
"operator": { "ru-RU": "Росгидромет / НПО Лавочкина", "en-US": "Roshydromet / Lavochkin Association" },
|
||||||
"country": "RU",
|
"country": "RU",
|
||||||
@@ -262,6 +253,7 @@
|
|||||||
"businessKey": "2021-016A",
|
"businessKey": "2021-016A",
|
||||||
"data": {
|
"data": {
|
||||||
"designator": "2021-016A",
|
"designator": "2021-016A",
|
||||||
|
"mission": "ARKTIKA_M",
|
||||||
"name": { "ru-RU": "Арктика-М № 1", "en-US": "Arktika-M No.1" },
|
"name": { "ru-RU": "Арктика-М № 1", "en-US": "Arktika-M No.1" },
|
||||||
"operator": { "ru-RU": "Росгидромет / НПО Лавочкина", "en-US": "Roshydromet / Lavochkin Association" },
|
"operator": { "ru-RU": "Росгидромет / НПО Лавочкина", "en-US": "Roshydromet / Lavochkin Association" },
|
||||||
"country": "RU",
|
"country": "RU",
|
||||||
|
|||||||
@@ -92,12 +92,9 @@
|
|||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
"mission": {
|
"mission": {
|
||||||
"type": "object",
|
"type": "string",
|
||||||
"x-localized": true,
|
"description": "Программа миссии — FK на mission.code",
|
||||||
"description": "Краткое описание миссии",
|
"x-references": "mission.code"
|
||||||
"patternProperties": {
|
|
||||||
"^[a-z]{2}-[A-Z]{2}$": { "type": "string" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
spacecraft.mission: x-localized свободный текст → FK на mission.code.
|
||||||
|
|
||||||
|
Было: mission = { "ru-RU": "...", "en-US": "..." } (краткое описание).
|
||||||
|
Стало: mission = "RESURS_P" (строка-код, x-references → mission.code).
|
||||||
|
|
||||||
|
Зачем: описание миссии дублировалось свободным текстом в каждом КА,
|
||||||
|
рассинхронизировалось, не связывалось со справочником mission (где уже
|
||||||
|
есть программы миссий + поле spacecraft_codes). FK даёт референсную
|
||||||
|
целостность и единый источник.
|
||||||
|
|
||||||
|
Маппинг КА → миссия взят из mission.spacecraft_codes (обратная связь
|
||||||
|
уже была в данных). Проверено: все 14 КА однозначно мапятся на 9 миссий,
|
||||||
|
0 пропусков, 0 конфликтов.
|
||||||
|
|
||||||
|
Смена типа поля — chicken-and-egg между схемой и записями (любой из них
|
||||||
|
по отдельности невалиден против другого), поэтому схема + данные
|
||||||
|
мигрируются одной транзакцией прямым SQL. Как и 0020-fix-draft-status —
|
||||||
|
raw SQL минует Envers _AUD и Kafka outbox; для разовой контролируемой
|
||||||
|
миграции это допустимо (потребители ре-синхронизируются, проекции
|
||||||
|
read-api пересоберутся). Forward-only, без rollback (исходный свободный
|
||||||
|
текст лоссится).
|
||||||
|
|
||||||
|
На свежей БД (spacecraft ещё не создан) — no-op: UPDATE'ы затрагивают
|
||||||
|
0 строк, cuod-bundle создаст spacecraft сразу в новой форме (v2.0.0).
|
||||||
|
-->
|
||||||
|
<changeSet id="0027-1-spacecraft-mission-fk" author="ordinis">
|
||||||
|
<comment>spacecraft.mission: x-localized текст → FK mission.code (схема + 14 записей, одна транзакция)</comment>
|
||||||
|
|
||||||
|
<!-- 1. Схема: поле mission → string + x-references, bump до 2.0.0 (breaking). -->
|
||||||
|
<sql>
|
||||||
|
UPDATE dictionary_definitions
|
||||||
|
SET schema_json = jsonb_set(
|
||||||
|
schema_json,
|
||||||
|
'{properties,mission}',
|
||||||
|
'{"type":"string","description":"Программа миссии — FK на mission.code","x-references":"mission.code"}'::jsonb
|
||||||
|
),
|
||||||
|
schema_version = '2.0.0',
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE name = 'spacecraft';
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<!-- 2. Данные: mission каждого КА → код миссии (маппинг из mission.spacecraft_codes). -->
|
||||||
|
<sql>
|
||||||
|
UPDATE dictionary_records r
|
||||||
|
SET data = jsonb_set(r.data, '{mission}', to_jsonb(m.code), true),
|
||||||
|
updated_at = NOW()
|
||||||
|
FROM (VALUES
|
||||||
|
('1999-068A', 'TERRA_AQUA'),
|
||||||
|
('2014-016A', 'SENTINEL_1'),
|
||||||
|
('2015-028A', 'SENTINEL_2'),
|
||||||
|
('2024-149E', 'KONDOR_FKA'),
|
||||||
|
('2013-030A', 'RESURS_P'),
|
||||||
|
('2014-085A', 'RESURS_P'),
|
||||||
|
('2016-016A', 'RESURS_P'),
|
||||||
|
('2024-046A', 'RESURS_P'),
|
||||||
|
('2012-039A', 'KANOPUS_V'),
|
||||||
|
('2017-076A', 'KANOPUS_V'),
|
||||||
|
('2014-037A', 'METEOR_M'),
|
||||||
|
('2019-038A', 'METEOR_M'),
|
||||||
|
('2015-074A', 'ELEKTRO_L'),
|
||||||
|
('2021-016A', 'ARKTIKA_M')
|
||||||
|
) AS m(bk, code)
|
||||||
|
WHERE r.business_key = m.bk
|
||||||
|
AND r.dictionary_id = (SELECT id FROM dictionary_definitions WHERE name = 'spacecraft');
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -36,5 +36,6 @@
|
|||||||
<include file="changes/0024-notification-read-state.xml" relativeToChangelogFile="true"/>
|
<include file="changes/0024-notification-read-state.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="changes/0025-notification-prefs-event-type.xml" relativeToChangelogFile="true"/>
|
<include file="changes/0025-notification-prefs-event-type.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="changes/0026-notification-quiet-hours.xml" relativeToChangelogFile="true"/>
|
<include file="changes/0026-notification-quiet-hours.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="changes/0027-spacecraft-mission-fk.xml" relativeToChangelogFile="true"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
Reference in New Issue
Block a user