52 changed files with 2501 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||
|
/mvnw text eol=lf |
||||
|
*.cmd text eol=crlf |
@ -0,0 +1,33 @@ |
|||||
|
HELP.md |
||||
|
target/ |
||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||
|
!**/src/main/**/target/ |
||||
|
!**/src/test/**/target/ |
||||
|
|
||||
|
### STS ### |
||||
|
.apt_generated |
||||
|
.classpath |
||||
|
.factorypath |
||||
|
.project |
||||
|
.settings |
||||
|
.springBeans |
||||
|
.sts4-cache |
||||
|
|
||||
|
### IntelliJ IDEA ### |
||||
|
.idea |
||||
|
*.iws |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
|
||||
|
### NetBeans ### |
||||
|
/nbproject/private/ |
||||
|
/nbbuild/ |
||||
|
/dist/ |
||||
|
/nbdist/ |
||||
|
/.nb-gradle/ |
||||
|
build/ |
||||
|
!**/src/main/**/build/ |
||||
|
!**/src/test/**/build/ |
||||
|
|
||||
|
### VS Code ### |
||||
|
.vscode/ |
@ -0,0 +1,19 @@ |
|||||
|
# Licensed to the Apache Software Foundation (ASF) under one |
||||
|
# or more contributor license agreements. See the NOTICE file |
||||
|
# distributed with this work for additional information |
||||
|
# regarding copyright ownership. The ASF licenses this file |
||||
|
# to you under the Apache License, Version 2.0 (the |
||||
|
# "License"); you may not use this file except in compliance |
||||
|
# with the License. You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, |
||||
|
# software distributed under the License is distributed on an |
||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
|
# KIND, either express or implied. See the License for the |
||||
|
# specific language governing permissions and limitations |
||||
|
# under the License. |
||||
|
wrapperVersion=3.3.2 |
||||
|
distributionType=only-script |
||||
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip |
@ -0,0 +1,14 @@ |
|||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
<parent> |
||||
|
<groupId>com.huaxing</groupId> |
||||
|
<artifactId>data-bridge</artifactId> |
||||
|
<version>0.0.1-SNAPSHOT</version> |
||||
|
</parent> |
||||
|
|
||||
|
<artifactId>data-common</artifactId> |
||||
|
|
||||
|
</project> |
@ -0,0 +1,15 @@ |
|||||
|
package com.huaxing.common.constant; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.base.constant |
||||
|
* @ClassName: AppConstant |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 系统常量 |
||||
|
* @Date: 2025/1/10 10:53 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
|
||||
|
public class AppConstant { |
||||
|
public static final String APP_NAME = "iot-data-bridge"; |
||||
|
} |
@ -0,0 +1,111 @@ |
|||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
<parent> |
||||
|
<groupId>com.huaxing</groupId> |
||||
|
<artifactId>data-bridge</artifactId> |
||||
|
<version>0.0.1-SNAPSHOT</version> |
||||
|
</parent> |
||||
|
|
||||
|
<artifactId>data-storage</artifactId> |
||||
|
<packaging>jar</packaging> |
||||
|
|
||||
|
<dependencies> |
||||
|
<dependency> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
<version>RELEASE</version> |
||||
|
<scope>provided</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.integration</groupId> |
||||
|
<artifactId>spring-integration-mqtt</artifactId> |
||||
|
<version>6.4.1</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.json</groupId> |
||||
|
<artifactId>json</artifactId> |
||||
|
<version>20240303</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter</artifactId> |
||||
|
<version>3.4.1</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||
|
<version>3.4.1</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>com.baomidou</groupId> |
||||
|
<artifactId>mybatis-plus-core</artifactId> |
||||
|
<version>3.5.8</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>junit</groupId> |
||||
|
<artifactId>junit</artifactId> |
||||
|
<version>4.13-beta-3</version> |
||||
|
<scope>compile</scope> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.aspectj</groupId> |
||||
|
<artifactId>aspectjweaver</artifactId> |
||||
|
<version>1.9.22.1</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>com.fasterxml.jackson.core</groupId> |
||||
|
<artifactId>jackson-databind</artifactId> |
||||
|
<version>2.18.2</version> |
||||
|
</dependency> |
||||
|
<!--dolphindb 相关--> |
||||
|
<dependency> |
||||
|
<groupId>com.dolphindb</groupId> |
||||
|
<artifactId>jdbc</artifactId> |
||||
|
<version>3.00.0.1</version> |
||||
|
</dependency> |
||||
|
<!-- https://mvnrepository.com/artifact/com.dolphindb/dolphindb-javaapi --> |
||||
|
<dependency> |
||||
|
<groupId>com.dolphindb</groupId> |
||||
|
<artifactId>dolphindb-javaapi</artifactId> |
||||
|
<version>3.00.2.3</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework</groupId> |
||||
|
<artifactId>spring-web</artifactId> |
||||
|
<version>6.2.1</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!--mybatis的开发包--> |
||||
|
<dependency> |
||||
|
<groupId>org.mybatis</groupId> |
||||
|
<artifactId>mybatis</artifactId> |
||||
|
<version>3.5.5</version> |
||||
|
</dependency> |
||||
|
<dependency> |
||||
|
<groupId>org.mybatis</groupId> |
||||
|
<artifactId>mybatis-spring</artifactId> |
||||
|
<version>3.0.4</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.mybatis.spring.boot</groupId> |
||||
|
<artifactId>mybatis-spring-boot-starter</artifactId> |
||||
|
<version>2.3.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-jdbc</artifactId> |
||||
|
<version>3.4.1</version> |
||||
|
</dependency> |
||||
|
|
||||
|
</dependencies> |
||||
|
|
||||
|
|
||||
|
|
||||
|
</project> |
@ -0,0 +1,16 @@ |
|||||
|
package com.huaxing; |
||||
|
|
||||
|
import org.mybatis.spring.annotation.MapperScan; |
||||
|
import org.springframework.boot.SpringApplication; |
||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
import org.springframework.context.annotation.ComponentScan; |
||||
|
|
||||
|
@SpringBootApplication |
||||
|
public class IotDataBridgeApplication { |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
SpringApplication.run(IotDataBridgeApplication.class, args); |
||||
|
System.out.println("================= iot-data-bridge started! ================="); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
//package com.huaxing;
|
||||
|
//
|
||||
|
//import com.xxdb.DBConnection;
|
||||
|
//import com.xxdb.data.Entity;
|
||||
|
//import org.junit.Test;
|
||||
|
//
|
||||
|
//import java.io.IOException;
|
||||
|
//
|
||||
|
//public class TestDBConnection {
|
||||
|
// // host
|
||||
|
// private static final String HOST = "localhost";
|
||||
|
// // port
|
||||
|
// private static final int PORT = 8848;
|
||||
|
//
|
||||
|
// DBConnection dbConnection = new DBConnection();
|
||||
|
//
|
||||
|
// @Test
|
||||
|
// public void testDBConnectAndRun() throws IOException {
|
||||
|
// dbConnection.connect(HOST, PORT);
|
||||
|
// dbConnection.login("admin", "123456", true);
|
||||
|
// Entity entity = dbConnection.run("INSERT INTO ZbWaterMeterStream (WM_WFA_Unit, temperature, humidity, time, projectId, deviceId) VALUES ('m3', 5.6, 4.5, 1736388176712, '48', '0jZU2102_0806_0000')");
|
||||
|
// Entity entity2 = dbConnection.run("addColumn(loadTable(\"dfs://ZbDB\", \"ZbWaterMeterDfs\"),`test3,STRING)");
|
||||
|
// dbConnection.close();
|
||||
|
// }
|
||||
|
//}
|
@ -0,0 +1,31 @@ |
|||||
|
package com.huaxing.data.dolphindb; |
||||
|
|
||||
|
import org.apache.ibatis.session.SqlSessionFactory; |
||||
|
import org.mybatis.spring.SqlSessionFactoryBean; |
||||
|
import org.mybatis.spring.SqlSessionTemplate; |
||||
|
import org.springframework.beans.factory.annotation.Qualifier; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; |
||||
|
import org.springframework.core.io.support.ResourcePatternResolver; |
||||
|
import javax.sql.DataSource; |
||||
|
|
||||
|
|
||||
|
@Configuration |
||||
|
public class MyBatisConfig { |
||||
|
@Bean |
||||
|
public SqlSessionFactory sqlSessionFactory(@Qualifier(value = "dataSource") DataSource dataSource) throws Exception { |
||||
|
SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); |
||||
|
bean.setDataSource(dataSource); |
||||
|
// 设置 Mapper 的 XML 文件位置
|
||||
|
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); |
||||
|
bean.setMapperLocations(resolver.getResources("classpath:com/huaxing/data/storage/mapper/*.xml")); |
||||
|
return bean.getObject(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Bean |
||||
|
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { |
||||
|
return new SqlSessionTemplate(sqlSessionFactory); |
||||
|
} |
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
package com.huaxing.data.dolphindb.base; |
||||
|
|
||||
|
import com.huaxing.data.dolphindb.connection.AbstractDbConnector; |
||||
|
import com.huaxing.data.storage.service.base.SqlConverterStatementHandle; |
||||
|
import com.xxdb.DBConnection; |
||||
|
import jakarta.annotation.Resource; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.base |
||||
|
* @ClassName: CommonService |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 全局通用 |
||||
|
* @Date: 2025/1/15 15:19 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public class CommonService extends SqlConverterStatementHandle { |
||||
|
|
||||
|
@Resource |
||||
|
AbstractDbConnector dbPool; |
||||
|
|
||||
|
/** |
||||
|
* 执行单条sql |
||||
|
* |
||||
|
* @param sql |
||||
|
*/ |
||||
|
public void executeOnce(String sql) { |
||||
|
DBConnection connection = dbPool.getConnection(); |
||||
|
try { |
||||
|
connection.run(sql); |
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} finally { |
||||
|
connection.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行单条sql |
||||
|
* |
||||
|
* @param sql |
||||
|
*/ |
||||
|
public void exec(String sql) { |
||||
|
DBConnection connection = dbPool.getConnection(); |
||||
|
try { |
||||
|
connection.run(sql); |
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} finally { |
||||
|
connection.close(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行批量sql |
||||
|
* @param sqlList |
||||
|
*/ |
||||
|
public void executeBatch(List<String> sqlList) { |
||||
|
DBConnection connection = dbPool.getConnection(); |
||||
|
sqlList.forEach(sql -> { |
||||
|
try { |
||||
|
connection.run(sql); |
||||
|
} catch (IOException e) { |
||||
|
log.error("AbstractDbConnector.executeBatch() Method执行异常:{}", e.getMessage()); |
||||
|
} |
||||
|
}); |
||||
|
connection.close(); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,69 @@ |
|||||
|
package com.huaxing.data.dolphindb.config; |
||||
|
|
||||
|
import com.xxdb.*; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* mqtt全局相关配置信息 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Setter |
||||
|
@Getter |
||||
|
@Configuration |
||||
|
@ConfigurationProperties("dolphindb") |
||||
|
@SuppressWarnings("all") |
||||
|
public class DolphinDbConfiguration extends SimpleDBConnectionPoolConfig { |
||||
|
|
||||
|
/** |
||||
|
* 连接地址 dfs://ZBdb
|
||||
|
*/ |
||||
|
private String dbPath; |
||||
|
/** |
||||
|
* 主机名 |
||||
|
*/ |
||||
|
private String host; |
||||
|
/** |
||||
|
* 端口号 |
||||
|
*/ |
||||
|
private int port; |
||||
|
/** |
||||
|
* 用户名 |
||||
|
*/ |
||||
|
private String username; |
||||
|
/** |
||||
|
* 密码 |
||||
|
*/ |
||||
|
private String password; |
||||
|
/** |
||||
|
* 连接池初始化大小 |
||||
|
*/ |
||||
|
private int initPoolSize; |
||||
|
/** |
||||
|
* 表示连接池中最小连接数,正整数,默认值为 5 |
||||
|
*/ |
||||
|
private boolean minimumPoolSize; |
||||
|
/** |
||||
|
* 是否开启高可用 |
||||
|
*/ |
||||
|
private boolean enableHighAvailability; |
||||
|
|
||||
|
/** |
||||
|
* 创建连接池配置初始化方法 |
||||
|
* @return SimpleDBConnectionPoolConfig |
||||
|
*/ |
||||
|
public SimpleDBConnectionPoolConfig loadConfig() { |
||||
|
SimpleDBConnectionPoolConfig config = new SimpleDBConnectionPoolConfig(); |
||||
|
config.setHostName(host); |
||||
|
config.setPort(port); |
||||
|
config.setUserId(username); |
||||
|
config.setPassword(password); |
||||
|
config.setInitialPoolSize(initPoolSize); |
||||
|
config.setEnableHighAvailability(enableHighAvailability); |
||||
|
return config; |
||||
|
} |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
package com.huaxing.data.dolphindb.config; |
||||
|
|
||||
|
import com.xxdb.*; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.dolphindb.config |
||||
|
* @ClassName: DbClient |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 数据库客户端 |
||||
|
* @Date: 2025/1/15 9:58 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Configuration |
||||
|
public class DolphinDbPoolConfiguration extends SimpleDBConnectionPool { |
||||
|
|
||||
|
public DBConnection dbConn = null; |
||||
|
|
||||
|
/** |
||||
|
* 构造方法 |
||||
|
* @param dbConfiguration |
||||
|
*/ |
||||
|
|
||||
|
public DolphinDbPoolConfiguration(DolphinDbConfiguration dbConfiguration) { |
||||
|
super(dbConfiguration.loadConfig()); |
||||
|
this.dbConn = super.getConnection(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package com.huaxing.data.dolphindb.connection; |
||||
|
|
||||
|
import com.xxdb.DBConnection; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.dolphindb.connection |
||||
|
* @ClassName: DbConnector |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: dolphindb 连接器 |
||||
|
* @Date: 2025/1/13 14:29 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Accessors(chain = true) |
||||
|
@Slf4j |
||||
|
@SuppressWarnings("all") |
||||
|
public abstract class AbstractDbConnector { |
||||
|
|
||||
|
/** |
||||
|
* 连接 dolphindb |
||||
|
*/ |
||||
|
public abstract DBConnection getConnection(); |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
package com.huaxing.data.dolphindb.connection; |
||||
|
|
||||
|
import com.huaxing.data.dolphindb.config.DolphinDbConfiguration; |
||||
|
import com.huaxing.data.dolphindb.config.DolphinDbPoolConfiguration; |
||||
|
import com.xxdb.DBConnection; |
||||
|
import jakarta.annotation.PostConstruct; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.dolphindb.connection |
||||
|
* @ClassName: DbConnectorHelper |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 数据库处理器 |
||||
|
* @Date: 2025/1/13 17:53 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
|
||||
|
@Component |
||||
|
@SuppressWarnings("all") |
||||
|
public class DbConnectorHelper extends AbstractDbConnector { |
||||
|
|
||||
|
final DolphinDbPoolConfiguration dbPool; |
||||
|
final DolphinDbConfiguration dbConfiguration; |
||||
|
public DbConnectorHelper(DolphinDbConfiguration dbConfiguration, DolphinDbPoolConfiguration dbPool) { |
||||
|
this.dbConfiguration = dbConfiguration; |
||||
|
this.dbPool = dbPool; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取数据库连接 |
||||
|
* @return DBConnection |
||||
|
*/ |
||||
|
@PostConstruct |
||||
|
@Override |
||||
|
public DBConnection getConnection() { |
||||
|
return dbPool.dbConn; |
||||
|
} |
||||
|
} |
@ -0,0 +1,130 @@ |
|||||
|
package com.huaxing.data.storage.controller; |
||||
|
|
||||
|
import com.huaxing.data.storage.domain.DataAnalysisDTO; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataQueryDfsService; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataQueryStreamService; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataStoredService; |
||||
|
import com.huaxing.data.tablemanagement.service.ITableStructureService; |
||||
|
import com.huaxing.data.util.JacksonUtil; |
||||
|
import com.huaxing.mqtt.processor.MqttMessageSender; |
||||
|
import jakarta.annotation.Resource; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.controller |
||||
|
* @ClassName: TestController |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 测试controller |
||||
|
* @Date: 2025/1/15 17:08 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@RequestMapping("/test") |
||||
|
@SuppressWarnings("all") |
||||
|
public class TestController { |
||||
|
|
||||
|
@Resource |
||||
|
private MqttMessageSender messageSender; |
||||
|
final IDeviceDataStoredService dataStoredService; |
||||
|
final IDeviceDataQueryDfsService dataQueryDfsService; |
||||
|
final IDeviceDataQueryStreamService dataQueryStreamService; |
||||
|
final ITableStructureService tableStructureService; |
||||
|
|
||||
|
// // 依赖注入
|
||||
|
public TestController(IDeviceDataStoredService dataStoredService, IDeviceDataQueryDfsService dataQueryDfsService, IDeviceDataQueryStreamService dataQueryStreamService, ITableStructureService tableStructureService) { |
||||
|
this.dataStoredService = dataStoredService; |
||||
|
this.dataQueryDfsService = dataQueryDfsService; |
||||
|
this.dataQueryStreamService = dataQueryStreamService; |
||||
|
this.tableStructureService = tableStructureService; |
||||
|
} |
||||
|
|
||||
|
// 测试插入数据
|
||||
|
@GetMapping(value = "/testInsert") // 成功
|
||||
|
public void testInsert() { |
||||
|
String sql = "INSERT INTO ZbWaterMeter1Stream (time, projectId, deviceId, WM_WFA, WM_WFA_Unit) VALUES (2024.11.01 00:00:00,'48', '0jZU2102_0806_0011', 124.656, 'm³')"; |
||||
|
dataStoredService.execute(sql); |
||||
|
log.info("SUCCESS"); |
||||
|
} |
||||
|
|
||||
|
// 测试Dfs表查询
|
||||
|
@GetMapping(value = "/testSelectDfs") |
||||
|
public List<Map<String, Object>> testSelectDfs() { |
||||
|
String dbPath = "dfs://ZbDB"; |
||||
|
String sql = String.format("select * from loadTable('%s','%s')", dbPath, "ZbWaterMeter1Dfs"); |
||||
|
return dataQueryDfsService.selectList(sql); |
||||
|
} |
||||
|
|
||||
|
// 测试订阅流表查询
|
||||
|
@GetMapping(value = "/testSelectStream") |
||||
|
public List<Map<String, Object>> testSelectStream() { |
||||
|
String sql = "select * from ZbWaterMeter1Stream"; |
||||
|
return dataQueryStreamService.selectList(sql); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 给指定的流表增加列字段
|
||||
|
@GetMapping(value = "/testStreamAddColumn") |
||||
|
public void testStreamAddColumn() { |
||||
|
String tableName = "ZbWaterMeter1Stream"; |
||||
|
String columnName = "WM_WFA_Unit14"; |
||||
|
String columnDefinition = "STRING"; |
||||
|
try { |
||||
|
tableStructureService.addStreamColumn(tableName, columnName, columnDefinition); |
||||
|
log.info("SUCCESS"); |
||||
|
} catch (Exception e) { |
||||
|
log.error("FAIL"); |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@GetMapping(value = "/testDfsAddColumn") |
||||
|
public void testDfsAddColumn() { |
||||
|
String tableName = "ZbWaterMeter1Dfs"; |
||||
|
String columnName = "test2"; |
||||
|
String columnDefinition = "STRING"; |
||||
|
try { |
||||
|
tableStructureService.addDfsColumn(tableName, columnName, columnDefinition); |
||||
|
log.info("SUCCESS"); |
||||
|
} catch (Exception e) { |
||||
|
log.error("FAIL"); |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 向消息队列中发送100W条数据
|
||||
|
@GetMapping(value = "/testSendMessage") |
||||
|
public void testSendMessage() { |
||||
|
for (int i = 0; i < 2; i++) { |
||||
|
List<Map<String, Object>> dataList = new ArrayList<>(); |
||||
|
dataList.add(handleMapByIndex(i)); |
||||
|
DataAnalysisDTO dataAnalysisDTO = DataAnalysisDTO.builder().tableName( "WaterMeterTsetStream").dataList(dataList).build(); |
||||
|
messageSender.send("iot/test1/in-storage", JacksonUtil.objectStr(dataAnalysisDTO)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Map<String, Object> handleMapByIndex(int index) { |
||||
|
Map<String, Object> map = new HashMap<>(); |
||||
|
map.put("time", "2025.01.01 00:00:00"); |
||||
|
map.put("projectId", "0jZU2102"); |
||||
|
map.put("deviceId", "0jZU2102_0806_0011"); |
||||
|
map.put("WM_WFA", 124.656 + index); |
||||
|
map.put("WM_WFA_Unit", "m³"); |
||||
|
return map; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package com.huaxing.data.storage.domain; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.domain |
||||
|
* @ClassName: CommonDeviceControl |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 设备入库数据接收实体 |
||||
|
* @Date: 2025/1/10 18:11 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Builder |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
@Accessors(chain = true) |
||||
|
@Data |
||||
|
public class DataAnalysisDTO { |
||||
|
|
||||
|
// 表名
|
||||
|
private String tableName; |
||||
|
|
||||
|
// 设备数据集
|
||||
|
private List<Map<String, Object>> dataList; |
||||
|
|
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package com.huaxing.data.storage.domain; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.domain |
||||
|
* @ClassName: CommonDeviceControl |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 数据查询DTO |
||||
|
* @Date: 2025/1/10 18:11 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
|
||||
|
@Accessors(chain = true) |
||||
|
@Data |
||||
|
public class DataQueryDTO { |
||||
|
// 表名
|
||||
|
private String tableName; |
||||
|
// 设备数据集
|
||||
|
private List<String> tableColumns; |
||||
|
|
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
package com.huaxing.data.storage.mapper; |
||||
|
|
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Select; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* ClassName: IDeviceDataQuery |
||||
|
* Description: 查询Dfs表数据 |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-16 16:49 |
||||
|
*/ |
||||
|
@Mapper |
||||
|
@SuppressWarnings("all") |
||||
|
public interface IDeviceDataQueryDfsMapper { |
||||
|
|
||||
|
@Select("${sql}") |
||||
|
List<Map<String, Object>> selectList(String sql); |
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
package com.huaxing.data.storage.mapper; |
||||
|
|
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Select; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* ClassName: IDeviceDataQuery |
||||
|
* Description: 查询Stream表数据 |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-16 16:49 |
||||
|
*/ |
||||
|
@Mapper |
||||
|
@SuppressWarnings("all") |
||||
|
public interface IDeviceDataQueryStreamMapper { |
||||
|
|
||||
|
@Select("${sql}") |
||||
|
List<Map<String, Object>> selectList(String sql); |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
package com.huaxing.data.storage.mapper; |
||||
|
|
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Select; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-16 15:23 |
||||
|
*/ |
||||
|
@Mapper |
||||
|
@SuppressWarnings("all") |
||||
|
public interface IDeviceDataStoredMapper { |
||||
|
|
||||
|
@Select("${sql}") |
||||
|
List<Map<String, Object>> selectList (String sql); |
||||
|
|
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.huaxing.data.storage.service; |
||||
|
|
||||
|
/** |
||||
|
* 入库数据解析服务 |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-13 11:16 |
||||
|
*/ |
||||
|
public interface IDataAnalysisService { |
||||
|
|
||||
|
// 解析入库数据
|
||||
|
void parseStoreData (String jsonData); |
||||
|
|
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package com.huaxing.data.storage.service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-16 16:45 |
||||
|
*/ |
||||
|
public interface IDeviceDataQueryDfsService { |
||||
|
|
||||
|
/** |
||||
|
* 查询Dfs表 list数据 |
||||
|
* @param sql |
||||
|
* @return |
||||
|
*/ |
||||
|
List<Map<String, Object>> selectList(String sql); |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package com.huaxing.data.storage.service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-16 16:45 |
||||
|
*/ |
||||
|
public interface IDeviceDataQueryStreamService { |
||||
|
|
||||
|
/** |
||||
|
* 查询Dfs表 list数据 |
||||
|
* @param sql |
||||
|
* @return |
||||
|
*/ |
||||
|
List<Map<String, Object>> selectList(String sql); |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package com.huaxing.data.storage.service; |
||||
|
|
||||
|
import com.huaxing.data.storage.domain.DataAnalysisDTO; |
||||
|
|
||||
|
/** |
||||
|
* ClassName: IDeviceDataStoredService |
||||
|
* Description: 设备采集入库与查询通用业务接口 |
||||
|
*/ |
||||
|
public interface IDeviceDataStoredService { |
||||
|
|
||||
|
/** |
||||
|
* 设备采集入库 |
||||
|
* @param analysisDTO |
||||
|
*/ |
||||
|
void insert(DataAnalysisDTO analysisDTO); |
||||
|
|
||||
|
void execute(String sql); |
||||
|
|
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
package com.huaxing.data.storage.service.abstracts; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.base |
||||
|
* @ClassName: SqlConverterStatement |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: SQL转换语句 |
||||
|
* @Date: 2025/1/14 14:01 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
|
||||
|
abstract public class SqlConverterStatement { |
||||
|
|
||||
|
/** |
||||
|
* 生成插入语句 |
||||
|
* @param tableName |
||||
|
* @param map |
||||
|
* @return |
||||
|
*/ |
||||
|
public abstract String generateInsertStreamStatement(String tableName, Map<String, Object> map); |
||||
|
|
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package com.huaxing.data.storage.service.base; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author 孟剑 |
||||
|
* @date 2025-01-16 16:54 |
||||
|
*/ |
||||
|
public interface IDbSqlFactory { |
||||
|
|
||||
|
// 生成插入流式语句
|
||||
|
String generateInsertStreamStatement(String tableName, Map<String, Object> map); |
||||
|
|
||||
|
// 生成Stream表查询语句
|
||||
|
String generateSelectStreamStatement(String tableName, Map<String, Object> map); |
||||
|
|
||||
|
// 生成Dfs表查询语句
|
||||
|
String generateSelectDfsStatement(String tableName, Map<String, Object> map); |
||||
|
} |
@ -0,0 +1,71 @@ |
|||||
|
package com.huaxing.data.storage.service.base; |
||||
|
|
||||
|
import com.huaxing.data.storage.service.abstracts.SqlConverterStatement; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.base |
||||
|
* @ClassName: SqlConverterStatementHandle |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: sql处理类 |
||||
|
* @Date: 2025/1/14 14:05 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
|
||||
|
public class SqlConverterStatementHandle extends SqlConverterStatement { |
||||
|
|
||||
|
/** |
||||
|
* 生成插入语句 |
||||
|
* |
||||
|
* @param tableName 表名 |
||||
|
* @param map 键值对 |
||||
|
* @return 插入语句 |
||||
|
*/ |
||||
|
@Override |
||||
|
public String generateInsertStreamStatement(String tableName, Map<String, Object> map) { |
||||
|
StringBuilder columnBuilder = new StringBuilder(); |
||||
|
StringBuilder valueBuilder = new StringBuilder(); |
||||
|
int index = 0; |
||||
|
for (Map.Entry<String, Object> entry : map.entrySet()) { |
||||
|
// 处理列名
|
||||
|
columnBuilder.append(entry.getKey()); |
||||
|
// 处理值
|
||||
|
if (entry.getValue() instanceof String) { |
||||
|
valueBuilder.append("'").append(entry.getValue()).append("'"); |
||||
|
} else if (entry.getValue() == null) { |
||||
|
valueBuilder.append("NULL"); |
||||
|
} else { |
||||
|
valueBuilder.append(entry.getValue()); |
||||
|
} |
||||
|
if (index < map.size() - 1) { |
||||
|
columnBuilder.append(", "); |
||||
|
valueBuilder.append(", "); |
||||
|
} |
||||
|
index++; |
||||
|
} |
||||
|
return "INSERT INTO " + tableName + " (" + columnBuilder + ") VALUES (" + valueBuilder + ")"; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 生成流表Stream查询语句 |
||||
|
* @param tableName |
||||
|
* @param columnNameList |
||||
|
* @return |
||||
|
*/ |
||||
|
public String generateSelectStreamStatement(String tableName, List<String> columnNameList) { |
||||
|
StringBuilder columnBuilder = new StringBuilder(); |
||||
|
if (columnNameList.isEmpty()) { |
||||
|
columnBuilder.append(" * "); |
||||
|
} else { |
||||
|
columnNameList.forEach(columnName -> columnBuilder.append(columnName).append(", ")); |
||||
|
} |
||||
|
return "SELECT " + columnBuilder.toString() + " FROM " + tableName; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
package com.huaxing.data.storage.service.impl; |
||||
|
|
||||
|
import com.huaxing.data.storage.domain.DataAnalysisDTO; |
||||
|
import com.huaxing.data.storage.service.IDataAnalysisService; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataStoredService; |
||||
|
import com.huaxing.data.util.JacksonUtil; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.impl |
||||
|
* @ClassName: DataAnalysisService |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 入库数据解析服务 |
||||
|
* @Date: 2025/1/13 11:16 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Service |
||||
|
public class DataAnalysisService implements IDataAnalysisService { |
||||
|
|
||||
|
final IDeviceDataStoredService dataStoredService; |
||||
|
|
||||
|
public DataAnalysisService(IDeviceDataStoredService dataStoredService) { |
||||
|
this.dataStoredService = dataStoredService; |
||||
|
} |
||||
|
|
||||
|
int row = 0; |
||||
|
/** |
||||
|
* @author: swordmeng8@163.com |
||||
|
* @date: 2025/1/13 11:16 |
||||
|
* @desc: 解析入库数据 |
||||
|
* @param jsonData |
||||
|
* @return void |
||||
|
*/ |
||||
|
@Override |
||||
|
public void parseStoreData(String jsonData) { |
||||
|
DataAnalysisDTO dataAnalysisDTO = JacksonUtil.strToObject(jsonData, DataAnalysisDTO.class); |
||||
|
log.info("入库数据解析完成"); |
||||
|
dataStoredService.insert(dataAnalysisDTO); |
||||
|
log.info("入库数据入库完成"); |
||||
|
row++; |
||||
|
log.info("入库数据入库完成,入库总数:{}", row); |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package com.huaxing.data.storage.service.impl; |
||||
|
|
||||
|
import com.huaxing.data.storage.mapper.IDeviceDataQueryDfsMapper; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataQueryDfsService; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.impl |
||||
|
* @ClassName: DeviceDataQueryDfsServiceImpl |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 设备数据查询业务层 |
||||
|
* @Date: 2025/1/16 16:45 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Service |
||||
|
public class DeviceDataQueryDfsServiceImpl implements IDeviceDataQueryDfsService { |
||||
|
// 构造器注入
|
||||
|
private final IDeviceDataQueryDfsMapper deviceDataQueryDfsMapper; |
||||
|
public DeviceDataQueryDfsServiceImpl(IDeviceDataQueryDfsMapper deviceDataQueryDfsMapper) { |
||||
|
this.deviceDataQueryDfsMapper = deviceDataQueryDfsMapper; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @author: swordmeng8@163.com |
||||
|
* @date: 2025/1/16 16:45 |
||||
|
* @desc: 查询数据 |
||||
|
* @param sql |
||||
|
* @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>> |
||||
|
*/ |
||||
|
@Override |
||||
|
public List<Map<String, Object>> selectList(String sql) { |
||||
|
return deviceDataQueryDfsMapper.selectList(sql); |
||||
|
} |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
package com.huaxing.data.storage.service.impl; |
||||
|
|
||||
|
import com.huaxing.data.storage.mapper.IDeviceDataQueryStreamMapper; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataQueryStreamService; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.impl |
||||
|
* @ClassName: DeviceDataQueryStreamServiceImpl |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 设备数据查询业务层 |
||||
|
* @Date: 2025/1/16 16:45 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Service |
||||
|
public class DeviceDataQueryStreamServiceImpl implements IDeviceDataQueryStreamService { |
||||
|
// 构造器注入
|
||||
|
private final IDeviceDataQueryStreamMapper dataQueryStreamMapper; |
||||
|
public DeviceDataQueryStreamServiceImpl(IDeviceDataQueryStreamMapper dataQueryStreamMapper) { |
||||
|
this.dataQueryStreamMapper = dataQueryStreamMapper; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @author: swordmeng8@163.com |
||||
|
* @date: 2025/1/16 16:45 |
||||
|
* @desc: 查询数据 |
||||
|
* @param sql |
||||
|
* @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>> |
||||
|
*/ |
||||
|
@Override |
||||
|
public List<Map<String, Object>> selectList(String sql) { |
||||
|
return dataQueryStreamMapper.selectList(sql); |
||||
|
} |
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package com.huaxing.data.storage.service.impl; |
||||
|
|
||||
|
import com.huaxing.data.storage.domain.DataAnalysisDTO; |
||||
|
import com.huaxing.data.storage.mapper.IDeviceDataStoredMapper; |
||||
|
import com.huaxing.data.storage.service.IDeviceDataStoredService; |
||||
|
import com.huaxing.data.dolphindb.base.CommonService; |
||||
|
import com.huaxing.data.util.JacksonUtil; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* @ClassName DeviceDataStoredServiceImpl |
||||
|
* @Description 设备采集入库与查询服务实现类 |
||||
|
* @Author swordmeng8@163.com |
||||
|
* @Date 2024/12/2 18:59 |
||||
|
* @Version 1.0 |
||||
|
**/ |
||||
|
@Slf4j |
||||
|
@Service |
||||
|
@SuppressWarnings("all") |
||||
|
public class DeviceDataStoredServiceImpl extends CommonService implements IDeviceDataStoredService { |
||||
|
|
||||
|
final IDeviceDataStoredMapper deviceDataStoredMapper; |
||||
|
|
||||
|
public DeviceDataStoredServiceImpl(IDeviceDataStoredMapper deviceDataStoredMapper) { |
||||
|
this.deviceDataStoredMapper = deviceDataStoredMapper; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 插入数据 |
||||
|
* @param dataAnalysis |
||||
|
*/ |
||||
|
@Override |
||||
|
public void insert(DataAnalysisDTO dataAnalysis) { |
||||
|
String tableName = dataAnalysis.getTableName(); |
||||
|
dataAnalysis.getDataList().forEach(map -> { |
||||
|
// CompletableFuture.runAsync(() -> {
|
||||
|
//
|
||||
|
// });
|
||||
|
log.info("入库数据:{}", JacksonUtil.objectStr(map)); |
||||
|
executeOnce(generateInsertStreamStatement(tableName, map)); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void execute(String sql) { |
||||
|
executeOnce(sql); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.huaxing.data.tablemanagement.domain; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.tablemanagement.domain |
||||
|
* @ClassName: TableColumnDTO |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 表列DTO |
||||
|
* @Date: 2025/1/15 16:05 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Data |
||||
|
public class TableColumnDTO { |
||||
|
/** |
||||
|
* 列名 |
||||
|
*/ |
||||
|
private String columnName; |
||||
|
/** |
||||
|
* 列类型 |
||||
|
*/ |
||||
|
private String columnType; |
||||
|
/** |
||||
|
* 列描述 |
||||
|
*/ |
||||
|
private String columnDesc; |
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
package com.huaxing.data.tablemanagement.domain; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.tablemanagement.domain |
||||
|
* @ClassName: TableDTO |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 表操作类 |
||||
|
* @Date: 2025/1/15 16:03 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
|
||||
|
@Data |
||||
|
public class TableDTO { |
||||
|
|
||||
|
/** |
||||
|
* 表名 |
||||
|
*/ |
||||
|
private String tableName; |
||||
|
/** |
||||
|
* 表列信息 |
||||
|
*/ |
||||
|
private List<TableColumnDTO> tableColumnList; |
||||
|
|
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
package com.huaxing.data.tablemanagement.service; |
||||
|
|
||||
|
import java.sql.SQLException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service |
||||
|
* @ClassName: ITableStructureService |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 表结构操作service |
||||
|
* @Date: 2025/1/13 11:02 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
public interface ITableStructureService { |
||||
|
|
||||
|
// 添加dfs表单列
|
||||
|
void addDfsColumn(String tableName, String columnName, String columnDefinition) throws SQLException; |
||||
|
// 添加dfs表多列
|
||||
|
void addDfsColumns(String tableName, List<String> columnName, String columnDefinition) throws SQLException; |
||||
|
|
||||
|
// 添加Stream流表单列
|
||||
|
void addStreamColumn(String tableName, String columnName, String columnDefinition) throws SQLException; |
||||
|
|
||||
|
// 添加Stream流表多列
|
||||
|
void addStreamColumns(String tableName, String columnName, String columnDefinition) throws SQLException; |
||||
|
|
||||
|
// 创建dfs表
|
||||
|
void createDfsTable(String tableName, String columnName, String columnDefinition) throws SQLException; |
||||
|
|
||||
|
// 创建Stream流表
|
||||
|
void createStreamTable(String tableName, String columnName, String columnDefinition) throws SQLException; |
||||
|
|
||||
|
// 订阅流表
|
||||
|
void subscribeStreamTable(String tableName, String columnName, String columnDefinition) throws SQLException; |
||||
|
|
||||
|
// 判断表是否存在
|
||||
|
boolean isTableExist(String tableName) throws SQLException; |
||||
|
|
||||
|
} |
@ -0,0 +1,65 @@ |
|||||
|
package com.huaxing.data.tablemanagement.service.impl; |
||||
|
|
||||
|
import com.huaxing.data.dolphindb.base.CommonService; |
||||
|
import com.huaxing.data.tablemanagement.service.ITableStructureService; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
import java.sql.SQLException; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* @ProjectName: iot-data-bridge |
||||
|
* @Package: com.huaxing.data.storage.service.impl |
||||
|
* @ClassName: TableStructureService |
||||
|
* @Author: swordmeng8@163.com |
||||
|
* @Description: 表结构操作service |
||||
|
* @Date: 2025/1/13 11:02 |
||||
|
* @Version: 1.0 |
||||
|
*/ |
||||
|
@Service |
||||
|
public class TableStructureService extends CommonService implements ITableStructureService { |
||||
|
|
||||
|
@Override |
||||
|
public void addDfsColumn(String tableName, String columnName, String columnDefinition) throws SQLException { |
||||
|
String script = |
||||
|
"pt = loadTable(\"dfs://ZbDB\", `" + tableName + ");\n" + |
||||
|
"alter table pt add " + columnName + " " + columnDefinition + ";"; |
||||
|
exec(script); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void addDfsColumns(String tableName, List<String> columnName, String columnDefinition) throws SQLException { |
||||
|
exec(""); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void addStreamColumn(String tableName, String columnName, String columnDefinition) throws SQLException { |
||||
|
exec("addColumn(" + tableName + ", " + columnName + "," + columnDefinition + ")"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void addStreamColumns(String tableName, String columnName, String columnDefinition) throws SQLException { |
||||
|
exec(""); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void createDfsTable(String tableName, String columnName, String columnDefinition) throws SQLException { |
||||
|
exec(""); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void createStreamTable(String tableName, String columnName, String columnDefinition) throws SQLException { |
||||
|
exec(""); |
||||
|
} |
||||
|
|
||||
|
// 订阅流表
|
||||
|
@Override |
||||
|
public void subscribeStreamTable(String tableName, String columnName, String columnDefinition) throws SQLException { |
||||
|
exec(""); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean isTableExist(String tableName) throws SQLException { |
||||
|
return false; |
||||
|
} |
||||
|
} |
@ -0,0 +1,77 @@ |
|||||
|
package com.huaxing.data.util; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.DeserializationFeature; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author kwl99 |
||||
|
* @since 2024-08-08 14:31 |
||||
|
*/ |
||||
|
public class JacksonUtil { |
||||
|
|
||||
|
/** |
||||
|
* 将对象转为json字符串 |
||||
|
*/ |
||||
|
public static String objectStr(Object object) { |
||||
|
ObjectMapper mapper = new ObjectMapper(); |
||||
|
try { |
||||
|
|
||||
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
||||
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); |
||||
|
|
||||
|
return mapper.writeValueAsString(object); |
||||
|
} catch (JsonProcessingException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将json字符串转为对象 |
||||
|
*/ |
||||
|
public static <T> T strToObject(String str, Class<T> clazz) { |
||||
|
ObjectMapper mapper = new ObjectMapper(); |
||||
|
try { |
||||
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
||||
|
return mapper.readValue(str, clazz); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 将json字符串转为对象列表 |
||||
|
*/ |
||||
|
public static <T> List<T> strToObjectList(String str, Class<T> clazz) { |
||||
|
ObjectMapper mapper = new ObjectMapper(); |
||||
|
try { |
||||
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
||||
|
return mapper.readValue(str, mapper.getTypeFactory().constructCollectionType(List.class, clazz)); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将json字符串转为对象列表 |
||||
|
*/ |
||||
|
public static List<Map<String, Object>> strToMapList(String str) { |
||||
|
ObjectMapper mapper = new ObjectMapper(); |
||||
|
try { |
||||
|
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
||||
|
return mapper.readValue(str, mapper.getTypeFactory().constructCollectionType(List.class, HashMap.class)); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,143 @@ |
|||||
|
package com.huaxing.mqtt.config; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.integration.annotation.IntegrationComponentScan; |
||||
|
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory; |
||||
|
import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
||||
|
import org.springframework.util.CollectionUtils; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* mqtt全局相关配置信息 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Setter |
||||
|
@Getter |
||||
|
@Configuration |
||||
|
@ConfigurationProperties("mqtt") |
||||
|
@IntegrationComponentScan(basePackages = "com.huaxing.mqtt.*") |
||||
|
public class MqttConfiguration { |
||||
|
|
||||
|
/** |
||||
|
* 用户名 |
||||
|
*/ |
||||
|
private String username; |
||||
|
|
||||
|
/** |
||||
|
* 密码 |
||||
|
*/ |
||||
|
private String password; |
||||
|
|
||||
|
/** |
||||
|
* 连接地址 |
||||
|
*/ |
||||
|
private String hostUrl; |
||||
|
|
||||
|
/** |
||||
|
* 客户Id |
||||
|
*/ |
||||
|
private String clientId; |
||||
|
|
||||
|
/** |
||||
|
* 默认连接话题 |
||||
|
*/ |
||||
|
private String defaultTopic; |
||||
|
|
||||
|
/** |
||||
|
* 超时时间 |
||||
|
*/ |
||||
|
private int timeout; |
||||
|
|
||||
|
/** |
||||
|
* qos |
||||
|
*/ |
||||
|
private int qos; |
||||
|
|
||||
|
/** |
||||
|
* 订阅超时时间 |
||||
|
*/ |
||||
|
private int completionTimeout; |
||||
|
|
||||
|
/** |
||||
|
* 保持连接数 |
||||
|
*/ |
||||
|
private int keepalive; |
||||
|
|
||||
|
/** |
||||
|
* 2024-12-06新增 |
||||
|
* 要订阅的其他主题 |
||||
|
*/ |
||||
|
private List<String> topics; |
||||
|
|
||||
|
/** |
||||
|
* 客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息 |
||||
|
*/ |
||||
|
private static final byte[] WILL_DATA = "offline".getBytes(); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取所有订阅的主题 |
||||
|
* @return 所有订阅的主题 |
||||
|
*/ |
||||
|
public String[] getAllTopics() { |
||||
|
// 校验配置文件是否配置
|
||||
|
if (CollectionUtils.isEmpty(topics)) { |
||||
|
this.topics = new ArrayList<>(); |
||||
|
} |
||||
|
// 将默认主题条件到其他主题里
|
||||
|
this.topics.add(defaultTopic); |
||||
|
// 返回主题数组
|
||||
|
return topics.toArray(new String[0]); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 注册MQTT客户端工厂 |
||||
|
* @return MqttPahoClientFactory |
||||
|
*/ |
||||
|
@Bean |
||||
|
public MqttPahoClientFactory mqttClientFactory() { |
||||
|
// 客户端工厂
|
||||
|
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); |
||||
|
|
||||
|
MqttConnectOptions options = new MqttConnectOptions(); |
||||
|
// 设置连接的用户名
|
||||
|
options.setUserName(username); |
||||
|
// 设置连接的密码
|
||||
|
options.setPassword(password.toCharArray()); |
||||
|
// 设置连接的地址
|
||||
|
options.setServerURIs(new String[]{hostUrl}); |
||||
|
|
||||
|
// 如果设置为 false,客户端和服务器将在客户端、服务器和连接重新启动时保持状态。随着状态的保持:
|
||||
|
// 即使客户端、服务器或连接重新启动,消息传递也将可靠地满足指定的 QOS。服务器将订阅视为持久的。
|
||||
|
// 如果设置为 true,客户端和服务器将不会在客户端、服务器或连接重新启动时保持状态。
|
||||
|
options.setCleanSession(true); |
||||
|
|
||||
|
// 设置超时时间,该值以秒为单位,必须>0,定义了客户端等待与 MQTT 服务器建立网络连接的最大时间间隔。
|
||||
|
// 默认超时为 30 秒。值 0 禁用超时处理,这意味着客户端将等待直到网络连接成功或失败。
|
||||
|
options.setConnectionTimeout(10); |
||||
|
|
||||
|
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线
|
||||
|
// 此值以秒为单位,定义发送或接收消息之间的最大时间间隔,必须>0
|
||||
|
// 但这个方法并没有重连的机制
|
||||
|
options.setKeepAliveInterval(20); |
||||
|
|
||||
|
// 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
|
||||
|
options.setWill("willTopic", WILL_DATA, 2, false); |
||||
|
|
||||
|
//自动重新连接
|
||||
|
options.setAutomaticReconnect(true); |
||||
|
factory.setConnectionOptions(options); |
||||
|
|
||||
|
log.info("初始化 MQTT 配置"); |
||||
|
|
||||
|
return factory; |
||||
|
} |
||||
|
} |
@ -0,0 +1,96 @@ |
|||||
|
package com.huaxing.mqtt.config; |
||||
|
|
||||
|
import com.huaxing.mqtt.constant.MqttConstant; |
||||
|
import com.huaxing.mqtt.processor.MqttMessageReceiver; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.integration.annotation.IntegrationComponentScan; |
||||
|
import org.springframework.integration.annotation.ServiceActivator; |
||||
|
import org.springframework.integration.channel.DirectChannel; |
||||
|
import org.springframework.integration.endpoint.MessageProducerSupport; |
||||
|
import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
||||
|
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter; |
||||
|
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; |
||||
|
import org.springframework.messaging.MessageChannel; |
||||
|
import org.springframework.messaging.MessageHandler; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* MQTT消费者配置 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Configuration |
||||
|
@IntegrationComponentScan |
||||
|
public class MqttConsumerConfiguration { |
||||
|
|
||||
|
final MqttConfiguration mqttConfiguration; |
||||
|
final MqttMessageReceiver mqttMessageReceiver; |
||||
|
|
||||
|
public MqttConsumerConfiguration(MqttConfiguration mqttConfiguration, MqttMessageReceiver mqttMessageReceiver) { |
||||
|
this.mqttConfiguration = mqttConfiguration; |
||||
|
this.mqttMessageReceiver = mqttMessageReceiver; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 此处可以使用其他消息通道 |
||||
|
* MQTT信息通道(消费者) |
||||
|
* Spring Integration默认的消息通道,它允许将消息发送给一个订阅者,然后阻碍发送直到消息被接收。 |
||||
|
*/ |
||||
|
@Bean |
||||
|
public MessageChannel mqttInBoundChannel() { |
||||
|
return new DirectChannel(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* mqtt入站消息处理工具,对于指定消息入站通道接收到生产者生产的消息后处理消息的工具。 |
||||
|
*/ |
||||
|
@Bean |
||||
|
@ServiceActivator(inputChannel = "mqttInBoundChannel") |
||||
|
public MessageHandler mqttMessageHandler() { |
||||
|
return this.mqttMessageReceiver; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* MQTT消息订阅绑定(消费者) |
||||
|
* 适配器, 两个topic共用一个adapter |
||||
|
* 客户端作为消费者,订阅主题,消费消息 |
||||
|
*/ |
||||
|
@Bean |
||||
|
public MessageProducerSupport mqttInbound() { |
||||
|
// 获取客户端id
|
||||
|
String clientId = mqttConfiguration.getClientId(); |
||||
|
// 获取默认主题
|
||||
|
// String defaultTopic = mqttConfiguration.getDefaultTopic();
|
||||
|
|
||||
|
// 获取所有配置的主题
|
||||
|
String[] topics = mqttConfiguration.getAllTopics(); |
||||
|
|
||||
|
// 获取客户端工厂
|
||||
|
MqttPahoClientFactory mqttPahoClientFactory = mqttConfiguration.mqttClientFactory(); |
||||
|
|
||||
|
// Paho客户端消息驱动通道适配器,主要用来订阅主题
|
||||
|
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( |
||||
|
UUID.randomUUID() + clientId + MqttConstant.CLIENT_SUFFIX_CONSUMERS, |
||||
|
mqttPahoClientFactory, |
||||
|
// 所有需要订阅的topic
|
||||
|
topics |
||||
|
); |
||||
|
adapter.setCompletionTimeout(mqttConfiguration.getCompletionTimeout()); |
||||
|
// Paho消息转换器
|
||||
|
DefaultPahoMessageConverter defaultPahoMessageConverter = new DefaultPahoMessageConverter(); |
||||
|
// 按字节接收消息
|
||||
|
// defaultPahoMessageConverter.setPayloadAsBytes(true);
|
||||
|
adapter.setConverter(defaultPahoMessageConverter); |
||||
|
// 设置QoS
|
||||
|
adapter.setQos(mqttConfiguration.getQos()); |
||||
|
// 设置订阅通道
|
||||
|
adapter.setOutputChannel(mqttInBoundChannel()); |
||||
|
return adapter; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,65 @@ |
|||||
|
package com.huaxing.mqtt.config; |
||||
|
|
||||
|
import com.huaxing.mqtt.constant.MqttConstant; |
||||
|
import jakarta.annotation.Resource; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.integration.annotation.ServiceActivator; |
||||
|
import org.springframework.integration.channel.DirectChannel; |
||||
|
import org.springframework.integration.mqtt.core.MqttPahoClientFactory; |
||||
|
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler; |
||||
|
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter; |
||||
|
import org.springframework.messaging.MessageChannel; |
||||
|
import org.springframework.messaging.MessageHandler; |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* MQTT生产者配置 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Configuration |
||||
|
@AllArgsConstructor |
||||
|
public class MqttProducerConfiguration { |
||||
|
|
||||
|
@Resource |
||||
|
private MqttConfiguration mqttConfiguration; |
||||
|
|
||||
|
/** |
||||
|
* MQTT信息通道(生产者) |
||||
|
*/ |
||||
|
@Bean |
||||
|
public MessageChannel mqttOutboundChannel() { |
||||
|
return new DirectChannel(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* MQTT消息处理器(生产者) |
||||
|
*/ |
||||
|
@Bean |
||||
|
@ServiceActivator(inputChannel = "mqttOutboundChannel") |
||||
|
public MessageHandler mqttOutbound() { |
||||
|
// 客户端id
|
||||
|
String clientId = mqttConfiguration.getClientId(); |
||||
|
// 默认主题
|
||||
|
String defaultTopic = mqttConfiguration.getDefaultTopic(); |
||||
|
MqttPahoClientFactory mqttPahoClientFactory = mqttConfiguration.mqttClientFactory(); |
||||
|
|
||||
|
// 发送消息和消费消息Channel可以使用相同MqttPahoClientFactory
|
||||
|
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId + MqttConstant.CLIENT_SUFFIX_PRODUCERS, mqttPahoClientFactory); |
||||
|
// true,异步,发送消息时将不会阻塞。
|
||||
|
messageHandler.setAsync(true); |
||||
|
messageHandler.setDefaultTopic(defaultTopic); |
||||
|
// 默认QoS
|
||||
|
messageHandler.setDefaultQos(1); |
||||
|
// Paho消息转换器
|
||||
|
DefaultPahoMessageConverter defaultPahoMessageConverter = new DefaultPahoMessageConverter(); |
||||
|
// defaultPahoMessageConverter.setPayloadAsBytes(true);
|
||||
|
// 发送默认按字节类型发送消息
|
||||
|
messageHandler.setConverter(defaultPahoMessageConverter); |
||||
|
return messageHandler; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
package com.huaxing.mqtt.constant; |
||||
|
/** |
||||
|
* 常量 |
||||
|
*/ |
||||
|
public class MqttConstant { |
||||
|
|
||||
|
/** |
||||
|
* 客户端id消费者后缀 |
||||
|
*/ |
||||
|
public static final String CLIENT_SUFFIX_CONSUMERS = "_consumers"; |
||||
|
/** |
||||
|
* 客户端id生产者后缀 |
||||
|
*/ |
||||
|
public static final String CLIENT_SUFFIX_PRODUCERS = "_producers"; |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,46 @@ |
|||||
|
package com.huaxing.mqtt.processor; |
||||
|
|
||||
|
import org.springframework.integration.annotation.MessagingGateway; |
||||
|
import org.springframework.integration.mqtt.support.MqttHeaders; |
||||
|
import org.springframework.messaging.handler.annotation.Header; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
/** |
||||
|
* 生产者处理器 |
||||
|
*/ |
||||
|
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel") |
||||
|
public interface MqttGateway { |
||||
|
|
||||
|
/** |
||||
|
* 发送mqtt消息 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
* @param payload 内容 |
||||
|
*/ |
||||
|
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload); |
||||
|
|
||||
|
/** |
||||
|
* 发送包含qos的消息 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
* @param qos 对消息处理的几种机制。 |
||||
|
* * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。<br> |
||||
|
* * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。<br> |
||||
|
* * 2 多了一次去重的动作,确保订阅者收到的消息有一次。 |
||||
|
* @param payload 消息体 |
||||
|
*/ |
||||
|
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload); |
||||
|
|
||||
|
/** |
||||
|
* 发送包含qos的消息 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
* @param qos 对消息处理的几种机制。 |
||||
|
* * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。<br> |
||||
|
* * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。<br> |
||||
|
* * 2 多了一次去重的动作,确保订阅者收到的消息有一次。 |
||||
|
* @param payload 消息体 |
||||
|
*/ |
||||
|
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, byte[] payload); |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package com.huaxing.mqtt.processor; |
||||
|
|
||||
|
import com.huaxing.data.storage.service.IDataAnalysisService; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.integration.mqtt.support.MqttHeaders; |
||||
|
import org.springframework.messaging.Message; |
||||
|
import org.springframework.messaging.MessageHandler; |
||||
|
import org.springframework.messaging.MessageHeaders; |
||||
|
import org.springframework.messaging.MessagingException; |
||||
|
import org.springframework.scheduling.annotation.Async; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
import java.util.concurrent.CompletableFuture; |
||||
|
|
||||
|
/** |
||||
|
* 消费者处理器 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Component |
||||
|
@SuppressWarnings("all") |
||||
|
public class MqttMessageReceiver implements MessageHandler { |
||||
|
|
||||
|
/** |
||||
|
* 数据处理服务 |
||||
|
*/ |
||||
|
final IDataAnalysisService analysisService; |
||||
|
public MqttMessageReceiver(IDataAnalysisService analysisService) { |
||||
|
this.analysisService = analysisService; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 消息处理 |
||||
|
* |
||||
|
* @param message 消息 |
||||
|
* @throws MessagingException 消息异常 |
||||
|
*/ |
||||
|
@Override |
||||
|
@Async("handleMessage") |
||||
|
public void handleMessage(Message<?> message) throws MessagingException { |
||||
|
try { |
||||
|
// 获取消息Topic
|
||||
|
MessageHeaders headers = message.getHeaders(); |
||||
|
String topic = String.valueOf(headers.get(MqttHeaders.RECEIVED_TOPIC)); |
||||
|
log.info("[获取到的消息的topic]:{} ", topic); |
||||
|
// 获取消息体
|
||||
|
String payload = (String) message.getPayload(); |
||||
|
log.info("[获取到的消息的payload]:{} ", payload); |
||||
|
// 数据库入库消息
|
||||
|
if (topic.contains("iot/test1/in-storage")) { |
||||
|
// 模拟1000000条数据入库
|
||||
|
log.info("接收到iot/test1/in-storage的消息啦,快去处理"); |
||||
|
long l = System.currentTimeMillis(); |
||||
|
System.currentTimeMillis(); |
||||
|
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { |
||||
|
analysisService.parseStoreData(payload); |
||||
|
}); |
||||
|
long l1 = System.currentTimeMillis(); |
||||
|
log.info("入库完成,耗时:{}", l1 - l); |
||||
|
// analysisService.parseStoreData(payload);
|
||||
|
} else if (topic.contains("table-update/")){} // TODO 表更新topic
|
||||
|
} catch (Exception e) { |
||||
|
log.error(e.toString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,52 @@ |
|||||
|
package com.huaxing.mqtt.processor; |
||||
|
|
||||
|
import org.json.JSONObject; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 生产者处理器 |
||||
|
*/ |
||||
|
@Component |
||||
|
public class MqttMessageSender { |
||||
|
|
||||
|
@Autowired |
||||
|
private MqttGateway mqttGateway; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 发送mqtt消息 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
* @param message 内容 |
||||
|
* @return void |
||||
|
*/ |
||||
|
public void send(String topic, String message) { |
||||
|
mqttGateway.sendToMqtt(topic, message); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送包含qos的消息 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
* @param qos 质量 |
||||
|
* @param messageBody 消息体 |
||||
|
* @return void |
||||
|
*/ |
||||
|
public void send(String topic, int qos, JSONObject messageBody) { |
||||
|
mqttGateway.sendToMqtt(topic, qos, messageBody.toString()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送包含qos的消息 |
||||
|
* |
||||
|
* @param topic 主题 |
||||
|
* @param qos 质量 |
||||
|
* @param message 消息体 |
||||
|
* @return void |
||||
|
*/ |
||||
|
public void send(String topic, int qos, byte[] message) { |
||||
|
mqttGateway.sendToMqtt(topic, qos, message); |
||||
|
} |
||||
|
} |
@ -0,0 +1,48 @@ |
|||||
|
|
||||
|
server: |
||||
|
port: 8088 |
||||
|
spring: |
||||
|
application: |
||||
|
name: iot-data-bridge |
||||
|
datasource: |
||||
|
url: jdbc:dolphindb://localhost:8848?databasePath=dfs://ZbDB |
||||
|
username: admin |
||||
|
password: 123456 |
||||
|
driver-class-name: com.dolphindb.jdbc.Driver |
||||
|
main: |
||||
|
lazy-initialization: false |
||||
|
|
||||
|
mybatis: |
||||
|
mapper-locations: classpath:com/huaxing/data/storage/mapper/*.xml |
||||
|
type-aliases-package: com.huaxing.data.storage.entity |
||||
|
configuration: |
||||
|
map-underscore-to-camel-case: true |
||||
|
sql-session-factory: |
||||
|
data-source: ${spring.datasource} |
||||
|
sql-session-template: |
||||
|
executor-type: BATCH |
||||
|
sql-session-factory-ref: sqlSessionFactory |
||||
|
|
||||
|
mqtt: |
||||
|
username: admin |
||||
|
password: 123456 |
||||
|
host-url: tcp://8.130.65.74:1883 |
||||
|
client-id: iot |
||||
|
timeout: 100 |
||||
|
keepalive: 100 |
||||
|
completion-timeout: 5000 |
||||
|
qos: 1 |
||||
|
default-topic: iot/data/# |
||||
|
topics: |
||||
|
- iot/test1/# |
||||
|
- iot/test2/# |
||||
|
|
||||
|
dolphindb: |
||||
|
db-path: dfs://ZbDB |
||||
|
host: 127.0.0.1 |
||||
|
port: 8848 |
||||
|
username: admin |
||||
|
password: 123456 |
||||
|
init-pool-size: 10 |
||||
|
minimum-pool-size: 5 |
||||
|
enable-high-availability: false |
@ -0,0 +1,79 @@ |
|||||
|
<!--<?xml version="1.0" encoding="UTF-8" ?>--> |
||||
|
<!--<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">--> |
||||
|
<!--<mapper namespace="com.huaxing.iot.mqtt.mapper.AirConditionerDfsMapper">--> |
||||
|
|
||||
|
<!-- <sql id="conditionSql">--> |
||||
|
<!-- <where>--> |
||||
|
<!-- projectId= #{projectId}--> |
||||
|
<!-- <if test="deviceIds !=null">--> |
||||
|
<!-- and deviceId in--> |
||||
|
<!-- <foreach collection="deviceIds" item="item" index="index"--> |
||||
|
<!-- separator="," open="(" close=")">--> |
||||
|
<!-- #{item}--> |
||||
|
<!-- </foreach>--> |
||||
|
<!-- </if>--> |
||||
|
<!-- <if test="startDate !=null and endDate !=null">--> |
||||
|
<!-- and <![CDATA[ time >=#{startDate} and time <= #{endDate}]]>--> |
||||
|
<!-- </if>--> |
||||
|
<!-- </where>--> |
||||
|
<!-- </sql>--> |
||||
|
|
||||
|
<!-- <select id="queryByTimespan" resultType="com.huaxing.iot.mqtt.entity.AirConditioner">--> |
||||
|
<!-- select time,projectId,deviceId,AstRu, AdSw, ArtemRAr, ArtemRArUnit, ArvSAr, ArvSArUnit, ArstMa, AtemRAr,--> |
||||
|
<!-- AtemRArUnit, AvSAr, AvSArUnit, AstMa, AmSw, Asw, Apl, ArmSw--> |
||||
|
<!-- from AirConditionerStream--> |
||||
|
<!-- <include refid="conditionSql"/>--> |
||||
|
<!-- order by time desc--> |
||||
|
<!-- </select>--> |
||||
|
|
||||
|
<!-- <select id="queryByTimespan_COUNT" resultType="Long">--> |
||||
|
<!-- select count(*)--> |
||||
|
<!-- from airConditioningBoxDfs--> |
||||
|
<!-- <include refid="conditionSql"/>--> |
||||
|
<!-- </select>--> |
||||
|
|
||||
|
<!-- <select id="queryLastPointInfo" resultType="com.huaxing.iot.mqtt.entity.AirConditioner">--> |
||||
|
<!-- SELECT t1.time, t1.projectId, t1.deviceId,--> |
||||
|
<!-- <choose>--> |
||||
|
<!-- <when test="devicePointCodes != null and devicePointCodes.size() > 0">--> |
||||
|
<!-- <foreach item="field" collection="devicePointCodes" separator=",">--> |
||||
|
<!-- t1.${field}--> |
||||
|
<!-- </foreach>--> |
||||
|
<!-- </when>--> |
||||
|
<!-- <otherwise>--> |
||||
|
<!-- t1.AstRu, t1.AdSw, t1.ArtemRAr, t1.ArtemRArUnit, t1.ArvSAr, t1.ArvSArUnit, t1.ArstMa, t1.AtemRAr,--> |
||||
|
<!-- t1.AtemRArUnit, t1.AvSAr, t1.AvSArUnit, t1.AstMa, t1.AmSw, t1.Asw, t1.Apl, t1.ArmSw--> |
||||
|
<!-- </otherwise>--> |
||||
|
<!-- </choose>--> |
||||
|
<!-- FROM AirConditionerStream t1--> |
||||
|
<!-- INNER JOIN (--> |
||||
|
<!-- SELECT deviceId, max(time) AS max_time--> |
||||
|
<!-- FROM AirConditionerStream--> |
||||
|
<!-- WHERE deviceId IN--> |
||||
|
<!-- <foreach collection="deviceIds" item="item" index="index" separator="," open="(" close=")">--> |
||||
|
<!-- #{item}--> |
||||
|
<!-- </foreach>--> |
||||
|
<!-- GROUP BY deviceId--> |
||||
|
<!-- ) t2 ON t1.deviceId = t2.deviceId AND t1.time = t2.max_time--> |
||||
|
<!-- <where>--> |
||||
|
<!-- <if test="projectId != null and projectId != ''">--> |
||||
|
<!-- and t1.projectId = #{projectId}--> |
||||
|
<!-- </if>--> |
||||
|
<!-- </where>--> |
||||
|
<!-- </select>--> |
||||
|
|
||||
|
<!-- <delete id="delete">--> |
||||
|
<!-- delete from AirConditionerStream--> |
||||
|
<!-- where projectId= #{projectId} and--> |
||||
|
<!-- deviceId in--> |
||||
|
<!-- <foreach collection="deviceIds" item="item" index="index"--> |
||||
|
<!-- separator="," open="(" close=")">--> |
||||
|
<!-- #{item}--> |
||||
|
<!-- </foreach>--> |
||||
|
<!-- </delete>--> |
||||
|
|
||||
|
<!-- <!–<insert id="insert" parameterType="com.huaxing.iot.mqtt.entity.Elevator">--> |
||||
|
<!-- insert into ElevatorDfs values(#{time},#{projectId},#{deviceId},#{onOffState},#{runMode})--> |
||||
|
|
||||
|
<!-- </insert>–>--> |
||||
|
<!--</mapper>--> |
@ -0,0 +1,9 @@ |
|||||
|
<!--<?xml version="1.0" encoding="UTF-8" ?>--> |
||||
|
<!--<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">--> |
||||
|
<!--<mapper namespace="com.huaxing.data.storage.mapper.IDeviceDataStoredMapper">--> |
||||
|
|
||||
|
<!-- <select id="selectList" resultType="map">--> |
||||
|
<!-- ${selectSql}--> |
||||
|
<!-- </select>--> |
||||
|
|
||||
|
<!--</mapper>--> |
@ -0,0 +1,3 @@ |
|||||
|
create.table.script=CREATE TABLE {0} (id INT PRIMARY KEY, name VARCHAR(255)) |
||||
|
add.column.script=ALTER TABLE {0} ADD COLUMN age INT |
||||
|
drop.column.script=ALTER TABLE {0} DROP COLUMN name |
@ -0,0 +1,71 @@ |
|||||
|
//分布式库名 |
||||
|
dbPath = "dfs://ZbDB" |
||||
|
//流表名 |
||||
|
stName = "WaterMeterTsetStream" |
||||
|
//分区表名 |
||||
|
ptName = "WaterMeterTsetDfs" |
||||
|
|
||||
|
//建库函数 |
||||
|
def createDB(dbPath){ |
||||
|
print('正在初始化数据库' + dbPath) |
||||
|
if (existsDatabase(dbPath)) { |
||||
|
print('当前数据库已存在,加载数据库:' + dbPath) |
||||
|
db = database(dbPath) |
||||
|
print('已加载数据库:' + dbPath) |
||||
|
return db |
||||
|
} |
||||
|
else { |
||||
|
print('当前数据库不存在,创建数据库:' + dbPath) |
||||
|
value = 2025.01M..2040.12M |
||||
|
db = database(dbPath, VALUE, value,,engine='OLAP') |
||||
|
print('已完成创建数据库:'+ dbPath) |
||||
|
return db |
||||
|
} |
||||
|
} |
||||
|
//建流表函数 |
||||
|
def createST(stName){ |
||||
|
if(not existsStreamTable(stName)){ |
||||
|
print('共享变量未定义,创建共享表:' + stName) |
||||
|
print(`正在创建Demo流表) |
||||
|
colNames = `time`projectId`deviceId`WM_WFA`WM_WFA_Unit |
||||
|
colTypes = [DATETIME,STRING,STRING,DECIMAL64(4),STRING] |
||||
|
st = streamTable(150000:0, colNames, colTypes) |
||||
|
print(`完成创建Demo流表) |
||||
|
enableTableShareAndPersistence(table=st, tableName=stName, retentionMinutes=10, cacheSize=100000, preCache=100000 ) |
||||
|
print('完成创建共享表') |
||||
|
return st |
||||
|
} |
||||
|
else{ |
||||
|
print('共享变量已存在,加载共享变量:' + stName) |
||||
|
return objByName(stName, true) |
||||
|
} |
||||
|
} |
||||
|
//建分区表函数 |
||||
|
def createPT(dbPath,ptName){ |
||||
|
db = createDB(dbPath) |
||||
|
if (existsTable(dbPath, ptName)){ |
||||
|
print('分区表已存在,加载分区表:' + ptName) |
||||
|
pt = loadTable(db,ptName) |
||||
|
return pt |
||||
|
} |
||||
|
else{ |
||||
|
print('分区表不存在,创建分区表:' + ptName) |
||||
|
print(`正在创建Demo分区表) |
||||
|
colNames = `time`projectId`deviceId`WM_WFA`WM_WFA_Unit |
||||
|
colTypes = [DATETIME,STRING,STRING,DECIMAL64(4),STRING] |
||||
|
t = table(1:0, colNames, colTypes) |
||||
|
pt = db.createPartitionedTable(table=t, tableName=ptName, partitionColumns=`time, sortColumns=`deviceId`time, keepDuplicates=LAST) |
||||
|
print('完成创建分区表:' + ptName) |
||||
|
return pt |
||||
|
} |
||||
|
} |
||||
|
//创建库 |
||||
|
//createDB(dbPath) |
||||
|
//创建分区表 |
||||
|
createPT(dbPath,ptName) |
||||
|
//创建流表 |
||||
|
unsubscribeTable(tableName=stName, actionName=`WaterMeterTsetChgTime) |
||||
|
// dropStreamTable(stName) |
||||
|
createST(stName) |
||||
|
//订阅流表 |
||||
|
subscribeTable(tableName=stName, actionName=`WaterMeterTsetChgTime, offset=-1, handler=loadTable(dbPath,ptName), msgAsTable=true) |
@ -0,0 +1,259 @@ |
|||||
|
#!/bin/sh |
||||
|
# ---------------------------------------------------------------------------- |
||||
|
# Licensed to the Apache Software Foundation (ASF) under one |
||||
|
# or more contributor license agreements. See the NOTICE file |
||||
|
# distributed with this work for additional information |
||||
|
# regarding copyright ownership. The ASF licenses this file |
||||
|
# to you under the Apache License, Version 2.0 (the |
||||
|
# "License"); you may not use this file except in compliance |
||||
|
# with the License. You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, |
||||
|
# software distributed under the License is distributed on an |
||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
|
# KIND, either express or implied. See the License for the |
||||
|
# specific language governing permissions and limitations |
||||
|
# under the License. |
||||
|
# ---------------------------------------------------------------------------- |
||||
|
|
||||
|
# ---------------------------------------------------------------------------- |
||||
|
# Apache Maven Wrapper startup batch script, version 3.3.2 |
||||
|
# |
||||
|
# Optional ENV vars |
||||
|
# ----------------- |
||||
|
# JAVA_HOME - location of a JDK home dir, required when download maven via java source |
||||
|
# MVNW_REPOURL - repo url base for downloading maven distribution |
||||
|
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven |
||||
|
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output |
||||
|
# ---------------------------------------------------------------------------- |
||||
|
|
||||
|
set -euf |
||||
|
[ "${MVNW_VERBOSE-}" != debug ] || set -x |
||||
|
|
||||
|
# OS specific support. |
||||
|
native_path() { printf %s\\n "$1"; } |
||||
|
case "$(uname)" in |
||||
|
CYGWIN* | MINGW*) |
||||
|
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" |
||||
|
native_path() { cygpath --path --windows "$1"; } |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
# set JAVACMD and JAVACCMD |
||||
|
set_java_home() { |
||||
|
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched |
||||
|
if [ -n "${JAVA_HOME-}" ]; then |
||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ]; then |
||||
|
# IBM's JDK on AIX uses strange locations for the executables |
||||
|
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
|
JAVACCMD="$JAVA_HOME/jre/sh/javac" |
||||
|
else |
||||
|
JAVACMD="$JAVA_HOME/bin/java" |
||||
|
JAVACCMD="$JAVA_HOME/bin/javac" |
||||
|
|
||||
|
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then |
||||
|
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 |
||||
|
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
else |
||||
|
JAVACMD="$( |
||||
|
'set' +e |
||||
|
'unset' -f command 2>/dev/null |
||||
|
'command' -v java |
||||
|
)" || : |
||||
|
JAVACCMD="$( |
||||
|
'set' +e |
||||
|
'unset' -f command 2>/dev/null |
||||
|
'command' -v javac |
||||
|
)" || : |
||||
|
|
||||
|
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then |
||||
|
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 |
||||
|
return 1 |
||||
|
fi |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
# hash string like Java String::hashCode |
||||
|
hash_string() { |
||||
|
str="${1:-}" h=0 |
||||
|
while [ -n "$str" ]; do |
||||
|
char="${str%"${str#?}"}" |
||||
|
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) |
||||
|
str="${str#?}" |
||||
|
done |
||||
|
printf %x\\n $h |
||||
|
} |
||||
|
|
||||
|
verbose() { :; } |
||||
|
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } |
||||
|
|
||||
|
die() { |
||||
|
printf %s\\n "$1" >&2 |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
trim() { |
||||
|
# MWRAPPER-139: |
||||
|
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. |
||||
|
# Needed for removing poorly interpreted newline sequences when running in more |
||||
|
# exotic environments such as mingw bash on Windows. |
||||
|
printf "%s" "${1}" | tr -d '[:space:]' |
||||
|
} |
||||
|
|
||||
|
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties |
||||
|
while IFS="=" read -r key value; do |
||||
|
case "${key-}" in |
||||
|
distributionUrl) distributionUrl=$(trim "${value-}") ;; |
||||
|
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; |
||||
|
esac |
||||
|
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" |
||||
|
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" |
||||
|
|
||||
|
case "${distributionUrl##*/}" in |
||||
|
maven-mvnd-*bin.*) |
||||
|
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ |
||||
|
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in |
||||
|
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; |
||||
|
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;; |
||||
|
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;; |
||||
|
:Linux*x86_64*) distributionPlatform=linux-amd64 ;; |
||||
|
*) |
||||
|
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 |
||||
|
distributionPlatform=linux-amd64 |
||||
|
;; |
||||
|
esac |
||||
|
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" |
||||
|
;; |
||||
|
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; |
||||
|
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; |
||||
|
esac |
||||
|
|
||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME |
||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> |
||||
|
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" |
||||
|
distributionUrlName="${distributionUrl##*/}" |
||||
|
distributionUrlNameMain="${distributionUrlName%.*}" |
||||
|
distributionUrlNameMain="${distributionUrlNameMain%-bin}" |
||||
|
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" |
||||
|
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" |
||||
|
|
||||
|
exec_maven() { |
||||
|
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : |
||||
|
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" |
||||
|
} |
||||
|
|
||||
|
if [ -d "$MAVEN_HOME" ]; then |
||||
|
verbose "found existing MAVEN_HOME at $MAVEN_HOME" |
||||
|
exec_maven "$@" |
||||
|
fi |
||||
|
|
||||
|
case "${distributionUrl-}" in |
||||
|
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; |
||||
|
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; |
||||
|
esac |
||||
|
|
||||
|
# prepare tmp dir |
||||
|
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then |
||||
|
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } |
||||
|
trap clean HUP INT TERM EXIT |
||||
|
else |
||||
|
die "cannot create temp dir" |
||||
|
fi |
||||
|
|
||||
|
mkdir -p -- "${MAVEN_HOME%/*}" |
||||
|
|
||||
|
# Download and Install Apache Maven |
||||
|
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." |
||||
|
verbose "Downloading from: $distributionUrl" |
||||
|
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" |
||||
|
|
||||
|
# select .zip or .tar.gz |
||||
|
if ! command -v unzip >/dev/null; then |
||||
|
distributionUrl="${distributionUrl%.zip}.tar.gz" |
||||
|
distributionUrlName="${distributionUrl##*/}" |
||||
|
fi |
||||
|
|
||||
|
# verbose opt |
||||
|
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' |
||||
|
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v |
||||
|
|
||||
|
# normalize http auth |
||||
|
case "${MVNW_PASSWORD:+has-password}" in |
||||
|
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; |
||||
|
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; |
||||
|
esac |
||||
|
|
||||
|
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then |
||||
|
verbose "Found wget ... using wget" |
||||
|
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" |
||||
|
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then |
||||
|
verbose "Found curl ... using curl" |
||||
|
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" |
||||
|
elif set_java_home; then |
||||
|
verbose "Falling back to use Java to download" |
||||
|
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" |
||||
|
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" |
||||
|
cat >"$javaSource" <<-END |
||||
|
public class Downloader extends java.net.Authenticator |
||||
|
{ |
||||
|
protected java.net.PasswordAuthentication getPasswordAuthentication() |
||||
|
{ |
||||
|
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); |
||||
|
} |
||||
|
public static void main( String[] args ) throws Exception |
||||
|
{ |
||||
|
setDefault( new Downloader() ); |
||||
|
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); |
||||
|
} |
||||
|
} |
||||
|
END |
||||
|
# For Cygwin/MinGW, switch paths to Windows format before running javac and java |
||||
|
verbose " - Compiling Downloader.java ..." |
||||
|
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" |
||||
|
verbose " - Running Downloader.java ..." |
||||
|
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" |
||||
|
fi |
||||
|
|
||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file |
||||
|
if [ -n "${distributionSha256Sum-}" ]; then |
||||
|
distributionSha256Result=false |
||||
|
if [ "$MVN_CMD" = mvnd.sh ]; then |
||||
|
echo "Checksum validation is not supported for maven-mvnd." >&2 |
||||
|
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 |
||||
|
exit 1 |
||||
|
elif command -v sha256sum >/dev/null; then |
||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then |
||||
|
distributionSha256Result=true |
||||
|
fi |
||||
|
elif command -v shasum >/dev/null; then |
||||
|
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then |
||||
|
distributionSha256Result=true |
||||
|
fi |
||||
|
else |
||||
|
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 |
||||
|
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
if [ $distributionSha256Result = false ]; then |
||||
|
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 |
||||
|
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# unzip and move |
||||
|
if command -v unzip >/dev/null; then |
||||
|
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" |
||||
|
else |
||||
|
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" |
||||
|
fi |
||||
|
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" |
||||
|
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" |
||||
|
|
||||
|
clean || : |
||||
|
exec_maven "$@" |
@ -0,0 +1,149 @@ |
|||||
|
<# : batch portion |
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||
|
@REM or more contributor license agreements. See the NOTICE file |
||||
|
@REM distributed with this work for additional information |
||||
|
@REM regarding copyright ownership. The ASF licenses this file |
||||
|
@REM to you under the Apache License, Version 2.0 (the |
||||
|
@REM "License"); you may not use this file except in compliance |
||||
|
@REM with the License. You may obtain a copy of the License at |
||||
|
@REM |
||||
|
@REM http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
@REM |
||||
|
@REM Unless required by applicable law or agreed to in writing, |
||||
|
@REM software distributed under the License is distributed on an |
||||
|
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
|
@REM KIND, either express or implied. See the License for the |
||||
|
@REM specific language governing permissions and limitations |
||||
|
@REM under the License. |
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
|
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
@REM Apache Maven Wrapper startup batch script, version 3.3.2 |
||||
|
@REM |
||||
|
@REM Optional ENV vars |
||||
|
@REM MVNW_REPOURL - repo url base for downloading maven distribution |
||||
|
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven |
||||
|
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output |
||||
|
@REM ---------------------------------------------------------------------------- |
||||
|
|
||||
|
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) |
||||
|
@SET __MVNW_CMD__= |
||||
|
@SET __MVNW_ERROR__= |
||||
|
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% |
||||
|
@SET PSModulePath= |
||||
|
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( |
||||
|
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) |
||||
|
) |
||||
|
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% |
||||
|
@SET __MVNW_PSMODULEP_SAVE= |
||||
|
@SET __MVNW_ARG0_NAME__= |
||||
|
@SET MVNW_USERNAME= |
||||
|
@SET MVNW_PASSWORD= |
||||
|
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) |
||||
|
@echo Cannot start maven from wrapper >&2 && exit /b 1 |
||||
|
@GOTO :EOF |
||||
|
: end batch / begin powershell #> |
||||
|
|
||||
|
$ErrorActionPreference = "Stop" |
||||
|
if ($env:MVNW_VERBOSE -eq "true") { |
||||
|
$VerbosePreference = "Continue" |
||||
|
} |
||||
|
|
||||
|
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties |
||||
|
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl |
||||
|
if (!$distributionUrl) { |
||||
|
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" |
||||
|
} |
||||
|
|
||||
|
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { |
||||
|
"maven-mvnd-*" { |
||||
|
$USE_MVND = $true |
||||
|
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" |
||||
|
$MVN_CMD = "mvnd.cmd" |
||||
|
break |
||||
|
} |
||||
|
default { |
||||
|
$USE_MVND = $false |
||||
|
$MVN_CMD = $script -replace '^mvnw','mvn' |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
# apply MVNW_REPOURL and calculate MAVEN_HOME |
||||
|
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash> |
||||
|
if ($env:MVNW_REPOURL) { |
||||
|
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } |
||||
|
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" |
||||
|
} |
||||
|
$distributionUrlName = $distributionUrl -replace '^.*/','' |
||||
|
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' |
||||
|
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" |
||||
|
if ($env:MAVEN_USER_HOME) { |
||||
|
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" |
||||
|
} |
||||
|
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' |
||||
|
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" |
||||
|
|
||||
|
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { |
||||
|
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" |
||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" |
||||
|
exit $? |
||||
|
} |
||||
|
|
||||
|
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { |
||||
|
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" |
||||
|
} |
||||
|
|
||||
|
# prepare tmp dir |
||||
|
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile |
||||
|
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" |
||||
|
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null |
||||
|
trap { |
||||
|
if ($TMP_DOWNLOAD_DIR.Exists) { |
||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } |
||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null |
||||
|
|
||||
|
# Download and Install Apache Maven |
||||
|
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." |
||||
|
Write-Verbose "Downloading from: $distributionUrl" |
||||
|
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" |
||||
|
|
||||
|
$webclient = New-Object System.Net.WebClient |
||||
|
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { |
||||
|
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) |
||||
|
} |
||||
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 |
||||
|
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null |
||||
|
|
||||
|
# If specified, validate the SHA-256 sum of the Maven distribution zip file |
||||
|
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum |
||||
|
if ($distributionSha256Sum) { |
||||
|
if ($USE_MVND) { |
||||
|
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." |
||||
|
} |
||||
|
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash |
||||
|
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { |
||||
|
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
# unzip and move |
||||
|
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null |
||||
|
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null |
||||
|
try { |
||||
|
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null |
||||
|
} catch { |
||||
|
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { |
||||
|
Write-Error "fail to move MAVEN_HOME" |
||||
|
} |
||||
|
} finally { |
||||
|
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } |
||||
|
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } |
||||
|
} |
||||
|
|
||||
|
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" |
@ -0,0 +1,37 @@ |
|||||
|
<?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"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
<groupId>com.huaxing</groupId> |
||||
|
<artifactId>data-bridge</artifactId> |
||||
|
<version>0.0.1-SNAPSHOT</version> |
||||
|
<packaging>pom</packaging> |
||||
|
<name>data-bridge</name> |
||||
|
<description>data-bridge</description> |
||||
|
|
||||
|
<modules> |
||||
|
<module>data-common</module> |
||||
|
<module>data-storage</module> |
||||
|
</modules> |
||||
|
|
||||
|
|
||||
|
|
||||
|
<repositories> |
||||
|
<repository> |
||||
|
<id>aliyunmaven</id> |
||||
|
<name>阿里云公共仓库</name> |
||||
|
<url>https://maven.aliyun.com/repository/public</url> |
||||
|
<releases> |
||||
|
<!-- 使用稳定版本 --> |
||||
|
<enabled>true</enabled> |
||||
|
</releases> |
||||
|
<snapshots> |
||||
|
<!-- 使用快照版本 --> |
||||
|
<enabled>false</enabled> |
||||
|
</snapshots> |
||||
|
</repository> |
||||
|
</repositories> |
||||
|
|
||||
|
|
||||
|
</project> |
Loading…
Reference in new issue