feat(persistence): FLIGHT_SCHD_DISPLAY 兼容视图 + 生产读切明细权威 (ACM2-29 P2-2)
- V1.3.1 迁移:flight_schd_display 视图投影首个/第二个资源与总数(gate1/gate2/ gate_total、chkc1/chkc2/checkin_total、belt1/belt_total、dely_code/strt), 完整明细仍在各明细表可查;PG LATERAL 方言,11g 随 P3-B 单独提供 - 生产读路径 mapFlightRow 切 assembleDetailOnly:集合键仅由明细表重建, 不再回退 V1.2.0 槽位/里程碑列(过渡双读仅保留在 FS7 对拍工具内) - FlightStateEngine 正确性修复:序号属性 "0" 条目现在正确翻译为显式 Clear (原实现 items 非空走 Replace,会存出 GTNO=0 脏明细行);混入常规条目的 0 标记 fail fast 拒绝猜测(v2 §4 非法结构显式失败) - JDBC 用例全部切 v2 写路径(persistNextStates),删除 legacy 写路径断言; 新增:显示视图投影/替换同步用例、明细级联删除断言、引擎 0 标记语义单测 验证:MSGX_PG_PORT=5433 真实 PG ./gradlew test --rerun-tasks 97 用例 0 失败 0 跳过
This commit is contained in:
@@ -27,7 +27,7 @@ object FlightStateEngine {
|
||||
"ERUT" to "RTNO",
|
||||
)
|
||||
|
||||
/** DNLD/FLOP 字段集 → 命令(出现即 Set/Replace;未出现即 Unchanged)。 */
|
||||
/** DNLD/FLOP 字段集 → 命令(出现即 Set/Replace;未出现即 Unchanged;序号 0 条目 = 显式清除标记)。 */
|
||||
fun commandsFromFields(flid: String, fields: FlightFields, snapshotReplace: Boolean): FlightFieldCommands {
|
||||
val scalars = linkedMapOf<String, ScalarCommand>()
|
||||
val collections = linkedMapOf<String, CollectionCommand>()
|
||||
@@ -35,15 +35,17 @@ object FlightStateEngine {
|
||||
if (key == "FLID") return@forEach
|
||||
when {
|
||||
key in COLLECTION_KEYS -> {
|
||||
val items = parseCollection(value)
|
||||
val seqAttr = SEQ_ATTR[key]
|
||||
if (items.isEmpty() && seqAttr != null && fieldsContainsClearOnly(value)) {
|
||||
collections[key] = CollectionCommand.Clear
|
||||
} else {
|
||||
collections[key] = CollectionCommand.Replace(items)
|
||||
val parsed = parseCollection(value)
|
||||
val markers = parsed.filter { seqAttr != null && it[seqAttr] == "0" }
|
||||
collections[key] = when {
|
||||
markers.isEmpty() -> CollectionCommand.Replace(parsed)
|
||||
markers.size == parsed.size -> CollectionCommand.Clear
|
||||
else -> throw IllegalArgumentException(
|
||||
"$key mixes explicit clear marker (seq=0) with regular items; refusing to guess",
|
||||
)
|
||||
}
|
||||
}
|
||||
snapshotReplace -> scalars[key] = ScalarCommand.Set(value)
|
||||
else -> scalars[key] = ScalarCommand.Set(value)
|
||||
}
|
||||
}
|
||||
@@ -116,15 +118,6 @@ object FlightStateEngine {
|
||||
}
|
||||
}
|
||||
|
||||
private fun fieldsContainsClearOnly(raw: String): Boolean {
|
||||
val node = runCatching { mapper.readTree(raw) }.getOrNull() ?: return false
|
||||
if (node.isArray && node.size() == 1) {
|
||||
val seq = node[0].fields().asSequence().firstOrNull { it.key.endsWith("NO") }?.value?.asText()
|
||||
return seq == "0"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/** 当前库态 → FlightNextState(供增量合并)。 */
|
||||
fun fromFlightFields(flid: String, fields: FlightFields, stateVersion: Long = 0L, lastMessageId: String = ""): FlightNextState {
|
||||
val scalars = linkedMapOf<String, String>()
|
||||
|
||||
+2
-2
@@ -657,7 +657,7 @@ class JdbcFlightSchdRepository(
|
||||
return totalDeleted
|
||||
}
|
||||
|
||||
/** 行 → 字段视图:标量列直读,集合键由明细表或槽位列重建(FS7 双读对拍见 FlightSchdReadAssembler)。 */
|
||||
/** 行 → 字段视图:标量列直读,集合键由明细表重建(v2 §8:完整明细权威读,P2-2 起不再回退槽位列)。 */
|
||||
private fun mapFlightRow(rs: java.sql.ResultSet): Pair<String, FlightFields> {
|
||||
val flid = rs.getString("flid")
|
||||
val row = linkedMapOf<String, String?>()
|
||||
@@ -665,7 +665,7 @@ class JdbcFlightSchdRepository(
|
||||
row[column] = rs.getString(column)
|
||||
}
|
||||
val detailCollections = FlightDetailTables.loadCollections(ds, flid)
|
||||
return flid to FlightSchdReadAssembler.assembleMerged(flid, row, detailCollections)
|
||||
return flid to FlightSchdReadAssembler.assembleDetailOnly(flid, row, detailCollections)
|
||||
}
|
||||
|
||||
override fun findByFlid(flid: String): FlightFields? =
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
-- flight-state-design-v2 §8 (ACM2-29 P2-2): compatibility read view.
|
||||
-- 提供「首个/第二个资源 + 总数」的运营查询投影;完整明细仍在各明细表可查。
|
||||
-- PG 专属语法(LATERAL);Oracle 11g 方言随 P3-B 单独提供 location。
|
||||
|
||||
CREATE OR REPLACE VIEW flight_schd_display AS
|
||||
SELECT
|
||||
f.flid,
|
||||
f.fday,
|
||||
f.flno,
|
||||
f.mvin,
|
||||
f.sodt,
|
||||
f.stnd,
|
||||
f.abdg,
|
||||
f.state_version,
|
||||
f.last_message_id,
|
||||
|
||||
g1.gate AS gate1,
|
||||
g2.gate AS gate2,
|
||||
COALESCE(g_total.cnt, 0) AS gate_total,
|
||||
|
||||
c1.chkc AS chkc1,
|
||||
c2.chkc AS chkc2,
|
||||
COALESCE(c_total.cnt, 0) AS checkin_total,
|
||||
|
||||
b1.belt AS belt1,
|
||||
COALESCE(b_total.cnt, 0) AS belt_total,
|
||||
|
||||
dly.code AS dely_code,
|
||||
dly.strt AS dely_strt
|
||||
FROM flight_schd f
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT gate FROM flight_gate WHERE flid = f.flid ORDER BY ordinal LIMIT 1 OFFSET 0
|
||||
) g1 ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT gate FROM flight_gate WHERE flid = f.flid ORDER BY ordinal LIMIT 1 OFFSET 1
|
||||
) g2 ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT count(*) AS cnt FROM flight_gate WHERE flid = f.flid
|
||||
) g_total ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT chkc FROM flight_checkin WHERE flid = f.flid ORDER BY ordinal LIMIT 1 OFFSET 0
|
||||
) c1 ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT chkc FROM flight_checkin WHERE flid = f.flid ORDER BY ordinal LIMIT 1 OFFSET 1
|
||||
) c2 ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT count(*) AS cnt FROM flight_checkin WHERE flid = f.flid
|
||||
) c_total ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT belt FROM flight_belt WHERE flid = f.flid ORDER BY ordinal LIMIT 1 OFFSET 0
|
||||
) b1 ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT count(*) AS cnt FROM flight_belt WHERE flid = f.flid
|
||||
) b_total ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT code, strt FROM flight_delay WHERE flid = f.flid ORDER BY ordinal LIMIT 1
|
||||
) dly ON TRUE;
|
||||
Reference in New Issue
Block a user