24 KiB
admin-api
成都天府/双流机场 OMMS(机场运行管理系统)的后端管理 API,为 Web 控制台提供基础数据、数据字典、UI 个性化设置、航班季度计划、历史航班检索与 Excel 导出能力。
| 项 | 值 |
|---|---|
| 坐标 | com.gzzn.omms:admin-api |
| 版本 | 1.2.1-SNAPSHOT |
| 技术栈 | Spring Boot 1.5.17 · Spring Cloud Edgware.SR5 · Java 8 · Maven |
| 服务注册 | Eureka,服务名 adminapi |
| 监听端口 | 8080(Dockerfile EXPOSE,配置文件未覆盖) |
| 仓库 | git@repo.windy.me:cdia/admin-api.git |
Java 版本要求:JDK 8。 项目基于 Spring Boot 1.5 和 JAXB 生成代码构建,未验证 JDK 11+;请使用 JDK 8 进行本地开发、构建和运行。
定位:只读数据服务层。 本服务不生产业务数据,OMMS 主数据由 Oracle 侧系统维护,本服务只做查询与对外暴露;仅 MySQL 侧的 UI 设置为本服务自有写数据。
不在本服务职责内:航班动态实时推送、航班保障流程、报文收发与解析落库(由 AODB/消息中间件侧负责)。本服务不保存航班数据,历史航班从 Elasticsearch 读取。
1. 架构
┌──────────────────────────────────────────┐
浏览器 ──> 网关 ───>│ admin-api (adminapi) │
注入 user 头 │ │
│ controller → dao → convert → dto │
└───┬──────────┬──────────┬──────────┬─────┘
│ │ │ │
MySQL Oracle Elasticsearch sysapi
(自有/写) (主数据/读) (历史航班/日志) (日志开关)
外部依赖与降级行为
| 依赖 | 用途 | 不可用时的影响 |
|---|---|---|
| MySQL(Primary) | 数据字典、UI 设置 | 字典与 UI 设置接口全部 500 |
| Oracle(Secondary) | 机场基础数据、航班季度计划 | 基础数据/季度计划接口全部 500 |
| Elasticsearch | 历史航班查询、操作日志写入 | 历史航班查询 500;写日志失败被 catch 吞掉,不影响主流程 |
sysapi(Eureka 服务名 SYSAPI) |
拉取操作日志开关 | UI 设置已落库;日志开关查询异常会被捕获并记录,不影响保存结果,但会丢失本次审计日志 |
| Eureka | 服务注册与 SYSAPI 寻址 |
注册失败则不被网关发现;SYSAPI 调用失败 |
身份与鉴权(重要)
本服务不做任何登录鉴权,也没有接口级权限控制。 完全信任网关:
/settings/uisettings通过请求头user(Base64 编码的 JSON)取得userId / userName / realName;头缺失时,该接口抛UserNoLoginException,全局处理返回 401 +USER_NOT_LOGGED_IN- 基础数据、字典、季度计划和历史航班接口当前不会读取
user头,可匿名访问 - 请求头
remoteAddress(同为 Base64 JSON)携带客户端来源地址,用于审计日志
因此本服务绝不能直接暴露到公网,必须经网关访问。否则基础数据、字典及历史航班查询接口会直接暴露;UI 设置的数据隔离也仅依赖
userId,属"代码层隔离"而非强制鉴权。
2. 快速开始
前置条件
- JDK 8、Maven 3
- 可达的 MySQL、Oracle、Elasticsearch、Eureka(本地开发可用
dev环境现有实例) - Oracle 驱动:pom 以
systemscope 引用src/main/resources/libs/ojdbc6-11.2.0.3.jar,该 jar 已在仓库内,无需额外安装
运行
# 开发模式(默认 profile=dev)
mvn spring-boot:run
# 打包
mvn clean package -Dmaven.test.skip=true
java -jar target/admin-api-1.2.1-SNAPSHOT.jar
# 指定环境
java -jar target/admin-api-1.2.1-SNAPSHOT.jar --spring.profiles.active=test
验证
# 接口文档(swagger-bootstrap-ui)
open http://localhost:8080/doc.html
open http://localhost:8080/swagger-ui.html
# 冒烟:字典查询(当前无需 user 头)
curl http://localhost:8080/dictionary/datadicItems/ROUTE_TYPE
注意:Swagger UI 可直接调试当前不读取
user头的接口;调试/settings/uisettings时需经网关注入user头,否则会返回 401。
3. 配置
application.yml(公共)+ application-{dev,test,pro}.yml,通过 spring.profiles.active 切换(默认 dev)。
| 配置项 | 说明 |
|---|---|
spring.datasource.primary.* |
MySQL 连接 + Tomcat JDBC 连接池参数 |
spring.datasource.secondary.* |
Oracle 连接,driver: oracle.jdbc.driver.OracleDriver |
eureka.client.service-url.defaultZone |
注册中心地址 |
elasticsearch.cluster.nodes / cluster.name / maxSize |
ES 集群(9300 传输端口) |
sysApi.getOpLogSwitchByLevleAndNameUrl |
操作日志开关接口,含 {level} {name} 占位符 |
logstash.host |
日志收集地址(host:port) |
环境对照
| dev | test | pro | |
|---|---|---|---|
| MySQL | 130.120.3.235:3306 |
130.120.3.158:3306 |
172.17.35.162:3306 |
| Oracle | 130.120.2.105:1521/orcl |
130.120.3.31:1521/orcl |
172.17.35.43:1521/omms |
| Eureka | 130.120.3.235:94 |
130.120.3.232:94 |
172.17.35.164:94 |
| ES 集群 | .234/.236/.238:9300 |
.234/.236/.238:9300 |
172.17.35.160/.161/.162:9300 |
| Logstash | 130.120.3.234:5000 |
130.120.3.234:5000 |
172.17.35.160:5000 |
密码加密(预留但未启用)
PrimaryDataSourceConfig 中保留了加密入口,当前被注释:
//dataSource.setPassword(ConfigTools.decrypt(properties.getPassword()));//解密数据库密码
dataSource.setPassword(properties.getPassword());
如需启用,取消注释并引入 Druid ConfigTools。目前所有环境明文口令已提交进 Git(含生产 root)。
4. 数据层
双数据源
本服务使用两套手工配置的 JPA 数据源:
| 数据源 | 数据库 | 当前用途 |
|---|---|---|
| Primary | MySQL | 数据字典、UI 设置(读写) |
| Secondary | Oracle | 机场基础数据、航班季度计划(当前业务只读) |
每一侧都拥有独立的 DataSource → EntityManagerFactory → TransactionManager。Primary/Secondary 的 DAO 与 Entity 分别由对应配置类按包扫描并绑定,因此新增表访问时必须将 Entity 和 DAO 放到同一侧的包中。
Secondary 未配置强制只读保护;跨库操作也不具备原子性。涉及写操作时必须明确指定事务管理器。
双数据源的实现原理、配置位置、新增 DAO/Entity 的步骤、常见错误与事务示例见 双数据源说明。
MySQL 自有表(4 张)
| 表 | 用途 |
|---|---|
adminapi_datadic_groups |
字典分组(如 FLIGHT_TASK 航班任务、ROUTE_TYPE 航线类别) |
adminapi_datadic_items |
字典项 |
adminapi_uisettings |
用户 UI 设置,按 (group_code, userid) 唯一 |
adminapi_uisettings_default |
系统默认 UI 设置,按 group_code |
uisettings.setting_content 存 JSON,含 userSettingCol(表格列)、userSettingColor(配色)、userSettingMsgAlert(消息告警订阅)、userSettingSelected(查询条件默认值)等分组。
Oracle 数据(当前业务只读)
基础数据:航司/机场/城市/国家及其分组、航站楼与区域、机位与机位类型、登机口、滑槽、值机柜台与分组、行李转盘、机型与机号、机型分组、航班状态与类型、航班代理。
航班计划:FIMS_FLIGHTSCHD_SEASON(季度计划)、FIMS_MIDAIRPORTS_SEASON(经停航段)。
Elasticsearch 索引
| 索引 | 用途 | 映射 |
|---|---|---|
flight_hts |
历史航班查询 | 实为别名,指向 flight_hts2;mapping 见 resources/es/v1.2.0_20190428_flightHstMapping.json |
oplog |
操作日志(审计) | 无 mapping 文件,由代码 createIndex 自动创建,依赖动态映射 |
索引与 mapping 需手工创建:ES 资源目录下的 JSON 是 Kibana/Dev Tools 可直接执行的脚本(
PUT flight_hts2→PUT /flight_hts2/_alias/flight_hts→PUT flight_hts2/_doc/_mapping)。oplog由代码首次写入时自动创建,但字段类型由 ES 动态推断,生产建议预先定义 mapping。
数据库变更
没有 Flyway / Liquibase,靠 src/main/resources/db/migration/ 下脚本人工执行:
v0.0.1.20190110_adminapi_ddl.sql 建 4 张表
v0.0.1.20190110_adminapi_initdata.sql 字典与默认 UI 设置初始化
v0.0.2.20190327_adminapi_updatecolorsetting.sql 更新默认配色
v1.2.0.20190329_adminapi_addUserSettingSelected.sql 新增 userSettingSelected 分组
v1.2.0.20190507_adminapi_updateUserSettingCol.sql 更新默认表格列(406 行)
命名规范:v{版本}.{日期}_{模块}_{说明}.sql。上线前必须确认脚本已在目标库执行,无自动校验。
5. API 概览
统一响应契约
{ "is_success": true, "err_code": 1, "err_msg": "成功", "body": {} }
成功 err_code=1,失败 err_code=0(业务细分码见 enums/ResultCode)。
错误码分段
| 段 | 含义 | 典型 |
|---|---|---|
1 / 0 |
通用成功 / 失败 | — |
10001–19999 |
参数错误 | PARAM_IS_INVALID PARAM_NOT_COMPLETE |
20001–29999 |
用户错误 | USER_NOT_LOGGED_IN USER_NOT_EXIST |
30001–39999 |
业务错误 | SPECIFIED_*_NOT_FOUND |
40001–49999 |
系统错误 | SYSTEM_INNER_ERROR |
50001–59999 |
数据错误 | RESULE_DATA_NONE DATA_ALREADY_EXISTED |
60001–69999 |
接口错误 | INTERFACE_INNER_INVOKE_ERROR |
70001–79999 |
权限错误 | PERMISSION_NO_ACCESS |
ExceptionHandle兜底所有未捕获异常 → HTTP 500 +SYSTEM_INNER_ERROR,响应体err_msg形如requestId:xxx,errorMsg:yyy。requestId是排障第一线索,见第 7 节。
端点清单
基础数据(controller/basicdata,全部 GET,全量返回无分页)
| 领域 | 端点 |
|---|---|
| 航空公司 / 集团 | /basicdata/sysAirlines、/basicdata/sysAirlineGroup |
| 机场 / 集团 | /basicdata/sysAirports、/basicdata/sysAirportGroups |
| 城市 / 国家 | /basicdata/sysCitys、/basicdata/sysCountry |
| 航站楼 / 区域 | /basicdata/ormsTerminals、/basicdata/ormsTerminalareas |
| 机位 / 机位类型 | /basicdata/ormsStands、/basicdata/ormsStandTypes |
| 登机口 / 滑槽 | /basicdata/ormsGates、/basicdata/ormsChuts |
| 值机柜台 / 转盘 | /basicdata/ormsCheckindesks、/basicdata/ormsCarousels |
| 值机分组 | /basicdata/checkinGroups |
| 机型 / 机号 / 机型分组 | /basicdata/sysAircrafttypes、/basicdata/sysAircrafts、/basicdata/sysAircrafttypesGroup |
| 航班状态 / 类型 / 代理 | /basicdata/sysFlightStatus、/basicdata/sysFlightTypes、/basicdata/sysFlightAgents |
| 航班状态定义 | /basicdata/flmsFlightStatusDefinition |
| 机位 → 廊桥号 | /basicdata/ormsStands/{standCode}/airbridgeCode |
业务接口
| 方法 | 端点 | 说明 |
|---|---|---|
GET |
/dictionary/datadicItems/{groupCode} |
按分组查字典项 |
GET |
/settings/uisettings |
查当前用户 UI 设置(envCode groupCode 可选) |
POST |
/settings/uisettings |
保存 UI 设置 + 写操作日志到 ES |
GET |
/schedule/flightSchdSeasons |
航班季度计划(startDate 年份可选) |
GET |
/schedule/flightSchdSeason/years |
季度计划可选年份 |
POST |
/hitFlightData/list |
历史航班检索(ES) |
POST |
/fltrs/toExcel |
航班动态导出 Excel |
6. 核心业务逻辑
6.1 UI 个性化设置 —— settings/UISettingsController
本项目唯一有真正业务密度的逻辑:用户设置覆盖系统默认设置的合并策略。
GET /settings/uisettings:
- 取全量
adminapi_uisettings_default载入 Map(key =groupCode) - 按
envCode/groupCode的空与非空,4 个分支走不同 DAO 查询当前用户的uisettings - 用户配置转 DTO;每命中一条,从默认 Map 移除同
groupCode项(用户优先) - 合并剩余默认项:仅当"两条件都为空"或"仅传 groupCode"时补回
POST /settings/uisettings:
- 按
(groupCode, userId)查旧值:存在则更新并复用原 id,不存在则CommonUtils.getUUID32()生成新 id save()落库- 写审计日志到 ES
oplog,记录新旧值 JSON、操作人、来源 IP、操作类型(ADD/UPDATE)
日志开关判定 ifNeedMarkLog(level, name):经 OplogSwithcInfoServiceImpl 调用 sysapi 的 /oplog/getOpLogSwitch/{level}/{name} 拉取开关列表(@LoadBalanced RestTemplate,走 Eureka 服务名 SYSAPI)。规则:父级(level 更小)关闭则整类不记录;同级需再比对 name。
已知风险:开关列表
opLogSwitchInfos缓存在 Controller 实例字段中,启动后不再刷新,开关变更需重启本服务才生效;首次拉取失败及 ES 写入异常均会被 catch 并记录 error 日志,不阻断主流程(日志丢失但不影响用户)。
6.2 历史航班检索 —— history/HistoryFlightDataController
POST /hitFlightData/list 查 ES 索引 flight_hts,不查数据库。
- 时间窗:默认前一天全天;传
hstFLightTime则查该日00:00:00 ~ 23:59:59 BoolQueryBuilder叠加:SODT(计划时间)range +MVIN(A到达 /D离港)term- 按
SODT升序,条数上限elasticsearch.maxSize(各环境均为 10000) - 映射为
SCHD.FLTR,过滤掉MAID != null的共享航班 - 时间需经
DateUtils.swichTimeToEn_ddMMMyyHHmm转英文格式(ddMMMyyHHmm)后查询——ES 中存的即此格式;startSODT、endSODT均可单独传入
6.3 航班动态导出 Excel —— fltrs/FltrController
POST /fltrs/toExcel:前端把已渲染在页面上的列定义与数据回传,服务端按 columns 顺序用 Apache POI 拼装 xlsx 直接写 HttpServletResponse。不查库,服务端不限定导出字段(历史演进见提交 b0ed00e)。失败返回 JSON 错误描述。
数据量由前端决定,无服务端上限;超大导出可能引起内存与超时问题。
6.4 报文实体 —— entity/msg(约 130 个类)
按机场行业标准报文(AIDX 风格)由 XSD 经 JAXB 生成。切勿手工修改——文件头已注明重新编译 Schema 会丢失改动。SCHD(航班计划)下嵌套 FLTR(航班),是历史航班检索与 Excel 导出的返回模型。字段释义见 resources/es/v0.0.1_20181213_hisPlaneDataDesc.json。
7. 可观测性
日志
logback-spring.xml 三个 Appender:
| Appender | 说明 |
|---|---|
STDOUT |
控制台(容器运行时进 docker logs) |
FILE |
logs/adminapi.log,按 10MB 滚动,保留 60 天,总上限 20GB |
SOCKET |
Logstash TCP,附带 app_name=cdairport、model_name=adminapi,由 logstash.host 指定 |
链路追踪
ExceptionAspect(AOP 切 controller..*)在每个请求进入时:
MDC.clear()后写入request_id(取请求头,缺失则生成 UUID)- 记录
url / method / content_type / body / params / remoteip - 返回时记录响应体
request_id 随 MDC 进入 Logstash,是跨服务串联与排障的主线索;同时被 ExceptionHandle 写入错误响应体,用户报错时可直接凭此 ID 检索日志。
健康检查现状
未引入 Spring Boot Actuator,因此没有
/health、/info、/metrics端点。容器编排与网关只能靠 TCP 8080 探活,无法感知数据库/ES 连通性。Kibana 检索建议:model_name: "adminapi"+request_id。
8. 构建与部署
CI/CD(jenkins/Jenkinsfile)
两阶段流水线:
- maven build — 在
maven:3-alpine容器中mvn clean package -Dmaven.test.skip=true(复用宿主~/.m2) - docker build & push — Jenkins Docker Pipeline 通过
docker.build(...).push()构建并推送至reg.int.it2000.com.cn/com.gzzn.omms/admin-api:{version}
镜像基于 alpine-oraclejdk8:slim,时区设为 Asia/Shanghai,ENTRYPOINT 为 java -jar /usr/local/adminapi/myapp.jar。
发布
maven-release-plugin 已接入。历史版本:admin-api-1.2.0(提交 1129afe)。
mvn release:prepare release:perform
pom.xml的<scm>仍指向旧地址git@git.int.it2000.com.cn:...,与当前实际 remote(git@repo.windy.me:cdia/admin-api.git)不一致,执行 release 前需修正。
部署检查清单
- 目标环境
db/migration/中未执行过的脚本已人工执行 - ES 索引
flight_hts2+ 别名flight_hts、mapping 已创建 application-{profile}.yml中数据库/Eureka/ES/Logstash 地址与实际环境一致- 确认
sysapi服务已在 Eureka 注册(否则 UI 设置审计日志可能丢失) - 镜像 tag 与
pom.xml版本一致
9. 运维排障
| 现象 | 排查方向 |
|---|---|
启动报 Table not found / Entity 找不到 |
Entity 或 DAO 放错包(primary ↔ secondary),见第 4 节"双数据源:实现原理" |
启动成功但查询报 ORA-00942 / Table 'xxx' doesn't exist |
Entity/DAO 放错包(primary ↔ secondary)。ddl-auto 未配置,Hibernate 启动时不校验表是否存在,错误延迟到首次查询才暴露,见第 4 节 |
/settings/uisettings 返回 401 USER_NOT_LOGGED_IN |
网关未注入 user 头,或头被中间层剥离 |
500 SYSTEM_INNER_ERROR |
取响应体 requestId,到 Kibana 检索完整堆栈 |
| UI 设置审计日志未写入 | 检查 sysapi 服务可用性、Eureka 注册、oplog 索引及 ES 日志;开关拉取或 ES 写入失败不影响 UI 设置保存 |
| 历史航班查不到数据 | 检查 flight_hts 别名是否存在、ES 数据是否已同步、SODT 格式是否为 ddMMMyyHHmm |
| 操作日志未写入 ES | 检查 oplog 索引与日志开关;ES 写入异常仅记 error 日志,不阻断业务 |
| 数据库连接耗尽 | Tomcat JDBC 池 max-active: 30,检查慢查询与连接泄漏;show-sql: true 便于定位 SQL |
| 服务未被网关发现 | eureka.instance.hostName: adminapi 且 prefer-ip-address: false,依赖主机名解析 |
已知技术债与风险
按处理优先级:
- 明文口令入库:三套环境(含生产
root)口令以明文提交进 Git。应尽快接入配置中心或环境变量,或启用已预留的ConfigTools.decrypt。 - 无健康检查端点:未引入 Actuator,无法探活依赖组件。建议加
spring-boot-starter-actuator并暴露/health。 - 生产开启
show-sql: true:三个 profile 全开,生产环境会产生大量 SQL 日志,影响性能与日志体积。建议生产关闭。 oplog索引无 mapping:依赖 ES 动态映射,字段类型不可控,易引发类型冲突。建议补 mapping 文件。- 无接口级鉴权:基础数据、字典、季度计划和历史航班接口可匿名访问;UI 设置仅靠
userId做代码层隔离。任何新接口必须补充认证授权,并在涉及用户数据时带上用户维度过滤。 - 日志开关不刷新:
opLogSwitchInfos缓存在实例字段,开关变更需重启。 - 基础数据接口无分页:全量返回,数据量增长后需改造。
SwaggerConfig.host硬编码130.120.3.231:91/adminapi,非当前环境地址,会导致 Swagger 调试请求发往错误主机。- 无自动数据库迁移:人工执行脚本,存在漏执行风险。建议引入 Flyway。
- 裸写
@Transactional会静默绑定到 primary:业务代码中目前无任何@Transactional,而 primary 的 TM 带@Primary。新增 Oracle 侧写逻辑时必须显式指定transactionManagerSecondary,否则事务落在 MySQL 上(详见第 4 节)。 entity/msg为 JAXB 生成代码:约 130 个类,报文结构变更须改 Schema 后重新生成。- 技术栈已 EOL:Spring Boot 1.5.17(2018)、Java 8、ES TransportClient(ES 7+ 已移除)、JAXB(Java 11+ 已移除)。升级需通盘规划。
10. 二次开发指南
分层约定
controller → dto/convert → domain.{primary|secondary}.dao → DB
(MapStruct)
- 基础数据类接口当前无 Service 层(Controller 直接注入 DAO),这对纯查询转发是可接受的既定模式;新增含业务规则的功能请引入 Service 层,勿继续在 Controller 中堆积逻辑(参见
UISettingsController,其业务规则已使 Controller 明显臃肿)。 - 对外一律返回 DTO,禁止直接序列化 Entity——会暴露 Oracle 表结构并可能引发循环引用。MapStruct Converter 位于各
dto/*/convert/包,编译期生成实现。
新增一个基础数据接口的步骤
domain.secondary.entity.baiscdata/建 Entity(Oracle 表映射到 Secondary,注意包名归属,见第 4 节)domain.secondary.dao.basicdata/建 DAO,继承CrudRepository<T, String>;复杂查询用@Querydto/basicdata/建 DTO,dto/basicdata/convert/建 MapStruct Convertercontroller/basicdata/建 Controller,注入 DAO + Converter,返回ResponseDto.success(dtoList)- 补
@ApiOperation(Swagger)
编码注意
- 修改
entity/msg下的类前,确认是否应改 Schema 重新生成 - 新增配置需在三个 profile 的 yml 中同步(当前无默认值兜底,缺失即启动失败)
- 涉及用户数据的查询,必须带上
userService.getCurrentUserId()条件
测试
src/test 下 7 个测试类,多为需要真实 Oracle/ES 的集成测试,运行前确保环境可达:
AdminApiApplicationTests、OrmsStandControllerTest、FlightSchdSeasonControllerTest、FltrControllerTest、SysAirlineGroupDaoTest、OrmsStandAirbridgeDaoTest、ElasticsearchUtilTest
CI 中均以 -Dmaven.test.skip=true 跳过,测试未纳入质量门禁。
11. 目录结构
src/main/java/com/gzzn/omms/adminapi/
├── AdminApiApplication.java # 启动类(@EnableEurekaClient)
├── config/ # 双数据源装配(见第 4 节)
│ ├── PrimaryDataSourceConfig / SecondaryDataSourceConfig # DataSource Bean
│ ├── PrimaryConfig / SecondaryConfig # EMF + TM + Repository 扫描
│ ├── PrimaryDsProperties / SecondaryDsProperties # yml 属性绑定
│ └── RestTemplateConfig / swagger/
├── controller/ # basicdata / dictionary / fltrs / history / schedule / settings
├── domain/ # ★ 包名决定走哪个库,勿放错
│ ├── primary/ # → MySQL: dictionary, settings(读写)
│ └── secondary/ # → Oracle: basicdata, schedule(当前业务只读)
├── dto/ # DTO + MapStruct convert
├── elasticsearch/ # TransportClient 配置、查询工具、操作日志模型
├── entity/msg/ # JAXB 生成的报文实体(勿手改)
├── enums/ResultCode.java # 错误码
├── exception/handler/ # 全局异常处理 + 请求日志 AOP
├── service/ # 当前用户解析、操作日志开关
└── utils/ # 日期 / JSON / UUID / Excel 导出
src/main/resources/
├── application{,-dev,-test,-pro}.yml
├── logback-spring.xml
├── db/migration/ # 手工执行的数据库变更脚本
├── es/ # ES 索引 mapping 与字段释义
└── libs/ojdbc6-11.2.0.3.jar # system scope 引入的 Oracle 驱动