pom.xml
@@ -267,6 +267,7 @@ <module>xp-robotaxi-vehicle-convoy-boot</module> <module>xp-robotaxi-job-boot</module> <module>xp-robotaxi-file-boot</module> <module>xp-robotaxi-traffic-control-boot</module> </modules> <packaging>pom</packaging> xp-robotaxi-map-boot/src/main/resources/bootstrap.yml
@@ -12,4 +12,150 @@ allow-circular-references: true profiles: # 环境配置 active: local active: dev # active: prod --- spring: config: activate: on-profile: dev cloud: nacos: discovery: # 服务注册地址 server-addr: 192.168.110.80:8848 service: ${spring.application.name} group: DEFAULT_GROUP namespace: 5185ac4b-6369-48eb-a21b-a7cbc441c5f1 username: nacos password: nacos ip: 192.168.110.85 config: # 配置中心地址 server-addr: 192.168.110.80:8848 namespace: 5185ac4b-6369-48eb-a21b-a7cbc441c5f1 group: DEFAULT_GROUP name: ${spring.application.name} username: nacos password: nacos # 配置文件格式 file-extension: yml # 共享配置 shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} --- seata: enabled: true application-id: ${spring.application.name} tx-service-group: seata_tx_group #此处配置自定义的seata事务分组名称 enable-auto-data-source-proxy: false #关闭数据库代理 service: vgroup-mapping: seata_tx_group: default data-source-proxy-mode: AT config: type: nacos nacos: # 开发环境 server-addr: 192.168.110.80:8848 namespace: 5185ac4b-6369-48eb-a21b-a7cbc441c5f1 group: DEFAULT_GROUP data-id: seata-server.properties username: nacos password: nacos # 生产环境 # server-addr: 192.168.0.137:8848,192.168.0.123:8848 # namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a # group: DEFAULT_GROUP # data-id: seata-server.properties # username: nacos # password: nacos registry: type: nacos nacos: application: seata-server # 开发环境 server-addr: 192.168.110.80:8848 namespace: 5185ac4b-6369-48eb-a21b-a7cbc441c5f1 group: DEFAULT_GROUP username: nacos password: nacos # 生产环境 # server-addr: 192.168.0.137:8848,192.168.0.123:8848 # namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a # group: DEFAULT_GROUP # username: nacos # password: nacos cloud: nacos: discovery: # 开发环境 server-addr: 192.168.110.80:8848 namespace: 5185ac4b-6369-48eb-a21b-a7cbc441c5f1 group: DEFAULT_GROUP application: seata-server #Nacos 中 Seata 名称 username: nacos password: nacos # 生产环境 # server-addr: 192.168.0.137:8848,192.168.0.123:8848 # namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a # group: DEFAULT_GROUP # application: seata-server #Nacos 中 Seata 名称 # username: nacos # password: nacos sentinel: transport: dashboard: 192.168.0.137:8080 # Sentinel控制台地址 eager: true --- spring: config: activate: on-profile: prod cloud: nacos: discovery: # 服务注册地址 server-addr: 192.168.0.137:8848,192.168.0.123:8848 service: ${spring.application.name} group: DEFAULT_GROUP namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a username: nacos password: nacos config: # 配置中心地址 server-addr: 192.168.0.137:8848,192.168.0.123:8848 namespace: c6cf40b5-44e8-43f9-be61-8d10fa830e2a username: nacos password: nacos group: DEFAULT_GROUP name: ${spring.application.name} # 配置文件格式 file-extension: yml # 共享配置 shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} --- spring: config: activate: on-profile: test cloud: nacos: discovery: # 服务注册地址 server-addr: 127.0.0.1:8848 service: ${spring.application.name} group: DEFAULT_GROUP namespace: 96712c7a-480b-4f40-b783-39f00f3b33ce config: # 配置中心地址 server-addr: 127.0.0.1:8848 namespace: 96712c7a-480b-4f40-b783-39f00f3b33ce group: DEFAULT_GROUP name: ${spring.application.name} # 配置文件格式 file-extension: yml # 共享配置 shared-configs: - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
@@ -457,4 +457,8 @@ } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -137,7 +137,6 @@ * @param firstElement 队列第一个元素 */ private void orderProcess(String quoteName, String firstElement) { System.out.println("进入处理环节"); XpTradingStrategy xpTradingStrategy = xpTradingStrategyService.getOne(null); String orderId = firstElement.split("_")[0]; String lonLat = firstElement.split("_")[1]; xp-robotaxi-order-boot/src/main/resources/bootstrap.yml
@@ -29,6 +29,7 @@ namespace: 5185ac4b-6369-48eb-a21b-a7cbc441c5f1 username: nacos password: nacos ip: 192.168.110.85 config: # 配置中心地址 server-addr: 192.168.110.80:8848 xp-robotaxi-traffic-control-boot/Dockerfile
New file @@ -0,0 +1,16 @@ FROM registry.logan.xiaopeng.local/logancloud/logan-base:1.1.4-rc1-J1.8.0_372 LABEL maintainer="Linzhaoming <linzm2@xiaopeng.com>" ENV JAVA_OPTS="-Xmx3072m -Xms1024m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 \ -XX:+UseCMSInitiatingOccupancyOnly -XX:ReservedCodeCacheSize=128M -XX:ParallelGCThreads=2 \ -XX:+ExplicitGCInvokesConcurrent -Duser.timezone=Asia/Shanghai" ENTRYPOINT [ "sh", "-c", "java $APP_OPTS $JMX_OPTS $JAVA_OPTS $GCLOG_OPTS -Djava.security.egd=file:/dev/./urandom -jar xp-robotaxi-vehicle-convoy-boot.jar" ] WORKDIR /home/xpmotors/local RUN mkdir -p /data/log/app/xp-robotaxi-vehicle-convoy-boot /data/tomcat && chown -R xpmotors:xpmotors /data/log/app/xp-robotaxi-vehicle-convoy-boot /data/tomcat COPY target/xp-robotaxi-vehicle-convoy-boot-*-exec.jar /home/xpmotors/local/xp-robotaxi-vehicle-convoy-boot.jar USER xpmotors xp-robotaxi-traffic-control-boot/pom.xml
New file @@ -0,0 +1,214 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.xiaopeng</groupId> <artifactId>robotaxi</artifactId> <version>0.1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>xp-robotaxi-traffic-control-boot</artifactId> <description> xp-modules-trafficControl交通监管模块 </description> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-loadbalancer</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.fox.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.xiaopeng</groupId> <artifactId>xp-common-log</artifactId> </dependency> <dependency> <groupId>com.xiaopeng</groupId> <artifactId>xp-common-swagger</artifactId> </dependency> <dependency> <groupId>com.xiaopeng</groupId> <artifactId>xp-robotaxi-api</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency> <dependency> <groupId>com.xiaopeng</groupId> <artifactId>unified-verification</artifactId> <version>1.5.2-v2</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.2.1</version> <exclusions> <exclusion> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> </exclusion> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-transaction-base-seata-at</artifactId> <version>5.2.1</version> </dependency> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.33</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.25.3</version> </dependency> <!-- easypoi --> <!-- Apache POI 核心依赖 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <!-- Apache POI OOXML 支持(用于 .xlsx 文件) --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <!-- Apache POI XMLBeans(可选,用于复杂 XML 处理) --> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>5.1.1</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> <version>4.4.0</version> <exclusions> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-web</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-annotation</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>com.xiaopeng.logan</groupId> <artifactId>xp-evi-oss-sdk</artifactId> <version>1.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> </resource> <resource> <directory>lib</directory> <targetPath>/BOOT-INF/lib/</targetPath> <includes> <include>**/*.jar</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project> xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/DecodingException.java
New file @@ -0,0 +1,17 @@ package com.xiaopeng.trafficControl; /** * 解码处理异常类</br> */ public class DecodingException extends RuntimeException { private static final long serialVersionUID = 1L; public DecodingException(String message) { super(message); } public DecodingException(Throwable cause) { super(cause); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/TrafficControlApplication.java
New file @@ -0,0 +1,29 @@ package com.xiaopeng.trafficControl; import com.xiaopeng.common.security.annotation.EnableCustomConfig; import com.xiaopeng.common.security.annotation.EnableRyFeignClients; import com.xiaopeng.common.swagger.annotation.EnableCustomSwagger2; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * 交通监管模块 */ @EnableCustomConfig @MapperScan({"com.xiaopeng.trafficControl.mapper"}) @EnableCustomSwagger2 @EnableRyFeignClients @SpringBootApplication @EnableScheduling//开启定时任务 @ServletComponentScan @EnableTransactionManagement//开启事务 public class TrafficControlApplication { public static void main(String[] args) { SpringApplication.run(TrafficControlApplication.class, args); System.out.println("(♥◠‿◠)ノ゙ 交通监管模块启动成功 ლ(´ڡ`ლ)゙ \n"); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/DataUpdateHandlerConfig.java
New file @@ -0,0 +1,71 @@ package com.xiaopeng.trafficControl.config; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.security.utils.SecurityUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.context.annotation.Configuration; import java.time.LocalDateTime; /** * @author xiaochen * @ClassName DataUpdateInterceptor * @Description 数据更新操作处理 * @date 2021-12-15 * <p> * 注意,之前在此处注入了 JwtTokenUtils * <p> * 造成spring循环依赖,项目支棱不起来 */ @Slf4j @Configuration public class DataUpdateHandlerConfig implements MetaObjectHandler { /** * 新增数据执行 * * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { String userName = ""; try{ // 获取登录信息 userName = SecurityUtils.getLoginUser().getSysUser().getNickName(); log.info("DataUpdateInterceptor insertFill"); } catch (Exception e) { log.error("DataUpdateInterceptor insertFill error:{}", e.getMessage()); } if (StringUtils.isNotBlank(userName)) { this.setFieldValByName("createBy", userName, metaObject); this.setFieldValByName("updateBy", userName, metaObject); } else { this.setFieldValByName("createBy", userName, metaObject); this.setFieldValByName("updateBy", userName, metaObject); } this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); } /** * 修改数据执行 * * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { String userName = ""; try{ // 获取登录信息 userName = SecurityUtils.getLoginUser().getSysUser().getNickName(); log.info("DataUpdateInterceptor insertFill"); } catch (Exception e) { log.error("DataUpdateInterceptor insertFill error:{}", e.getMessage()); } this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); this.setFieldValByName("updateBy", userName, metaObject); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MessageLocalConfig.java
New file @@ -0,0 +1,59 @@ package com.xiaopeng.trafficControl.config; import org.springframework.boot.autoconfigure.context.MessageSourceProperties; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.util.StringUtils; import org.springframework.web.servlet.LocaleResolver; import java.nio.charset.StandardCharsets; import java.time.Duration; /** * 国际化配置 */ @Configuration public class MessageLocalConfig { @Bean public MessageSourceProperties messageSourceProperties() { MessageSourceProperties properties = new MessageSourceProperties(); properties.setBasename("i18n/messages"); properties.setEncoding(StandardCharsets.UTF_8); properties.setFallbackToSystemLocale(false); properties.setCacheDuration(Duration.ofSeconds(-1)); properties.setAlwaysUseMessageFormat(false); properties.setUseCodeAsDefaultMessage(true); return properties; } @Bean public MessageSource messageSource(MessageSourceProperties properties) { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); if (StringUtils.hasText(properties.getBasename())) { messageSource.setBasenames(StringUtils .commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename()))); } if (properties.getEncoding() != null) { messageSource.setDefaultEncoding(properties.getEncoding().name()); } messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale()); Duration cacheDuration = properties.getCacheDuration(); if (cacheDuration != null) { messageSource.setCacheMillis(cacheDuration.toMillis()); } messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat()); messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage()); return messageSource; } @Bean//注册国际化组件 public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MultipartFilterConfig.java
New file @@ -0,0 +1,64 @@ package com.xiaopeng.trafficControl.config; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; /** * 修复 multipart 文件上传被 ReplaceRequestFilter 破坏的问题。 * <p> * ReplaceRequestFilter(来自 request-wrapper jar,logan-starter-core 传递依赖) * 会读取 request 的 InputStream 来包装请求体,导致后续 Spring 解析 multipart 时 * 抛出 "Stream closed" 异常。 * <p> * 解决方案:注册一个高优先级 Filter,对 multipart 请求提前触发 Servlet 容器的 * multipart 解析(通过调用 request.getParts()),使文件数据被缓存到临时文件中。 * 这样即使后续 ReplaceRequestFilter 消费了 InputStream,multipart 数据仍然可用。 */ @Slf4j @Configuration public class MultipartFilterConfig { private static final String MULTIPART = "multipart/"; @Bean public FilterRegistrationBean<Filter> earlyMultipartParsingFilter() { FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>(); registration.setFilter(new EarlyMultipartParsingFilter()); registration.addUrlPatterns("/*"); // 确保在 ReplaceRequestFilter 之前执行 registration.setOrder(Integer.MIN_VALUE + 10); registration.setName("earlyMultipartParsingFilter"); return registration; } /** * 在 filter 链最前面对 multipart 请求提前解析, * 让 Tomcat 将文件数据缓存到磁盘,避免后续 filter 消费 InputStream 导致 Stream closed。 */ static class EarlyMultipartParsingFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { HttpServletRequest httpRequest = (HttpServletRequest) request; String contentType = httpRequest.getContentType(); if (contentType != null && contentType.toLowerCase().startsWith(MULTIPART)) { // 提前触发 multipart 解析,Tomcat 会将 parts 缓存 try { httpRequest.getParts(); } catch (Exception e) { log.warn("Early multipart parsing failed, will retry later: {}", e.getMessage()); } } } chain.doFilter(request, response); } } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MyLocaleResolver.java
New file @@ -0,0 +1,83 @@ package com.xiaopeng.trafficControl.config; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import org.springframework.util.StringUtils; import org.springframework.web.servlet.LocaleResolver; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Locale; public class MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request) { //1=中文,2=英文,3=印尼文 //1=Chinese, 2=English, 3=Indonesian. String language = getQueryParams(request, "language"); Locale locale = null; if (StringUtils.isEmpty(language)) { locale = Locale.getDefault(); language = "1"; } //如果请求不为空 //If the request is not empty. if(!StringUtils.isEmpty(language)){ if (language.equals("1")) { locale = new Locale("zh", "CN"); } else if (language.equals("2")) { locale = new Locale("en", "US"); } } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } public String getQueryParams(HttpServletRequest request, String key) { try { String method = request.getMethod(); if("POST".equals(method)){ String parameter = request.getParameter(key); if(!StringUtils.isEmpty(parameter)){ return parameter; } Object attribute = request.getAttribute(key); if(null != attribute){ return attribute.toString(); } StringBuffer sb = new StringBuffer(); ServletInputStream inputStream = request.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String s; while ((s = bufferedReader.readLine()) != null){ sb.append(s); } bufferedReader.close(); inputStream.close(); JSONObject jsonObject = JSON.parseObject(sb.toString()); return jsonObject.getString(key); }else{ String parameter = request.getParameter(key); if(!StringUtils.isEmpty(parameter)){ return parameter; } Object attribute = request.getAttribute(key); if(null != attribute){ return attribute.toString(); } } }catch (Exception e){ e.printStackTrace(); } return null; } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MybatisPlusConfig.java
New file @@ -0,0 +1,51 @@ package com.xiaopeng.trafficControl.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.core.config.GlobalConfig; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xiaochen * @ClassName MybatisPlusConfig * @Description MybatisPlus相关配置 * @date 2020-09-22 11:22、 * 直接以实现类作为bean的注入(有事务管理的类) * @EnableTransactionManagement(proxyTargetClass = true) */ @Configuration public class MybatisPlusConfig { private final DataUpdateHandlerConfig dataUpdateHandler; @Autowired public MybatisPlusConfig(DataUpdateHandlerConfig dataUpdateHandler) { this.dataUpdateHandler = dataUpdateHandler; } /** * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } /** * 自动填充功能 * * @return */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setMetaObjectHandler(dataUpdateHandler); return globalConfig; } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/TransactionConfiguration.java
New file @@ -0,0 +1,27 @@ package com.xiaopeng.trafficControl.config; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * 分布式事务配置 */ @Configuration @EnableTransactionManagement public class TransactionConfiguration { @Bean public PlatformTransactionManager txManager(final DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean public JdbcTemplate jdbcTemplate(final DataSource dataSource) { return new JdbcTemplate(dataSource); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/FeignLoggingAspect.java
New file @@ -0,0 +1,83 @@ package com.xiaopeng.trafficControl.config.log; import com.alibaba.fastjson2.JSON; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Arrays; /** * Feign 调用日志切面 * * <p>拦截所有带 @FeignClient 注解的接口方法,记录: * <ul> * <li>调用的接口类名 + 方法名</li> * <li>入参(JSON 序列化)</li> * <li>出参(JSON 序列化)</li> * <li>耗时(ms)</li> * <li>异常信息(含完整堆栈)</li> * </ul> */ @Slf4j @Aspect @Component public class FeignLoggingAspect { /** * 拦截所有带 @FeignClient 注解的接口的所有方法 */ @Around("within(@org.springframework.cloud.openfeign.FeignClient *)") public Object logFeignCall(ProceedingJoinPoint pjp) throws Throwable { return doLog(pjp); } private Object doLog(ProceedingJoinPoint pjp) throws Throwable { MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); // 使用接口声明类名,避免打印动态代理类名(如 $Proxy270) String className = signature.getDeclaringType().getSimpleName(); String methodName = method.getName(); String fullMethod = className + "#" + methodName; Object[] args = pjp.getArgs(); String argsJson = safeToJson(args); log.info("[Feign] >>> 调用开始 | method={} | args={}", fullMethod, argsJson); long start = System.currentTimeMillis(); try { Object result = pjp.proceed(); long elapsed = System.currentTimeMillis() - start; String resultJson = safeToJson(result); log.info("[Feign] <<< 调用成功 | method={} | elapsed={}ms | result={}", fullMethod, elapsed, resultJson); return result; } catch (Throwable ex) { long elapsed = System.currentTimeMillis() - start; log.error("[Feign] <<< 调用异常 | method={} | elapsed={}ms | error={} | args={}", fullMethod, elapsed, ex.getMessage(), argsJson, ex); throw ex; } } /** * 安全序列化为 JSON,避免序列化失败导致日志切面本身抛异常 */ private String safeToJson(Object obj) { if (obj == null) { return "null"; } try { if (obj instanceof Object[]) { return JSON.toJSONString(Arrays.asList((Object[]) obj)); } return JSON.toJSONString(obj); } catch (Exception e) { return "[序列化失败: " + obj.getClass().getSimpleName() + "]"; } } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/RequestResponseLoggingFilter.java
New file @@ -0,0 +1,139 @@ package com.xiaopeng.trafficControl.config.log; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * 请求/响应日志过滤器 * 打印:traceId、method、uri、queryString、请求体、响应状态、耗时、响应体 */ @Slf4j @Component @Order(Ordered.HIGHEST_PRECEDENCE + 10) public class RequestResponseLoggingFilter extends OncePerRequestFilter { /** 不打印 body 的路径前缀(健康检查、actuator 等) */ private static final Set<String> EXCLUDE_PATHS = new HashSet<>(Arrays.asList( "/actuator", "/healthcheck", "/health", "/prometheus" )); /** 只对这些 Content-Type 打印 body(避免打印二进制流) */ private static final Set<String> LOGGABLE_CONTENT_TYPES = new HashSet<>(Arrays.asList( MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.TEXT_PLAIN_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_XML_VALUE, "application/x-www-form-urlencoded" )); private static final int MAX_BODY_LENGTH = 2000; @Override protected boolean shouldNotFilter(HttpServletRequest request) { String uri = request.getRequestURI(); return EXCLUDE_PATHS.stream().anyMatch(uri::startsWith); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // 初始化 MDC(Filter 先于 Interceptor 执行,在此统一设置) String traceId = request.getHeader("X-Trace-Id"); if (traceId == null || traceId.isEmpty()) { traceId = java.util.UUID.randomUUID().toString().replace("-", ""); } MDC.put("traceId", traceId); MDC.put("clientIp", getClientIp(request)); MDC.put("requestUri", request.getRequestURI()); response.setHeader("X-Trace-Id", traceId); ContentCachingRequestWrapper reqWrapper = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper respWrapper = new ContentCachingResponseWrapper(response); long start = System.currentTimeMillis(); try { chain.doFilter(reqWrapper, respWrapper); } finally { long cost = System.currentTimeMillis() - start; logRequest(reqWrapper); logResponse(respWrapper, cost); // 必须把响应体写回,否则客户端收不到数据 respWrapper.copyBodyToResponse(); MDC.clear(); } } private String getClientIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) { return ip.split(",")[0].trim(); } ip = request.getHeader("X-Real-IP"); if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) { return ip; } return request.getRemoteAddr(); } private void logRequest(ContentCachingRequestWrapper request) { String traceId = MDC.get("traceId"); String uri = request.getRequestURI(); String query = request.getQueryString(); String method = request.getMethod(); String body = extractBody(request.getContentAsByteArray(), request.getContentType()); log.info(">>> [{}] {} {}{} | body={}", traceId, method, uri, query != null ? "?" + query : "", body); } private void logResponse(ContentCachingResponseWrapper response, long cost) { String traceId = MDC.get("traceId"); int status = response.getStatus(); String body = extractBody(response.getContentAsByteArray(), response.getContentType()); log.info("<<< [{}] status={} cost={}ms | body={}", traceId, status, cost, body); } private String extractBody(byte[] bodyBytes, String contentType) { if (bodyBytes == null || bodyBytes.length == 0) { return ""; } if (!isLoggableContentType(contentType)) { return "[binary, " + bodyBytes.length + " bytes]"; } String body = new String(bodyBytes, StandardCharsets.UTF_8); if (body.length() > MAX_BODY_LENGTH) { body = body.substring(0, MAX_BODY_LENGTH) + "...(truncated)"; } return body; } private boolean isLoggableContentType(String contentType) { if (contentType == null) return true; String ct = contentType.toLowerCase(); return LOGGABLE_CONTENT_TYPES.stream().anyMatch(ct::contains); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/constant/I18nKeyConstant.java
New file @@ -0,0 +1,69 @@ package com.xiaopeng.trafficControl.constant; /** * @author MiTao * @date 2026-02-06 17:34 * 国际化消息键常量 * */ public interface I18nKeyConstant { //========== 小程序业务流程 ========== String ORDER_NOT_EXISTS = "order.not.exists"; String VEHICLE_NOT_EXISTS = "vehicle.not.exists"; // ========== 车辆导入相关 ========== String VEHICLE_IMPORT_FAILED = "vehicle.import.failed"; String VEHICLE_IMPORT_DATA_EMPTY = "vehicle.import.data.empty"; String VEHICLE_IMPORT_FIELDS_REQUIRED = "vehicle.import.fields.required"; // ========== 车队删除相关 ========== String FLEET_DELETE_HAS_BOUND_VEHICLE_OR_USER = "fleet.delete.has.bound.vehicle.or.user"; // ========== 车辆运营相关 ========== String VEHICLE_OPERATION_INFO_INCOMPLETE = "vehicle.operation.info.incomplete"; String VEHICLE_BATCH_OPERATION_CONDITION_NOT_MET = "vehicle.batch.operation.condition.not.met"; String VEHICLE_TIME_EXPIRED = "vehicle.time.expired"; // ========== 车队相关 ========== String FLEET_OPERATION_USER_BOUND_V1 = "fleet.operation.user.bound.v1"; String FLEET_OPERATION_USER_BOUND_2 = "fleet.operation.user.bound.v2"; String FLEET_VEHICLE_BOUND_V1 = "fleet.vehicle.bound.v1"; String FLEET_VEHICLE_BOUND_V2 = "fleet.vehicle.bound.v2"; // ========== 车辆开关车门 ========== String VEHICLE_NOT_P_GEAR = "vehicle.not.p.gear"; String VEHICLE_DISTANCE_TOO_LONG = "vehicle.distance.too.long"; // ========== 车辆温度控制 ========== String VEHICLE_TEMPERATURE_UP = "vehicle.temperature.up"; String VEHICLE_TEMPERATURE_DOWN = "vehicle.temperature.down"; // ========== 车辆新增/编辑校验 ========== String VEHICLE_CODE_INVALID = "vehicle.code.invalid"; String VEHICLE_CODE_EXISTS = "vehicle.code.exists"; String VEHICLE_VIN_INVALID = "vehicle.vin.invalid"; String VEHICLE_VIN_EXISTS = "vehicle.vin.exists"; String VEHICLE_PLATE_INVALID = "vehicle.plate.invalid"; String VEHICLE_PLATE_EXISTS = "vehicle.plate.exists"; String VEHICLE_COLOR_INVALID = "vehicle.color.invalid"; String VEHICLE_ELECTRONIC_NOT_EXISTS = "vehicle.electronic.not.exists"; // ========== 车辆导入校验 ========== String VEHICLE_IMPORT_VALIDATE_FAIL = "vehicle.import.validate.fail"; String VEHICLE_IMPORT_CODE_INVALID = "vehicle.import.code.invalid"; String VEHICLE_IMPORT_VIN_INVALID = "vehicle.import.vin.invalid"; String VEHICLE_IMPORT_PLATE_INVALID = "vehicle.import.plate.invalid"; String VEHICLE_IMPORT_COLOR_INVALID = "vehicle.import.color.invalid"; String VEHICLE_IMPORT_CODE_DUPLICATE = "vehicle.import.code.duplicate"; String VEHICLE_IMPORT_VIN_DUPLICATE = "vehicle.import.vin.duplicate"; String VEHICLE_IMPORT_PLATE_DUPLICATE = "vehicle.import.plate.duplicate"; String VEHICLE_IMPORT_CODE_EXISTS = "vehicle.import.code.exists"; String VEHICLE_IMPORT_VIN_EXISTS = "vehicle.import.vin.exists"; String VEHICLE_IMPORT_PLATE_EXISTS = "vehicle.import.plate.exists"; // ========== 车辆删除相关 ========== String VEHICLE_DELETE_HAS_BOUND_FLEET = "vehicle.delete.has.bound.fleet"; } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/enums/SignatureType.java
New file @@ -0,0 +1,15 @@ package com.xiaopeng.trafficControl.enums; /** 签名类型枚举 */ public enum SignatureType { MD5("MD5"), SHA1("SHA1"), SHA256("SHA256"); private String value; private SignatureType(String value) { this.value = value; } public String getValue() { return value; } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HMACSHA1.java
New file @@ -0,0 +1,35 @@ package com.xiaopeng.trafficControl.util; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class HMACSHA1 { private static final String MAC_NAME = "HmacSHA1"; private static final String ENCODING = "UTF-8"; /** * 使用 HMAC-SHA1 签名方法对对encryptText进行签名 * * @param encryptText 被签名的字符串 * @param encryptKey 密钥 * @return * @throws Exception */ public static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey) throws Exception { byte[] data = encryptKey.getBytes(ENCODING); // 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称 Mac mac = Mac.getInstance(MAC_NAME); SecretKey secretKey = new SecretKeySpec(data, MAC_NAME); // 生成一个指定 Mac 算法 的 Mac 对象 // 用给定密钥初始化 Mac 对象 mac.init(secretKey); byte[] text = encryptText.getBytes(ENCODING); // 完成 Mac 操作 return mac.doFinal(text); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HexUtil.java
New file @@ -0,0 +1,36 @@ package com.xiaopeng.trafficControl.util; import com.xiaopeng.trafficControl.DecodingException; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; /** * 与字节16进制相关的工具类<br/> */ public class HexUtil { /** 将十六进制字符串转成字节数组 */ public static byte[] toHexBytes(String hexText) { try { return Hex.decodeHex(hexText.toCharArray()); } catch (DecoderException e) { throw new DecodingException(e); } } /** 将字节数组转成十六进制字符串(字母小写) */ public static String toLowerCase(byte[] hex) { return new String(Hex.encodeHex(hex, true)); } /** 将字节数组转成十六进制字符串(字母大写) */ public static String toUpperCase(byte[] hex) { return new String(Hex.encodeHex(hex, false)); } /** 将字节数组转成整数 */ public static int hashToInt(byte[] hash) { return (int) (hash[3] << 24 | hash[2] << 16 | hash[1] << 8 | hash[0]); } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ResourceUtil.java
New file @@ -0,0 +1,82 @@ package com.xiaopeng.trafficControl.util; import java.io.*; import java.lang.reflect.Method; import java.net.URL; /** * 资源工具类 * @author binghuoliu * */ public class ResourceUtil { /** * 读取classpath下的文件<br> * 读取classpath下的文件 一般用于读取classpath下的配置文件,注意读取的配置文件路径是相对传入参数clazz的load入路径. 例:<br> * ClassTest c1;//假设c1是从/usr/local/classpath/ 载入<br> * ClassTest c2;//假设c2是从/home/classpath/ 载入<br> * ............<br> * loadConfigFile("a.xml",c1.class);//从/usr/local/classpath1/下寻找a.xml<br> * loadConfigFile("a.xml",c2.class);//从/home/classpath/下寻找a.xml<br> * @param resource 资源路径 * @param clazz * @return InputStream 文件的inputstram,如果找不到则返回null */ @SuppressWarnings("unchecked") public static InputStream loadConfigFile(String resource, Class clazz) { ClassLoader classLoader = null; try { Method method = Thread.class.getMethod("getContextClassLoader"); classLoader = (ClassLoader) method.invoke(Thread.currentThread()); } catch (Exception e) { throw new RuntimeException("loadConfigFile error: ", e); } if (classLoader == null) { classLoader = clazz.getClassLoader(); } try { if (classLoader != null) { URL url = classLoader.getResource(resource); if (url == null) { System.out.println("Can not find resource:" + resource); return null; } if (url.toString().startsWith("jar:file:")) { System.out.println("Get resource \"" + resource + "\" from jar:\t" + url.toString()); return clazz.getResourceAsStream(resource.startsWith("/") ? resource : "/" + resource); } else { System.out.println("Get resource \"" + resource + "\" from:\t" + url.toString()); return new FileInputStream(new File(url.toURI())); } } } catch (Exception e) { throw new RuntimeException("loadConfigFile error: ", e); } return null; } /** * 释放资源(有异常抛出) * @param closeable * @throws IOException */ public static void close(Closeable closeable) throws IOException { if (closeable != null) { closeable.close(); } } /** * 以优雅的形式释放资源(内部吃掉了异常) * @param closeable */ public static void closeGracefully(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException e) { } } } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SHA1Util.java
New file @@ -0,0 +1,118 @@ package com.xiaopeng.trafficControl.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; /** * 该API支持线程安全<br/> */ public class SHA1Util { // --------------------------计算SHA1大写字符串---------------------------------------------- /** * 计算出文件的SHA1大写字符串 * * @param file * @return * @throws IOException */ public static String getSHA1UpperString(File file) throws IOException { return HexUtil.toUpperCase(getSHA1Bytes(file)); } /** * 计算出SHA1后十六进制结果(大写) * * @param s * @return */ public static String getSHA1UpperString(String s) { try { return getSHA1UpperString(s.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 计算出SHA1后十六进制结果(大写) * * @param bytes * @return */ public static String getSHA1UpperString(byte[] bytes) { return HexUtil.toUpperCase(getSHA1Bytes(bytes)); } // --------------------------计算SHA1小写字符串---------------------------------------------- /** * 计算出文件的SHA1小写字符串 * * @param file * @return * @throws IOException */ public static String getSHA1LowerString(File file) throws IOException { return HexUtil.toLowerCase(getSHA1Bytes(file)); } /** * 计算出SHA1后十六进制结果(小写) * * @param s * @return */ public static String getSHA1LowerString(String s) { try { return getSHA1LowerString(s.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * 计算出SHA1后十六进制结果(小写) * * @param bytes * @return */ public static String getSHA1LowerString(byte[] bytes) { return HexUtil.toLowerCase(getSHA1Bytes(bytes)); } // ------------------------计算出SHA1的byte[]------------------------------------------------ public static byte[] getSHA1Bytes(byte[] bytes) { ThreadLocalSHA1.current().update(bytes); return ThreadLocalSHA1.current().digest(); } public static byte[] getSHA1Bytes(File file) throws IOException { FileInputStream in = null; FileChannel ch = null; try { in = new FileInputStream(file); ch = in.getChannel(); MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); ThreadLocalSHA1.current().update(byteBuffer); return ThreadLocalSHA1.current().digest(); } finally { ResourceUtil.closeGracefully(ch); ResourceUtil.closeGracefully(in); } } // ------------------------------------------------------------------------ public static boolean checkPassword(String originContent, String sha1Content) { String s = getSHA1LowerString(originContent); return s.equals(sha1Content); } // ------------------------------------------------------------------------ } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SignatureUtils.java
New file @@ -0,0 +1,114 @@ package com.xiaopeng.trafficControl.util; import com.xiaopeng.unifiedverification.util.md5.MD5Util; import com.xiaopeng.unifiedverification.util.sha256.SHA256Util; import com.xiaopeng.trafficControl.enums.SignatureType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Comparator; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; public class SignatureUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SignatureUtils.class); /** * 通用签名方法,签名算法为SHA1<br/> * 签名原文拼装规则:<br/> * ①paraMap按key的自然排序,并按key1+value1+key2+value2的规则拼接,中间无分隔符<br/> * ②在第①步骤组装的String前面追加appid的值<br/> * ③在第①步骤组装的String后面追加appSecret的值<br/> * * @param appId 业务系统唯一标识 * @param paraMap 所需要传递的参数map * @param appSecret 分配给业务系统的秘钥 * @return */ public static String getSignatureSHA1(String appId, Map<String, String> paraMap, String appSecret) { return getSignature(appId, paraMap, appSecret, SignatureType.SHA1); } /** * 通用签名方法<br/> * 签名原文拼装规则:<br/> * ①paraMap按key的自然排序,并按key1+value1+key2+value2的规则拼接,中间无分隔符<br/> * ②在第①步骤组装的String前面追加appid的值<br/> * ③在第①步骤组装的String后面追加appSecret的值<br/> * * @param appId 业务系统唯一标识 * @param paraMap 所需要传递的参数map * @param appSecret 分配给业务系统的秘钥 * @param signatureType 签名算法类型(参考SignatureType) * @return */ public static String getSignature(String appId, Map<String, String> paraMap, String appSecret, SignatureType signatureType) { String originStr = buildAllSignatureOriginStr(appId, paraMap, appSecret); LOGGER.info("originStr: {}", originStr); return getSignature(originStr, signatureType); } // ------------------------------------------------------------------------------------------ /** * 构造计算签名的完整消息内容<br/> */ public static String buildAllSignatureOriginStr(String appId, Map<String, String> paraMap, String appSecret) { StringBuilder sb = new StringBuilder(128); String result = sb.append(appId).append(buildMiddleSignatureOriginStr(paraMap)).append(appSecret).toString(); // 用于签名的原文 return result; } /** * 构造计算签名的中间消息内容<br/> */ public static String buildMiddleSignatureOriginStr(Map<String, String> map) { if (map.isEmpty()) { return ""; } Map<String, String> treeMap = new TreeMap<String, String>(new Comparator<String>() { public int compare(String obj1, String obj2) { // 升序排序 return obj1.compareTo(obj2); } }); treeMap.putAll(map); Iterator<Map.Entry<String, String>> iter = treeMap.entrySet().iterator(); StringBuilder stringBuilder = new StringBuilder(128); while (iter.hasNext()) { Map.Entry<String, String> entry = iter.next(); stringBuilder.append(entry.getKey()).append(entry.getValue()); } return stringBuilder.toString(); } /** * 计算签名 * * @param originStr * 用于签名的原始内容 * @param signatureType * 签名算法类型(参考SignatureType) * @return 签名值 */ public static String getSignature(String originStr, SignatureType signatureType) { if (SignatureType.MD5 == signatureType) { return MD5Util.getMD5LowerString(originStr); } else if (SignatureType.SHA1 == signatureType) { return SHA1Util.getSHA1LowerString(originStr); } else if (SignatureType.SHA256 == signatureType) { return SHA256Util.getSHA256LowerString(originStr); } else { // 默认用SHA1 return SHA1Util.getSHA1LowerString(originStr); } } } xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ThreadLocalSHA1.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.trafficControl.util; import java.security.MessageDigest; /** * SHA1算法计算散列值的ThreadLocal,避免多线程场景下的线程安全问题<br/> */ public class ThreadLocalSHA1 { private static final ThreadLocal<MessageDigest> localFormat = new ThreadLocal<MessageDigest>() { protected MessageDigest initialValue() { try { return MessageDigest.getInstance("SHA1"); } catch (Exception e) { System.err.println(SHA1Util.class.getName() + "初始化失败,MessageDigest不支持SHA1Util。"); e.printStackTrace(); return null; } }; }; /** 获取当前线程对应的实例 */ public static MessageDigest current() { return localFormat.get(); } } xp-robotaxi-traffic-control-boot/src/main/resources/application-local.properties
New file @@ -0,0 +1,104 @@ spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure spring.shardingsphere.props.sql.show=true spring.shardingsphere.props.show-sql=true spring.shardingsphere.datasource.names=xp0 spring.shardingsphere.datasource.xp0.type=com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.xp0.driver-class-name=com.mysql.cj.jdbc.Driver spring.shardingsphere.datasource.xp0.url=jdbc:mysql://pc-bp1o444934f546768po.rwlb.rds.aliyuncs.com:3306/xp_rt_maintenance?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 spring.shardingsphere.datasource.xp0.username=xp_rt_maintenance spring.shardingsphere.datasource.xp0.password=gWgoAv_bi02um8sN spring.shardingsphere.datasource.xp0.druid.initial-size=10 spring.shardingsphere.datasource.xp0.druid.min-idle=10 spring.shardingsphere.datasource.xp0.druid.max-active=100 spring.shardingsphere.datasource.xp0.druid.max-wait=60000 spring.shardingsphere.datasource.xp0.druid.time-between-eviction-runs-millis=60000 spring.shardingsphere.datasource.xp0.druid.min-evictable-idle-time-millis=300000 spring.shardingsphere.datasource.xp0.druid.validation-query=SELECT 1 spring.shardingsphere.datasource.xp0.druid.test-while-idle=true spring.shardingsphere.datasource.xp0.druid.test-on-borrow=false spring.shardingsphere.datasource.xp0.druid.test-on-return=false spring.shardingsphere.datasource.xp0.druid.pool-prepared-statements=true spring.shardingsphere.datasource.xp0.druid.max-pool-prepared-statement-per-connection-size=20 spring.shardingsphere.datasource.xp0.druid.filters=stat,wall,log4j spring.shardingsphere.datasource.xp0.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 spring.shardingsphere.datasource.xp0.druid.remove-abandoned=true spring.shardingsphere.datasource.xp0.druid.remove-abandoned-timeout=1800 spring.shardingsphere.datasource.xp0.druid.log-abandoned=true spring.shardingsphere.datasource.xp0.druid.stat-view-servlet.enabled=true spring.shardingsphere.datasource.xp0.druid.stat-view-servlet.url-pattern=/druid/* spring.shardingsphere.datasource.xp0.druid.stat-view-servlet.login-username=admin spring.shardingsphere.datasource.xp0.druid.stat-view-servlet.login-password=admin spring.shardingsphere.datasource.xp0.druid.stat-view-servlet.reset-enable=false spring.shardingsphere.datasource.xp0.druid.wall.config.multi-statement-allow=false spring.shardingsphere.rules.sharding.tables.xp_charging_record.actual-data-nodes=xp0.xp_charging_record_$->{0..9} spring.shardingsphere.rules.sharding.tables.xp_charging_record.table-strategy.standard.sharding-column=vehicle_id spring.shardingsphere.rules.sharding.tables.xp_charging_record.table-strategy.standard.sharding-algorithm-name=xp-charging-record-inline spring.shardingsphere.rules.sharding.tables.xp_charging_record.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_charging_record.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_clock_in.actual-data-nodes=xp0.xp_clock_in_$->{0..9} spring.shardingsphere.rules.sharding.tables.xp_clock_in.table-strategy.standard.sharding-column=app_user_id spring.shardingsphere.rules.sharding.tables.xp_clock_in.table-strategy.standard.sharding-algorithm-name=xp-clock-in-inline spring.shardingsphere.rules.sharding.tables.xp_clock_in.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_clock_in.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_task.actual-data-nodes=xp0.xp_task_$->{0..19} spring.shardingsphere.rules.sharding.tables.xp_task.table-strategy.standard.sharding-column=vehicle_id spring.shardingsphere.rules.sharding.tables.xp_task.table-strategy.standard.sharding-algorithm-name=xp-task-inline spring.shardingsphere.rules.sharding.tables.xp_task.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_task.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail.actual-data-nodes=xp0.xp_task_handle_detail_$->{0..19} spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail.table-strategy.standard.sharding-column=task_id spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail.table-strategy.standard.sharding-algorithm-name=xp-task-handle-detail-inline spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail_routine.actual-data-nodes=xp0.xp_task_handle_detail_routine_$->{0..19} spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail_routine.table-strategy.standard.sharding-column=task_id spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail_routine.table-strategy.standard.sharding-algorithm-name=xp-task-handle-detail-routine-inline spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail_routine.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_task_handle_detail_routine.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_vehicle_detection.actual-data-nodes=xp0.xp_vehicle_detection_$->{0..9} spring.shardingsphere.rules.sharding.tables.xp_vehicle_detection.table-strategy.standard.sharding-column=task_id spring.shardingsphere.rules.sharding.tables.xp_vehicle_detection.table-strategy.standard.sharding-algorithm-name=xp-vehicle-detection-inline spring.shardingsphere.rules.sharding.tables.xp_vehicle_detection.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_vehicle_detection.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_vehicle_preparation.actual-data-nodes=xp0.xp_vehicle_preparation_$->{0..9} spring.shardingsphere.rules.sharding.tables.xp_vehicle_preparation.table-strategy.standard.sharding-column=task_id spring.shardingsphere.rules.sharding.tables.xp_vehicle_preparation.table-strategy.standard.sharding-algorithm-name=xp-vehicle-preparation-inline spring.shardingsphere.rules.sharding.tables.xp_vehicle_preparation.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_vehicle_preparation.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_vehicle_repair.actual-data-nodes=xp0.xp_vehicle_repair_$->{0..9} spring.shardingsphere.rules.sharding.tables.xp_vehicle_repair.table-strategy.standard.sharding-column=task_id spring.shardingsphere.rules.sharding.tables.xp_vehicle_repair.table-strategy.standard.sharding-algorithm-name=xp-vehicle-repair-inline spring.shardingsphere.rules.sharding.tables.xp_vehicle_repair.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_vehicle_repair.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_work_order.actual-data-nodes=xp0.xp_work_order_$->{0..9} spring.shardingsphere.rules.sharding.tables.xp_work_order.table-strategy.standard.sharding-column=feedback_user_phone spring.shardingsphere.rules.sharding.tables.xp_work_order.table-strategy.standard.sharding-algorithm-name=xp-work-order-inline spring.shardingsphere.rules.sharding.tables.xp_work_order.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_work_order.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.tables.xp_work_order_log.actual-data-nodes=xp0.xp_work_order_log_$->{0..19} spring.shardingsphere.rules.sharding.tables.xp_work_order_log.table-strategy.standard.sharding-column=work_order_id spring.shardingsphere.rules.sharding.tables.xp_work_order_log.table-strategy.standard.sharding-algorithm-name=xp-work-order-log-inline spring.shardingsphere.rules.sharding.tables.xp_work_order_log.key-generate-strategy.column=id spring.shardingsphere.rules.sharding.tables.xp_work_order_log.key-generate-strategy.key-generator-name=snowflake spring.shardingsphere.rules.sharding.sharding-algorithms.xp-charging-record-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-charging-record-inline.props.algorithm-expression=xp_charging_record_${vehicle_id % 20} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-clock-in-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-clock-in-inline.props.algorithm-expression=xp_clock_in_${app_user_id % 10} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-task-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-task-inline.props.algorithm-expression=xp_task_${vehicle_id % 20} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-task-handle-detail-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-task-handle-detail-inline.props.algorithm-expression=xp_task_handle_detail_${task_id % 20} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-task-handle-detail-routine-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-task-handle-detail-routine-inline.props.algorithm-expression=xp_task_handle_detail_routine_${task_id % 20} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-vehicle-detection-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-vehicle-detection-inline.props.algorithm-expression=xp_vehicle_detection_${task_id % 10} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-vehicle-preparation-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-vehicle-preparation-inline.props.algorithm-expression=xp_vehicle_preparation_${task_id % 10} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-vehicle-repair-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-vehicle-repair-inline.props.algorithm-expression=xp_vehicle_repair_${task_id % 10} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-work-order-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-work-order-inline.props.algorithm-expression=xp_work_order_${feedback_user_phone % 10} spring.shardingsphere.rules.sharding.sharding-algorithms.xp-work-order-log-inline.type=INLINE spring.shardingsphere.rules.sharding.sharding-algorithms.xp-work-order-log-inline.props.algorithm-expression=xp_work_order_log_${work_order_id % 20} spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE seata.enabled=false xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages.properties
xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_en_US.properties
xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_zh_CN.properties
xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml
New file @@ -0,0 +1,222 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,比如: 如果设置为WARN,则低于WARN的信息都不会输出 --> <!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true --> <!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="true" scanPeriod="10 seconds"> <contextName>logback</contextName> <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使“${}”来使用变量。 --> <property name="log.path" value="/data/log/app/xp-robotaxi-traffic-control-boot"/> <!--0. 日志格式和颜色渲染 --> <!-- 彩色日志依赖的渲染类 --> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> <!-- 彩色日志格式 --> <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <!--1. 输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debug</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!-- 设置字符集 --> <charset>UTF-8</charset> </encoder> </appender> <!--2. 输出到文档--> <!-- 2.1 level为 DEBUG 日志,时间滚动输出 --> <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/debug.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志归档 --> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录debug级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>debug</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.2 level为 INFO 日志,时间滚动输出 --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/info.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日志归档路径以及格式 --> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录info级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>info</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.3 level为 WARN 日志,时间滚动输出 --> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/warn.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录warn级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.4 level为 ERROR 日志,时间滚动输出 --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/error.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录ERROR级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.5 所有 除了DEBUG级别的其它高于DEBUG的 日志,记录到一个文件 --> <appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/all.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/all-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档记录除了DEBUG级别的其它高于DEBUG的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> </appender> <!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 以及指定<appender>。<logger>仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前logger将会继承上级的级别。 addtivity:是否向上级logger传递打印信息。默认是true。 <logger name="org.springframework.web" level="info"/> <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/> --> <!-- root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 不能设置为INHERITED或者同义词NULL。默认是DEBUG 可以包含零个或多个元素,标识这个appender将会添加到这个logger。 --> <!-- 4 最终的策略: 基本策略(root级) + 根据profile在启动时, logger标签中定制化package日志级别(优先级高于上面的root级)--> <springProfile name="dev"> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="ALL_FILE" /> </root> <logger name="com.stylefeng.guns.modular.system.dao" level="debug"/> </springProfile> <springProfile name="test"> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="ALL_FILE" /> </root> </springProfile> <springProfile name="prod"> <root level="info"> <!-- 生产环境最好不配置console写文件 --> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="ALL_FILE" /> </root> </springProfile> </configuration> xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml.bak
New file @@ -0,0 +1,222 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,比如: 如果设置为WARN,则低于WARN的信息都不会输出 --> <!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true --> <!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --> <!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="true" scanPeriod="10 seconds"> <contextName>logback</contextName> <!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后,可以使"${}"来使用变量。 --> <property name="log.path" value="/data/log/app/ruoyi-account"/> <!--0. 日志格式和颜色渲染 --> <!-- 彩色日志依赖的渲染类 --> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" /> <!-- 彩色日志格式 --> <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <!--1. 输出到控制台--> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>debug</level> </filter> <encoder> <Pattern>${CONSOLE_LOG_PATTERN}</Pattern> <!-- 设置字符集 --> <charset>UTF-8</charset> </encoder> </appender> <!--2. 输出到文档--> <!-- 2.1 level为 DEBUG 日志,时间滚动输出 --> <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/debug.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志归档 --> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录debug级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>debug</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.2 level为 INFO 日志,时间滚动输出 --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/info.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 每天日志归档路径以及格式 --> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录info级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>info</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.3 level为 WARN 日志,时间滚动输出 --> <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/warn.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录warn级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>warn</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.4 level为 ERROR 日志,时间滚动输出 --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/error.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档只记录ERROR级别的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- 2.5 所有 除了DEBUG级别的其它高于DEBUG的 日志,记录到一个文件 --> <appender name="ALL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 正在记录的日志文档的路径及文档名 --> <file>${log.path}/all.log</file> <!--日志文档输出格式--> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> <!-- 此处设置字符集 --> </encoder> <!-- 日志记录器的滚动策略,按日期,按大小记录 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log.path}/%d{yyyy-MM-dd}/all-%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <!--日志文档保留天数--> <maxHistory>15</maxHistory> </rollingPolicy> <!-- 此日志文档记录除了DEBUG级别的其它高于DEBUG的 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMatch>DENY</onMatch> <onMismatch>ACCEPT</onMismatch> </filter> </appender> <!-- <logger>用来设置某一个包或者具体的某一个类的日志打印级别、 以及指定<appender>。<logger>仅有一个name属性, 一个可选的level和一个可选的addtivity属性。 name:用来指定受此logger约束的某一个包或者具体的某一个类。 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前logger将会继承上级的级别。 addtivity:是否向上级logger传递打印信息。默认是true。 <logger name="org.springframework.web" level="info"/> <logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/> --> <!-- root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性 level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF, 不能设置为INHERITED或者同义词NULL。默认是DEBUG 可以包含零个或多个元素,标识这个appender将会添加到这个logger。 --> <!-- 4 最终的策略: 基本策略(root级) + 根据profile在启动时, logger标签中定制化package日志级别(优先级高于上面的root级)--> <springProfile name="dev"> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="ALL_FILE" /> </root> <logger name="com.stylefeng.guns.modular.system.dao" level="debug"/> </springProfile> <springProfile name="test"> <root level="info"> <appender-ref ref="CONSOLE" /> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="ALL_FILE" /> </root> </springProfile> <springProfile name="prod"> <root level="info"> <!-- 生产环境最好不配置console写文件 --> <appender-ref ref="DEBUG_FILE" /> <appender-ref ref="INFO_FILE" /> <appender-ref ref="WARN_FILE" /> <appender-ref ref="ERROR_FILE" /> <appender-ref ref="ALL_FILE" /> </root> </springProfile> </configuration> xp-robotaxi-traffic-control-boot/src/main/resources/mybatis-config.xml
New file @@ -0,0 +1,25 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 打印查询语句 不会写入到日志文件中--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--<setting name="logImpl" value="LOG4J" />--> <!-- 控制全局缓存(二级缓存),按美团技术团队的说法,尽量别用缓存机制 emmmm.... --> <setting name="cacheEnabled" value="true"/> <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false --> <!-- <setting name="lazyLoadingEnabled" value="true"/> --> <setting name="mapUnderscoreToCamelCase" value="true"/><!--是否将map下划线方式转为驼峰式命名--> <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖--> <!-- <setting name="aggressiveLazyLoading" value="false"/>--> <!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST --> <!--<setting name="proxyFactory" value="CGLIB" />--> <!-- 关于mybatis的一二级缓存 请参照:https://tech.meituan.com/2018/01/19/mybatis-cache.html --> <!-- 一级缓存范围默认:SESSION ,此范围在复杂应用场景中可能会出现脏读数据--> <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 --> <!--<setting name="localCacheScope" value="STATEMENT"/>--> <setting name="localCacheScope" value="STATEMENT"/> </settings> </configuration> xp-robotaxi-traffic-control-boot/src/main/resources/seata.conf
New file @@ -0,0 +1,7 @@ # seata.conf shardingsphere.transaction.seata.at.enable = true shardingsphere.transaction.seata.tx.timeout = 60 client { application.id = "xp-robotaxi-traffic-control-boot" # 与spring.application.name一致 transaction.service.group = "seata_tx_group" } xp-robotaxi-traffic-control-boot/src/test/java/com/ruoyi/account/RuoYiAccountApplicationTests.java
New file @@ -0,0 +1,30 @@ //package com.ruoyi.account; // //import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; //import com.ruoyi.common.security.service.TokenService; //import com.ruoyi.system.api.model.LoginUserApplet; //import org.junit.jupiter.api.Test; //import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.boot.test.context.SpringBootTest; // //import java.util.HashMap; //import java.util.Map; // //@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = RuoYiAccountApplication.class) //public class RuoYiAccountApplicationTests { // @Autowired // private TokenService tokenService; // // @Test // public void test(){ // LoginUserApplet loginUserApplet = new LoginUserApplet(); // loginUserApplet.setUserId(1839480334642704385L); // loginUserApplet.setUserIdStr("1839480334642704385"); // loginUserApplet.setName("158****3127"); // loginUserApplet.setPhone("15828353127"); // loginUserApplet.setAvatar(""); // loginUserApplet.setAddress(""); // Map<String, Object> tokenInfos = new HashMap<>(); // System.err.println(tokenService.createTokenApplet(loginUserApplet)); // } //} xp-robotaxi-traffic-control-boot/xp-robotaxi-vehicle-convoy-boot.iml
New file @@ -0,0 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <module version="4"> <component name="AdditionalModuleElements"> <content url="file://$MODULE_DIR$" dumb="true"> <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> </content> </component> </module>