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 |
<dubbo:reference> 跨模块调用 |
进程内 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/<entity>/ # REST,自动挂 /admin-api
├── controller/admin/<entity>/vo/ # ReqVO / RespVO
├── controller/app/<entity>/ # 移动端,自动挂 /app-api
├── service/<entity>/XxxService + XxxServiceImpl
├── dal/dataobject/<entity>/XxxDO # 新表(MyBatis-Plus)
├── dal/mysql/<entity>/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 |
注解
// 新代码(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<Long> 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<T>(code=0成功),业务错误抛ServiceException - 存量搬迁的 Service:签名仍返回
BizResult<T>(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<T>(去掉 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 参考工程移植时必须改的三处:
- Spring Security 5.7:
authorizeRequests()+antMatchers()、csrf().disable()、@EnableGlobalMethodSecurity(prePostEnabled = true) - URL 前缀:
WebMvcConfigurer#configurePathMatch+addPathPrefix(prefix, predicate) 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<T> 标记接口,老代码注释里就写了「该接口不能被扫描到,否则会出错」)。只列具体 *.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 工程红线
- ❌ 修改
parking-dependencies/pom.xml之外的依赖版本号(版本只在这一处管) - ❌ 修改
BizResult/ResultBean/ErrorType的结构与返回格式 - ❌ 把
.yaml配置改回.properties(本项目用 yaml;老工程用 properties,互不影响) - ❌ 在循环里查数据库(必须批量)
- ❌ 提交真实密钥/密码/Token(一律走环境变量,见
application-local.yaml的写法) - ❌ 擅自重构存量业务逻辑(搬迁以「只改 import / 注解 / 包名」为限)
- ❌ 删除或修改
lombok.config(见 §3) - ❌ 在
parking-server里写业务代码(它只是空壳启动器) - ❌ 让
ParkingServerApplication扫描com.zteits.parking.framework包 (会把各 starter 的@AutoConfiguration类二次注册,导致重复 Bean) - ❌ 修改
application-*.yaml里的外部连接信息(数据库/Redis/MQ 地址一律走环境变量)
5. 构建与运行
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 相对原计划的两处刻意偏离,不是遗漏:
- 未建
parking-module-infra——文件/字典/操作日志暂由parking-module-system承担, 等真有文件上传与定时任务需求时再拆(拆的时候字典/日志要一起搬)- 图形验证码未启用——错误码与枚举已预留,
captcha-info返回captchaEnable=false; 后端置 true 时前端无需改动P0 的内存占位实现(
controller/admin/p0/、p0/P0StubConfiguration.java、parking.p0.enabled) 已于 P1 全部删除并换成真实实现。当前认证 = Spring Security + Redis Token(system_oauth2_*表)。