YudaoServerApplication.java 4.23 KB
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 存量业务)
 *
 * <h2>扫描顺序是启动能否成功的硬约束</h2>
 * 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 实测记录。
 *
 * <h2>为什么不用 @SpringBootApplication</h2>
 * 需要 {@code excludeFilters} 精确排除封闭 jar 里的 {@code ChargeConfig}(它自己
 * {@code @ComponentScan("com.zteits.charge")} 与我们的扫描重复,会启动冲突)。
 * {@code @SpringBootApplication} 自带的扫描注解不接受过滤器,故展开为语义等价的三个注解。
 *
 * <h2>MapperScan</h2>
 * 存量 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);
    }

}