Files
msgexchange-v2/doc/msgexchange-api-legacy-user-stories.md
T
windyboy 38d5cecfac docs: copy legacy user-stories baseline (ACMA-4) for golden replay
Behavior-comparison baseline referenced by ACM2-3 (G13 golden matrix).
Copied from legacy repo working copy (was untracked there).
2026-09-06 16:36:29 +08:00

47 KiB
Raw Blame History

msgexchange-api 旧项目业务逻辑与用户故事文档

0. 文档说明

  • 本文档记录 msgexchange-api(现役旧项目)的完整业务逻辑,并以用户故事形式组织,作为新系统(Micronaut + Kotlin)迁移设计及一致性对拍的行为基线。
  • 内容依据仓库源码与资源配置编写:src/main/java/com/gzzn/omms/msgexchange-api/**src/main/resources/*.yml;分支 feature/add_dev_containers_supportsHEAD eb72a668)。
  • 报文结构依据 SIS_AODB_RMS-V0.1.mddoc/unisysaodbsis.xsd
  • 文档中"关联代码"均为相对仓库根目录的源码路径,可据此逐条复核。

1. 系统定位与集成边界

msgexchange-api 为成都机场 OMMS 消息交换服务(com.gzzn.omms:msgexchange-api1.2.1-SNAPSHOTSpring Boot 1.5.17 / Spring Cloud Edgware / Java 8;服务名 msgexchangeapi)。端口 8080 为 Spring Boot 缺省值——各 application-*.yml 均未配置 server.port,迁移时勿寻找该配置项。

职责概述:

  1. 接收上游系统(AODB / CIIMS 等)下发的航班计划与动态 XML 报文;
  2. 将报文落库(CMINMSGS)后异步解析处理,维护"最新动态航班库"Redis);
  3. 通过 Kafka 将航班变更通知前端(ACDW);
  4. 将已结束运营的航班沉淀为历史数据(Elasticsearch、MySQL 历史表);
  5. 提供 HTTP 接口用于收报、日计划请求、动态航班查询与运维操作。

外部集成点如下:

集成对象 用途 说明
MySQL(库 cdairport 消息持久化 CMINMSGS(入站报文)、CMINMSGS_HST(历史)、CMINMSGS_TEST(测试)、COUTMSGS(出站报文)
Redis Cluster 动态航班库、基础数据缓存 key flightInfoorms_standorms_stand_airbridgeRedisTemplate 启用 Jackson enableDefaultTyping
Kafka 通知前端 业务主题 msg(单条事件)、schd(定时批量);另提供任意主题转发接口;本服务仅生产、不消费(无 @KafkaListener
Elasticsearch 6.x 历史航班存储 transport 客户端(9300);索引 flight_htsflight_hts2dev 配置 cluster.name=opensearch
Eureka 服务注册、admin-api 服务发现 @LoadBalanced RestTemplate 按服务名 ADMINAPI 调用
admin-apiACAA 基础数据 机位列表、机位→登机桥编码两个接口(basicdata 模块)
Logstashlogback 日志采集 TCP socket 输出 JSON 日志

参与角色:报文发送方(上游系统或手工工具)、前端使用者(ACDW)、计划/签派操作员、值班运维人员、系统自身(轮询线程与定时任务)。

2. 术语与数据语义

术语 定义
CMINMSGS 记录 一条入站原始报文。列:CMINMSGS_ID(自增主键)、CMINMSGS_CLOB_MSG(原始 XML)、CMINMSGS_DATE_RECEIVED(接收时间)、CMINMSGS_DATE_PROCESSEDCMINMSGS_STATUSCMINMSGS_SUBSYSTEM_*(子系统信息;现役处理时未回填)
处理标记语义 收报时 CMINMSGS_DATE_PROCESSED=NULL;处理完成(含失败或命中忽略清单)后由 Handler 置为当前时间。Handler 返回失败且未置位时(如日计划刷新失败、ADFT 数量校验失败),后续轮询将再次处理该消息
MSG 报文根对象(entity/msg/MSG.java):METATYPE / STYP / SNDR / DTTM / SEQN)与业务体(SCHD / FLOP / RQFD 等)
SCHD.FLTR 动态航班对象,存储于 Redis flightInfo。关键字段:FLID(航班标识)、SODT/ACTT/CNCL(计划/实际/取消时间,格式 ddMMMyyHHmm)、MVINA=到港,D=离港)、MAID(共享航班主航班 FLID)、MAFL(主航班的共享航班列表)、ABTM 及派生字段 abdgPSDT/LPSDT(当前/历史计划机位)、DELY/ABN(延误与异常)
ABN 异常列表 类型:DLY(延误数组)、CAN(取消)、RTN(返航)、ALT(备降)
共享航班 MAID != NULL 的航班。其自身变更不直接发送 Kafka 消息,随主航班(MAID 指向的航班)变更一并下发
Handler 消息子类型的处理类,命名规则 msghandler/{type小写}/{STYP}Handler。Handler 非 Spring 管理的 Bean:由 Dispatcher 反射实例化,依赖经 SpringUtil 获取
ignoreMsg 忽略规则,逗号分隔的 TYPE-STYPTYPE-*。命中的消息不进入 Handler,直接标记为已处理
flightInfo Redis hashfield=FLIDvalue=SCHD.FLTR(带类型信息的 JSON 序列化)
orms_stand Redis hashfield=机位编码,value=OrmsStand 对象
orms_stand_airbridge Redis hashfield=机位编码,value=登机桥编码数组的 JSON 字符串
COUTMSGS 出站报文表,列清单见 5.5

3. 端到端业务流程

3.1 入站收报

POST /cminmsgs/send,请求体为原始 XML 字符串。

调用链:CminmsgsController.sendCminmsgServiceImpl.sendXmlMsg。处理内容:

  1. 创建 CMINMSGS 记录:CLOB_MSG 保存原始 XMLDATE_RECEIVED 设为当前时间,DATE_PROCESSED 为空;
  2. 落库并返回自增 IDResponseDto.success(id))。

收报接口不解析、不校验报文内容;业务处理异步进行。

3.2 消息采集与分派

  • 启动:AppRunnerCommandLineRunner)调用 MsgExchangeServiceImpl.start(),以 ScheduledExecutorService(单线程,命名 msgexchange-%d)按 msgExchange.intervalSeconds(各环境均配置为 1 秒,代码无默认值)固定频率执行 MsgExchangeRunner
  • 采集:MsgExchangeRunner.run 调用 cminmsgService.getNewMsgsAfterId(0L),即查询 CMINMSGS_ID > 0 且 DATE_PROCESSED IS NULL 的记录,逐条调用 MsgHandlerDispatcher.dispatch;异常仅记录日志,不中断批次。
  • 分派:MsgHandlerDispatcher.dispatch 执行以下步骤:
    1. exchangeService.xmlToMsg(clob) 将 XML 解析为 MSGJackson XmlMapper + JAXB 注解 introspection),构造 CminmsgWapper
    2. 读取 META.TYPEMETA.STYP;若命中 ignoreMsg,将该记录标记为已处理并返回成功;
    3. 否则按命名规则反射加载 Handler(Class.forName + newInstance),调用 run(CminmsgWapper)
    4. 任一步骤抛出异常:将该记录标记为已处理(避免滞留待处理队列)、记录错误日志、返回失败(不重试)。

3.3 计划类(SCHD-*)处理

分派目标为 msghandler/schd/*Handler(继承 SchdBaseHandler)。

RESP / DNLD(日航班计划刷新,实现相同)

RESPHandler.run / DNLDHandler.run 调用 FlightInfoServiceImpl.updateByDaySchd

  1. 读取 SCHD.FLTR 全量列表;
  2. updateMafltr:将共享航班(MAID != NULL)生成 MAFLDATA(FLID, FLNO),追加到对应主航班的 MAFL 列表;
  3. updatefltrBridge:将各航班 ABTM 中的 ABDG 按序直接追加到派生字段 abdg(代码不插入分隔符——分隔符依赖 ABDG 数据本身是否带逗号),拼接结果若以逗号结尾则仅删除末尾一个逗号
  4. 调用 redisService.hmset("flightInfo", FLID→FLTR) 写入航班集合。

说明:RedisService.hmset 实现为 opsForHash().putAll,仅新增/覆盖 field;新快照中未出现的旧 field 不会被删除,即 flightInfo 为合并写入语义,并非整体替换。快照差集清理须由新系统显式实现(见 6 节迁移验收边界)。

刷新成功:调用 sendDschd 向 Kafka 主题 msg 发送日计划到达通知(MSG 仅保留 META,业务体为空),随后将该记录标记为已处理,返回成功。

刷新失败(updateByDaySchd 返回 false):不标记已处理,返回失败;该消息在后续轮询中重试。

ADFT(计划外航班添加)

ADFTHandler.run(实际执行顺序):

  1. 校验 SCHD.FLTR 数量为 1,否则直接 HandlerResult.failure() 返回且不标记已处理(与 RESP/DNLD 刷新失败同属"返回失败未置位"分支,后续轮询重试,见 5.7-2);
  2. saveFltr 将航班写入 Redishset);
  3. 非共享航班(MAID == NULL):加入发送缓冲并调用 sendMsg 发送完整消息 JSON 至 Kafka msg
  4. 标记已处理(此步先于下方共享航班联动执行,代码中非共享发送与标记先于共享联动,与"处理完再标记"的直觉不同);
  5. 共享航班(MAID != NULL):不直接发送;从 Redis 取主航班(flightInfoService.getByFlid),向主航班内存对象 MAFL 追加 MAFLDATA(FLID, FLNO),再将变更后的主航班加入发送缓冲——未调用 saveFltr 写回 Redis(与 FDEL 同类遗留缺陷,见 5.7-14);若主航班不存在(getByFlid 返回 null),此处对 null 取 getMafl() 会抛空指针(Dispatcher 异常兜底会再次标记已处理)。

3.4 动态类(FLOP-*)处理

分派目标为 msghandler/flop/*Handler(共 29 个,继承 FlopBaseHandler)。通用处理流程 FlopBaseHandler.run

  1. FLOP.FLID,调用 flightInfoService.getByFlid 查询 Redis
  2. 航班不存在:标记已处理、记录告警、返回失败(视为处理完成,不再重试);
  3. 航班存在:
    • 子类覆写的 updateFltr(FLOP, FLTR) 就地更新动态航班(子类语义见 5.3);
    • saveFltr 写回 Redis
    • 若为非共享航班(MAID == NULL):加入发送缓冲并调用 sendMsg 发送至 Kafka msg
    • 标记已处理,返回 success(FLTR)

例外:FDELHandler 覆写 run(删除航班)。处理内容:

  1. 航班存在:deleteFltr 删除 Redis 记录;无条件调用 sendMsg 发送删除消息(由前端执行本地删除),不区分共享/非共享航班(与下述通用规则不一致,见 5.7);标记已处理;
  2. 被删航班为共享航班:从主航班 MAFL 中移除对应元素,并将变更后的主航班加入发送缓冲。该修改仅作用于从 Redis 读出的内存对象(未调用 saveFltr 写回 Redis),元素比对使用 ==MAFLDATA.getFlid()FLTR.getFLID() 均为 BigInteger,故为引用比较而非值比较),属遗留缺陷(见 5.7);
  3. 航班不存在:标记已处理并返回失败。

共享航班联动规则(通用,FDEL 除外):动态与计划 Handler 对共享航班(MAID != NULL)自身变更不单独发送消息;主航班自身变更(更新、删除联动)将主航班放入发送缓冲,共享航班列表随主航班一并下发。

3.5 前端通知(Kafka

  • 即时事件(主题 msg):由 3.3、3.4 中 sendMsg / sendDschd 发送。exchangeService.msgToJson 序列化 META 与 TYPE 对应的业务体。
  • 批量缓冲(主题 schd):FltrSendScheduled(每 3 秒,cron scheduled.flightSendCron)调用 FltrSendBufferServiceImpl.takeAllFltr 取空进程内队列(ConcurrentLinkedQueue),经 JsonUtil.getString 序列化后发送至 Kafka schd。队列为空时不发送。
  • 现役为 at-most-oncetakeAllFltr 在 Kafka 发送前即复制并清空队列;序列化或发送异常仅记录日志、不回灌队列,该批消息可能整体丢失(见 US-B2、5.7-17)。

3.6 入站报文历史归档

CminmsgsHisScheduledcron 0 0 3 * * ?,即 scheduled.cminmsgHisCron)调用 CminmsgHstDao.moveToHistory,以原生 SQL 在单事务内执行:

  1. INSERT INTO cminmsgs_hst SELECT ... FROM cminmsgs src WHERE NOT EXISTScminmsgs_hst 中已存在相同 IDAND src.CMINMSGS_DATE_RECEIVED < NOW() - 1 DAY
  2. DELETE FROM cminmsgs WHERE CMINMSGS_DATE_RECEIVED < NOW() - 1 DAY

说明:CmoutmsgHstServiceImplinsertToTable 中按 CMINMSGS_ID % 10 分表(cminmsgs_hst_0cminmsgs_hst_9)的历史归档实现仍保留于代码中,但未被当前定时任务使用。

3.7 动态航班转历史

FlightHisScheduledcron 0 30 3 * * ?,即 scheduled.flightInfoCron)执行以下步骤:

  1. flightInfoService.findAll:读取 Redis flightInfo 全部动态航班;
  2. FlightHisServiceImpl.getHisToBeTran:按下列任一条件判定可转历史(时间字段格式 ddMMMyyHHmm,阈值为毫秒,见 5.4):
    • SODT(计划时间)距当前时间超过 SODT_HST_TIME3 天);
    • CNCL(取消时间)距当前时间超过 CANCEL_HST_TIME1 小时);
    • 备降:FDIV.DDES = CTUFDIV.DDIR = FROM(应降成都、实际备降他处),且 SODT 距当前时间超过 FDIV_HST_TIME
    • 离港(MVIN = D):计划时间早于当日且存在实际时间 ACTT,且 ACTT 不晚于当前时间;
    • 到港(MVIN = A):计划时间早于当日且存在实际时间,且 ACTT 距当前时间超过 ARRIVE_HST_TIME1 小时);
  3. FlightHisServiceImpl.save:逐条按 SODT + FLID 查询 ES 索引 flight_htstype _doc),存在则更新、不存在则新增;写入内容为 JSONObject.parseObject(JsonUtil.getString(FLTR));单条失败记录日志并跳过;
  4. 保存成功的 FLID 列表经 flightInfoService.batchDeleteFltr 从 Redis 批量删除。

说明:getHisToBeTran(步骤 2)的逐条判定未做单条异常隔离:SODT 在四个判定方法中均无空值保护直接解析(缺失/非法即抛异常);ACTT/CNCL 为空有保护、仅"非空但格式非法"才抛;MVIN 为空且 ACTT 存在时 equals 调用(isDMatch:236、isAMatch:264)会 NPE。任一条抛异常都会由 FlightHisScheduled 外层捕获并中断当次整批,剩余航班不再处理;"单条失败不影响其他航班"仅适用于步骤 3 save 的 ES 写入循环(见 US-D2、5.7-18)。

3.8 HTTP 查询与运维接口

  • GET /all/flights:读取 Redis 全部动态航班,过滤非共享航班(MAID == NULL)后返回 ResponseDto<List<SCHD.FLTR>>
  • GET /flights/migratescroll 读取 ES flight_hts 全量数据,经 transToNewFltrs 还原 SCHD.FLTR(将 ABN[].BODY 按类型 DLY / CAN / RTN / ALT 映射回对象字段),逐条写入 ES flight_hts2。该接口为一次性数据迁移工具。
  • POST /kafka/topics/{name}/msgs:请求体先按 JSON(Map)解析,成功则直接发送至 Kafka 主题 {name};解析失败则按 XML 解析为 MSG 再转 JSON 发送。该接口提供 XML / JSON 双模转发能力。
  • POST /schd/sync:见 3.9。

3.9 日计划请求(出站报文构造)

POST /schd/sync,请求体 SchdSyncDto {startDate, endDate},时间格式 yyyy-MM-dd hh:mm

SchdController.sync 解析时间后调用 CmoutmsgServiceImpl.sendSchdGetMsg

  1. 生成序号:SEQN = max(COUTMSGS_ID) + 1;无记录时为 1
  2. 构造报文:META{SNDR=OSH5, TYPE=RQFD, STYP=NONE, DTTM=yyyyMMddHHmmss, SEQN},业务体 RQFD{STDB, STDE}(时间格式 ddMMMyyHHmm,大写);使用 MSGBuilderMETABuilder
  3. exchangeService.msgToXml 序列化为 XML(根元素 MSG,含 xsi:noNamespaceSchemaLocation="unisysaodbsis.xsd");
  4. 写入 COUTMSGSCLOB_MSG 为报文 XMLDATE_INSERTED 为当前时间,ROUTINGID = OSH5RQFD

代码检索结果:现役工程中不存在读取或发送 COUTMSGS 的执行器(对 CmoutmsgDao 的引用仅存在于 CmoutmsgServiceImplsavefindMaxId)。/schd/sync 仅完成报文构造与落库,实际发送通道需另行确认。

3.10 基础数据(机位与登机桥)

  • OrmsStandManager(单例,非 Spring Bean):读取 Redis orms_stand;键不存在时经 OrmsStandServiceImpl.findAllRestTemplate GET adminApi.ormsStandsUrl,按 Eureka 服务名 ADMINAPI 解析)拉取全量机位,以 standCode → OrmsStand 写入 Redis(过期时间 3600 秒),随后读取返回。
  • OrmsStandAirbridgeManager(单例,非 Spring Bean):读取 Redis orms_stand_airbridge;机位字段无缓存时经 getAirbridgeCodeByStandCodeGET .../ormsStands/{standCode}/airbridgeCode)拉取,以 JSON 数组字符串存入 hash 字段,再读取并解析为 List<String>
  • 使用方:机位相关 Handler(如 PSDTHandler.updateAbg)。处理规则:取最后一个计划机位编码查询机位基础数据;机位类型 standType = near(近机位)时将登机桥编码拼接至 abdg;远机位或清空机位消息时置 abdg 为空。

4. 用户故事

用户故事格式:ID | 角色 | 期望能力 | 业务目标。每条包含触发条件、处理流程(代码链路)与验收标准。 角色代号:上游 = 报文发送方;前端 = ACDW 使用者;计划 = 计划/签派操作员;运维 = 值班运维人员;系统 = 系统自身(轮询线程与定时任务)。

EPIC-A 入站消息生命周期

US-A1 接收并持久化上游 XML 报文

  • 角色 / 期望 / 目标:作为上游,期望提交完整 XML 报文并即时取得受理标识,以便确认报文已受理待处理。
  • 触发条件:POST /cminmsgs/send,请求体为 XML 文本。
  • 处理流程:CminmsgsController.sendCminmsgServiceImpl.sendXmlMsgCMINMSGS 落库:CLOB_MSG 保存原始报文、DATE_RECEIVED 为当前时间、DATE_PROCESSED 为空)→ 返回受理 ID。
  • 验收标准:
    • 响应结构 {is_success: true, body: <自增 ID>}
    • 数据表新增一条记录,且 DATE_PROCESSED 为空;
    • 报文原样保存,不做解析或校验。
  • 关联代码:controller/CminmsgsController.javaservice/CminmsgServiceImpl.javadao/CminmsgDao.javaentity/Cminmsg.java

US-A2 定期采集未处理报文并分派

  • 角色 / 期望 / 目标:作为系统,期望周期性获取未处理报文并按类型转交对应 Handler,以便报文得到业务处理。
  • 触发条件:应用启动后,按 msgExchange.intervalSeconds(各环境均配置为 1 秒,代码无默认值)周期执行。
  • 处理流程:MsgExchangeServiceImpl.startMsgExchangeRunner.run(查询 ID > 0 且 DATE_PROCESSED IS NULL)→ MsgHandlerDispatcher.dispatch(解析 XML、反射调用 Handler)。
  • 验收标准:
    • 已处理(DATE_PROCESSED 非空)的记录不会被再次采集;
    • 单条消息处理异常不影响同批其他消息;
    • 需区分"抛异常"与"Handler 返回失败":抛异常由 Dispatcher 兜底标记已处理(不会无限滞留);Handler 返回失败且未标记的分支(RESP/DNLD 刷新失败、ADFT 数量校验失败)会被轮询持续重试,直至每日归档移入历史表(见 5.7-2)。
  • 关联代码:runner/AppRunner.javarunner/MsgExchangeRunner.javaservice/MsgExchangeServiceImpl.javamsghandler/MsgHandlerDispatcher.javaservice/exchange/ExchangeServiceImpl.java

US-A3 忽略清单内消息仅登记不处理

  • 角色 / 期望 / 目标:作为上游,期望指定类型消息(如 LDM-*REGN-*RSTA-*EROR-*)跳过业务处理,以避免无效处理与噪声日志。
  • 触发条件:报文的 META.TYPE-STYP 命中 msgExchange.ignoreMsg 规则(逗号分隔,支持 TYPE-STYPTYPE-*,大小写不敏感)。
  • 处理流程:MsgHandlerDispatcher.isNeedHandler 判定为不需要处理 → updateBatchProcessed 标记已处理 → 返回成功。
  • 验收标准:
    • 命中规则的记录被标记为已处理;
    • 不触发任何 Handler 反射调用。
  • 关联代码:msghandler/MsgHandlerDispatcher.java;配置项 msgExchange.ignoreMsg 于各环境 application-*.yml(注意:test 环境值为 error-*,其余环境为 eror-*,属现存差异)。

US-A4 日航班计划快照整体刷新(SCHD-RESP / SCHD-DNLD

  • 角色 / 期望 / 目标:作为上游,期望下发某时段的日航班计划(含多航班 FLTR 列表),以便本服务与前端获得最新计划基线。
  • 触发条件:收到 SCHD-RESPSCHD-DNLD 报文。
  • 处理流程:RESPHandler.run / DNLDHandler.runFlightInfoServiceImpl.updateByDaySchd
    1. 重组共享航班(MAFLDATA 挂入主航班 MAFL);
    2. 重组登机桥(ABTMABDG 按序直接追加为 abdg,见 3.3);
    3. hmset("flightInfo", FLID→FLTR) 合并写入(新增/覆盖 field,不删除快照中未出现的旧 field);
    4. 成功后发送日计划到达通知至 Kafka msg,并标记已处理。
  • 验收标准:
    • Redis flightInfo 合并写入:报文内航班被新增/覆盖;快照中不存在而 Redis 已存在的旧航班不被清除(合并语义,非整体替换);
    • 主航班 MAFL 包含各共享航班的 FLID / FLNO
    • 各航班 abdgABDG 值按序直接追加的结果:分隔符依赖数据本身,代码不插入分隔符;仅当拼接结果以逗号结尾时删除末尾一个逗号。回放测试需按该真实机制设定期望(见 3.3);
    • Kafka msg 收到仅含 META 的通知消息;
    • 刷新失败时该消息不标记已处理,后续轮询重试。
  • 关联代码:msghandler/schd/RESPHandler.javamsghandler/schd/DNLDHandler.javaservice/flightInfo/FlightInfoServiceImpl.javaservice/exchange/ExchangeServiceImpl.javaservice/CminmsgServiceImpl.java

US-A5 添加计划外临时航班(SCHD-ADFT)

  • 角色 / 期望 / 目标:作为上游,期望推送单条临时航班计划,以便其进入动态航班库并即时通知前端。
  • 触发条件:收到 SCHD-ADFT 报文。
  • 处理流程:ADFTHandler.run(按代码实际顺序;约束:仅处理 FLTR 恰含 1 条的报文,数量≠1 属失败路径而非超出处理范围):
    1. 校验 FLTR 数量为 1;不满足则返回失败且不标记已处理(后续轮询重试,见 5.7-2);
    2. saveFltr 写入 Redis
    3. 非共享航班(MAID == NULL)加入缓冲并发送 Kafka msg
    4. 标记已处理(先于共享航班联动执行);
    5. 共享航班:向从 Redis 取出的主航班内存对象 MAFL 追加 MAFLDATA 并将主航班加入缓冲(该修改仅作用于内存对象,未写回 Redis,见 5.7-14);主航班不存在时此处抛空指针。
  • 验收标准(现役可证实范围):
    • 单条航班写入 Redis
    • 共享航班联动仅作用于发送缓冲中的主航班内存对象;Redis 内主航班记录不保证被同步更新(遗留缺陷,见 5.7-14);
    • 通知与缓冲规则与动态航班一致(见 US-B1、US-B2)。
  • 关联代码:msghandler/schd/ADFTHandler.java

US-A6 按 FLOP- 事件增量更新动态航班*

  • 角色 / 期望 / 目标:作为上游,期望按 FLID 下发指定事件的航班变更,以便动态航班库字段精确更新并同步前端。
  • 触发条件:META.TYPE=FLOPSTYP 对应 29 个动态 Handler 之一。
  • 处理流程:FlopBaseHandler.run
    1. FLID 查询 Redis
    2. 航班存在:子类 updateFltr 就地更新 → saveFltr 写回 → 非共享航班加入缓冲并发送 msg → 标记已处理;
    3. 航班不存在:标记已处理并返回失败。
  • 验收标准:
    • 仅当目标航班存在时执行更新;
    • 更新结果写回 Redis
    • 共享航班自身不直接发送消息。
  • 关联代码:msghandler/flop/base/FlopBaseHandler.java 及各覆写 updateFltr 的子类(清单见 5.3)。

US-A7 各 FLOP 子事件字段更新语义

  • 说明:以下子事件语义依据各 Handler 实现代码核实;不表示均已具备自动化测试。现有测试仅覆盖 ABTM、CHOT、DELY、LACL、PSDT 五个子类型(src/test/java/.../msghandler/flop/);ACTT、CNCL、FDEL 等关键路径无对应测试。按 29 个 STYP 建立「输入样本 × Redis 状态变更 × Kafka 通知」验收矩阵为新系统要求(见 6 节)。
    • ACTT 实际时间:FLTR.ACTT = FLOP.ACTTACTTHandler);
    • ABTM 靠桥 / 撤桥:按 ABOPA=靠桥,D=撤桥)在 ABTM 列表中定位同动作元素,存在则仅更新 AOTM,不存在则新增(ABTMHandler);
    • PSDT 机位:机位相同则直接返回;否则将旧机位写入 LPSDT、清空并写入新 PSDT(机位号 PSNO=0 且无状态视为清空);随后按最后一个机位及基础数据计算 abdgPSDTHandler);
    • DELY 延误:清空旧延误;空 CODE 表示取消延误(同步清理 ABN 中的 DLY 元素);否则写入 DELY 并替换 ABN 中 DLY 元素(DELYHandler);
    • CNCL 取消:写入 CNCL,并维护 ABN 中 CAN 元素(CNCLHandler);
    • FDEL 删除:见 US-A8。
  • 其余子类型(FRET / PADT / CLDT / FTSS / TRML / FDIV / HNAG / STND / ROUT / RENO / BOTM / BDPB / REMC / VIPP / FLIN / LACL / ESTT / TAOP / GTDT / CKDT / FLTY / ACFT / CHOT)的字段更新逻辑以各自 updateFltr() 实现为准。
  • 关联代码:msghandler/flop/*.javasrc/test/java/.../msghandler/flop/*Test.java

US-A8 删除动态航班(FLOP-FDEL

  • 角色 / 期望 / 目标:作为上游,期望通知某航班从运营计划中删除,以便本服务删除实时记录并通知前端同步删除。
  • 处理流程:FDELHandler.run
    1. 航班存在:deleteFltr 删除 Redis 记录 → 无条件发送删除消息至 Kafka msg(不区分共享/非共享航班)→ 标记已处理;
    2. 被删航班为共享航班:从内存中的主航班 MAFL 移除对应元素并缓冲主航班(该修改未写回 Redis,见 5.7);
    3. 航班不存在:标记已处理并返回失败。
  • 验收标准(现役可证实范围):
    • Redis 中对应 FLID 被删除;
    • 被删航班为共享航班时:发送缓冲中主航班的 MAFL 不再包含被删共享航班;Redis 内主航班记录不受该操作影响(遗留缺陷,见 5.7)。
  • 关联代码:msghandler/flop/FDELHandler.javaservice/flightInfo/FlightInfoServiceImpl.java

EPIC-B 前端通知

US-B1 单条动态即时通知

  • 角色 / 期望 / 目标:作为前端,期望在航班动态更新、删除或日计划完成时即时收到单条 JSON 消息,以便实时更新展示。
  • 处理流程:FlopBaseHandler.sendMsg / SchdBaseHandler.sendMsg|sendDschdexchangeService.msgToJson(msg)kafkaservice.msgSend("msg", json)
  • 验收标准:消息发送至主题 msg;动态与计划 Handler 中共享航班自身不触发单条通知。
  • 例外(遗留行为):FDELHandler 对共享航班的删除同样发送 msg 删除消息(无条件调用 sendMsg)。是否保留该通知语义由迁移决策确定(见 5.7)。
  • 关联代码:msghandler/flop/base/FlopBaseHandler.javamsghandler/schd/base/SchdBaseHandler.javaservice/KafkaServiceImpl.javaservice/exchange/ExchangeServiceImpl.java

US-B2 批量缓冲定时下发

  • 角色 / 期望 / 目标:作为前端,期望定期收到本窗口内变更航班的聚合消息,以便合并刷新并降低消息量。
  • 处理流程:FltrSendScheduledcron */3 * * * * ?)→ takeAllFltr 取空进程内队列 → JsonUtil.getString(fltrs) → 发送至 Kafka schd
  • 验收标准:消息发送至主题 schd,内容为 FLTR 数组 JSON;队列为空时不发送。
  • 现役限制:takeAllFltr 先取空队列再发送,发送/序列化异常仅记录日志、不回灌,为 at-most-once(批次可能整体丢失),"定期收到"不保证可靠投递(见 3.5、5.7-17、6.2-5)。
  • 关联代码:scheduled/FltrSendScheduled.javaservice/FltrSendBufferServiceImpl.java

EPIC-C 日计划请求

US-C1 按时间范围发起日计划请求

  • 角色 / 期望 / 目标:作为计划人员,期望提交时间范围,以登记一次日航班计划请求(RQFD 出站报文写入 COUTMSGS)。现役能力止于"登记出站意图",不含实际投递。
  • 触发条件:POST /schd/sync,请求体 {startDate, endDate}(时间格式 yyyy-MM-dd hh:mm)。
  • 处理流程:SchdController.sync(时间解析)→ CmoutmsgServiceImpl.sendSchdGetMsg
    1. SEQN = max(COUTMSGS_ID) + 1
    2. 构造 RQFD-NONE 报文(RQFD.STDB / STDE 格式 ddMMMyyHHmm,大写);
    3. msgToXml 序列化后写入 COUTMSGSROUTINGID=OSH5RQFD)。
  • 验收标准(现役可证实范围):
    • 报文 META{SNDR=OSH5, TYPE=RQFD, STYP=NONE} 且含时间段字段;
    • COUTMSGS 新增记录,XML 根元素为 MSG 并声明 unisysaodbsis.xsd
  • 现役实现约束(经代码核实):
    • 控制器不校验 sendSchdGetMsg 返回值;
    • 时间解析采用 12 小时制 hh(无 AM/PM 标记),空值、区间合法性与解析失败均无校验;
    • 工程内无 COUTMSGS 读取/发送执行器(见 3.9)。
  • 迁移要求:新系统须显式定义投递责任与出站状态机,提供重试与可观测性;时间格式改为 24 小时制(HH);对时间空值、区间及解析失败做输入校验(见 6 节)。
  • 关联代码:controller/SchdController.javaservice/CmoutmsgServiceImpl.javaentity/msg/builder/MSGBuilder.javaentity/msg/builder/METABuilder.java

EPIC-D 历史与归档

US-D1 每日归档过期入站报文

  • 角色 / 期望 / 目标:作为系统,期望在每日固定时间将接收超过一天的 CMINMSGS 转入历史表并清理原表,以控制主表数据量。
  • 处理流程:CminmsgsHisScheduledcron 0 0 3 * * ?)→ CminmsgHstDao.moveToHistoryINSERT...SELECT NOT EXISTS + DELETE,单事务)。
  • 验收标准:
    • cminmsgs_hst 增加符合条件记录(列见 5.5);
    • 原表删除一天前的记录;
    • 历史表中已存在的 ID 不重复归档。
  • 关联代码:scheduled/CminmsgsHisScheduled.javadao/CminmsgHstDaoImpl.java

US-D2 每日将过期动态航班写入历史索引

  • 角色 / 期望 / 目标:作为系统,期望将已结束运营的航班从实时库转入 Elasticsearch 历史库,以便历史查询可用并保持实时库精简。
  • 处理流程:FlightHisScheduledcron 0 30 3 * * ?)→ findAllgetHisToBeTran(判定条件见 3.7)→ FlightHisServiceImpl.save(按 SODT + FLID upsert 至 flight_hts)→ 成功航班从 Redis 删除。
  • 验收标准:
    • 满足任一历史条件的航班写入 ES
    • 仅写入成功的航班从 Redis 删除;
    • "单条失败不影响其他航班"仅适用于 ES 写入阶段(save 循环逐条捕获异常);getHisToBeTran 资格判定阶段未逐条隔离,空/非法时间字段会中断当次整批(见 3.7、5.7-18)。
  • 关联代码:scheduled/FlightHisScheduled.javaservice/flightInfo/FlightHisServiceImpl.javaelasticsearch/ElasticsearchUtil.javaservice/flightInfo/FlightInfoServiceImpl.java

US-D3 一次性历史索引迁移

  • 角色 / 期望 / 目标:作为运维,期望将 flight_hts 全量历史迁移至新索引 flight_hts2,以完成索引结构变更。
  • 触发条件:GET /flights/migrate
  • 处理流程:scroll 全量读取 flight_htstransToNewFltrs(还原 ABN 子对象 DLY / CAN / RTN / ALT)→ 逐条写入 flight_hts2
  • 验收标准:数据完整写入 flight_hts2,异常子对象还原正确。
  • 关联代码:controller/FlightsController.javamigrateFlightHst / transToNewFltrs / mapToDly)。

EPIC-E 实时查询与基础数据

US-E1 查询全部最新动态航班

  • 角色 / 期望 / 目标:作为前端或调用方,期望获取当前全量动态航班(不含共享航班),以便页面渲染。
  • 触发条件:GET /all/flights
  • 处理流程:findAllRedis hmget)→ 过滤 MAID == NULLResponseDto.success
  • 验收标准:返回结果不含共享航班记录,内容与 Redis flightInfo 一致。
  • 关联代码:controller/FlightsController.javaservice/flightInfo/FlightInfoServiceImpl.java

US-E2 以机位与登机桥基础数据完善动态航班

  • 角色 / 期望 / 目标:作为系统,期望依据计划机位获取机位类型与登机桥编码,以生成动态航班的登机桥字段。
  • 处理流程:PSDTHandler.updateAbgOrmsStandManager / OrmsStandAirbridgeManager(读取 Redis 缓存,未命中时经 RestTemplate 调用 admin-api)。
  • 验收标准:近机位(standType = near)生成 abdg;远机位或清空机位消息时 abdg 为空。
  • 关联代码:basicdata/manager/OrmsStandManager.javabasicdata/manager/OrmsStandAirbridgeManager.javabasicdata/service/OrmsStandServiceImpl.javamsghandler/flop/PSDTHandler.java

EPIC-F 运维工具

US-F1 转发报文至指定 Kafka 主题

  • 角色 / 期望 / 目标:作为运维,期望将 JSON 或 XML 报文发送至指定主题,以支持联调、补发与压测。
  • 触发条件:POST /kafka/topics/{name}/msgs
  • 处理流程:请求体按 JSON 解析成功则直接发送;失败则按 XML 解析为 MSG 后转 JSON 发送。
  • 验收标准:JSON 直发;XML 解析后发送;两种输入均返回成功响应。
  • 关联代码:controller/KafkaController.javautils/JsonUtil.javaservice/exchange/ExchangeServiceImpl.java

US-F2 服务启动即开始消息处理

  • 角色 / 期望 / 目标:作为运维,期望服务启动后自动开始消息轮询处理,无需人工干预。
  • 触发条件:应用启动(CommandLineRunner 执行完成)。
  • 处理流程:AppRunner.runCommandLineRunner)→ MsgExchangeServiceImpl.start
  • 验收标准:启动后即按 msgExchange.intervalSeconds 周期开始轮询并分派未处理报文(见 US-A2)。
  • 关联代码:runner/AppRunner.java

5. 附录

5.1 HTTP 接口清单

方法 / 路径 请求体 响应(ResponseDto 功能 实现
POST /cminmsgs/send XML 文本 body=CMINMSGS_ID 收报入库 CminmsgsController
POST /schd/sync {startDate, endDate} 成功 构造 RQFD-NONE 报文并写入 COUTMSGS SchdController
GET /all/flights body=List<SCHD.FLTR>(非共享) 全量动态航班查询 FlightsController
GET /flights/migrate 成功 迁移 flight_htsflight_hts2 FlightsController
POST /kafka/topics/{name}/msgs JSON 或 XML 成功 转发至指定 Kafka 主题 KafkaController

统一响应封装:dto/ResponseDto.java(字段 is_successerr_codeerr_msgbody)与 enums/ResultCode.java

5.2 定时任务与后台线程

名称 周期(配置 key 行为 实现
消息轮询处理 msgExchange.intervalSeconds(各环境均配置为 1 秒,代码无默认值) 采集未处理 CMINMSGS 并分派 AppRunnerMsgExchangeServiceImplMsgExchangeRunner
入站报文历史归档 scheduled.cminmsgHisCron0 0 3 * * ? 一天前报文转 CMINMSGS_HST 并清理 CminmsgsHisScheduled
动态航班转历史 scheduled.flightInfoCron0 30 3 * * ? 过期航班写 ES flight_hts,从 Redis 删除 FlightHisScheduled
缓冲批量发送 scheduled.flightSendCron*/3 * * * * ? 取空缓冲并发送 Kafka schd FltrSendScheduled
异步执行线程池 scheduled.corePoolSize / maxPoolSize / queueCapacity 定时任务异步执行 config/ScheduledAsyncConfig.java

5.3 消息类型与 Handler 清单

计划类(msghandler/schd/,继承 SchdBaseHandler):

STYP Handler 行为
RESP RESPHandler 日计划快照刷新、通知、标记已处理;失败时重试
DNLD DNLDHandler 同 RESP
ADFT ADFTHandler 添加单条计划外航班并联动共享航班

动态类(msghandler/flop/,共 29 个,继承 FlopBaseHandler;除 FDEL 覆写 run 外,其余覆写 updateFltr):

ABTMFRETPADTACTTPSDTCLDTFTSSTRMLFDELFDIVHNAGSTNDDELYROUTRENOBOTMCNCLBDPBREMCVIPPFLINLACLESTTTAOPGTDTCKDTFLTYACFTCHOT

已核实语义:ACTT 实际时间、ABTM 靠 / 撤桥时间、PSDT 机位、DELY 延误、CNCL 取消、FDEL 删除;其余子类型以各自 updateFltr() 实现及 SIS 规范为准。

忽略清单默认值:dev / local / pro = ldm-*,regn-*,rsta-*,eror-*test = ldm-*,regn-*,rsta-*,error-*

5.4 配置项(示例取自 application-dev.yml

  • spring.jpaddl-auto=none;物理命名策略 PhysicalNamingStrategyStandardImpl
  • spring.datasourceMySQL,库 cdairport
  • spring.redis.clusternodes / password / pool。
  • kafka.producer.*servers、retries=0、linger=1、batch、buffer、max-requestkafka.consumer.*:含 zookeeper 遗留配置,无监听器使用。
  • msgExchangeintervalSeconds=1ignoreMsgwaitDateLeftOffset=600000(各环境均有配置,但 Java 代码零引用,未被使用,迁移时可剔除)。
  • logstash.hostdev = hz011.int.it2000.com.cn:5555local = test = 130.120.3.234:5000pro = 172.17.35.160:5000application-pro.yml:60,与 local/test 不同)。
  • scheduledflightInfoCron=0 30 3 * * ?cminmsgHisCron=0 0 3 * * ?flightSendCron=*/3 * * * * ?corePoolSize=10maxPoolSize=50queueCapacity=10
  • hstCondition(单位毫秒):ARRIVE_HST_TIME=36000001 小时)、CANCEL_HST_TIME=36000001 小时)、FDIV_HST_TIME=59400000SODT_HST_TIME=2592000003 天)。
  • elasticsearchip / port=9300 / pool=5 / cluster.namedev = opensearch/ nodes。
  • eureka.client.service-url.defaultZoneeureka.instance.hostName / prefer-ip-address
  • adminApischema / baseUrldev、test、pro 使用服务名 ADMINAPI;仅 local 使用 host:port 130.120.3.231:92)、ormsStandsUrlgetAirbridgeCodeByStandCodeUrl

5.5 数据表列清单(实体映射)

CMINMSGSentity/Cminmsg.java):CMINMSGS_IDCMINMSGS_CLOB_MSGCMINMSGS_DATE_PROCESSEDCMINMSGS_DATE_RECEIVEDCMINMSGS_STATUSCMINMSGS_SUBSYSTEM_DATE_SENTCMINMSGS_SUBSYSTEM_NAMECMINMSGS_SUBSYSTEM_SEQUENCECMINMSGS_SUBSYSTEM_SUBTYPECMINMSGS_SUBSYSTEM_TYPE

CMINMSGS_HSTentity/CminmsgHst.java)与 CMINMSGS_TESTentity/CminmsgTest.java):列同上。

COUTMSGSentity/Cmoutmsg.java):COUTMSGS_IDCOUTMSGS_ACK_DATE_RECVCOUTMSGS_ACK_REQDCOUTMSGS_ACK_RESEND_TIMESCOUTMSGS_CLOB_MSGCOUTMSGS_DATE_INSERTEDCOUTMSGS_DATE_SENTCOUTMSGS_ENCRYPTCOUTMSGS_ERRORCOUTMSGS_FINAL_GROUP_INDCOUTMSGS_GROUP_IDCOUTMSGS_GROUP_ORDERCOUTMSGS_NO_MESSAGESCOUTMSGS_TRUEFALS_GROUPROUTINGID

5.6 Redis key 汇总

key 结构 内容 维护方式
flightInfo hashFLID → FLTR 动态航班实时库 日计划合并写入(hmset = putAll,不清理差集)/ 动态增量 / 转历史后删除
orms_stand hashstandCode → OrmsStand 机位基础数据 未命中时全量拉取并写入,过期 3600 秒
orms_stand_airbridge hashstandCode → JSON 数组字符串 机位与登机桥关系 逐机位懒加载写入,过期 3600 秒

5.7 实现特征与已知问题

以下为经代码核实的实现特征,供新系统设计对照:

  1. Handler 由 Dispatcher 反射实例化(非 Spring Bean),依赖经 SpringUtil 手动获取;分派通过反射调用 run
  2. 处理标记不区分成功与失败:Handler 内部失败亦标记已处理;不标记分支包括 RESP / DNLD 刷新失败(updateByDaySchd 返回 false)与 ADFT 数量校验失败(直接 HandlerResult.failure())。这些消息按轮询周期持续重试,无显式重试上限/死信;每日 3:00 归档任务按 CMINMSGS_DATE_RECEIVED < 1 天 将其移入历史表并删除(重试窗口约 1 天,见 3.6)。
  3. XML 解析与序列化基于 Jackson XMLJAXB 注解 introspection);msgToXml / msgToJson 通过反射 get{TYPE}() 获取业务体;xmlToMsg 含 BDPB 空元素兼容处理。
  4. RedisTemplate 启用 Jackson enableDefaultTypingorms_stand_airbridge 的值为 JSON 字符串(存在二次编码)。
  5. JSON 处理并存两套工具:JsonUtilJackson)与 fastjson JSONObjectES 写入、FlightsController);部分控制器直接实例化 ObjectMapper
  6. CmoutmsgHstServiceImplCMINMSGS_ID % 10 分表归档逻辑及 insertToTable 仍保留,但未被当前定时任务使用(现使用单表 cminmsgs_hstmoveToHistory)。
  7. Kafka 仅生产不消费;kafka.consumer.* 与 zookeeper 配置无消费方;业务主题固定为 msgschd
  8. COUTMSGS 仅写入无读取;/schd/sync 产物无发送执行器(见 3.9)。
  9. 明文口令直接存放于各环境 application-*.yml;默认激活环境为 devspring.profiles.active=dev)。
  10. 忽略清单在 test 环境配置为 error-*,与其他环境(eror-*)不一致。
  11. hstCondition 各阈值按毫秒计算(配置注释亦为毫秒),而相关代码注释存在单位表述不一致。
  12. updateByDaySchdRedisService.hmsetputAll)合并写入:新日计划快照中未出现的旧航班不会被清除(见 3.3)。如采用整体替换语义,新系统须显式清理差集并以回放测试固定预期。
  13. FDELHandler 对共享航班的删除同样调用 sendMsg 发送 msg 删除消息(与"共享航班不直接发送"的通用规则不一致)。
  14. FDELHandler 删除共享航班、ADFTHandler 添加共享航班时,对主航班 MAFL 的修改均仅作用于内存对象并进入发送缓冲,未调用 saveFltr 写回 Redis;FDEL 元素比对使用 ==MAFLDATA.getFlid()FLTR.getFLID() 均为 BigInteger,故为引用比较而非值比较)。属遗留缺陷,新系统应采用值相等比较并在同一原子状态变更中持久化主/共享航班关系。
  15. /schd/sync 时间解析使用 12 小时制 hh(无 AM/PM 标记),且对空值、区间、解析失败及服务返回值均无校验。
  16. updatefltrBridgeABDG按序直接追加,代码不插入分隔符(分隔符依赖数据自身是否带逗号),仅当拼接结果以逗号结尾时删除末尾一个逗号(见 3.3)。
  17. Kafka 批量缓冲(主题 schd)为 at-most-onceFltrSendScheduledtakeAllFltr 先取空进程内队列,序列化或发送异常仅记录日志、不回灌队列,该批消息可能整体丢失(见 3.5、US-B2)。
  18. FlightHisServiceImpl.getHisToBeTran(资格判定)未对单条航班做异常隔离:SODT 缺失/非法(四个判定方法均无空值保护直接解析)、ACTT/CNCL 非空但格式非法、MVIN 为空且 ACTT 存在(isDMatch/isAMatch 的 equals NPE)等任一条异常都会由 FlightHisScheduled 外层捕获并中断当次整批,剩余航班不再处理;仅 save(ES 写入)循环逐条捕获异常(见 3.7、US-D2)。

6. 迁移验收边界(评审并入)

本节内容依据 Plane ACMA-4 评审意见整理,用于区分"现役行为"(正文与附录的描述对象)与"新系统验收要求"(本节),避免迁移对拍时把待实现语义当作现役事实。

6.1 类别区分

  • 现役行为:文档正文与附录所描述内容,均可经"关联代码"复核。
  • 迁移目标:新系统须显式实现的能力(见 6.2),不以现役实现为上限。
  • 待确认外部契约:COUTMSGS 投递通道与协议(3.9)、搜索集群类型(ES 或 OpenSearch,见 5.4)、前端对 FDEL 通知语义的依赖(US-B1 例外)。

6.2 迁移验收边界

  1. 快照替换语义:若日航班计划采用整体替换,须显式清理差集,并以回放测试固定「输入样本 → Redis 终态」预期(现役为合并语义,见 3.3)。
  2. 共享航班增删链路:决定保留或消除 FDEL 对共享航班的通知(US-B1 例外);主/共享航班关系(ADFT 添加、FDEL 删除)须在同一原子状态变更中持久化(修复 ADFT/FDEL 未写回与 FDEL == 引用比较缺陷,见 5.7-14)。
  3. 可执行验收矩阵:覆盖 3 个 SCHD 与 29 个 FLOP 子类型,维度为「输入样本 × Redis 状态变更 × Kafka 通知结果」;对当前无测试的关键路径(如 ACTT、CNCL、FDEL、RESP、DNLD、ADFT)补自动化用例。
  4. 入站可靠性:每类消息的幂等键、重复投递与多实例并发行为。
  5. 失败处理:解析、业务、Kafka、Redis、ES 失败的分类、重试上限、死信与人工重放。
  6. 一致性:跨 MySQL、Redis、Kafka、ES 的一致性与可恢复性约束。
  7. 契约:消息顺序、共享航班更新与前端可观察事件的契约测试。
  8. 安全基线:移除 application-*.yml 明文凭据与 Redis 默认多态反序列化(enableDefaultTyping)。

6.3 评审采纳记录

ACMA-4 评审意见 处理 文档落点
1 日计划"整体覆盖"表述不符 采纳,改为合并写入语义 3.3、US-A4、5.6、5.7-12、6.2-1
2 FDEL 共享航班亦发送通知 采纳,标注例外 3.4、US-A8、US-B1、5.7-13、6.2-2
3 共享航班删除关系不保证持久化 采纳,标注遗留缺陷 3.4、US-A8、5.7-14、6.2-2
4 测试覆盖声明过度 采纳,区分"语义核实"与"测试覆盖" US-A7、5.7、6.2-3
5 RQFD 故事业务承诺超出现役 采纳,改为"登记出站意图" US-C1、3.9、5.7-15、6.2
补充验收边界 采纳 6.2、6.3

6.4 第二轮评审采纳记录(2026-09-03 ZCode 自动评审 + 二次全面复审)

评审项 处理 文档落点
1 logstash.host pro 环境值错误 采纳,pro = 172.17.35.160:5000 5.4
2 adminApi.baseUrl test 环境应为服务名 采纳,dev/test/pro 用 ADMINAPI,仅 local 用 host:port 5.4
3 ADFT 数量校验失败不标记、持续重试 采纳,纳入"不标记"分支,区分抛异常/返回失败 3.3、US-A2、US-A5、5.7-2
4 ADFT 共享航班联动同样不持久化主航班 MAFL 采纳,扩展 5.7-14 为 ADFT/FDEL;验收收敛为缓冲对象 3.3、US-A5、5.7-14、6.2-2
5 abdg 拼接机制为按序直接追加 采纳,改写 3.3 与 US-A4 验收 3.3、US-A4、5.7-16
6 FDEL == 为 BigInteger 引用比较 采纳,修正 Long 表述 3.4、5.7-14
7 ADFT 标记已处理先于共享联动 采纳,按代码顺序重写 3.3、US-A5
8 端口 8080 为隐式缺省 采纳,注明无 server.port 配置 1 节
9 附录 C/D/E 悬空引用 采纳,改为 5.3/5.4/5.5 2、3.4、3.7、US-A6、US-D1、6.1
10 US-A5 缺触发条件、US-F2 缺验收标准 采纳,补齐格式字段 US-A5、US-F2
11 waitDateLeftOffset 未被使用 采纳,标注未使用 5.4
12 失败消息隐式重试至归档(约 1 天窗口) 采纳,补充说明 5.7-2、3.6
二次全面复审:批量 Kafka 通知为 at-most-once 采纳,标注现役限制 3.5、US-B2、5.7-17、6.2-5
二次全面复审:历史资格判定坏数据中断整批 采纳,收窄验收至 ES 写入阶段 3.7、US-D2、5.7-18
R1 5.7-18/3.7 坏数据举例不精确(SODT 无保护、ACTT 缺失有保护、MVIN 空 NPE 采纳,按代码实况改述 3.7、5.7-18
R2 "默认 1 秒"实为各环境配置值(代码无默认) 采纳,改为"各环境均配置 1 秒、代码无默认值" 3.2、US-A2、5.2
R3 US-A5 触发条件括注移入流程约束;2 节补 ADFT 失败示例 采纳 US-A5、2 节

本节与新系统实施计划(ACMA-3)同步维护;迁移实施 issue 仅引用本修订版用户故事作为一致性对拍基线。6.4 采纳记录含首轮(6.3)、第二轮与第三轮(R1–R3)评审意见的落点,正文与附录均以修订后描述为准。