# parking-cloud — 项目全局约束文档 > 本工程是停车平台(老工程 `../zteits-clouds`,Dubbo 微服务)重构后的**模块化单体**: > **去 Dubbo / 去 Zookeeper,前后端分离,发布物只有一个 Jar**。 > 架构与代码风格对齐参考工程 `/Users/wangbiao/IdeaProjects/JCSS/qy/urbanops`(yudao / ruoyi-vue-pro 血统)。 > 所有后续开发需求严格按照本文档既定规范执行。 --- ## 1. 项目简介 - **项目名称**:parking-cloud - **groupId**:`com.zteits.boot` - **基础包名**:`com.zteits.parking` - **Java**:1.8(**必须用 JDK 8 编译**,不是高版本 JDK 加 `source/target=1.8`) - **Spring Boot**:2.7.18(**javax 命名空间**,不是 jakarta) - **ORM**:MyBatis-Plus 3.5.7 + 存量手写 mapper XML(混合数据访问) - **数据库**:MySQL 8 单库 `parking_cloud`(停车六库已合并),政务库 P6 以多数据源接入 - **缓存**:Redis - **接口契约**:`/admin-api`(管理后台)/ `/app-api`(移动端),`Authorization: Bearer`,`CommonResult{code=0}` 表示成功 ### 与老工程的关系 | 老 | 新 | |---|---| | `zteits-api` 416 个 Dubbo 接口 | `parking-module-api`(`BizResult` 兼容层,接口随模块搬迁) | | 10 个 Dubbo provider + 9 个 portal | 各 `parking-module-*` 的 `controller/admin` + `service` | | `` 跨模块调用 | 进程内 Spring `@Resource` 注入 | | 每个服务一个 Jar | `parking-server` 一个 Jar | | `x-auth-token` + `BizResult(8888)` | `Authorization: Bearer` + `CommonResult(0)` | --- ## 2. 模块结构 ``` parking-cloud/ ├── parking-dependencies/ # BOM:全项目唯一的版本目录 ├── parking-framework/ # 框架层(Spring Boot Starter 集合,不含业务) │ ├── parking-common │ ├── parking-spring-boot-starter-web │ ├── parking-spring-boot-starter-security │ ├── parking-spring-boot-starter-mybatis │ ├── parking-spring-boot-starter-redis │ ├── parking-spring-boot-starter-job │ ├── parking-spring-boot-starter-mq │ └── parking-spring-boot-starter-excel ├── parking-module-api/ # 跨模块契约(接口 + DTO,无实现) ├── parking-module-*/ # 业务模块(system / infra / park / order / pay / bill / charge / gov-*) ├── parking-server/ # ★ 唯一启动模块,产出唯一 Jar └── sql/ # 建库脚本 + 合并迁移脚本 ``` **模块内部包结构**(严格固定,Controller 的 URL 前缀由包名自动映射): ``` parking-module-xxx/src/main/java/com/zteits/parking/module/xxx/ ├── controller/admin// # REST,自动挂 /admin-api ├── controller/admin//vo/ # ReqVO / RespVO ├── controller/app// # 移动端,自动挂 /app-api ├── service//XxxService + XxxServiceImpl ├── dal/dataobject//XxxDO # 新表(MyBatis-Plus) ├── dal/mysql//XxxMapper # 新表(extends BaseMapperX) ├── dal/redis/ ├── domain/ + resources/mybatis/ # ← 存量实体与 XML 原样落位(包名不许改,见 §4.3) ├── api/ # 本模块对外接口的实现,类名 XxxApiImpl └── enums/ job/ mq/ util/ config/ ``` --- ## 3. 编码规范 ### 命名 | 元素 | 模式 | 示例 | |---|---|---| | 领域对象(新表) | `{Entity}DO` | `ParkLotDO` | | 领域对象(存量表) | `{Entity}`(原样) | `SysOrg` | | Mapper(新) | `{Entity}Mapper` extends `BaseMapperX` | `ParkLotMapper` | | DAO(存量) | `{Entity}Dao` / `{Entity}Mapper`(原样) | `TdClRuleMapper` | | Service | `XxxService` / `XxxServiceImpl` | `ParkLotServiceImpl` | | 请求 VO | `{Entity}PageReqVO` / `{Entity}SaveReqVO` | `ParkLotPageReqVO` | | 响应 VO | `{Entity}RespVO` | `ParkLotRespVO` | | 跨模块接口实现 | `XxxApiImpl` | `DictDataApiImpl` | ### 注解 ```java // 新代码(yudao 风格) @Tag(name = "管理后台 - 停车场") @RestController @RequestMapping("/park/park-lot") @Validated public class ParkLotController { @Resource // 模块内/跨模块一律 @Resource(对齐参考工程) private ParkLotService parkLotService; @PostMapping("/create") @Operation(summary = "创建停车场") @PreAuthorize("@ss.hasPermission('park:park-lot:create')") public CommonResult createParkLot(@Valid @RequestBody ParkLotSaveReqVO reqVO) { return success(parkLotService.createParkLot(reqVO)); } } // 存量搬迁代码 @Service("com.zteits.sys.biz.SysOrgServiceImpl") // ← 老写法:Dubbo 全限定名 public class SysOrgServiceImpl implements SysOrgService { ... } ``` ### ⚠️ Lombok 链式 setter 根目录 `lombok.config` 里 `lombok.accessors.chain=true` 是**必需的**: 框架层与存量搬迁代码大量使用 `new LoginUser().setId(x).setUserType(y)` 这类链式写法, 删掉该配置会出现一批「无法取消引用 void」的编译错误。**不要动这个文件。** ### 返回值 - **框架与新代码**:统一 `CommonResult`(`code=0` 成功),业务错误抛 `ServiceException` - **存量搬迁的 Service**:签名仍返回 `BizResult`(`parking-module-api` 的兼容层), **不改方法签名**(改了等于要求 2000+ 个实现类跟着改); **由 Controller 层负责 `BizResult` → `CommonResult` 适配** --- ## 4. 关键约束 ### 4.1 版本红线(Java 8 硬上限,踩中直接编译/启动失败) | 依赖 | 必须 | 原因 | |---|---|---| | MyBatis-Plus | **3.5.7** | 3.5.8 起依赖 jsqlparser 5.x(Java 11 编译)。**不要引入 `mybatis-plus-jsqlparser`** | | mybatis-plus-join | **P0 不引入** | 1.5.x 要求 MP ≥ 3.5.9;故 `BaseMapperX extends BaseMapper`(去掉 `selectJoinPage*`) | | jsqlparser | **4.9** | MP 3.5.7 与 pagehelper 6.1.1 的共用版本 | | pagehelper | **6.1.1**(starter 仍 1.4.7) | 5.3.3 编译期依赖 jsqlparser 4.5 的 `SelectBody`(4.7 已删除)→ 启动时 `NoClassDefFoundError` | | Druid | `druid-spring-boot-starter` **1.2.23** | 不是 `-spring-boot-3-starter` | | springdoc | `springdoc-openapi-ui` **1.6.15** | 包路径 `org.springdoc.core.*`(**不是** `.core.models`) | | Knife4j | `knife4j-openapi3-spring-boot-starter` **4.5.0** | **不要** `-jakarta-` 变体,**不要** 3.0.3(springfox 线) | | dynamic-datasource | **3.6.1,P6 才引** | 4.x 仅支持 Boot 3 | | validation | `jakarta.validation:jakarta.validation-api:2.0.2` | Boot 2.7 线里它提供的正是 `javax.validation.*` 包;**不要**同时再声明 `javax.validation:validation-api` | | Redisson | 3.45.1(P1 起) | 必须排除 `redisson-spring-data-34`,换 `redisson-spring-data-27` | **Java 8 禁用 API**(从高版本参考工程抄代码时最常见): `String.isBlank()` → `StrUtil.isBlank()`;`List.of()`;`URLEncoder.encode(String, Charset)` → `URLUtil.encode()`; `instanceof` 模式匹配;`cn.hutool.extra.servlet.JakartaServletUtil` → `ServletUtil`。 **从 Boot 3 参考工程移植时必须改的三处**: 1. Spring Security 5.7:`authorizeRequests()` + `antMatchers()`、`csrf().disable()`、`@EnableGlobalMethodSecurity(prePostEnabled = true)` 2. URL 前缀:`WebMvcConfigurer#configurePathMatch` + `addPathPrefix(prefix, predicate)` 3. `jakarta.*` → `javax.*` ### 4.2 跨模块调用 - 只允许调用 `parking-module-api` 里的接口;**禁止**跨模块注入别人的 `ServiceImpl`,**禁止**跨模块直接查对方的表 - 跨模块接口的实现在各自模块的 `api/` 包下,类名 `XxxApiImpl`,用 `@Service` 暴露 ### 4.3 数据访问(混合策略) | 场景 | 做法 | |---|---| | 存量 SQL(1311 个 mapper XML) | **原样搬进** `resources/mybatis/{mapper,smapper}/`,**只改 namespace 包名,不改列名** | | 存量实体 | 纯 POJO 原样保留,靠 XML 的 `resultMap` 映射;**不要**套 `BaseDO` / 加 `@TableField` | | 新表 / 新页面 | `XxxDO extends BaseDO` + `XxxMapper extends BaseMapperX` + `LambdaQueryWrapperX`,不写 XML | | 分页 | 新代码用 `BaseMapperX.selectPage(...) → PageResult`;**新代码禁止再用 PageHelper**(存量可用) | | `@MapperScan` | **不要**扫 `com.zteits.*.util` 包(里面有 tk.mybatis 的 `MyMapper` 标记接口,老代码注释里就写了「该接口不能被扫描到,否则会出错」)。只列具体 `*.mapper` 包 | > ⚠️ **逻辑删除**:存量库**没有** `del_flag` 列(1311 个 XML 中出现 0 次,实测), > 软删除语义由 `data_state` 表达。因此**禁止**配置全局 `logic-delete-field`,`BaseDO` 也不带 `@TableLogic`。 > 详见 `sql/README.md`。 ### 4.4 认证与返回码 - 未登录:HTTP 200 + 响应体 `code=401`;无权限:HTTP 200 + 响应体 `code=403` (与参考工程一致——前后端约定以**响应体的 code** 为准,前端 `request.ts` 按 code 分支处理) - `permitAll` 的 URL 走 `parking.security.permit-all-urls` 配置,或方法/类上加 `@PermitAll` ### 4.5 工程红线 1. ❌ 修改 `parking-dependencies/pom.xml` 之外的依赖版本号(版本只在这一处管) 2. ❌ 修改 `BizResult` / `ResultBean` / `ErrorType` 的结构与返回格式 3. ❌ 把 `.yaml` 配置改回 `.properties`(本项目用 yaml;老工程用 properties,互不影响) 4. ❌ 在循环里查数据库(必须批量) 5. ❌ 提交真实密钥/密码/Token(一律走环境变量,见 `application-local.yaml` 的写法) 6. ❌ 擅自重构存量业务逻辑(搬迁以「只改 import / 注解 / 包名」为限) 7. ❌ 删除或修改 `lombok.config`(见 §3) 8. ❌ 在 `parking-server` 里写业务代码(它只是空壳启动器) 9. ❌ 让 `ParkingServerApplication` 扫描 `com.zteits.parking.framework` 包 (会把各 starter 的 `@AutoConfiguration` 类二次注册,导致重复 Bean) 10. ❌ 修改 `application-*.yaml` 里的外部连接信息(数据库/Redis/MQ 地址一律走环境变量) --- ## 5. 构建与运行 ```bash export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home export PATH=$JAVA_HOME/bin:$PATH # 整仓构建 mvn -T 1C clean install -DskipTests # 只打服务 Jar(唯一发布物) mvn -pl parking-server -am -DskipTests clean package ls -lh parking-server/target/parking-server-*.jar # 本地启动 java -jar parking-server/target/parking-server-*.jar --spring.profiles.active=local # 冒烟 curl -s http://127.0.0.1:48080/actuator/health # {"status":"UP"} open http://127.0.0.1:48080/doc.html # Knife4j 接口文档 # 依赖体检(两项硬约束) mvn -pl parking-server dependency:tree | grep -i dubbo # 期望:无输出 mvn -pl parking-server dependency:tree | grep -iE 'jakarta|jsqlparser' # 期望:jsqlparser 4.9;无 jakarta 运行时包 ``` - 端口:`48080` - 日志:`./logs/parking-server/parking-server.log` - 启动必须先有 MySQL(`parking_cloud`)与 Redis --- ## 6. 分阶段 | 阶段 | 内容 | 状态 | |---|---|---| | **P0** | 骨架:Maven 多模块 + BOM + 7 个 starter + 单 Jar 跑通;前端 `parking-cloud-ui` 骨架 | ✅ 已完成 | | **P1** | `parking-module-system`;真认证/权限/菜单;**删除 P0 占位实现**;前端系统管理 6 页 | ✅ 已完成 | | P2 | `park + order + pay + bill` 一次性并入(解 Dubbo 循环);六库合一灌表 | ← 下一步 | | P3 | `parking-module-charge` + 充电页面 | | | P4 | 停车前端逐页迁移 | | | P5 | PDA/小程序迁 `/app-api`;xxl-job → Quartz | | | P6 | 政务平台并入(多数据源 + 109 页前端) | | | P7 | 可视化大屏独立工程 `parking-cloud-screen` | | | P8 | 老工程全面下线 | | > ⚠️ **P1 相对原计划的两处刻意偏离**,不是遗漏: > 1. **未建 `parking-module-infra`**——文件/字典/操作日志暂由 `parking-module-system` 承担, > 等真有文件上传与定时任务需求时再拆(拆的时候字典/日志要一起搬) > 2. **图形验证码未启用**——错误码与枚举已预留,`captcha-info` 返回 `captchaEnable=false`; > 后端置 true 时前端无需改动 > > P0 的内存占位实现(`controller/admin/p0/`、`p0/P0StubConfiguration.java`、`parking.p0.enabled`) > **已于 P1 全部删除并换成真实实现**。当前认证 = Spring Security + Redis Token(`system_oauth2_*` 表)。