package com.gzzn.omms.msgexchange.ingress import com.fasterxml.jackson.databind.ObjectMapper import com.gzzn.omms.msgexchange.processing.FlightProjectionPort import io.micronaut.http.HttpResponse import io.micronaut.http.HttpStatus import io.micronaut.http.MediaType import io.micronaut.http.annotation.Controller import io.micronaut.http.annotation.Get import io.micronaut.http.annotation.Produces /** * `GET /all/flights`:从 Redis 查询投影读当前全部动态航班,不含共享航班(`US-12`、`C-11`)。 * * 数据只从投影来,不回落 PG:查询与网页客户端必须读同一份,不然两边会看到不同的航班 * (`INV-11`)。投影读不出来就返回错误——空列表会被前端当成"现在没有航班"(`US-12` AC2)。 * * 成功:裸 JSON 数组、不套旧系统的 `ResponseDto`;失败:503 加错误对象(`C-11`)。 * 数组元素与 `KAFKA:schd` 同形,都是日计划 `FLTR` 转成的 JSON(`C-9`、`C-11`)。 */ @Controller("/all/flights") class FlightQueryController( private val projection: FlightProjectionPort, private val mapper: ObjectMapper, ) { private val log = org.slf4j.LoggerFactory.getLogger(FlightQueryController::class.java) @Get @Produces(MediaType.APPLICATION_JSON) fun all(): HttpResponse = try { HttpResponse.ok(mapper.writeValueAsString(nonSharedFlights())) } catch (e: Exception) { // 读投影失败(Redis 不可用、未接通、载荷读不动)一律报错:这里没有可信的兜底数据源 val reason = e.message ?: e.javaClass.simpleName log.error("GET /all/flights unavailable reason={}", reason, e) HttpResponse.status(HttpStatus.SERVICE_UNAVAILABLE) .body(mapper.writeValueAsString(mapOf("error" to PROJECTION_UNAVAILABLE, "reason" to reason))) } /** * 共享航班由 `MAID`(主航班 FLID)非空标识,随主航班下发,不单独出现在列表里 * (`US-06` AC2 的主/共享关系,见 docs/implementation.md「航班域」)。 */ private fun nonSharedFlights(): List = projection.readAll() .map { mapper.readTree(it) } .filter { it.path("MAID").asText("").isBlank() } private companion object { private const val PROJECTION_UNAVAILABLE = "FLIGHT_PROJECTION_UNAVAILABLE" } }