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);
}
}