parking-cloud-yudao 迁移手册
新工程根目录。老工程
/Users/wangbiao/IdeaProjects/parking/parking-cloud与老服务 48082 保持原样,本工程的任何操作都不改它。 所有新文档都放在本目录下;设计稿见docs/superpowers/specs/2026-11-02-yudao-replatform-design.md。
0. 两个新仓库
| 仓库 | 路径 | 来源 | 版本 |
|---|---|---|---|
| 后端 | parking-cloud-yudao |
chifeng/ruoyi-vue-pro 的 master-jdk17 |
commit 0b1889a8e8,Boot 3.5.15 / JDK17 / MP 3.5.17 |
| 前端 | parking-cloud-yudao-ui |
yudao-ui-admin-vue3 最新 |
2026.10-snapshot,Vue 3.5.34 / EP 2.13.7 |
1. 当前进度
- [x] 新建后端仓库(
master-jdk17) - [x] 新建前端仓库(官方最新)
- [x] 设计稿落盘
- [x] 数据库拷贝 + 上游表准备(
parking_cloud_yudao,重名旧表改pcbak_system_*) - [x] 上游模块裁剪(只保留 system / infra / iot)
- [x] parking 业务模块搬迁(10 个模块编译通过)
- [x] 系统兼容桥(
LoginUserBridge打通 yudao 登录态 → 存量UserInfo) - [x] 前端业务页面搬迁(park/order/finance/report/ops/... 已进
src/views,构建通过) - [x] 后端单 Jar 在 48083 启动成功,登录 + 存量接口冒烟通过(2026-11-02)
- [x] 全页面运行巡检:58 个停车页 + 12 个系统页,
code != 0调用 0 个(2026-11-02) - [x] 与老环境隔离:新服务 Redis 用 db1(老服务 db0)+
scripts/warm-cache.sh缓存预热(2026-11-02) - [x] 菜单 path 归一化:子页改相对路径,侧边栏不再出现
/park/park/park-lot404(2026-11-02,见 §7.4) - [x] 业务页全局样式回流:令牌/版式类映射到芋道主题(
src/styles/parking-pages.scss)(2026-11-02,见 §7.3 #5) - [ ] 菜单/权限收口(462 个
@PreAuthorize权限串尚无菜单记录;admin 不受影响) - [ ] 调度去重(48082 与 48083 同时跑存量
@Scheduled) - [ ] 组织 id 空间打通(
system_org↔ 老sys_org) - [ ] 视频接入(WVP-PRO + ZLMediaKit)与 IoT 时序存储(TDengine)
2. 关键决策(已定)
| 编号 | 决策 |
|---|---|
| D1 | 独立新仓库,不原地改老工程 |
| D2 | 业务代码保留 com.zteits.* 包名与 parking-module-* artifactId |
| D3 | 框架层用 yudao-framework;只保留 parking-common + parking-legacy-common 作兼容层 |
| D4 | 上游 yudao-module-system 接管登录/权限/菜单/字典/日志;parking 系统模块按「丢弃 / 保留 / 桥接」三类拆分 |
| D4.1 | 只挂上游 4 个模块:system / infra / iot(im 暂不挂)。上游 pay/member/report/mall/bpm/crm/erp/ai/mp/mes/wms/hrm/fms/pms/oa 全部不挂,避免与 parking 业务路由/表冲突 |
| D5 | 新工程连 parking_cloud_yudao(parking_cloud 的拷贝);原库不动 |
| D6 | 业务菜单 id 整体 +100000 导入,避免与上游菜单 id 冲突 |
| D7 | 前端只搬业务 views / api / 自研组件,框架壳用上游 |
| D8 | 业务 Controller 继续返回 com.zteits.parking.framework.common.pojo.CommonResult;JSON 与上游一致 |
| D9 | 保留 pagehelper 6.1.1 + jsqlparser 4.9,约 700 处 PageHelper.startPage 不动 |
| D10 | 启动类扫描顺序:com.clouds.common.redis/threadpool/web/oss → cn.iocoder.yudao.server/.module → com.zteits.* |
3. 数据库方案(安全优先)
新库 = parking_cloud_yudao,是 parking_cloud 的完整拷贝。
mysqldump parking_cloud→ 还原到parking_cloud_yudao。- 在拷贝里把 12 张与上游重名的旧表改名为
pcbak_system_*(system_org是停车专有表,保留原名)。 - 从上游模板库
yudao_template导入system_* / infra_* / iot_* / QRTZ_*。 - 旧管理员密码写入新
system_usersid=1,登录密码不变。 - 旧业务菜单(id>=153 的 345 条 + parking 专有系统页)以
id+100000导入,parent_id按规则重映射。 - 旧字典(5 类 / 17 条)以新 id 导入新
system_dict_*。 - 原库
parking_cloud完全不动,老服务 48082 继续可用。
脚本:scripts/migrate-db.sh(拷贝 + 建表)、scripts/migrate-menus.sql(菜单/字典/管理员)。
4. 模块范围
4.1 保留的上游模块
yudao-dependencies
yudao-framework # 全部 starter
yudao-module-system
yudao-module-infra
yudao-module-iot # core / gateway / biz
yudao-server
4.2 搬迁的 parking 模块
parking-framework/parking-common # 67 个,兼容 POJO
parking-framework/parking-legacy-common # 567 个,存量工具
parking-module-api # 2581 个,存量契约
parking-module-park # 1150
parking-module-order # 1018
parking-module-pay # 977
parking-module-bill # 413
parking-module-charge # 67
parking-module-business # 100
parking-module-datacollect # 1(空实现)
parking-module-system-legacy # 从 parking-module-system 剥出的停车专有系统代码
4.3 系统模块三分法
- 丢弃(上游已有):
auth / user / role / menu / permission / dict / notice / operate-log / oauth2。 - 保留(停车专有):
org / refresh-cache / tb-work-order / tb-work-order-process / aliyuncs-ecs / message-center / app-news。 - 桥接(存量引用):
UserDO / MenuDO / RoleDO / OrgDO / OperateLogDO / PermissionService等。
已核对冲突:parking 有 8 条 /system/* 路由与上游重名,全部属于「丢弃」类;保留类路径与上游不冲突。
5. 兼容层清单(业务模块只引用 36 个唯一类)
| 类别 | 处理 |
|---|---|
common.*(CommonResult / PageResult / PageParam / ServiceException / 枚举 / 校验 / 工具) |
保留 parking-common 原类 |
BaseDO / BaseMapperX / LambdaQueryWrapperX / QueryWrapperX |
从 parking-spring-boot-starter-mybatis 摘出 4 个类,去掉自动配置,放进 parking-common 或 parking-framework-compat |
SecurityFrameworkUtils / LoginUser |
薄适配到上游 SecurityContext |
OperateLog / OperateTypeEnum |
先保留注解(不生效也不影响编译),后续映射到上游操作日志 |
ExcelUtils |
复制工具类 |
common.biz.system.oauth2/permission DTO |
薄适配到上游 system API |
注意:不要保留 parking 的 ParkingMybatisAutoConfiguration / ParkingSecurityAutoConfiguration / ParkingWebAutoConfiguration,它们会和上游自动配置冲突。
6. 启动类改造要点
新启动类 cn.iocoder.yudao.server.YudaoServerApplication:
@SpringBootApplication(scanBasePackages = {
"com.clouds.common.redis", "com.clouds.common.threadpool",
"com.clouds.common.web", "com.clouds.common.oss",
"${yudao.info.base-package}.server", "${yudao.info.base-package}.module",
"com.zteits.park", "com.zteits.order", "com.zteits.pay",
"com.zteits.bill", "com.zteits.charge", "com.zteits.business",
"com.zteits.sys", "com.zteits.ipay", "com.zteits.configuration",
"com.zteits.wxcp", "com.zteits.glockcollect"
})
@MapperScan(basePackages = {
"com.zteits.park.mapper", "com.zteits.order.mapper", "com.zteits.pay.mapper",
"com.zteits.bill.mapper", "com.zteits.charge.mapper",
"com.zteits.business.mapper", "com.zteits.sys.mapper"
}, nameGenerator = LegacyMapperBeanNameGenerator.class)
com.clouds.common.*必须排最前,否则存量CacheUtil静态字段被钉成 null,启动 NPE。LegacyMapperBeanNameGenerator需要从老工程搬过来。- 上游
yudao.info.base-package=cn.iocoder.yudao保持不变。 - 租户关闭:
yudao.tenant.enable: false。 - 审计字段:合并一个
MetaObjectHandler,同时填createTime/updateTime/createBy/updateBy与creator/updater。
7. 前端搬迁
从老 parking-cloud-ui 搬:
src/api/{park,order,pay,report,ops,system,index,login,ext}
src/views/{park,order,finance,report,ops,member,assess,ext}
src/components/ 下自研组件
src/locales/ 中停车文案
保留上游:layout / components 基础组件 / utils / store / router / permission / config / styles。
- 版本漂移:Vue 3.3→3.5、Element Plus 2.4→2.13、Vite 4→最新、UnoCSS 0.57→最新。
- 先搬高频 10 页跑通,再批量修剩余。
7.1 兼容层 src/utils/request.ts
老 API 文件用 axios 原生风格 request.post(url, data),URL 里已经带了 /admin-api
(ADMIN_API_PREFIX);而上游 axios 实例的 baseURL 是
VITE_BASE_URL + VITE_API_URL,同样以 /admin-api 结尾。兼容层必须
剥离 URL 里的前缀,否则实际请求会变成 /admin-api/admin-api/... → 404。
(2026-11-02 实测踩到,已在 stripApiPrefix 里修掉。)
7.2 本地联调配置(.env.local)
| 变量 | 值 | 说明 |
|---|---|---|
VITE_BASE_URL |
http://localhost:48083 |
新后端 |
VITE_APP_TENANT_ENABLE |
false |
后端 yudao.tenant.enable=false |
VITE_APP_CAPTCHA_ENABLE |
false |
后端 yudao.captcha.enable=false |
⚠️ 端口:VITE_PORT=80 在 macOS 非 root 下绑不了;联调时用
pnpm dev --port 5175(5173/5174 可能被其他工程占用)。
7.3 浏览器实测(2026-11-02)
用 playwright-cli 在 http://localhost:5175 实测:
- 登录页 →
admin/admin123→ 进入/index,无 401; /park/park-lot:车场列表返回真实数据(中国传感网国际创新园停车场等 2700 泊位);/order/parking-record、/park/caryard-overview、/park/berth均有数据、无报错。
全量页面巡检(同日):用 page.goto 逐页跑完
system_menu 里 55 个停车页面(订单 12 / 财务 11 / 运维 9 / 会员 4 / 报表 8 /
停车设施 6 / 考核·外部 2)+ 12 个系统页面,逐页收集 console.error 与
HTTP ≥400 的后端请求。结果:0 个页面报错、0 个失败请求;有数据的页面
(停车记录、车场、泊位、工单、巡检、会员卡、报表等)均渲染出真实行,
无数据的页面落「暂无数据」空态而非报错。
第二轮巡检(同日,修正版):第一轮只看 console.error 与 HTTP ≥400,漏掉了
「HTTP 200 但响应体 code=500」的静默失败。第二轮改为捕获所有
/admin-api JSON 响应的 code != 0,58 个停车页面 + 12 个系统页面全部复跑,
修完后结果 [](0 个失败调用)。详见 §8.1 的 #12–#15。
侧边栏点击巡检(同日):不止按 URL 巡检,还从侧边栏逐项点进去。范围 = 迁移的
60 个停车页面(8 个停车目录 55 个 + 挂在芋道「系统管理」下的组织管理/收费员账号/
消息中心/规则设置/运行概况 5 个),记录落地 path、是否出现「您访问的页面不存在」、
以及响应体 code != 0。结果:60/60 正确落地,0 个 404、0 个失败调用。
(脚本坑:路由切换后 el-menu 会自动收起子菜单,点击前必须重新展开;另 2 个报表项
因滚动/动画导致脚本点击超时,人工复核可直接进入,不是页面问题。)
巡检同时修掉 4 个「老页面写法 vs 上游组件契约」的前端兼容问题(改的都是共享层, 不是逐页改):
| # | 现象 | 根因 | 修法 |
|---|---|---|---|
| 1 | 请求变成 /admin-api/admin-api/... 404 |
老 API 文件 URL 自带前缀,上游 axios baseURL 也带 | request.ts 加 stripApiPrefix |
| 2 | hasPermi 指令抛 permission.hasPermission |
老页面写字符串(198 处),上游指令只收数组 | hasPermi.ts 同时接受 string / array |
| 3 | Missing required prop: "type" |
老页面用 <DictTag dict-type=...> |
迁过来的页面统一 <DictTag type="..." :value="..."> |
| 4 | value.trim is not a function |
存量接口时间字段是毫秒数字,formatDateTime 只处理字符串 |
format.ts 用 dayjs 格式化数字,0/负数/null 显示 - |
| 5 | 业务页面内容区完全没有样式(标题与数值竖排、表格无边框、无内边距) | 老工程 main.ts 全局引入了 styles/tokens.css + styles/index.scss(--text-1/--fs-12/.stat-strip/.table-card/.search-field 等 100+ 页共用的令牌与版式);新工程只搬了页面视图,两个全局文件没跟着搬 |
新增 src/styles/parking-pages.scss 并在 src/styles/index.scss 里 @use:业务令牌映射到 Element Plus 变量(--text-1: var(--el-text-color-primary)、--brand-600: var(--el-color-primary) …),页面随芋道主题(含暗色)走;.stat-strip/.table-card/.search-field 版式类原样保留,表格紧凑规则限定在 .table-card 内,不污染上游系统页。没有照搬老 tokens.css 对 --el-* 的整套覆盖(那会把整站改成政务蓝) |
| 6 | 查询区还是老政务版式:label 在控件上方、控件通栏、按钮独占底部一行、空白过大 | 老 SearchBar 按「政务表单」设计;芋道标准是 ContentWrap + inline form(label 在左、控件 240px、搜索/重置跟在条件后同一行) |
重写 components/SearchBar/index.vue 外壳:flex 自动换行替代列数栅格、搜索/重置 带图标与条件同行;styles/parking-pages.scss 里 .search-field 改 inline-flex(label 90px 右对齐)、控件统一 240px。100+ 个页面用法不变,无需逐页改 |
| 7 | 50 处 userStore.userInfo?.nickname/username/orgId 取到空值(打印/导出「操作人」为 -),pnpm ts:check 报 50 个 TS2339 |
老工程 store 暴露 userInfo;芋道 store 只有 user/getUser(),且 get-permission-info 只返回 nickname/deptId |
store/modules/user.ts 新增兼容 getter userInfo(nickname 兜底 username、透出 id/deptId);orgId 刻意不填——老 orgId 是 sys_org.id,deptId 是另一套 id,把 deptId 当 orgId 传给存量接口会静默返回空数据,正确做法是不传、由后端 LoginUserBridge 换算 |
7.4 菜单 path 归一化(侧边栏 404 的根因)
现象:点侧边栏「停车设施 → 车场列表」,地址变成 /park/park/park-lot,
页面显示「抱歉,您访问的页面不存在」。
根因:老 system_menu 的子页 path 写的是绝对路径(/park/park-lot),
而 yudao 约定是「目录绝对路径 + 子页相对路径」(上游自己就是 /system + user)。
src/utils/routerHelper.ts 的 pathResolve(parentPath, path) 会无条件拼接:
const childPath = path.startsWith('/') ? path : `/${path}`
return `${parentPath}${childPath}`.replace(/\/+/g, '/') // /park + /park/park-lot
所以只要子页 path 以 / 开头且目录已经有同名首段,就必然重复一段。
修法(两处,缺一不可):
- 数据:
system_menu里id>=100000的 60 条子页记录,path 去掉目录首段 (/park/park-lot→park-lot、/order/parking-record→parking-record、/system/org→org、/finance/bill→bill…)。 - 脚本:
scripts/migrate-menus.sql的 INSERT 加CASE归一化, 重建库时不会再写回绝对路径:
CASE
WHEN type = 2 AND path LIKE '/%' AND LOCATE('/', path, 2) > 0
THEN SUBSTRING(path, LOCATE('/', path, 2) + 1)
ELSE path
END
(type=2 才是菜单页;type=1 目录保持绝对路径,type=3 按钮无 path。)
验证:重新登录后展开「停车设施」点「车场列表」,location.pathname
= /park/park-lot,标题「芋道管理系统 - 车场列表」,0 条 console error。
8. 视频接入(项目要求)
上游 yudao-module-iot 没有 GB28181 / ONVIF / RTSP 视频接入能力;它只有设备/产品/物模型/OTA/规则/告警和 TCP/UDP/HTTP/MQTT/EMQX/CoAP/Modbus 协议。
建议方案(待选型):
- 先挂载
yudao-module-iot作为设备接入底座; - 新增
parking-module-video,对接开源视频网关:- WVP-PRO(GB28181 信令 + 设备管理)+ ZLMediaKit(流媒体);
- 或直接接 RTSP/ONVIF 的轻量网关。
- 该模块独立于 IoT,通过
parking-module-api暴露视频分页/预览/回放接口。
8.1 启动实测与踩坑记录(2026-11-02)
48083 首次完整跑通:Started YudaoServerApplication in 18.156 seconds,登录
POST /admin-api/system/auth/login(admin/admin123,tenant-id 头可带可不带)返回 code=0,
存量接口 POST /admin-api/park/area/query-area-and-block-tree 返回省市区数据。
启动路上逐个踩掉的坑(都已修在代码/配置里,不是临时命令行参数):
| # | 现象 | 根因 | 修法 |
|---|---|---|---|
| 1 | NoClassDefFoundError: WxRedisOps |
老微信 cp starter 3.3.0 拖来 weixin-java-common 3.3.0,没有该类 | parking-dependencies 钉 weixin-java-common 4.8.6-* |
| 2 | BeanDefinitionOverrideException: springContextUtil |
easy-trans TransServiceConfig 注册了同名 @Bean |
存量 SpringContextUtil 改 Bean 名 legacySpringContextUtil(全仓只用静态方法) |
| 3 | No qualifying bean RocketMQTemplate |
Boot 3 不再读老 starter 的 spring.factories |
parking-legacy-common 补 NoopRocketMQTemplate + ParkingLegacyRocketMQAutoConfiguration,走 AutoConfiguration.imports |
| 4 | MailService 缺 JavaMailSender |
没配 spring.mail |
application.yaml 新增 spring.mail.*(全部环境变量) |
| 5 | NoSuchFieldError: okhttp3...Companion |
okhttp 4.12 配 okio 1.11 | BOM 的 okio 改 3.6.0 |
| 6 | WxCpProperties.getAppConfigs() NPE |
WxCpConfiguration.initServices() 直接 .stream() |
wechat.cp.app-configs: [] |
| 7 | PlaceholderResolutionException: parking.refresh-cache.token |
存量配置类强依赖 | application.yaml 加 parking.refresh-cache.token: ${REFRESH_CACHE_TOKEN:} |
| 8 | IotStorageInitRunner 启动成功后又 System.exit(1) |
本机没有 TDengine/IoTDB | 启动类 excludeFilters 排除该 Runner(接视频/时序数据时部署 TDengine 再移除) |
| 9 | 存量接口全部 NPE(UserInfo.getOrgId() 空) |
合并工程里生效的是 yudao 安全栈,parking 的 starter 没注册(其 OAuth2TokenCommonApi 无实现),SecurityContext 里是 yudao 的 LoginUser |
LoginUserBridge 增加 yudao LoginUser 兜底转换;OAuth2TokenServiceImpl#buildUserInfo 补 username |
| 10 | 报表页报「请求参数类型错误:2026-11-01 00:00:00」 | 存量 DTO 的 java.util.Date 字段;yudao 只配了序列化(毫秒),没配反序列化格式 |
parking-legacy-common 新增 LegacyDateAutoConfiguration(Jackson Date 反序列化器 + MVC String→Date 转换器),同时接受毫秒数字、yyyy-MM-dd HH:mm:ss、ISO-8601;走 AutoConfiguration.imports 注册 |
| 11 | 日志刷 45 条 ClassCastException: EmptyMap cannot be cast to List(RedissonStreamCommands.xReadGroup) |
yudao BOM 的 Redisson 4.8.0 + redisson-spring-data-35 有 RESP2 xReadGroup 适配 bug |
yudao-dependencies 钉 Redisson 3.52.0(自带 redisson-spring-data-35);重启后 0 条 |
| 12 | 充电稽核/对账/会员卡等 5 个页面 HTTP 200 但 code=500:MybatisPlusException: Failed to process ... ParseException: Encountered unexpected token: "AND" |
上游 DataPermissionInterceptor 对每条 mapper 语句先用 JSqlParser 解析;合并工程为 PageHelper 6.1.1 固定 JSqlParser 4.9,存量 SQL(UNION ALL、动态 GROUP BY、count 包裹)大量解析失败 |
新增 LegacySafeDataPermissionInterceptor:非 cn.iocoder.yudao.* 的语句在解析前直接放行。配套 LegacyDataPermissionAutoConfiguration 重建上游 3 个 Bean,并在 application.yaml 与 application-local.yaml 两处 exclude 上游 YudaoDataPermissionAutoConfiguration(profile 的 autoconfigure.exclude 会替换而非合并基座值,只改一处会 Bean 冲突) |
| 13 | /order/charger-audit 报 only_full_group_by(charger_income_statistic_day.charger_name) |
MySQL 8 默认开启 ONLY_FULL_GROUP_BY,存量 SQL 按 5.7 写 |
JDBC URL 加 sessionVariables=sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'(仅本应用连接降级,不动 MySQL 全局,老 48082 不受影响) |
| 14 | /finance/balance 报 near 'desc ) tmp_count';另有 order_park/income_finance_bill/income_statistic_day/enclosed_parking_lot_config 的同类 SQL |
MySQL 8 已删除 GROUP BY col DESC/ASC 语法(5.7 起废弃),PageHelper 的 count 包裹后报语法错 |
逐条改为等价写法:GROUP BY check_date ORDER BY check_date DESC、GROUP BY year,num ORDER BY year DESC,num DESC、GROUP BY staticDate ORDER BY staticDate DESC、GROUP BY l.sort ORDER BY l.sort asc;子查询里无意义的 desc 直接去掉。已全仓 grep 排查,无残留 |
| 15 | /finance/reconciliation 报 业务错误:appId为空!,汇总报 near 'and a.report_date >=...' |
桥取 LoginUser.deptId(本地 admin=103,芋道演示部门树遗留)换算组织;org-id-mapping 只配了 1:10003,103 原样落到 sys_org_app_id 查不到 → appIds 为空 |
本地映射改为 103:10003,1:10003;对账入参变成 orgIds=[10003],分页与汇总均 BIZ_SUCCESS |
| 16 | 新服务把老服务的出场延迟消息抢走(潜在) | 新老服务默认都连 Redis db 0;存量 EqpOutDelayQueueSchedule 每 10s poll() 同一个 RBlockingQueue,缓存 key(zteits-park:*)也同名 |
application-local.yaml 的 spring.data.redis.database 改 1(老服务仍是 0)。登录令牌/缓存/队列全部隔离,老环境不受影响 |
| 17 | 切到 db1 后 /order/park-record/query-park-order-for-finance 500:ParkingLotDTO.getOrgId() NPE |
存量代码直接读 Redis hash(ParkingLotCacheUtil),新 db 里缓存为空 |
启动后调用 /admin-api/system/refresh-cache/{park,sys,pay}-cache?type=-1 与 refresh-bill-cache?type=1;已封装成 scripts/warm-cache.sh,首次启动或清 Redis 后执行 |
已知未决(不阻塞当前联调):
- 两套调度同时在跑:老 48082 与新 48083 都会执行存量
@Scheduled/Quartz, 联调期先只开一个;正式切换前把存量定时任务收口到新工程的 Quartz。 - 组织 id 空间:
LoginUserBridge用parking.legacy.org-id-mapping(本地103:10003,1:10003)换算; 长期方案是把system_users.dept_id对齐到真实组织、老sys_org的 id 原样迁进system_org,然后删掉映射。 - 菜单/权限收口:672 个
@PreAuthorize权限点中约 462 个在system_menu无记录, 目前 admin 可通,非超管角色需补菜单(含导出权限)后才可用。
9. 常用命令
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
# 后端构建(裁模块后)
mvn -T 1C -DskipTests clean package
mvn -pl yudao-server -am -DskipTests package
# 启动(新服务 48083,避免与老服务 48082 冲突;配置全在 application-local.yaml)
java -jar yudao-server/target/yudao-server.jar --spring.profiles.active=local
# 预热存量缓存(首次启动 / 清过 Redis 后必须执行,否则依赖 Redis 的存量接口会 NPE)
# 注意:同一账号再登录会把旧 token 踢下线;脚本内部会登录,跑完浏览器需重新登录
./scripts/warm-cache.sh
# 冒烟
curl -s -X POST http://127.0.0.1:48083/admin-api/system/auth/login \
-H 'Content-Type: application/json' -d '{"username":"admin","password":"admin123"}'
# 前端(VITE_PORT=80 在 macOS 非 root 下绑不了,用 5175)
cd ../parking-cloud-yudao-ui && pnpm install && pnpm dev --port 5175
10. 续跑口令
在新目录开 Claude 窗口后,直接说:
读
MIGRATION.md和docs/superpowers/specs/2026-11-02-yudao-replatform-design.md,从「当前进度」的下一项继续迁移。先确认mvn -pl yudao-server -am -DskipTests package能过,再按模块推进。数据库用parking_cloud_yudao,不要动parking_cloud。
11. 已知坑
- 上游
pay/member/report/...不能挂,路由和表都会撞。 - 两个
MetaObjectHandler不能同时注册。 com.clouds.common.*扫描顺序错 → 启动 NPE。- 上游全局
logic-delete只配了值、没配字段,parking 无deleted的 DO 安全。 - 菜单 id 必须错开,否则和上游 seed 冲突。
- 老库
system_org与上游system_dept是两套,先保留 org,后续再谈合并。 pnpm ts:check还有 28 个存量页面的类型错误(18 个 EPel-tag/el-select可空 prop、2 个orgName、8 个未使用变量), 不影响pnpm dev与vite build(构建不跑 vue-tsc);新代码提交前请保证自己改的文件不新增错误。