From 3ee4725e50f2494fc291ba841730942004e22956 Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期二, 07 四月 2026 17:05:49 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
xp-robotaxi-traffic-control-boot/src/test/java/com/ruoyi/account/RuoYiAccountApplicationTests.java | 30
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/FeignLoggingAspect.java | 83 ++
xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_zh_CN.properties | 0
xp-robotaxi-traffic-control-boot/xp-robotaxi-vehicle-convoy-boot.iml | 9
xp-robotaxi-traffic-control-boot/pom.xml | 214 +++++
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ThreadLocalSHA1.java | 26
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MyLocaleResolver.java | 83 ++
xp-robotaxi-traffic-control-boot/src/main/resources/application-local.properties | 104 ++
xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages.properties | 0
pom.xml | 1
xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml.bak | 222 ++++++
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HexUtil.java | 36 +
xp-robotaxi-traffic-control-boot/src/main/resources/mybatis-config.xml | 25
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ResourceUtil.java | 82 ++
xp-robotaxi-traffic-control-boot/Dockerfile | 16
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/DataUpdateHandlerConfig.java | 71 +
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/TrafficControlApplication.java | 29
xp-robotaxi-traffic-control-boot/src/main/resources/seata.conf | 7
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java | 4
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java | 1
xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml | 222 ++++++
xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_en_US.properties | 0
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HMACSHA1.java | 35
xp-robotaxi-map-boot/src/main/resources/bootstrap.yml | 148 ++++
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SignatureUtils.java | 114 +++
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/DecodingException.java | 17
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/RequestResponseLoggingFilter.java | 139 +++
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SHA1Util.java | 118 +++
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/constant/I18nKeyConstant.java | 69 +
xp-robotaxi-order-boot/src/main/resources/bootstrap.yml | 1
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MultipartFilterConfig.java | 64 +
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/TransactionConfiguration.java | 27
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MessageLocalConfig.java | 59 +
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/enums/SignatureType.java | 15
xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MybatisPlusConfig.java | 51 +
35 files changed, 2,120 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 46a9729..5130865 100644
--- a/pom.xml
+++ b/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>
diff --git a/xp-robotaxi-map-boot/src/main/resources/bootstrap.yml b/xp-robotaxi-map-boot/src/main/resources/bootstrap.yml
index 36fdb64..bfcf8c0 100644
--- a/xp-robotaxi-map-boot/src/main/resources/bootstrap.yml
+++ b/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}
\ No newline at end of file
diff --git a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
index 5e9f636..c578622 100644
--- a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
+++ b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
@@ -457,4 +457,8 @@
}
+
+
+
+
}
diff --git a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java b/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
index 5c7422e..365703f 100644
--- a/xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
+++ b/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];
diff --git a/xp-robotaxi-order-boot/src/main/resources/bootstrap.yml b/xp-robotaxi-order-boot/src/main/resources/bootstrap.yml
index e4631d7..73d792f 100644
--- a/xp-robotaxi-order-boot/src/main/resources/bootstrap.yml
+++ b/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
diff --git a/xp-robotaxi-traffic-control-boot/Dockerfile b/xp-robotaxi-traffic-control-boot/Dockerfile
new file mode 100644
index 0000000..f598f98
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/Dockerfile
@@ -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
diff --git a/xp-robotaxi-traffic-control-boot/pom.xml b/xp-robotaxi-traffic-control-boot/pom.xml
new file mode 100644
index 0000000..349b4f1
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/pom.xml
@@ -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>
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/DecodingException.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/DecodingException.java
new file mode 100644
index 0000000..a1416fd
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/DecodingException.java
@@ -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);
+ }
+
+}
\ No newline at end of file
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/TrafficControlApplication.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/TrafficControlApplication.java
new file mode 100644
index 0000000..e9fe0df
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/TrafficControlApplication.java
@@ -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");
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/DataUpdateHandlerConfig.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/DataUpdateHandlerConfig.java
new file mode 100644
index 0000000..cc030b6
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/DataUpdateHandlerConfig.java
@@ -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);
+
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MessageLocalConfig.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MessageLocalConfig.java
new file mode 100644
index 0000000..37ce6c2
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MessageLocalConfig.java
@@ -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();
+ }
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MultipartFilterConfig.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MultipartFilterConfig.java
new file mode 100644
index 0000000..3810404
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MultipartFilterConfig.java
@@ -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);
+ }
+ }
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MyLocaleResolver.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MyLocaleResolver.java
new file mode 100644
index 0000000..f23bb21
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MyLocaleResolver.java
@@ -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;
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MybatisPlusConfig.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..4642647
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/MybatisPlusConfig.java
@@ -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;
+ }
+
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/TransactionConfiguration.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/TransactionConfiguration.java
new file mode 100644
index 0000000..a814162
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/TransactionConfiguration.java
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/FeignLoggingAspect.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/FeignLoggingAspect.java
new file mode 100644
index 0000000..a2da7be
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/FeignLoggingAspect.java
@@ -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() + "]";
+ }
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/RequestResponseLoggingFilter.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/RequestResponseLoggingFilter.java
new file mode 100644
index 0000000..5c46133
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/config/log/RequestResponseLoggingFilter.java
@@ -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);
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/constant/I18nKeyConstant.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/constant/I18nKeyConstant.java
new file mode 100644
index 0000000..dff7880
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/constant/I18nKeyConstant.java
@@ -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";
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/enums/SignatureType.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/enums/SignatureType.java
new file mode 100644
index 0000000..d1871c4
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/enums/SignatureType.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HMACSHA1.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HMACSHA1.java
new file mode 100644
index 0000000..b2d23d9
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HMACSHA1.java
@@ -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);
+ }
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HexUtil.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HexUtil.java
new file mode 100644
index 0000000..b845c1d
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/HexUtil.java
@@ -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]);
+ }
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ResourceUtil.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ResourceUtil.java
new file mode 100644
index 0000000..162dda6
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ResourceUtil.java
@@ -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) {
+ }
+ }
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SHA1Util.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SHA1Util.java
new file mode 100644
index 0000000..d123471
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SHA1Util.java
@@ -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);
+ }
+
+ // ------------------------------------------------------------------------
+
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SignatureUtils.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SignatureUtils.java
new file mode 100644
index 0000000..950e47c
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/SignatureUtils.java
@@ -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);
+ }
+ }
+}
diff --git a/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ThreadLocalSHA1.java b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ThreadLocalSHA1.java
new file mode 100644
index 0000000..9907197
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/java/com/xiaopeng/trafficControl/util/ThreadLocalSHA1.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/application-local.properties b/xp-robotaxi-traffic-control-boot/src/main/resources/application-local.properties
new file mode 100644
index 0000000..347eecd
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/application-local.properties
@@ -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
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages.properties b/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages.properties
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_en_US.properties b/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_en_US.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_en_US.properties
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_zh_CN.properties b/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_zh_CN.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/i18n/messages_zh_CN.properties
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml b/xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml
new file mode 100644
index 0000000..b0510fb
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml
@@ -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>
\ No newline at end of file
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml.bak b/xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml.bak
new file mode 100644
index 0000000..14a129a
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/logback.xml.bak
@@ -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>
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/mybatis-config.xml b/xp-robotaxi-traffic-control-boot/src/main/resources/mybatis-config.xml
new file mode 100644
index 0000000..53c5587
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/mybatis-config.xml
@@ -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>
diff --git a/xp-robotaxi-traffic-control-boot/src/main/resources/seata.conf b/xp-robotaxi-traffic-control-boot/src/main/resources/seata.conf
new file mode 100644
index 0000000..d798498
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/main/resources/seata.conf
@@ -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"
+}
\ No newline at end of file
diff --git a/xp-robotaxi-traffic-control-boot/src/test/java/com/ruoyi/account/RuoYiAccountApplicationTests.java b/xp-robotaxi-traffic-control-boot/src/test/java/com/ruoyi/account/RuoYiAccountApplicationTests.java
new file mode 100644
index 0000000..9302669
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/src/test/java/com/ruoyi/account/RuoYiAccountApplicationTests.java
@@ -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));
+// }
+//}
diff --git a/xp-robotaxi-traffic-control-boot/xp-robotaxi-vehicle-convoy-boot.iml b/xp-robotaxi-traffic-control-boot/xp-robotaxi-vehicle-convoy-boot.iml
new file mode 100644
index 0000000..68a9707
--- /dev/null
+++ b/xp-robotaxi-traffic-control-boot/xp-robotaxi-vehicle-convoy-boot.iml
@@ -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>
\ No newline at end of file
--
Gitblit v1.7.1