From 40a310b160eb62bd05158efa3d108f0625fe4717 Mon Sep 17 00:00:00 2001 From: gengby <858962040@qq.com> Date: Fri, 27 Feb 2026 17:25:21 +0800 Subject: [PATCH] =?UTF-8?q?add:=E5=9F=BA=E7=A1=80=E6=95=B0=E6=8D=AE+IOT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/AgvLayoutServiceImpl.java | 6 +- .../controller/BaseDataDeviceController.java | 34 +- .../module/device/entity/BaseDataDevice.java | 26 +- .../param/BaseDataDeviceImportParam.java | 43 ++ .../device/service/BaseDataDeviceService.java | 12 + .../impl/BaseDataDeviceServiceImpl.java | 111 +++++ .../module/point/PointStatusController.java | 50 -- .../controller/BaseDataPointController.java | 138 +++++ .../org/nl/module/point/dto/PointStatus.java | 33 -- .../nl/module/point/entity/BaseDataPoint.java | 68 +++ .../point/mapper/BaseDataPointMapper.java | 13 + .../point/param/BaseDataPointImportParam.java | 40 ++ .../point/service/BaseDataPointService.java | 23 + .../impl/BaseDataPointServiceImpl.java | 126 +++++ nl-iot/pom.xml | 28 ++ .../iot/controller/IotConfigController.java | 162 ++++++ .../iot/controller/IotConnectController.java | 152 ++++++ .../iot/controller/IotDeviceController.java | 161 ++++++ .../iot/controller/IotModelController.java | 127 +++++ .../controller/IotModelTagsController.java | 135 +++++ .../org/nl/module/iot/entity/IotConfig.java | 71 +++ .../org/nl/module/iot/entity/IotConnect.java | 65 +++ .../org/nl/module/iot/entity/IotDevice.java | 71 +++ .../org/nl/module/iot/entity/IotModel.java | 50 ++ .../nl/module/iot/entity/IotModelTags.java | 68 +++ .../nl/module/iot/mapper/IotConfigMapper.java | 13 + .../module/iot/mapper/IotConnectMapper.java | 13 + .../nl/module/iot/mapper/IotDeviceMapper.java | 13 + .../nl/module/iot/mapper/IotModelMapper.java | 13 + .../module/iot/mapper/IotModelTagsMapper.java | 13 + .../iot/param/IotConfigImportParam.java | 43 ++ .../iot/param/IotConnectImportParam.java | 37 ++ .../iot/param/IotDeviceImportParam.java | 37 ++ .../iot/param/IotModelTagsImportParam.java | 37 ++ .../module/iot/service/IotConfigService.java | 23 + .../module/iot/service/IotConnectService.java | 23 + .../module/iot/service/IotDeviceService.java | 23 + .../module/iot/service/IotModelService.java | 15 + .../iot/service/IotModelTagsService.java | 23 + .../service/impl/IotConfigServiceImpl.java | 135 +++++ .../service/impl/IotConnectServiceImpl.java | 128 +++++ .../service/impl/IotDeviceServiceImpl.java | 125 +++++ .../iot/service/impl/IotModelServiceImpl.java | 48 ++ .../service/impl/IotModelTagsServiceImpl.java | 128 +++++ .../modular/file/mapper/DevFileMapper.java | 3 + nl-vue/src/api/baseData/deviceApi.js | 14 + nl-vue/src/api/baseData/pointApi.js | 39 ++ nl-vue/src/api/iot/configApi.js | 37 ++ nl-vue/src/api/iot/connectApi.js | 34 ++ nl-vue/src/api/iot/deviceApi.js | 34 ++ nl-vue/src/api/iot/modelApi.js | 21 + nl-vue/src/api/iot/modelTagsApi.js | 34 ++ nl-vue/src/views/nl_base_data/device/form.vue | 177 +++---- .../src/views/nl_base_data/device/import.vue | 126 +++++ .../src/views/nl_base_data/device/index.vue | 70 ++- nl-vue/src/views/nl_base_data/point/form.vue | 131 +++++ .../src/views/nl_base_data/point/import.vue | 126 +++++ nl-vue/src/views/nl_base_data/point/index.vue | 219 ++++++++ nl-vue/src/views/nl_iot/config/form.vue | 159 ++++++ nl-vue/src/views/nl_iot/config/import.vue | 126 +++++ nl-vue/src/views/nl_iot/config/index.vue | 251 ++++++++++ nl-vue/src/views/nl_iot/connect/form.vue | 119 +++++ nl-vue/src/views/nl_iot/connect/import.vue | 126 +++++ nl-vue/src/views/nl_iot/connect/index.vue | 167 +++++++ nl-vue/src/views/nl_iot/device/form.vue | 140 ++++++ nl-vue/src/views/nl_iot/device/import.vue | 126 +++++ nl-vue/src/views/nl_iot/device/index.vue | 471 ++++++++++++++++++ nl-vue/src/views/nl_iot/model/form.vue | 88 ++++ nl-vue/src/views/nl_iot/model/index.vue | 342 +++++++++++++ .../src/views/nl_iot/model/tagsBatchForm.vue | 154 ++++++ nl-vue/src/views/nl_iot/model/tagsForm.vue | 123 +++++ nl-vue/src/views/nl_iot/model/tagsImport.vue | 129 +++++ nl-web-app/pom.xml | 6 + pom.xml | 1 + 74 files changed, 6073 insertions(+), 223 deletions(-) create mode 100644 nl-base-data/src/main/java/org/nl/module/device/param/BaseDataDeviceImportParam.java delete mode 100644 nl-base-data/src/main/java/org/nl/module/point/PointStatusController.java create mode 100644 nl-base-data/src/main/java/org/nl/module/point/controller/BaseDataPointController.java delete mode 100644 nl-base-data/src/main/java/org/nl/module/point/dto/PointStatus.java create mode 100644 nl-base-data/src/main/java/org/nl/module/point/entity/BaseDataPoint.java create mode 100644 nl-base-data/src/main/java/org/nl/module/point/mapper/BaseDataPointMapper.java create mode 100644 nl-base-data/src/main/java/org/nl/module/point/param/BaseDataPointImportParam.java create mode 100644 nl-base-data/src/main/java/org/nl/module/point/service/BaseDataPointService.java create mode 100644 nl-base-data/src/main/java/org/nl/module/point/service/impl/BaseDataPointServiceImpl.java create mode 100644 nl-iot/pom.xml create mode 100644 nl-iot/src/main/java/org/nl/module/iot/controller/IotConfigController.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/controller/IotConnectController.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/controller/IotDeviceController.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/controller/IotModelController.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/controller/IotModelTagsController.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/entity/IotConfig.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/entity/IotConnect.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/entity/IotDevice.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/entity/IotModel.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/entity/IotModelTags.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/mapper/IotConfigMapper.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/mapper/IotConnectMapper.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/mapper/IotDeviceMapper.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelMapper.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelTagsMapper.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/param/IotConfigImportParam.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/param/IotConnectImportParam.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/param/IotDeviceImportParam.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/param/IotModelTagsImportParam.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/IotConfigService.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/IotConnectService.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/IotDeviceService.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/IotModelService.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/IotModelTagsService.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConfigServiceImpl.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConnectServiceImpl.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/impl/IotDeviceServiceImpl.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelServiceImpl.java create mode 100644 nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelTagsServiceImpl.java create mode 100644 nl-vue/src/api/baseData/pointApi.js create mode 100644 nl-vue/src/api/iot/configApi.js create mode 100644 nl-vue/src/api/iot/connectApi.js create mode 100644 nl-vue/src/api/iot/deviceApi.js create mode 100644 nl-vue/src/api/iot/modelApi.js create mode 100644 nl-vue/src/api/iot/modelTagsApi.js create mode 100644 nl-vue/src/views/nl_base_data/device/import.vue create mode 100644 nl-vue/src/views/nl_base_data/point/form.vue create mode 100644 nl-vue/src/views/nl_base_data/point/import.vue create mode 100644 nl-vue/src/views/nl_base_data/point/index.vue create mode 100644 nl-vue/src/views/nl_iot/config/form.vue create mode 100644 nl-vue/src/views/nl_iot/config/import.vue create mode 100644 nl-vue/src/views/nl_iot/config/index.vue create mode 100644 nl-vue/src/views/nl_iot/connect/form.vue create mode 100644 nl-vue/src/views/nl_iot/connect/import.vue create mode 100644 nl-vue/src/views/nl_iot/connect/index.vue create mode 100644 nl-vue/src/views/nl_iot/device/form.vue create mode 100644 nl-vue/src/views/nl_iot/device/import.vue create mode 100644 nl-vue/src/views/nl_iot/device/index.vue create mode 100644 nl-vue/src/views/nl_iot/model/form.vue create mode 100644 nl-vue/src/views/nl_iot/model/index.vue create mode 100644 nl-vue/src/views/nl_iot/model/tagsBatchForm.vue create mode 100644 nl-vue/src/views/nl_iot/model/tagsForm.vue create mode 100644 nl-vue/src/views/nl_iot/model/tagsImport.vue diff --git a/nl-agv/src/main/java/org/nl/module/layout/service/impl/AgvLayoutServiceImpl.java b/nl-agv/src/main/java/org/nl/module/layout/service/impl/AgvLayoutServiceImpl.java index 52dd457..5e57d63 100644 --- a/nl-agv/src/main/java/org/nl/module/layout/service/impl/AgvLayoutServiceImpl.java +++ b/nl-agv/src/main/java/org/nl/module/layout/service/impl/AgvLayoutServiceImpl.java @@ -1,8 +1,6 @@ package org.nl.module.layout.service.impl; import jakarta.annotation.Resource; -import org.nl.common.localStorage.service.LocalStorageService; -import org.nl.common.localStorage.service.entity.LocalStorage; import org.nl.dev.api.DevFileApi; import org.nl.dev.dto.DevFileApiDto; import org.nl.module.layout.service.AgvLayoutService; @@ -31,9 +29,9 @@ public class AgvLayoutServiceImpl extends ServiceImpl ids) { + baseDataDeviceService.removeBatchByIds(ids); + return CommonResult.ok(); + } + + /** + * 下载导入模板 + */ + @Operation(summary = "下载导入模板") + @GetMapping("/downloadImportTemplate") + public void downloadImportTemplate(HttpServletResponse response) { + baseDataDeviceService.downloadImportTemplate(response); + } + + /** + * 导入设备信息 + */ + @Operation(summary = "导入设备信息") + @PostMapping("/import") + public CommonResult importDevice(@RequestParam("file") MultipartFile file) { + return CommonResult.data(baseDataDeviceService.importDevice(file)); + } @Operation(summary = "设备组件列表") @GetMapping("compentList") @SaIgnore diff --git a/nl-base-data/src/main/java/org/nl/module/device/entity/BaseDataDevice.java b/nl-base-data/src/main/java/org/nl/module/device/entity/BaseDataDevice.java index ac88701..cc4d697 100644 --- a/nl-base-data/src/main/java/org/nl/module/device/entity/BaseDataDevice.java +++ b/nl-base-data/src/main/java/org/nl/module/device/entity/BaseDataDevice.java @@ -1,6 +1,7 @@ package org.nl.module.device.entity; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.v3.oas.annotations.media.Schema; @@ -33,16 +34,20 @@ public class BaseDataDevice implements Serializable { @Schema(description = "设备名称") private String name; - @Schema(description = "类型") - private String type; + @Schema(description = "区域") + private String region; - @Schema(description = "描述") - private String description; @Schema(description = "扩展参数") private String editParam; - @Schema(description = "吃否启用") - private Boolean isUsed; + @Schema(description = "是否启用") + private Boolean enabled; + + @Schema(description = "图标文件ID") + private String icon; + + @Schema(description = "角度") + private Integer angle; @Schema(description = "x坐标") private Integer x; @@ -50,14 +55,11 @@ public class BaseDataDevice implements Serializable { @Schema(description = "y坐标") private Integer y; - @Schema(description = "角度") - private Integer angle; - @Schema(description = "放大比例") - private Integer size; + private Integer scale; - @Schema(description = "图标地址") - private String icon; + @Schema(description = "描述") + private String description; @Schema(description = "图标文件ID") private String fileId; diff --git a/nl-base-data/src/main/java/org/nl/module/device/param/BaseDataDeviceImportParam.java b/nl-base-data/src/main/java/org/nl/module/device/param/BaseDataDeviceImportParam.java new file mode 100644 index 0000000..83ce3b6 --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/device/param/BaseDataDeviceImportParam.java @@ -0,0 +1,43 @@ +package org.nl.module.device.param; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * 设备导入参数 + */ +@Getter +@Setter +public class BaseDataDeviceImportParam { + + @ExcelProperty("设备编码") + private String code; + + @ExcelProperty("设备名称") + private String name; + + @ExcelProperty("所属区域") + private String region; + + @ExcelProperty("图标文件ID") + private String icon; + + @ExcelProperty("X坐标") + private String x; + + @ExcelProperty("Y坐标") + private String y; + + @ExcelProperty("角度") + private String angle; + + @ExcelProperty("放大比例") + private String scale; + + @ExcelProperty("启用状态") + private String enabled; + + @ExcelProperty("备注") + private String description; +} diff --git a/nl-base-data/src/main/java/org/nl/module/device/service/BaseDataDeviceService.java b/nl-base-data/src/main/java/org/nl/module/device/service/BaseDataDeviceService.java index 1565d8b..5482ead 100644 --- a/nl-base-data/src/main/java/org/nl/module/device/service/BaseDataDeviceService.java +++ b/nl-base-data/src/main/java/org/nl/module/device/service/BaseDataDeviceService.java @@ -1,11 +1,23 @@ package org.nl.module.device.service; import com.baomidou.mybatisplus.extension.service.IService; +import cn.hutool.json.JSONObject; +import jakarta.servlet.http.HttpServletResponse; import org.nl.module.device.entity.BaseDataDevice; +import org.springframework.web.multipart.MultipartFile; /** * 设备信息表Service接口 */ public interface BaseDataDeviceService extends IService { + /** + * 下载导入模板 + */ + void downloadImportTemplate(HttpServletResponse response); + + /** + * 导入设备信息 + */ + JSONObject importDevice(MultipartFile file); } diff --git a/nl-base-data/src/main/java/org/nl/module/device/service/impl/BaseDataDeviceServiceImpl.java b/nl-base-data/src/main/java/org/nl/module/device/service/impl/BaseDataDeviceServiceImpl.java index 79e4286..bcb9a18 100644 --- a/nl-base-data/src/main/java/org/nl/module/device/service/impl/BaseDataDeviceServiceImpl.java +++ b/nl-base-data/src/main/java/org/nl/module/device/service/impl/BaseDataDeviceServiceImpl.java @@ -1,10 +1,26 @@ package org.nl.module.device.service.impl; +import com.alibaba.excel.EasyExcel; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.common.exception.CommonException; +import org.nl.common.util.CommonDownloadUtil; +import org.nl.common.util.CommonResponseUtil; import org.nl.module.device.entity.BaseDataDevice; import org.nl.module.device.mapper.BaseDataDeviceMapper; +import org.nl.module.device.param.BaseDataDeviceImportParam; import org.nl.module.device.service.BaseDataDeviceService; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; /** * 设备信息表Service实现类 @@ -12,4 +28,99 @@ import org.springframework.stereotype.Service; @Service public class BaseDataDeviceServiceImpl extends ServiceImpl implements BaseDataDeviceService { + @Override + public void downloadImportTemplate(HttpServletResponse response) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + EasyExcel.write(outputStream, BaseDataDeviceImportParam.class) + .sheet("设备模板") + .doWrite(Collections.emptyList()); + CommonDownloadUtil.download("设备导入模板.xlsx", outputStream.toByteArray(), response); + } catch (Exception e) { + try { + CommonResponseUtil.renderError(response, "下载设备导入模板失败"); + } catch (IOException ignored) { + // ignored + } + } + } + + @Override + public JSONObject importDevice(MultipartFile file) { + try { + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List importList = EasyExcel.read(file.getInputStream()) + .head(BaseDataDeviceImportParam.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + for (int i = 0; i < importList.size(); i++) { + JSONObject result = this.doImport(importList.get(i), i); + if (result.getBool("success")) { + successCount += 1; + } else { + errorCount += 1; + errorDetail.add(result); + } + } + return JSONUtil.createObj() + .set("totalCount", importList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); + } catch (Exception e) { + throw new CommonException("设备导入失败"); + } + } + + private JSONObject doImport(BaseDataDeviceImportParam param, int index) { + if (ObjectUtil.hasEmpty(param.getCode(), param.getName())) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "必填字段存在空值"); + } + BaseDataDevice device = new BaseDataDevice(); + device.setCode(param.getCode()); + device.setName(param.getName()); + device.setRegion(param.getRegion()); + device.setIcon(param.getIcon()); + device.setX(parseInteger(param.getX())); + device.setY(parseInteger(param.getY())); + device.setAngle(parseInteger(param.getAngle())); + device.setScale(parseInteger(param.getScale())); + device.setEnabled(parseBoolean(param.getEnabled(), true)); + device.setDescription(param.getDescription()); + this.save(device); + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", true) + .set("msg", "成功"); + } + + private Integer parseInteger(String value) { + if (ObjectUtil.isEmpty(value)) { + return null; + } + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + return null; + } + } + + private Boolean parseBoolean(String value, boolean defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + String normalized = value.trim(); + if ("1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "是".equals(normalized) || "启用".equals(normalized)) { + return true; + } + if ("0".equals(normalized) || "false".equalsIgnoreCase(normalized) || "否".equals(normalized) || "禁用".equals(normalized)) { + return false; + } + return defaultValue; + } } diff --git a/nl-base-data/src/main/java/org/nl/module/point/PointStatusController.java b/nl-base-data/src/main/java/org/nl/module/point/PointStatusController.java deleted file mode 100644 index bdfcb89..0000000 --- a/nl-base-data/src/main/java/org/nl/module/point/PointStatusController.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright [2022] [https://www.xiaonuo.vip] - * - * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: - * - * 1.请不要删除和修改根目录下的LICENSE文件。 - * 2.请不要删除和修改Snowy源码头部的版权声明。 - * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 - * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip - * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 - * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip - */ -package org.nl.module.point; - - -import cn.dev33.satoken.annotation.SaIgnore; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.nl.common.pojo.CommonResult; -import org.nl.module.point.dto.PointStatus; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -@Tag(name = "AGV模块") -@RestController() -@RequestMapping("/api/baseData/point") -@Validated -@SaIgnore -public class PointStatusController { - static { - System.out.println("初始化AGV模块------------"); - } - @GetMapping("status") - public CommonResult> status() { - List list = new ArrayList<>(); - for (int i = 0; i < new Random().nextInt(100)+10; i++) { - PointStatus build = PointStatus.builder().pointCode("A1_1" + i) - .pointName("货位" + i) - .x(new Random().nextInt(1280)) - .y(new Random().nextInt(960)).status(String.valueOf(i / 2)).build(); - list.add(build); - } - return CommonResult.data(list); - } -} diff --git a/nl-base-data/src/main/java/org/nl/module/point/controller/BaseDataPointController.java b/nl-base-data/src/main/java/org/nl/module/point/controller/BaseDataPointController.java new file mode 100644 index 0000000..bce29d1 --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/point/controller/BaseDataPointController.java @@ -0,0 +1,138 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.module.point.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.pojo.CommonResult; +import org.nl.module.point.entity.BaseDataPoint; +import org.nl.module.point.service.BaseDataPointService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +/** + * 点位表Controller + */ +@Tag(name = "点位信息管理") +@RestController +@RequestMapping("/api/baseData/point") +@Validated +public class BaseDataPointController { + + @Autowired + private BaseDataPointService baseDataPointService; + + /** + * 获取点位列表 + */ + @Operation(summary = "获取点位列表") + @GetMapping("/list") + public CommonResult list(@RequestParam(required = false) String searchKey, + @RequestParam(required = false) String region, + @RequestParam(required = false) Integer subDeviceId, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 关键词搜索(点位名称或编码) + if (StringUtils.isNotBlank(searchKey)) { + queryWrapper.and(wrapper -> wrapper + .like(BaseDataPoint::getName, searchKey) + .or() + .like(BaseDataPoint::getCode, searchKey) + ); + } + + // 区域搜索 + if (StringUtils.isNotBlank(region)) { + queryWrapper.like(BaseDataPoint::getRegion, region); + } + + // 子设备筛选 + if (subDeviceId != null) { + queryWrapper.eq(BaseDataPoint::getSubDeviceId, subDeviceId); + } + + // 按创建时间倒序 + queryWrapper.orderByDesc(BaseDataPoint::getCreateTime); + + Page page = new Page<>(current, size); + Page result = baseDataPointService.page(page, queryWrapper); + + return CommonResult.data(result); + } + + /** + * 新增点位信息 + */ + @Operation(summary = "新增点位信息") + @PostMapping("/add") + public CommonResult add(@RequestBody BaseDataPoint point) { + baseDataPointService.save(point); + return CommonResult.ok(); + } + + /** + * 修改点位信息 + */ + @Operation(summary = "修改点位信息") + @PostMapping("/edit") + public CommonResult edit(@RequestBody BaseDataPoint point) { + baseDataPointService.updateById(point); + return CommonResult.ok(); + } + + /** + * 删除点位信息 + */ + @Operation(summary = "删除点位信息") + @PostMapping("/delete") + public CommonResult delete(@RequestBody BaseDataPoint point) { + baseDataPointService.removeById(point.getId()); + return CommonResult.ok(); + } + + /** + * 批量删除点位信息 + */ + @Operation(summary = "批量删除点位信息") + @PostMapping("/deleteBatch") + public CommonResult deleteBatch(@RequestBody java.util.List ids) { + baseDataPointService.removeBatchByIds(ids); + return CommonResult.ok(); + } + + /** + * 下载导入模板 + */ + @Operation(summary = "下载导入模板") + @GetMapping("/downloadImportTemplate") + public void downloadImportTemplate(HttpServletResponse response) { + baseDataPointService.downloadImportTemplate(response); + } + + /** + * 导入点位信息 + */ + @Operation(summary = "导入点位信息") + @PostMapping("/import") + public CommonResult importPoint(@RequestParam("file") MultipartFile file) { + return CommonResult.data(baseDataPointService.importPoint(file)); + } +} diff --git a/nl-base-data/src/main/java/org/nl/module/point/dto/PointStatus.java b/nl-base-data/src/main/java/org/nl/module/point/dto/PointStatus.java deleted file mode 100644 index 9fbfddc..0000000 --- a/nl-base-data/src/main/java/org/nl/module/point/dto/PointStatus.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.nl.module.point.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class PointStatus { - /** - * 点位编码 - */ - private String pointCode; - /** - * 车辆类型 - */ - private String pointName; - /** - * 坐标X - */ - private int x; - /** - * 坐标Y - */ - private int y; - /** - * 点位状态 0无货1有货 - */ - private String status; -} diff --git a/nl-base-data/src/main/java/org/nl/module/point/entity/BaseDataPoint.java b/nl-base-data/src/main/java/org/nl/module/point/entity/BaseDataPoint.java new file mode 100644 index 0000000..aecc19e --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/point/entity/BaseDataPoint.java @@ -0,0 +1,68 @@ +package org.nl.module.point.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 点位表实体类 + */ +@Data +@TableName("base_data_point") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "点位表") +public class BaseDataPoint implements Serializable { + + @TableId(type = IdType.AUTO) + @Schema(description = "主键ID") + private Integer id; + + @Schema(description = "点位编码") + private String code; + + @Schema(description = "点位名称") + private String name; + + @Schema(description = "点位区域") + private String region; + + @Schema(description = "X坐标") + private Integer x; + + @Schema(description = "Y坐标") + private Integer y; + + @Schema(description = "有无货") + private Boolean isHasgoods; + + @Schema(description = "关联子设备ID") + private Integer subDeviceId; + + @Schema(description = "启用状态") + private Boolean enabled; + + @Schema(description = "描述") + private String description; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建用户") + private String createName; + + @Schema(description = "修改时间") + private Date updateTime; + + @Schema(description = "修改用户") + private String updateName; +} diff --git a/nl-base-data/src/main/java/org/nl/module/point/mapper/BaseDataPointMapper.java b/nl-base-data/src/main/java/org/nl/module/point/mapper/BaseDataPointMapper.java new file mode 100644 index 0000000..ac485e0 --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/point/mapper/BaseDataPointMapper.java @@ -0,0 +1,13 @@ +package org.nl.module.point.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.module.point.entity.BaseDataPoint; + +/** + * 点位表Mapper + */ +@Mapper +public interface BaseDataPointMapper extends BaseMapper { + +} diff --git a/nl-base-data/src/main/java/org/nl/module/point/param/BaseDataPointImportParam.java b/nl-base-data/src/main/java/org/nl/module/point/param/BaseDataPointImportParam.java new file mode 100644 index 0000000..76d0073 --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/point/param/BaseDataPointImportParam.java @@ -0,0 +1,40 @@ +package org.nl.module.point.param; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * 点位导入参数 + */ +@Getter +@Setter +public class BaseDataPointImportParam { + + @ExcelProperty("点位编码") + private String code; + + @ExcelProperty("点位名称") + private String name; + + @ExcelProperty("点位区域") + private String region; + + @ExcelProperty("X坐标") + private String x; + + @ExcelProperty("Y坐标") + private String y; + + @ExcelProperty("有无货") + private String isHasgoods; + + @ExcelProperty("子设备ID") + private String subDeviceId; + + @ExcelProperty("启用状态") + private String enabled; + + @ExcelProperty("备注") + private String description; +} diff --git a/nl-base-data/src/main/java/org/nl/module/point/service/BaseDataPointService.java b/nl-base-data/src/main/java/org/nl/module/point/service/BaseDataPointService.java new file mode 100644 index 0000000..f1d1ba4 --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/point/service/BaseDataPointService.java @@ -0,0 +1,23 @@ +package org.nl.module.point.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.hutool.json.JSONObject; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.module.point.entity.BaseDataPoint; +import org.springframework.web.multipart.MultipartFile; + +/** + * 点位表Service接口 + */ +public interface BaseDataPointService extends IService { + + /** + * 下载导入模板 + */ + void downloadImportTemplate(HttpServletResponse response); + + /** + * 导入点位信息 + */ + JSONObject importPoint(MultipartFile file); +} diff --git a/nl-base-data/src/main/java/org/nl/module/point/service/impl/BaseDataPointServiceImpl.java b/nl-base-data/src/main/java/org/nl/module/point/service/impl/BaseDataPointServiceImpl.java new file mode 100644 index 0000000..8f4aa17 --- /dev/null +++ b/nl-base-data/src/main/java/org/nl/module/point/service/impl/BaseDataPointServiceImpl.java @@ -0,0 +1,126 @@ +package org.nl.module.point.service.impl; + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.common.exception.CommonException; +import org.nl.common.util.CommonDownloadUtil; +import org.nl.common.util.CommonResponseUtil; +import org.nl.module.point.entity.BaseDataPoint; +import org.nl.module.point.mapper.BaseDataPointMapper; +import org.nl.module.point.param.BaseDataPointImportParam; +import org.nl.module.point.service.BaseDataPointService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * 点位表Service实现类 + */ +@Service +public class BaseDataPointServiceImpl extends ServiceImpl implements BaseDataPointService { + + @Override + public void downloadImportTemplate(HttpServletResponse response) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + EasyExcel.write(outputStream, BaseDataPointImportParam.class) + .sheet("点位模板") + .doWrite(Collections.emptyList()); + CommonDownloadUtil.download("点位导入模板.xlsx", outputStream.toByteArray(), response); + } catch (Exception e) { + try { + CommonResponseUtil.renderError(response, "下载点位导入模板失败"); + } catch (IOException ignored) { + // ignored + } + } + } + + @Override + public JSONObject importPoint(MultipartFile file) { + try { + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List importList = EasyExcel.read(file.getInputStream()) + .head(BaseDataPointImportParam.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + for (int i = 0; i < importList.size(); i++) { + JSONObject result = this.doImport(importList.get(i), i); + if (result.getBool("success")) { + successCount += 1; + } else { + errorCount += 1; + errorDetail.add(result); + } + } + return JSONUtil.createObj() + .set("totalCount", importList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); + } catch (Exception e) { + throw new CommonException("点位导入失败"); + } + } + + private JSONObject doImport(BaseDataPointImportParam param, int index) { + if (ObjectUtil.hasEmpty(param.getCode(), param.getName())) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "必填字段存在空值"); + } + Integer subDeviceId = parseInteger(param.getSubDeviceId()); + BaseDataPoint point = new BaseDataPoint(); + point.setCode(param.getCode()); + point.setName(param.getName()); + point.setRegion(param.getRegion()); + point.setX(parseInteger(param.getX())); + point.setY(parseInteger(param.getY())); + point.setIsHasgoods(parseBoolean(param.getIsHasgoods(), false)); + point.setSubDeviceId(subDeviceId); + point.setEnabled(parseBoolean(param.getEnabled(), true)); + point.setDescription(param.getDescription()); + this.save(point); + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", true) + .set("msg", "成功"); + } + + private Integer parseInteger(String value) { + if (ObjectUtil.isEmpty(value)) { + return null; + } + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + return null; + } + } + + private Boolean parseBoolean(String value, boolean defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + String normalized = value.trim(); + if ("1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "是".equals(normalized) || "有".equals(normalized) || "启用".equals(normalized)) { + return true; + } + if ("0".equals(normalized) || "false".equalsIgnoreCase(normalized) || "否".equals(normalized) || "无".equals(normalized) || "禁用".equals(normalized)) { + return false; + } + return defaultValue; + } +} diff --git a/nl-iot/pom.xml b/nl-iot/pom.xml new file mode 100644 index 0000000..8456bb6 --- /dev/null +++ b/nl-iot/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + org.nl + nl-tool-platform + 3.0.0 + + + nl-iot + + + 17 + 17 + UTF-8 + + + + + + org.nl + nl-common + + + + \ No newline at end of file diff --git a/nl-iot/src/main/java/org/nl/module/iot/controller/IotConfigController.java b/nl-iot/src/main/java/org/nl/module/iot/controller/IotConfigController.java new file mode 100644 index 0000000..128190f --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/controller/IotConfigController.java @@ -0,0 +1,162 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.module.iot.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.pojo.CommonResult; +import org.nl.module.iot.entity.IotConfig; +import org.nl.module.iot.service.IotDeviceService; +import org.nl.module.iot.service.IotConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +/** + * 信号配置表Controller + */ +@Tag(name = "信号配置管理") +@RestController +@RequestMapping("/api/iot/config") +@Validated +public class IotConfigController { + + @Autowired + private IotConfigService iotConfigService; + + @Autowired + private IotDeviceService iotDeviceService; + + /** + * 获取信号配置列表 + */ + @Operation(summary = "获取信号配置列表") + @GetMapping("/list") + public CommonResult list(@RequestParam(required = false) String searchKey, + @RequestParam(required = false) Integer connectId, + @RequestParam(required = false) Integer subDeviceId, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + if (StringUtils.isNotBlank(searchKey)) { + queryWrapper.and(wrapper -> wrapper + .like(IotConfig::getAlias, searchKey) + .or() + .like(IotConfig::getAliasName, searchKey) + ); + } + + if (connectId != null) { + queryWrapper.eq(IotConfig::getConnectId, connectId); + } + + if (subDeviceId != null) { + queryWrapper.eq(IotConfig::getSubDeviceId, subDeviceId); + } + + queryWrapper.orderByAsc(IotConfig::getSubDeviceId) + .orderByAsc(IotConfig::getSort); + + Page page = new Page<>(current, size); + Page result = iotConfigService.page(page, queryWrapper); + + return CommonResult.data(result); + } + + /** + * 新增信号配置 + */ + @Operation(summary = "新增信号配置") + @PostMapping("/add") + public CommonResult add(@RequestBody IotConfig config) { + iotConfigService.save(config); + return CommonResult.ok(); + } + + /** + * 修改信号配置 + */ + @Operation(summary = "修改信号配置") + @PostMapping("/edit") + public CommonResult edit(@RequestBody IotConfig config) { + iotConfigService.updateById(config); + return CommonResult.ok(); + } + + /** + * 批量保存信号配置 + */ + @Operation(summary = "批量保存信号配置") + @PostMapping("/saveBatch") + public CommonResult saveBatch(@RequestBody java.util.List configs) { + iotConfigService.saveOrUpdateBatch(configs); + if (configs != null && !configs.isEmpty()) { + java.util.Set deviceIds = configs.stream() + .map(IotConfig::getSubDeviceId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!deviceIds.isEmpty()) { + iotDeviceService.lambdaUpdate() + .in(org.nl.module.iot.entity.IotDevice::getId, deviceIds) + .set(org.nl.module.iot.entity.IotDevice::getIsConfig, true) + .update(); + } + } + return CommonResult.ok(); + } + + /** + * 下载导入模板 + */ + @Operation(summary = "下载导入模板") + @GetMapping("/downloadImportTemplate") + public void downloadImportTemplate(HttpServletResponse response) { + iotConfigService.downloadImportTemplate(response); + } + + /** + * 导入信号配置 + */ + @Operation(summary = "导入信号配置") + @PostMapping("/import") + public CommonResult importConfig(@RequestParam("file") MultipartFile file) { + return CommonResult.data(iotConfigService.importConfig(file)); + } + + /** + * 删除信号配置 + */ + @Operation(summary = "删除信号配置") + @PostMapping("/delete") + public CommonResult delete(@RequestBody IotConfig config) { + iotConfigService.removeById(config.getId()); + return CommonResult.ok(); + } + + /** + * 批量删除信号配置 + */ + @Operation(summary = "批量删除信号配置") + @PostMapping("/deleteBatch") + public CommonResult deleteBatch(@RequestBody java.util.List ids) { + iotConfigService.removeBatchByIds(ids); + return CommonResult.ok(); + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/controller/IotConnectController.java b/nl-iot/src/main/java/org/nl/module/iot/controller/IotConnectController.java new file mode 100644 index 0000000..6459493 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/controller/IotConnectController.java @@ -0,0 +1,152 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.module.iot.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.pojo.CommonResult; +import org.nl.module.iot.entity.IotConnect; +import org.nl.module.iot.entity.IotConfig; +import org.nl.module.iot.service.IotConfigService; +import org.nl.module.iot.service.IotConnectService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; +import org.nl.common.exception.CommonException; + +/** + * 连接表Controller + */ +@Tag(name = "连接管理") +@RestController +@RequestMapping("/api/iot/connect") +@Validated +public class IotConnectController { + + @Autowired + private IotConnectService iotConnectService; + + @Autowired + private IotConfigService iotConfigService; + + /** + * 获取连接列表 + */ + @Operation(summary = "获取连接列表") + @GetMapping("/list") + public CommonResult list(@RequestParam(required = false) String searchKey, + @RequestParam(required = false) String protocol, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + if (StringUtils.isNotBlank(searchKey)) { + queryWrapper.and(wrapper -> wrapper + .like(IotConnect::getCode, searchKey) + .or() + .like(IotConnect::getHost, searchKey) + ); + } + + if (StringUtils.isNotBlank(protocol)) { + queryWrapper.eq(IotConnect::getProtocol, protocol); + } + + queryWrapper.orderByDesc(IotConnect::getCreateTime); + + Page page = new Page<>(current, size); + Page result = iotConnectService.page(page, queryWrapper); + + return CommonResult.data(result); + } + + /** + * 新增连接 + */ + @Operation(summary = "新增连接") + @PostMapping("/add") + public CommonResult add(@RequestBody IotConnect connect) { + iotConnectService.save(connect); + return CommonResult.ok(); + } + + /** + * 修改连接 + */ + @Operation(summary = "修改连接") + @PostMapping("/edit") + public CommonResult edit(@RequestBody IotConnect connect) { + iotConnectService.updateById(connect); + return CommonResult.ok(); + } + + /** + * 删除连接 + */ + @Operation(summary = "删除连接") + @PostMapping("/delete") + public CommonResult delete(@RequestBody IotConnect connect) { + if (connect.getId() == null) { + throw new CommonException("连接ID不能为空"); + } + long count = iotConfigService.lambdaQuery() + .eq(IotConfig::getConnectId, connect.getId()) + .count(); + if (count > 0) { + throw new CommonException("连接已被信号配置使用,无法删除"); + } + iotConnectService.removeById(connect.getId()); + return CommonResult.ok(); + } + + /** + * 批量删除连接 + */ + @Operation(summary = "批量删除连接") + @PostMapping("/deleteBatch") + public CommonResult deleteBatch(@RequestBody java.util.List ids) { + if (ids != null && !ids.isEmpty()) { + long count = iotConfigService.lambdaQuery() + .in(IotConfig::getConnectId, ids) + .count(); + if (count > 0) { + throw new CommonException("所选连接存在信号配置,无法删除"); + } + } + iotConnectService.removeBatchByIds(ids); + return CommonResult.ok(); + } + + /** + * 下载导入模板 + */ + @Operation(summary = "下载导入模板") + @GetMapping("/downloadImportTemplate") + public void downloadImportTemplate(HttpServletResponse response) { + iotConnectService.downloadImportTemplate(response); + } + + /** + * 导入连接信息 + */ + @Operation(summary = "导入连接信息") + @PostMapping("/import") + public CommonResult importConnect(@RequestParam("file") MultipartFile file) { + return CommonResult.data(iotConnectService.importConnect(file)); + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/controller/IotDeviceController.java b/nl-iot/src/main/java/org/nl/module/iot/controller/IotDeviceController.java new file mode 100644 index 0000000..aafea76 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/controller/IotDeviceController.java @@ -0,0 +1,161 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.module.iot.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.pojo.CommonResult; +import org.nl.module.iot.entity.IotDevice; +import org.nl.module.iot.entity.IotConfig; +import org.nl.module.iot.service.IotDeviceService; +import org.nl.module.iot.service.IotConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; +import org.nl.common.exception.CommonException; + +/** + * 子设备表Controller + */ +@Tag(name = "子设备管理") +@RestController +@RequestMapping("/api/iot/device") +@Validated +public class IotDeviceController { + + @Autowired + private IotDeviceService iotDeviceService; + + @Autowired + private IotConfigService iotConfigService; + /** + * 获取子设备列表 + */ + @Operation(summary = "获取子设备列表") + @GetMapping("/list") + public CommonResult list(@RequestParam(required = false) String searchKey, + @RequestParam(required = false) String region, + @RequestParam(required = false) Integer mainDeviceId, + @RequestParam(required = false) Integer modelId, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + if (StringUtils.isNotBlank(searchKey)) { + queryWrapper.and(wrapper -> wrapper + .like(IotDevice::getName, searchKey) + .or() + .like(IotDevice::getCode, searchKey) + ); + } + + if (StringUtils.isNotBlank(region)) { + queryWrapper.like(IotDevice::getRegion, region); + } + + if (mainDeviceId != null) { + queryWrapper.eq(IotDevice::getMainDeviceId, mainDeviceId); + } + + if (modelId != null) { + queryWrapper.eq(IotDevice::getModelId, modelId); + } + + queryWrapper.orderByDesc(IotDevice::getCreateTime); + + Page page = new Page<>(current, size); + Page result = iotDeviceService.page(page, queryWrapper); + + return CommonResult.data(result); + } + + /** + * 新增子设备 + */ + @Operation(summary = "新增子设备") + @PostMapping("/add") + public CommonResult add(@RequestBody IotDevice device) { + iotDeviceService.save(device); + return CommonResult.ok(); + } + + /** + * 修改子设备 + */ + @Operation(summary = "修改子设备") + @PostMapping("/edit") + public CommonResult edit(@RequestBody IotDevice device) { + iotDeviceService.updateById(device); + return CommonResult.ok(); + } + + /** + * 删除子设备 + */ + @Operation(summary = "删除子设备") + @PostMapping("/delete") + public CommonResult delete(@RequestBody IotDevice device) { + if (device.getId() == null) { + throw new CommonException("子设备ID不能为空"); + } + long count = iotConfigService.lambdaQuery() + .eq(IotConfig::getSubDeviceId, device.getId()) + .count(); + if (count > 0) { + throw new CommonException("子设备已配置信号,无法删除"); + } + iotDeviceService.removeById(device.getId()); + return CommonResult.ok(); + } + + /** + * 批量删除子设备 + */ + @Operation(summary = "批量删除子设备") + @PostMapping("/deleteBatch") + public CommonResult deleteBatch(@RequestBody java.util.List ids) { + if (ids != null && !ids.isEmpty()) { + long count = iotConfigService.lambdaQuery() + .in(IotConfig::getSubDeviceId, ids) + .count(); + if (count > 0) { + throw new CommonException("所选子设备存在信号配置,无法删除"); + } + } + iotDeviceService.removeBatchByIds(ids); + return CommonResult.ok(); + } + + /** + * 下载导入模板 + */ + @Operation(summary = "下载导入模板") + @GetMapping("/downloadImportTemplate") + public void downloadImportTemplate(HttpServletResponse response) { + iotDeviceService.downloadImportTemplate(response); + } + + /** + * 导入子设备 + */ + @Operation(summary = "导入子设备") + @PostMapping("/import") + public CommonResult importDevice(@RequestParam("file") MultipartFile file) { + return CommonResult.data(iotDeviceService.importDevice(file)); + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/controller/IotModelController.java b/nl-iot/src/main/java/org/nl/module/iot/controller/IotModelController.java new file mode 100644 index 0000000..c4d4db3 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/controller/IotModelController.java @@ -0,0 +1,127 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.module.iot.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.pojo.CommonResult; +import org.nl.module.iot.entity.IotModel; +import org.nl.module.iot.entity.IotModelTags; +import org.nl.module.iot.service.IotModelService; +import org.nl.module.iot.service.IotModelTagsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 模型表Controller + */ +@Tag(name = "模型管理") +@RestController +@RequestMapping("/api/iot/model") +@Validated +public class IotModelController { + + @Autowired + private IotModelService iotModelService; + + @Autowired + private IotModelTagsService iotModelTagsService; + + /** + * 获取模型列表 + */ + @Operation(summary = "获取模型列表") + @GetMapping("/list") + public CommonResult list(@RequestParam(required = false) String searchKey, + @RequestParam(required = false) String deviceType, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + if (StringUtils.isNotBlank(searchKey)) { + queryWrapper.and(wrapper -> wrapper + .like(IotModel::getName, searchKey) + .or() + .like(IotModel::getCode, searchKey) + ); + } + + if (StringUtils.isNotBlank(deviceType)) { + queryWrapper.eq(IotModel::getDeviceType, deviceType); + } + + queryWrapper.orderByDesc(IotModel::getCreateTime); + + Page page = new Page<>(current, size); + Page result = iotModelService.page(page, queryWrapper); + + return CommonResult.data(result); + } + + /** + * 新增模型 + */ + @Operation(summary = "新增模型") + @PostMapping("/add") + public CommonResult add(@RequestBody IotModel model) { + iotModelService.save(model); + return CommonResult.ok(); + } + + /** + * 修改模型 + */ + @Operation(summary = "修改模型") + @PostMapping("/edit") + public CommonResult edit(@RequestBody IotModel model) { + iotModelService.updateById(model); + return CommonResult.ok(); + } + + /** + * 删除模型 + */ + @Operation(summary = "删除模型") + @PostMapping("/delete") + @Transactional(rollbackFor = Exception.class) + public CommonResult delete(@RequestBody IotModel iotModel) { + iotModelTagsService.lambdaUpdate() + .eq(IotModelTags::getModeId, String.valueOf(iotModel.getId())) + .remove(); + iotModelService.removeById(iotModel.getId()); + return CommonResult.ok(); + } + + /** + * 批量删除模型 + */ + @Operation(summary = "批量删除模型") + @PostMapping("/deleteBatch") + @Transactional(rollbackFor = Exception.class) + public CommonResult deleteBatch(@RequestBody List ids) { + if (ids != null && !ids.isEmpty()) { + iotModelTagsService.lambdaUpdate() + .in(IotModelTags::getModeId, ids.stream().map(String::valueOf).toList()) + .remove(); + } + iotModelService.removeBatchByIds(ids); + return CommonResult.ok(); + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/controller/IotModelTagsController.java b/nl-iot/src/main/java/org/nl/module/iot/controller/IotModelTagsController.java new file mode 100644 index 0000000..321ede0 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/controller/IotModelTagsController.java @@ -0,0 +1,135 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.module.iot.controller; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; +import org.nl.common.pojo.CommonResult; +import org.nl.module.iot.entity.IotModelTags; +import org.nl.module.iot.service.IotModelTagsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +/** + * 模型信号表Controller + */ +@Tag(name = "模型信号管理") +@RestController +@RequestMapping("/api/iot/modelTags") +@Validated +public class IotModelTagsController { + + @Autowired + private IotModelTagsService iotModelTagsService; + + /** + * 获取模型信号列表 + */ + @Operation(summary = "获取模型信号列表") + @GetMapping("/list") + public CommonResult list(@RequestParam(required = false) String searchKey, + @RequestParam(required = false) String modeId, + @RequestParam(defaultValue = "1") Integer current, + @RequestParam(defaultValue = "10") Integer size) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + if (StringUtils.isNotBlank(searchKey)) { + queryWrapper.and(wrapper -> wrapper + .like(IotModelTags::getAlias, searchKey) + .or() + .like(IotModelTags::getAliasName, searchKey) + ); + } + + if (StringUtils.isNotBlank(modeId)) { + queryWrapper.eq(IotModelTags::getModeId, modeId); + } + + queryWrapper.orderByAsc(IotModelTags::getSort) + .orderByDesc(IotModelTags::getCreateTime); + + Page page = new Page<>(current, size); + Page result = iotModelTagsService.page(page, queryWrapper); + + return CommonResult.data(result); + } + + /** + * 新增模型信号 + */ + @Operation(summary = "新增模型信号") + @PostMapping("/add") + public CommonResult add(@RequestBody IotModelTags modelTags) { + iotModelTagsService.save(modelTags); + return CommonResult.ok(); + } + + /** + * 批量新增模型信号 + */ + @Operation(summary = "批量新增模型信号") + @PostMapping("/addBatch") + public CommonResult addBatch(@RequestBody java.util.List modelTagsList) { + iotModelTagsService.saveBatch(modelTagsList); + return CommonResult.ok(); + } + + /** + * 修改模型信号 + */ + @Operation(summary = "修改模型信号") + @PostMapping("/edit") + public CommonResult edit(@RequestBody IotModelTags modelTags) { + iotModelTagsService.updateById(modelTags); + return CommonResult.ok(); + } + + /** + * 删除模型信号 + */ + @Operation(summary = "删除模型信号") + @PostMapping("/delete") + public CommonResult delete(@RequestBody IotModelTags modelTags) { + if (modelTags == null || modelTags.getId() == null) { + return CommonResult.error("id不能为空"); + } + iotModelTagsService.removeById(modelTags.getId()); + return CommonResult.ok(); + } + + /** + * 下载导入模板 + */ + @Operation(summary = "下载导入模板") + @GetMapping("/downloadImportTemplate") + public void downloadImportTemplate(HttpServletResponse response) { + iotModelTagsService.downloadImportTemplate(response); + } + + /** + * 导入模型信号 + */ + @Operation(summary = "导入模型信号") + @PostMapping("/import") + public CommonResult importTags(@RequestParam("file") MultipartFile file, + @RequestParam("modeId") String modeId) { + return CommonResult.data(iotModelTagsService.importTags(file, modeId)); + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/entity/IotConfig.java b/nl-iot/src/main/java/org/nl/module/iot/entity/IotConfig.java new file mode 100644 index 0000000..763c5fc --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/entity/IotConfig.java @@ -0,0 +1,71 @@ +package org.nl.module.iot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 信号配置表实体类 + */ +@Data +@TableName("iot_config") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "信号配置表") +public class IotConfig implements Serializable { + + @TableId(type = IdType.AUTO) + @Schema(description = "主键ID") + private Integer id; + + @Schema(description = "连接ID") + private Integer connectId; + + @Schema(description = "子设备ID") + private Integer subDeviceId; + + @Schema(description = "信号别名") + private String alias; + + @Schema(description = "寄存器地址") + private String registerAddress; + + @Schema(description = "别名含义") + private String aliasName; + + @Schema(description = "数据类型") + private String dataType; + + @Schema(description = "只读(1只读/0可写)") + private Boolean readonly; + + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "备注/描述") + private String description; + + @Schema(description = "启用状态") + private Boolean enabled; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建用户") + private String createName; + + @Schema(description = "修改时间") + private Date updateTime; + + @Schema(description = "修改用户") + private String updateName; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/entity/IotConnect.java b/nl-iot/src/main/java/org/nl/module/iot/entity/IotConnect.java new file mode 100644 index 0000000..4bcd3fc --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/entity/IotConnect.java @@ -0,0 +1,65 @@ +package org.nl.module.iot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 连接表实体类 + */ +@Data +@TableName("iot_connect") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "连接表") +public class IotConnect implements Serializable { + + @TableId(type = IdType.AUTO) + @Schema(description = "主键ID") + private Integer id; + + @Schema(description = "连接编码") + private String code; + + @Schema(description = "主机地址/IP") + private String host; + + @Schema(description = "端口") + private Integer port; + + @Schema(description = "协议") + private String protocol; + + @Schema(description = "采集模式") + private String collectMode; + + @Schema(description = "扩展参数(JSON)") + private String properties; + + @Schema(description = "启用状态") + private Boolean enabled; + + @Schema(description = "备注/描述") + private String description; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建用户") + private String createName; + + @Schema(description = "修改时间") + private Date updateTime; + + @Schema(description = "修改用户") + private String updateName; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/entity/IotDevice.java b/nl-iot/src/main/java/org/nl/module/iot/entity/IotDevice.java new file mode 100644 index 0000000..e58fc62 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/entity/IotDevice.java @@ -0,0 +1,71 @@ +package org.nl.module.iot.entity; + +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 子设备表实体类 + */ +@Data +@TableName("iot_device") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "子设备表") +public class IotDevice implements Serializable { + + @TableId(type = IdType.AUTO) + @Schema(description = "主键ID") + private Integer id; + + @Schema(description = "子设备编码") + private String code; + + @Schema(description = "子设备名称") + private String name; + + @Schema(description = "子设备区域") + private String region; + + @Schema(description = "设备类型") + private String type; + + @Schema(description = "主设备ID") + @TableField(updateStrategy = FieldStrategy.IGNORED) + private Integer mainDeviceId; + + @Schema(description = "模型ID") + private Integer modelId; + + @Schema(description = "是否配置") + private Boolean isConfig; + + @Schema(description = "启用状态") + private Boolean enabled; + + @Schema(description = "备注/描述") + private String description; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建用户") + private String createName; + + @Schema(description = "修改时间") + private Date updateTime; + + @Schema(description = "修改用户") + private String updateName; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/entity/IotModel.java b/nl-iot/src/main/java/org/nl/module/iot/entity/IotModel.java new file mode 100644 index 0000000..a920a65 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/entity/IotModel.java @@ -0,0 +1,50 @@ +package org.nl.module.iot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 模型表实体类 + */ +@Data +@TableName("iot_model") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "模型表") +public class IotModel implements Serializable { + + @TableId(type = IdType.AUTO) + @Schema(description = "主键ID") + private Integer id; + + @Schema(description = "设备类型") + private String deviceType; + + @Schema(description = "模型编码") + private String code; + + @Schema(description = "模型名称") + private String name; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建用户") + private String createName; + + @Schema(description = "修改时间") + private Date updateTime; + + @Schema(description = "修改用户") + private String updateName; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/entity/IotModelTags.java b/nl-iot/src/main/java/org/nl/module/iot/entity/IotModelTags.java new file mode 100644 index 0000000..2013a10 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/entity/IotModelTags.java @@ -0,0 +1,68 @@ +package org.nl.module.iot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 模型信号表实体类 + */ +@Data +@TableName("iot_model_tags") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "模型信号表") +public class IotModelTags implements Serializable { + + @TableId(type = IdType.AUTO) + @Schema(description = "主键ID") + private Integer id; + + @Schema(description = "关联模型ID") + private String modeId; + + @Schema(description = "别名(逻辑点位)") + private String alias; + + @Schema(description = "别名含义") + private String aliasName; + + @Schema(description = "数据类型") + private String dataType; + + @Schema(description = "只读(1只读/0可读写)") + private Boolean readonly; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "是否记录日志") + private Boolean isLog; + + @Schema(description = "备注/描述") + private String description; + + @Schema(description = "创建时间") + private Date createTime; + + @Schema(description = "创建用户") + private String createName; + + @Schema(description = "修改时间") + private Date updateTime; + + @Schema(description = "修改用户") + private String updateName; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/mapper/IotConfigMapper.java b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotConfigMapper.java new file mode 100644 index 0000000..7981959 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotConfigMapper.java @@ -0,0 +1,13 @@ +package org.nl.module.iot.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.module.iot.entity.IotConfig; + +/** + * 信号配置表Mapper + */ +@Mapper +public interface IotConfigMapper extends BaseMapper { + +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/mapper/IotConnectMapper.java b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotConnectMapper.java new file mode 100644 index 0000000..42570d4 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotConnectMapper.java @@ -0,0 +1,13 @@ +package org.nl.module.iot.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.module.iot.entity.IotConnect; + +/** + * 连接表Mapper + */ +@Mapper +public interface IotConnectMapper extends BaseMapper { + +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/mapper/IotDeviceMapper.java b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotDeviceMapper.java new file mode 100644 index 0000000..aca4367 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotDeviceMapper.java @@ -0,0 +1,13 @@ +package org.nl.module.iot.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.module.iot.entity.IotDevice; + +/** + * 子设备表Mapper + */ +@Mapper +public interface IotDeviceMapper extends BaseMapper { + +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelMapper.java b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelMapper.java new file mode 100644 index 0000000..c740a9b --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelMapper.java @@ -0,0 +1,13 @@ +package org.nl.module.iot.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.module.iot.entity.IotModel; + +/** + * 模型表Mapper + */ +@Mapper +public interface IotModelMapper extends BaseMapper { + +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelTagsMapper.java b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelTagsMapper.java new file mode 100644 index 0000000..430b3d5 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/mapper/IotModelTagsMapper.java @@ -0,0 +1,13 @@ +package org.nl.module.iot.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.nl.module.iot.entity.IotModelTags; + +/** + * 模型信号表Mapper + */ +@Mapper +public interface IotModelTagsMapper extends BaseMapper { + +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/param/IotConfigImportParam.java b/nl-iot/src/main/java/org/nl/module/iot/param/IotConfigImportParam.java new file mode 100644 index 0000000..3cdfac7 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/param/IotConfigImportParam.java @@ -0,0 +1,43 @@ +package org.nl.module.iot.param; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * 信号配置导入参数 + */ +@Getter +@Setter +public class IotConfigImportParam { + + @ExcelProperty("连接ID") + private String connectId; + + @ExcelProperty("子设备ID") + private String subDeviceId; + + @ExcelProperty("别名") + private String alias; + + @ExcelProperty("别名含义") + private String aliasName; + + @ExcelProperty("寄存器地址") + private String registerAddress; + + @ExcelProperty("数据类型") + private String dataType; + + @ExcelProperty("只读") + private String readonly; + + @ExcelProperty("排序") + private String sort; + + @ExcelProperty("启用状态") + private String enabled; + + @ExcelProperty("备注") + private String description; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/param/IotConnectImportParam.java b/nl-iot/src/main/java/org/nl/module/iot/param/IotConnectImportParam.java new file mode 100644 index 0000000..a36900c --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/param/IotConnectImportParam.java @@ -0,0 +1,37 @@ +package org.nl.module.iot.param; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * 连接信息导入参数 + */ +@Getter +@Setter +public class IotConnectImportParam { + + @ExcelProperty("连接编码") + private String code; + + @ExcelProperty("主机地址") + private String host; + + @ExcelProperty("端口") + private String port; + + @ExcelProperty("协议") + private String protocol; + + @ExcelProperty("采集模式") + private String collectMode; + + @ExcelProperty("扩展参数(JSON)") + private String properties; + + @ExcelProperty("启用状态") + private String enabled; + + @ExcelProperty("备注") + private String description; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/param/IotDeviceImportParam.java b/nl-iot/src/main/java/org/nl/module/iot/param/IotDeviceImportParam.java new file mode 100644 index 0000000..827ad2c --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/param/IotDeviceImportParam.java @@ -0,0 +1,37 @@ +package org.nl.module.iot.param; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * 子设备导入参数 + */ +@Getter +@Setter +public class IotDeviceImportParam { + + @ExcelProperty("子设备编码") + private String code; + + @ExcelProperty("子设备名称") + private String name; + + @ExcelProperty("子设备区域") + private String region; + + @ExcelProperty("设备类型") + private String type; + + @ExcelProperty("主设备ID") + private String mainDeviceId; + + @ExcelProperty("模型ID") + private String modelId; + + @ExcelProperty("启用状态") + private String enabled; + + @ExcelProperty("备注") + private String description; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/param/IotModelTagsImportParam.java b/nl-iot/src/main/java/org/nl/module/iot/param/IotModelTagsImportParam.java new file mode 100644 index 0000000..f463d7f --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/param/IotModelTagsImportParam.java @@ -0,0 +1,37 @@ +package org.nl.module.iot.param; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Getter; +import lombok.Setter; + +/** + * 模型信号导入参数 + */ +@Getter +@Setter +public class IotModelTagsImportParam { + + @ExcelProperty("别名") + private String alias; + + @ExcelProperty("别名含义") + private String aliasName; + + @ExcelProperty("数据类型") + private String dataType; + + @ExcelProperty("只读") + private String readonly; + + @ExcelProperty("单位") + private String unit; + + @ExcelProperty("排序") + private String sort; + + @ExcelProperty("记录日志") + private String isLog; + + @ExcelProperty("备注") + private String description; +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/IotConfigService.java b/nl-iot/src/main/java/org/nl/module/iot/service/IotConfigService.java new file mode 100644 index 0000000..814491b --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/IotConfigService.java @@ -0,0 +1,23 @@ +package org.nl.module.iot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.hutool.json.JSONObject; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.module.iot.entity.IotConfig; +import org.springframework.web.multipart.MultipartFile; + +/** + * 信号配置表Service接口 + */ +public interface IotConfigService extends IService { + + /** + * 下载导入模板 + */ + void downloadImportTemplate(HttpServletResponse response); + + /** + * 导入信号配置 + */ + JSONObject importConfig(MultipartFile file); +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/IotConnectService.java b/nl-iot/src/main/java/org/nl/module/iot/service/IotConnectService.java new file mode 100644 index 0000000..4ff46ca --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/IotConnectService.java @@ -0,0 +1,23 @@ +package org.nl.module.iot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.hutool.json.JSONObject; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.module.iot.entity.IotConnect; +import org.springframework.web.multipart.MultipartFile; + +/** + * 连接表Service接口 + */ +public interface IotConnectService extends IService { + + /** + * 下载导入模板 + */ + void downloadImportTemplate(HttpServletResponse response); + + /** + * 导入连接信息 + */ + JSONObject importConnect(MultipartFile file); +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/IotDeviceService.java b/nl-iot/src/main/java/org/nl/module/iot/service/IotDeviceService.java new file mode 100644 index 0000000..b01e60e --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/IotDeviceService.java @@ -0,0 +1,23 @@ +package org.nl.module.iot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.hutool.json.JSONObject; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.module.iot.entity.IotDevice; +import org.springframework.web.multipart.MultipartFile; + +/** + * 子设备表Service接口 + */ +public interface IotDeviceService extends IService { + + /** + * 下载导入模板 + */ + void downloadImportTemplate(HttpServletResponse response); + + /** + * 导入子设备 + */ + JSONObject importDevice(MultipartFile file); +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/IotModelService.java b/nl-iot/src/main/java/org/nl/module/iot/service/IotModelService.java new file mode 100644 index 0000000..43678f3 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/IotModelService.java @@ -0,0 +1,15 @@ +package org.nl.module.iot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.nl.module.iot.entity.IotModel; + +/** + * 模型表Service接口 + */ +public interface IotModelService extends IService { + + /** + * 根据编码查询模型 + */ + IotModel getByCode(String code); +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/IotModelTagsService.java b/nl-iot/src/main/java/org/nl/module/iot/service/IotModelTagsService.java new file mode 100644 index 0000000..ce1d87c --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/IotModelTagsService.java @@ -0,0 +1,23 @@ +package org.nl.module.iot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.hutool.json.JSONObject; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.module.iot.entity.IotModelTags; +import org.springframework.web.multipart.MultipartFile; + +/** + * 模型信号表Service接口 + */ +public interface IotModelTagsService extends IService { + + /** + * 下载导入模板 + */ + void downloadImportTemplate(HttpServletResponse response); + + /** + * 导入模型信号 + */ + JSONObject importTags(MultipartFile file, String modeId); +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConfigServiceImpl.java b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConfigServiceImpl.java new file mode 100644 index 0000000..f6a26ed --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConfigServiceImpl.java @@ -0,0 +1,135 @@ +package org.nl.module.iot.service.impl; + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.common.exception.CommonException; +import org.nl.common.util.CommonDownloadUtil; +import org.nl.common.util.CommonResponseUtil; +import org.nl.module.iot.entity.IotConfig; +import org.nl.module.iot.mapper.IotConfigMapper; +import org.nl.module.iot.param.IotConfigImportParam; +import org.nl.module.iot.service.IotConfigService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * 信号配置表Service实现类 + */ +@Service +public class IotConfigServiceImpl extends ServiceImpl implements IotConfigService { + + @Override + public void downloadImportTemplate(HttpServletResponse response) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + EasyExcel.write(outputStream, IotConfigImportParam.class) + .sheet("信号配置模板") + .doWrite(Collections.emptyList()); + CommonDownloadUtil.download("信号配置导入模板.xlsx", outputStream.toByteArray(), response); + } catch (Exception e) { + try { + CommonResponseUtil.renderError(response, "下载信号配置导入模板失败"); + } catch (IOException ignored) { + // ignored + } + } + } + + @Override + public JSONObject importConfig(MultipartFile file) { + try { + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List importList = EasyExcel.read(file.getInputStream()) + .head(IotConfigImportParam.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + for (int i = 0; i < importList.size(); i++) { + JSONObject result = this.doImport(importList.get(i), i); + if (result.getBool("success")) { + successCount += 1; + } else { + errorCount += 1; + errorDetail.add(result); + } + } + return JSONUtil.createObj() + .set("totalCount", importList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); + } catch (Exception e) { + throw new CommonException("信号配置导入失败"); + } + } + + private JSONObject doImport(IotConfigImportParam param, int index) { + if (ObjectUtil.hasEmpty(param.getConnectId(), param.getSubDeviceId(), param.getAlias(), + param.getRegisterAddress(), param.getAliasName(), param.getDataType())) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "必填字段存在空值"); + } + IotConfig config = new IotConfig(); + config.setConnectId(parseInteger(param.getConnectId())); + config.setSubDeviceId(parseInteger(param.getSubDeviceId())); + config.setAlias(param.getAlias()); + config.setAliasName(param.getAliasName()); + config.setRegisterAddress(param.getRegisterAddress()); + config.setDataType(param.getDataType()); + config.setReadonly(parseBoolean(param.getReadonly(), true)); + config.setSort(parseInteger(param.getSort(), 1)); + config.setEnabled(parseBoolean(param.getEnabled(), true)); + config.setDescription(param.getDescription()); + this.save(config); + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", true) + .set("msg", "成功"); + } + + private Integer parseInteger(String value) { + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + return null; + } + } + + private Integer parseInteger(String value, Integer defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + private Boolean parseBoolean(String value, boolean defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + String normalized = value.trim(); + if ("1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "是".equals(normalized) || "只读".equals(normalized) || "启用".equals(normalized)) { + return true; + } + if ("0".equals(normalized) || "false".equalsIgnoreCase(normalized) || "否".equals(normalized) || "可写".equals(normalized) || "禁用".equals(normalized)) { + return false; + } + return defaultValue; + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConnectServiceImpl.java b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConnectServiceImpl.java new file mode 100644 index 0000000..bda95ed --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotConnectServiceImpl.java @@ -0,0 +1,128 @@ +package org.nl.module.iot.service.impl; + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.common.exception.CommonException; +import org.nl.common.util.CommonDownloadUtil; +import org.nl.common.util.CommonResponseUtil; +import org.nl.module.iot.entity.IotConnect; +import org.nl.module.iot.mapper.IotConnectMapper; +import org.nl.module.iot.param.IotConnectImportParam; +import org.nl.module.iot.service.IotConnectService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * 连接表Service实现类 + */ +@Service +public class IotConnectServiceImpl extends ServiceImpl implements IotConnectService { + + @Override + public void downloadImportTemplate(HttpServletResponse response) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + EasyExcel.write(outputStream, IotConnectImportParam.class) + .sheet("连接信息模板") + .doWrite(Collections.emptyList()); + CommonDownloadUtil.download("连接信息导入模板.xlsx", outputStream.toByteArray(), response); + } catch (Exception e) { + try { + CommonResponseUtil.renderError(response, "下载连接信息导入模板失败"); + } catch (IOException ignored) { + // ignored + } + } + } + + @Override + public JSONObject importConnect(MultipartFile file) { + try { + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List importList = EasyExcel.read(file.getInputStream()) + .head(IotConnectImportParam.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + for (int i = 0; i < importList.size(); i++) { + JSONObject result = this.doImport(importList.get(i), i); + if (result.getBool("success")) { + successCount += 1; + } else { + errorCount += 1; + errorDetail.add(result); + } + } + return JSONUtil.createObj() + .set("totalCount", importList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); + } catch (Exception e) { + throw new CommonException("连接信息导入失败"); + } + } + + private JSONObject doImport(IotConnectImportParam param, int index) { + if (ObjectUtil.hasEmpty(param.getCode(), param.getHost(), param.getPort(), param.getProtocol())) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "必填字段存在空值"); + } + Integer port = parseInteger(param.getPort()); + if (port == null) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "端口格式错误"); + } + IotConnect connect = new IotConnect(); + connect.setCode(param.getCode()); + connect.setHost(param.getHost()); + connect.setPort(port); + connect.setProtocol(param.getProtocol()); + connect.setCollectMode(ObjectUtil.isEmpty(param.getCollectMode()) ? "polling" : param.getCollectMode().trim()); + connect.setProperties(param.getProperties()); + connect.setEnabled(parseBoolean(param.getEnabled(), true)); + connect.setDescription(param.getDescription()); + this.save(connect); + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", true) + .set("msg", "成功"); + } + + private Integer parseInteger(String value) { + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + return null; + } + } + + private Boolean parseBoolean(String value, boolean defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + String normalized = value.trim(); + if ("1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "是".equals(normalized) || "启用".equals(normalized)) { + return true; + } + if ("0".equals(normalized) || "false".equalsIgnoreCase(normalized) || "否".equals(normalized) || "禁用".equals(normalized)) { + return false; + } + return defaultValue; + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotDeviceServiceImpl.java b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotDeviceServiceImpl.java new file mode 100644 index 0000000..94a7c78 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotDeviceServiceImpl.java @@ -0,0 +1,125 @@ +package org.nl.module.iot.service.impl; + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.common.exception.CommonException; +import org.nl.common.util.CommonDownloadUtil; +import org.nl.common.util.CommonResponseUtil; +import org.nl.module.iot.entity.IotDevice; +import org.nl.module.iot.mapper.IotDeviceMapper; +import org.nl.module.iot.param.IotDeviceImportParam; +import org.nl.module.iot.service.IotDeviceService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * 子设备表Service实现类 + */ +@Service +public class IotDeviceServiceImpl extends ServiceImpl implements IotDeviceService { + + @Override + public void downloadImportTemplate(HttpServletResponse response) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + EasyExcel.write(outputStream, IotDeviceImportParam.class) + .sheet("子设备模板") + .doWrite(Collections.emptyList()); + CommonDownloadUtil.download("子设备导入模板.xlsx", outputStream.toByteArray(), response); + } catch (Exception e) { + try { + CommonResponseUtil.renderError(response, "下载子设备导入模板失败"); + } catch (IOException ignored) { + // ignored + } + } + } + + @Override + public JSONObject importDevice(MultipartFile file) { + try { + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List importList = EasyExcel.read(file.getInputStream()) + .head(IotDeviceImportParam.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + for (int i = 0; i < importList.size(); i++) { + JSONObject result = this.doImport(importList.get(i), i); + if (result.getBool("success")) { + successCount += 1; + } else { + errorCount += 1; + errorDetail.add(result); + } + } + return JSONUtil.createObj() + .set("totalCount", importList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); + } catch (Exception e) { + throw new CommonException("子设备导入失败"); + } + } + + private JSONObject doImport(IotDeviceImportParam param, int index) { + if (ObjectUtil.hasEmpty(param.getCode(), param.getName())) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "必填字段存在空值"); + } + IotDevice device = new IotDevice(); + device.setCode(param.getCode()); + device.setName(param.getName()); + device.setRegion(param.getRegion()); + device.setType(param.getType()); + device.setMainDeviceId(parseInteger(param.getMainDeviceId())); + device.setModelId(parseInteger(param.getModelId())); + device.setEnabled(parseBoolean(param.getEnabled(), true)); + device.setIsConfig(false); + device.setDescription(param.getDescription()); + this.save(device); + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", true) + .set("msg", "成功"); + } + + private Integer parseInteger(String value) { + if (ObjectUtil.isEmpty(value)) { + return null; + } + try { + return Integer.parseInt(value.trim()); + } catch (Exception e) { + return null; + } + } + + private Boolean parseBoolean(String value, boolean defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + String normalized = value.trim(); + if ("1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "是".equals(normalized) || "启用".equals(normalized)) { + return true; + } + if ("0".equals(normalized) || "false".equalsIgnoreCase(normalized) || "否".equals(normalized) || "禁用".equals(normalized)) { + return false; + } + return defaultValue; + } +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelServiceImpl.java b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelServiceImpl.java new file mode 100644 index 0000000..4ae966c --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelServiceImpl.java @@ -0,0 +1,48 @@ +package org.nl.module.iot.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.apache.commons.lang3.StringUtils; +import org.nl.module.iot.entity.IotModel; +import org.nl.module.iot.mapper.IotModelMapper; +import org.nl.module.iot.service.IotModelService; +import org.springframework.stereotype.Service; + +/** + * 模型表Service实现类 + */ +@Service +public class IotModelServiceImpl extends ServiceImpl implements IotModelService { + + @Override + public boolean save(IotModel entity) { + validateCodeUnique(entity.getCode(), null); + return super.save(entity); + } + + @Override + public IotModel getByCode(String code) { + return this.lambdaQuery() + .eq(IotModel::getCode, code) + .one(); + } + + @Override + public boolean updateById(IotModel entity) { + validateCodeUnique(entity.getCode(), entity.getId()); + return super.updateById(entity); + } + + private void validateCodeUnique(String code, Integer id) { + if (StringUtils.isBlank(code)) { + return; + } + boolean exists = this.lambdaQuery() + .eq(IotModel::getCode, code) + .ne(id != null, IotModel::getId, id) + .exists(); + if (exists) { + throw new RuntimeException("模型编码已存在"); + } + } + +} diff --git a/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelTagsServiceImpl.java b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelTagsServiceImpl.java new file mode 100644 index 0000000..2be9c60 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/module/iot/service/impl/IotModelTagsServiceImpl.java @@ -0,0 +1,128 @@ +package org.nl.module.iot.service.impl; + +import com.alibaba.excel.EasyExcel; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.nl.common.exception.CommonException; +import org.nl.common.util.CommonDownloadUtil; +import org.nl.common.util.CommonResponseUtil; +import org.nl.module.iot.entity.IotModelTags; +import org.nl.module.iot.mapper.IotModelTagsMapper; +import org.nl.module.iot.param.IotModelTagsImportParam; +import org.nl.module.iot.service.IotModelTagsService; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * 模型信号表Service实现类 + */ +@Service +public class IotModelTagsServiceImpl extends ServiceImpl implements IotModelTagsService { + + @Override + public void downloadImportTemplate(HttpServletResponse response) { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + EasyExcel.write(outputStream, IotModelTagsImportParam.class) + .sheet("模型信号模板") + .doWrite(Collections.emptyList()); + CommonDownloadUtil.download("模型信号导入模板.xlsx", outputStream.toByteArray(), response); + } catch (Exception e) { + try { + CommonResponseUtil.renderError(response, "下载模型信号导入模板失败"); + } catch (IOException ignored) { + // ignored + } + } + } + + @Override + public JSONObject importTags(MultipartFile file, String modeId) { + if (ObjectUtil.isEmpty(modeId)) { + throw new CommonException("模型ID不能为空"); + } + try { + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List importList = EasyExcel.read(file.getInputStream()) + .head(IotModelTagsImportParam.class) + .sheet() + .headRowNumber(1) + .doReadSync(); + for (int i = 0; i < importList.size(); i++) { + JSONObject result = this.doImport(importList.get(i), modeId, i); + if (result.getBool("success")) { + successCount += 1; + } else { + errorCount += 1; + errorDetail.add(result); + } + } + return JSONUtil.createObj() + .set("totalCount", importList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); + } catch (Exception e) { + throw new CommonException("模型信号导入失败"); + } + } + + private JSONObject doImport(IotModelTagsImportParam param, String modeId, int index) { + if (ObjectUtil.hasEmpty(param.getAlias(), param.getDataType())) { + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", false) + .set("msg", "必填字段存在空值"); + } + IotModelTags tags = new IotModelTags(); + tags.setModeId(modeId); + tags.setAlias(param.getAlias()); + tags.setAliasName(param.getAliasName()); + tags.setDataType(param.getDataType()); + tags.setReadonly(parseBoolean(param.getReadonly(), true)); + tags.setUnit(param.getUnit()); + tags.setSort(parseInteger(param.getSort(), 1)); + tags.setIsLog(parseBoolean(param.getIsLog(), true)); + tags.setDescription(param.getDescription()); + this.save(tags); + return JSONUtil.createObj() + .set("index", index + 1) + .set("success", true) + .set("msg", "成功"); + } + + private Boolean parseBoolean(String value, boolean defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + String normalized = value.trim(); + if ("1".equals(normalized) || "true".equalsIgnoreCase(normalized) || "是".equals(normalized) || "只读".equals(normalized)) { + return true; + } + if ("0".equals(normalized) || "false".equalsIgnoreCase(normalized) || "否".equals(normalized) || "可读写".equals(normalized)) { + return false; + } + return defaultValue; + } + + private Integer parseInteger(String value, Integer defaultValue) { + if (ObjectUtil.isEmpty(value)) { + return defaultValue; + } + try { + return Integer.parseInt(value.trim()); + } catch (NumberFormatException e) { + return defaultValue; + } + } +} diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/mapper/DevFileMapper.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/mapper/DevFileMapper.java index 53163e4..332e6a5 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/mapper/DevFileMapper.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/mapper/DevFileMapper.java @@ -13,6 +13,9 @@ package org.nl.dev.modular.file.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Options; +import org.nl.common.localStorage.service.entity.LocalStorage; import org.nl.dev.modular.file.entity.DevFile; /** diff --git a/nl-vue/src/api/baseData/deviceApi.js b/nl-vue/src/api/baseData/deviceApi.js index 628f7a7..c5dc952 100644 --- a/nl-vue/src/api/baseData/deviceApi.js +++ b/nl-vue/src/api/baseData/deviceApi.js @@ -18,8 +18,22 @@ export default { edit(data) { return request('edit', data) }, + // 下载设备导入模板 + downloadImportTemplate(data) { + return request('downloadImportTemplate', data, 'get', { + responseType: 'blob' + }) + }, + // 设备导入 + importDevice(data) { + return request('import', data) + }, // 删除设备信息 delete(data) { return request('delete', data, 'post') + }, + // 批量删除设备信息 + deleteBatch(data) { + return request('deleteBatch', data, 'post') } } diff --git a/nl-vue/src/api/baseData/pointApi.js b/nl-vue/src/api/baseData/pointApi.js new file mode 100644 index 0000000..c172265 --- /dev/null +++ b/nl-vue/src/api/baseData/pointApi.js @@ -0,0 +1,39 @@ +/** + * 点位管理API + */ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/api/baseData/point/` + url, ...arg) + +export default { + // 获取点位列表 + list(data) { + return request('list', data, 'get') + }, + // 新增点位信息 + add(data) { + return request('add', data) + }, + // 修改点位信息 + edit(data) { + return request('edit', data) + }, + // 下载点位导入模板 + downloadImportTemplate(data) { + return request('downloadImportTemplate', data, 'get', { + responseType: 'blob' + }) + }, + // 点位导入 + importPoint(data) { + return request('import', data) + }, + // 删除点位信息 + delete(data) { + return request('delete', data, 'post') + }, + // 批量删除点位信息 + deleteBatch(data) { + return request('deleteBatch', data, 'post') + } +} diff --git a/nl-vue/src/api/iot/configApi.js b/nl-vue/src/api/iot/configApi.js new file mode 100644 index 0000000..0b443b0 --- /dev/null +++ b/nl-vue/src/api/iot/configApi.js @@ -0,0 +1,37 @@ +/** + * 信号配置管理API + */ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/api/iot/config/` + url, ...arg) + +export default { + list(data) { + return request('list', data, 'get') + }, + add(data) { + return request('add', data) + }, + edit(data) { + return request('edit', data) + }, + saveBatch(data) { + return request('saveBatch', data) + }, + // 下载信号配置导入模板 + downloadImportTemplate(data) { + return request('downloadImportTemplate', data, 'get', { + responseType: 'blob' + }) + }, + // 信号配置导入 + importConfig(data) { + return request('import', data) + }, + delete(data) { + return request('delete', data, 'post') + }, + deleteBatch(data) { + return request('deleteBatch', data, 'post') + } +} diff --git a/nl-vue/src/api/iot/connectApi.js b/nl-vue/src/api/iot/connectApi.js new file mode 100644 index 0000000..4a8ff3f --- /dev/null +++ b/nl-vue/src/api/iot/connectApi.js @@ -0,0 +1,34 @@ +/** + * 连接管理API + */ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/api/iot/connect/` + url, ...arg) + +export default { + list(data) { + return request('list', data, 'get') + }, + add(data) { + return request('add', data) + }, + edit(data) { + return request('edit', data) + }, + // 下载连接导入模板 + downloadImportTemplate(data) { + return request('downloadImportTemplate', data, 'get', { + responseType: 'blob' + }) + }, + // 连接导入 + importConnect(data) { + return request('import', data) + }, + delete(data) { + return request('delete', data, 'post') + }, + deleteBatch(data) { + return request('deleteBatch', data, 'post') + } +} diff --git a/nl-vue/src/api/iot/deviceApi.js b/nl-vue/src/api/iot/deviceApi.js new file mode 100644 index 0000000..c0521af --- /dev/null +++ b/nl-vue/src/api/iot/deviceApi.js @@ -0,0 +1,34 @@ +/** + * 子设备管理API + */ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/api/iot/device/` + url, ...arg) + +export default { + list(data) { + return request('list', data, 'get') + }, + add(data) { + return request('add', data) + }, + edit(data) { + return request('edit', data) + }, + // 下载子设备导入模板 + downloadImportTemplate(data) { + return request('downloadImportTemplate', data, 'get', { + responseType: 'blob' + }) + }, + // 子设备导入 + importDevice(data) { + return request('import', data) + }, + delete(data) { + return request('delete', data, 'post') + }, + deleteBatch(data) { + return request('deleteBatch', data, 'post') + } +} diff --git a/nl-vue/src/api/iot/modelApi.js b/nl-vue/src/api/iot/modelApi.js new file mode 100644 index 0000000..862b59c --- /dev/null +++ b/nl-vue/src/api/iot/modelApi.js @@ -0,0 +1,21 @@ +/** + * 模型管理API + */ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/api/iot/model/` + url, ...arg) + +export default { + list(data) { + return request('list', data, 'get') + }, + add(data) { + return request('add', data) + }, + edit(data) { + return request('edit', data) + }, + delete(data) { + return request('delete', data, 'post') + } +} diff --git a/nl-vue/src/api/iot/modelTagsApi.js b/nl-vue/src/api/iot/modelTagsApi.js new file mode 100644 index 0000000..8790d2d --- /dev/null +++ b/nl-vue/src/api/iot/modelTagsApi.js @@ -0,0 +1,34 @@ +/** + * 模型信号管理API + */ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/api/iot/modelTags/` + url, ...arg) + +export default { + list(data) { + return request('list', data, 'get') + }, + add(data) { + return request('add', data) + }, + addBatch(data) { + return request('addBatch', data) + }, + edit(data) { + return request('edit', data) + }, + delete(data) { + return request('delete', data, 'post') + }, + // 下载模型信号导入模板 + downloadImportTemplate(data) { + return request('downloadImportTemplate', data, 'get', { + responseType: 'blob' + }) + }, + // 模型信号导入 + importTags(data) { + return request('import', data) + } +} diff --git a/nl-vue/src/views/nl_base_data/device/form.vue b/nl-vue/src/views/nl_base_data/device/form.vue index a94c354..58f7bd1 100644 --- a/nl-vue/src/views/nl_base_data/device/form.vue +++ b/nl-vue/src/views/nl_base_data/device/form.vue @@ -7,56 +7,82 @@ @close="onClose" >
- -
-
设备图片预览
-
- - - -
-
- - -
- - - - - - - - - - - - - - - - - - -
- -
上传图片
-
-
-
支持jpg、png格式,建议尺寸200x200像素
-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
上传图片
+
+
+
支持jpg、png格式,建议尺寸200x200像素
+
+
+
+
-