Commit 33b6d592c77c135e8fcbe835c45fd5de50b98243

Authored by 黄 x
1 parent c495fa5b

fix: fix many to many device generate excel bug

@@ -34,7 +34,7 @@ public class YtReportGenerateRecordController extends BaseController { @@ -34,7 +34,7 @@ public class YtReportGenerateRecordController extends BaseController {
34 @RequestParam(PAGE_SIZE) int pageSize, 34 @RequestParam(PAGE_SIZE) int pageSize,
35 @RequestParam(PAGE) int page, 35 @RequestParam(PAGE) int page,
36 @RequestParam(value = "reportConfigName", required = false) String reportConfigName, 36 @RequestParam(value = "reportConfigName", required = false) String reportConfigName,
37 - @RequestParam(value = "status", required = false) Integer status, 37 + @RequestParam(value = "executeStatus", required = false) Integer executeStatus,
38 @RequestParam(value = ORDER_FILED, required = false) String orderBy, 38 @RequestParam(value = ORDER_FILED, required = false) String orderBy,
39 @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType) 39 @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType)
40 throws ThingsboardException { 40 throws ThingsboardException {
@@ -44,7 +44,7 @@ public class YtReportGenerateRecordController extends BaseController { @@ -44,7 +44,7 @@ public class YtReportGenerateRecordController extends BaseController {
44 queryMap.put(PAGE, page); 44 queryMap.put(PAGE, page);
45 queryMap.put(ORDER_FILED, orderBy); 45 queryMap.put(ORDER_FILED, orderBy);
46 queryMap.put("reportConfigName", reportConfigName); 46 queryMap.put("reportConfigName", reportConfigName);
47 - queryMap.put("status", status); 47 + queryMap.put("executeStatus", executeStatus);
48 queryMap.put("tenantId", getCurrentUser().getCurrentTenantId()); 48 queryMap.put("tenantId", getCurrentUser().getCurrentTenantId());
49 if (orderType != null) { 49 if (orderType != null) {
50 queryMap.put(ORDER_TYPE, orderType.name()); 50 queryMap.put(ORDER_TYPE, orderType.name());
1 package org.thingsboard.server.common.data.yunteng.utils; 1 package org.thingsboard.server.common.data.yunteng.utils;
2 2
3 import com.alibaba.excel.EasyExcel; 3 import com.alibaba.excel.EasyExcel;
  4 +import com.alibaba.excel.ExcelWriter;
  5 +import com.alibaba.excel.write.metadata.WriteSheet;
4 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; 6 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
5 import org.apache.commons.lang3.StringUtils; 7 import org.apache.commons.lang3.StringUtils;
6 import org.springframework.beans.BeanUtils; 8 import org.springframework.beans.BeanUtils;
@@ -17,6 +19,7 @@ import java.util.List; @@ -17,6 +19,7 @@ import java.util.List;
17 19
18 public class ExcelUtil { 20 public class ExcelUtil {
19 private ExcelUtil() {} 21 private ExcelUtil() {}
  22 +
20 public static void exportExcel( 23 public static void exportExcel(
21 HttpServletResponse response, 24 HttpServletResponse response,
22 String fileName, 25 String fileName,
@@ -54,14 +57,26 @@ public class ExcelUtil { @@ -54,14 +57,26 @@ public class ExcelUtil {
54 exportExcel(response, fileName, sheetName, targetList, targetClass); 57 exportExcel(response, fileName, sheetName, targetList, targetClass);
55 } 58 }
56 59
57 - public static ByteArrayOutputStream noModelWrite(String fileName, List<List<String>> heads, List<?> data) { 60 + public static ByteArrayOutputStream noModelWrite(
  61 + String fileName, List<List<String>> heads, List<?> data) {
58 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 62 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
59 EasyExcel.write(byteArrayOutputStream) 63 EasyExcel.write(byteArrayOutputStream)
60 - .head(heads)  
61 - .sheet(fileName)  
62 - // 自适应列宽  
63 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())  
64 - .doWrite(data); 64 + .head(heads)
  65 + .sheet(fileName)
  66 + // 自适应列宽
  67 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
  68 + .doWrite(data);
65 return byteArrayOutputStream; 69 return byteArrayOutputStream;
66 } 70 }
  71 +
  72 + public static void noModelWrite(
  73 + ExcelWriter excelWriter,
  74 + List<List<String>> heads,
  75 + List<?> data,
  76 + int sheetNo,
  77 + String newSheetName) {
  78 + WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, newSheetName).head(heads).build();
  79 + excelWriter.write(data, writeSheet);
  80 + }
  81 +
67 } 82 }
1 package org.thingsboard.server.dao.util.yunteng.task; 1 package org.thingsboard.server.dao.util.yunteng.task;
2 2
3 -import com.google.common.util.concurrent.FutureCallback;  
4 -import com.google.common.util.concurrent.Futures;  
5 -import com.google.common.util.concurrent.MoreExecutors;  
6 import lombok.RequiredArgsConstructor; 3 import lombok.RequiredArgsConstructor;
7 import lombok.extern.slf4j.Slf4j; 4 import lombok.extern.slf4j.Slf4j;
8 -import org.jetbrains.annotations.NotNull;  
9 -import org.springframework.http.HttpStatus;  
10 -import org.springframework.http.ResponseEntity;  
11 import org.springframework.stereotype.Component; 5 import org.springframework.stereotype.Component;
12 -import org.springframework.web.context.request.async.DeferredResult;  
13 -import org.thingsboard.common.util.JacksonUtil;  
14 -import org.thingsboard.server.common.data.id.DeviceId;  
15 -import org.thingsboard.server.common.data.id.TenantId;  
16 -import org.thingsboard.server.common.data.kv.*;  
17 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; 6 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
18 -import org.thingsboard.server.common.data.yunteng.core.utils.FileStorageService;  
19 import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO; 7 import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO;
20 -import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO;  
21 -import org.thingsboard.server.common.data.yunteng.dto.report.BasicData;  
22 import org.thingsboard.server.common.data.yunteng.dto.request.ExecuteAttributesDTO; 8 import org.thingsboard.server.common.data.yunteng.dto.request.ExecuteAttributesDTO;
23 -import org.thingsboard.server.common.data.yunteng.dto.request.QueryConditionDTO;  
24 -import org.thingsboard.server.common.data.yunteng.enums.StatusEnum;  
25 -import org.thingsboard.server.common.data.yunteng.utils.ExcelUtil;  
26 -import org.thingsboard.server.dao.timeseries.TimeseriesService;  
27 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService; 9 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
28 import org.thingsboard.server.dao.yunteng.service.YtReportGenerateRecordService; 10 import org.thingsboard.server.dao.yunteng.service.YtReportGenerateRecordService;
29 -  
30 -import java.io.ByteArrayInputStream;  
31 -import java.io.ByteArrayOutputStream;  
32 -import java.io.InputStream;  
33 -import java.sql.Timestamp;  
34 -import java.time.format.DateTimeFormatter;  
35 import java.util.*; 11 import java.util.*;
36 -import java.util.stream.Collectors;  
37 12
38 @Component("reportTask") 13 @Component("reportTask")
39 @RequiredArgsConstructor 14 @RequiredArgsConstructor
40 @Slf4j 15 @Slf4j
41 public class ReportTask { 16 public class ReportTask {
42 - private static final String CONTENT_TYPE =  
43 - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";  
44 private final YtReportFormConfigService ytReportFormConfigService; 17 private final YtReportFormConfigService ytReportFormConfigService;
45 - private final TimeseriesService tsService;  
46 - private final FileStorageService fileStorageService; 18 +
47 private final YtReportGenerateRecordService ytReportGenerateRecordService; 19 private final YtReportGenerateRecordService ytReportGenerateRecordService;
48 20
49 public void multipleParams(String s, Boolean b, Long l, Double d, Integer i) { 21 public void multipleParams(String s, Boolean b, Long l, Double d, Integer i) {
@@ -68,147 +40,22 @@ public class ReportTask { @@ -68,147 +40,22 @@ public class ReportTask {
68 if (dtoList.size() == FastIotConstants.MagicNumber.ONE) { 40 if (dtoList.size() == FastIotConstants.MagicNumber.ONE) {
69 try { 41 try {
70 ExecuteAttributesDTO attributesDTO = dtoList.get(0); 42 ExecuteAttributesDTO attributesDTO = dtoList.get(0);
71 - getTsKv(formConfigDTO, attributesDTO, reportGenerateRecordId); 43 + getTsKvForGenerateExcel(formConfigDTO, attributesDTO, reportGenerateRecordId);
72 44
73 } catch (Exception e) { 45 } catch (Exception e) {
74 log.error(e.getMessage()); 46 log.error(e.getMessage());
75 } 47 }
76 } else { 48 } else {
77 for (ExecuteAttributesDTO dto : dtoList) { 49 for (ExecuteAttributesDTO dto : dtoList) {
78 - getTsKv(formConfigDTO, dto, reportGenerateRecordId); 50 + getTsKvForGenerateExcel(formConfigDTO, dto, reportGenerateRecordId);
79 } 51 }
80 } 52 }
81 } 53 }
82 } 54 }
83 55
84 - private void getTsKv( 56 + private void getTsKvForGenerateExcel(
85 ReportFormConfigDTO formConfigDTO, ExecuteAttributesDTO dto, String reportGenerateRecordId) { 57 ReportFormConfigDTO formConfigDTO, ExecuteAttributesDTO dto, String reportGenerateRecordId) {
86 - Long startTs = formConfigDTO.getStartTs();  
87 - Long endTs = formConfigDTO.getEndTs();  
88 - QueryConditionDTO queryCondition = formConfigDTO.getQueryCondition();  
89 - boolean useStrictDataTypes = queryCondition.isUseStrictDataTypes();  
90 - Long interval = queryCondition.getInterval();  
91 - int limit = queryCondition.getLimit();  
92 - Aggregation agg = queryCondition.getAgg();  
93 - String orderBy = queryCondition.getOrderBy();  
94 - List<String> keys = dto.getAttributes();  
95 - DeviceId entityId = DeviceId.fromString(dto.getDevice());  
96 - String reportTenantId = formConfigDTO.getTenantId();  
97 - TenantId tenantId = TenantId.fromUUID(UUID.fromString(reportTenantId));  
98 - final DeferredResult<ResponseEntity> result = new DeferredResult<>();  
99 - List<ReadTsKvQuery> queries =  
100 - keys.stream()  
101 - .map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy))  
102 - .collect(Collectors.toList());  
103 -  
104 - Futures.addCallback(  
105 - tsService.findAll(tenantId, entityId, queries),  
106 - getTsKvListCallback(  
107 - result,  
108 - useStrictDataTypes,  
109 - formConfigDTO.getName(),  
110 - dto.getName(),  
111 - reportGenerateRecordId,  
112 - reportTenantId),  
113 - MoreExecutors.directExecutor());  
114 - }  
115 -  
116 - private FutureCallback<List<TsKvEntry>> getTsKvListCallback(  
117 - final DeferredResult<ResponseEntity> response,  
118 - Boolean useStrictDataTypes,  
119 - String reportFormName,  
120 - String deviceName,  
121 - String reportGenerateRecordId,  
122 - String tenantId) {  
123 - return new FutureCallback<>() {  
124 - @Override  
125 - public void onSuccess(List<TsKvEntry> data) {  
126 - Map<String, List<BasicData>> result = new LinkedHashMap<>();  
127 - for (TsKvEntry entry : data) {  
128 - Object value = useStrictDataTypes ? getKvValue(entry) : entry.getValueAsString();  
129 - result  
130 - .computeIfAbsent(entry.getKey(), k -> new ArrayList<>())  
131 - .add(new BasicData(entry.getTs(), value));  
132 - }  
133 - response.setResult(new ResponseEntity<>(result, HttpStatus.OK));  
134 - generateExcel(deviceName, reportFormName, result, reportGenerateRecordId, tenantId);  
135 - }  
136 -  
137 - @Override  
138 - public void onFailure(@NotNull Throwable e) {  
139 - log.error("Failed to fetch historical data", e);  
140 - }  
141 - };  
142 - }  
143 -  
144 - private Object getKvValue(KvEntry entry) {  
145 - if (entry.getDataType() == DataType.JSON) {  
146 - Optional<String> json = entry.getJsonValue();  
147 - return json.map(JacksonUtil::toJsonNode).orElse(null);  
148 - }  
149 - return entry.getValue();  
150 - }  
151 -  
152 - private void generateExcel(  
153 - String deviceName,  
154 - String reportFormName,  
155 - Map<String, List<BasicData>> result,  
156 - String reportGenerateRecordId,  
157 - String tenantId) {  
158 - List<List<String>> heads = new ArrayList<>();  
159 - List<List<Object>> values = new ArrayList<>();  
160 - int firstKey = 0;  
161 - for (String key : result.keySet()) {  
162 - List<String> headValue = new ArrayList<>();  
163 - headValue.add(deviceName);  
164 - headValue.add(key + "采集值");  
165 - List<String> tsValue = new ArrayList<>();  
166 - tsValue.add(deviceName);  
167 - tsValue.add(key + "采集时间");  
168 - heads.add(headValue);  
169 - heads.add(tsValue);  
170 - List<BasicData> basicData = result.get(key);  
171 - for (int i = 0; i < basicData.size(); i++) {  
172 - BasicData item = basicData.get(i);  
173 - List<Object> listValue;  
174 - if (firstKey == 0) {  
175 - listValue = new ArrayList<>();  
176 - } else {  
177 - listValue = values.get(i);  
178 - }  
179 - Object value = item.getValue();  
180 - listValue.add(value);  
181 - DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");  
182 - Timestamp t = new Timestamp(item.getTs());  
183 - listValue.add(t.toLocalDateTime().format(dtf));  
184 - if (firstKey == 0) {  
185 - values.add(listValue);  
186 - }  
187 - }  
188 - firstKey++;  
189 - }  
190 - ByteArrayOutputStream byteArrayOutputStream =  
191 - ExcelUtil.noModelWrite(reportFormName, heads, values);  
192 - String fileName = reportFormName + System.currentTimeMillis() + ".xlsx";  
193 - int status = StatusEnum.SUCCESS.getIndex();  
194 - InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());  
195 - String response = null;  
196 - try {  
197 - response = fileStorageService.uploadFile(fileName, CONTENT_TYPE, inputStream);  
198 - } catch (Exception e) {  
199 - log.error(e.getMessage());  
200 - }  
201 - ReportGenerateRecordDTO recordDTO =  
202 - ytReportGenerateRecordService.findReportGenerateRecordById(  
203 - reportGenerateRecordId, tenantId);  
204 - if (null != recordDTO) {  
205 - if (response != null) {  
206 - recordDTO.setReportPath(response);  
207 - } else {  
208 - status = StatusEnum.FAIL.getIndex();  
209 - }  
210 - recordDTO.setExecuteStatus(status);  
211 - ytReportGenerateRecordService.saveOrUpdateReportGenerateRecord(recordDTO);  
212 - } 58 + ytReportGenerateRecordService.generateExcelUpdateReportRecord(
  59 + formConfigDTO, dto, reportGenerateRecordId);
213 } 60 }
214 } 61 }
@@ -24,6 +24,7 @@ import org.thingsboard.server.dao.yunteng.mapper.OrganizationMapper; @@ -24,6 +24,7 @@ import org.thingsboard.server.dao.yunteng.mapper.OrganizationMapper;
24 import org.thingsboard.server.dao.yunteng.mapper.ReportFormConfigMapper; 24 import org.thingsboard.server.dao.yunteng.mapper.ReportFormConfigMapper;
25 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; 25 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
26 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService; 26 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
  27 +import org.thingsboard.server.dao.yunteng.service.YtReportGenerateRecordService;
27 import org.thingsboard.server.dao.yunteng.service.YtSysJobService; 28 import org.thingsboard.server.dao.yunteng.service.YtSysJobService;
28 29
29 import java.util.*; 30 import java.util.*;
@@ -37,6 +38,7 @@ public class YtReportFromConfigServiceImpl @@ -37,6 +38,7 @@ public class YtReportFromConfigServiceImpl
37 implements YtReportFormConfigService { 38 implements YtReportFormConfigService {
38 private final OrganizationMapper organizationMapper; 39 private final OrganizationMapper organizationMapper;
39 private final YtSysJobService ytSysJobService; 40 private final YtSysJobService ytSysJobService;
  41 + private final YtReportGenerateRecordService ytReportGenerateRecordService;
40 42
41 @Override 43 @Override
42 public YtPageData<ReportFormConfigDTO> page(Map<String, Object> queryMap) { 44 public YtPageData<ReportFormConfigDTO> page(Map<String, Object> queryMap) {
@@ -161,7 +163,7 @@ public class YtReportFromConfigServiceImpl @@ -161,7 +163,7 @@ public class YtReportFromConfigServiceImpl
161 @Override 163 @Override
162 public ReportFormConfigDTO findReportFormConfigById(String id) { 164 public ReportFormConfigDTO findReportFormConfigById(String id) {
163 return Optional.ofNullable(baseMapper.selectById(id)) 165 return Optional.ofNullable(baseMapper.selectById(id))
164 - .map(obj -> getReportFormConfigDTOByEntity(obj)) 166 + .map(this::getReportFormConfigDTOByEntity)
165 .orElseThrow( 167 .orElseThrow(
166 () -> { 168 () -> {
167 throw new YtDataValidationException(ErrorMessage.INTERNAL_ERROR.getMessage()); 169 throw new YtDataValidationException(ErrorMessage.INTERNAL_ERROR.getMessage());
@@ -174,6 +176,8 @@ public class YtReportFromConfigServiceImpl @@ -174,6 +176,8 @@ public class YtReportFromConfigServiceImpl
174 if (isNowExecute) { 176 if (isNowExecute) {
175 if (Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex())) { 177 if (Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex())) {
176 // 立即执行报表生成,并创建一条报表执行记录 178 // 立即执行报表生成,并创建一条报表执行记录
  179 + // 生成Excel,并更新报表执行记录
  180 + generateExcelUpdateReportRecord(reportFormConfig.getDTO(ReportFormConfigDTO.class));
177 } 181 }
178 } else { 182 } else {
179 createSysJob(reportFormConfig); 183 createSysJob(reportFormConfig);
@@ -195,7 +199,8 @@ public class YtReportFromConfigServiceImpl @@ -195,7 +199,8 @@ public class YtReportFromConfigServiceImpl
195 ytSysJobService.deleteJob(sysJobDTO); 199 ytSysJobService.deleteJob(sysJobDTO);
196 if (enableStatus) { 200 if (enableStatus) {
197 // 立即执行报表生成,并创建一条报表执行记录 201 // 立即执行报表生成,并创建一条报表执行记录
198 - // TODO hxp 202 + // 生成Excel,并更新报表执行记录
  203 + generateExcelUpdateReportRecord(reportFormConfig.getDTO(ReportFormConfigDTO.class));
199 } 204 }
200 } else { 205 } else {
201 // 修改cron表达式 206 // 修改cron表达式
@@ -207,7 +212,8 @@ public class YtReportFromConfigServiceImpl @@ -207,7 +212,8 @@ public class YtReportFromConfigServiceImpl
207 if (isNowExecute) { 212 if (isNowExecute) {
208 if (enableStatus) { 213 if (enableStatus) {
209 // 立即执行报表生成,并创建一条报表执行记录 214 // 立即执行报表生成,并创建一条报表执行记录
210 - // TODO hxp 215 + // 生成Excel,并更新报表执行记录
  216 + generateExcelUpdateReportRecord(reportFormConfig.getDTO(ReportFormConfigDTO.class));
211 } 217 }
212 } else { 218 } else {
213 createSysJob(reportFormConfig); 219 createSysJob(reportFormConfig);
@@ -272,6 +278,7 @@ public class YtReportFromConfigServiceImpl @@ -272,6 +278,7 @@ public class YtReportFromConfigServiceImpl
272 for (JsonNode key : attribute) { 278 for (JsonNode key : attribute) {
273 attributes.add(JacksonUtil.convertValue(key, String.class)); 279 attributes.add(JacksonUtil.convertValue(key, String.class));
274 } 280 }
  281 + assert attributesDTO != null;
275 attributesDTO.setAttributes(attributes); 282 attributesDTO.setAttributes(attributes);
276 list.add(attributesDTO); 283 list.add(attributesDTO);
277 } 284 }
@@ -281,4 +288,19 @@ public class YtReportFromConfigServiceImpl @@ -281,4 +288,19 @@ public class YtReportFromConfigServiceImpl
281 dto.setQueryCondition(queryCondition); 288 dto.setQueryCondition(queryCondition);
282 return dto; 289 return dto;
283 } 290 }
  291 +
  292 + private void generateExcelUpdateReportRecord(ReportFormConfigDTO reportFormConfigDTO) {
  293 + if (reportFormConfigDTO == null) {
  294 + throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
  295 + }
  296 + // 立即执行报表生成,并创建一条报表执行记录
  297 + ReportGenerateRecordDTO recordDTO =
  298 + ytReportGenerateRecordService.generateReportRecord(
  299 + reportFormConfigDTO.getId(), reportFormConfigDTO.getTenantId(), null);
  300 + // 生成Excel,并更新报表执行记录
  301 + for (ExecuteAttributesDTO dto : reportFormConfigDTO.getExecuteAttributes()) {
  302 + ytReportGenerateRecordService.generateExcelUpdateReportRecord(
  303 + reportFormConfigDTO, dto, recordDTO.getId());
  304 + }
  305 + }
284 } 306 }
@@ -2,18 +2,36 @@ package org.thingsboard.server.dao.yunteng.impl; @@ -2,18 +2,36 @@ package org.thingsboard.server.dao.yunteng.impl;
2 2
3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 import com.baomidou.mybatisplus.core.metadata.IPage; 4 import com.baomidou.mybatisplus.core.metadata.IPage;
  5 +import com.google.common.util.concurrent.FutureCallback;
  6 +import com.google.common.util.concurrent.Futures;
  7 +import com.google.common.util.concurrent.MoreExecutors;
5 import lombok.RequiredArgsConstructor; 8 import lombok.RequiredArgsConstructor;
  9 +import lombok.extern.slf4j.Slf4j;
6 import org.apache.commons.lang3.StringUtils; 10 import org.apache.commons.lang3.StringUtils;
  11 +import org.jetbrains.annotations.NotNull;
  12 +import org.springframework.http.HttpStatus;
  13 +import org.springframework.http.ResponseEntity;
7 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
  15 +import org.springframework.web.context.request.async.DeferredResult;
  16 +import org.thingsboard.common.util.JacksonUtil;
  17 +import org.thingsboard.server.common.data.id.DeviceId;
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +import org.thingsboard.server.common.data.kv.*;
8 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; 20 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
9 import org.thingsboard.server.common.data.yunteng.core.exception.YtDataValidationException; 21 import org.thingsboard.server.common.data.yunteng.core.exception.YtDataValidationException;
10 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; 22 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
  23 +import org.thingsboard.server.common.data.yunteng.core.utils.FileStorageService;
11 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; 24 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
12 import org.thingsboard.server.common.data.yunteng.dto.OrganizationDTO; 25 import org.thingsboard.server.common.data.yunteng.dto.OrganizationDTO;
13 import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO; 26 import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO;
14 import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO; 27 import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO;
  28 +import org.thingsboard.server.common.data.yunteng.dto.report.BasicData;
  29 +import org.thingsboard.server.common.data.yunteng.dto.request.ExecuteAttributesDTO;
  30 +import org.thingsboard.server.common.data.yunteng.dto.request.QueryConditionDTO;
15 import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; 31 import org.thingsboard.server.common.data.yunteng.enums.StatusEnum;
  32 +import org.thingsboard.server.common.data.yunteng.utils.ExcelUtil;
16 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData; 33 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
  34 +import org.thingsboard.server.dao.timeseries.TimeseriesService;
17 import org.thingsboard.server.dao.yunteng.entities.ReportGenerateRecord; 35 import org.thingsboard.server.dao.yunteng.entities.ReportGenerateRecord;
18 import org.thingsboard.server.dao.yunteng.mapper.ReportGenerateRecordMapper; 36 import org.thingsboard.server.dao.yunteng.mapper.ReportGenerateRecordMapper;
19 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; 37 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
@@ -21,18 +39,29 @@ import org.thingsboard.server.dao.yunteng.service.YtOrganizationService; @@ -21,18 +39,29 @@ import org.thingsboard.server.dao.yunteng.service.YtOrganizationService;
21 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService; 39 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
22 import org.thingsboard.server.dao.yunteng.service.YtReportGenerateRecordService; 40 import org.thingsboard.server.dao.yunteng.service.YtReportGenerateRecordService;
23 41
  42 +import java.io.ByteArrayInputStream;
  43 +import java.io.ByteArrayOutputStream;
  44 +import java.io.InputStream;
  45 +import java.sql.Timestamp;
24 import java.time.LocalDateTime; 46 import java.time.LocalDateTime;
25 -import java.util.Map;  
26 -import java.util.Optional; 47 +import java.time.ZoneOffset;
  48 +import java.time.format.DateTimeFormatter;
  49 +import java.util.*;
  50 +import java.util.stream.Collectors;
27 51
28 @Service 52 @Service
29 @RequiredArgsConstructor 53 @RequiredArgsConstructor
  54 +@Slf4j
30 public class YtReportGenerateRecordServiceImpl 55 public class YtReportGenerateRecordServiceImpl
31 extends AbstractBaseService<ReportGenerateRecordMapper, ReportGenerateRecord> 56 extends AbstractBaseService<ReportGenerateRecordMapper, ReportGenerateRecord>
32 implements YtReportGenerateRecordService { 57 implements YtReportGenerateRecordService {
33 58
  59 + private static final String CONTENT_TYPE =
  60 + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
34 private final YtReportFormConfigService ytReportFormConfigService; 61 private final YtReportFormConfigService ytReportFormConfigService;
35 private final YtOrganizationService ytOrganizationService; 62 private final YtOrganizationService ytOrganizationService;
  63 + private final TimeseriesService tsService;
  64 + private final FileStorageService fileStorageService;
36 65
37 @Override 66 @Override
38 public YtPageData<ReportGenerateRecordDTO> page(Map<String, Object> queryMap) { 67 public YtPageData<ReportGenerateRecordDTO> page(Map<String, Object> queryMap) {
@@ -42,8 +71,8 @@ public class YtReportGenerateRecordServiceImpl @@ -42,8 +71,8 @@ public class YtReportGenerateRecordServiceImpl
42 (LocalDateTime) Optional.ofNullable(queryMap.get("startTime")).orElse(null); 71 (LocalDateTime) Optional.ofNullable(queryMap.get("startTime")).orElse(null);
43 LocalDateTime endTime = 72 LocalDateTime endTime =
44 (LocalDateTime) Optional.ofNullable(queryMap.get("endTime")).orElse(null); 73 (LocalDateTime) Optional.ofNullable(queryMap.get("endTime")).orElse(null);
45 - Integer status =  
46 - Optional.ofNullable(queryMap.get("status")) 74 + Integer executeStatus =
  75 + Optional.ofNullable(queryMap.get("executeStatus"))
47 .map(obj -> Integer.valueOf(obj.toString())) 76 .map(obj -> Integer.valueOf(obj.toString()))
48 .orElse(null); 77 .orElse(null);
49 IPage<ReportGenerateRecord> iPage = 78 IPage<ReportGenerateRecord> iPage =
@@ -54,7 +83,7 @@ public class YtReportGenerateRecordServiceImpl @@ -54,7 +83,7 @@ public class YtReportGenerateRecordServiceImpl
54 StringUtils.isNotEmpty(reportConfigName), 83 StringUtils.isNotEmpty(reportConfigName),
55 ReportGenerateRecord::getReportConfigName, 84 ReportGenerateRecord::getReportConfigName,
56 reportConfigName) 85 reportConfigName)
57 - .eq(null != status, ReportGenerateRecord::getExecuteStatus, status) 86 + .eq(null != executeStatus, ReportGenerateRecord::getExecuteStatus, executeStatus)
58 .eq(ReportGenerateRecord::getTenantId, queryMap.get("tenantId").toString()) 87 .eq(ReportGenerateRecord::getTenantId, queryMap.get("tenantId").toString())
59 .between( 88 .between(
60 null != startTime && null != endTime, 89 null != startTime && null != endTime,
@@ -126,4 +155,152 @@ public class YtReportGenerateRecordServiceImpl @@ -126,4 +155,152 @@ public class YtReportGenerateRecordServiceImpl
126 dto.setExecuteWay(reportFormConfigDTO.getExecuteWay()); 155 dto.setExecuteWay(reportFormConfigDTO.getExecuteWay());
127 return saveOrUpdateReportGenerateRecord(dto); 156 return saveOrUpdateReportGenerateRecord(dto);
128 } 157 }
  158 +
  159 + @Override
  160 + public void generateExcelUpdateReportRecord(
  161 + ReportFormConfigDTO formConfigDTO, ExecuteAttributesDTO dto, String recordId) {
  162 + Long startTs = formConfigDTO.getStartTs();
  163 + Long endTs = formConfigDTO.getEndTs();
  164 + QueryConditionDTO queryCondition = formConfigDTO.getQueryCondition();
  165 + boolean useStrictDataTypes = queryCondition.isUseStrictDataTypes();
  166 + Long interval = queryCondition.getInterval();
  167 + int limit = queryCondition.getLimit();
  168 + Aggregation agg = queryCondition.getAgg();
  169 + String orderBy = queryCondition.getOrderBy();
  170 + List<String> keys = dto.getAttributes();
  171 + DeviceId entityId = DeviceId.fromString(dto.getDevice());
  172 + String reportTenantId = formConfigDTO.getTenantId();
  173 + TenantId tenantId = TenantId.fromUUID(UUID.fromString(reportTenantId));
  174 + final DeferredResult<ResponseEntity> result = new DeferredResult<>();
  175 + // 如果报表配置是定时执行,获取当前定时任务的执行时间,并计算新的开始时间、结束时间
  176 + if (formConfigDTO.getExecuteWay() == FastIotConstants.MagicNumber.ONE) {
  177 + long differenceTs = endTs - startTs;
  178 + endTs = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
  179 + startTs = endTs - differenceTs;
  180 + }
  181 + Long finalStartTs = startTs;
  182 + Long finalEndTs = endTs;
  183 + List<ReadTsKvQuery> queries =
  184 + keys.stream()
  185 + .map(
  186 + key ->
  187 + new BaseReadTsKvQuery(
  188 + key, finalStartTs, finalEndTs, interval, limit, agg, orderBy))
  189 + .collect(Collectors.toList());
  190 +
  191 + Futures.addCallback(
  192 + tsService.findAll(tenantId, entityId, queries),
  193 + getTsKvListCallback(
  194 + result,
  195 + useStrictDataTypes,
  196 + formConfigDTO.getName(),
  197 + dto.getName(),
  198 + recordId,
  199 + reportTenantId),
  200 + MoreExecutors.directExecutor());
  201 + }
  202 +
  203 + private FutureCallback<List<TsKvEntry>> getTsKvListCallback(
  204 + final DeferredResult<ResponseEntity> response,
  205 + Boolean useStrictDataTypes,
  206 + String reportFormName,
  207 + String deviceName,
  208 + String reportGenerateRecordId,
  209 + String tenantId) {
  210 + return new FutureCallback<>() {
  211 + @Override
  212 + public void onSuccess(List<TsKvEntry> data) {
  213 + Map<String, List<BasicData>> result = new LinkedHashMap<>();
  214 + for (TsKvEntry entry : data) {
  215 + Object value = useStrictDataTypes ? getKvValue(entry) : entry.getValueAsString();
  216 + result
  217 + .computeIfAbsent(entry.getKey(), k -> new ArrayList<>())
  218 + .add(new BasicData(entry.getTs(), value));
  219 + }
  220 + response.setResult(new ResponseEntity<>(result, HttpStatus.OK));
  221 + generateExcel(deviceName, reportFormName, result, reportGenerateRecordId, tenantId);
  222 + }
  223 +
  224 + @Override
  225 + public void onFailure(@NotNull Throwable e) {
  226 + log.error("Failed to fetch historical data", e);
  227 + }
  228 + };
  229 + }
  230 +
  231 + private Object getKvValue(KvEntry entry) {
  232 + if (entry.getDataType() == DataType.JSON) {
  233 + Optional<String> json = entry.getJsonValue();
  234 + return json.map(JacksonUtil::toJsonNode).orElse(null);
  235 + }
  236 + return entry.getValue();
  237 + }
  238 +
  239 + private void generateExcel(
  240 + String deviceName,
  241 + String reportFormName,
  242 + Map<String, List<BasicData>> result,
  243 + String reportGenerateRecordId,
  244 + String tenantId) {
  245 + List<List<String>> heads = new ArrayList<>();
  246 + List<List<Object>> values = new ArrayList<>();
  247 + int firstKey = 0;
  248 + for (String key : result.keySet()) {
  249 + List<String> headValue = new ArrayList<>();
  250 + headValue.add(deviceName);
  251 + headValue.add(key + "采集值");
  252 + List<String> tsValue = new ArrayList<>();
  253 + tsValue.add(deviceName);
  254 + tsValue.add(key + "采集时间");
  255 + heads.add(headValue);
  256 + heads.add(tsValue);
  257 + List<BasicData> basicData = result.get(key);
  258 + for (int i = 0; i < basicData.size(); i++) {
  259 + BasicData item = basicData.get(i);
  260 + List<Object> listValue;
  261 + if (firstKey == 0) {
  262 + listValue = new ArrayList<>();
  263 + } else {
  264 + listValue = values.get(i);
  265 + }
  266 + Object value = item.getValue();
  267 + listValue.add(value);
  268 + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  269 + Timestamp t = new Timestamp(item.getTs());
  270 + listValue.add(t.toLocalDateTime().format(dtf));
  271 + if (firstKey == 0) {
  272 + values.add(listValue);
  273 + }
  274 + }
  275 + firstKey++;
  276 + }
  277 + ByteArrayOutputStream byteArrayOutputStream =
  278 + ExcelUtil.noModelWrite(reportFormName, heads, values);
  279 + String fileName = reportFormName + System.currentTimeMillis() + ".xlsx";
  280 + int status = StatusEnum.SUCCESS.getIndex();
  281 + InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
  282 + String response = null;
  283 + try {
  284 + response = fileStorageService.uploadFile(fileName, CONTENT_TYPE, inputStream);
  285 + } catch (Exception e) {
  286 + log.error(e.getMessage());
  287 + }
  288 + ReportGenerateRecordDTO recordDTO =
  289 + findReportGenerateRecordById(reportGenerateRecordId, tenantId);
  290 + if (null != recordDTO) {
  291 + if (response != null) {
  292 + String reportPath = recordDTO.getReportPath();
  293 + if (null != reportPath) {
  294 + reportPath += "," + response;
  295 + } else {
  296 + reportPath = response;
  297 + }
  298 + recordDTO.setReportPath(reportPath);
  299 + } else {
  300 + status = StatusEnum.FAIL.getIndex();
  301 + }
  302 + recordDTO.setExecuteStatus(status);
  303 + saveOrUpdateReportGenerateRecord(recordDTO);
  304 + }
  305 + }
129 } 306 }
1 package org.thingsboard.server.dao.yunteng.service; 1 package org.thingsboard.server.dao.yunteng.service;
2 2
3 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; 3 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
  4 +import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO;
4 import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO; 5 import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO;
  6 +import org.thingsboard.server.common.data.yunteng.dto.request.ExecuteAttributesDTO;
5 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData; 7 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
6 8
7 import java.util.Map; 9 import java.util.Map;
@@ -25,4 +27,12 @@ public interface YtReportGenerateRecordService { @@ -25,4 +27,12 @@ public interface YtReportGenerateRecordService {
25 * @return 报表生成记录 27 * @return 报表生成记录
26 */ 28 */
27 ReportGenerateRecordDTO generateReportRecord(String sourceId, String tenantId, String jobId); 29 ReportGenerateRecordDTO generateReportRecord(String sourceId, String tenantId, String jobId);
  30 +
  31 + /**
  32 + * 生成报表记录
  33 + * @param formConfigDTO 报表配置
  34 + * @param dto 执行属性
  35 + * @param recordId recordId 记录ID
  36 + */
  37 + void generateExcelUpdateReportRecord(ReportFormConfigDTO formConfigDTO, ExecuteAttributesDTO dto, String recordId);
28 } 38 }