From 03e8aca3ad6201c0d74e00d4c8d7367cdaaa54f9 Mon Sep 17 00:00:00 2001
From: Jay <csj123456>
Date: 星期五, 01 十一月 2024 11:49:51 +0800
Subject: [PATCH] tag新增导入导出功能

---
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/entity/HttpTagEntity.java                          |    2 
 iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java                                        |   15 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagExportExcelVO.java                            |   55 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/impl/ChannelModbusTagServiceImpl.java    |   53 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/ChannelModbusTagService.java             |   13 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/HttpTagService.java                        |    4 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/impl/ChannelOPCDATagServiceImpl.java      |   54 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagExportExcelVO.java                     |   51 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagRespVO.java                            |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/controller/admin/ChannelKioTagController.java       |   52 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/controller/admin/ChannelModbusTagController.java |   60 +++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagRespVO.java                            |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/impl/HttpTagServiceImpl.java               |   52 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/entity/ChannelKioTagEntity.java                     |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/vo/KioTagRespVO.java                                |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/ChannelOPCDATagService.java               |    7 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagImportExcelVO.java                     |   53 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/controller/admin/ChannelOPCUATagController.java   |   65 ++
 iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java                        |   77 ++-
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/entity/ChannelModBusTagEntity.java               |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagImportExcelVO.java                   |   31 +
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/entity/ChannelOPCDATagEntity.java                 |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagExportExcelVO.java                     |   59 +++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/entity/ChannelOPCUATagEntity.java                 |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportExcelVO.java                            |   56 ++
 iailab-module-data/iailab-module-data-api/src/main/java/com/iailab/module/data/enums/ErrorCodeConstants.java                                   |    5 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/controller/admin/HttpTagController.java            |   53 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/impl/ChannelKioTagServiceImpl.java          |   52 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/vo/HttpTagRespVO.java                              |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagExportExcelVO.java                   |   28 +
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/impl/ChannelOPCUATagServiceImpl.java      |   54 ++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/ChannelKioTagService.java                   |    5 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagRespVO.java                          |    2 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportRespVO.java                             |   26 +
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/ChannelOPCUATagService.java               |    4 
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/controller/admin/ChannelOPCDATagController.java   |   64 +++
 iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagImportExcelVO.java                     |   61 +++
 37 files changed, 1,046 insertions(+), 83 deletions(-)

diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java
index 7efdc0a..9e5e3df 100644
--- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java
+++ b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/handler/SelectSheetWriteHandler.java
@@ -7,24 +7,21 @@
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.hutool.poi.excel.ExcelUtil;
-import com.iailab.framework.common.core.KeyValue;
-import com.iailab.framework.dict.core.DictFrameworkUtils;
-import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
-import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
 import com.alibaba.excel.annotation.ExcelProperty;
 import com.alibaba.excel.write.handler.SheetWriteHandler;
 import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
 import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import com.iailab.framework.common.core.KeyValue;
+import com.iailab.framework.dict.core.DictFrameworkUtils;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.hssf.usermodel.HSSFDataValidation;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddressList;
 
 import java.lang.reflect.Field;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
 
@@ -38,7 +35,6 @@
 
     /**
      * 数据起始行从 0 开始
-     *
      * 约定:本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改
      */
     public static final int FIRST_ROW = 1;
@@ -54,22 +50,29 @@
      */
     private final Map<Integer, List<String>> selectMap = new HashMap<>();
 
-    public SelectSheetWriteHandler(Class<?> head) {
+    private static Boolean ifSetSelect;
+
+    public SelectSheetWriteHandler(Class<?> head, Boolean selectFlag) {
+        ifSetSelect = selectFlag;
         // 加载下拉数据获取接口
         Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class);
         if (MapUtil.isEmpty(beansMap)) {
             return;
         }
-
+        List<Field> fields = new ArrayList<>();
+        for (Class<?> c = head; c != null; c = c.getSuperclass()) {
+            Collections.addAll(fields, c.getDeclaredFields());
+        }
         // 解析下拉数据
         int colIndex = 0;
-        for (Field field : head.getDeclaredFields()) {
+        for (Field field : fields) {
             if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
                 ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                 if (excelProperty != null && excelProperty.index() != -1) {
-                    colIndex = excelProperty.index();
+                    getSelectDataList(excelProperty.index(), field);
+                }else{
+                    getSelectDataList(colIndex, field);
                 }
-                getSelectDataList(colIndex, field);
             }
             colIndex++;
         }
@@ -106,27 +109,45 @@
         if (CollUtil.isEmpty(selectMap)) {
             return;
         }
-
         // 1. 获取相应操作对象
         DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手
         Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿
         List<KeyValue<Integer, List<String>>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue()));
         keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
-
-        // 2. 创建数据字典的 sheet 页
-        Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
-        for (KeyValue<Integer, List<String>> keyValue : keyValues) {
-            int rowLength = keyValue.getValue().size();
-            // 2.1 设置字典 sheet 页的值,每一列一个字典项
-            for (int i = 0; i < rowLength; i++) {
-                Row row = dictSheet.getRow(i);
-                if (row == null) {
-                    row = dictSheet.createRow(i);
+        if (ifSetSelect){
+            for (KeyValue<Integer, List<String>> keyValue : keyValues) {
+                /*起始行、终止行、起始列、终止列  起始行为1即表示表头不设置**/
+                CellRangeAddressList addressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW, keyValue.getKey(), keyValue.getKey());
+                /*设置下拉框数据**/
+                DataValidationConstraint constraint = helper.createExplicitListConstraint(keyValue.getValue().toArray(new String[0]));
+                DataValidation dataValidation = helper.createValidation(constraint, addressList);
+                if (dataValidation instanceof HSSFDataValidation) {
+                    dataValidation.setSuppressDropDownArrow(false);
+                } else {
+                    dataValidation.setSuppressDropDownArrow(true);
+                    dataValidation.setShowErrorBox(true);
                 }
-                row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
+                // 2.2 阻止输入非下拉框的值
+                dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);
+                dataValidation.createErrorBox("提示", "此值不存在于下拉选择中!");
+                writeSheetHolder.getSheet().addValidationData(dataValidation);
             }
-            // 2.2 设置单元格下拉选择
-            setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
+        }else{
+            // 2. 创建数据字典的 sheet 页
+            Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
+            for (KeyValue<Integer, List<String>> keyValue : keyValues) {
+                int rowLength = keyValue.getValue().size();
+                // 2.1 设置字典 sheet 页的值,每一列一部字典项
+                for (int i = 0; i < rowLength; i++) {
+                    Row row = dictSheet.getRow(i);
+                    if (row == null) {
+                        row = dictSheet.createRow(i);
+                    }
+                    row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
+                }
+                // 2.2 设置单元格下拉选择
+                setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
+            }
         }
     }
 
diff --git a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java
index 0eb35ac..f16434e 100644
--- a/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java
+++ b/iailab-framework/iailab-common-excel/src/main/java/com/iailab/framework/excel/core/util/ExcelUtils.java
@@ -36,7 +36,7 @@
         EasyExcel.write(response.getOutputStream(), head)
                 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
                 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
-                .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框
+                .registerWriteHandler(new SelectSheetWriteHandler(head,false)) // 基于固定 sheet 实现下拉框
                 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
                 .sheet(sheetName).doWrite(data);
         // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
@@ -50,4 +50,17 @@
                 .doReadAllSync();
     }
 
+    public static <T> void write(HttpServletResponse response, String filename, String sheetName,
+                                 Class<T> head, List<T> data, boolean selectFlag) throws IOException {
+        // 输出 Excel
+        EasyExcel.write(response.getOutputStream(), head)
+                .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
+                .registerWriteHandler(new SelectSheetWriteHandler(head,selectFlag)) // 基于固定 sheet 实现下拉框
+                .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
+                .sheet(sheetName).doWrite(data);
+        // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
+        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
+        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-api/src/main/java/com/iailab/module/data/enums/ErrorCodeConstants.java b/iailab-module-data/iailab-module-data-api/src/main/java/com/iailab/module/data/enums/ErrorCodeConstants.java
index 59b3717..aa886b6 100644
--- a/iailab-module-data/iailab-module-data-api/src/main/java/com/iailab/module/data/enums/ErrorCodeConstants.java
+++ b/iailab-module-data/iailab-module-data-api/src/main/java/com/iailab/module/data/enums/ErrorCodeConstants.java
@@ -7,4 +7,9 @@
     ErrorCode POINT_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_001_001_001, "导入测点数据不能为空!");
     ErrorCode POINT_NOT_EXISTS = new ErrorCode(1_002_001_000, "测点配置不存在");
     ErrorCode POINT_EXISTS = new ErrorCode(1_002_002_000, "测点配置已经存在");
+
+    ErrorCode TAG_IMPORT_LIST_IS_EMPTY = new ErrorCode(2_001_001_001, "导入Tag数据不能为空!");
+
+    ErrorCode TAG_EXISTS = new ErrorCode(2_002_002_001, "Tag已经存在");
+
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/controller/admin/HttpTagController.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/controller/admin/HttpTagController.java
index c014183..ceeac7b 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/controller/admin/HttpTagController.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/controller/admin/HttpTagController.java
@@ -1,21 +1,37 @@
 package com.iailab.module.data.channel.http.controller.admin;
 
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
 import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageParam;
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.common.util.object.ConvertUtils;
+import com.iailab.framework.excel.core.util.ExcelUtils;
 import com.iailab.module.data.channel.http.entity.HttpTagEntity;
 import com.iailab.module.data.channel.http.service.HttpTagService;
 import com.iailab.module.data.channel.http.vo.HttpTagPageReqVO;
 import com.iailab.module.data.channel.http.vo.HttpTagRespVO;
+import com.iailab.module.data.channel.tag.vo.TagExportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
 
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static com.iailab.framework.common.pojo.CommonResult.success;
 
 
@@ -75,4 +91,41 @@
         tagService.delete(id);
         return success(true);
     }
+
+    @GetMapping("/export")
+    @Operation(summary = "导出modbus tag列表")
+    @PreAuthorize("@ss.hasPermission('data:channel-http-tag:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportPointList(@Validated HttpTagPageReqVO reqVO, HttpServletResponse response) throws IOException {
+        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        PageResult<HttpTagEntity> page = tagService.queryPage(reqVO);
+        List<TagExportExcelVO> list = ConvertUtils.sourceToTarget(page.getList(), TagExportExcelVO.class);
+        ExcelUtils.write(response, "tag列表.xls", "数据", TagExportExcelVO.class, list, true);
+    }
+
+    @GetMapping("/get-import-template")
+    @Operation(summary = "获得tag导入模板")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        // 手动创建导出 demo
+        List<TagImportExcelVO> list = Collections.singletonList(
+                TagImportExcelVO.builder().tagName("Tag名称").tagDesc("Tag描述").dataType("String").enabled(1)
+                        .build()
+        );
+        // 输出
+        ExcelUtils.write(response, "tag导入模板.xls", "tag列表", TagImportExcelVO.class, list,true);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "导入tag")
+    @Parameters({
+            @Parameter(name = "file", description = "Excel 文件", required = true),
+            @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
+    })
+    @PreAuthorize("@ss.hasPermission('data:channel-http-tag:import')")
+    public CommonResult<TagImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+                                                     @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport,
+                                                     @RequestParam("device") String device) throws Exception {
+        List<TagImportExcelVO> list = ExcelUtils.read(file, TagImportExcelVO.class);
+        return success(tagService.importHttpTagList(list, updateSupport, device));
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/entity/HttpTagEntity.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/entity/HttpTagEntity.java
index 4ab81e1..4df9344 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/entity/HttpTagEntity.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/entity/HttpTagEntity.java
@@ -49,7 +49,7 @@
     /**
      * 是否启用
      */
-    private Boolean enabled;
+    private Integer enabled;
 
     /**
      * 创建时间
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/HttpTagService.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/HttpTagService.java
index 2cd7521..9adccbb 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/HttpTagService.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/HttpTagService.java
@@ -3,6 +3,8 @@
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.module.data.channel.http.entity.HttpTagEntity;
 import com.iailab.module.data.channel.http.vo.HttpTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 
 import java.util.List;
 import java.util.Map;
@@ -23,4 +25,6 @@
     List<HttpTagEntity> selectList(Map<String, Object> params);
 
     List<HttpTagEntity> getApiId(String code);
+
+    TagImportRespVO importHttpTagList(List<TagImportExcelVO> importTags, boolean isUpdateSupport, String apiId);
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/impl/HttpTagServiceImpl.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/impl/HttpTagServiceImpl.java
index 9825c91..841706d 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/impl/HttpTagServiceImpl.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/service/impl/HttpTagServiceImpl.java
@@ -1,20 +1,28 @@
 package com.iailab.module.data.channel.http.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.common.util.object.ConvertUtils;
 import com.iailab.module.data.channel.http.dao.HttpTagDao;
 import com.iailab.module.data.channel.http.entity.HttpApiEntity;
 import com.iailab.module.data.channel.http.entity.HttpTagEntity;
 import com.iailab.module.data.channel.http.service.HttpApiService;
 import com.iailab.module.data.channel.http.service.HttpTagService;
 import com.iailab.module.data.channel.http.vo.HttpTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import com.iailab.module.data.common.enums.CommonConstant;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.data.enums.ErrorCodeConstants.*;
 
 @Slf4j
 @Service
@@ -73,4 +81,44 @@
                 .orderByDesc("create_time"));
     }
 
+    @Override
+    public TagImportRespVO importHttpTagList(List<TagImportExcelVO> importTags, boolean isUpdateSupport, String apiId) {
+        // 1.1 参数校验
+        if (CollUtil.isEmpty(importTags)) {
+            throw exception(TAG_IMPORT_LIST_IS_EMPTY);
+        }
+        // 2. 遍历,逐个创建 or 更新
+        TagImportRespVO respVO = TagImportRespVO.builder().createTagNames(new ArrayList<>())
+                .updateTagNames(new ArrayList<>()).failureTagNames(new LinkedHashMap<>()).build();
+        importTags.forEach(importTag -> {
+            // 判断如果不存在,再进行插入
+            HttpTagEntity existTag = httpTagDao.selectOne(new QueryWrapper<HttpTagEntity>()
+                    .eq("api_id", apiId)
+                    .eq("tag_name",importTag.getTagName()));
+            if (existTag == null) {
+                HttpTagEntity httpTagEntity = ConvertUtils.sourceToTarget(importTag, HttpTagEntity.class);
+                httpTagEntity.setId(UUID.randomUUID().toString());
+                httpTagEntity.setEnabled(CommonConstant.IS_ENABLE);
+                httpTagEntity.setApiId(apiId);
+                httpTagEntity.setCreateTime(new Date());
+                httpTagDao.insert(httpTagEntity);
+
+                respVO.getCreateTagNames().add(httpTagEntity.getTagName());
+                return;
+            }
+
+            // 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureTagNames().put(importTag.getTagName(), TAG_EXISTS.getMsg());
+                return;
+            }
+
+            HttpTagEntity updateTag = BeanUtils.toBean(importTag, HttpTagEntity.class);
+            updateTag.setId(existTag.getId());
+            baseMapper.updateById(updateTag);
+            respVO.getUpdateTagNames().add(importTag.getTagName());
+        });
+        return respVO;
+    }
+
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/vo/HttpTagRespVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/vo/HttpTagRespVO.java
index c8a3d5a..fbac91a 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/vo/HttpTagRespVO.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/http/vo/HttpTagRespVO.java
@@ -39,7 +39,7 @@
 
     @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("是否启用")
-    private Boolean enabled;
+    private Integer enabled;
 
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("创建时间")
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/controller/admin/ChannelKioTagController.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/controller/admin/ChannelKioTagController.java
index 6122d90..b4ed847 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/controller/admin/ChannelKioTagController.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/controller/admin/ChannelKioTagController.java
@@ -1,21 +1,37 @@
 package com.iailab.module.data.channel.kio.controller.admin;
 
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
 import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageParam;
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.common.util.object.ConvertUtils;
+import com.iailab.framework.excel.core.util.ExcelUtils;
 import com.iailab.module.data.channel.kio.entity.ChannelKioTagEntity;
 import com.iailab.module.data.channel.kio.service.ChannelKioTagService;
 import com.iailab.module.data.channel.kio.vo.KioTagPageReqVO;
 import com.iailab.module.data.channel.kio.vo.KioTagRespVO;
+import com.iailab.module.data.channel.tag.vo.TagExportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.UUID;
 
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static com.iailab.framework.common.pojo.CommonResult.success;
 
 /**
@@ -70,4 +86,40 @@
         return success(true);
     }
 
+    @GetMapping("/export")
+    @Operation(summary = "导出modbus tag列表")
+    @PreAuthorize("@ss.hasPermission('data:channel-kio-tag:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportPointList(@Validated KioTagPageReqVO reqVO, HttpServletResponse response) throws IOException {
+        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        PageResult<ChannelKioTagEntity> page = channelKioTagService.queryPage(reqVO);
+        List<TagExportExcelVO> list = ConvertUtils.sourceToTarget(page.getList(), TagExportExcelVO.class);
+        ExcelUtils.write(response, "tag列表.xls", "数据", TagExportExcelVO.class, list, true);
+    }
+    
+    @GetMapping("/get-import-template")
+    @Operation(summary = "获得tag导入模板")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        // 手动创建导出 demo
+        List<TagImportExcelVO> list = Collections.singletonList(
+                TagImportExcelVO.builder().tagName("Tag名称").tagDesc("Tag描述").dataType("String").enabled(1)
+                        .build()
+        );
+        // 输出
+        ExcelUtils.write(response, "tag导入模板.xls", "tag列表", TagImportExcelVO.class, list,true);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "导入tag")
+    @Parameters({
+            @Parameter(name = "file", description = "Excel 文件", required = true),
+            @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
+    })
+    @PreAuthorize("@ss.hasPermission('data:channel-kio-tag:import')")
+    public CommonResult<TagImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+                                                     @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport,
+                                                     @RequestParam("device") String device) throws Exception {
+        List<TagImportExcelVO> list = ExcelUtils.read(file, TagImportExcelVO.class);
+        return success(channelKioTagService.importKioTagList(list, updateSupport, device));
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/entity/ChannelKioTagEntity.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/entity/ChannelKioTagEntity.java
index 1a89990..3ab3f14 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/entity/ChannelKioTagEntity.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/entity/ChannelKioTagEntity.java
@@ -50,7 +50,7 @@
     /**
      * 是否可以tag
      */
-    private Boolean enabled;
+    private Integer enabled;
 
     /**
      * 关联设备
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/ChannelKioTagService.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/ChannelKioTagService.java
index 179fbbe..fdc91f0 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/ChannelKioTagService.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/ChannelKioTagService.java
@@ -3,6 +3,8 @@
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.module.data.channel.kio.entity.ChannelKioTagEntity;
 import com.iailab.module.data.channel.kio.vo.KioTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 
 import java.util.List;
 
@@ -28,4 +30,7 @@
     ChannelKioTagEntity getByTagName(String tagName);
 
     void deleteByDeviceName(String name);
+
+    TagImportRespVO importKioTagList(List<TagImportExcelVO> importTags, boolean isUpdateSupport, String device);
+
 }
\ No newline at end of file
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/impl/ChannelKioTagServiceImpl.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/impl/ChannelKioTagServiceImpl.java
index de8c47d..bb8034b 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/impl/ChannelKioTagServiceImpl.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/service/impl/ChannelKioTagServiceImpl.java
@@ -1,18 +1,26 @@
 package com.iailab.module.data.channel.kio.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.common.util.object.ConvertUtils;
 import com.iailab.module.data.channel.kio.dao.ChannelKioTagDao;
 import com.iailab.module.data.channel.kio.entity.ChannelKioTagEntity;
 import com.iailab.module.data.channel.kio.service.ChannelKioTagService;
 import com.iailab.module.data.channel.kio.vo.KioTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import com.iailab.module.data.common.enums.CommonConstant;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.data.enums.ErrorCodeConstants.*;
 
 /**
  * @author lirm
@@ -71,4 +79,44 @@
     public void deleteByDeviceName(String name) {
         channelKioTagDao.delete(new QueryWrapper<ChannelKioTagEntity>().eq("device",name));
     }
+
+    @Override
+    public TagImportRespVO importKioTagList(List<TagImportExcelVO> importTags, boolean isUpdateSupport, String device) {
+        // 1.1 参数校验
+        if (CollUtil.isEmpty(importTags)) {
+            throw exception(TAG_IMPORT_LIST_IS_EMPTY);
+        }
+        // 2. 遍历,逐个创建 or 更新
+        TagImportRespVO respVO = TagImportRespVO.builder().createTagNames(new ArrayList<>())
+                .updateTagNames(new ArrayList<>()).failureTagNames(new LinkedHashMap<>()).build();
+        importTags.forEach(importTag -> {
+            // 判断如果不存在,再进行插入
+            ChannelKioTagEntity existTag = channelKioTagDao.selectOne(new QueryWrapper<ChannelKioTagEntity>()
+                    .eq("device", device)
+                    .eq("tag_name",importTag.getTagName()));
+            if (existTag == null) {
+                ChannelKioTagEntity channelKioTagEntity = ConvertUtils.sourceToTarget(importTag, ChannelKioTagEntity.class);
+                channelKioTagEntity.setId(UUID.randomUUID().toString());
+                channelKioTagEntity.setEnabled(CommonConstant.IS_ENABLE);
+                channelKioTagEntity.setDevice(device);
+                channelKioTagEntity.setCreateTime(new Date());
+                channelKioTagDao.insert(channelKioTagEntity);
+
+                respVO.getCreateTagNames().add(channelKioTagEntity.getTagName());
+                return;
+            }
+
+            // 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureTagNames().put(importTag.getTagName(), TAG_EXISTS.getMsg());
+                return;
+            }
+
+            ChannelKioTagEntity updateTag = BeanUtils.toBean(importTag, ChannelKioTagEntity.class);
+            updateTag.setId(existTag.getId());
+            baseMapper.updateById(updateTag);
+            respVO.getUpdateTagNames().add(importTag.getTagName());
+        });
+        return respVO;
+    }
 }
\ No newline at end of file
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/vo/KioTagRespVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/vo/KioTagRespVO.java
index 35fb5eb..a1db769 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/vo/KioTagRespVO.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/kio/vo/KioTagRespVO.java
@@ -39,7 +39,7 @@
 
     @Schema(description = "是否可以tag", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("是否可以tag")
-    private Boolean enabled;
+    private Integer enabled;
 
     @Schema(description = "关联设备", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("关联设备")
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/controller/admin/ChannelModbusTagController.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/controller/admin/ChannelModbusTagController.java
index 38b5a98..16f7080 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/controller/admin/ChannelModbusTagController.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/controller/admin/ChannelModbusTagController.java
@@ -1,20 +1,37 @@
 package com.iailab.module.data.channel.modbus.controller.admin;
 
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
 import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageParam;
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.framework.common.util.object.BeanUtils;
-import com.iailab.module.data.channel.modbus.entity.ChannelModBusTagEntity;
+import com.iailab.framework.common.util.object.ConvertUtils;
+import com.iailab.framework.excel.core.util.ExcelUtils;
 import com.iailab.module.data.channel.modbus.entity.ChannelModBusTagEntity;
 import com.iailab.module.data.channel.modbus.service.ChannelModbusTagService;
+import com.iailab.module.data.channel.modbus.vo.ModBusTagExportExcelVO;
+import com.iailab.module.data.channel.modbus.vo.ModBusTagImportExcelVO;
 import com.iailab.module.data.channel.modbus.vo.ModBusTagPageReqVO;
 import com.iailab.module.data.channel.modbus.vo.ModBusTagRespVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.UUID;
 
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static com.iailab.framework.common.pojo.CommonResult.success;
 
 /**
@@ -31,7 +48,6 @@
     @GetMapping("/page")
     public CommonResult<PageResult<ModBusTagRespVO>> list(@Valid ModBusTagPageReqVO reqVO) {
         PageResult<ChannelModBusTagEntity> page = channelModbusTagService.queryPage(reqVO);
-
         return success(BeanUtils.toBean(page, ModBusTagRespVO.class));
     }
 
@@ -82,4 +98,44 @@
         channelModbusTagService.delete(id);
         return success(true);
     }
+
+    @GetMapping("/export")
+    @Operation(summary = "导出modbus tag列表")
+    @PreAuthorize("@ss.hasPermission('data:channel-modbus-tag:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportPointList(@Validated ModBusTagPageReqVO reqVO, HttpServletResponse response) throws IOException {
+        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        PageResult<ChannelModBusTagEntity> page = channelModbusTagService.queryPage(reqVO);
+
+        List<ModBusTagExportExcelVO> list = ConvertUtils.sourceToTarget(page.getList(), ModBusTagExportExcelVO.class);
+        ExcelUtils.write(response, "tag列表.xls", "数据", ModBusTagExportExcelVO.class, list, true);
+    }
+
+
+    @GetMapping("/get-import-template")
+    @Operation(summary = "获得tag导入模板")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        // 手动创建导出 demo
+        List<ModBusTagImportExcelVO> list = Collections.singletonList(
+                ModBusTagImportExcelVO.builder().tagName("Tag名称").tagDesc("Tag描述").dataType("String").
+                        address("123").format("1").samplingRate(1000).enabled(1)
+                        .build()
+        );
+        // 输出
+        ExcelUtils.write(response, "tag导入模板.xls", "tag列表", ModBusTagImportExcelVO.class, list,true);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "导入tag")
+    @Parameters({
+            @Parameter(name = "file", description = "Excel 文件", required = true),
+            @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
+    })
+    @PreAuthorize("@ss.hasPermission('data:channel-modbus-tag:import')")
+    public CommonResult<TagImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+                                                     @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport,
+                                                     @RequestParam("device") String device) throws Exception {
+        List<ModBusTagImportExcelVO> list = ExcelUtils.read(file, ModBusTagImportExcelVO.class);
+        return success(channelModbusTagService.importModBusTagList(list, updateSupport,device));
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/entity/ChannelModBusTagEntity.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/entity/ChannelModBusTagEntity.java
index b2a1bca..194e1fd 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/entity/ChannelModBusTagEntity.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/entity/ChannelModBusTagEntity.java
@@ -39,7 +39,7 @@
     /**
      * 是否启用
      */
-    private Boolean enabled;
+    private Integer enabled;
 
     /**
      * 大小端
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/ChannelModbusTagService.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/ChannelModbusTagService.java
index 6ec084c..6548e13 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/ChannelModbusTagService.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/ChannelModbusTagService.java
@@ -3,7 +3,9 @@
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.module.data.channel.modbus.dto.ChannelModbusTagDTO;
 import com.iailab.module.data.channel.modbus.entity.ChannelModBusTagEntity;
+import com.iailab.module.data.channel.modbus.vo.ModBusTagImportExcelVO;
 import com.iailab.module.data.channel.modbus.vo.ModBusTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 
 import java.util.List;
 
@@ -18,7 +20,7 @@
      *
      * @param reqVO
      */
-    PageResult queryPage(ModBusTagPageReqVO reqVO);
+    PageResult<ChannelModBusTagEntity> queryPage(ModBusTagPageReqVO reqVO);
     
     /**
      * 查询tag详情
@@ -70,12 +72,5 @@
      */
     void deleteByDeviceName(String name);
 
-//    /**
-//     * 导入Tag
-//     *
-//     * @param device
-//     * @param file
-//     * @throws Exception
-//     */
-//    void importTag(String device, MultipartFile file) throws Exception;
+    TagImportRespVO importModBusTagList(List<ModBusTagImportExcelVO> importTags, boolean isUpdateSupport, String device);
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/impl/ChannelModbusTagServiceImpl.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/impl/ChannelModbusTagServiceImpl.java
index 7e80908..64d3084 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/impl/ChannelModbusTagServiceImpl.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/service/impl/ChannelModbusTagServiceImpl.java
@@ -1,21 +1,28 @@
 package com.iailab.module.data.channel.modbus.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
 import com.iailab.framework.common.util.object.ConvertUtils;
 import com.iailab.module.data.channel.modbus.dao.ChannelModBusTagDao;
 import com.iailab.module.data.channel.modbus.dto.ChannelModbusTagDTO;
 import com.iailab.module.data.channel.modbus.entity.ChannelModBusTagEntity;
 import com.iailab.module.data.channel.modbus.service.ChannelModbusTagService;
+import com.iailab.module.data.channel.modbus.vo.ModBusTagImportExcelVO;
 import com.iailab.module.data.channel.modbus.vo.ModBusTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import com.iailab.module.data.common.enums.CommonConstant;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.data.enums.ErrorCodeConstants.*;
 
 /**
  * @author lirm
@@ -82,4 +89,46 @@
         baseMapper.delete(new QueryWrapper<ChannelModBusTagEntity>().eq("device", name));
     }
 
+    @Override
+    public TagImportRespVO importModBusTagList(List<ModBusTagImportExcelVO> importTags, boolean isUpdateSupport, String device) {
+        // 1.1 参数校验
+        if (CollUtil.isEmpty(importTags)) {
+            throw exception(TAG_IMPORT_LIST_IS_EMPTY);
+        }
+
+        // 2. 遍历,逐个创建 or 更新
+        TagImportRespVO respVO = TagImportRespVO.builder().createTagNames(new ArrayList<>())
+                .updateTagNames(new ArrayList<>()).failureTagNames(new LinkedHashMap<>()).build();
+        importTags.forEach(importTag -> {
+
+            // 判断如果不存在,再进行插入
+            ChannelModBusTagEntity existTag = channelModBusTagDao.selectOne(new QueryWrapper<ChannelModBusTagEntity>()
+                    .eq("device", device)
+                    .eq("tag_name",importTag.getTagName()));
+            if (existTag == null) {
+                ChannelModBusTagEntity channelModBusTagEntity = ConvertUtils.sourceToTarget(importTag, ChannelModBusTagEntity.class);
+                channelModBusTagEntity.setId(UUID.randomUUID().toString());
+                channelModBusTagEntity.setEnabled(CommonConstant.IS_ENABLE);
+                channelModBusTagEntity.setDevice(device);
+                channelModBusTagEntity.setCreateTime(new Date());
+                channelModBusTagDao.insert(channelModBusTagEntity);
+
+                respVO.getCreateTagNames().add(channelModBusTagEntity.getTagName());
+                return;
+            }
+
+            // 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureTagNames().put(importTag.getTagName(), TAG_EXISTS.getMsg());
+                return;
+            }
+
+            ChannelModBusTagEntity updateTag = BeanUtils.toBean(importTag, ChannelModBusTagEntity.class);
+            updateTag.setId(existTag.getId());
+            baseMapper.updateById(updateTag);
+            respVO.getUpdateTagNames().add(importTag.getTagName());
+        });
+        return respVO;
+    }
+
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagExportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagExportExcelVO.java
new file mode 100644
index 0000000..a4f0566
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagExportExcelVO.java
@@ -0,0 +1,28 @@
+package com.iailab.module.data.channel.modbus.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.module.data.channel.tag.vo.TagExportExcelVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author PanZhibao
+ * @Description
+ * @createTime 2024年08月22日
+ */
+@Schema(description = "导出 - ModBusTag信息")
+@Data
+public class ModBusTagExportExcelVO extends TagExportExcelVO {
+
+    @Schema(description = "地址")
+    @ExcelProperty(value = "地址",index = 3)
+    private String address;
+
+    @Schema(description = "大小端")
+    @ExcelProperty(value = "大小端",index = 4)
+    private String format;
+
+    @Schema(description = "采集频率")
+    @ExcelProperty(value = "采集频率",index = 5)
+    private Integer samplingRate;
+}
\ No newline at end of file
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagImportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagImportExcelVO.java
new file mode 100644
index 0000000..09edccf
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagImportExcelVO.java
@@ -0,0 +1,31 @@
+package com.iailab.module.data.channel.modbus.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.module.data.channel.tag.vo.TagImportExcelVO;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * 用户 Excel 导入 VO
+ * @author Jay
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
+@SuperBuilder
+public class ModBusTagImportExcelVO extends TagImportExcelVO {
+
+    @ExcelProperty(value = "地址", index = 3)
+    private String address;
+
+    @ExcelProperty(value = "大小端", index = 4)
+    private String format;
+
+    @ExcelProperty(value = "采集频率", index = 5)
+    private Integer samplingRate;
+
+}
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagRespVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagRespVO.java
index 2486441..ef9c2a0 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagRespVO.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/modbus/vo/ModBusTagRespVO.java
@@ -31,7 +31,7 @@
 
     @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("是否启用")
-    private Boolean enabled;
+    private Integer enabled;
 
     @Schema(description = "大小端", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("大小端")
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/controller/admin/ChannelOPCDATagController.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/controller/admin/ChannelOPCDATagController.java
index 2a6fb45..d3e5a94 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/controller/admin/ChannelOPCDATagController.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/controller/admin/ChannelOPCDATagController.java
@@ -1,22 +1,38 @@
 package com.iailab.module.data.channel.opcda.controller.admin;
 
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
 import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageParam;
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.common.util.object.ConvertUtils;
+import com.iailab.framework.excel.core.util.ExcelUtils;
 import com.iailab.module.data.channel.opcda.entity.ChannelOPCDATagEntity;
 import com.iailab.module.data.channel.opcda.service.ChannelOPCDATagService;
+import com.iailab.module.data.channel.opcda.vo.OpcDaTagExportExcelVO;
+import com.iailab.module.data.channel.opcda.vo.OpcDaTagImportExcelVO;
 import com.iailab.module.data.channel.opcda.vo.OpcDaTagPageReqVO;
 import com.iailab.module.data.channel.opcda.vo.OpcDaTagRespVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 import com.iailab.module.data.common.exception.RRException;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.UUID;
 
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static com.iailab.framework.common.pojo.CommonResult.success;
 
 /**
@@ -31,21 +47,21 @@
     @Autowired
     private ChannelOPCDATagService channelOPCDATagService;
 
-    @PreAuthorize("@ss.hasPermission('data:channel-opcua:query')")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda:query')")
     @GetMapping("page")
     public CommonResult<PageResult<OpcDaTagRespVO>> list(@Valid OpcDaTagPageReqVO reqVO) {
         PageResult<ChannelOPCDATagEntity> page = channelOPCDATagService.queryPage(reqVO);
         return success(BeanUtils.toBean(page, OpcDaTagRespVO.class));
     }
 
-    @PreAuthorize("@ss.hasPermission('data:channel-opcua:query')")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda:query')")
     @GetMapping("/info/{id}")
     public CommonResult<ChannelOPCDATagEntity> info(@PathVariable("id") String id) {
         ChannelOPCDATagEntity info = channelOPCDATagService.info(id);
         return success(info);
     }
 
-    @PreAuthorize("@ss.hasPermission('data:channel-opcua:create')")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda:create')")
     @PostMapping("/create")
     public CommonResult<Boolean> create(@RequestBody ChannelOPCDATagEntity channelOPCDATagEntity) {
         String id = UUID.randomUUID().toString();
@@ -55,7 +71,7 @@
         return success(true);
     }
 
-    @PreAuthorize("@ss.hasPermission('data:channel-opcua:update')")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda:update')")
     @PutMapping("/update")
     public CommonResult<Boolean> update(@RequestBody ChannelOPCDATagEntity channelOPCDATagEntity) {
         channelOPCDATagEntity.setUpdateTime(new Date());
@@ -63,7 +79,7 @@
         return success(true);
     }
 
-    @PreAuthorize("@ss.hasPermission('data:channel-opcua:delete')")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda:delete')")
     @DeleteMapping("/delete")
     public CommonResult<Boolean> delete(@RequestParam("id") String id) {
         channelOPCDATagService.delete(id);
@@ -82,4 +98,42 @@
         }
         return success("上传成功");
     }
+
+    @GetMapping("/export")
+    @Operation(summary = "导出modbus tag列表")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda-tag:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportPointList(@Validated OpcDaTagPageReqVO reqVO, HttpServletResponse response) throws IOException {
+        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        PageResult<ChannelOPCDATagEntity> page = channelOPCDATagService.queryPage(reqVO);
+        List<OpcDaTagExportExcelVO> list = ConvertUtils.sourceToTarget(page.getList(), OpcDaTagExportExcelVO.class);
+        ExcelUtils.write(response, "tag列表.xls", "数据", OpcDaTagExportExcelVO.class, list, true);
+    }
+
+
+    @GetMapping("/get-import-template")
+    @Operation(summary = "获得tag导入模板")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        // 手动创建导出 demo
+        List<OpcDaTagImportExcelVO> list = Collections.singletonList(
+                OpcDaTagImportExcelVO.builder().tagName("Tag名称").dataType("String").enabled(1)
+                        .build()
+        );
+        // 输出
+        ExcelUtils.write(response, "tag导入模板.xls", "tag列表", OpcDaTagImportExcelVO.class, list,true);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "导入tag")
+    @Parameters({
+            @Parameter(name = "file", description = "Excel 文件", required = true),
+            @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
+    })
+    @PreAuthorize("@ss.hasPermission('data:channel-opcda-tag:import')")
+    public CommonResult<TagImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+                                                     @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport,
+                                                     @RequestParam("device") String device) throws Exception {
+        List<OpcDaTagImportExcelVO> list = ExcelUtils.read(file, OpcDaTagImportExcelVO.class);
+        return success(channelOPCDATagService.importOpcDaTagList(list, updateSupport,device));
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/entity/ChannelOPCDATagEntity.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/entity/ChannelOPCDATagEntity.java
index 9a4cf85..fe1c54a 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/entity/ChannelOPCDATagEntity.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/entity/ChannelOPCDATagEntity.java
@@ -42,7 +42,7 @@
     /**
      * 是否可以tag,如果为false,即使定义了但是runtime不会读取该数据
      */
-    private Boolean enabled;
+    private Integer enabled;
 
     /**
      * itemId
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/ChannelOPCDATagService.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/ChannelOPCDATagService.java
index c0594a6..09f9810 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/ChannelOPCDATagService.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/ChannelOPCDATagService.java
@@ -3,12 +3,12 @@
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.module.data.channel.opcda.dto.ChannelOPCDATagDTO;
 import com.iailab.module.data.channel.opcda.entity.ChannelOPCDATagEntity;
+import com.iailab.module.data.channel.opcda.vo.OpcDaTagImportExcelVO;
 import com.iailab.module.data.channel.opcda.vo.OpcDaTagPageReqVO;
-import com.iailab.module.data.common.utils.PageUtils;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author lirm
@@ -36,4 +36,7 @@
     void update(ChannelOPCDATagEntity channelOPCDATagEntity);
 
     void delete(String id);
+
+    TagImportRespVO importOpcDaTagList(List<OpcDaTagImportExcelVO> importTags, boolean isUpdateSupport, String serverId);
+
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/impl/ChannelOPCDATagServiceImpl.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/impl/ChannelOPCDATagServiceImpl.java
index 875faec..30e1aea 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/impl/ChannelOPCDATagServiceImpl.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/service/impl/ChannelOPCDATagServiceImpl.java
@@ -1,16 +1,21 @@
 package com.iailab.module.data.channel.opcda.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
 import com.iailab.framework.common.util.object.ConvertUtils;
 import com.iailab.module.data.channel.opcda.dao.ChannelOPCDATagDao;
 import com.iailab.module.data.channel.opcda.dto.ChannelOPCDATagDTO;
 import com.iailab.module.data.channel.opcda.entity.ChannelOPCDATagEntity;
 import com.iailab.module.data.channel.opcda.service.ChannelOPCDATagService;
+import com.iailab.module.data.channel.opcda.vo.OpcDaTagImportExcelVO;
 import com.iailab.module.data.channel.opcda.vo.OpcDaTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import com.iailab.module.data.common.enums.CommonConstant;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.ss.usermodel.CellType;
 import org.apache.poi.xssf.usermodel.XSSFRow;
@@ -23,9 +28,10 @@
 import javax.annotation.Resource;
 import java.io.File;
 import java.io.FileInputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.data.enums.ErrorCodeConstants.*;
 
 /**
  * @author lirm
@@ -116,7 +122,7 @@
                 tagEntity.setTagName(row.getCell(1).getStringCellValue());
                 tagEntity.setDataType(row.getCell(2).getStringCellValue());
                 tagEntity.setItemId(row.getCell(3).getStringCellValue());
-                tagEntity.setEnabled(true);
+                tagEntity.setEnabled(CommonConstant.IS_ENABLE);
                 tagEntity.setServerId(serverId);
                 dangerList.add(tagEntity);
             }
@@ -138,4 +144,44 @@
         }
     }
 
+    @Override
+    public TagImportRespVO importOpcDaTagList(List<OpcDaTagImportExcelVO> importTags, boolean isUpdateSupport, String serverId) {
+        // 1.1 参数校验
+        if (CollUtil.isEmpty(importTags)) {
+            throw exception(TAG_IMPORT_LIST_IS_EMPTY);
+        }
+        // 2. 遍历,逐个创建 or 更新
+        TagImportRespVO respVO = TagImportRespVO.builder().createTagNames(new ArrayList<>())
+                .updateTagNames(new ArrayList<>()).failureTagNames(new LinkedHashMap<>()).build();
+        importTags.forEach(importTag -> {
+            // 判断如果不存在,再进行插入
+            ChannelOPCDATagEntity existTag = channelOPCDATagDao.selectOne(new QueryWrapper<ChannelOPCDATagEntity>()
+                    .eq("server_id", serverId)
+                    .eq("tag_name",importTag.getTagName()));
+            if (existTag == null) {
+                ChannelOPCDATagEntity channelOPCDATagEntity = ConvertUtils.sourceToTarget(importTag, ChannelOPCDATagEntity.class);
+                channelOPCDATagEntity.setId(UUID.randomUUID().toString());
+                channelOPCDATagEntity.setEnabled(CommonConstant.IS_ENABLE);
+                channelOPCDATagEntity.setServerId(serverId);
+                channelOPCDATagEntity.setCreateTime(new Date());
+                channelOPCDATagDao.insert(channelOPCDATagEntity);
+
+                respVO.getCreateTagNames().add(channelOPCDATagEntity.getTagName());
+                return;
+            }
+
+            // 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureTagNames().put(importTag.getTagName(), TAG_EXISTS.getMsg());
+                return;
+            }
+
+            ChannelOPCDATagEntity updateTag = BeanUtils.toBean(importTag, ChannelOPCDATagEntity.class);
+            updateTag.setId(existTag.getId());
+            baseMapper.updateById(updateTag);
+            respVO.getUpdateTagNames().add(importTag.getTagName());
+        });
+        return respVO;
+    }
+
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagExportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagExportExcelVO.java
new file mode 100644
index 0000000..6d895cf
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagExportExcelVO.java
@@ -0,0 +1,51 @@
+package com.iailab.module.data.channel.opcda.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.framework.excel.core.annotations.DictFormat;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.convert.DictConvert;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author PanZhibao
+ * @Description
+ * @createTime 2024年08月22日
+ */
+@Schema(description = "导出 - Tag信息")
+@Data
+@ExcelIgnoreUnannotated
+@Component
+public class OpcDaTagExportExcelVO implements ExcelColumnSelectFunction {
+
+    @Schema(description = "Tag名称")
+    @ExcelProperty(value = "Tag名称")
+    private String tagName;
+
+    @Schema(description = "数据类型")
+    @ExcelProperty(value = "数据类型", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "tag_data_type")
+    @DictFormat("tag_data_type")
+    private String dataType;
+
+    @Schema(description = "是否启用")
+    @ExcelProperty(value = "是否启用", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "com_is_int")
+    @DictFormat("com_is_int")
+    private Integer enabled;
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public List<String> getOptions() {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagImportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagImportExcelVO.java
new file mode 100644
index 0000000..846ff70
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagImportExcelVO.java
@@ -0,0 +1,53 @@
+package com.iailab.module.data.channel.opcda.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.framework.excel.core.annotations.DictFormat;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.convert.DictConvert;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 用户 Excel 导入 VO
+ * @author Jay
+ */
+@Data
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
+@Component
+public class OpcDaTagImportExcelVO implements ExcelColumnSelectFunction {
+
+    @ExcelProperty(value = "Tag名称")
+    private String tagName;
+
+    @ExcelProperty(value = "数据类型")
+    @ExcelColumnSelect(dictType = "tag_data_type")
+    @DictFormat("tag_data_type")
+    private String dataType;
+
+    @Schema(description = "是否启用")
+    @ExcelProperty(value = "是否启用", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "com_is_int")
+    @DictFormat("com_is_int")
+    private Integer enabled;
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public List<String> getOptions() {
+        return null;
+    }
+}
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagRespVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagRespVO.java
index 89709db..c4a263a 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagRespVO.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcda/vo/OpcDaTagRespVO.java
@@ -35,7 +35,7 @@
 
     @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("是否启用")
-    private Boolean enabled;
+    private Integer enabled;
 
     @Schema(description = "itemID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("itemID")
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/controller/admin/ChannelOPCUATagController.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/controller/admin/ChannelOPCUATagController.java
index 981ef30..365bcc2 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/controller/admin/ChannelOPCUATagController.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/controller/admin/ChannelOPCUATagController.java
@@ -1,20 +1,37 @@
 package com.iailab.module.data.channel.opcua.controller.admin;
 
+import com.iailab.framework.apilog.core.annotation.ApiAccessLog;
 import com.iailab.framework.common.pojo.CommonResult;
+import com.iailab.framework.common.pojo.PageParam;
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.framework.common.util.object.BeanUtils;
+import com.iailab.framework.common.util.object.ConvertUtils;
+import com.iailab.framework.excel.core.util.ExcelUtils;
 import com.iailab.module.data.channel.opcua.entity.ChannelOPCUATagEntity;
 import com.iailab.module.data.channel.opcua.service.ChannelOPCUATagService;
+import com.iailab.module.data.channel.opcua.vo.OpcUaTagExportExcelVO;
+import com.iailab.module.data.channel.opcua.vo.OpcUaTagImportExcelVO;
 import com.iailab.module.data.channel.opcua.vo.OpcUaTagPageReqVO;
 import com.iailab.module.data.channel.opcua.vo.OpcUaTagRespVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
 import java.util.UUID;
 
+import static com.iailab.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static com.iailab.framework.common.pojo.CommonResult.success;
 
 /**
@@ -68,16 +85,40 @@
         return success(true);
     }
 
-//    @PostMapping("/import/{device}")
-//    public R importTag(@PathVariable("device") String device, @RequestParam("file") MultipartFile file) {
-//        try {
-//            if (file.isEmpty()) {
-//                throw new RRException("上传文件不能为空");
-//            }
-//            channelOpcuaTagService.importTag(device, file);
-//        } catch (Exception ex) {
-//            return R.error(ex.getMessage());
-//        }
-//        return R.ok();
-//    }
+    @GetMapping("/export")
+    @Operation(summary = "导出modbus tag列表")
+    @PreAuthorize("@ss.hasPermission('data:channel-opcua-tag:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportPointList(@Validated OpcUaTagPageReqVO reqVO, HttpServletResponse response) throws IOException {
+        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        PageResult<ChannelOPCUATagEntity> page = channelOpcuaTagService.queryPage(reqVO);
+        List<OpcUaTagExportExcelVO> list = ConvertUtils.sourceToTarget(page.getList(), OpcUaTagExportExcelVO.class);
+        ExcelUtils.write(response, "tag列表.xls", "数据", OpcUaTagExportExcelVO.class, list, true);
+    }
+
+    @GetMapping("/get-import-template")
+    @Operation(summary = "获得tag导入模板")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        // 手动创建导出 demo
+        List<OpcUaTagImportExcelVO> list = Collections.singletonList(
+                OpcUaTagImportExcelVO.builder().tagName("Tag名称").dataType("String").address("123").samplingRate(1000).enabled(1)
+                        .build()
+        );
+        // 输出
+        ExcelUtils.write(response, "tag导入模板.xls", "tag列表", OpcUaTagImportExcelVO.class, list,true);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "导入tag")
+    @Parameters({
+            @Parameter(name = "file", description = "Excel 文件", required = true),
+            @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
+    })
+    @PreAuthorize("@ss.hasPermission('data:channel-opcua-tag:import')")
+    public CommonResult<TagImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+                                                     @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport,
+                                                     @RequestParam("device") String device) throws Exception {
+        List<OpcUaTagImportExcelVO> list = ExcelUtils.read(file, OpcUaTagImportExcelVO.class);
+        return success(channelOpcuaTagService.importOpcUaTagList(list, updateSupport,device));
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/entity/ChannelOPCUATagEntity.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/entity/ChannelOPCUATagEntity.java
index 857c0b4..b555614 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/entity/ChannelOPCUATagEntity.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/entity/ChannelOPCUATagEntity.java
@@ -38,7 +38,7 @@
     /**
      * 是否可以tag,如果为false,即使定义了但是runtime不会读取该数据
      */
-    private Boolean enabled;
+    private Integer enabled;
 
     /**
      * 关联设备
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/ChannelOPCUATagService.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/ChannelOPCUATagService.java
index d8e2627..a689227 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/ChannelOPCUATagService.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/ChannelOPCUATagService.java
@@ -3,7 +3,9 @@
 import com.iailab.framework.common.pojo.PageResult;
 import com.iailab.module.data.channel.opcua.dto.ChannelOPCUATagDTO;
 import com.iailab.module.data.channel.opcua.entity.ChannelOPCUATagEntity;
+import com.iailab.module.data.channel.opcua.vo.OpcUaTagImportExcelVO;
 import com.iailab.module.data.channel.opcua.vo.OpcUaTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
@@ -34,4 +36,6 @@
 
     void importTag(String device, MultipartFile file) throws Exception;
 
+    TagImportRespVO importOpcUaTagList(List<OpcUaTagImportExcelVO> importTags, boolean isUpdateSupport, String device);
+
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/impl/ChannelOPCUATagServiceImpl.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/impl/ChannelOPCUATagServiceImpl.java
index 97562b0..074aae8 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/impl/ChannelOPCUATagServiceImpl.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/service/impl/ChannelOPCUATagServiceImpl.java
@@ -1,16 +1,21 @@
 package com.iailab.module.data.channel.opcua.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.iailab.framework.common.pojo.PageResult;
+import com.iailab.framework.common.util.object.BeanUtils;
 import com.iailab.framework.common.util.object.ConvertUtils;
 import com.iailab.module.data.channel.opcua.dao.ChannelOPCUATagDao;
 import com.iailab.module.data.channel.opcua.dto.ChannelOPCUATagDTO;
 import com.iailab.module.data.channel.opcua.entity.ChannelOPCUATagEntity;
 import com.iailab.module.data.channel.opcua.service.ChannelOPCUATagService;
+import com.iailab.module.data.channel.opcua.vo.OpcUaTagImportExcelVO;
 import com.iailab.module.data.channel.opcua.vo.OpcUaTagPageReqVO;
+import com.iailab.module.data.channel.tag.vo.TagImportRespVO;
+import com.iailab.module.data.common.enums.CommonConstant;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.ss.usermodel.CellType;
 import org.apache.poi.xssf.usermodel.XSSFRow;
@@ -23,9 +28,10 @@
 import javax.annotation.Resource;
 import java.io.File;
 import java.io.FileInputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
+
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.module.data.enums.ErrorCodeConstants.*;
 
 /**
  * @author lirm
@@ -122,7 +128,7 @@
                 }else if(row.getCell(3).getStringCellValue().equals("3")){
                     tagEntity.setAddress(String.format("4%04d",Integer.parseInt(row.getCell(4).getStringCellValue())));
                 }
-                tagEntity.setEnabled(true);
+                tagEntity.setEnabled(1);
                 tagEntity.setDevice(device);
                 tagEntity.setSamplingRate(1);
                 dangerList.add(tagEntity);
@@ -144,4 +150,44 @@
             throw ex;
         }
     }
+
+    @Override
+    public TagImportRespVO importOpcUaTagList(List<OpcUaTagImportExcelVO> importTags, boolean isUpdateSupport, String device) {
+        // 1.1 参数校验
+        if (CollUtil.isEmpty(importTags)) {
+            throw exception(TAG_IMPORT_LIST_IS_EMPTY);
+        }
+        // 2. 遍历,逐个创建 or 更新
+        TagImportRespVO respVO = TagImportRespVO.builder().createTagNames(new ArrayList<>())
+                .updateTagNames(new ArrayList<>()).failureTagNames(new LinkedHashMap<>()).build();
+        importTags.forEach(importTag -> {
+            // 判断如果不存在,再进行插入
+            ChannelOPCUATagEntity existTag = channelOPCUATagDao.selectOne(new QueryWrapper<ChannelOPCUATagEntity>()
+                    .eq("device", device)
+                    .eq("tag_name",importTag.getTagName()));
+            if (existTag == null) {
+                ChannelOPCUATagEntity channelOpCuaTagEntity = ConvertUtils.sourceToTarget(importTag, ChannelOPCUATagEntity.class);
+                channelOpCuaTagEntity.setId(UUID.randomUUID().toString());
+                channelOpCuaTagEntity.setEnabled(CommonConstant.IS_ENABLE);
+                channelOpCuaTagEntity.setDevice(device);
+                channelOpCuaTagEntity.setCreateTime(new Date());
+                channelOPCUATagDao.insert(channelOpCuaTagEntity);
+
+                respVO.getCreateTagNames().add(channelOpCuaTagEntity.getTagName());
+                return;
+            }
+
+            // 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureTagNames().put(importTag.getTagName(), TAG_EXISTS.getMsg());
+                return;
+            }
+
+            ChannelOPCUATagEntity updateTag = BeanUtils.toBean(importTag, ChannelOPCUATagEntity.class);
+            updateTag.setId(existTag.getId());
+            baseMapper.updateById(updateTag);
+            respVO.getUpdateTagNames().add(importTag.getTagName());
+        });
+        return respVO;
+    }
 }
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagExportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagExportExcelVO.java
new file mode 100644
index 0000000..f531c10
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagExportExcelVO.java
@@ -0,0 +1,59 @@
+package com.iailab.module.data.channel.opcua.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.framework.excel.core.annotations.DictFormat;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.convert.DictConvert;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author PanZhibao
+ * @Description
+ * @createTime 2024年08月22日
+ */
+@Schema(description = "导出 - Tag信息")
+@Data
+@ExcelIgnoreUnannotated
+@Component
+public class OpcUaTagExportExcelVO implements ExcelColumnSelectFunction {
+
+    @Schema(description = "Tag名称")
+    @ExcelProperty(value = "Tag名称")
+    private String tagName;
+
+    @Schema(description = "数据类型")
+    @ExcelProperty(value = "数据类型", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "tag_data_type")
+    @DictFormat("tag_data_type")
+    private String dataType;
+
+    @Schema(description = "地址")
+    @ExcelProperty(value = "地址")
+    private String address;
+
+    @Schema(description = "采集频率")
+    @ExcelProperty(value = "采集频率")
+    private Integer samplingRate;
+
+    @Schema(description = "是否启用")
+    @ExcelProperty(value = "是否启用", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "com_is_int")
+    @DictFormat("com_is_int")
+    private Integer enabled;
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public List<String> getOptions() {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagImportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagImportExcelVO.java
new file mode 100644
index 0000000..df1a247
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagImportExcelVO.java
@@ -0,0 +1,61 @@
+package com.iailab.module.data.channel.opcua.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.framework.excel.core.annotations.DictFormat;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.convert.DictConvert;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 用户 Excel 导入 VO
+ * @author Jay
+ */
+@Data
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
+@Component
+public class OpcUaTagImportExcelVO implements ExcelColumnSelectFunction {
+
+    @ExcelProperty(value = "Tag名称")
+    private String tagName;
+
+    @ExcelProperty(value = "数据类型")
+    @ExcelColumnSelect(dictType = "tag_data_type")
+    @DictFormat("tag_data_type")
+    private String dataType;
+
+    @Schema(description = "地址")
+    @ExcelProperty(value = "地址")
+    private String address;
+
+    @Schema(description = "采集频率")
+    @ExcelProperty(value = "采集频率")
+    private Integer samplingRate;
+
+    @Schema(description = "是否启用")
+    @ExcelProperty(value = "是否启用", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "com_is_int")
+    @DictFormat("com_is_int")
+    private Integer enabled;
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public List<String> getOptions() {
+        return null;
+    }
+}
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagRespVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagRespVO.java
index 2da2f1b..feeac98 100644
--- a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagRespVO.java
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/opcua/vo/OpcUaTagRespVO.java
@@ -31,7 +31,7 @@
 
     @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("是否启用")
-    private Boolean enabled;
+    private Integer enabled;
 
     @Schema(description = "关联设备", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
     @ExcelProperty("关联设备")
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagExportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagExportExcelVO.java
new file mode 100644
index 0000000..cd6076e
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagExportExcelVO.java
@@ -0,0 +1,55 @@
+package com.iailab.module.data.channel.tag.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.framework.excel.core.annotations.DictFormat;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.convert.DictConvert;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author PanZhibao
+ * @Description
+ * @createTime 2024年08月22日
+ */
+@Schema(description = "导出 - Tag信息")
+@Data
+@ExcelIgnoreUnannotated
+@Component
+public class TagExportExcelVO implements ExcelColumnSelectFunction {
+
+    @Schema(description = "Tag名称")
+    @ExcelProperty(value = "Tag名称", index = 0)
+    private String tagName;
+
+    @Schema(description = "Tag描述")
+    @ExcelProperty(value = "Tag描述", index = 1)
+    private String tagDesc;
+
+    @Schema(description = "数据类型")
+    @ExcelProperty(value = "数据类型", index = 2, converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "tag_data_type")
+    @DictFormat("tag_data_type")
+    private String dataType;
+
+    @Schema(description = "是否启用")
+    @ExcelProperty(value = "是否启用", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "com_is_int")
+    @DictFormat("com_is_int")
+    private Integer enabled;
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public List<String> getOptions() {
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportExcelVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportExcelVO.java
new file mode 100644
index 0000000..cf24ea4
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportExcelVO.java
@@ -0,0 +1,56 @@
+package com.iailab.module.data.channel.tag.vo;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.iailab.framework.excel.core.annotations.DictFormat;
+import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
+import com.iailab.framework.excel.core.convert.DictConvert;
+import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import lombok.experimental.SuperBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 用户 Excel 导入 VO
+ * @author Jay
+ */
+@Data
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
+@Component
+public class TagImportExcelVO  implements ExcelColumnSelectFunction {
+
+    @ExcelProperty(value = "Tag名称", index = 0)
+    private String tagName;
+
+    @ExcelProperty(value = "Tag描述", index = 1)
+    private String tagDesc;
+
+    @ExcelProperty(value = "数据类型", index = 2)
+    @ExcelColumnSelect(dictType = "tag_data_type")
+    @DictFormat("tag_data_type")
+    private String dataType;
+
+    @Schema(description = "是否启用")
+    @ExcelProperty(value = "是否启用", converter = DictConvert.class)
+    @ExcelColumnSelect(dictType = "com_is_int")
+    @DictFormat("com_is_int")
+    private Integer enabled;
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public List<String> getOptions() {
+        return null;
+    }
+}
diff --git a/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportRespVO.java b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportRespVO.java
new file mode 100644
index 0000000..a3c269c
--- /dev/null
+++ b/iailab-module-data/iailab-module-data-biz/src/main/java/com/iailab/module/data/channel/tag/vo/TagImportRespVO.java
@@ -0,0 +1,26 @@
+package com.iailab.module.data.channel.tag.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Jay
+ */
+@Schema(description = "管理后台 - 用户导入 Response VO")
+@Data
+@Builder
+public class TagImportRespVO {
+
+    @Schema(description = "创建成功的Tag名称数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private List<String> createTagNames;
+
+    @Schema(description = "更新成功的Tag名称数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private List<String> updateTagNames;
+
+    @Schema(description = "导入失败的Tag集合,key 为Tag名称,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Map<String, String> failureTagNames;
+}

--
Gitblit v1.9.3