From 19400d5e2bd37436c76ba4328a02ee8b83e4adca Mon Sep 17 00:00:00 2001 From: xins Date: Wed, 20 Dec 2023 13:35:46 +0800 Subject: [PATCH] =?UTF-8?q?TDEngine=E6=9C=8D=E5=8A=A1=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hw-api/hw-api-tdengine/pom.xml | 28 + .../tdengine/api/RemoteTdEngineService.java | 66 +++ .../hw/tdengine/api/domain/AlterTagVo.java | 33 ++ .../hw/tdengine/api/domain/DeviceStatus.java | 104 ++++ .../com/hw/tdengine/api/domain/TdField.java | 40 ++ .../com/hw/tdengine/api/domain/TdFieldVo.java | 68 +++ .../api/domain/TdHistorySelectDto.java | 40 ++ .../tdengine/api/domain/TdReturnDataVo.java | 13 + .../hw/tdengine/api/domain/TdSelectDto.java | 35 ++ .../api/domain/TdSuperTableSelectVo.java | 52 ++ .../tdengine/api/domain/TdSuperTableVo.java | 57 +++ .../com/hw/tdengine/api/domain/TdTableVo.java | 42 ++ .../RemoteTdEngineFallbackFactory.java | 107 ++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + hw-api/pom.xml | 1 + hw-common/hw-common-core/pom.xml | 9 +- .../core/constant/ServiceNameConstants.java | 7 +- .../core/constant/TdEngineConstants.java | 134 +++++ .../hw/common/core/enums/DataTypeEnums.java | 115 +++++ .../validated/tdengine/AddTdSTableColumn.java | 4 + .../validated/tdengine/InsertTdTable.java | 4 + .../hw-gen/src/main/resources/bootstrap.yml | 4 + hw-modules/hw-jindie/install-jindie-jar.bat | 2 + .../hw-jindie/k3cloud-webapi-sdk8.0.6.jar | Bin 0 -> 68092 bytes hw-modules/hw-jindie/kdwebapi.properties | 19 + hw-modules/hw-jindie/pom.xml | 115 +++++ .../hw-jindie/src/main/java/com/hw/Main.java | 19 + .../com/hw/jindie/HwJindieApplication.java | 34 ++ .../java/com/hw/jindie/common/SeqHelper.java | 22 + .../java/com/hw/jindie/test/BdCustomer.java | 50 ++ .../com/hw/jindie/test/BdCustomerTest.java | 237 +++++++++ .../hw-jindie/src/main/resources/banner.txt | 10 + .../src/main/resources/bootstrap.yml | 29 ++ .../hw-jindie/src/main/resources/logback.xml | 74 +++ hw-modules/hw-tdengine/pom.xml | 106 ++++ .../hw/tdengine/HwTdengineApplication.java | 34 ++ .../controller/TdEngineController.java | 483 ++++++++++++++++++ .../tdengine/mapper/DeviceStatusMapper.java | 44 ++ .../hw/tdengine/mapper/TdEngineMapper.java | 177 +++++++ .../service/IDeviceStatusService.java | 36 ++ .../hw/tdengine/service/ITdEngineService.java | 142 +++++ .../service/impl/DeviceStatusServiceImpl.java | 99 ++++ .../service/impl/TdEngineServiceImpl.java | 314 ++++++++++++ .../hw-tdengine/src/main/resources/banner.txt | 10 + .../src/main/resources/bootstrap.yml | 29 ++ .../src/main/resources/logback.xml | 74 +++ .../mapper/tdengine/DeviceStatusMapper.xml | 45 ++ .../mapper/tdengine/TdEngineMapper.xml | 329 ++++++++++++ hw-modules/hw-wms/pom.xml | 100 ++++ .../java/com/hw/wms/HwWmsApplication.java | 34 ++ .../controller/WmsRawOutstockController.java | 105 ++++ .../com/hw/wms/domain/WmsRawOutstock.java | 354 +++++++++++++ .../hw/wms/domain/WmsRawOutstockDetail.java | 298 +++++++++++ .../hw/wms/mapper/WmsRawOutstockMapper.java | 87 ++++ .../wms/service/IWmsRawOutstockService.java | 61 +++ .../impl/WmsRawOutstockServiceImpl.java | 131 +++++ .../hw-wms/src/main/resources/banner.txt | 10 + .../hw-wms/src/main/resources/bootstrap.yml | 29 ++ .../hw-wms/src/main/resources/logback.xml | 74 +++ .../mapper/wms/WmsRawOutstockMapper.xml | 209 ++++++++ hw-modules/pom.xml | 3 + pom.xml | 9 +- 62 files changed, 4997 insertions(+), 4 deletions(-) create mode 100644 hw-api/hw-api-tdengine/pom.xml create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/RemoteTdEngineService.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/AlterTagVo.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/DeviceStatus.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdField.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdFieldVo.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdHistorySelectDto.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdReturnDataVo.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSelectDto.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableSelectVo.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableVo.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdTableVo.java create mode 100644 hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/factory/RemoteTdEngineFallbackFactory.java create mode 100644 hw-api/hw-api-tdengine/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/TdEngineConstants.java create mode 100644 hw-common/hw-common-core/src/main/java/com/hw/common/core/enums/DataTypeEnums.java create mode 100644 hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/AddTdSTableColumn.java create mode 100644 hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/InsertTdTable.java create mode 100644 hw-modules/hw-jindie/install-jindie-jar.bat create mode 100644 hw-modules/hw-jindie/k3cloud-webapi-sdk8.0.6.jar create mode 100644 hw-modules/hw-jindie/kdwebapi.properties create mode 100644 hw-modules/hw-jindie/pom.xml create mode 100644 hw-modules/hw-jindie/src/main/java/com/hw/Main.java create mode 100644 hw-modules/hw-jindie/src/main/java/com/hw/jindie/HwJindieApplication.java create mode 100644 hw-modules/hw-jindie/src/main/java/com/hw/jindie/common/SeqHelper.java create mode 100644 hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomer.java create mode 100644 hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomerTest.java create mode 100644 hw-modules/hw-jindie/src/main/resources/banner.txt create mode 100644 hw-modules/hw-jindie/src/main/resources/bootstrap.yml create mode 100644 hw-modules/hw-jindie/src/main/resources/logback.xml create mode 100644 hw-modules/hw-tdengine/pom.xml create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/HwTdengineApplication.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/controller/TdEngineController.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/DeviceStatusMapper.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/TdEngineMapper.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/IDeviceStatusService.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/ITdEngineService.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/DeviceStatusServiceImpl.java create mode 100644 hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/TdEngineServiceImpl.java create mode 100644 hw-modules/hw-tdengine/src/main/resources/banner.txt create mode 100644 hw-modules/hw-tdengine/src/main/resources/bootstrap.yml create mode 100644 hw-modules/hw-tdengine/src/main/resources/logback.xml create mode 100644 hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/DeviceStatusMapper.xml create mode 100644 hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/TdEngineMapper.xml create mode 100644 hw-modules/hw-wms/pom.xml create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/HwWmsApplication.java create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/controller/WmsRawOutstockController.java create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstock.java create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstockDetail.java create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/mapper/WmsRawOutstockMapper.java create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/service/IWmsRawOutstockService.java create mode 100644 hw-modules/hw-wms/src/main/java/com/hw/wms/service/impl/WmsRawOutstockServiceImpl.java create mode 100644 hw-modules/hw-wms/src/main/resources/banner.txt create mode 100644 hw-modules/hw-wms/src/main/resources/bootstrap.yml create mode 100644 hw-modules/hw-wms/src/main/resources/logback.xml create mode 100644 hw-modules/hw-wms/src/main/resources/mapper/wms/WmsRawOutstockMapper.xml diff --git a/hw-api/hw-api-tdengine/pom.xml b/hw-api/hw-api-tdengine/pom.xml new file mode 100644 index 00000000..ddeb7b41 --- /dev/null +++ b/hw-api/hw-api-tdengine/pom.xml @@ -0,0 +1,28 @@ + + + + com.hw + hw-api + 3.6.3 + + 4.0.0 + + hw-api-tdengine + + + hw-api-tdengine时序数据库模块api + + + + + + + com.hw + hw-common-core + + + + + diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/RemoteTdEngineService.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/RemoteTdEngineService.java new file mode 100644 index 00000000..54a7acaa --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/RemoteTdEngineService.java @@ -0,0 +1,66 @@ +package com.hw.tdengine.api; + +import com.hw.common.core.constant.SecurityConstants; +import com.hw.common.core.constant.ServiceNameConstants; +import com.hw.common.core.domain.R; +import com.hw.common.core.validated.tdengine.AddTdSTableColumn; +import com.hw.common.core.validated.tdengine.InsertTdTable; +import com.hw.tdengine.api.domain.*; +import com.hw.tdengine.api.factory.RemoteTdEngineFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +import java.util.List; +import java.util.Map; + +@FeignClient(contextId = "remoteTdEngineService", value = ServiceNameConstants.TDENGINE_SERVICE, fallbackFactory = RemoteTdEngineFallbackFactory.class) +public interface RemoteTdEngineService { + + @PostMapping("/tdengine/createDatabase") + R createDataBase(@RequestBody String databaseName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/createSuperTable") + R createSuperTable(@Validated @RequestBody TdSuperTableVo tdSuperTableVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/createTable") + R createTable(@Validated @RequestBody TdTableVo tdTableVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/addSuperTableColumn") + R addSuperTableColumn(@Validated(AddTdSTableColumn.class) @RequestBody TdSuperTableVo tdSuperTableVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/modifySuperTableColumn") + R modifySuperTableColumn(@Validated(AddTdSTableColumn.class) @RequestBody TdSuperTableVo tdSuperTableVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/dropSuperTableColumn") + R dropColumnForSuperTable(@Validated(AddTdSTableColumn.class) @RequestBody TdSuperTableVo tdSuperTableVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/alterTableTag") + public R alterTableTag(@Validated @RequestBody AlterTagVo alterTagVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/insertTable") + R insertTable(@Validated(InsertTdTable.class) @RequestBody TdTableVo tdTableVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getLatestData") + R getLatestData(@Validated @RequestBody TdSelectDto tdSelectDto, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getLatestDataByTags") + R>> getLatestDataByTags(@Validated @RequestBody TdSuperTableSelectVo tdSuperTableSelectVo, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getHistoryData") + R getHistoryData(@Validated @RequestBody TdHistorySelectDto tdHistorySelectDto, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getCountOfHistoryData") + R getCountOfHistoryData(@Validated @RequestBody TdHistorySelectDto tdHistorySelectDto, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getOnlineDevicesGroupByDay") + R getOnlineDevicesGroupByDay(@Validated @RequestBody DeviceStatus queryDeviceStatus, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getLastOnlineDevices") + R getLastOnlineDevices(@Validated @RequestBody DeviceStatus queryDeviceStatus, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/tdengine/getDeviceStatusList") + R>> getDeviceStatusList(@Validated @RequestBody DeviceStatus queryDeviceStatus, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/AlterTagVo.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/AlterTagVo.java new file mode 100644 index 00000000..e893304f --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/AlterTagVo.java @@ -0,0 +1,33 @@ +package com.hw.tdengine.api.domain; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @Description: 更新子表tag实体类 + * @ClassName: AlterTagVo + * @Author : xins + * @Date :2023-08-30 10:56 + * @Version :1.0 + */ +@Data +public class AlterTagVo { + + //数据库名称 + @NotBlank(message="databaseName cannot be empty") + private String databaseName; + + //子表名称 + @NotBlank(message="tableName cannot be empty") + private String tableName; + + @NotBlank(message="tagName cannot be empty") + private String tagName; + + @NotNull(message="tagValue cannot be empty") + private Object tagValue; + + +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/DeviceStatus.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/DeviceStatus.java new file mode 100644 index 00000000..b0aefc48 --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/DeviceStatus.java @@ -0,0 +1,104 @@ +package com.hw.tdengine.api.domain; + +import com.hw.common.core.web.domain.BaseEntity; + +import java.util.List; + +/** + * @Description: 设备状态对象 + * @ClassName: DeviceStatus + * @Author : xins + * @Date :2023-09-05 11:35 + * @Version :1.0 + */ +public class DeviceStatus extends BaseEntity { + + private Long ts; + + private Long deviceId; + + private String deviceCode; + + private Integer deviceType; + + private Integer onlineStatus; + + private Long sceneId; + + private Long startTime; + private Long endTime; + + private List schemaFields; + + public Long getTs() { + return ts; + } + + public void setTs(Long ts) { + this.ts = ts; + } + + public Long getDeviceId() { + return deviceId; + } + + public void setDeviceId(Long deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceCode() { + return deviceCode; + } + + public void setDeviceCode(String deviceCode) { + this.deviceCode = deviceCode; + } + + public Integer getDeviceType() { + return deviceType; + } + + public void setDeviceType(Integer deviceType) { + this.deviceType = deviceType; + } + + public Integer getOnlineStatus() { + return onlineStatus; + } + + public void setOnlineStatus(Integer onlineStatus) { + this.onlineStatus = onlineStatus; + } + + public Long getSceneId() { + return sceneId; + } + + public void setSceneId(Long sceneId) { + this.sceneId = sceneId; + } + + public Long getStartTime() { + return startTime; + } + + public void setStartTime(Long startTime) { + this.startTime = startTime; + } + + public Long getEndTime() { + return endTime; + } + + public void setEndTime(Long endTime) { + this.endTime = endTime; + } + + public List getSchemaFields() { + return schemaFields; + } + + public void setSchemaFields(List schemaFields) { + this.schemaFields = schemaFields; + } +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdField.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdField.java new file mode 100644 index 00000000..683f6f1e --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdField.java @@ -0,0 +1,40 @@ +package com.hw.tdengine.api.domain; + +import com.hw.common.core.enums.DataTypeEnums; +import lombok.Data; + +/** + * @ClassName: TdField + * @Author : xins + * @Date :2023-08-28 9:47 + * @Description: TDengine filed,建表的字段实体类 + * @Version :1.0 + */ +@Data +public class TdField { + + //字段名称 + private String fieldName; + + //字段值 + private Object fieldValue; + + //字段数据类型code + private Integer dataTypeCode; + + //字段数据类型 + private String dataType; + + //字段字节大小 + private Integer size; + + public TdField() { + } + + public TdField(String fieldName, Integer dataTypeCode, Integer size) { + this.fieldName = fieldName; + this.dataTypeCode = dataTypeCode; + this.dataType = DataTypeEnums.dataCodeTypeMap.get(dataTypeCode); + this.size = size; + } +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdFieldVo.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdFieldVo.java new file mode 100644 index 00000000..2f73e9fb --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdFieldVo.java @@ -0,0 +1,68 @@ +package com.hw.tdengine.api.domain; + +import com.hw.common.core.enums.DataTypeEnums; +import org.apache.commons.lang3.StringUtils; +import org.springframework.validation.annotation.Validated; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description: Tdengine 创建表的字段实体类 + * @ClassName: TdFieldsVo + * @Author : xins + * @Date :2023-08-28 14:34 + * @Version :1.0 + */ +public class TdFieldVo { + private static final long serialVersionUID = 1L; + + private String fieldName; + + private String dataType; + + private Integer size; + + + public TdFieldVo(String fieldName, Integer dataTypeCode, Integer size) { + this.fieldName = fieldName; + this.dataType = DataTypeEnums.dataCodeTypeMap.get(dataTypeCode); + this.size = size; + } + + /** + * @description 将TdFields转换为创建表的实体类TdFieldsVo,主要是datatype转换 + * @author xins + * @date 2023-08-28 14:40 + * @param field + * @return TdFieldsVo + */ + public static TdFieldVo convertField(@Validated TdField field) throws SQLException { + String fieldName = field.getFieldName(); + Integer datatypeCode = field.getDataTypeCode(); + Integer size = field.getSize(); + if(StringUtils.isBlank(fieldName) || datatypeCode == null){ + throw new SQLException("fieldname and datatype cannot be null"); + } + TdFieldVo tdFieldsVo = new TdFieldVo(fieldName, datatypeCode, size); + return tdFieldsVo; + } + + + /** + * @description 批量转换创建表的字段 + * @author xins + * @date 2023-08-28 14:58 + * @param fieldList + * @return List + */ + public static List batchConvertFields(List fieldList) throws SQLException{ + List tdFieldsVoList = new ArrayList<>(); + for(TdField tdField:fieldList){ + tdFieldsVoList.add(convertField(tdField)); + } + return tdFieldsVoList; + } + +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdHistorySelectDto.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdHistorySelectDto.java new file mode 100644 index 00000000..bc3f756d --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdHistorySelectDto.java @@ -0,0 +1,40 @@ +package com.hw.tdengine.api.domain; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +public class TdHistorySelectDto { + + //数据库名称 + @NotBlank(message="databaseName cannot be empty") + private String databaseName; + + //子表名称 + @NotBlank(message="tableName cannot be empty") + private String tableName; + + //控制输出条数 + private int limit; + + //指定从第几条之后输出(例如:limit 2,5,输出第3行到第7行的数据) + private int offset; + + /** + * 第一个字段,数据类型必须是timestamp + */ + private String firstFieldName; + + //过滤条件:开始时间 + private long startTime; + + //过滤条件:结束时间 + private long endTime; + + //排序字段 + private String orderByFieldName; + + //排序方式 + private String sort; +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdReturnDataVo.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdReturnDataVo.java new file mode 100644 index 00000000..07a01ddd --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdReturnDataVo.java @@ -0,0 +1,13 @@ +package com.hw.tdengine.api.domain; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class TdReturnDataVo { + + public int count; + public List> dataList; +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSelectDto.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSelectDto.java new file mode 100644 index 00000000..fd885fd8 --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSelectDto.java @@ -0,0 +1,35 @@ +package com.hw.tdengine.api.domain; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @Description: TDengine查询数据入参数据传输对象 + * @ClassName: TdSelectDto + * @Author : xins + * @Date :2023-08-29 10:39 + * @Version :1.0 + */ +@Data +public class TdSelectDto { + + //数据库名称 + @NotBlank(message="databaseName cannot be empty") + private String databaseName; + + //子表名称 + @NotBlank(message="tableName cannot be empty") + private String tableName; + + //超级表名称 + private String superTableName; + + //tags名称 + private String tagsName; + + private List schemaFieldValues; + +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableSelectVo.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableSelectVo.java new file mode 100644 index 00000000..c923b119 --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableSelectVo.java @@ -0,0 +1,52 @@ +package com.hw.tdengine.api.domain; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * @Description: 用于查询超级表最新数据 + * @ClassName: TdSuperTableSelectVo + * @Author : xins + * @Date :2023-09-16 13:48 + * @Version :1.0 + */ +@Data +public class TdSuperTableSelectVo { + //数据库名称 + @NotBlank(message="databaseName cannot be empty") + private String databaseName; + + //超级表名称 + @NotBlank(message="superTableName cannot be empty") + private String superTableName; + + //group by tags名称 + @NotBlank(message="groupByTagsName cannot be empty") + private String groupByTagsName; + + //控制输出条数 + private int limit; + + //指定从第几条之后输出(例如:limit 2,5,输出第3行到第7行的数据) + private int offset; + + /** + * 第一个字段,数据类型必须是timestamp + */ + private String firstFieldName; + + //过滤条件:开始时间 + private long startTime; + + //过滤条件:结束时间 + private long endTime; + + private String deviceCode; + + private String deviceName; + + private List schemaFieldValues; + +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableVo.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableVo.java new file mode 100644 index 00000000..499b5391 --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdSuperTableVo.java @@ -0,0 +1,57 @@ +package com.hw.tdengine.api.domain; + +import com.hw.common.core.validated.tdengine.AddTdSTableColumn; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @ClassDescription: 创建超级表需要的入参的实体类 + * @ClassName: TdSuperTableVo + * @Author: xins + * @Date: 2023-08-28 09:30:45 + * @Version 1.0 + */ +@Data +public class TdSuperTableVo { + + //数据库名称 + @NotEmpty(message="databaseName cannot be empty",groups = AddTdSTableColumn.class) + private String databaseName; + + //超级表名称 + @NotEmpty(message="supertableName cannot be empty",groups = AddTdSTableColumn.class) + private String superTableName; + + /** + * 第一个字段,数据类型必须是timestamp + */ + private String firstFieldName; + + /** + * 超级表的表结构Schema,根据物模型创建 + * 第一列必须为时间戳timestamp,也就是第一个字段的数据类型必须是timestamp + * 字符相关数据类型必须指定大小 + * 字段名称和字段数据类型不能为空 + */ + @NotEmpty(message="schemeFields cannot be empty") + private List schemaFields; + + + /** + * 超级表的标签字段Schema,采集点的静态属性,可以作为子表在超级表里的标识 + * TAGS 列名不能与其他列名相同。 + * TAGS 列名不能为预留关键字。 + * TAGS 最多允许 128 个,至少 1 个,总长度不超过 16 KB。 + */ + @NotEmpty(message="tagsFields cannot be empty") + private List tagsFields; + + + /** + * 字段信息对象,超级表添加列时使用该属性 + */ + private TdField field; + +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdTableVo.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdTableVo.java new file mode 100644 index 00000000..045a25bf --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/domain/TdTableVo.java @@ -0,0 +1,42 @@ +package com.hw.tdengine.api.domain; + +import com.hw.common.core.validated.tdengine.InsertTdTable; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @Description: 子表实体类 + * @ClassName: TdTableVo + * @Author : xins + * @Date :2023-08-28 15:45 + * @Version :1.0 + */ +@Data +public class TdTableVo { + + //数据库名称 + @NotEmpty(message="databaseName cannot be empty",groups = InsertTdTable.class) + private String databaseName; + + //超级表名称 + @NotEmpty(message="supertableName cannot be empty") + private String superTableName; + + //子表名称 + @NotEmpty(message="tableName cannot be empty",groups = InsertTdTable.class) + private String tableName; + + /** + * 子表的标签字段的值,创建子表时标签字段值的数据类型要与超级表的对应上 + */ + private List tagsFieldValues; + + /** + * 子表的schema字段的值,掺入子表时schema字段值的数据类型要与超级表的对应上 + */ + @NotEmpty(message="schemaFields cannot be empty",groups = InsertTdTable.class) + private List schemaFields; + +} diff --git a/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/factory/RemoteTdEngineFallbackFactory.java b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/factory/RemoteTdEngineFallbackFactory.java new file mode 100644 index 00000000..b82c4a6b --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/java/com/hw/tdengine/api/factory/RemoteTdEngineFallbackFactory.java @@ -0,0 +1,107 @@ +package com.hw.tdengine.api.factory; + +import com.alibaba.fastjson2.JSONObject; +import com.hw.common.core.domain.R; +import com.hw.tdengine.api.RemoteTdEngineService; +import com.hw.tdengine.api.domain.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * @Description: TDengine服务降级处理 + * @ClassName: RemoteTdEngineFallbackFactory + * @Author : xins + * @Date :2023-09-01 14:14 + * @Version :1.0 + */ +@Component +public class RemoteTdEngineFallbackFactory implements FallbackFactory { + + private static final Logger log = LoggerFactory.getLogger(RemoteTdEngineFallbackFactory.class); + + @Override + public RemoteTdEngineService create(Throwable throwable) { + return new RemoteTdEngineService() { + @Override + public R createDataBase(String databaseName, String source) { + return R.fail("创建数据库失败:" + throwable.getMessage()); + } + + @Override + public R createSuperTable(TdSuperTableVo tdSuperTableVo, String source) { + return R.fail("创建超级表失败:" + throwable.getMessage()); + } + + @Override + public R createTable(TdTableVo tdTableVo, String source) { + return R.fail("创建子表失败:" + throwable.getMessage()); + } + + @Override + public R addSuperTableColumn(TdSuperTableVo tdSuperTableVo, String source) { + return R.fail("添加超级表Column失败:" + throwable.getMessage()); + } + + @Override + public R modifySuperTableColumn(TdSuperTableVo tdSuperTableVo, String source) { + return R.fail("修改超级表Column失败:" + throwable.getMessage()); + } + + @Override + public R dropColumnForSuperTable(TdSuperTableVo tdSuperTableVo, String source) { + return R.fail("删除超级表Column失败:" + throwable.getMessage()); + } + + @Override + public R alterTableTag(AlterTagVo alterTagVo, String source) { + return R.fail("修改子表tag值失败:" + throwable.getMessage()); + } + + @Override + public R insertTable(TdTableVo tdTableVo, String source) { + return R.fail("插入数据失败:" + throwable.getMessage()); + } + + @Override + public R getLatestData(TdSelectDto tdSelectDto, String source) { + return R.fail("获取子表最新数据失败:" + throwable.getMessage()); + } + + @Override + public R>> getLatestDataByTags(TdSuperTableSelectVo tdSuperTableSelectVo, String source) { + return R.fail("根据tags获取超级表最新数据失败:" + throwable.getMessage()); + } + + @Override + public R getHistoryData(TdHistorySelectDto tdHistorySelectDto, String source) { + return R.fail("获取历史数据失败:" + throwable.getMessage()); + } + + @Override + public R getCountOfHistoryData(TdHistorySelectDto tdHistorySelectDto, String source) { + return R.fail("获取历史数据数量失败:" + throwable.getMessage()); + } + + @Override + public R getOnlineDevicesGroupByDay(DeviceStatus queryDeviceStatus, String source) { + return R.fail("获取设备状态失败:" + throwable.getMessage()); + } + + @Override + public R getLastOnlineDevices(DeviceStatus queryDeviceStatus, String source) { + return R.fail("获取最近设备状态失败:" + throwable.getMessage()); + } + + @Override + public R>> getDeviceStatusList(DeviceStatus queryDeviceStatus, String source) { + return R.fail("获取设备状态信息失败:" + throwable.getMessage()); + } + + }; + } +} diff --git a/hw-api/hw-api-tdengine/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/hw-api/hw-api-tdengine/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..e1a853bb --- /dev/null +++ b/hw-api/hw-api-tdengine/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.hw.tdengine.api.factory.RemoteTdEngineFallbackFactory diff --git a/hw-api/pom.xml b/hw-api/pom.xml index 649a6045..da4265be 100644 --- a/hw-api/pom.xml +++ b/hw-api/pom.xml @@ -10,6 +10,7 @@ hw-api-system + hw-api-tdengine hw-api diff --git a/hw-common/hw-common-core/pom.xml b/hw-common/hw-common-core/pom.xml index 3fdeb41e..59e37dc3 100644 --- a/hw-common/hw-common-core/pom.xml +++ b/hw-common/hw-common-core/pom.xml @@ -10,7 +10,7 @@ 4.0.0 hw-common-core - + hw-common-core核心模块 @@ -22,7 +22,7 @@ org.springframework.cloud spring-cloud-starter-openfeign - + org.springframework.cloud @@ -113,6 +113,11 @@ swagger-annotations + + org.projectlombok + lombok + + diff --git a/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/ServiceNameConstants.java b/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/ServiceNameConstants.java index 9e21c915..a66809aa 100644 --- a/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/ServiceNameConstants.java +++ b/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/ServiceNameConstants.java @@ -2,7 +2,7 @@ package com.hw.common.core.constant; /** * 服务名称 - * + * * @author ruoyi */ public class ServiceNameConstants @@ -21,4 +21,9 @@ public class ServiceNameConstants * 文件服务的serviceid */ public static final String FILE_SERVICE = "hw-file"; + + /** + * Tdengine服务的serviceid + */ + public static final String TDENGINE_SERVICE = "hw-tdengine"; } diff --git a/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/TdEngineConstants.java b/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/TdEngineConstants.java new file mode 100644 index 00000000..dd4c5699 --- /dev/null +++ b/hw-common/hw-common-core/src/main/java/com/hw/common/core/constant/TdEngineConstants.java @@ -0,0 +1,134 @@ +package com.hw.common.core.constant; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Description: TdEngine服务常量 + * @ClassName: TdEngineConstants + * @Author : xins + * @Date :2023-09-05 9:47 + * @Version :1.0 + */ +public class TdEngineConstants { + + public static final String DEFAULT_FIRST_FIELD_NAME = "ts";//timestamp格式首字段,必须 + + public static final String DEFAULT_ORDER_BY_MODE = "desc";//默认排序方式,逆序 + + public static final String PAYLOAD_TS = "timestamp";//协议上传ts的key + + public static final String PAYLOAD_PARAM = "param";//协议上传的param key + + public static final String PAYLOAD_DATATYPE = "datatype";//协议上传数据数据类型的key + public static final String PAYLOAD_DATAVALUE = "datavalue";//协议上传数据的key + + public static final String PAYLOAD_DEVICE_CODE = "uid";//协议上传设备编号的key + + public static final String PAYLOAD_DEVICE_DATA_TYPE_IMAGE = "image";//协议上传图片的key + + public static final String PAYLOAD_DEVICE_DATA_TYPE_TYPE = "type";//协议上传图片类型的key + public static final String PAYLOAD_DEVICE_DATA_TYPE_TYPE_FIRSET_UPPER = "Type";//协议上传图片类型的key + +// public static final String DEFAULT_DB_NAME_PREFIX = "db_scene_";//数据库名称前缀 + + public static final String DEFAULT_SUPER_TABLE_NAME_PREFIX = "st_devicemode_";//超级表名称前缀 + public static final String DEFAULT_TABLE_NAME_PREFIX = "t_device_";//数据表名称前缀 + + public static final String DEFAULT_DEVICE_STATUS_SUPER_TABLE_NAME= "st_ds";//设备状态超级表名称 + public static final String DEFAULT_DEVICE_STATUS_TABLE_NAME_PREFIX = "t_ds_";//设备状态数据表名称前缀 + + public static final String PLATFORM_DB_NAME = "db_hwsaas";//全局数据库名称 + + public static final String ST_TAG_DEVICECODE = "devicecode"; + public static final int ST_TAG_DEVICECODE_TYPE = 10; + public static final int ST_TAG_DEVICECODE_SIZE=50; + + public static final String ST_TAG_DEVICENAME = "devicename"; + + public static final String ST_TAG_DEVICETYPE = "devicetype"; + public static final String ST_TAG_ONLINESTATUS = "onlinestatus"; + public static final int ST_TAG_DEVICENAME_TYPE = 10; + public static final int ST_TAG_DEVICENAME_SIZE=200; + + public static final String ST_TAG_DEVICEID = "deviceid"; + public static final int ST_TAG_DEVICEID_TYPE = 2; + + public static final String ST_TAG_DEVICEMODEID = "devicemodeid"; + public static final int ST_TAG_DEVICEMODEID_TYPE = 2; + + public static final String ST_TAG_MONITORUNITID = "monitorunitid"; + + public static final String ST_TAG_SCENEID = "sceneid"; + + public static final int ST_TAG_MONITORUNITID_TYPE = 2; + + /** + * 需要转换的key + */ + public static final Map TDENGINE_KEY_TRANSFER_MAP = new HashMap(); + + static { + TDENGINE_KEY_TRANSFER_MAP.put("value", "value1"); + } + + /** + * 禁止的功能标识符 + */ + public static final List ABNDON_FUNCTION_IDENTIFIERS = Arrays.asList("ts","value1","type"); + + public static final Map DEVICE_DATA_COLUMN_MAP = new HashMap(); + + static { + DEVICE_DATA_COLUMN_MAP.put(ST_TAG_DEVICECODE, "设备编号"); + DEVICE_DATA_COLUMN_MAP.put(ST_TAG_DEVICENAME, "设备名称"); + DEVICE_DATA_COLUMN_MAP.put(DEFAULT_FIRST_FIELD_NAME, "时间"); + } + + /** + * @return String + * @param: sceneId + * @description 获取数据库名称 + * @author xins + * @date 2023-09-05 9:42 + */ + public static String getDatabaseName() { + return PLATFORM_DB_NAME; + } + + /** + * @param: deviceModeId + * @description 获取超级表名称 + * @author xins + * @date 2023-09-16 14:42 + * @return String + */ + public static String getSuperTableName(Long deviceModeId) { + return DEFAULT_SUPER_TABLE_NAME_PREFIX + deviceModeId; + } + + /** + * @param: deviceId + * @return String + * @description 获取设备数据子表名称 + * @author xins + * @date 2023-09-05 9:42 + */ + public static String getDeviceDataTableName(Long deviceId) { + return DEFAULT_TABLE_NAME_PREFIX + deviceId; + } + + /** + * @param: deviceId + * @return String + * @description 获取设备状态信息数据子表名称 + * @author xins + * @date 2023-09-05 9:42 + */ + public static String getDeviceStatusTableName(Long deviceId) { + return DEFAULT_DEVICE_STATUS_TABLE_NAME_PREFIX + deviceId; + } + +} diff --git a/hw-common/hw-common-core/src/main/java/com/hw/common/core/enums/DataTypeEnums.java b/hw-common/hw-common-core/src/main/java/com/hw/common/core/enums/DataTypeEnums.java new file mode 100644 index 00000000..2d352d72 --- /dev/null +++ b/hw-common/hw-common-core/src/main/java/com/hw/common/core/enums/DataTypeEnums.java @@ -0,0 +1,115 @@ +package com.hw.common.core.enums; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public enum DataTypeEnums { + + /** + * 时间戳。缺省精度毫秒,可支持微秒和纳秒 + */ + TIMESTAMP(1,"timestamp"), + + /** + * 整型,范围 [-2^31, 2^31-1] + */ + INT(2, "int"), + + /** + * 长整型,范围 [-2^63, 2^63-1] + */ + BIGINT(3, "bigint"), + + /** + * 浮点型,有效位数 6-7,范围 [-3.4E38, 3.4E38] + */ + FLOAT(4, "float"), + + /** + * 双精度浮点型,有效位数 15-16,范围 [-1.7E308, 1.7E308] + */ + DOUBLE(5, "double"), + + /** + * 记录单字节字符串,建议只用于处理 ASCII 可见字符,最大长度16000,中文等多字节字符需使用 NCHAR + */ + BINARY(6, "binary"), + + /** + * 短整型, 范围 [-32768, 32767] + */ + SMALLINT(7, "smallint"), + + /** + * 单字节整型,范围 [-128, 127] + */ + TINYINT(8, "tinyint"), + + /** + * 布尔型,{true, false} + */ + BOOL(9, "bool"), + + /** + * 记录包含多字节字符在内的字符串,如中文字符。 + * 每个 NCHAR 字符占用 4 字节的存储空间。字符串两端使用单引号引用,字符串内的单引号需用转义字符 \'。 + * NCHAR 使用时须指定字符串大小,类型为 NCHAR(10) 的列表示此列的字符串最多存储 10 个 NCHAR 字符。 + * 如果用户字符串长度超出声明长度,将会报错。最大长度4093 + */ + NCHAR(10, "nchar"), + + + /** + * json数据类型 只有tag类型可以是json格式 + */ + JSON(11, "json"); + + // 性别值数组 + public static final List genderValueList = Arrays.stream(values()).map(DataTypeEnums::getDataCode).collect(Collectors.toList()); + + // 性别名数组 + public static final List genderNameList = Arrays.stream(values()).map(DataTypeEnums::getDataType).collect(Collectors.toList()); + + // 属性值与属性名Map + public static final Map dataCodeTypeMap = Arrays.stream(values()).collect( + Collectors.toMap( + DataTypeEnums::getDataCode, + DataTypeEnums::getDataType + ) + ); + + // 属性名与属性值Map + public static final Map dataTypeCodeMap = Arrays.stream(values()).collect( + Collectors.toMap( + DataTypeEnums::getDataType, + DataTypeEnums::getDataCode + ) + ); + + // 性别值 + private final Integer dataCode; + + // 性别名 + private final String dataType; + + DataTypeEnums(Integer dataCode, String dataType) { + this.dataCode = dataCode; + this.dataType = dataType; + } + + public Integer getDataCode() { + return dataCode; + } + + public String getDataType() { + return dataType; + } + + + public static void main(String[] args) { + System.out.println(dataCodeTypeMap.get(1)); + } +} diff --git a/hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/AddTdSTableColumn.java b/hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/AddTdSTableColumn.java new file mode 100644 index 00000000..2877c5b8 --- /dev/null +++ b/hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/AddTdSTableColumn.java @@ -0,0 +1,4 @@ +package com.hw.common.core.validated.tdengine; + +public interface AddTdSTableColumn { +} diff --git a/hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/InsertTdTable.java b/hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/InsertTdTable.java new file mode 100644 index 00000000..79c26cf2 --- /dev/null +++ b/hw-common/hw-common-core/src/main/java/com/hw/common/core/validated/tdengine/InsertTdTable.java @@ -0,0 +1,4 @@ +package com.hw.common.core.validated.tdengine; + +public interface InsertTdTable { +} diff --git a/hw-modules/hw-gen/src/main/resources/bootstrap.yml b/hw-modules/hw-gen/src/main/resources/bootstrap.yml index b509e365..c50ece62 100644 --- a/hw-modules/hw-gen/src/main/resources/bootstrap.yml +++ b/hw-modules/hw-gen/src/main/resources/bootstrap.yml @@ -15,9 +15,13 @@ spring: discovery: # 服务注册地址 server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP config: # 配置中心地址 server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP # 配置文件格式 file-extension: yml # 共享配置 diff --git a/hw-modules/hw-jindie/install-jindie-jar.bat b/hw-modules/hw-jindie/install-jindie-jar.bat new file mode 100644 index 00000000..6baf4b8a --- /dev/null +++ b/hw-modules/hw-jindie/install-jindie-jar.bat @@ -0,0 +1,2 @@ +call mvn install:install-file -Dfile=D:\IdeaProjects\HwMes\hw-modules\hw-jindie\k3cloud-webapi-sdk8.0.6.jar -DgroupId=com.kingdee.bos -DartifactId=k3cloud-webapi-sdk -Dversion=8.0.6 -Dpackaging=jar +@pause \ No newline at end of file diff --git a/hw-modules/hw-jindie/k3cloud-webapi-sdk8.0.6.jar b/hw-modules/hw-jindie/k3cloud-webapi-sdk8.0.6.jar new file mode 100644 index 0000000000000000000000000000000000000000..7847a05482fb94030865cd963ed5772cc5415e8c GIT binary patch literal 68092 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

HEfxHCqnQD$;V_J`w*n?@kxy?%?aM z2d4KgAL;*t^y;XuZ)k5X_?M0O-{p^g&HZ2pKTlbHFiC0G`N*vlAOyuusqH|V_#_&T zk_BdMSQ?8qN}iE9xp;gDKxA=kw}7s)VQU-s{c2fthHqDrmp8-SPah*Qzh!JK@j+9< zgJO`b$T{R4EnGDP;De5%(^%Y5y*+lY-{fYmN)zLuaT(G`74CXV54ju*0)0gLxbATv z=T4hS4AB1!zVpl8shdQ(L%#N=FMAN^yX|T8qS>i&#(2r4|Am*Q4Sav6K>#J(PJ`8& z=&PP0&kcuI7I(^rp5$luE*iVvw ?cm1R@&j@h%SW!}D661nPP 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 +X-KDApi-AcctID =657953112be3c4 +# +X-KDApi-AppID=265686_SY0M09lvzkp/18wG4/RBRa1GRs2V2pls +# +X-KDApi-AppSec =317a6730c9ec4fafa757ca4a3499d059 +# +X-KDApi-UserName =张铭潇 +# +X-KDApi-LCID=2052 +# +X-KDApi-ServerUrl=http://1.15.183.55/k3cloud/ diff --git a/hw-modules/hw-jindie/pom.xml b/hw-modules/hw-jindie/pom.xml new file mode 100644 index 00000000..2fcb0624 --- /dev/null +++ b/hw-modules/hw-jindie/pom.xml @@ -0,0 +1,115 @@ + + + + com.hw + hw-modules + 3.6.3 + + 4.0.0 + + hw-modules-jindie + + + hw-modules-jindie金蝶ERP对接模块 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + com.mysql + mysql-connector-j + + + + + com.hw + hw-common-datasource + + + + + com.hw + hw-common-datascope + + + + + com.hw + hw-common-log + + + + + com.hw + hw-common-swagger + + + + com.kingdee.bos + k3cloud-webapi-sdk + 8.0.6 + + + junit + junit + + + com.google.code.gson + gson + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/hw-modules/hw-jindie/src/main/java/com/hw/Main.java b/hw-modules/hw-jindie/src/main/java/com/hw/Main.java new file mode 100644 index 00000000..0dcc1da8 --- /dev/null +++ b/hw-modules/hw-jindie/src/main/java/com/hw/Main.java @@ -0,0 +1,19 @@ +package com.hw; + +// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`, +// then press Enter. You can now see whitespace characters in your code. +public class Main { + public static void main(String[] args) { + // Press Alt+Enter with your caret at the highlighted text to see how + // IntelliJ IDEA suggests fixing it. + System.out.printf("Hello and welcome!"); + + // Press Shift+F10 or click the green arrow button in the gutter to run the code. + for (int i = 1; i <= 5; i++) { + + // Press Shift+F9 to start debugging your code. We have set one breakpoint + // for you, but you can always add more by pressing Ctrl+F8. + System.out.println("i = " + i); + } + } +} \ No newline at end of file diff --git a/hw-modules/hw-jindie/src/main/java/com/hw/jindie/HwJindieApplication.java b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/HwJindieApplication.java new file mode 100644 index 00000000..c2c95b2e --- /dev/null +++ b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/HwJindieApplication.java @@ -0,0 +1,34 @@ +package com.hw.jindie; + +import com.hw.common.security.annotation.EnableCustomConfig; +import com.hw.common.security.annotation.EnableRyFeignClients; +import com.hw.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 系统模块 + * + * @author ruoyi + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class HwJindieApplication +{ + public static void main(String[] args) + { + SpringApplication.run(HwJindieApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 金蝶ERP对接模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/hw-modules/hw-jindie/src/main/java/com/hw/jindie/common/SeqHelper.java b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/common/SeqHelper.java new file mode 100644 index 00000000..e949f05b --- /dev/null +++ b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/common/SeqHelper.java @@ -0,0 +1,22 @@ +package com.hw.jindie.common; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class SeqHelper { + static long seq = 0L; + static int mod = 10000; + + public SeqHelper() { + } + + public static long genSeq() { + return ++seq; + } + + public static String genNumber(String pre) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMHHmmss"); + String id = sdf.format(new Date()); + return pre == null ? id + (genSeq() + (long)mod) : pre + id + (genSeq() + (long)mod); + } +} diff --git a/hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomer.java b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomer.java new file mode 100644 index 00000000..4b8f65a1 --- /dev/null +++ b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomer.java @@ -0,0 +1,50 @@ +package com.hw.jindie.test; + +public class BdCustomer { + long CustID; + String FNumber; + String FName; + Long msterID; + + public long getCustID() { + return CustID; + } + + public void setCustID(long custID) { + CustID = custID; + } + + public String getFNumber() { + return FNumber; + } + + public void setFNumber(String FNumber) { + this.FNumber = FNumber; + } + + public String getFName() { + return FName; + } + + public void setFName(String FName) { + this.FName = FName; + } + + public String getFShortName() { + return FShortName; + } + + public void setFShortName(String FShortName) { + this.FShortName = FShortName; + } + + String FShortName; + + public Long getMsterID() { + return msterID; + } + + public void setMsterID(Long msterID) { + this.msterID = msterID; + } +} diff --git a/hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomerTest.java b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomerTest.java new file mode 100644 index 00000000..be2c04fc --- /dev/null +++ b/hw-modules/hw-jindie/src/main/java/com/hw/jindie/test/BdCustomerTest.java @@ -0,0 +1,237 @@ +package com.hw.jindie.test; + +import static org.junit.Assert.fail; + +import java.util.*; + +import com.google.gson.JsonObject; +import com.kingdee.bos.webapi.entity.*; +import com.kingdee.bos.webapi.sdk.K3CloudApi; +import com.google.gson.Gson; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import com.hw.jindie.common.SeqHelper; + + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class BdCustomerTest { + static String FNumber = SeqHelper.genNumber("BC"); + static String FNumber2 = SeqHelper.genNumber("BC"); + static String FName = "aukh_" + UUID.randomUUID().toString(); + static String FShortName = "aukh_" + UUID.randomUUID().toString(); + static K3CloudApi api = new K3CloudApi(); + + + /* 本接口用于实现客户 (BD_Customer) 的保存功能 */ + @Test + public void atestSaveCustomer() throws Exception { + String para = "{\"FName\": "+"\""+FName+"\""+",\"FNumber\": "+"\""+FNumber+"\""+",\"FCreateOrgId\": {\"FNumber\": \"100\"},\"FUseOrgId\": {\"FNumber\": \"100\"},\"FCOUNTRY\": { \"FNumber\": \"China\"},\"FINVOICETITLE\": \"zzl\",\"FCustTypeId\": { \"FNumber\": \"KHLB001_SYS\"},\"FTRADINGCURRID\": { \"FNumber\": \"PRE001\"},\"FInvoiceType\": \"1\",\"FTaxType\": { \"FNumber\": \"SFL02_SYS\"},\"FPriority\": 1,\"FTaxRate\": { \"FNumber\": \"SL02_SYS\"},\"FISCREDITCHECK\": true,\"FIsTrade\": true }"; + Map map = new HashMap<>(); + map = new Gson().fromJson(para, map.getClass()); + SaveResult sRet = api.save("BD_Customer", new SaveParam(map)); + Gson gson = new Gson(); + if (sRet.isSuccessfully()) { + System.out.printf("客户保存接口: %s%n", gson.toJson(sRet.getResult())); + } else { + fail("客户保存接口: " + gson.toJson(sRet.getResult())); + } + } + + /* 本接口用于实现客户 (BD_Customer) 的保存功能 */ + @Test + public void btestSaveCustomer2() throws Exception { + String para2 = "{\"FName\": \"zzl\",\"FNumber\": "+"\""+FNumber2+"\""+",\"FCreateOrgId\": {\"FNumber\": \"100\"},\"FUseOrgId\": {\"FNumber\": \"100\"},\"FCOUNTRY\": { \"FNumber\": \"China\"},\"FINVOICETITLE\": \"zzl\",\"FCustTypeId\": { \"FNumber\": \"KHLB001_SYS\"},\"FTRADINGCURRID\": { \"FNumber\": \"PRE001\"},\"FInvoiceType\": \"1\",\"FTaxType\": { \"FNumber\": \"SFL02_SYS\"},\"FPriority\": 1,\"FTaxRate\": { \"FNumber\": \"SL02_SYS\"},\"FISCREDITCHECK\": true,\"FIsTrade\": true }"; + Map map = new HashMap<>(); + map = new Gson().fromJson(para2, map.getClass()); + SaveResult sRet = api.save("BD_Customer", new SaveParam(map),InvokeMode.Syn); + Gson gson = new Gson(); + if (sRet.isSuccessfully()) { + System.out.printf("客户保存接口: %s%n", gson.toJson(sRet.getResult())); + } else { + fail("客户保存接口: " + gson.toJson(sRet.getResult())); + } + } + + /*本接口用于实现客户 (BD_Customer) 的提交功能*/ + @Test + public void ctestSubmitCustomer() throws Exception { + OperateParam para = new OperateParam(); + List Numbers = Arrays.asList(FNumber);; + para.setNumbers(Numbers); + OperatorResult result = api.submit("BD_Customer", para); + Gson gson = new Gson(); + if (result.getResult().getResponseStatus().isIsSuccess()) { + System.out.printf("客户提交接口: %s%n", gson.toJson(result.getResult())); + } else { + fail("客户提交接口:" + gson.toJson(result.getResult())); + } + + } + + /*本接口用于实现客户 (BD_Customer) 的审核功能*/ + @Test + public void dtestauditCustomer() throws Exception { + OperateParam para = new OperateParam(); + List Numbers = Arrays.asList(FNumber);; + para.setNumbers(Numbers); + OperatorResult result = api.audit("BD_Customer", para ); + Gson gson = new Gson(); + if (result.getResult().getResponseStatus().isIsSuccess()) { + System.out.printf("客户审核接口: %s%n", gson.toJson(result.getResult())); + } else { + fail("客户审核接口: " + gson.toJson(result.getResult().getResponseStatus())); + } + + } + + /*本接口用于实现客户 (BD_Customer) 的反审核功能*/ + @Test + public void etestunauditCustomer() throws Exception { + OperateParam para = new OperateParam(); + List Numbers = Arrays.asList(FNumber); + para.setNumbers(Numbers); + OperatorResult result = api.unAudit("BD_Customer", para); + Gson gson = new Gson(); + if (result.getResult().getResponseStatus().isIsSuccess()) { + + System.out.printf("客户反审核接口: %s%n", gson.toJson(result.getResult())); + } else { + fail("客户反审核接口: " + gson.toJson(result.getResult().getResponseStatus())); + } + } + + + /*本接口用于实现客户 (BD_Customer) 的禁用功能*/ + @Test + public void ftestForbidCustomer() throws Exception { + JsonObject jsonData = new JsonObject(); + jsonData.addProperty("Numbers", FNumber); + String result = api.excuteOperation("BD_Customer","Forbid", jsonData.toString()); + Gson gson = new Gson(); + RepoRet repoRet = gson.fromJson(result, RepoRet.class); + if (repoRet.getResult().getResponseStatus().isIsSuccess()) { + + System.out.printf("客户禁用接口: %s%n", gson.toJson(repoRet.getResult())); + } else { + fail("客户禁用接口: " + gson.toJson(repoRet.getResult().getResponseStatus())); + } + } + + /*本接口用于实现客户 (BD_Customer) 的反禁用功能*/ + @Test + public void gtestenableCustomer() throws Exception { + JsonObject jsonData = new JsonObject(); + jsonData.addProperty("Numbers", FNumber); + String result = api.excuteOperation("BD_Customer","enable", jsonData.toString()); + Gson gson = new Gson(); + RepoRet repoRet = gson.fromJson(result, RepoRet.class); + if (repoRet.getResult().getResponseStatus().isIsSuccess()) { + + System.out.printf("客户反禁用接口: %s%n", gson.toJson(repoRet.getResult())); + } else { + fail("客户反禁用接口: %s%n "+ gson.toJson(repoRet.getResult().getResponseStatus())); + } + } + + /*本接口用于实现客户 (BD_Customer) 的查看功能*/ + @Test + public void htestviewCustomer() throws Exception { + OperateParam para = new OperateParam(); + para.setNumber(FNumber); + OperatorResult result = api.view("BD_Customer", para); + Gson gson = new Gson(); + if (result.getResult().getResponseStatus().isIsSuccess()) { + System.out.printf("客户查看接口: %s%n", gson.toJson(result.getResult())); + } else { + fail("客户查看接口: " + gson.toJson(result.getResult())); + } + } + + /*本接口用于实现客户 (BD_Customer) 的查看功能*/ + @Test + public void htestviewCustomer2() throws Exception { + OperateParam para = new OperateParam(); + para.setNumber(FNumber); + OperatorResult result = api.view("BD_Customer", para, BdCustomer.class); + Gson gson = new Gson(); + System.out.printf("result: %s%n", gson.toJson(result)); + if (result.getResult().getResponseStatus().isIsSuccess()) { + System.out.printf("客户查看接口: %s%n", gson.toJson(result.getResult())); + } else { + fail("客户查看接口: " + gson.toJson(result.getResult())); + } + } + + /*本接口用于实现客户 (BD_Customer)的单据查询功能*/ + @Test + public void itestQueryCustomer() throws Exception { + QueryParam para = new QueryParam(); + para.setFormId("BD_Customer"); + para.setFieldKeys("FNumber,FName,"); + para.setFilterString("Fnumber=\'"+FNumber+"\'"); + Gson gson = new Gson(); + List result = api.executeBillQuery(para,BdCustomer.class); + System.out.println("客户单据查询接口: " + gson.toJson(result)); + + + } + + /*本接口用于实现客户 (BD_Customer) 的删除功能*/ + @Test + public void jtestdeleteCustomer() throws Exception { + OperateParam para = new OperateParam(); + List Numbers = Arrays.asList(FNumber); + para.setNumbers(Numbers); + OperatorResult result = api.delete("BD_Customer",para); + Gson gson = new Gson(); + if (result.getResult().getResponseStatus().isIsSuccess()) { + System.out.printf("客户删除接口: %s%n", gson.toJson(result.getResult())); + } else { + fail("客户删除接口: " + gson.toJson(result.getResult().getResponseStatus())); + } + } + + /*本接口用于实现客户 (BD_Customer) 的批量提交功能*/ + @Test + public void kbatchsaveCustomer() throws Exception { + String para = "[{\"FName\": "+"\""+"aukh_" + UUID.randomUUID().toString()+"\""+",\"FNumber\": "+"\""+SeqHelper.genNumber("KH")+"\""+",\"FCreateOrgId\": {\"FNumber\": \"100\"},\"FUseOrgId\": {\"FNumber\": \"100\"},\"FCOUNTRY\": {\"FNumber\": \"China\"},\"FINVOICETITLE\": \"zzl\",\"FCustTypeId\": {\"FNumber\": \"KHLB001_SYS\"},\"FTRADINGCURRID\": {\"FNumber\": \"PRE001\"},\"FInvoiceType\": \"1\",\"FTaxType\": {\"FNumber\": \"SFL02_SYS\"},\"FPriority\": 1,\"FTaxRate\": {\"FNumber\": \"SL02_SYS\"},\"FISCREDITCHECK\": true,\"FIsTrade\": true},{\"FName\": "+"\""+"auwl_" + UUID.randomUUID().toString()+"\""+",\"FNumber\": "+"\""+SeqHelper.genNumber("KH")+"\""+",\"FCreateOrgId\": {\"FNumber\": \"100\"},\"FUseOrgId\": {\"FNumber\": \"100\"},\"FCOUNTRY\": {\"FNumber\": \"China\"},\"FINVOICETITLE\": \"zzl\",\"FCustTypeId\": {\"FNumber\": \"KHLB001_SYS\"},\"FTRADINGCURRID\": {\"FNumber\": \"PRE001\"},\"FInvoiceType\": \"1\",\"FTaxType\": {\"FNumber\": \"SFL02_SYS\"},\"FPriority\": 1,\"FTaxRate\": {\"FNumber\": \"SL02_SYS\"},\"FISCREDITCHECK\": true,\"FIsTrade\": true}]"; + // 批量保存客户信息 + List> list = new ArrayList<>(); + list = new Gson().fromJson(para, list.getClass()); + SaveResult sRet = api.batchSave("BD_Customer", new BatchSave<>(list)); + Gson gson = new Gson(); + if (sRet.isSuccessfully()) { + System.out.printf("客户批量保存接口: %s%n", gson.toJson(sRet.getResult())); + } else { + fail("客户保存接口: " + gson.toJson(sRet.getResult())); + } + } + + /*本接口用于实现客户 (BD_Customer) 的批量提交功能2*/ + @Test + public void lbatchsaveCustomer2() throws Exception { + String para = "[{\"FName\": "+"\""+"aukh_" + UUID.randomUUID().toString()+"\""+",\"FNumber\": "+"\""+SeqHelper.genNumber("KH")+"\""+",\"FCreateOrgId\": {\"FNumber\": \"100\"},\"FUseOrgId\": {\"FNumber\": \"100\"},\"FCOUNTRY\": {\"FNumber\": \"China\"},\"FINVOICETITLE\": \"zzl\",\"FCustTypeId\": {\"FNumber\": \"KHLB001_SYS\"},\"FTRADINGCURRID\": {\"FNumber\": \"PRE001\"},\"FInvoiceType\": \"1\",\"FTaxType\": {\"FNumber\": \"SFL02_SYS\"},\"FPriority\": 1,\"FTaxRate\": {\"FNumber\": \"SL02_SYS\"},\"FISCREDITCHECK\": true,\"FIsTrade\": true}]"; + // 批量保存客户信息(传请求模式) + List> list = new ArrayList<>(); + list = new Gson().fromJson(para, list.getClass()); + SaveResult sRet = api.batchSave("BD_Customer", new BatchSave<>(list),InvokeMode.Syn); + Gson gson = new Gson(); + if (sRet.isSuccessfully()) { + System.out.printf("客户批量保存接口: %s%n", gson.toJson(sRet.getResult())); + } else { + fail("客户保存接口: " + gson.toJson(sRet.getResult())); + } + } + + /*本接口用于获取账套信息*/ + @Test + public void ngetDataCenters() throws Exception { + List datacenter = api.getDataCenters(); + Gson gson = new Gson(); + System.out.printf("或则环境账套信息:%s%n",gson.toJson(datacenter)); + + + } + +} diff --git a/hw-modules/hw-jindie/src/main/resources/banner.txt b/hw-modules/hw-jindie/src/main/resources/banner.txt new file mode 100644 index 00000000..fbd45f53 --- /dev/null +++ b/hw-modules/hw-jindie/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ _ + (_) | | + _ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___ +| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ +| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | | +|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_| + __/ | __/ | + |___/ |___/ \ No newline at end of file diff --git a/hw-modules/hw-jindie/src/main/resources/bootstrap.yml b/hw-modules/hw-jindie/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..a6ccdc58 --- /dev/null +++ b/hw-modules/hw-jindie/src/main/resources/bootstrap.yml @@ -0,0 +1,29 @@ +# Tomcat +server: + port: 7303 + +# Spring +spring: + application: + # 应用名称 + name: hw-jindie + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP + config: + # 配置中心地址 + server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/hw-modules/hw-jindie/src/main/resources/logback.xml b/hw-modules/hw-jindie/src/main/resources/logback.xml new file mode 100644 index 00000000..c6be9e6a --- /dev/null +++ b/hw-modules/hw-jindie/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/hw-modules/hw-tdengine/pom.xml b/hw-modules/hw-tdengine/pom.xml new file mode 100644 index 00000000..6e568379 --- /dev/null +++ b/hw-modules/hw-tdengine/pom.xml @@ -0,0 +1,106 @@ + + + + com.hw + hw-modules + 3.6.3 + + 4.0.0 + + hw-modules-tdengine + + + hw-modules-tdengine 时序数据库处理模块 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + com.hw + hw-common-datasource + + + + + com.hw + hw-common-datascope + + + + + com.hw + hw-common-log + + + + + com.hw + hw-common-swagger + + + + + com.taosdata.jdbc + taos-jdbcdriver + 3.1.0 + + + com.hw + hw-api-tdengine + 3.6.3 + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/HwTdengineApplication.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/HwTdengineApplication.java new file mode 100644 index 00000000..0d4f6a5b --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/HwTdengineApplication.java @@ -0,0 +1,34 @@ +package com.hw.tdengine; + +import com.hw.common.security.annotation.EnableCustomConfig; +import com.hw.common.security.annotation.EnableRyFeignClients; +import com.hw.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 系统模块 + * + * @author xins + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class HwTdengineApplication +{ + public static void main(String[] args) + { + SpringApplication.run(HwTdengineApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 时序数据库模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/controller/TdEngineController.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/controller/TdEngineController.java new file mode 100644 index 00000000..d696f10c --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/controller/TdEngineController.java @@ -0,0 +1,483 @@ +package com.hw.tdengine.controller; + +import com.hw.common.core.domain.R; +import com.hw.common.core.validated.tdengine.AddTdSTableColumn; +import com.hw.common.core.validated.tdengine.InsertTdTable; +import com.hw.common.security.annotation.InnerAuth; +import com.hw.tdengine.api.domain.*; +import com.hw.tdengine.service.IDeviceStatusService; +import com.hw.tdengine.service.ITdEngineService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.UncategorizedSQLException; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @ClassDescription: TdEngine Controller + * @ClassName: TdEngineController + * @Author: xins + * @Date: 2023-08-25 16:38:44 + * @Version 1.0 + */ +@RestController +@RequestMapping("/tdengine") +public class TdEngineController { + + @Autowired + private ITdEngineService tdEngineService; + + @Autowired + private IDeviceStatusService deviceStatusService; + + private static final Logger log = LoggerFactory.getLogger(TdEngineController.class); + + /* @description 创建数据库 + * @author xins + * @date 2023-08-28 11:38 + * @param databaseName + * @return R + */ + @InnerAuth + @PostMapping("/createDatabase") + public R createDataBase(@RequestBody() String databaseName) { + try { + //调用创建数据库方法 + this.tdEngineService.createDatabase(databaseName); + log.info("successfully created database " + databaseName); + return R.ok("successfully created database " + databaseName); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /* * @description:创建超级表 + * @author xins + * @date 2023-08-28 11:41 + * @param tdSuperTableVo + * @return R + */ + @InnerAuth + @PostMapping("/createSuperTable") + public R createSuperTable(@Validated @RequestBody TdSuperTableVo tdSuperTableVo) { + //数据库名称 + String databaseName = tdSuperTableVo.getDatabaseName(); + //超级表名称 + String superTableName = tdSuperTableVo.getSuperTableName(); + //第一个字段名称 + String firstFieldName = tdSuperTableVo.getFirstFieldName(); + //schema列字段(超级表结构)对象集合 + List schemaFields = tdSuperTableVo.getSchemaFields(); + //标签字段对象集合 + List tagsFields = tdSuperTableVo.getTagsFields(); + + try { + //将Schema字段对象和标签字段对象转换为创建表的字段Vo类对象集合 + List schemaFieldsVos = TdFieldVo.batchConvertFields(schemaFields); + List tagsFieldsVos = TdFieldVo.batchConvertFields(tagsFields); + this.tdEngineService.createSuperTable(databaseName, superTableName, firstFieldName, schemaFieldsVos, tagsFieldsVos); + log.info("successfully created superTable " + superTableName); + return R.ok("successfully created superTable " + superTableName); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R + * @param: tableDto + * @description 创建子表 + * @author xins + * @date 2023-08-28 16:17 + */ + @InnerAuth + @PostMapping("/createTable") + public R createTable(@Validated @RequestBody TdTableVo tdTableVo) { + try { + String databaseName = tdTableVo.getDatabaseName(); + String superTableName = tdTableVo.getSuperTableName(); + String tableName = tdTableVo.getTableName(); + List tagsFieldValues = tdTableVo.getTagsFieldValues(); + this.tdEngineService.createTable(databaseName, superTableName, tableName, tagsFieldValues); + log.info("successfully created table " + tableName); + return R.ok("successfully created table " + tableName); + } catch (Exception e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + log.error(ex.getMessage()); + } + log.error(message); + return R.fail(message); + } + } + + + /** + * @return R + * @param: tdSuperTableVo + * @description 超级表增加列 + * @author xins + * @date 2023-08-28 17:55 + */ + @InnerAuth + @PostMapping("/addSuperTableColumn") + public R addSuperTableColumn(@Validated(AddTdSTableColumn.class) @RequestBody TdSuperTableVo tdSuperTableVo) { + String databaseName = tdSuperTableVo.getDatabaseName(); + String superTableName = tdSuperTableVo.getSuperTableName(); + TdField addTdField = tdSuperTableVo.getField(); + try { + TdFieldVo addFieldVo = TdFieldVo.convertField(addTdField); + this.tdEngineService.addSuperTableColumn(databaseName, superTableName, addFieldVo); + log.info("successfully added column for superTable " + superTableName); + return R.ok("successfully added column for superTable " + superTableName); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R + * @param: tdSuperTableVo + * @description 超级表增加列 + * @author xins + * @date 2023-08-28 17:55 + */ + @InnerAuth + @PostMapping("/modifySuperTableColumn") + public R modifySuperTableColumn(@Validated(AddTdSTableColumn.class) @RequestBody TdSuperTableVo tdSuperTableVo) { + String databaseName = tdSuperTableVo.getDatabaseName(); + String superTableName = tdSuperTableVo.getSuperTableName(); + TdField modifyTdField = tdSuperTableVo.getField(); + try { + TdFieldVo modifyTdFieldVo = TdFieldVo.convertField(modifyTdField); + this.tdEngineService.modifySuperTableColumn(databaseName, superTableName, modifyTdFieldVo); + log.info("successfully modified column for superTable {} " , superTableName); + return R.ok("successfully modified column for superTable " + superTableName); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + + /** + * @return R + * @param: tdSuperTableVo + * @description 超级表删除列 + * @author xins + * @date 2023-08-28 17:56 + */ + @InnerAuth + @PostMapping("/dropSuperTableColumn") + public R dropColumnForSuperTable(@Validated(AddTdSTableColumn.class) @RequestBody TdSuperTableVo tdSuperTableVo) { + String databaseName = tdSuperTableVo.getDatabaseName(); + String superTableName = tdSuperTableVo.getSuperTableName(); + TdField dropField = tdSuperTableVo.getField(); + + try { +// TdFieldVo dropFieldVo = TdFieldVo.convertField(dropField); + this.tdEngineService.dropSuperTableColumn(databaseName, superTableName, dropField); + log.info("successfully droped column of superTable " + superTableName); + return R.ok("successfully droped column of superTable " + superTableName); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R + * @param: alterTagVo + * @description 修改子表tag值 + * @author xins + * @date 2023-08-30 11:17 + */ + @InnerAuth + @PostMapping("/alterTableTag") + public R alterTableTag(@Validated @RequestBody AlterTagVo alterTagVo) { + String databaseName = alterTagVo.getDatabaseName(); + String tableName = alterTagVo.getTableName(); + String tagName = alterTagVo.getTagName(); + Object tagValue = alterTagVo.getTagValue(); + + try { + this.tdEngineService.alterTableTag(databaseName, tableName, tagName, tagValue); + log.info("successfully altered tag " + tagName + " value to " + tagValue + " of tableName"); + return R.ok("successfully altered tag " + tagName + " value to " + tagValue + " of tableName"); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R + * @param: tdTableVo + * @description 插入数据 + * @author xins + * @date 2023-08-29 10:04 + */ + @InnerAuth + @PostMapping("/insertTable") + public R insertTable(@Validated(InsertTdTable.class) @RequestBody TdTableVo tdTableVo) { + try { + String databaseName = tdTableVo.getDatabaseName(); + String tableName = tdTableVo.getTableName(); + List schemaFields = tdTableVo.getSchemaFields(); + + this.tdEngineService.insertTable(databaseName, tableName, schemaFields); + log.info("success for insert table " + databaseName + "." + tableName); + return R.ok(); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R + * @param: tdSelectDto + * @description 获取某个子表最新一条数据 + * @author xins + * @date 2023-08-29 11:26 + */ + @InnerAuth + @PostMapping("/getLatestData") + public R getLatestData(@Validated @RequestBody TdSelectDto tdSelectDto) { + try { + return R.ok(this.tdEngineService.getLatestData(tdSelectDto)); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R>> + * @param: tdSelectDto + * @description 根据tagsname获取超级表最新信息 + * @author xins + * @date 2023-08-29 14:51 + */ + @InnerAuth + @PostMapping("/getLatestDataByTags") + public R>> getLatestDataByTags(@Validated @RequestBody TdSuperTableSelectVo tdSelectDto) { + try { + return R.ok(this.tdEngineService.getLatestDataByTags(tdSelectDto)); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + + /** + * @param tdHistorySelectDto + * @return R + * @MethodDescription 可视化数据 返回格式,时间,数值列 + * 历史数据 SELECT voltage ,ts FROM test.meters where ts between '2017-07-14 02:40:00.000' and '2017-07-14 02:40:00.001' LIMIT [topnums] + * ts > now - 24h; + * 实时数据 select col from table LIMIT [topnums] + * 聚合数据 select [avg/max/sum/count..](col) from table where ts between '2017-07-14 02:40:00.000' and '2017-07-14 02:40:00.001' group by col LIMIT [topnums] + */ + @InnerAuth + @PostMapping("/getHistoryData") + public R getHistoryData(@Validated @RequestBody TdHistorySelectDto tdHistorySelectDto) { + try { +// if (selectVisualDto.getType() == 0) {//查询历史 +// return R.ok(this.tdEngineService.getHistoryData(selectVisualDto)); +// }else if(selectVisualDto.getType() == 1) {//查询实时 +// return R.ok(this.tdEngineService.getRealtimeData(selectVisualDto)); +// }else {//查询聚合 +// return R.ok(this.tdEngineService.getAggregateData(selectVisualDto)); +// } + int count = this.tdEngineService.getCountOfHistoryData(tdHistorySelectDto); + TdReturnDataVo returnDataVo = new TdReturnDataVo(); + returnDataVo.setCount(count); + returnDataVo.setDataList(this.tdEngineService.getHistoryData(tdHistorySelectDto)); + return R.ok(returnDataVo); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + /** + * @return R + * @param: queryDeviceStatus + * @description 获取每天在线设备信息 + * @author xins + * @date 2023-08-29 11:26 + */ + @InnerAuth + @PostMapping("/getOnlineDevicesGroupByDay") + public R getOnlineDevicesGroupByDay(@RequestBody DeviceStatus queryDeviceStatus) { + try { + List deviceStatuses = this.deviceStatusService.getOnlineDevicesGroupByDay(queryDeviceStatus); + Map> deviceStatusMap = deviceStatuses.stream() + .collect(Collectors.groupingBy(DeviceStatus::getDeviceId)); + return R.ok(deviceStatusMap); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + + /** + * @return R + * @param: queryDeviceStatus + * @description 获取最近一次在线设备信息 + * @author xins + * @date 2023-08-29 11:26 + */ + @InnerAuth + @PostMapping("/getLastOnlineDevices") + public R getLastOnlineDevices(@RequestBody DeviceStatus queryDeviceStatus) { + try { + List> deviceStatusMapList = this.deviceStatusService.getLastOnlineDevices(queryDeviceStatus); + return R.ok(deviceStatusMapList); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + + + /** + * @return R + * @param: tdSelectDto + * @description 获取最近一次在线设备信息 + * @author xins + * @date 2023-08-29 11:26 + */ + @InnerAuth + @PostMapping("/getDeviceStatusList") + public R>> getDeviceStatusList(@RequestBody DeviceStatus queryDeviceStatus) { + try { + List> deviceStatusMapList = this.deviceStatusService.getDeviceStatusList(queryDeviceStatus); + return R.ok(deviceStatusMapList); + } catch (UncategorizedSQLException e) { + String message = e.getCause().getMessage(); + try { + message = message.substring(message.lastIndexOf("invalid operation")); + } catch (Exception ex) { + } + log.error(message); + return R.fail(message); + } catch (Exception e) { + log.error(e.getMessage()); + return R.fail(e.getMessage()); + } + } + +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/DeviceStatusMapper.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/DeviceStatusMapper.java new file mode 100644 index 00000000..1651f2af --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/DeviceStatusMapper.java @@ -0,0 +1,44 @@ +package com.hw.tdengine.mapper; + +import com.hw.tdengine.api.domain.DeviceStatus; + +import java.util.List; +import java.util.Map; + +/** + * @Description:TDengine设备状态信息交互接口 + * @ClassName: DeviceStatusMapper + * @Author : xins + * @Date :2023-09-05 13:20 + * @Version :1.0 + */ +public interface DeviceStatusMapper { + + /** + * @param: deviceStatus + * @description 获取每天在线设备列表 + * @author xins + * @date 2023-09-05 11:43 + * @return List + */ + List getOnlineDevicesGroupByDay(DeviceStatus deviceStatus); + + /** + * @param: deviceStatus + * @description 获取最近的在线设备列表 + * @author xins + * @date 2023-09-05 11:43 + * @return List + */ + List> getLastOnlineDevices(DeviceStatus deviceStatus); + + /** + * @param: deviceStatus + * @description 获取设备状态信息列表 + * @author xins + * @date 2023-09-05 11:43 + * @return List + */ + List> getDeviceStatusList(DeviceStatus deviceStatus); + +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/TdEngineMapper.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/TdEngineMapper.java new file mode 100644 index 00000000..87fd769a --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/mapper/TdEngineMapper.java @@ -0,0 +1,177 @@ +package com.hw.tdengine.mapper; + +import com.hw.tdengine.api.domain.*; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * @ClassDescription:TDengine数据库交互接口 + * @ClassName: TdEngineMapper + * @Author: thinglinks + * @Date: 2021-12-27 14:52:34 + * @Version 1.0 + */ +public interface TdEngineMapper { + + /** + * @param databaseName + * @description 创建数据库 + * @author xins + * @date 2023-08-28 13:16 + */ + void createDatabase(String databaseName); + + /** + * @param databaseName + * @param superTableName + * @param firstFieldName + * @param schemaFields + * @param tagsFields + * @description 创建超级表 + * @author xins + * @date 2023-08-28 11:09 + */ + void createSuperTable(@Param("databaseName") String databaseName, + @Param("superTableName") String superTableName, + @Param("firstFieldName") String firstFieldName, + @Param("schemaFields") List schemaFields, + @Param("tagsFields") List tagsFields + ); + + /** + * @param: databaseName + * @param: superTableName + * @param: tableName + * @param: tagsFieldValues + * @description 创建子表 + * @author xins + * @date 2023-08-28 15:58 + */ + void createTable(@Param("databaseName") String databaseName, + @Param("superTableName") String superTableName, + @Param("tableName") String tableName, + @Param("tagsFieldValues") List tagsFieldValues); + + + /** + * @param: databaseName + * @param: superTableName + * @param: addFieldsVo + * @description 增加超级表的列 + * @author xins + * @date 2023-08-28 17:11 + */ + void addSuperTableColumn(@Param("databaseName") String databaseName, + @Param("superTableName") String superTableName, + @Param("addFieldsVo") TdFieldVo addFieldsVo); + + /** + * @param: databaseName + * @param: superTableName + * @param: modifyFieldsVo + * @description 修改超级表列宽(只适用于nchar类型) + * @author xins + * @date 2023-10-11 9:01 + + */ + void modifySuperTableColumn(@Param("databaseName") String databaseName, + @Param("superTableName") String superTableName, + @Param("modifyFieldsVo") TdFieldVo modifyFieldsVo); + + /** + * @param: databaseName + * @param: superTableName + * @param: dropFieldsVo + * @description 删除超级表的列 + * @author xins + * @date 2023-08-28 17:11 + */ + void dropSuperTableColumn(@Param("databaseName") String databaseName, + @Param("superTableName") String superTableName, + @Param("dropField") TdField dropField); + + + /** + * @param: databaseName + * @param: tableName + * @param: tagName + * @param: tagValue + * @description 修改子表的tagValue + * @author xins + * @date 2023-08-30 11:02 + */ + void alterTableTag(@Param("databaseName") String databaseName, + @Param("tableName") String tableName, + @Param("tagName") String tagName, + @Param("tagValue") Object tagValue + ); + + + /** + * @param: databaseName + * @param: tableName + * @param: schemaFields + * @description 插入数据 + * @author xins + * @date 2023-08-29 10:03 + */ + void insertTable(@Param("databaseName") String databaseName, + @Param("tableName") String tableName, + @Param("schemaFields") List schemaFields); + + + /** + * @return List> + * @param: tdSelectDto + * @description 查询具体某个子表最新数据,适用于设备监测页面,先根据监控单元获取设备信息,再根据设备来获取最新的数据 + * @author xins + * @date 2023-08-29 10:43 + */ + List> getLatestData(TdSelectDto tdSelectDto); + + /** + * @return List> + * @param: tdSelectDto + * @description 根据tagsname获取超级表最新信息,适用于设备汇总页面,先分页获取设备信息,再获取所有此物模型下的最新信息(冗余,但每页只获取一次,不需要每个设备都获取一次 + * 如果需要根据时间周期获取最新的数据,则需要用select last_row() + * @author xins + * @date 2023-08-29 14:42 + */ + List> getLatestDataByTags(TdSuperTableSelectVo tdSuperTableSelectVo); + + + /** + * @return List> + * @param: tdSelectDto + * @description 获取历史数据 + * @author xins + * @date 2023-08-29 15:50 + */ + List> getHistoryData(TdHistorySelectDto tdHistorySelectDto); + + int getCountOfHistoryData(TdHistorySelectDto tdHistorySelectDto); + +// /** +// * 检查表是否存在 +// * @param dataBaseName +// * @param tableName 可以为超级表名或普通表名 +// * @return +// */ +// Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName")String tableName); + + +// +// List> getRealtimeData(SelectVisualDto selectVisualDto); +// +// List> getAggregateData(SelectVisualDto selectVisualDto); + + + // void addTagForSuperTable(@Param("superTableName") String superTableName, +// @Param("fieldsVo") FieldsVo fieldsVo); +// +// void dropTagForSuperTable(@Param("superTableName") String superTableName, +// @Param("fieldsVo") FieldsVo fieldsVo); + +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/IDeviceStatusService.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/IDeviceStatusService.java new file mode 100644 index 00000000..7565c3e1 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/IDeviceStatusService.java @@ -0,0 +1,36 @@ +package com.hw.tdengine.service; + +import com.hw.tdengine.api.domain.DeviceStatus; + +import java.util.List; +import java.util.Map; + +public interface IDeviceStatusService { + + /** + * @param: DeviceStatus + * @description 获取时间范围内每天的在线设备 + * @author xins + * @date 2023-09-05 13:23 + * @return List + */ + public List getOnlineDevicesGroupByDay(DeviceStatus queryDeviceStatus); + + /** + * @return List> + * @param: DeviceStatus + * @description 获取某个事件前最新的在线设备 + * @author xins + * @date 2023-09-05 13:23 + */ + public List> getLastOnlineDevices(DeviceStatus queryDeviceStatus); + + /** + * @return List> + * @param: DeviceStatus + * @description 获取设备状态信息列表 + * @author xins + * @date 2023-09-05 13:23 + */ + public List> getDeviceStatusList(DeviceStatus queryDeviceStatus); +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/ITdEngineService.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/ITdEngineService.java new file mode 100644 index 00000000..0c2e219a --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/ITdEngineService.java @@ -0,0 +1,142 @@ +package com.hw.tdengine.service; + +import com.hw.tdengine.api.domain.*; + +import java.util.List; +import java.util.Map; + +/** + * @InterfaceName: ITdEngineService + * @Author : xins + * @Date :2023-08-28 13:14 + * @Description: TdEngineService接口 + * @Version :1.0 + */ +public interface ITdEngineService { + + /** + * @param databaseName + * @description:创建数据库 + * @author xins + * @date 2023-08-28 11:32 + */ + public void createDatabase(String databaseName) throws Exception; + + /** + * @param databaseName + * @param superTableName + * @param firstFieldName + * @param schemaFields + * @param tagsFields + * @description 创建超级表 + * @author xins + * @date 2023-08-28 13:18 + */ + public void createSuperTable(String databaseName, String superTableName, String firstFieldName, List schemaFields, List tagsFields) throws Exception; + + /** + * @param: databaseName + * @param: superTableName + * @param: tableName + * @param: tagsFieldValues + * @description 创建子表 + * @author xins + * @date 2023-08-28 16:16 + */ + public void createTable(String databaseName, String superTableName, String tableName, List tagsFieldValues) throws Exception; + + /** + * @param: databaseName + * @param: superTableName + * @param: addFieldsVo + * @description 增加超级表的列 + * @author xins + * @date 2023-08-28 17:12 + */ + + public void addSuperTableColumn(String databaseName,String superTableName, TdFieldVo addFieldsVo) throws Exception; + + /** + * @param: databaseName + * @param: superTableName + * @param: modifyFieldsVo + * @description 修改超级表列宽(只适用于nchar类型) + * @author xins + * @date 2023-10-11 9:02 + */ + public void modifySuperTableColumn(String databaseName, String superTableName, TdFieldVo modifyFieldsVo) throws Exception; + + /** + * @param: databaseName + * @param: superTableName + * @param: dropFieldsVo + * @description 删除超级表的列 + * @author xins + * @date 2023-08-28 17:13 + */ + public void dropSuperTableColumn(String databaseName,String superTableName, TdField dropField) throws Exception; + + /** + * @param: databaseName + * @param: tableName + * @param: tagName + * @param: tagValue + * @description 修改子表的tagValue + * @author xins + * @date 2023-08-30 11:03 + */ + public void alterTableTag(String databaseName,String tableName,String tagName,Object tagValue); + /** + * @param: databaseName + * @param: tableName + * @param: schemaFields + * @description 插入数据 + * @author xins + * @date 2023-08-29 10:10 + */ + public void insertTable(String databaseName,String tableName,List schemaFields) throws Exception; + + /** + * @param: tdSelectDto + * @description 获取某个子表最新一条数据 + * @author xins + * @date 2023-08-29 11:28 + * @return List> + */ + public List> getLatestData(TdSelectDto tdSelectDto) throws Exception; + + /** + * @return List> + * @param: tdSelectDto + * @description 根据tagsname获取超级表最新信息,适用于设备汇总页面,先分页获取设备信息,再获取所有此物模型下的最新信息(冗余,但每页只获取一次,不需要每个设备都获取一次 + * * 如果需要根据时间周期获取最新的数据,则需要用select last_row() + * @author xins + * @date 2023-08-29 14:47 + */ + public List> getLatestDataByTags(TdSuperTableSelectVo tdSuperTableSelectVo); + + /** + * @param: tdHistorySelectDto + * @description 获取历史数据 + * @author xins + * @date 2023-08-29 15:52 + * @return List> + */ + public List> getHistoryData(TdHistorySelectDto tdHistorySelectDto); + + /** + * @param: tdHistorySelectDto + * @description 获取历史数据总数量 + * @author xins + * @date 2023-09-25 16:20 + * @return int + */ + public int getCountOfHistoryData(TdHistorySelectDto tdHistorySelectDto); + + +// void initSTableFrame(String msg) throws Exception; + +// List> getRealtimeData(SelectVisualDto selectVisualDto); +// +// List> getAggregateData(SelectVisualDto selectVisualDto); +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/DeviceStatusServiceImpl.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/DeviceStatusServiceImpl.java new file mode 100644 index 00000000..c5c922a3 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/DeviceStatusServiceImpl.java @@ -0,0 +1,99 @@ +package com.hw.tdengine.service.impl; + +import com.hw.common.core.constant.TdEngineConstants; +import com.hw.tdengine.api.domain.DeviceStatus; +import com.hw.tdengine.mapper.DeviceStatusMapper; +import com.hw.tdengine.service.IDeviceStatusService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * @Description: 设备状态信息业务服务 + * @ClassName: DeviceStatusServiceImpl + * @Author : xins + * @Date :2023-09-05 13:27 + * @Version :1.0 + */ +@Service +public class DeviceStatusServiceImpl implements IDeviceStatusService { + + @Autowired + private DeviceStatusMapper deviceStatusMapper; + + /** + * @return List + * @param: DeviceStatus + * @description 获取时间范围内每天的在线设备 + * @author xins + * @date 2023-09-05 13:23 + */ + @Override + public List getOnlineDevicesGroupByDay(DeviceStatus queryDeviceStatus) { + + Map params = queryDeviceStatus.getParams(); + params.put("databaseName", TdEngineConstants.PLATFORM_DB_NAME); + params.put("superTableName", TdEngineConstants.DEFAULT_DEVICE_STATUS_SUPER_TABLE_NAME); +// DeviceStatus queryDeviceStatus = new DeviceStatus(); +// params.put("beginTime", beginTime); +// params.put("endTime", endTime); +// queryDeviceStatus.setParams(params); +// if (sceneId != null) { +// queryDeviceStatus.setSceneId(sceneId); +// } + + List deviceStatuses = deviceStatusMapper.getOnlineDevicesGroupByDay(queryDeviceStatus); + + return deviceStatuses; + } + + + /** + * @return List> + * @param: DeviceStatus + * @description 获取某个事件前最新的在线设备 + * @author xins + * @date 2023-09-05 13:23 + */ + @Override + public List> getLastOnlineDevices(DeviceStatus queryDeviceStatus) { + + Map params = queryDeviceStatus.getParams(); + params.put("databaseName", TdEngineConstants.PLATFORM_DB_NAME); + params.put("superTableName", TdEngineConstants.DEFAULT_DEVICE_STATUS_SUPER_TABLE_NAME); + List> deviceStatusMap = deviceStatusMapper.getLastOnlineDevices(queryDeviceStatus); + List> filterMaps = new ArrayList<>(); + + //tdengine,在select时row会带着括号,需要将括号过滤掉 + for (Map latestMap : deviceStatusMap) { + Map filterMap = latestMap.entrySet() + .stream() + .filter(entry -> entry.getValue() != null) + .collect(HashMap::new, (m, v) -> + m.put(v.getKey().substring(v.getKey().indexOf("(") + 1, + v.getKey().indexOf(")")), v.getValue()), HashMap::putAll); + filterMaps.add(filterMap); + } + + return filterMaps; + } + + + /** + * @return List> + * @param: DeviceStatus + * @description 获取设备状态信息列表 + * @author xins + * @date 2023-09-05 13:23 + */ + @Override + public List> getDeviceStatusList(DeviceStatus queryDeviceStatus) { + Map params = queryDeviceStatus.getParams(); + params.put("databaseName", TdEngineConstants.PLATFORM_DB_NAME); + params.put("tableName", TdEngineConstants.getDeviceStatusTableName(queryDeviceStatus.getDeviceId())); + List> deviceStatusMap = deviceStatusMapper.getDeviceStatusList(queryDeviceStatus); + return deviceStatusMap; + } + +} diff --git a/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/TdEngineServiceImpl.java b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/TdEngineServiceImpl.java new file mode 100644 index 00000000..e25621a5 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/java/com/hw/tdengine/service/impl/TdEngineServiceImpl.java @@ -0,0 +1,314 @@ +package com.hw.tdengine.service.impl; + +import com.hw.tdengine.api.domain.*; +import com.hw.tdengine.mapper.TdEngineMapper; +import com.hw.tdengine.service.ITdEngineService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * @ClassDescription: TdEngine业务实现逻辑 + * @ClassName: TdEngineServiceImpl + * @Author: xins + * @Date: 2023-08-25 15:55:50 + * @Version 1.0 + */ +@Service +//@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) +public class TdEngineServiceImpl implements ITdEngineService { + private static final String DEFAULT_FIRST_FIELD_NAME = "ts"; + + @Autowired + private TdEngineMapper tdEngineMapper; + +// @Autowired +// private RedisService redisService; + + /** + * @param databaseName + * @description:创建数据库 + * @author xins + * @date 2023-08-28 11:32 + */ + @Override + public void createDatabase(String databaseName) throws Exception { + this.tdEngineMapper.createDatabase(databaseName); + } + + /** + * @param databaseName + * @param superTableName + * @param firstFieldName + * @param schemaFields + * @param tagsFields + * @description 创建超级表 + * @author xins + * @date 2023-08-28 13:18 + */ + @Override + public void createSuperTable(String databaseName, String superTableName, String firstFieldName, List schemaFields, List tagsFields) throws Exception { + if (StringUtils.isBlank(firstFieldName)) { + firstFieldName = DEFAULT_FIRST_FIELD_NAME; + } + this.tdEngineMapper.createSuperTable(databaseName, superTableName, firstFieldName, schemaFields, tagsFields); + } + + /** + * @param: databaseName + * @param: superTableName + * @param: tableName + * @param: tagsFieldValues + * @description 创建子表 + * @author xins + * @date 2023-08-28 16:16 + */ + @Override + public void createTable(String databaseName, String superTableName, String tableName, List tagsFieldValues) throws Exception { + this.tdEngineMapper.createTable(databaseName, superTableName, tableName, tagsFieldValues); + + } + + + /** + * @param: databaseName + * @param: superTableName + * @param: addFieldsVo + * @description 增加超级表的列 + * @author xins + * @date 2023-08-28 17:12 + */ + @Override + public void addSuperTableColumn(String databaseName, String superTableName, TdFieldVo addFieldsVo) throws Exception { + this.tdEngineMapper.addSuperTableColumn(databaseName, superTableName, addFieldsVo); + } + + /** + * @param: databaseName + * @param: superTableName + * @param: modifyFieldsVo + * @description 修改超级表列宽(只适用于nchar类型) + * @author xins + * @date 2023-10-11 9:02 + */ + @Override + public void modifySuperTableColumn(String databaseName, String superTableName, TdFieldVo modifyFieldsVo) throws Exception { + this.tdEngineMapper.modifySuperTableColumn(databaseName, superTableName, modifyFieldsVo); + } + + + /** + * @param: databaseName + * @param: superTableName + * @param: dropFieldsVo + * @description 删除超级表的列 + * @author xins + * @date 2023-08-28 17:13 + */ + public void dropSuperTableColumn(String databaseName, String superTableName, TdField dropField) throws Exception { + this.tdEngineMapper.dropSuperTableColumn(databaseName, superTableName, dropField); + } + + /** + * @param: databaseName + * @param: tableName + * @param: tagName + * @param: tagValue + * @description 修改子表的tagValue + * @author xins + * @date 2023-08-30 11:03 + */ + @Override + public void alterTableTag(String databaseName, String tableName, String tagName, Object tagValue) { + this.tdEngineMapper.alterTableTag(databaseName, tableName, tagName, tagValue); + } + + /** + * @param: databaseName + * @param: tableName + * @param: schemaFields + * @description 插入数据 + * @author xins + * @date 2023-08-29 10:10 + */ + @Override + public void insertTable(String databaseName, String tableName, List schemaFields) throws Exception { + this.tdEngineMapper.insertTable(databaseName, tableName, schemaFields); + } + + /** + * @return List> + * @param: tdSelectDto + * @description 查询具体某个子表最新数据,适用于设备监测页面,先根据监控单元获取设备信息,再根据设备来获取最新的数据 + * @author xins + * @date 2023-08-29 11:28 + */ + @Override + public List> getLatestData(TdSelectDto tdSelectDto) throws Exception { + List> latestMaps = this.tdEngineMapper.getLatestData(tdSelectDto); + + List> filterMaps = new ArrayList<>(); + +// String i18nKey = "tdengine_supertable1_"; + String i18nKey = "";//todo:国际化看在哪处理 + /** + * 由于通过last(*)获取则获取的key为last(key),所以需要以下过滤,需要获取括号中间的真正key + */ + for (Map latestMap : latestMaps) { + Map filterMap = latestMap.entrySet() + .stream() + .filter(entry -> entry.getValue() != null) + .collect(HashMap::new, (m, v) -> + m.put(i18nKey + v.getKey().substring(v.getKey().indexOf("(") + 1, + v.getKey().indexOf(")")), v.getValue()), HashMap::putAll); + filterMaps.add(filterMap); + } + return filterMaps; + } + + /** + * @return List> + * @param: tdSelectDto + * @description 根据tagsname获取超级表最新信息,适用于设备汇总页面,先分页获取设备信息,再获取所有此物模型下的最新信息(冗余,但每页只获取一次,不需要每个设备都获取一次 + * * 如果需要根据时间周期获取最新的数据,则需要用select last_row() + * @author xins + * @date 2023-08-29 14:47 + */ + @Override + public List> getLatestDataByTags(TdSuperTableSelectVo tdSuperTableSelectVo) { + List> latestMaps = this.tdEngineMapper.getLatestDataByTags(tdSuperTableSelectVo); + latestMaps.replaceAll(latestMap -> { + return latestMap.entrySet() + .stream() + .filter(entry -> entry.getValue() != null) + .collect(HashMap::new, (m, v) -> + m.put(v.getKey().substring(v.getKey().indexOf("(") + 1, + v.getKey().indexOf(")")), v.getValue()), HashMap::putAll); + }); + // Map> tagsMap = new HashMap<>(); +// for (Map latestMap : latestMaps) { +// Map filterMap = latestMap.entrySet() +// .stream() +// .filter(entry -> entry.getValue() != null) +// .collect(HashMap::new, (m, v) -> +// m.put(v.getKey().substring(v.getKey().indexOf("(") + 1, +// v.getKey().indexOf(")")), v.getValue()), HashMap::putAll); +// tagsMap.put(filterMap.get(tdSuperTableSelectVo.getTagsName()).toString(), filterMap); +// } + return latestMaps; + } + + + /** + * @return List> + * @param: tdHistorySelectDto + * @description 获取历史数据 + * @author xins + * @date 2023-08-29 15:52 + */ + @Override + public List> getHistoryData(TdHistorySelectDto tdHistorySelectDto) { +// if (StringUtils.isBlank(tdHistorySelectDto.getOrderByFieldName())) { +// tdHistorySelectDto.setOrderByFieldName(TdEngineConstants.DEFAULT_FIRST_FIELD_NAME); +// } +// if (StringUtils.isBlank(tdHistorySelectDto.getOrderByMode())) { +// tdHistorySelectDto.setOrderByMode(TdEngineConstants.DEFAULT_ORDER_BY_MODE); +// } + + List> historyDataMaps = this.tdEngineMapper.getHistoryData(tdHistorySelectDto); + + return historyDataMaps; + } + + /** + * @param: tdHistorySelectDto + * @description 获取历史数据总数量 + * @author xins + * @date 2023-09-25 16:20 + * @return int + */ + @Override + public int getCountOfHistoryData(TdHistorySelectDto tdHistorySelectDto){ + int count = this.tdEngineMapper.getCountOfHistoryData(tdHistorySelectDto); + return count; + } +// +// /** +// * 检查数据库表是否存在 +// * +// * @param dataBaseName 数据库名 +// * @param tableName tableName 可以为超级表名或普通表名 +// * @return +// */ +// public boolean checkTableExists(String dataBaseName, String tableName) throws Exception { +// try { +// Integer count = tdEngineMapper.checkTableExists(dataBaseName, tableName); +// return count == 1; +// } catch (Exception e) { +// log.warn("{},{} 数据库表不存在", dataBaseName, tableName); +// return false; +// } +// } +// +// @Override +// public void initSTableFrame(String msg) throws Exception { +// final SuperTableDto superTableDto = JSONObject.toJavaObject(JSONObject.parseObject(msg), SuperTableDto.class); +// //从入参对象获取列字段(超级表结构)对象集合 +// List schemaFields = superTableDto.getSchemaFields(); +// //从入参对象获取标签字段对象集合 +// List tagsFields = superTableDto.getTagsFields(); +// //从入参获取数据库名称 +// String dataBaseName = superTableDto.getDataBaseName(); +// //从入参获取超级表名称 +// String superTableName = superTableDto.getSuperTableName(); +// final boolean tableExists = this.checkTableExists(dataBaseName, superTableName); +// if (tableExists) { +// log.info("超级表{}已存在", superTableName); +// return; +// } +// //获取列字段对象集合的第一个对象的字段数据类型 +// DataTypeEnum dataType = schemaFields.get(0).getDataType(); +// //如果该数据类型不是时间戳,打印和返回报错信息 +// if (dataType == null || !"timestamp".equals(dataType.getDataType())) { +// log.error("invalid operation: first column must be timestamp"); +// return; +// } +// //将列字段对象集合和标签字段对象集合转码为字段Vo类对象集合 +// List schemaFieldsVoList = FieldsVo.fieldsTranscoding(schemaFields); +// List tagsFieldsVoList = FieldsVo.fieldsTranscoding(tagsFields); +// //创建超级表 +// this.createSuperTable(schemaFieldsVoList, tagsFieldsVoList, dataBaseName, superTableName); +// log.info("create {} super table success", superTableName); +// } +// + +// +// /** +// * @param tagsSelectDao +// * @return +// */ +// @Override + +// +// + +// +// @Override +// public List> getRealtimeData(SelectVisualDto selectVisualDto) { +// List> maps = this.tdEngineMapper.getRealtimeData(selectVisualDto); +// return maps; +// } +// +// @Override +// public List> getAggregateData(SelectVisualDto selectVisualDto) { +// List> maps = this.tdEngineMapper.getAggregateData(selectVisualDto); +// return maps; +// } + +} diff --git a/hw-modules/hw-tdengine/src/main/resources/banner.txt b/hw-modules/hw-tdengine/src/main/resources/banner.txt new file mode 100644 index 00000000..fbd45f53 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ _ + (_) | | + _ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___ +| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ +| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | | +|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_| + __/ | __/ | + |___/ |___/ \ No newline at end of file diff --git a/hw-modules/hw-tdengine/src/main/resources/bootstrap.yml b/hw-modules/hw-tdengine/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..5119aeb0 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/resources/bootstrap.yml @@ -0,0 +1,29 @@ +# Tomcat +server: + port: 7302 + +# Spring +spring: + application: + # 应用名称 + name: hw-tdengine + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP + config: + # 配置中心地址 + server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/hw-modules/hw-tdengine/src/main/resources/logback.xml b/hw-modules/hw-tdengine/src/main/resources/logback.xml new file mode 100644 index 00000000..147e3988 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/DeviceStatusMapper.xml b/hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/DeviceStatusMapper.xml new file mode 100644 index 00000000..c197ee35 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/DeviceStatusMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + diff --git a/hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/TdEngineMapper.xml new file mode 100644 index 00000000..dd81aa81 --- /dev/null +++ b/hw-modules/hw-tdengine/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -0,0 +1,329 @@ + + + + + + create database if not exists #{databaseName} + + + + create table if not exists #{databaseName}.#{superTableName} + (${firstFieldName} timestamp, + + + + ${item.fieldName} + + + + + timestamp + + + tinyint + + + smallint + + + int + + + bigint + + + float + + + double + + + binary + + + nchar + + + bool + + + json + + + + + + (#{item.size}) + + + ) tags + + + + ${item.fieldName} + + + + + timestamp + + + int + + + bigint + + + float + + + double + + + binary + + + smallint + + + tinyint + + + bool + + + nchar + + + json + + + + + (#{item.size}) + + + + + + create table if not exists #{databaseName}.#{tableName} + using #{databaseName}.#{superTableName} + ${item.fieldName} + tags + #{item.fieldValue} + + + + + + ALTER STABLE #{databaseName}.#{superTableName} ADD COLUMN + + ${addFieldsVo.fieldName} + + + + + timestamp + + + int + + + bigint + + + float + + + double + + + binary + + + smallint + + + tinyint + + + bool + + + nchar + + + json + + + + + (#{addFieldsVo.size}) + + + + + + + ALTER STABLE #{databaseName}.#{superTableName} modify COLUMN + + ${modifyFieldsVo.fieldName} nchar(#{modifyFieldsVo.size}) + + + + + + + + ALTER STABLE #{databaseName}.#{superTableName} DROP COLUMN + + ${dropField.fieldName} + + + + + ALTER TABLE #{databaseName}.#{tableName} SET TAG #{tagName}=${tagValue}; + + + + + + + insert into #{databaseName}.#{tableName} ${item.fieldName} values #{item.fieldValue} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hw-modules/hw-wms/pom.xml b/hw-modules/hw-wms/pom.xml new file mode 100644 index 00000000..83210c96 --- /dev/null +++ b/hw-modules/hw-wms/pom.xml @@ -0,0 +1,100 @@ + + + + com.hw + hw-modules + 3.6.3 + + 4.0.0 + + hw-modules-wms + + + hw-modules-wms仓储模块 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + com.mysql + mysql-connector-j + + + + + com.hw + hw-common-datasource + + + + + com.hw + hw-common-datascope + + + + + com.hw + hw-common-log + + + + + com.hw + hw-common-swagger + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/HwWmsApplication.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/HwWmsApplication.java new file mode 100644 index 00000000..169e6bcc --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/HwWmsApplication.java @@ -0,0 +1,34 @@ +package com.hw.wms; + +import com.hw.common.security.annotation.EnableCustomConfig; +import com.hw.common.security.annotation.EnableRyFeignClients; +import com.hw.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 系统模块 + * + * @author xins + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class HwWmsApplication +{ + public static void main(String[] args) + { + SpringApplication.run(HwWmsApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 仓储模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/controller/WmsRawOutstockController.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/controller/WmsRawOutstockController.java new file mode 100644 index 00000000..647ed333 --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/controller/WmsRawOutstockController.java @@ -0,0 +1,105 @@ +package com.hw.wms.controller; + +import java.util.List; +import java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.hw.common.log.annotation.Log; +import com.hw.common.log.enums.BusinessType; +import com.hw.common.security.annotation.RequiresPermissions; +import com.hw.wms.domain.WmsRawOutstock; +import com.hw.wms.service.IWmsRawOutstockService; +import com.hw.common.core.web.controller.BaseController; +import com.hw.common.core.web.domain.AjaxResult; +import com.hw.common.core.utils.poi.ExcelUtil; +import com.hw.common.core.web.page.TableDataInfo; + +/** + * 原材料出库记录Controller + * + * @author xs + * @date 2023-12-20 + */ +@RestController +@RequestMapping("/rawoutstock") +public class WmsRawOutstockController extends BaseController +{ + @Autowired + private IWmsRawOutstockService wmsRawOutstockService; + + /** + * 查询原材料出库记录列表 + */ + @RequiresPermissions("wms:rawoutstock:list") + @GetMapping("/list") + public TableDataInfo list(WmsRawOutstock wmsRawOutstock) + { + startPage(); + List list = wmsRawOutstockService.selectWmsRawOutstockList(wmsRawOutstock); + return getDataTable(list); + } + + /** + * 导出原材料出库记录列表 + */ + @RequiresPermissions("wms:rawoutstock:export") + @Log(title = "原材料出库记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, WmsRawOutstock wmsRawOutstock) + { + List list = wmsRawOutstockService.selectWmsRawOutstockList(wmsRawOutstock); + ExcelUtil util = new ExcelUtil(WmsRawOutstock.class); + util.exportExcel(response, list, "原材料出库记录数据"); + } + + /** + * 获取原材料出库记录详细信息 + */ + @RequiresPermissions("wms:rawoutstock:query") + @GetMapping(value = "/{rawOutstockId}") + public AjaxResult getInfo(@PathVariable("rawOutstockId") Long rawOutstockId) + { + return success(wmsRawOutstockService.selectWmsRawOutstockByRawOutstockId(rawOutstockId)); + } + + /** + * 新增原材料出库记录 + */ + @RequiresPermissions("wms:rawoutstock:add") + @Log(title = "原材料出库记录", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody WmsRawOutstock wmsRawOutstock) + { + return toAjax(wmsRawOutstockService.insertWmsRawOutstock(wmsRawOutstock)); + } + + /** + * 修改原材料出库记录 + */ + @RequiresPermissions("wms:rawoutstock:edit") + @Log(title = "原材料出库记录", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody WmsRawOutstock wmsRawOutstock) + { + return toAjax(wmsRawOutstockService.updateWmsRawOutstock(wmsRawOutstock)); + } + + /** + * 删除原材料出库记录 + */ + @RequiresPermissions("wms:rawoutstock:remove") + @Log(title = "原材料出库记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{rawOutstockIds}") + public AjaxResult remove(@PathVariable Long[] rawOutstockIds) + { + return toAjax(wmsRawOutstockService.deleteWmsRawOutstockByRawOutstockIds(rawOutstockIds)); + } +} diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstock.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstock.java new file mode 100644 index 00000000..f3a1bfe3 --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstock.java @@ -0,0 +1,354 @@ +package com.hw.wms.domain; + +import java.util.List; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.hw.common.core.annotation.Excel; +import com.hw.common.core.web.domain.BaseEntity; + +/** + * 原材料出库记录对象 wms_raw_outstock + * + * @author xs + * @date 2023-12-20 + */ +public class WmsRawOutstock extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 原材料出库记录ID */ + private Long rawOutstockId; + + /** 任务编号;移库时必须 */ + @Excel(name = "任务编号;移库时必须") + private String taskCode; + + /** 仓库ID;领料时需要保存 */ + @Excel(name = "仓库ID;领料时需要保存") + private Long warehouseId; + + /** 库位编码;移库和合库时需要保存 */ + @Excel(name = "库位编码;移库和合库时需要保存") + private String locationCode; + + /** 工单ID,关联pd_base_order_info的order_id */ + @Excel(name = "工单ID,关联pd_base_order_info的order_id") + private Long orderId; + + /** 计划ID,关联pd_base_plan_info的plan_id */ + @Excel(name = "计划ID,关联pd_base_plan_info的plan_id") + private Long planId; + + /** 计划明细ID,关联pd_base_plan_detail的plan_detail_id */ + @Excel(name = "计划明细ID,关联pd_base_plan_detail的plan_detail_id") + private Long planDetailId; + + /** 所属工位,关联pd_base_station_info的station_id */ + @Excel(name = "所属工位,关联pd_base_station_info的station_id") + private Long stationId; + + /** 成品ID,关联物料表物料id */ + @Excel(name = "成品ID,关联物料表物料id") + private Long productId; + + /** 操作类型(0自动,1手动,2强制,3调度) */ + @Excel(name = "操作类型(0自动,1手动,2强制,3调度)") + private String operationType; + + /** 任务类型(1生产领料,2移库出库,3合库出库,9其他领料) */ + @Excel(name = "任务类型(1生产领料,2移库出库,3合库出库,9其他领料)") + private String taskType; + + /** 申请原因 */ + @Excel(name = "申请原因") + private String applyReason; + + /** 审核原因 */ + @Excel(name = "审核原因") + private String auditReason; + + /** 审核状态(0待审核,1审核通过,2审核未通过) */ + @Excel(name = "审核状态(0待审核,1审核通过,2审核未通过)") + private String auditStatus; + + /** 执行状态(0待执行,1执行中,2执行完成) */ + @Excel(name = "执行状态(0待执行,1执行中,2执行完成)") + private String executeStatus; + + /** 申请人 */ + @Excel(name = "申请人") + private String applyBy; + + /** 申请时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "申请时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date applyDate; + + /** 审核人 */ + @Excel(name = "审核人") + private String auditBy; + + /** 审核时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "审核时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date auditDate; + + /** 最后更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "最后更新时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date updateDate; + + /** 执行开始时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "执行开始时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date beginTime; + + /** 执行结束时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "执行结束时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date endTime; + + /** 原材料出库记录明细信息 */ + private List wmsRawOutstockDetailList; + + public void setRawOutstockId(Long rawOutstockId) + { + this.rawOutstockId = rawOutstockId; + } + + public Long getRawOutstockId() + { + return rawOutstockId; + } + public void setTaskCode(String taskCode) + { + this.taskCode = taskCode; + } + + public String getTaskCode() + { + return taskCode; + } + public void setWarehouseId(Long warehouseId) + { + this.warehouseId = warehouseId; + } + + public Long getWarehouseId() + { + return warehouseId; + } + public void setLocationCode(String locationCode) + { + this.locationCode = locationCode; + } + + public String getLocationCode() + { + return locationCode; + } + public void setOrderId(Long orderId) + { + this.orderId = orderId; + } + + public Long getOrderId() + { + return orderId; + } + public void setPlanId(Long planId) + { + this.planId = planId; + } + + public Long getPlanId() + { + return planId; + } + public void setPlanDetailId(Long planDetailId) + { + this.planDetailId = planDetailId; + } + + public Long getPlanDetailId() + { + return planDetailId; + } + public void setStationId(Long stationId) + { + this.stationId = stationId; + } + + public Long getStationId() + { + return stationId; + } + public void setProductId(Long productId) + { + this.productId = productId; + } + + public Long getProductId() + { + return productId; + } + public void setOperationType(String operationType) + { + this.operationType = operationType; + } + + public String getOperationType() + { + return operationType; + } + public void setTaskType(String taskType) + { + this.taskType = taskType; + } + + public String getTaskType() + { + return taskType; + } + public void setApplyReason(String applyReason) + { + this.applyReason = applyReason; + } + + public String getApplyReason() + { + return applyReason; + } + public void setAuditReason(String auditReason) + { + this.auditReason = auditReason; + } + + public String getAuditReason() + { + return auditReason; + } + public void setAuditStatus(String auditStatus) + { + this.auditStatus = auditStatus; + } + + public String getAuditStatus() + { + return auditStatus; + } + public void setExecuteStatus(String executeStatus) + { + this.executeStatus = executeStatus; + } + + public String getExecuteStatus() + { + return executeStatus; + } + public void setApplyBy(String applyBy) + { + this.applyBy = applyBy; + } + + public String getApplyBy() + { + return applyBy; + } + public void setApplyDate(Date applyDate) + { + this.applyDate = applyDate; + } + + public Date getApplyDate() + { + return applyDate; + } + public void setAuditBy(String auditBy) + { + this.auditBy = auditBy; + } + + public String getAuditBy() + { + return auditBy; + } + public void setAuditDate(Date auditDate) + { + this.auditDate = auditDate; + } + + public Date getAuditDate() + { + return auditDate; + } + public void setUpdateDate(Date updateDate) + { + this.updateDate = updateDate; + } + + public Date getUpdateDate() + { + return updateDate; + } + public void setBeginTime(Date beginTime) + { + this.beginTime = beginTime; + } + + public Date getBeginTime() + { + return beginTime; + } + public void setEndTime(Date endTime) + { + this.endTime = endTime; + } + + public Date getEndTime() + { + return endTime; + } + + public List getWmsRawOutstockDetailList() + { + return wmsRawOutstockDetailList; + } + + public void setWmsRawOutstockDetailList(List wmsRawOutstockDetailList) + { + this.wmsRawOutstockDetailList = wmsRawOutstockDetailList; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("rawOutstockId", getRawOutstockId()) + .append("taskCode", getTaskCode()) + .append("warehouseId", getWarehouseId()) + .append("locationCode", getLocationCode()) + .append("orderId", getOrderId()) + .append("planId", getPlanId()) + .append("planDetailId", getPlanDetailId()) + .append("stationId", getStationId()) + .append("productId", getProductId()) + .append("operationType", getOperationType()) + .append("taskType", getTaskType()) + .append("applyReason", getApplyReason()) + .append("auditReason", getAuditReason()) + .append("auditStatus", getAuditStatus()) + .append("executeStatus", getExecuteStatus()) + .append("applyBy", getApplyBy()) + .append("applyDate", getApplyDate()) + .append("auditBy", getAuditBy()) + .append("auditDate", getAuditDate()) + .append("updateBy", getUpdateBy()) + .append("updateDate", getUpdateDate()) + .append("beginTime", getBeginTime()) + .append("endTime", getEndTime()) + .append("wmsRawOutstockDetailList", getWmsRawOutstockDetailList()) + .toString(); + } +} diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstockDetail.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstockDetail.java new file mode 100644 index 00000000..b70f1902 --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/domain/WmsRawOutstockDetail.java @@ -0,0 +1,298 @@ +package com.hw.wms.domain; + +import java.math.BigDecimal; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.hw.common.core.annotation.Excel; +import com.hw.common.core.web.domain.BaseEntity; + +/** + * 原材料出库记录明细对象 wms_raw_outstock_detail + * + * @author xs + * @date 2023-12-20 + */ +public class WmsRawOutstockDetail extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 原材料出库记录明细ID */ + private Long rawOutstockDetailId; + + /** 原材料出库ID */ + @Excel(name = "原材料出库ID") + private Long rawOutstockId; + + /** 库位编码 */ + @Excel(name = "库位编码") + private String locationCode; + + /** 物料条码 */ + @Excel(name = "物料条码") + private String materialBarcode; + + /** 物料ID */ + @Excel(name = "物料ID") + private Long materialId; + + /** 批次;扫描条码时,从打印条码记录表中获取 */ + @Excel(name = "批次;扫描条码时,从打印条码记录表中获取") + private String instockBatch; + + /** 生产日期;扫描条码时,从打印条码记录表中获取 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "生产日期;扫描条码时,从打印条码记录表中获取", width = 30, dateFormat = "yyyy-MM-dd") + private Date materialProductionDate; + + /** 计划数量 */ + @Excel(name = "计划数量") + private BigDecimal planAmount; + + /** 出库数量 */ + @Excel(name = "出库数量") + private BigDecimal outstockAmount; + + /** 执行状态(0待执行,1执行中,2执行完成) */ + @Excel(name = "执行状态(0待执行,1执行中,2执行完成)") + private String executeStatus; + + /** 同步ERP状态(0:失败,1成功) */ + @Excel(name = "同步ERP状态(0:失败,1成功)") + private String erpStatus; + + /** 出库人 */ + @Excel(name = "出库人") + private String outstockPerson; + + /** 出库时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "出库时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date outstockTime; + + /** 出库方式( 0:PC出库 1:手持出库 2:AGV出库 ) */ + @Excel(name = "出库方式", readConverterExp = "0=:PC出库,1=:手持出库,2=:AGV出库") + private String outstockWay; + + /** 使用机台名称;出库扫描条码时,从打印条码记录表中获取 */ + @Excel(name = "使用机台名称;出库扫描条码时,从打印条码记录表中获取") + private String machineName; + + /** 质检状态(0:待质检,1:合格,2:NG) */ + @Excel(name = "质检状态(0:待质检,1:合格,2:NG)") + private String qualityStatus; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date createDate; + + /** 最后更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "最后更新时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date updateDate; + + /** 每托数量 */ + @Excel(name = "每托数量") + private BigDecimal stackAmount; + + public void setRawOutstockDetailId(Long rawOutstockDetailId) + { + this.rawOutstockDetailId = rawOutstockDetailId; + } + + public Long getRawOutstockDetailId() + { + return rawOutstockDetailId; + } + public void setRawOutstockId(Long rawOutstockId) + { + this.rawOutstockId = rawOutstockId; + } + + public Long getRawOutstockId() + { + return rawOutstockId; + } + public void setLocationCode(String locationCode) + { + this.locationCode = locationCode; + } + + public String getLocationCode() + { + return locationCode; + } + public void setMaterialBarcode(String materialBarcode) + { + this.materialBarcode = materialBarcode; + } + + public String getMaterialBarcode() + { + return materialBarcode; + } + public void setMaterialId(Long materialId) + { + this.materialId = materialId; + } + + public Long getMaterialId() + { + return materialId; + } + public void setInstockBatch(String instockBatch) + { + this.instockBatch = instockBatch; + } + + public String getInstockBatch() + { + return instockBatch; + } + public void setMaterialProductionDate(Date materialProductionDate) + { + this.materialProductionDate = materialProductionDate; + } + + public Date getMaterialProductionDate() + { + return materialProductionDate; + } + public void setPlanAmount(BigDecimal planAmount) + { + this.planAmount = planAmount; + } + + public BigDecimal getPlanAmount() + { + return planAmount; + } + public void setOutstockAmount(BigDecimal outstockAmount) + { + this.outstockAmount = outstockAmount; + } + + public BigDecimal getOutstockAmount() + { + return outstockAmount; + } + public void setExecuteStatus(String executeStatus) + { + this.executeStatus = executeStatus; + } + + public String getExecuteStatus() + { + return executeStatus; + } + public void setErpStatus(String erpStatus) + { + this.erpStatus = erpStatus; + } + + public String getErpStatus() + { + return erpStatus; + } + public void setOutstockPerson(String outstockPerson) + { + this.outstockPerson = outstockPerson; + } + + public String getOutstockPerson() + { + return outstockPerson; + } + public void setOutstockTime(Date outstockTime) + { + this.outstockTime = outstockTime; + } + + public Date getOutstockTime() + { + return outstockTime; + } + public void setOutstockWay(String outstockWay) + { + this.outstockWay = outstockWay; + } + + public String getOutstockWay() + { + return outstockWay; + } + public void setMachineName(String machineName) + { + this.machineName = machineName; + } + + public String getMachineName() + { + return machineName; + } + public void setQualityStatus(String qualityStatus) + { + this.qualityStatus = qualityStatus; + } + + public String getQualityStatus() + { + return qualityStatus; + } + public void setCreateDate(Date createDate) + { + this.createDate = createDate; + } + + public Date getCreateDate() + { + return createDate; + } + public void setUpdateDate(Date updateDate) + { + this.updateDate = updateDate; + } + + public Date getUpdateDate() + { + return updateDate; + } + public void setStackAmount(BigDecimal stackAmount) + { + this.stackAmount = stackAmount; + } + + public BigDecimal getStackAmount() + { + return stackAmount; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("rawOutstockDetailId", getRawOutstockDetailId()) + .append("rawOutstockId", getRawOutstockId()) + .append("locationCode", getLocationCode()) + .append("materialBarcode", getMaterialBarcode()) + .append("materialId", getMaterialId()) + .append("instockBatch", getInstockBatch()) + .append("materialProductionDate", getMaterialProductionDate()) + .append("planAmount", getPlanAmount()) + .append("outstockAmount", getOutstockAmount()) + .append("executeStatus", getExecuteStatus()) + .append("erpStatus", getErpStatus()) + .append("outstockPerson", getOutstockPerson()) + .append("outstockTime", getOutstockTime()) + .append("outstockWay", getOutstockWay()) + .append("machineName", getMachineName()) + .append("qualityStatus", getQualityStatus()) + .append("createBy", getCreateBy()) + .append("createDate", getCreateDate()) + .append("updateBy", getUpdateBy()) + .append("updateDate", getUpdateDate()) + .append("stackAmount", getStackAmount()) + .toString(); + } +} diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/mapper/WmsRawOutstockMapper.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/mapper/WmsRawOutstockMapper.java new file mode 100644 index 00000000..77dd77d7 --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/mapper/WmsRawOutstockMapper.java @@ -0,0 +1,87 @@ +package com.hw.wms.mapper; + +import java.util.List; +import com.hw.wms.domain.WmsRawOutstock; +import com.hw.wms.domain.WmsRawOutstockDetail; + +/** + * 原材料出库记录Mapper接口 + * + * @author xs + * @date 2023-12-20 + */ +public interface WmsRawOutstockMapper +{ + /** + * 查询原材料出库记录 + * + * @param rawOutstockId 原材料出库记录主键 + * @return 原材料出库记录 + */ + public WmsRawOutstock selectWmsRawOutstockByRawOutstockId(Long rawOutstockId); + + /** + * 查询原材料出库记录列表 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 原材料出库记录集合 + */ + public List selectWmsRawOutstockList(WmsRawOutstock wmsRawOutstock); + + /** + * 新增原材料出库记录 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 结果 + */ + public int insertWmsRawOutstock(WmsRawOutstock wmsRawOutstock); + + /** + * 修改原材料出库记录 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 结果 + */ + public int updateWmsRawOutstock(WmsRawOutstock wmsRawOutstock); + + /** + * 删除原材料出库记录 + * + * @param rawOutstockId 原材料出库记录主键 + * @return 结果 + */ + public int deleteWmsRawOutstockByRawOutstockId(Long rawOutstockId); + + /** + * 批量删除原材料出库记录 + * + * @param rawOutstockIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteWmsRawOutstockByRawOutstockIds(Long[] rawOutstockIds); + + /** + * 批量删除原材料出库记录明细 + * + * @param rawOutstockIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteWmsRawOutstockDetailByRawOutstockIds(Long[] rawOutstockIds); + + /** + * 批量新增原材料出库记录明细 + * + * @param wmsRawOutstockDetailList 原材料出库记录明细列表 + * @return 结果 + */ + public int batchWmsRawOutstockDetail(List wmsRawOutstockDetailList); + + + /** + * 通过原材料出库记录主键删除原材料出库记录明细信息 + * + * @param rawOutstockId 原材料出库记录ID + * @return 结果 + */ + public int deleteWmsRawOutstockDetailByRawOutstockId(Long rawOutstockId); +} diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/service/IWmsRawOutstockService.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/service/IWmsRawOutstockService.java new file mode 100644 index 00000000..1ebdbe97 --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/service/IWmsRawOutstockService.java @@ -0,0 +1,61 @@ +package com.hw.wms.service; + +import java.util.List; +import com.hw.wms.domain.WmsRawOutstock; + +/** + * 原材料出库记录Service接口 + * + * @author xs + * @date 2023-12-20 + */ +public interface IWmsRawOutstockService +{ + /** + * 查询原材料出库记录 + * + * @param rawOutstockId 原材料出库记录主键 + * @return 原材料出库记录 + */ + public WmsRawOutstock selectWmsRawOutstockByRawOutstockId(Long rawOutstockId); + + /** + * 查询原材料出库记录列表 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 原材料出库记录集合 + */ + public List selectWmsRawOutstockList(WmsRawOutstock wmsRawOutstock); + + /** + * 新增原材料出库记录 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 结果 + */ + public int insertWmsRawOutstock(WmsRawOutstock wmsRawOutstock); + + /** + * 修改原材料出库记录 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 结果 + */ + public int updateWmsRawOutstock(WmsRawOutstock wmsRawOutstock); + + /** + * 批量删除原材料出库记录 + * + * @param rawOutstockIds 需要删除的原材料出库记录主键集合 + * @return 结果 + */ + public int deleteWmsRawOutstockByRawOutstockIds(Long[] rawOutstockIds); + + /** + * 删除原材料出库记录信息 + * + * @param rawOutstockId 原材料出库记录主键 + * @return 结果 + */ + public int deleteWmsRawOutstockByRawOutstockId(Long rawOutstockId); +} diff --git a/hw-modules/hw-wms/src/main/java/com/hw/wms/service/impl/WmsRawOutstockServiceImpl.java b/hw-modules/hw-wms/src/main/java/com/hw/wms/service/impl/WmsRawOutstockServiceImpl.java new file mode 100644 index 00000000..0dc53551 --- /dev/null +++ b/hw-modules/hw-wms/src/main/java/com/hw/wms/service/impl/WmsRawOutstockServiceImpl.java @@ -0,0 +1,131 @@ +package com.hw.wms.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.util.ArrayList; +import com.hw.common.core.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import com.hw.wms.domain.WmsRawOutstockDetail; +import com.hw.wms.mapper.WmsRawOutstockMapper; +import com.hw.wms.domain.WmsRawOutstock; +import com.hw.wms.service.IWmsRawOutstockService; + +/** + * 原材料出库记录Service业务层处理 + * + * @author xs + * @date 2023-12-20 + */ +@Service +public class WmsRawOutstockServiceImpl implements IWmsRawOutstockService +{ + @Autowired + private WmsRawOutstockMapper wmsRawOutstockMapper; + + /** + * 查询原材料出库记录 + * + * @param rawOutstockId 原材料出库记录主键 + * @return 原材料出库记录 + */ + @Override + public WmsRawOutstock selectWmsRawOutstockByRawOutstockId(Long rawOutstockId) + { + return wmsRawOutstockMapper.selectWmsRawOutstockByRawOutstockId(rawOutstockId); + } + + /** + * 查询原材料出库记录列表 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 原材料出库记录 + */ + @Override + public List selectWmsRawOutstockList(WmsRawOutstock wmsRawOutstock) + { + return wmsRawOutstockMapper.selectWmsRawOutstockList(wmsRawOutstock); + } + + /** + * 新增原材料出库记录 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 结果 + */ + @Transactional + @Override + public int insertWmsRawOutstock(WmsRawOutstock wmsRawOutstock) + { + int rows = wmsRawOutstockMapper.insertWmsRawOutstock(wmsRawOutstock); + insertWmsRawOutstockDetail(wmsRawOutstock); + return rows; + } + + /** + * 修改原材料出库记录 + * + * @param wmsRawOutstock 原材料出库记录 + * @return 结果 + */ + @Transactional + @Override + public int updateWmsRawOutstock(WmsRawOutstock wmsRawOutstock) + { + wmsRawOutstockMapper.deleteWmsRawOutstockDetailByRawOutstockId(wmsRawOutstock.getRawOutstockId()); + insertWmsRawOutstockDetail(wmsRawOutstock); + return wmsRawOutstockMapper.updateWmsRawOutstock(wmsRawOutstock); + } + + /** + * 批量删除原材料出库记录 + * + * @param rawOutstockIds 需要删除的原材料出库记录主键 + * @return 结果 + */ + @Transactional + @Override + public int deleteWmsRawOutstockByRawOutstockIds(Long[] rawOutstockIds) + { + wmsRawOutstockMapper.deleteWmsRawOutstockDetailByRawOutstockIds(rawOutstockIds); + return wmsRawOutstockMapper.deleteWmsRawOutstockByRawOutstockIds(rawOutstockIds); + } + + /** + * 删除原材料出库记录信息 + * + * @param rawOutstockId 原材料出库记录主键 + * @return 结果 + */ + @Transactional + @Override + public int deleteWmsRawOutstockByRawOutstockId(Long rawOutstockId) + { + wmsRawOutstockMapper.deleteWmsRawOutstockDetailByRawOutstockId(rawOutstockId); + return wmsRawOutstockMapper.deleteWmsRawOutstockByRawOutstockId(rawOutstockId); + } + + /** + * 新增原材料出库记录明细信息 + * + * @param wmsRawOutstock 原材料出库记录对象 + */ + public void insertWmsRawOutstockDetail(WmsRawOutstock wmsRawOutstock) + { + List wmsRawOutstockDetailList = wmsRawOutstock.getWmsRawOutstockDetailList(); + Long rawOutstockId = wmsRawOutstock.getRawOutstockId(); + if (StringUtils.isNotNull(wmsRawOutstockDetailList)) + { + List list = new ArrayList(); + for (WmsRawOutstockDetail wmsRawOutstockDetail : wmsRawOutstockDetailList) + { + wmsRawOutstockDetail.setRawOutstockId(rawOutstockId); + list.add(wmsRawOutstockDetail); + } + if (list.size() > 0) + { + wmsRawOutstockMapper.batchWmsRawOutstockDetail(list); + } + } + } +} diff --git a/hw-modules/hw-wms/src/main/resources/banner.txt b/hw-modules/hw-wms/src/main/resources/banner.txt new file mode 100644 index 00000000..fbd45f53 --- /dev/null +++ b/hw-modules/hw-wms/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ _ + (_) | | + _ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___ +| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ +| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | | +|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_| + __/ | __/ | + |___/ |___/ \ No newline at end of file diff --git a/hw-modules/hw-wms/src/main/resources/bootstrap.yml b/hw-modules/hw-wms/src/main/resources/bootstrap.yml new file mode 100644 index 00000000..900e86ba --- /dev/null +++ b/hw-modules/hw-wms/src/main/resources/bootstrap.yml @@ -0,0 +1,29 @@ +# Tomcat +server: + port: 7304 + +# Spring +spring: + application: + # 应用名称 + name: hw-wms + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP + config: + # 配置中心地址 + server-addr: 175.27.215.92:8848 + namespace: jyhb + group: DEFAULT_GROUP + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/hw-modules/hw-wms/src/main/resources/logback.xml b/hw-modules/hw-wms/src/main/resources/logback.xml new file mode 100644 index 00000000..cd8503b5 --- /dev/null +++ b/hw-modules/hw-wms/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + diff --git a/hw-modules/hw-wms/src/main/resources/mapper/wms/WmsRawOutstockMapper.xml b/hw-modules/hw-wms/src/main/resources/mapper/wms/WmsRawOutstockMapper.xml new file mode 100644 index 00000000..af243aa8 --- /dev/null +++ b/hw-modules/hw-wms/src/main/resources/mapper/wms/WmsRawOutstockMapper.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select raw_outstock_id, task_code, warehouse_id, location_code, order_id, plan_id, plan_detail_id, station_id, product_id, operation_type, task_type, apply_reason, audit_reason, audit_status, execute_status, apply_by, apply_date, audit_by, audit_date, update_by, update_date, begin_time, end_time from wms_raw_outstock + + + + + + + + insert into wms_raw_outstock + + task_code, + warehouse_id, + location_code, + order_id, + plan_id, + plan_detail_id, + station_id, + product_id, + operation_type, + task_type, + apply_reason, + audit_reason, + audit_status, + execute_status, + apply_by, + apply_date, + audit_by, + audit_date, + update_by, + update_date, + begin_time, + end_time, + + + #{taskCode}, + #{warehouseId}, + #{locationCode}, + #{orderId}, + #{planId}, + #{planDetailId}, + #{stationId}, + #{productId}, + #{operationType}, + #{taskType}, + #{applyReason}, + #{auditReason}, + #{auditStatus}, + #{executeStatus}, + #{applyBy}, + #{applyDate}, + #{auditBy}, + #{auditDate}, + #{updateBy}, + #{updateDate}, + #{beginTime}, + #{endTime}, + + + + + update wms_raw_outstock + + task_code = #{taskCode}, + warehouse_id = #{warehouseId}, + location_code = #{locationCode}, + order_id = #{orderId}, + plan_id = #{planId}, + plan_detail_id = #{planDetailId}, + station_id = #{stationId}, + product_id = #{productId}, + operation_type = #{operationType}, + task_type = #{taskType}, + apply_reason = #{applyReason}, + audit_reason = #{auditReason}, + audit_status = #{auditStatus}, + execute_status = #{executeStatus}, + apply_by = #{applyBy}, + apply_date = #{applyDate}, + audit_by = #{auditBy}, + audit_date = #{auditDate}, + update_by = #{updateBy}, + update_date = #{updateDate}, + begin_time = #{beginTime}, + end_time = #{endTime}, + + where raw_outstock_id = #{rawOutstockId} + + + + delete from wms_raw_outstock where raw_outstock_id = #{rawOutstockId} + + + + delete from wms_raw_outstock where raw_outstock_id in + + #{rawOutstockId} + + + + + delete from wms_raw_outstock_detail where raw_outstock_id in + + #{rawOutstockId} + + + + + delete from wms_raw_outstock_detail where raw_outstock_id = #{rawOutstockId} + + + + insert into wms_raw_outstock_detail( raw_outstock_detail_id, raw_outstock_id, location_code, material_barcode, material_id, instock_batch, material_production_Date, plan_amount, outstock_amount, execute_status, erp_status, outstock_person, outstock_time, outstock_way, machine_name, quality_status, create_by, create_date, update_by, update_date, stack_amount) values + + ( #{item.rawOutstockDetailId}, #{item.rawOutstockId}, #{item.locationCode}, #{item.materialBarcode}, #{item.materialId}, #{item.instockBatch}, #{item.materialProductionDate}, #{item.planAmount}, #{item.outstockAmount}, #{item.executeStatus}, #{item.erpStatus}, #{item.outstockPerson}, #{item.outstockTime}, #{item.outstockWay}, #{item.machineName}, #{item.qualityStatus}, #{item.createBy}, #{item.createDate}, #{item.updateBy}, #{item.updateDate}, #{item.stackAmount}) + + + \ No newline at end of file diff --git a/hw-modules/pom.xml b/hw-modules/pom.xml index 04ffcb22..eba69f32 100644 --- a/hw-modules/pom.xml +++ b/hw-modules/pom.xml @@ -14,6 +14,9 @@ hw-job hw-file hw-ems + hw-jindie + hw-tdengine + hw-wms hw-modules diff --git a/pom.xml b/pom.xml index 099ee899..6ec6930a 100644 --- a/pom.xml +++ b/pom.xml @@ -206,6 +206,13 @@ ${hw.version} + + + com.hw + hw-api-tdengine + ${hw.version} + + @@ -282,4 +289,4 @@ - \ No newline at end of file +