diff --git a/backend/.env.example b/backend/.env.example
new file mode 100644
index 0000000..111a596
--- /dev/null
+++ b/backend/.env.example
@@ -0,0 +1,30 @@
+# 数据库配置
+DB_URL=jdbc:mysql://localhost:3306/stdproject?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
+DB_USERNAME=root
+DB_PASSWORD=your_password_here
+
+# 表前缀(可选)
+DB_TABLE_PREFIX=
+
+# CORS配置
+CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
+CORS_MAX_AGE=3600
+
+# JWT配置
+JWT_ENABLED=false
+JWT_SECRET=YourJWTSecretKeyForStdProjectBackendApplicationWhichIsVeryLongAndSecure2024!@#$%^&*()
+JWT_EXPIRATION=1800000
+JWT_REFRESH_EXPIRATION=604800000
+
+# 日志配置
+LOG_LEVEL_ROOT=INFO
+LOG_LEVEL_APP=DEBUG
+LOG_LEVEL_SECURITY=WARN
+LOG_LEVEL_SQL=WARN
+LOG_LEVEL_SQL_PARAMS=WARN
+
+# MyBatis日志配置
+MYBATIS_LOG_IMPL=org.apache.ibatis.logging.nologging.NoLoggingImpl
+
+# Spring配置
+SPRING_PROFILES_ACTIVE=dev
\ No newline at end of file
diff --git a/backend/pom.xml b/backend/pom.xml
index 3e3ecb8..556a08d 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -21,7 +21,7 @@
3.0.3
3.5.6
0.11.5
- 2.0.2
+ 2.3.0
1.32.0
33.0.0-jre
@@ -58,6 +58,7 @@
2.0.0
system
${project.basedir}/libs/sdk-bundle-2.0.jar
+
@@ -71,7 +72,7 @@
mybatis-spring
${mybatis-spring.version}
-
+
com.baomidou
mybatis-plus-boot-starter
@@ -84,6 +85,7 @@
8.0.30
runtime
+
org.xerial
sqlite-jdbc
diff --git a/backend/src/main/java/com/stdproject/config/JwtAuthenticationFilter.java b/backend/src/main/java/com/stdproject/config/JwtAuthenticationFilter.java
index 342f781..5d3aaca 100644
--- a/backend/src/main/java/com/stdproject/config/JwtAuthenticationFilter.java
+++ b/backend/src/main/java/com/stdproject/config/JwtAuthenticationFilter.java
@@ -45,7 +45,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
String jwt = getJwtFromRequest(request);
// 拦截器中校验
- if (webConfig.loginuserCache().get(jwt) != null) {
+ if (StringUtils.hasText(jwt) && webConfig.timedCache().get(jwt) != null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token 已失效");
return;
}
diff --git a/backend/src/main/java/com/stdproject/config/OpenApiConfig.java b/backend/src/main/java/com/stdproject/config/OpenApiConfig.java
deleted file mode 100644
index 92cdd40..0000000
--- a/backend/src/main/java/com/stdproject/config/OpenApiConfig.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.stdproject.config;
-
-import io.swagger.v3.oas.annotations.OpenAPIDefinition;
-import io.swagger.v3.oas.annotations.info.Info;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-@OpenAPIDefinition(info = @Info(title = "Standard Project API", description = "API documentation for Standard Project"))
-public class OpenApiConfig {
-}
\ No newline at end of file
diff --git a/backend/src/main/java/com/stdproject/config/SecurityConfig.java b/backend/src/main/java/com/stdproject/config/SecurityConfig.java
index 896ac40..ee0ffd3 100644
--- a/backend/src/main/java/com/stdproject/config/SecurityConfig.java
+++ b/backend/src/main/java/com/stdproject/config/SecurityConfig.java
@@ -52,7 +52,7 @@ public class SecurityConfig {
// 公开路径配置
private static final String[] PUBLIC_PATHS = {
- "/auth/**",
+ "/api/auth/**",
"/api/public/**",
"/swagger-ui/**",
"/v3/api-docs/**",
@@ -133,19 +133,18 @@ public class SecurityConfig {
// 异常处理配置
.exceptionHandling(ex -> ex
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
- )
-
- // 安全头配置
- .headers(headers -> headers
- // 禁用iframe嵌入,防止点击劫持攻击
- .frameOptions().deny()
- // 设置X-Content-Type-Options头为nosniff,防止MIME类型嗅探攻击
- .contentTypeOptions().and()
- // 设置Referrer Policy为strict-origin-when-cross-origin
- // 跨域请求时只发送源(origin),同源请求发送完整referrer
- .referrerPolicy("strict-origin-when-cross-origin")
- // 注意:已移除HSTS配置,不再强制使用HTTPS
);
+ // 安全头配置
+// .headers(headers -> headers
+// // 禁用iframe嵌入,防止点击劫持攻击
+// .frameOptions().deny()
+// // 设置X-Content-Type-Options头为nosniff,防止MIME类型嗅探攻击
+// .contentTypeOptions()
+// // 设置Referrer Policy为strict-origin-when-cross-origin
+// // 跨域请求时只发送源(origin),同源请求发送完整referrer
+// .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
+// // 注意:已移除HSTS配置,不再强制使用HTTPS
+// )
// 根据配置决定是否启用JWT认证
if (jwtEnabled) {
diff --git a/backend/src/main/java/com/stdproject/config/WebConfig.java b/backend/src/main/java/com/stdproject/config/WebConfig.java
index ec54cfe..7c80abc 100644
--- a/backend/src/main/java/com/stdproject/config/WebConfig.java
+++ b/backend/src/main/java/com/stdproject/config/WebConfig.java
@@ -17,14 +17,15 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class WebConfig implements WebMvcConfigurer {
@Value("${spring.security.jwt.expiration-ms}")
- private Long expirationMs;
+ private Long jwtExpirationMs;
/**
* 用户缓存
*/
@Bean
- public Cache loginuserCache() {
- return CacheUtil.newTimedCache(expirationMs);
+ public Cache timedCache() {
+ // 创建定时缓存,缓存时间与JWT过期时间一致
+ return CacheUtil.newTimedCache(jwtExpirationMs);
}
/**
diff --git a/backend/src/main/java/com/stdproject/controller/AuthController.java b/backend/src/main/java/com/stdproject/controller/AuthController.java
index 56a4a8b..b2346de 100644
--- a/backend/src/main/java/com/stdproject/controller/AuthController.java
+++ b/backend/src/main/java/com/stdproject/controller/AuthController.java
@@ -223,7 +223,7 @@ public class AuthController {
// 计算剩余有效时间(毫秒)
long remainingMillis = expirationMillis - nowMillis;
if (remainingMillis > 0) {
- webConfig.loginuserCache().put(token, "1", remainingMillis);
+ webConfig.timedCache().put(token, remainingMillis);
}
}
diff --git a/backend/src/main/java/com/stdproject/listener/DataSourceInitStartListener.java b/backend/src/main/java/com/stdproject/listener/DataSourceInitStartListener.java
new file mode 100644
index 0000000..20479a7
--- /dev/null
+++ b/backend/src/main/java/com/stdproject/listener/DataSourceInitStartListener.java
@@ -0,0 +1,23 @@
+package com.stdproject.listener;
+
+import com.stdproject.service.provider.CalciteProvider;
+import jakarta.annotation.Resource;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+@Component
+@Order(value = 2)
+public class DataSourceInitStartListener implements ApplicationListener {
+ @Resource
+ private CalciteProvider calciteProvider;
+ @Override
+ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
+ try {
+ calciteProvider.initConnectionPool();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/backend/src/main/java/com/stdproject/service/impl/DynamicDataServiceImpl.java b/backend/src/main/java/com/stdproject/service/impl/DynamicDataServiceImpl.java
index f845ded..59eda99 100644
--- a/backend/src/main/java/com/stdproject/service/impl/DynamicDataServiceImpl.java
+++ b/backend/src/main/java/com/stdproject/service/impl/DynamicDataServiceImpl.java
@@ -33,10 +33,10 @@ public class DynamicDataServiceImpl implements IDynamicDataService {
public boolean addTableData(Long datasourceId, String tableData) throws Exception {
// 根据数据源 id 查询数据源信息,调用通用数据源执行器
CoreDatasource coreDatasource = coreDatasourceMapper.selectById(datasourceId);
- coreDatasource.setConfiguration(String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())));
if (coreDatasource == null) {
BusinessException.throwException("数据源不存在");
}
+ coreDatasource.setConfiguration(String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())));
// 解析 tableData JSON 字符串 "{ \"tableName\": \"user\", \"data\": [ { \"fieldName\": \"id\", \"fieldType\": \"varchar\", \"IsPrimaryKey\": true, \"fieldValue\": \"0001\" }, { \"fieldName\": \"name\", \"fieldType\": \"varchar\", \"fieldValue\": \"张三\" } ] }";
Map dataMap = JsonUtil.parseObject(tableData, Map.class);
String tableName = (String) dataMap.get("tableName");
@@ -99,6 +99,7 @@ public class DynamicDataServiceImpl implements IDynamicDataService {
if (coreDatasource == null) {
BusinessException.throwException("数据源不存在");
}
+ coreDatasource.setConfiguration(String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())));
// 解析 JSON 数据
//condtion={ \"tableName\": \"user\", key:[{ \"fieldName\": \"id\",\"fieldValue\": \"0001\"]
Map dataMap = JsonUtil.parseObject(condtion, Map.class);
@@ -182,6 +183,7 @@ public class DynamicDataServiceImpl implements IDynamicDataService {
if (coreDatasource == null) {
BusinessException.throwException("数据源不存在");
}
+ coreDatasource.setConfiguration(String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())));
//String tableDataJson = "{ \"tableName\": \"user\", key:[{ \"fieldName\": \"id\",\"fieldValue\": \"0001\"], \"data\": [ { \"fieldName\": \"name\", \"fieldType\": \"varchar\", \"fieldValue\": \"李四\" } ] }";
// 解析 JSON 数据
Map dataMap = JsonUtil.parseObject(tableData, Map.class);
@@ -242,7 +244,7 @@ public class DynamicDataServiceImpl implements IDynamicDataService {
CoreDatasource coreDatasource = coreDatasourceMapper.selectById(datasourceId);
if (coreDatasource == null) {
BusinessException.throwException("数据源不存在"); }
-
+ coreDatasource.setConfiguration(String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())));
// 解析 JSON 数据
//String tableDataJson = "{ \"tableName\": \"user\", key:[{ \"fieldName\": \"id\",\"fieldValue\": \"0001\"] }";
Map dataMap = JsonUtil.parseObject(condtion, Map.class);
diff --git a/backend/src/main/java/com/stdproject/service/manage/EngineManage.java b/backend/src/main/java/com/stdproject/service/manage/EngineManage.java
index f05885b..829f6ab 100644
--- a/backend/src/main/java/com/stdproject/service/manage/EngineManage.java
+++ b/backend/src/main/java/com/stdproject/service/manage/EngineManage.java
@@ -7,6 +7,7 @@ import com.stdproject.mapper.CoreDatasourceMapper;
import com.stdproject.mapper.CoreDeEngineMapper;
import com.stdproject.service.type.H2;
import com.stdproject.service.type.Mysql;
+import com.stdproject.utils.EncryptUtils;
import io.gisbi.exception.DEException;
import io.gisbi.extensions.datasource.dto.DatasourceDTO;
import io.gisbi.extensions.datasource.dto.DatasourceRequest;
@@ -101,44 +102,31 @@ public class EngineManage {
public void initSimpleEngine() throws Exception {
initLocalDataSource();
QueryWrapper queryWrapper = new QueryWrapper<>();
- if (ModelUtils.isDesktop()) {
- queryWrapper.eq("type", engineType.h2.name());
- } else {
- queryWrapper.eq("type", engineType.mysql.name());
- }
+ queryWrapper.eq("type", engineType.mysql.name());
List deEngines = deEngineMapper.selectList(queryWrapper);
if (!CollectionUtils.isEmpty(deEngines)) {
return;
}
CoreDeEngine engine = new CoreDeEngine();
- if (ModelUtils.isDesktop()) {
- engine.setType(engineType.h2.name());
- H2 h2 = new H2();
- h2.setJdbc(engineUrl);
- h2.setDataBase("PUBLIC");
- h2.setUsername(env.getProperty("spring.datasource.username"));
- h2.setPassword(env.getProperty("spring.datasource.password"));
- engine.setConfiguration(JsonUtil.toJSONString(h2).toString());
- } else {
- engine.setType(engineType.mysql.name());
- Mysql mysqlConfiguration = new Mysql();
- Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:mysql://(.*):(\\d+)/(.*)");
- Matcher matcher = WITH_SQL_FRAGMENT.matcher(env.getProperty("spring.datasource.url"));
- if (!matcher.find()) {
- return;
- }
- mysqlConfiguration.setHost(matcher.group(1));
- mysqlConfiguration.setPort(Integer.valueOf(matcher.group(2)));
- String[] databasePrams = matcher.group(3).split("\\?");
- mysqlConfiguration.setDataBase(databasePrams[0]);
- if (databasePrams.length == 2) {
- mysqlConfiguration.setExtraParams(databasePrams[1]);
- }
- mysqlConfiguration.setUsername(env.getProperty("spring.datasource.username"));
- mysqlConfiguration.setPassword(env.getProperty("spring.datasource.password"));
- engine.setConfiguration(JsonUtil.toJSONString(mysqlConfiguration).toString());
+ engine.setType(engineType.mysql.name());
+ Mysql mysqlConfiguration = new Mysql();
+ Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:mysql://(.*):(\\d+)/(.*)");
+ Matcher matcher = WITH_SQL_FRAGMENT.matcher(env.getProperty("spring.datasource.url"));
+ if (!matcher.find()) {
+ return;
}
+ mysqlConfiguration.setHost(matcher.group(1));
+ mysqlConfiguration.setPort(Integer.valueOf(matcher.group(2)));
+ String[] databasePrams = matcher.group(3).split("\\?");
+ mysqlConfiguration.setDataBase(databasePrams[0]);
+ if (databasePrams.length == 2) {
+ mysqlConfiguration.setExtraParams(databasePrams[1]);
+ }
+ mysqlConfiguration.setUsername(env.getProperty("spring.datasource.username"));
+ mysqlConfiguration.setPassword(env.getProperty("spring.datasource.password"));
+ String config=JsonUtil.toJSONString(mysqlConfiguration).toString();
+ engine.setConfiguration((String) EncryptUtils.aesEncrypt(config));
engine.setName("默认引擎");
engine.setDescription("默认引擎");
deEngineMapper.insert(engine);
@@ -163,7 +151,7 @@ public class EngineManage {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", 985188400292302848L);
queryWrapper.ne("create_time", 1715053684176L);
- if (!datasourceMapper.exists(queryWrapper) && !ModelUtils.isDesktop()) {
+ if (!datasourceMapper.exists(queryWrapper)) {
Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:mysql://(.*):(\\d+)/(.*)\\?(.*)");
Matcher matcher = WITH_SQL_FRAGMENT.matcher(env.getProperty("spring.datasource.url"));
if (!matcher.find()) {
@@ -182,7 +170,8 @@ public class EngineManage {
initDatasource.setName("Demo");
initDatasource.setType("mysql");
initDatasource.setPid(0L);
- initDatasource.setConfiguration(JsonUtil.toJSONString(configuration).toString());
+ String config=JsonUtil.toJSONString(configuration).toString();
+ initDatasource.setConfiguration((String) EncryptUtils.aesEncrypt(config));
initDatasource.setCreateTime(System.currentTimeMillis());
initDatasource.setUpdateTime(System.currentTimeMillis());
initDatasource.setCreateBy("1");
diff --git a/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java b/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java
index 054a764..cae0212 100644
--- a/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java
+++ b/backend/src/main/java/com/stdproject/service/provider/CalciteProvider.java
@@ -8,6 +8,7 @@ import com.stdproject.entity.CoreDriver;
import com.stdproject.mapper.CoreDatasourceMapper;
import com.stdproject.service.manage.EngineManage;
import com.stdproject.service.type.*;
+import com.stdproject.utils.EncryptUtils;
import com.stdproject.utils.FieldUtils;
import io.gisbi.constant.SQLConstants;
import io.gisbi.exception.DEException;
@@ -746,17 +747,11 @@ public class CalciteProvider extends Provider {
private Connection getCalciteConnection() {
registerDriver();
Properties info = new Properties();
-// info.setProperty(CalciteConnectionProperty.LEX.camelName(), "JAVA");
-// info.setProperty(CalciteConnectionProperty.FUN.camelName(), "all");
-// info.setProperty(CalciteConnectionProperty.CASE_SENSITIVE.camelName(), "false");
-// info.setProperty(CalciteConnectionProperty.PARSER_FACTORY.camelName(), "org.apache.calcite.sql.parser.impl.SqlParserImpl#FACTORY");
-// info.setProperty(CalciteConnectionProperty.DEFAULT_NULL_COLLATION.camelName(), NullCollation.LAST.name());
- info.setProperty("lex", "JAVA");
- info.setProperty("fun", "all");
- info.setProperty("caseSensitive", "false");
- info.setProperty("parserFactory", "org.apache.calcite.sql.parser.impl.SqlParserImpl#FACTORY");
- info.setProperty("defaultNullCollation", NullCollation.LAST.name());
- info.setProperty("remarks", "true");
+ info.setProperty(CalciteConnectionProperty.LEX.camelName(), "JAVA");
+ info.setProperty(CalciteConnectionProperty.FUN.camelName(), "all");
+ info.setProperty(CalciteConnectionProperty.CASE_SENSITIVE.camelName(), "false");
+ info.setProperty(CalciteConnectionProperty.PARSER_FACTORY.camelName(), "org.apache.calcite.sql.parser.impl.SqlParserImpl#FACTORY");
+ info.setProperty(CalciteConnectionProperty.DEFAULT_NULL_COLLATION.camelName(), NullCollation.LAST.name());
Connection connection = null;
try {
Class.forName("org.apache.calcite.jdbc.Driver");
@@ -1348,6 +1343,7 @@ public class CalciteProvider extends Provider {
Map dsMap = new HashMap<>();
DatasourceSchemaDTO datasourceSchemaDTO = new DatasourceSchemaDTO();
BeanUtils.copyBean(datasourceSchemaDTO, coreDatasource);
+ datasourceSchemaDTO.setConfiguration(String.valueOf(EncryptUtils.aesDecrypt(coreDatasource.getConfiguration())));
datasourceSchemaDTO.setSchemaAlias(String.format(SQLConstants.SCHEMA, datasourceSchemaDTO.getId()));
dsMap.put(datasourceSchemaDTO.getId(), datasourceSchemaDTO);
commonThreadPool.addTask(() -> {
diff --git a/backend/src/main/java/com/stdproject/utils/JwtUtils.java b/backend/src/main/java/com/stdproject/utils/JwtUtils.java
index 4f6382d..6d9c921 100644
--- a/backend/src/main/java/com/stdproject/utils/JwtUtils.java
+++ b/backend/src/main/java/com/stdproject/utils/JwtUtils.java
@@ -23,13 +23,13 @@ import java.util.Map;
public class JwtUtils {
@Value("${spring.security.jwt.secret}")
- private String secret;
+ private String jwtSecret;
@Value("${spring.security.jwt.expiration-ms}")
- private Long expirationMs;
+ private Long jwtExpirationMs;
@Value("${spring.security.jwt.refresh-expiration-ms}")
- private Long refreshExpirationMs;
+ private Long jwtRefreshExpirationMs;
/**
* 生成JWT令牌
@@ -54,7 +54,7 @@ public class JwtUtils {
*/
private String createToken(Map claims, String subject) {
Date now = new Date();
- Date expiryDate = new Date(now.getTime() + expirationMs);
+ Date expiryDate = new Date(now.getTime() + jwtExpirationMs);
return Jwts.builder()
.setClaims(claims)
@@ -70,7 +70,7 @@ public class JwtUtils {
*/
private String createRefreshToken(Map claims, String subject) {
Date now = new Date();
- Date expiryDate = new Date(now.getTime() + refreshExpirationMs);
+ Date expiryDate = new Date(now.getTime() + jwtRefreshExpirationMs);
return Jwts.builder()
.setClaims(claims)
@@ -182,7 +182,7 @@ public class JwtUtils {
* @return 签名密钥
*/
private SecretKey getSigningKey() {
- byte[] keyBytes = secret.getBytes();
+ byte[] keyBytes = jwtSecret.getBytes();
return Keys.hmacShaKeyFor(keyBytes);
}
diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml
index 6ac7405..9f9acdd 100644
--- a/backend/src/main/resources/application.yml
+++ b/backend/src/main/resources/application.yml
@@ -33,19 +33,6 @@ spring:
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
-# url: jdbc:sqlite:D:/Trae_space/StdProject/backend/db/project.db
-# username: # SQLite 不需要用户名
-# password: # SQLite 不需要密码
-# driver-class-name: org.sqlite.JDBC
-# hikari:
-# pool-name: StdProjectHikariCP
-# minimum-idle: 5
-# maximum-pool-size: 20
-# auto-commit: true
-# idle-timeout: 30000
-# max-lifetime: 1800000
-# connection-timeout: 30000
-# connection-test-query: SELECT 1
cache:
jcache:
@@ -57,8 +44,8 @@ spring:
jwt:
enabled: ${JWT_ENABLED:false} # 控制是否启用JWT认证
secret: ${JWT_SECRET:YourJWTSecretKeyForStdProjectBackendApplicationWhichIsVeryLongAndSecure2024!@#$%^&*()}
- expiration-ms: ${JWT_EXPIRATION:1800000} # Token 过期时间 (例如: 24小时)
- refresh-expiration-ms: ${JWT_REFRESH_EXPIRATION:1800000} # 刷新Token过期时间 (例如: 30分钟)
+ expiration-ms: ${JWT_EXPIRATION:86400000} # Token 过期时间 (例如: 24小时)
+ refresh-expiration-ms: ${JWT_REFRESH_EXPIRATION:604800000} # 刷新Token过期时间 (例如: 7天)
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml # MyBatis Mapper XML文件位置
diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..c7d3485
--- /dev/null
+++ b/backend/src/main/resources/i18n/messages.properties
@@ -0,0 +1,2 @@
+login.validator.name1=
+login.validator.pwd1=
\ No newline at end of file
diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties
new file mode 100644
index 0000000..13c7b02
--- /dev/null
+++ b/backend/src/main/resources/i18n/messages_en_US.properties
@@ -0,0 +1,205 @@
+
+login.validator.name1=Username/Email/Phone number cannot be empty
+login.validator.pwd1=Password cannot be empty
+
+i18n_menu.home=Home
+i18n_menu.workbranch=Workbench
+i18n_menu.visualized=Visualization
+i18n_menu.template=Template
+i18n_menu.application=Application
+i18n_menu.system=System Management
+i18n_menu.template-market=Template Market
+i18n_menu.template-setting=Template Management
+i18n_menu.view=Data Display
+i18n_menu.data=Data Preparation
+i18n_menu.panel=Dashboard
+i18n_menu.data-filling-manage=Data Reporting
+i18n_menu.screen=Data Screen
+i18n_menu.dataset=Dataset
+i18n_menu.datasource=Data Source
+i18n_menu.user=User Management
+i18n_menu.org=Organization Management
+i18n_menu.auth=Permission Configuration
+i18n_menu.report=Scheduled Report
+i18n_menu.sync=Synchronization Management
+i18n_menu.association=Bloodline Analysis
+i18n_menu.threshold=Alert Management
+i18n_menu.webhook=Webhook Management
+i18n_menu.summary=Overview
+i18n_menu.ds=Data Connection Management
+i18n_menu.task=Task Management
+i18n_menu.embedded=Embedded Management
+i18n_menu.plugin=Plugin Management
+i18n_menu.platform=Platform Integration
+i18n_menu.appearance=Appearance Configuration
+i18n_menu.sysVariable=System Variables
+i18n_menu.sysTypeface=Font Management
+i18n_menu.font=Font Management
+i18n_menu.msg-fill=Reporting Task
+i18n_field_name_repeat=Duplicate field name:
+i18n_pid_not_eq_id=The target for moving cannot be itself or its subdirectory
+i18n_ds_name_exists=Name is duplicated under this group
+i18n_table_id_can_not_empty=Query node cannot be empty
+i18n_no_fields=Fields cannot be empty
+i18n_no_field=The field does not exist
+i18n_union_ds_no_checked=No fields are selected
+i18n_field_name_duplicated=Field name is duplicated
+i18n_can_not_cross_ds=Cross dataset operations are not supported
+i18n_dataset_ds_error=The data source used by the current dataset has been deleted
+i18n_union_field_can_not_empty=Association field cannot be empty
+i18n_table_duplicate=The same node needs to be dragged in again to continue creating a new dataset
+i18n_no_column_permission=No column permissions
+i18n_fetch_error=SQL execution failed, please check if the table, fields, association relationships, etc., are correct and edit again.
+i18n_no_datasource_permission=No data source access permissions
+i18n_no_dataset_permission=No dataset access permissions
+i18n_not_full=The current data source does not support full join
+
+i18n_field_circular_ref=Fields have circular references
+
+i18n_chart_not_handler=Unable to handle this chart type
+i18n_chart_delete=Chart does not exist
+i18n_no_ds=Dataset does not exist or no permissions
+i18n_datasource_delete=Data source does not exist
+i18n_gauge_field_change=The field used has changed, please edit again
+i18n_gauge_field_delete=The field used has been deleted, please edit again
+i18n_no_id=ID cannot be empty
+i18n_name_limit_100=Name cannot exceed 100 characters
+i18n_field_circular_error=Field parsing error, possible reasons: field has been deleted, calculation field reference level is too deep, circular references exist, etc., please check the table nodes and fields and edit again.
+
+i18n_invalid_ds=Data source is invalid
+
+i18n_user_disable=User has been disabled and cannot log in
+i18n_login_name_pwd_err=Username or password is incorrect
+i18n_error_login_type=Login type error
+i18n_schema_is_empty=Schema is empty!
+i18n_table_name_repeat=Name is duplicated:
+i18n_sql_not_empty=SQL cannot be empty
+i18n_menu.parameter=System Parameters
+i18n_user_old_pwd_error=Original password is incorrect
+i18n_menu.toolbox-log=Operation Logs
+
+i18n_year=Year
+i18n_month=Month
+i18n_day=Day
+i18n_hour=Hour
+i18n_minute=Minute
+i18n_second=Second
+
+i18n_no_datasource_permission_to_create_column=No data source access permissions, unable to create table fields
+i18n_df_folder_cannot_to_search=Folders cannot query data
+i18n_df_no_primary_key=No primary key
+i18n_df_cannot_operate_folder=Cannot operate on folders
+i18n_df_cannot_be_none=[%s] cannot be empty
+i18n_df_value_cannot_be_none=[%s] value: %s cannot be empty
+i18n_df_value_exists_in_database=[%s] value: %s already exists in the database, cannot be duplicated
+i18n_df_data=Data
+i18n_df_start=Start
+i18n_df_end=End
+i18n_df_datasource_not_found=No data source found
+i18n_df_datasource_does_not_enable_data_filling=This data source has not enabled data reporting configuration
+i18n_df_builtin_datasource=Built-in database
+i18n_df_folder_required=Folder is required
+i18n_df_form_not_exists=Form does not exist
+i18n_df_name_can_not_empty=Name cannot be empty
+i18n_df_template=Template
+i18n_df_task_status_is_null_or_finished=Task status is null or completed
+i18n_df_task_need_task_id=Task ID must be specified
+i18n_df_not_current_task_user=Not the target user for the current task
+i18n_df_miss_parameter=Missing parameter
+i18n_df_no_running_instance=No running instances for the current task
+i18n_df_value=Value
+i18n_df_format_error=Format parsing error
+i18n_df_cannot_earlier_than=Cannot be earlier than
+i18n_df_cannot_be_all_null=Cannot have only one empty
+i18n_df_value_not_in_range=Value is not within the range
+i18n_df_value_value_not_in_range=Value: %s is not within the range
+i18n_df_required=Required
+i18n_df_must_unique=Duplicate values are not allowed
+i18n_df_excel_parsing_error=Excel parsing error
+i18n_df_excel_is_empty=This Excel has no data
+i18n_df_excel_template_column_not_fit=Template field count does not match
+i18n_df_selection=Option value is
+i18n_df_date_format=Date format
+i18n_df_integer=Integer
+i18n_df_decimal=Decimal
+i18n_df_multiple_value_split=Multiple values are separated by semicolon "; "
+i18n_df_email_type=Email format
+i18n_df_phone_type=Phone number format
+i18n_df_lt_check=Value needs to be less than %s: %s
+i18n_df_gt_check=Value needs to be greater than %s: %s
+i18n_df_le_check=Value needs to be less than or equal to %s: %s
+i18n_df_ge_check=Value needs to be greater than or equal to %s: %s
+i18n_df_column_exists=The column: %s exists
+
+i18n_wrong_email=Email format is incorrect
+i18n_wrong_tel=Phone number format is incorrect
+
+
+i18n_copilot_cross_ds_error=Cross-source datasets do not support this feature
+
+i18n_template_recommend=Recommended
+i18n_template_recent=Recently Used
+
+i18n_default_org=Default Organization
+i18n_org_admin=Organization Admin
+i18n_ordinary_role=Ordinary User
+i18n_sys_admin=System Admin
+
+i18n_threshold_logic_eq=Equal to
+i18n_threshold_logic_not_eq=Not equal to
+i18n_threshold_logic_lt=Less than
+i18n_threshold_logic_le=Less than or equal to
+i18n_threshold_logic_gt=Greater than
+i18n_threshold_logic_ge=Greater than or equal to
+i18n_threshold_logic_in=Belong to
+i18n_threshold_logic_not_in=Do not belong to
+i18n_threshold_logic_like=Contains
+i18n_threshold_logic_not_like=Does not contain
+i18n_threshold_logic_null=Empty
+i18n_threshold_logic_not_null=Not empty
+i18n_threshold_logic_empty=Empty string
+i18n_threshold_logic_not_empty=Non-empty string
+i18n_threshold_logic_between=Range is
+i18n_threshold_logic_and=And
+i18n_threshold_logic_or=Or
+i18n_threshold_max=Maximum value
+i18n_threshold_min=Minimum value
+i18n_threshold_average=Average value
+i18n_time_year=Year
+i18n_time_month=Month
+i18n_time_date=Day
+i18n_time_hour=Hour
+i18n_time_minute=Minute
+i18n_time_second=Second
+i18n_time_ago=Ago
+i18n_time_later=Later
+i18n_time_year_current=Current year
+i18n_time_year_last=Last year
+i18n_time_year_next=Next year
+i18n_time_month_current=Current month
+i18n_time_month_last=Last month
+i18n_time_month_next=Next month
+i18n_time_month_start=Beginning of the year
+i18n_time_month_end=End of the year
+i18n_time_date_current=Today
+i18n_time_date_last=Yesterday
+i18n_time_date_next=Tomorrow
+i18n_time_date_start=Beginning of the month
+i18n_time_date_end=End of the month
+
+i18n_dataset_create_error=Create error,please try again
+i18n_dataset_ds_delete=Datasource deleted,this dataset can not be show
+i18n_dataset_plugin_error=Datasource plugin is not exist
+i18n_dataset_cross_error=Dataset with more than two data sources is not supported
+i18n_board=Board
+i18n_invalid_connection=Invalid connection.
+i18n_check_datasource_connection=Please check the validity of the datasource.
+
+i18n_datasource_not_exists=Datasource not exists!
+
+i18n_geo_exists=An area with the same name already exists\uFF01
+i18n_geo_sub_exists=A sub-area with the same name already exists\uFF01
+i18n_user_new_pwd_error=Password format: 8-20 characters and must include at least one uppercase letter, one lowercase letter, one number, and one special character.
+i18n_user_pwd_same_error=Old and new passwords cannot be the same
+
+i18n_copilot_ds=Only supports MySQL datasource
diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties
new file mode 100644
index 0000000..e7122b8
--- /dev/null
+++ b/backend/src/main/resources/i18n/messages_zh_CN.properties
@@ -0,0 +1,204 @@
+login.validator.name1=\u8D26\u53F7/\u90AE\u7BB1/\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A
+login.validator.pwd1=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
+
+i18n_menu.home=\u9996\u9875
+i18n_menu.workbranch=\u5DE5\u4F5C\u53F0
+i18n_menu.visualized=\u53EF\u89C6\u5316
+i18n_menu.template=\u6A21\u7248
+i18n_menu.application=\u5E94\u7528
+i18n_menu.system=\u7CFB\u7EDF\u7BA1\u7406
+i18n_menu.template-market=\u6A21\u677F\u5E02\u573A
+i18n_menu.template-setting=\u6A21\u677F\u7BA1\u7406
+i18n_menu.view=\u6570\u636E\u5C55\u793A
+i18n_menu.data=\u6570\u636E\u51C6\u5907
+i18n_menu.panel=\u4EEA\u8868\u677F
+i18n_menu.data-filling-manage=\u6570\u636E\u586B\u62A5
+i18n_menu.screen=\u6570\u636E\u5927\u5C4F
+i18n_menu.dataset=\u6570\u636E\u96C6
+i18n_menu.datasource=\u6570\u636E\u6E90
+i18n_menu.user=\u7528\u6237\u7BA1\u7406
+i18n_menu.org=\u7EC4\u7EC7\u7BA1\u7406
+i18n_menu.auth=\u6743\u9650\u914D\u7F6E
+i18n_menu.report=\u5B9A\u65F6\u62A5\u544A
+i18n_menu.sync=\u540C\u6B65\u7BA1\u7406
+i18n_menu.association=\u8840\u7F18\u5206\u6790
+i18n_menu.threshold=\u544A\u8B66\u7BA1\u7406
+i18n_menu.webhook=Webhook \u7BA1\u7406
+i18n_menu.summary=\u6982\u89C8
+i18n_menu.ds=\u6570\u636E\u8FDE\u63A5\u7BA1\u7406
+i18n_menu.task=\u4EFB\u52A1\u7BA1\u7406
+i18n_menu.embedded=\u5D4C\u5165\u5F0F\u7BA1\u7406
+i18n_menu.plugin=\u63D2\u4EF6\u7BA1\u7406
+i18n_menu.platform=\u5E73\u53F0\u5BF9\u63A5
+i18n_menu.appearance=\u5916\u89C2\u914D\u7F6E
+i18n_menu.sysVariable=\u7CFB\u7EDF\u53D8\u91CF
+i18n_menu.sysTypeface=\u5B57\u4F53\u7BA1\u7406
+i18n_menu.font=\u5B57\u4F53\u7BA1\u7406
+i18n_menu.msg-fill=\u586B\u62A5\u4EFB\u52A1
+i18n_field_name_repeat=\u6709\u91CD\u590D\u5B57\u6BB5\u540D\uFF1A
+i18n_pid_not_eq_id=\u79FB\u52A8\u76EE\u6807\u4E0D\u80FD\u662F\u81EA\u5DF1\u6216\u5B50\u76EE\u5F55
+i18n_ds_name_exists=\u8BE5\u5206\u7EC4\u4E0B\u540D\u79F0\u91CD\u590D
+i18n_table_id_can_not_empty=\u67E5\u8BE2\u8282\u70B9\u4E0D\u80FD\u4E3A\u7A7A
+i18n_no_fields=\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A
+i18n_no_field=\u8BE5\u5B57\u6BB5\u4E0D\u5B58\u5728
+i18n_union_ds_no_checked=\u6CA1\u6709\u5B57\u6BB5\u9009\u4E2D
+i18n_field_name_duplicated=\u5B57\u6BB5\u540D\u91CD\u590D
+i18n_can_not_cross_ds=\u4E0D\u652F\u6301\u8DE8\u6570\u636E\u96C6\u64CD\u4F5C
+i18n_dataset_ds_error=\u5F53\u524D\u6570\u636E\u96C6\u7528\u5230\u7684\u6570\u636E\u6E90\u5DF2\u88AB\u5220\u9664
+i18n_union_field_can_not_empty=\u5173\u8054\u5B57\u6BB5\u4E0D\u80FD\u4E3A\u7A7A
+i18n_table_duplicate=\u76F8\u540C\u8282\u70B9\u9700\u91CD\u65B0\u62D6\u5165\u624D\u80FD\u7EE7\u7EED\u65B0\u5EFA\u6570\u636E\u96C6
+i18n_no_column_permission=\u6CA1\u6709\u5217\u6743\u9650
+i18n_fetch_error=SQL\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8868\u3001\u5B57\u6BB5\u3001\u5173\u8054\u5173\u7CFB\u7B49\u4FE1\u606F\u662F\u5426\u6B63\u786E\u5E76\u91CD\u65B0\u7F16\u8F91\u3002
+i18n_no_datasource_permission=\u65E0\u6570\u636E\u6E90\u8BBF\u95EE\u6743\u9650
+i18n_no_dataset_permission=\u65E0\u6570\u636E\u96C6\u8BBF\u95EE\u6743\u9650
+i18n_not_full=\u5F53\u524D\u6570\u636E\u6E90\u4E0D\u652F\u6301\u5168\u8FDE\u63A5
+
+i18n_field_circular_ref=\u5B57\u6BB5\u5B58\u5728\u5FAA\u73AF\u5F15\u7528
+
+i18n_chart_not_handler=\u65E0\u6CD5\u5904\u7406\u8BE5\u56FE\u8868\u7C7B\u578B
+i18n_chart_delete=\u56FE\u8868\u4E0D\u5B58\u5728
+i18n_no_ds=\u6570\u636E\u96C6\u4E0D\u5B58\u5728\u6216\u6CA1\u6709\u6743\u9650
+i18n_datasource_delete=\u6570\u636E\u6E90\u4E0D\u5B58\u5728
+i18n_gauge_field_change=\u6240\u7528\u5B57\u6BB5\u53D1\u751F\u53D8\u66F4\uFF0C\u8BF7\u91CD\u65B0\u7F16\u8F91
+i18n_gauge_field_delete=\u6240\u7528\u5B57\u6BB5\u5DF2\u5220\u9664\uFF0C\u8BF7\u91CD\u65B0\u7F16\u8F91
+i18n_no_id=id\u4E0D\u80FD\u4E3A\u7A7A
+i18n_name_limit_100=\u540D\u79F0\u4E0D\u80FD\u8D85\u8FC7100\u5B57\u7B26
+i18n_field_circular_error=\u5B57\u6BB5\u89E3\u6790\u9519\u8BEF\uFF0C\u53EF\u80FD\u539F\u56E0\uFF1A\u5B57\u6BB5\u5DF2\u5220\u9664\u3001\u8BA1\u7B97\u5B57\u6BB5\u5F15\u7528\u5C42\u7EA7\u8FC7\u6DF1\u3001\u5B58\u5728\u5FAA\u73AF\u5F15\u7528\u7B49\uFF0C\u8BF7\u68C0\u67E5\u8868\u8282\u70B9\u548C\u5B57\u6BB5\u5E76\u91CD\u65B0\u7F16\u8F91\u3002
+
+i18n_invalid_ds=\u6570\u636E\u6E90\u65E0\u6548
+
+i18n_user_disable=\u7528\u6237\u5DF2\u88AB\u7981\u7528\uFF0C\u65E0\u6CD5\u767B\u5F55
+i18n_login_name_pwd_err=\u7528\u6237\u540D\u6216\u5BC6\u7801\u9519\u8BEF
+i18n_error_login_type=\u767B\u5F55\u7C7B\u578B\u9519\u8BEF
+i18n_schema_is_empty=schema \u4E3A\u7A7A\uFF01
+i18n_table_name_repeat=\u540D\u79F0\u91CD\u590D:
+i18n_sql_not_empty=sql \u4E0D\u80FD\u4E3A\u7A7A
+i18n_menu.parameter=\u7CFB\u7EDF\u53C2\u6570
+i18n_user_old_pwd_error=\u539F\u59CB\u5BC6\u7801\u9519\u8BEF
+i18n_menu.toolbox-log=\u64CD\u4F5C\u65E5\u5FD7
+
+i18n_year=\u5E74
+i18n_month=\u6708
+i18n_day=\u5929
+i18n_hour=\u5C0F\u65F6
+i18n_minute=\u5206\u949F
+i18n_second=\u79D2
+
+i18n_no_datasource_permission_to_create_column=\u65E0\u6570\u636E\u6E90\u8BBF\u95EE\u6743\u9650\uFF0C\u65E0\u6CD5\u521B\u5EFA\u8868\u5B57\u6BB5
+i18n_df_folder_cannot_to_search=\u6587\u4EF6\u5939\u4E0D\u80FD\u67E5\u8BE2\u6570\u636E
+i18n_df_no_primary_key=\u6CA1\u6709\u4E3B\u952E
+i18n_df_cannot_operate_folder=\u4E0D\u80FD\u64CD\u4F5C\u6587\u4EF6\u5939
+i18n_df_cannot_be_none=[%s] \u4E0D\u80FD\u4E3A\u7A7A
+i18n_df_value_cannot_be_none=[%s] \u503C: %s \u4E0D\u80FD\u4E3A\u7A7A
+i18n_df_value_exists_in_database=[%s] \u503C: %s \u5728\u6570\u636E\u5E93\u4E2D\u5DF2\u5B58\u5728, \u4E0D\u80FD\u91CD\u590D
+i18n_df_data=\u6570\u636E
+i18n_df_start=\u5F00\u59CB
+i18n_df_end=\u7ED3\u675F
+i18n_df_datasource_not_found=\u6CA1\u6709\u627E\u5230\u6570\u636E\u6E90
+i18n_df_datasource_does_not_enable_data_filling=\u8BE5\u6570\u636E\u6E90\u6CA1\u6709\u542F\u7528\u6570\u636E\u586B\u62A5\u914D\u7F6E
+i18n_df_builtin_datasource=\u5185\u5EFA\u6570\u636E\u5E93
+i18n_df_folder_required=\u76EE\u5F55\u5FC5\u9009
+i18n_df_form_not_exists=\u8868\u5355\u4E0D\u5B58\u5728
+i18n_df_name_can_not_empty=\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
+i18n_df_template=\u6A21\u677F
+i18n_df_task_status_is_null_or_finished=\u4EFB\u52A1\u72B6\u6001\u4E3A\u7A7A\u6216\u5DF2\u5B8C\u6210
+i18n_df_task_need_task_id=\u9700\u6307\u5B9A\u4EFB\u52A1ID
+i18n_df_not_current_task_user=\u4E0D\u662F\u5F53\u524D\u4EFB\u52A1\u7684\u76EE\u6807\u7528\u6237
+i18n_df_miss_parameter=\u7F3A\u5931\u53C2\u6570
+i18n_df_no_running_instance=\u5F53\u524D\u4EFB\u52A1\u6682\u65F6\u65E0\u8FD0\u884C\u5B9E\u4F8B
+i18n_df_value=\u503C
+i18n_df_format_error=\u683C\u5F0F\u89E3\u6790\u9519\u8BEF
+i18n_df_cannot_earlier_than=\u4E0D\u80FD\u65E9\u4E8E
+i18n_df_cannot_be_all_null=\u4E0D\u80FD\u53EA\u6709\u4E00\u4E2A\u4E3A\u7A7A
+i18n_df_value_not_in_range=\u503C\u4E0D\u5728\u8303\u56F4\u5185
+i18n_df_value_value_not_in_range=\u503C: %s \u4E0D\u5728\u8303\u56F4\u5185
+i18n_df_required=\u5FC5\u586B
+i18n_df_must_unique=\u4E0D\u5141\u8BB8\u91CD\u590D\u503C
+i18n_df_excel_parsing_error=Excel\u89E3\u6790\u9519\u8BEF
+i18n_df_excel_is_empty=\u8BE5Excel\u6CA1\u6709\u6570\u636E
+i18n_df_excel_template_column_not_fit=\u6A21\u677F\u5B57\u6BB5\u4E2A\u6570\u4E0D\u5339\u914D
+i18n_df_selection=\u9009\u9879\u503C\u4E3A
+i18n_df_date_format=\u65E5\u671F\u683C\u5F0F
+i18n_df_integer=\u6574\u5F62\u6570\u5B57
+i18n_df_decimal=\u5C0F\u6570\u6570\u5B57
+i18n_df_multiple_value_split=\u591A\u4E2A\u503C\u4F7F\u7528\u5206\u53F7";"\u5206\u5272
+i18n_df_email_type=\u90AE\u7BB1\u683C\u5F0F
+i18n_df_phone_type=\u624B\u673A\u53F7\u683C\u5F0F
+i18n_df_lt_check=\u503C\u9700\u8981\u5C0F\u4E8E %s: %s
+i18n_df_gt_check=\u503C\u9700\u8981\u5927\u4E8E %s: %s
+i18n_df_le_check=\u503C\u9700\u8981\u5C0F\u4E8E\u7B49\u4E8E %s: %s
+i18n_df_ge_check=\u503C\u9700\u8981\u5927\u4E8E\u7B49\u4E8E %s: %s
+i18n_df_column_exists=\u5B57\u6BB5: %s \u5DF2\u5B58\u5728
+
+i18n_wrong_email=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
+i18n_wrong_tel=\u624B\u673A\u53F7\u7801\u683C\u5F0F\u9519\u8BEF
+
+
+i18n_copilot_cross_ds_error=\u8DE8\u6E90\u6570\u636E\u96C6\u4E0D\u652F\u6301\u8BE5\u529F\u80FD
+
+i18n_template_recommend=\u63A8\u8350
+i18n_template_recent=\u6700\u8FD1\u4F7F\u7528
+
+i18n_default_org=\u9ED8\u8BA4\u7EC4\u7EC7
+i18n_org_admin=\u7EC4\u7EC7\u7BA1\u7406\u5458
+i18n_ordinary_role=\u666E\u901A\u7528\u6237
+i18n_sys_admin=\u7CFB\u7EDF\u7BA1\u7406\u5458
+
+i18n_threshold_logic_eq=\u7B49\u4E8E
+i18n_threshold_logic_not_eq=\u4E0D\u7B49\u4E8E
+i18n_threshold_logic_lt=\u5C0F\u4E8E
+i18n_threshold_logic_le=\u5C0F\u4E8E\u7B49\u4E8E
+i18n_threshold_logic_gt=\u5927\u4E8E
+i18n_threshold_logic_ge=\u5927\u4E8E\u7B49\u4E8E
+i18n_threshold_logic_in=\u5C5E\u4E8E
+i18n_threshold_logic_not_in=\u4E0D\u5C5E\u4E8E
+i18n_threshold_logic_like=\u5305\u542B
+i18n_threshold_logic_not_like=\u4E0D\u5305\u542B
+i18n_threshold_logic_null=\u7A7A
+i18n_threshold_logic_not_null=\u4E0D\u7A7A
+i18n_threshold_logic_empty=\u7A7A\u5B57\u7B26\u4E32
+i18n_threshold_logic_not_empty=\u975E\u7A7A\u5B57\u7B26\u4E32
+i18n_threshold_logic_between=\u8303\u56F4\u662F
+i18n_threshold_logic_and=\u4E14
+i18n_threshold_logic_or=\u6216
+i18n_threshold_max=\u6700\u5927\u503C
+i18n_threshold_min=\u6700\u5C0F\u503C
+i18n_threshold_average=\u5E73\u5747\u503C
+i18n_time_year=\u5E74
+i18n_time_month=\u6708
+i18n_time_date=\u65E5
+i18n_time_hour=\u65F6
+i18n_time_minute=\u5206
+i18n_time_second=\u79D2
+i18n_time_ago=\u524D
+i18n_time_later=\u540E
+i18n_time_year_current=\u5F53\u5E74
+i18n_time_year_last=\u53BB\u5E74
+i18n_time_year_next=\u660E\u5E74
+i18n_time_month_current=\u5F53\u6708
+i18n_time_month_last=\u4E0A\u4E2A\u6708
+i18n_time_month_next=\u4E0B\u4E2A\u6708
+i18n_time_month_start=\u5E74\u521D
+i18n_time_month_end=\u5E74\u672B
+i18n_time_date_current=\u4ECA\u5929
+i18n_time_date_last=\u6628\u5929
+i18n_time_date_next=\u660E\u5929
+i18n_time_date_start=\u6708\u521D
+i18n_time_date_end=\u6708\u672B
+
+i18n_dataset_create_error=\u6570\u636E\u96C6\u56E0\u5F02\u5E38\u5BFC\u81F4\u65E0\u6CD5\u4F7F\u7528\uFF0C\u8BF7\u91CD\u65B0\u521B\u5EFA
+i18n_dataset_ds_delete=\u7531\u4E8E\u6570\u636E\u96C6\u6240\u7528\u7684\u6570\u636E\u6E90\u5DF2\u88AB\u5220\u9664,\u65E0\u6CD5\u663E\u793A\u6570\u636E\u96C6
+i18n_dataset_plugin_error=\u5F53\u524D\u6570\u636E\u6E90\u63D2\u4EF6\u4E0D\u5B58\u5728
+i18n_dataset_cross_error=\u8DE8\u6E90\u6570\u636E\u96C6\u4E0D\u652F\u6301\u8BE5\u529F\u80FD
+i18n_board=\u8FB9\u6846
+i18n_invalid_connection=\u8FDE\u63A5\u65E0\u6548,
+i18n_check_datasource_connection=\u8BF7\u68C0\u67E5\u6570\u636E\u6E90\u7684\u6709\u6548\u6027
+
+i18n_datasource_not_exists=\u6570\u636E\u6E90\u4E0D\u5B58\u5728\uFF01
+
+i18n_geo_exists=\u5DF2\u5B58\u5728\u540C\u540D\u533A\u57DF\uFF01
+i18n_geo_sub_exists=\u5DF2\u5B58\u5728\u540C\u540D\u5B50\u533A\u57DF\uFF01
+i18n_user_new_pwd_error=\u5BC6\u7801\u683C\u5F0F\uFF1A8-20\u4F4D\u4E14\u81F3\u5C11\u4E00\u4F4D\u5927\u5199\u5B57\u6BCD\u3001\u5C0F\u5199\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u7279\u6B8A\u5B57\u7B26
+i18n_user_pwd_same_error=\u65B0\u65E7\u5BC6\u7801\u4E0D\u80FD\u76F8\u540C
+
+i18n_copilot_ds=\u5F53\u524D\u4EC5\u652F\u6301MySQL\u6570\u636E\u6E90