zcloud-gbs-primeport/project-analysis.md

374 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# zcloud-gbs-primeport 项目梳理文档
## 一、项目概览
**系统名称**港口口门门禁管理系统primeport应用名 `zcloud-gbs-primeport`,网关前缀 `/primeport`
**技术栈**
- Spring Boot 2.6.6 + Spring Cloud AlibabaNacos 配置中心/服务发现)
- COLA 4.0 分层架构 + MyBatis-Plus + Druid + MySQL/PostgreSQL 双数据源
- Redis、RocketMQ、XXL-JOB、Flyway、Swagger、Hutool、fastjson
- 自研 jjb-saas 框架(网关/认证/系统客户端)
- 大华 ICC SDK`com.dahuatech.icc:java-sdk-oauth:1.0.13.15`
**分层架构**6 个 Maven 模块):
```
start(启动) ──> web-adapter(控制器) ──> web-app(业务编排) ──> web-client(API契约+DTO) ──> web-domain(领域模型)
│ │ │
└──────────────────────┴──> web-infrastructure(基础设施+第三方对接) ──> web-domain
```
调用链路示例:`Controller → ServiceImpl → Exe(命令执行器) → Gateway(领域接口) → GatewayImpl(基础设施) → Repository → Mapper → DB`
---
## 二、业务模块总览
系统按 **三类角色** 组织功能监管端JGD、分公司端FGS、相关方/驻港单位端XGF/ZGDW通过 `MenuEnum` 控制约 60 项菜单权限。
### 1. 一级口门基础管理
| 模块 | Controller | 核心职责 |
|------|-----------|---------|
| 口门信息 | MkmjController | 口门名称、级别(1一级/2二级)、类型(人行/车行/综合)、所属港区、经纬度 |
| 门禁通道 | MkmjPassageController | 通道类型、所属口门、状态 |
| 闸机管理 | MkmjGateController | 闸机标识、型号、所属通道 |
| 审批人 | MkmjApprovalUserController | 审批人企业/部门/岗位,人员/车辆/临时三类审核权限 |
| 摄像头 | VideoController | 绑定口门或闸机,移动/平台类型 |
| 刷卡记录 | MkmjSwipeCardRecordController | 接收大华推送的刷卡记录(`/dockSave`),分页查询 |
### 2. 封闭区域管理(二级口门)
| 模块 | Controller | 核心职责 |
|------|-----------|---------|
| 封闭区域 | ClosedAreaController | 树级结构(parentId)、按港区/管辖单位查询、统计 |
| 人员申请 | ClosedAreaPersonApplyController | 申请/审批/授权(含二级门禁)、随行人员管理 |
| 车辆申请 | ClosedAreaCarApplyController | 新增/分页/审批(auditFlag=2通过/3驳回) |
| 临时申请 | ClosedTmpApplyController | 统一入口,按 TmpApplyEnum 路由到4种申请类型 |
### 3. 车辆管理
| 模块 | Controller | 核心职责 |
|------|-----------|---------|
| 车辆申请 | VehicleApplyController | 9种车辆归属、长期录入、车牌校验、相关方授权 |
| 车辆审批 | VehicleAuditController | 待审批/审批记录/审批操作 |
| 车辆黑名单 | VehicleBlackController | 新增/分页/删除 |
| 车辆违规 | VehicleViolationsController | 违规记录,超阈值自动拉黑 |
| 车辆门禁信息 | VehicleMessageController | 审批通过的固化信息,用于门禁对接 |
| 车辆对接日志 | VehicleDockLogController | 一级/二级口门对接日志 |
### 4. 人员管理
| 模块 | Controller | 核心职责 |
|------|-----------|---------|
| 人员申请 | PersonApplyController | 相关方人员申请/审批/授权、临时访客H5申请、分公司人员管理 |
| 人员门禁信息 | PersonMessageController | 审批通过的人员信息,用于门禁对接 |
### 5. 电动车数据接入
| 模块 | Controller | 核心职责 |
|------|-----------|---------|
| 电动车基础信息 | EVehicleBasicInfoController | 车辆备案(长期/一次性/内部/临时)、EDW数据溯源 |
| 到离港信息 | EVehicleArrivalDepartureInfoController | 到达/离开闸口与时间记录 |
### 6. App 统计
| 模块 | Controller | 核心职责 |
|------|-----------|---------|
| App角标 | AppCountController | App端统计角标数据 |
---
## 三、核心业务流程
### 流程 A口门车辆申请全链路
```
申请提交 → VehicleApplyAddExe
├─ 按归属类型回填(股份个人车直接 mkmjPermission=1)
├─ 初始状态: auditFlag=1(待审), mkmjPermission=2(无权限)
├─ 插入 VehicleAuditE 审批记录(changeAfter快照)
└─ 推待办给审批人(RocketMQ)
修改 → VehicleApplyUpdateExe (锁定车型/车牌/归属不可改, 重置auditFlag=1, 记录变更对比)
审核 → VehicleAuditUpdateExe.audit
├─ 通过(2): mkmjPermission=1 → CarDockUtil同步一级车行
│ ├─ 临时/相关方车 → tmpVehicleSave(按港区portId)
│ └─ 内部车 → executeInternalVehicle(先删后存)
└─ 驳回(3): mkmjPermission=2
```
### 流程 B封闭区域车辆通行全链路
```
申请 → ClosedAreaCarApplyAddExe (回填用户信息, auditFlag=1, 推待办)
审批 → ClosedAreaCarApplyUpdateExe.auditCarApply
└─ 通过时 → TwoCarUtil.levelTwoCarCompanyByApplyId
├─ 二级海康: HKDeviceUtil.saveOrUpdCar + vehicleTopUp(当日有效)
└─ 一级车行: 先查vehicle_apply是否已有有效权限无则新增(不覆盖)
```
### 流程 C封闭区域人员授权链路最复杂
```
申请 → ClosedAreaPersonApplyAddExe (auditFlag=1, 推待办)
审核 → ClosedAreaPersonApplyUpdateExe.auditPersonApply
└─ 通过时 → TwoPersonUtil.levelTwoPeopleCompanyByApplyId (海康+大华同步)
授权 → ClosedAreaPersonApplyUpdateExe.authPerson
├─ 删旧auth+area重建auth+area(区域关联)
└─ mkmjPermission=1时 → OnePersonUtil:
├─ 查大华是否已有该身份证用户
├─ 无 → dockLongTermUserByUserCardAndFaceImg (账号hbqa+身份证, 当日有效)
└─ 有 → 保留不覆盖
```
### 流程 D车辆违规自动拉黑链路
```
违规录入 → VehicleViolationsAddExe
├─ 查历史违规数,若 (历史+1) >= 配置上限 → 自动拉黑
│ ├─ vehicleBlackGateway.saveIfAbsent
│ └─ vehicleApplyGateway.updateEvehicleBlockedByLicenceNo(...,1)
└─ 新增违规记录(处理状态=2未处理)
```
### 流程 E相关方人员申请链路
```
PersonApplyAddExe.xgfPersonSave
├─ personBelongType∈{1,2} → auditFlag=2(自动通过) + mkmjPermission=1
├─ 其他 → auditFlag=1(待审) + mkmjPermission=2
├─ 推待办
└─ 子表PersonApplyE批量保存(绑定主表XgfApplyPersonE)
```
---
## 四、第三方接口对接
### 对接系统总览
| # | 第三方系统 | 对接方式 | 用途 | 级别 |
|---|----------|---------|------|-----|
| 1 | 大华 ICC 门禁 | SDK + HTTP(RSA加密) | 一级人员门禁:用户创建/有效期/冻结 | 一级 |
| 2 | 海康威视门禁 | SDK(静态工具类) | 二级口门人员/车辆授权 | 二级 |
| 3 | 秦皇岛港车行系统 | Hutool HTTP POST | 一级口门车辆(内部/临时/黑名单) | 一级 |
| 4 | zcloud 用户中心 | Dubbo RPC | 用户信息、人脸图片base64 | 平台内 |
| 5 | 待办列表 MQ | RocketMQ | 审批待办推送/完成 | 平台内 |
| 6 | 大华刷卡推送 | HTTP 被动接收 | 刷卡记录入库 | 一级 |
### 1. 大华 ICC 对接(一级人员门禁)
**配置类**
- `web-domain/.../config/DaHuaOauthProperties.java``@ConfigurationProperties(prefix = "dahua.oauth")`host / clientId / clientSecret / username / password / http / port
- `web-domain/.../config/DaHuaSdkConfig.java`(注入 `OauthConfigUserPwdInfo` Bean
- `start/src/main/resources/sdk.yml``dahua.oauth` 配置
**对接工具类**
- `web-infrastructure/.../plan/mjDevice/OnePersonUtil.java`
- `web-infrastructure/.../plan/mjDevice/TwoPersonUtil.java`
- `web-infrastructure/.../plan/mjDevice/OauthUtil.java`
**核心方法**
- `dockLongTermUserByUserCardAndFaceImg` - 按身份证查用户,不存在则新增(含人脸图片、有效期)
- `expirationDateRefreshedUserCard` - 刷新用户门禁有效期
- `freezeByUserCard` - 用户冻结(2)/解冻(1)
- `getMkmjUserInfoByUserCard` - 按身份证批量查询用户
**OauthUtil 调用流程**
1. `getPublicKey()` - GET 公钥接口忽略SSL证书的 RestTemplate
2. `getToken()` - POST 获取 Token密码通过 RSA 加密)
3. `getGenerateId() / getGenerateIdList(idCount)` - 获取全局用户ID单个/批量)
4. `getDepartmentId() / getDepartmentIdList(idCount)` - 获取部门ID
5. `imgInspect(map)` - 图片质量检测,支持文件/URL/base64 三种格式
**配置项**Nacos 下发,`icc.*`
- `icc.clientId` / `icc.clientSecret` / `icc.username` / `icc.password`
- `icc.url` - ICC 基础地址
- `icc.publicKeyUrl` - 公钥获取地址
- `icc.request.getGenerateIdUrl` / `icc.request.getGenerateIdListUrl` - 全局用户ID接口
- `icc.request.imgInspectUrl` - 图片质检接口
- `icc.request.getDepartmentIdUrl` - 部门ID接口
### 2. 海康威视对接(二级口门)
**对接工具类**
- `web-infrastructure/.../plan/mjDevice/TwoCarUtil.java`
- `web-infrastructure/.../plan/mjDevice/TwoPersonUtil.java`
**核心方法**
- 车辆:
- `HKDeviceUtil.saveOrUpdCar(licenceNo, 0)` - 新增/更新车辆
- `HKDeviceUtil.vehicleTopUp(licenceNo, 0, hkIndexCode, khParkSysCode, startDate, endDate)` - 车辆授权充值
- 人员:
- `HKDeviceUtil.getUserListByPhone(phoneList)` - 按手机号查人限20人/次)
- `HKDeviceUtil.getUserListByUserCard(userCard)` - 按身份证查人
- `HKDeviceUtil.saveUser(name, cardNo, faceBase64, phone)` - 新增人员
- `HKDeviceUtil.authPersonArea(personIds, resourceInfos, startTime, endTime)` - 授权门禁范围限1000人/次)
**业务要点**
- 同步前通过 Dubbo `listBase64ByUrl` 把人脸URL转base64
- 随行人员一并同步授权
- 手机号查不到再用身份证查(处理手机号变更场景)
### 3. 秦皇岛港车行系统对接(一级口门车辆)
**对接工具类**
- `web-infrastructure/.../plan/mjDevice/OneLevelCarUtil.java`
- `web-infrastructure/.../plan/mjDevice/CarDockUtil.java`
**配置**
- `${kmmj.dockflag}` - 对接开关1=启用)
- `${oneLevelCar.prefix}` - 服务前缀
**接口列表**HTTP POST前缀 `HD_QHD_IGATE_1.0`
| 方法 | URL后缀 | 用途 |
|------|--------|------|
| internalVehicleSave | `/webresources/login/CTruck/qaSaveInternal` | 内部车辆保存 |
| internalVehicleRemove | `/webresources/login/CTruck/qaDeleteInternal` | 内部车辆删除 |
| tmpVehicleSave | `/webresources/login/CTruck/qaSaveTemp` | 临时车辆保存 |
| tmpVehicleRemove | `/webresources/login/CTruck/qaDeleTemp` | 临时车辆删除 |
| blackVehicleSave | `/webresources/login/CTruck/qaSaveBlack` | 黑名单新增 |
| blackVehicleRemove | `/webresources/login/CTruck/qaDeleteBlack` | 黑名单删除 |
**CarDockUtil 协调器**
- `execute(licenceNo)` - 临时/相关方车走 `tmpVehicleSave`
- `executeInternalVehicle(applyE)` - 内部车先 `internalVehicleRemove``internalVehicleSave`(覆盖更新)
- `executeBlackSave / executeBlackRemove` - 黑名单新增/删除
每次对接写入 `VehicleDockLogDO` 日志表level=1一级、dockFlag=2已对接、dockStatus=返回code、reasonsRefusal=返回msg
### 4. zcloud 用户中心Dubbo
通过 `@DubboReference ZcloudUserFacade`
| 调用位置 | 方法 | 用途 |
|---------|------|------|
| TwoPersonUtil | `listBase64ByUrl(zcloudUserImgQry)` | 根据人脸URL列表批量获取 base64 |
| ClosedAreaCarApplyAddExe | Dubbo 引用 | 闭区车辆申请新增时回填用户信息 |
| ClosedAreaPersonApplyAddExe | Dubbo 引用 | 闭区人员申请新增时回填用户信息 |
| ClosedAreaPersonApplyUpdateExe.authPerson | `getInfoByUserId(userId)` | 授权时获取用户身份证/姓名/人脸URL |
### 5. 待办列表 MQRocketMQ
依赖:`spring-cloud-starter-stream-rocketmq`,工具类 `TodoListEventPusherUtil`
| 触发时机 | 事件类型 | 场景 |
|---------|---------|------|
| 申请提交 | TodoListAddEvent | 车辆/闭区车辆/闭区人员申请 |
| 审批完成 | TodoListCompleteEvent | 车辆/闭区车辆/闭区人员审批 |
### 6. 大华刷卡记录推送(被动接收)
- **接收入口**`POST /primeport/mkmjSwipeCardRecord/dockSave`
- **数据结构**`SwipeRecordPushVo`(外层)→ `SwipeRecordPushInfo`info含节点编码/设备编号/报警时间/报警状态)→ `SwipeRecordPushExtend`extend含刷卡人详细信息/进出/开门结果/抓拍图/卡信息)
- **处理逻辑**Controller 将三层嵌套结构扁平化为 `MkmjSwipeCardRecordAddCmd`,调用 Service 入库到 `mkmj_swipe_card_record`
---
## 五、定时任务XXL-JOB
全部位于 `web-infrastructure/plan/` 目录,每日凌晨执行:
| 任务类 | Cron | 职责 | 调用 |
|-------|------|------|------|
| VehicleExpiredExpirationXxlJob | `0 10 0 * * ?` | 车辆过期处理(更新状态+关闭门禁权限) | 仅更新数据库,门禁移除已注释 |
| OneLevelUserXxlJon | `0 14 0 * * ?` | 一级人员门禁当日授权(相关方/临时访客) | `onePersonUtil.execute()` |
| VehicleXgfVehicleXxlJob | `0 15 0 * * ?` | 相关方/临时车辆当日可进港区判断+推送一级门禁 | `carDockUtil.execute(param)` |
| TwoLevelPersonXxlJob | `0 20 0 * * ?` | 二级门禁人员同步(含一级大华) | `twoPersonUtil.scheduledPeopleCompany()` |
| TwoLevelCarXxlJob | `0 25 0 * * ?` | 二级门禁车辆同步(含一级车行) | `twoCarUtil.scheduledCarCompany()` |
> 二级任务支持"单个异常跳过不影响其他"模式。
---
## 六、领域模型
| 实体 | 中文 | 关键字段 |
|------|------|---------|
| MkmjE | 门口信息 | mkmjName、mkmjLevel(1一级/2二级)、mkmjType(人行/车行/综合)、hgAuthArea、出入方向区域、经纬度、closedAreaId |
| MkmjPassageE | 通道 | passageName、passageType、mkmjId |
| MkmjGateE | 闸机 | gateNumber、gateName、gateType、passageId、mkmjId |
| MkmjApprovalUserE | 门禁审批人 | corpId/deptId/userId、人员/车辆/临时审核权限 |
| MkmjSwipeCardRecordE | 刷卡记录 | 节点/报警/通道/人员/卡号/进出/开门结果/抓拍图 |
| ClosedAreaE | 封闭区域 | parentId、hgAuthArea、closedAreaName、jurisdictionalCorpId、location |
| ClosedAreaPersonApplyE | 封闭区域人员申请 | personBelongType、一级/二级口门、closedAreaId、申请/审批信息、随行人员 |
| ClosedAreaCarApplyE | 封闭区域车辆申请 | carBelongType、车牌、一级/二级口门、行驶证、随行人员 |
| ClosedAreaFgsPersonAreaE | 分公司人员区域授权 | fgsPersonAuthId、closedAreaId |
| ClosedAreaFgsPersonAuthE | 分公司人员授权 | corpId、departmentId、userId、mkmjPermission |
| VehicleApplyE | 车辆申请 | 车牌/类型/所属类型(1-9)、授权范围、审核状态、访问时间、企业/部门/申请人/审批人 |
| VehicleAuditE | 车辆审批 | vehicleApplyId、batchType(普通/危化)、审批人、审批状态、变更前后 |
| VehicleBlackE | 车辆黑名单 | licenceNo、blockReason |
| VehicleViolationsE | 车辆违规 | vehicleId、violationType、location、illegalImagesId、processingStatus |
| VehicleMessageE | 车辆门禁对接信息 | vehicleApplyId、syncStatus、syncRemarks |
| VehicleDockLogE | 车辆对接日志 | level(1一级/2二级)、portArea(0全部/1东港/2西港)、dockFlag/dockStatus |
| PersonApplyE | 人员申请 | 企业/部门/人员、头像/手机/身份证 |
| XgfApplyPersonE | 相关方人员申请 | personBelongType、mkmjId、gateLevelAuthArea、审批/驳回信息 |
| PersonMessageE | 人员门禁对接信息 | personApplyId、syncStatus、syncRemarks |
| VideoE | 摄像头 | foreignId、deviceType(1口门/2闸机)、videoType(移动/平台) |
| EVehicleBasicInfoE | 一级口门车辆基础信息 | vehicleRecordId、备案字段、EDW数据溯源字段 |
| EVehicleArrivalDepartureInfoE | 一级口门车辆到离信息 | 到达/离开闸口与时间、车辆序列号 |
---
## 七、关键状态枚举
| 枚举类 | 值 | 说明 |
|-------|---|------|
| **AuditEnum** | 1审核中 / 2通过 / 3驳回 / 4无需审批(检查部门车) | 审批状态 |
| **VehicleBelongTypeEnum** | 1股份员工 / 2股份单位 / 3分公司员工 / 4分公司单位 / 5相关方 / 6临时 / 7检查部门 / 8驻港单位员工 / 9驻港单位公车 | 车辆归属类型 |
| **CloseCarBelongTypeEnum** | 1股份 / 2分公司 / 3相关方 / 4临时车 / 5分公司内部车 | 封闭区域车辆申请归属 |
| **TmpApplyEnum** | CAR_APPLY/PERSON_APPLY × 一级/二级 | 临时申请类型 |
| **FileTypeEnum** | File / url / base64 | 文件格式 |
| **SaveTypeEnum** | 保留 / 删除 / 检测通过保留 | 图片保存策略 |
| **MenuEnum** | 约60项JGD_*/FGS_*/XGS_*/ZGDW_* | 菜单与权限枚举 |
---
## 八、审批触发对接入口汇总
| 执行类 | 方法 | 触发对接 |
|-------|------|---------|
| VehicleAuditUpdateExe.audit | 车辆审批通过(auditStatus=2) | 临时/相关方车 → `carDockUtil.execute`;其他 → `carDockUtil.executeInternalVehicle` |
| VehicleBlackAddExe.execute | 车辆拉黑 | `carDockUtil.executeBlackSave` |
| VehicleBlackRemoveExe.execute | 车辆解除黑名单 | `carDockUtil.executeBlackRemove` |
| ClosedAreaCarApplyUpdateExe.auditCarApply | 闭区车辆审批通过 | `twoCarUtil.levelTwoCarCompanyByApplyId`(先二级海康,再一级车行) |
| ClosedAreaPersonApplyUpdateExe.auditPersonApply | 闭区人员审批通过 | `twoPersonUtil.levelTwoPeopleCompanyByApplyId`(二级海康+一级大华) |
| ClosedAreaPersonApplyUpdateExe.authPerson | 人员授权开启(mkmjPermission=1) | 查大华已有权限,没有则 `onePersonUtil.dockLongTermUserByUserCardAndFaceImg` |
---
## 九、配置文件结构
| 文件 | 作用 |
|------|------|
| bootstrap.yml | 主入口import prodnacos.yml + sdk.yml + swagger.yml |
| prodnacos.yml | 生产 Nacos 配置prod-nacos:8848namespace=jjb-dragonprofile=prod |
| nacos.yml | 测试 Nacos 配置192.168.20.100:30290profile=test |
| ds.yml | 多数据源配置MySQL(Druid) + PostgreSQL |
| sdk.yml | SDK网关路由、开放平台OpenAPI(SM2加密)、大华oauth配置 |
| swagger.yml | Swagger 文档开关与路径 |
**Nacos 共享配置**`config-common.yml`、`config-port.yml`、`config-mq.yml`、`config-log.yml`、`config-sdk-server.yml`、`config-actuator.yml`、`config-job.yml`、`config-mysql.yml`、`config-redis.yml`、`config-cache.yml`、`config-spring.yml`、`config-mybatis.yml`、`config-sdk.yml`、`config-flyway.yml` 以及 `${application.name}-conf.yml`
---
## 十、关键风险点
1. **硬编码凭据**OnePersonUtil/TwoPersonUtil 中大华 URL、用户名、密码、clientId/clientSecret 全部硬编码(`https://skqhdg.porthebei.com:9008/yjkmRy/`、`shuangkong`、`qinan@202604`),存在安全隐患
2. **对接开关失效**`dockFlag` 是 static 字段且默认 0`@Value` 注解被注释掉,无法通过配置切换对接开关
3. **有效期仅当日**:大华用户新增时有效期仅 `今天 00:00~23:59`,依赖每日定时任务刷新,任务中断则权限失效
4. **事务边界风险**外部调用Dubbo/HK/大华/MQ`@Transactional` 内执行,外部系统已写入的数据无法回滚,存在数据不一致风险
5. **不覆盖策略**:一级门禁对接普遍采用"先查再决定是否新增",避免覆盖已有长期权限;但二级海康按当日有效期充值
6. **电动车模块薄弱**:两个 Exe 均为纯 CRUD到离港记录未与基本信息做关联校验无状态机
7. **残留代码**ClosedAreaPersonApplyAddExe 含 main 方法Base64 测试ClosedAreaCountExe 的 corpId 过滤被注释
8. **架构不一致**MkmjApprovalUserAddExe 直接注入 Repository 而非 Gateway绕过了领域层抽象
9. **硬编码状态值**:部分 Exe 使用字面量(如 `1`、`equals(2)`)而非 AuditEnum 枚举,可读性差
10. **DaHuaSdkUtil 是占位类**login() 返回 null实际大华调用走的是 gbscommon 包中的 DaHuaDeviceCommon