CLAUDE.md 12.3 KB

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 参考工程移植时必须改的三处:

  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<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 工程红线

  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. 构建与运行

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_* 表)。