Files
admin-api/README.md
T

450 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 进行本地开发、构建和运行。
**Podman 测试镜像构建**:见 [构建说明](docs/build.md),使用 `./build.sh -t localhost/admin-api:1.2.1-pg-test`Maven 缓存默认放在 `../chengdu-repository`
**定位:只读数据服务层。** 本服务不生产业务数据,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 以 `system` scope 引用 `src/main/resources/libs/ojdbc6-11.2.0.3.jar`,该 jar 已在仓库内,**无需额外安装**
### 运行
```bash
# 开发模式(默认 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
```
### 验证
```bash
# 接口文档(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` 中保留了加密入口,当前**被注释**:
```java
//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 的步骤、常见错误与事务示例见 **[双数据源说明](docs/datasource.md)**。
### 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 概览
### 统一响应契约
```json
{ "is_success": true, "err_code": 1, "err_msg": "成功", "body": {} }
```
成功 `err_code=1`,失败 `err_code=0`(业务细分码见 `enums/ResultCode`)。
### 错误码分段
| 段 | 含义 | 典型 |
|---|---|---|
| `1` / `0` | 通用成功 / 失败 | — |
| `1000119999` | 参数错误 | `PARAM_IS_INVALID` `PARAM_NOT_COMPLETE` |
| `2000129999` | 用户错误 | `USER_NOT_LOGGED_IN` `USER_NOT_EXIST` |
| `3000139999` | 业务错误 | `SPECIFIED_*_NOT_FOUND` |
| `4000149999` | 系统错误 | `SYSTEM_INNER_ERROR` |
| `5000159999` | 数据错误 | `RESULE_DATA_NONE` `DATA_ALREADY_EXISTED` |
| `6000169999` | 接口错误 | `INTERFACE_INNER_INVOKE_ERROR` |
| `7000179999` | 权限错误 | `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`
1. 取全量 `adminapi_uisettings_default` 载入 Mapkey = `groupCode`
2.`envCode` / `groupCode` 的空与非空,4 个分支走不同 DAO 查询当前用户的 `uisettings`
3. 用户配置转 DTO;**每命中一条,从默认 Map 移除同 `groupCode` 项**(用户优先)
4. 合并剩余默认项:仅当"两条件都为空"或"仅传 groupCode"时补回
`POST /settings/uisettings`
1.`(groupCode, userId)` 查旧值:存在则更新并复用原 id,不存在则 `CommonUtils.getUUID32()` 生成新 id
2. `save()` 落库
3. **写审计日志到 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..*`)在**每个请求进入时**
1. `MDC.clear()` 后写入 `request_id`(取请求头,缺失则生成 UUID
2. 记录 `url / method / content_type / body / params / remoteip`
3. 返回时记录响应体
`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`
两阶段流水线:
1. **maven build** — 在 `maven:3-alpine` 容器中 `mvn clean package -Dmaven.test.skip=true`(复用宿主 `~/.m2`
2. **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`)。
```bash
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`,依赖主机名解析 |
### 已知技术债与风险
按处理优先级:
1. **明文口令入库**:三套环境(含生产 `root`)口令以明文提交进 Git。应尽快接入配置中心或环境变量,或启用已预留的 `ConfigTools.decrypt`
2. **无健康检查端点**:未引入 Actuator,无法探活依赖组件。建议加 `spring-boot-starter-actuator` 并暴露 `/health`
3. **生产开启 `show-sql: true`**:三个 profile 全开,生产环境会产生大量 SQL 日志,影响性能与日志体积。建议生产关闭。
4. **`oplog` 索引无 mapping**:依赖 ES 动态映射,字段类型不可控,易引发类型冲突。建议补 mapping 文件。
5. **无接口级鉴权**:基础数据、字典、季度计划和历史航班接口可匿名访问;UI 设置仅靠 `userId` 做代码层隔离。任何新接口必须补充认证授权,并在涉及用户数据时带上用户维度过滤。
6. **日志开关不刷新**`opLogSwitchInfos` 缓存在实例字段,开关变更需重启。
7. **基础数据接口无分页**:全量返回,数据量增长后需改造。
8. **`SwaggerConfig.host` 硬编码** `130.120.3.231:91/adminapi`,非当前环境地址,会导致 Swagger 调试请求发往错误主机。
9. **无自动数据库迁移**:人工执行脚本,存在漏执行风险。建议引入 Flyway。
10. **裸写 `@Transactional` 会静默绑定到 primary**:业务代码中目前无任何 `@Transactional`,而 primary 的 TM 带 `@Primary`。新增 Oracle 侧写逻辑时必须显式指定 `transactionManagerSecondary`,否则事务落在 MySQL 上(详见第 4 节)。
11. **`entity/msg` 为 JAXB 生成代码**:约 130 个类,报文结构变更须改 Schema 后重新生成。
12. **技术栈已 EOL**Spring Boot 1.5.172018)、Java 8、ES TransportClientES 7+ 已移除)、JAXBJava 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/` 包,编译期生成实现。
### 新增一个基础数据接口的步骤
1. `domain.secondary.entity.baiscdata/` 建 EntityOracle 表映射到 Secondary**注意包名归属**,见第 4 节)
2. `domain.secondary.dao.basicdata/` 建 DAO,继承 `CrudRepository<T, String>`;复杂查询用 `@Query`
3. `dto/basicdata/` 建 DTO`dto/basicdata/convert/` 建 MapStruct Converter
4. `controller/basicdata/` 建 Controller,注入 DAO + Converter,返回 `ResponseDto.success(dtoList)`
5.`@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 驱动
```