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 34 @RequestParam(PAGE_SIZE) int pageSize,
35 35 @RequestParam(PAGE) int page,
36 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 38 @RequestParam(value = ORDER_FILED, required = false) String orderBy,
39 39 @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType)
40 40 throws ThingsboardException {
... ... @@ -44,7 +44,7 @@ public class YtReportGenerateRecordController extends BaseController {
44 44 queryMap.put(PAGE, page);
45 45 queryMap.put(ORDER_FILED, orderBy);
46 46 queryMap.put("reportConfigName", reportConfigName);
47   - queryMap.put("status", status);
  47 + queryMap.put("executeStatus", executeStatus);
48 48 queryMap.put("tenantId", getCurrentUser().getCurrentTenantId());
49 49 if (orderType != null) {
50 50 queryMap.put(ORDER_TYPE, orderType.name());
... ...
1 1 package org.thingsboard.server.common.data.yunteng.utils;
2 2
3 3 import com.alibaba.excel.EasyExcel;
  4 +import com.alibaba.excel.ExcelWriter;
  5 +import com.alibaba.excel.write.metadata.WriteSheet;
4 6 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
5 7 import org.apache.commons.lang3.StringUtils;
6 8 import org.springframework.beans.BeanUtils;
... ... @@ -17,6 +19,7 @@ import java.util.List;
17 19
18 20 public class ExcelUtil {
19 21 private ExcelUtil() {}
  22 +
20 23 public static void exportExcel(
21 24 HttpServletResponse response,
22 25 String fileName,
... ... @@ -54,14 +57,26 @@ public class ExcelUtil {
54 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 62 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
59 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 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 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 3 import lombok.RequiredArgsConstructor;
7 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 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 6 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
18   -import org.thingsboard.server.common.data.yunteng.core.utils.FileStorageService;
19 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 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 9 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
28 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 11 import java.util.*;
36   -import java.util.stream.Collectors;
37 12
38 13 @Component("reportTask")
39 14 @RequiredArgsConstructor
40 15 @Slf4j
41 16 public class ReportTask {
42   - private static final String CONTENT_TYPE =
43   - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
44 17 private final YtReportFormConfigService ytReportFormConfigService;
45   - private final TimeseriesService tsService;
46   - private final FileStorageService fileStorageService;
  18 +
47 19 private final YtReportGenerateRecordService ytReportGenerateRecordService;
48 20
49 21 public void multipleParams(String s, Boolean b, Long l, Double d, Integer i) {
... ... @@ -68,147 +40,22 @@ public class ReportTask {
68 40 if (dtoList.size() == FastIotConstants.MagicNumber.ONE) {
69 41 try {
70 42 ExecuteAttributesDTO attributesDTO = dtoList.get(0);
71   - getTsKv(formConfigDTO, attributesDTO, reportGenerateRecordId);
  43 + getTsKvForGenerateExcel(formConfigDTO, attributesDTO, reportGenerateRecordId);
72 44
73 45 } catch (Exception e) {
74 46 log.error(e.getMessage());
75 47 }
76 48 } else {
77 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 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 24 import org.thingsboard.server.dao.yunteng.mapper.ReportFormConfigMapper;
25 25 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
26 26 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
  27 +import org.thingsboard.server.dao.yunteng.service.YtReportGenerateRecordService;
27 28 import org.thingsboard.server.dao.yunteng.service.YtSysJobService;
28 29
29 30 import java.util.*;
... ... @@ -37,6 +38,7 @@ public class YtReportFromConfigServiceImpl
37 38 implements YtReportFormConfigService {
38 39 private final OrganizationMapper organizationMapper;
39 40 private final YtSysJobService ytSysJobService;
  41 + private final YtReportGenerateRecordService ytReportGenerateRecordService;
40 42
41 43 @Override
42 44 public YtPageData<ReportFormConfigDTO> page(Map<String, Object> queryMap) {
... ... @@ -161,7 +163,7 @@ public class YtReportFromConfigServiceImpl
161 163 @Override
162 164 public ReportFormConfigDTO findReportFormConfigById(String id) {
163 165 return Optional.ofNullable(baseMapper.selectById(id))
164   - .map(obj -> getReportFormConfigDTOByEntity(obj))
  166 + .map(this::getReportFormConfigDTOByEntity)
165 167 .orElseThrow(
166 168 () -> {
167 169 throw new YtDataValidationException(ErrorMessage.INTERNAL_ERROR.getMessage());
... ... @@ -174,6 +176,8 @@ public class YtReportFromConfigServiceImpl
174 176 if (isNowExecute) {
175 177 if (Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex())) {
176 178 // 立即执行报表生成,并创建一条报表执行记录
  179 + // 生成Excel,并更新报表执行记录
  180 + generateExcelUpdateReportRecord(reportFormConfig.getDTO(ReportFormConfigDTO.class));
177 181 }
178 182 } else {
179 183 createSysJob(reportFormConfig);
... ... @@ -195,7 +199,8 @@ public class YtReportFromConfigServiceImpl
195 199 ytSysJobService.deleteJob(sysJobDTO);
196 200 if (enableStatus) {
197 201 // 立即执行报表生成,并创建一条报表执行记录
198   - // TODO hxp
  202 + // 生成Excel,并更新报表执行记录
  203 + generateExcelUpdateReportRecord(reportFormConfig.getDTO(ReportFormConfigDTO.class));
199 204 }
200 205 } else {
201 206 // 修改cron表达式
... ... @@ -207,7 +212,8 @@ public class YtReportFromConfigServiceImpl
207 212 if (isNowExecute) {
208 213 if (enableStatus) {
209 214 // 立即执行报表生成,并创建一条报表执行记录
210   - // TODO hxp
  215 + // 生成Excel,并更新报表执行记录
  216 + generateExcelUpdateReportRecord(reportFormConfig.getDTO(ReportFormConfigDTO.class));
211 217 }
212 218 } else {
213 219 createSysJob(reportFormConfig);
... ... @@ -272,6 +278,7 @@ public class YtReportFromConfigServiceImpl
272 278 for (JsonNode key : attribute) {
273 279 attributes.add(JacksonUtil.convertValue(key, String.class));
274 280 }
  281 + assert attributesDTO != null;
275 282 attributesDTO.setAttributes(attributes);
276 283 list.add(attributesDTO);
277 284 }
... ... @@ -281,4 +288,19 @@ public class YtReportFromConfigServiceImpl
281 288 dto.setQueryCondition(queryCondition);
282 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 2
3 3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 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 8 import lombok.RequiredArgsConstructor;
  9 +import lombok.extern.slf4j.Slf4j;
6 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 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 20 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
9 21 import org.thingsboard.server.common.data.yunteng.core.exception.YtDataValidationException;
10 22 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
  23 +import org.thingsboard.server.common.data.yunteng.core.utils.FileStorageService;
11 24 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
12 25 import org.thingsboard.server.common.data.yunteng.dto.OrganizationDTO;
13 26 import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO;
14 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 31 import org.thingsboard.server.common.data.yunteng.enums.StatusEnum;
  32 +import org.thingsboard.server.common.data.yunteng.utils.ExcelUtil;
16 33 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
  34 +import org.thingsboard.server.dao.timeseries.TimeseriesService;
17 35 import org.thingsboard.server.dao.yunteng.entities.ReportGenerateRecord;
18 36 import org.thingsboard.server.dao.yunteng.mapper.ReportGenerateRecordMapper;
19 37 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
... ... @@ -21,18 +39,29 @@ import org.thingsboard.server.dao.yunteng.service.YtOrganizationService;
21 39 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
22 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 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 52 @Service
29 53 @RequiredArgsConstructor
  54 +@Slf4j
30 55 public class YtReportGenerateRecordServiceImpl
31 56 extends AbstractBaseService<ReportGenerateRecordMapper, ReportGenerateRecord>
32 57 implements YtReportGenerateRecordService {
33 58
  59 + private static final String CONTENT_TYPE =
  60 + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
34 61 private final YtReportFormConfigService ytReportFormConfigService;
35 62 private final YtOrganizationService ytOrganizationService;
  63 + private final TimeseriesService tsService;
  64 + private final FileStorageService fileStorageService;
36 65
37 66 @Override
38 67 public YtPageData<ReportGenerateRecordDTO> page(Map<String, Object> queryMap) {
... ... @@ -42,8 +71,8 @@ public class YtReportGenerateRecordServiceImpl
42 71 (LocalDateTime) Optional.ofNullable(queryMap.get("startTime")).orElse(null);
43 72 LocalDateTime endTime =
44 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 76 .map(obj -> Integer.valueOf(obj.toString()))
48 77 .orElse(null);
49 78 IPage<ReportGenerateRecord> iPage =
... ... @@ -54,7 +83,7 @@ public class YtReportGenerateRecordServiceImpl
54 83 StringUtils.isNotEmpty(reportConfigName),
55 84 ReportGenerateRecord::getReportConfigName,
56 85 reportConfigName)
57   - .eq(null != status, ReportGenerateRecord::getExecuteStatus, status)
  86 + .eq(null != executeStatus, ReportGenerateRecord::getExecuteStatus, executeStatus)
58 87 .eq(ReportGenerateRecord::getTenantId, queryMap.get("tenantId").toString())
59 88 .between(
60 89 null != startTime && null != endTime,
... ... @@ -126,4 +155,152 @@ public class YtReportGenerateRecordServiceImpl
126 155 dto.setExecuteWay(reportFormConfigDTO.getExecuteWay());
127 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 1 package org.thingsboard.server.dao.yunteng.service;
2 2
3 3 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
  4 +import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO;
4 5 import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO;
  6 +import org.thingsboard.server.common.data.yunteng.dto.request.ExecuteAttributesDTO;
5 7 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
6 8
7 9 import java.util.Map;
... ... @@ -25,4 +27,12 @@ public interface YtReportGenerateRecordService {
25 27 * @return 报表生成记录
26 28 */
27 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 }
... ...