package cn.iocoder.yudao.server; import cn.iocoder.yudao.module.iot.framework.storage.config.IotStorageInitRunner; import com.clouds.common.mybatis.LegacyMapperBeanNameGenerator; import com.zteits.charge.config.ChargeConfig; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; /** * parking-cloud-yudao 启动类(yudao 上游骨架 + parking 存量业务) * *

扫描顺序是启动能否成功的硬约束

* 4 个 {@code com.clouds.common.*} 必须排在所有 {@code com.zteits.*}(包括 yudao 的 * {@code ${yudao.info.base-package}.server/.module})之前。老框架里有 41 个 CacheUtil * 把 {@code RedisCacheUtil} 缓存进静态字段,而该字段在自己的 setApplicationContext 里才赋值; * 一旦有 com.zteits.* Bean 先创建并类加载了这些 CacheUtil,静态字段就被永久钉成 null, * 之后每次调用 NPE。详见老工程 ParkingServerApplication 的类注释与 2026-10-15 实测记录。 * *

为什么不用 @SpringBootApplication

* 需要 {@code excludeFilters} 精确排除封闭 jar 里的 {@code ChargeConfig}(它自己 * {@code @ComponentScan("com.zteits.charge")} 与我们的扫描重复,会启动冲突)。 * {@code @SpringBootApplication} 自带的扫描注解不接受过滤器,故展开为语义等价的三个注解。 * *

MapperScan

* 存量 7 个包 + parking 新风格 Mapper;必须用 {@link LegacyMapperBeanNameGenerator} * (全限定类名做 Bean 名),否则双包同名 Mapper 会在扫描阶段抛 * {@code ConflictingBeanDefinitionException}。绝不能扫 {@code com.zteits.*.util} * (里面有 tk.mybatis 的 MyMapper 标记接口)。 */ @SpringBootConfiguration @EnableAutoConfiguration @EnableAsync @EnableScheduling @ComponentScan( basePackages = { // ---- 存量基础设施(必须最前)---- "com.clouds.common.redis", "com.clouds.common.threadpool", "com.clouds.common.web", "com.clouds.common.oss", // ---- 上游 yudao ---- "${yudao.info.base-package}.server", "${yudao.info.base-package}.module", // ---- parking 存量业务 ---- "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", "com.zteits.clouds", // ---- parking 里残留的 yudao 风格代码(org / user 桥接)---- "com.zteits.parking.module" }, excludeFilters = { // 存量充电 starter 里的重复扫描配置 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ChargeConfig.class), // 本地没有 TDengine/IoTDB:该 Runner 校验失败会 System.exit(1)。 // 本机联调不需要 IoT 时序存储;接视频/设备数据时再部署 TDengine 并移除本排除。 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = IotStorageInitRunner.class) }) @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", "com.zteits.parking.module.system.dal.mysql" }, nameGenerator = LegacyMapperBeanNameGenerator.class) public class YudaoServerApplication { public static void main(String[] args) { SpringApplication.run(YudaoServerApplication.class, args); } }