Commit c495fa5bc6814d91b37a22a7ec8e0b90f1086310

Authored by 黄 x
1 parent 0657018b

fix: generate excel and save it. fix schedule bug

Showing 24 changed files with 591 additions and 209 deletions
1 1 package org.thingsboard.server.controller.yunteng;
2 2
3 3 import io.swagger.annotations.Api;
  4 +import io.swagger.annotations.ApiOperation;
4 5 import lombok.RequiredArgsConstructor;
5 6 import org.quartz.SchedulerException;
6 7 import org.springframework.security.access.prepost.PreAuthorize;
... ... @@ -37,6 +38,7 @@ public class YtReportFormConfigController extends BaseController {
37 38 private final YtReportFormConfigService reportFormConfigService;
38 39
39 40 @GetMapping(params = {PAGE_SIZE, PAGE})
  41 + @ApiOperation("分页")
40 42 public YtPageData<ReportFormConfigDTO> page(
41 43 @RequestParam(PAGE_SIZE) int pageSize,
42 44 @RequestParam(PAGE) int page,
... ... @@ -62,9 +64,10 @@ public class YtReportFormConfigController extends BaseController {
62 64 }
63 65
64 66 @PostMapping
  67 + @ApiOperation("新增")
65 68 public ResponseResult<ReportFormConfigDTO> saveReportFromConfig(
66 69 @Validated({AddGroup.class}) @RequestBody ReportFormConfigDTO configDTO)
67   - throws ThingsboardException, SchedulerException {
  70 + throws ThingsboardException, SchedulerException {
68 71 if (StringUtils.isNotEmpty(configDTO.getId())) {
69 72 throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
70 73 }
... ... @@ -72,30 +75,43 @@ public class YtReportFormConfigController extends BaseController {
72 75 }
73 76
74 77 @PutMapping
  78 + @ApiOperation("编辑")
75 79 public ResponseResult<ReportFormConfigDTO> updateReportFromConfig(
76 80 @Validated({UpdateGroup.class}) @RequestBody ReportFormConfigDTO configDTO)
77   - throws ThingsboardException, SchedulerException {
  81 + throws ThingsboardException, SchedulerException {
78 82 return saveOrUpdate(configDTO);
79 83 }
80 84
81 85 @PutMapping("/{id}/{status}")
  86 + @ApiOperation("修改状态")
82 87 public ResponseResult<ReportFormConfigDTO> updateStatusById(
83   - @PathVariable String id, @PathVariable Integer status) throws ThingsboardException, SchedulerException {
  88 + @PathVariable String id, @PathVariable Integer status)
  89 + throws ThingsboardException, SchedulerException {
84 90 return ResponseResult.success(
85 91 reportFormConfigService.updateStatusById(
86 92 getCurrentUser().getCurrentTenantId(), status, id));
87 93 }
88 94
89 95 @DeleteMapping
  96 + @ApiOperation("删除")
90 97 public ResponseResult<Boolean> deleteReportFormConfig(
91   - @Validated({DeleteGroup.class}) @RequestBody DeleteDTO deleteDTO) {
  98 + @Validated({DeleteGroup.class}) @RequestBody DeleteDTO deleteDTO) throws SchedulerException {
92 99 return reportFormConfigService.deleteReportFormConfig(deleteDTO)
93 100 ? ResponseResult.success(FastIotConstants.StateValue.DELETE_SUCCESS)
94 101 : ResponseResult.failed(FastIotConstants.StateValue.DELETE_FAILED);
95 102 }
96 103
  104 + @GetMapping("/{id}")
  105 + @ApiOperation("获取报表配置信息")
  106 + public ResponseResult<ReportFormConfigDTO> getReportFormConfig(@PathVariable String id)
  107 + throws ThingsboardException, SchedulerException {
  108 + return ResponseResult.success(
  109 + reportFormConfigService.findReportFormConfigById(
  110 + id, getCurrentUser().getCurrentTenantId()));
  111 + }
  112 +
97 113 private ResponseResult<ReportFormConfigDTO> saveOrUpdate(ReportFormConfigDTO configDTO)
98   - throws ThingsboardException, SchedulerException {
  114 + throws ThingsboardException, SchedulerException {
99 115 configDTO.setTenantId(getCurrentUser().getCurrentTenantId());
100 116 ReportFormConfigDTO newDTO = reportFormConfigService.saveOrUpdateReportFormConfig(configDTO);
101 117 return ResponseResult.success(newDTO);
... ...
1 1 package org.thingsboard.server.common.data.yunteng.core.utils;
2 2
3   -
4 3 import org.springframework.http.ResponseEntity;
5 4 import org.springframework.web.multipart.MultipartFile;
6 5 import org.thingsboard.server.common.data.yunteng.dto.FileUploadResponse;
7 6
8 7 import javax.servlet.http.HttpServletRequest;
9 8 import javax.servlet.http.HttpServletResponse;
  9 +import java.io.InputStream;
10 10
11 11 public interface FileStorageService {
12 12
... ... @@ -26,4 +26,6 @@ public interface FileStorageService {
26 26 */
27 27 ResponseEntity<?> download(
28 28 String fileName, HttpServletRequest request, HttpServletResponse response);
  29 +
  30 + String uploadFile(String fileName, String contentType, InputStream inputStream) throws Exception;
29 31 }
... ...
... ... @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.yunteng.dto.FileUploadResponse;
22 22 import javax.servlet.http.HttpServletRequest;
23 23 import javax.servlet.http.HttpServletResponse;
24 24 import java.io.IOException;
  25 +import java.io.InputStream;
25 26 import java.net.MalformedURLException;
26 27 import java.nio.file.Files;
27 28 import java.nio.file.Path;
... ... @@ -58,26 +59,7 @@ public class LocalFileStorageService implements FileStorageService {
58 59 String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
59 60
60 61 try {
61   - // random fileName if needed
62   - if (fileStorageProperties.isRandomFileName()) {
63   - if (fileName.contains(".")) {
64   - fileName =
65   - RandomStringUtils.randomAlphabetic(15)
66   - + fileName.substring(fileName.lastIndexOf("."));
67   - } else {
68   - fileName = RandomStringUtils.randomAlphabetic(15);
69   - }
70   - }
71   - // Check if the file's name contains invalid characters
72   - if (fileName.contains("..")) {
73   - throw new FileStorageException(ErrorMessage.STORE_FILE_FAILED);
74   - }
75   -
76   - // Copy file to the target location (Replacing existing file with the same name)
77   - Path targetLocation = this.fileStorageLocation.resolve(fileName);
78   - Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
79   -
80   - return fileName;
  62 + return storeFileByInputStream(fileName, file.getInputStream());
81 63 } catch (IOException ex) {
82 64 throw new FileStorageException(ErrorMessage.STORE_FILE_FAILED);
83 65 }
... ... @@ -136,4 +118,37 @@ public class LocalFileStorageService implements FileStorageService {
136 118 "attachment; filename=\"" + resource.getFilename() + "\"")
137 119 .body(resource);
138 120 }
  121 +
  122 + @Override
  123 + public String uploadFile(String fileName, String contentType, InputStream inputStream)
  124 + throws IOException {
  125 + String name = storeFileByInputStream(fileName, inputStream);
  126 + String ossStaticPath =
  127 + ServletUriComponentsBuilder.fromCurrentContextPath().toUriString() + ossFileUrl;
  128 + ossStaticPath = ossStaticPath.replace("**", name);
  129 + return ossStaticPath;
  130 + }
  131 +
  132 + private String storeFileByInputStream(String fileName, InputStream inputStream)
  133 + throws IOException {
  134 + // random fileName if needed
  135 + if (fileStorageProperties.isRandomFileName()) {
  136 + if (fileName.contains(".")) {
  137 + fileName =
  138 + RandomStringUtils.randomAlphabetic(15) + fileName.substring(fileName.lastIndexOf("."));
  139 + } else {
  140 + fileName = RandomStringUtils.randomAlphabetic(15);
  141 + }
  142 + }
  143 + // Check if the file's name contains invalid characters
  144 + if (fileName.contains("..")) {
  145 + throw new FileStorageException(ErrorMessage.STORE_FILE_FAILED);
  146 + }
  147 +
  148 + // Copy file to the target location (Replacing existing file with the same name)
  149 + Path targetLocation = this.fileStorageLocation.resolve(fileName);
  150 + Files.copy(inputStream, targetLocation, StandardCopyOption.REPLACE_EXISTING);
  151 +
  152 + return fileName;
  153 + }
139 154 }
... ...
1 1 package org.thingsboard.server.common.data.yunteng.core.utils;
2 2
3 3 import io.minio.*;
  4 +import io.minio.errors.*;
4 5 import lombok.extern.slf4j.Slf4j;
5 6 import okhttp3.OkHttpClient;
6 7 import org.apache.commons.lang3.RandomStringUtils;
... ... @@ -22,8 +23,10 @@ import org.thingsboard.server.common.data.yunteng.dto.FileUploadResponse;
22 23 import javax.net.ssl.*;
23 24 import javax.servlet.http.HttpServletRequest;
24 25 import javax.servlet.http.HttpServletResponse;
  26 +import java.io.IOException;
25 27 import java.io.InputStream;
26 28 import java.io.OutputStream;
  29 +import java.security.InvalidKeyException;
27 30 import java.security.KeyManagementException;
28 31 import java.security.NoSuchAlgorithmException;
29 32 import java.security.SecureRandom;
... ... @@ -36,7 +39,7 @@ import java.util.Objects;
36 39 public class MinioFileStorageService implements FileStorageService {
37 40
38 41 private final MinioFileStorageProperties fileStorageProperties;
39   - private MinioClient minioClient;
  42 + private final MinioClient minioClient;
40 43
41 44 @Autowired
42 45 public MinioFileStorageService(MinioFileStorageProperties fileStorageProperties)
... ... @@ -53,36 +56,47 @@ public class MinioFileStorageService implements FileStorageService {
53 56 checkBucket();
54 57 }
55 58
  59 + public static OkHttpClient getUnsafeOkHttpClient() throws KeyManagementException {
  60 + try {
  61 + final TrustManager[] trustAllCerts =
  62 + new TrustManager[] {
  63 + new X509TrustManager() {
  64 + @Override
  65 + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}
  66 +
  67 + @Override
  68 + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}
  69 +
  70 + @Override
  71 + public X509Certificate[] getAcceptedIssuers() {
  72 + return new X509Certificate[] {};
  73 + }
  74 + }
  75 + };
  76 +
  77 + final SSLContext sslContext = SSLContext.getInstance("SSL");
  78 + sslContext.init(null, trustAllCerts, new SecureRandom());
  79 + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
  80 + OkHttpClient.Builder builder = new OkHttpClient.Builder();
  81 + builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
  82 +
  83 + builder.hostnameVerifier((s, sslSession) -> true);
  84 + return builder.build();
  85 +
  86 + } catch (NoSuchAlgorithmException e) {
  87 + e.printStackTrace();
  88 + }
  89 + return null;
  90 + }
  91 +
56 92 // 储存文件
57 93 public String storeFile(MultipartFile file) {
58 94 // Normalize file name
59 95 String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
60 96 // random fileName if needed
61   - if (fileStorageProperties.isRandomFileName()) {
62   - if (fileName.contains(".")) {
63   - fileName =
64   - RandomStringUtils.randomAlphabetic(15) + fileName.substring(fileName.lastIndexOf("."));
65   - } else {
66   - fileName = RandomStringUtils.randomAlphabetic(15);
67   - }
68   - }
69   - // Check if the file's name contains invalid characters
70   - if (fileName.contains("..")) {
71   - throw new FileStorageException(ErrorMessage.STORE_FILE_FAILED);
72   - }
73   -
74 97 // 储存
75 98 try {
76   - checkBucket();
77   - PutObjectArgs build =
78   - PutObjectArgs.builder()
79   - .bucket(fileStorageProperties.getBucketName())
80   - .object(fileName)
81   - .contentType(file.getContentType())
82   - .stream(file.getInputStream(), file.getInputStream().available(), -1)
83   - .build();
84   - minioClient.putObject(build);
85   - return fileName;
  99 + return storeFileByInputStream(fileName, file.getContentType(), file.getInputStream());
86 100 } catch (Exception ex) {
87 101 ex.printStackTrace();
88 102 throw new FileStorageException(ErrorMessage.STORE_FILE_FAILED);
... ... @@ -122,7 +136,7 @@ public class MinioFileStorageService implements FileStorageService {
122 136 .object(fileName)
123 137 .build());
124 138 // 获取输出流
125   - OutputStream outputStream = response.getOutputStream(); ) {
  139 + OutputStream outputStream = response.getOutputStream()) {
126 140 // 清空
127 141 response.reset();
128 142 // 写入流
... ... @@ -141,6 +155,16 @@ public class MinioFileStorageService implements FileStorageService {
141 155 }
142 156 }
143 157
  158 + @Override
  159 + public String uploadFile(String fileName, String contentType, InputStream inputStream)
  160 + throws IOException {
  161 + try {
  162 + return getPath(storeFileByInputStream(fileName, contentType, inputStream));
  163 + } catch (Exception e) {
  164 + throw new IOException(e.getMessage());
  165 + }
  166 + }
  167 +
144 168 // 检查储存桶情况
145 169 private void checkBucket() {
146 170 try {
... ... @@ -181,37 +205,32 @@ public class MinioFileStorageService implements FileStorageService {
181 205 .concat(fileName);
182 206 }
183 207
184   - public static OkHttpClient getUnsafeOkHttpClient() throws KeyManagementException {
185   - try {
186   - final TrustManager[] trustAllCerts =
187   - new TrustManager[] {
188   - new X509TrustManager() {
189   - @Override
190   - public void checkClientTrusted(X509Certificate[] x509Certificates, String s){}
191   -
192   - @Override
193   - public void checkServerTrusted(X509Certificate[] x509Certificates, String s){}
194   -
195   - @Override
196   - public X509Certificate[] getAcceptedIssuers() {
197   - return new X509Certificate[] {};
198   - }
199   - }
200   - };
201   -
202   - final SSLContext sslContext = SSLContext.getInstance("SSL");
203   - sslContext.init(null, trustAllCerts, new SecureRandom());
204   - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
205   - OkHttpClient.Builder builder = new OkHttpClient.Builder();
206   - builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
207   -
208   - builder.hostnameVerifier(
209   - (s, sslSession) -> true);
210   - return builder.build();
211   -
212   - } catch (NoSuchAlgorithmException e) {
213   - e.printStackTrace();
  208 + private String storeFileByInputStream(
  209 + String fileName, String contentType, InputStream inputStream)
  210 + throws ServerException, InsufficientDataException, ErrorResponseException, IOException,
  211 + NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException,
  212 + XmlParserException, InternalException {
  213 + if (fileStorageProperties.isRandomFileName()) {
  214 + if (!fileName.contains(".")) {
  215 + fileName = RandomStringUtils.randomAlphabetic(15);
  216 + } else {
  217 + fileName =
  218 + RandomStringUtils.randomAlphabetic(15) + fileName.substring(fileName.lastIndexOf("."));
  219 + }
214 220 }
215   - return null;
  221 + // Check if the file's name contains invalid characters
  222 + if (fileName.contains("..")) {
  223 + throw new FileStorageException(ErrorMessage.STORE_FILE_FAILED);
  224 + }
  225 + checkBucket();
  226 + PutObjectArgs build =
  227 + PutObjectArgs.builder()
  228 + .bucket(fileStorageProperties.getBucketName())
  229 + .object(fileName)
  230 + .contentType(contentType)
  231 + .stream(inputStream, inputStream.available(), -1)
  232 + .build();
  233 + minioClient.putObject(build);
  234 + return fileName;
216 235 }
217 236 }
... ...
... ... @@ -26,6 +26,8 @@ public class ReportFormConfigDTO extends TenantDTO {
26 26 @NotEmpty(message = "组织不能为空或空字符串", groups = AddGroup.class)
27 27 private String organizationId;
28 28
  29 + private OrganizationDTO organizationDTO;
  30 +
29 31 /** 执行方式:0立即执行 1定时执行 */
30 32 @ApiModelProperty(value = "执行方式", required = true)
31 33 @NotNull(message = "执行方式不能为空", groups = AddGroup.class)
... ... @@ -39,15 +41,11 @@ public class ReportFormConfigDTO extends TenantDTO {
39 41 @Size(message = "至少需要一个执行设备及属性", min = 1,groups = AddGroup.class)
40 42 private List<ExecuteAttributesDTO> executeAttributes;
41 43
42   - /** 属性性质:0共有属性 1全部属性 */
43   - @ApiModelProperty(value = "属性性质", required = true)
44   - @NotNull(message = "属性性质不能为空", groups = AddGroup.class)
45   - private Integer attributeNature;
46 44
47   - /** 数据对比:0历史数据 1环比 2同比 */
48   - @ApiModelProperty(value = "数据对比", required = true)
49   - @NotNull(message = "数据对比不能为空", groups = AddGroup.class)
50   - private Integer dataCompare;
  45 + /** 数据类型:0历史数据 1聚合数据 */
  46 + @ApiModelProperty(value = "数据类型:0原始数据 1聚合数据", required = true)
  47 + @NotNull(message = "数据类型不能为空", groups = AddGroup.class)
  48 + private Integer dataType;
51 49
52 50 /** 数据周期:单位毫秒 */
53 51 @ApiModelProperty(value = "开始时间", required = true)
... ...
  1 +package org.thingsboard.server.common.data.yunteng.dto.report;
  2 +
  3 +import lombok.Data;
  4 +import org.jetbrains.annotations.NotNull;
  5 +
  6 +@Data
  7 +public class BasicData implements Comparable<BasicData> {
  8 + private final Object value;
  9 +
  10 + private final long ts;
  11 +
  12 + public BasicData(long ts, Object value) {
  13 + super();
  14 + this.ts = ts;
  15 + this.value = value;
  16 + }
  17 +
  18 + @Override
  19 + public int compareTo(@NotNull BasicData o) {
  20 + return Long.compare(ts, o.ts);
  21 + }
  22 +}
... ...
  1 +package org.thingsboard.server.common.data.yunteng.dto.report;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +/** 报表导出:Echarts图表展示数据 */
  8 +@Data
  9 +public class ChartReportDTO {
  10 + /** 报表查询:开始时间 */
  11 + private Long startTs;
  12 +
  13 + /** 报表查询:结束时间 */
  14 + private Long endTs;
  15 +
  16 + /** 查询返回的数据 */
  17 + private List<ReportDataSourceDTO> data;
  18 +}
... ...
  1 +package org.thingsboard.server.common.data.yunteng.dto.report;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import java.util.List;
  6 +
  7 +/** 基础报表数据 */
  8 +@Data
  9 +public class ReportDataSourceDTO {
  10 +
  11 + /** 采集属性 */
  12 + private String attribute;
  13 +
  14 + /** 采集的数据 */
  15 + private List<BasicData> data;
  16 +}
... ...
1   -package org.thingsboard.server.common.data.yunteng.dto.report;
2   -
3   -/**+
4   - * 导出报表:非聚合数据生成
5   - */
6   -public class UnAggregationReportDTO {
7   -}
... ... @@ -4,12 +4,17 @@ import io.swagger.annotations.ApiModel;
4 4 import io.swagger.annotations.ApiModelProperty;
5 5 import lombok.Data;
6 6
  7 +import java.util.List;
  8 +
7 9 @Data
8 10 @ApiModel(value = "执行设备及属性")
9 11 public class ExecuteAttributesDTO {
10 12 @ApiModelProperty(value = "执行设备", required = true)
11 13 private String device;
12 14
  15 + @ApiModelProperty(value = "设备名称", required = true)
  16 + private String name;
  17 +
13 18 @ApiModelProperty(value = "执行属性", required = true)
14   - private String attribute;
  19 + private List<String> attributes;
15 20 }
... ...
... ... @@ -3,21 +3,22 @@ package org.thingsboard.server.common.data.yunteng.dto.request;
3 3 import io.swagger.annotations.ApiModel;
4 4 import io.swagger.annotations.ApiModelProperty;
5 5 import lombok.Data;
  6 +import org.thingsboard.server.common.data.kv.Aggregation;
6 7
7 8 @Data
8 9 @ApiModel(value = "查询条件")
9 10 public class QueryConditionDTO {
10   - @ApiModelProperty("聚合间隔:单位毫秒")
11   - private Long interval;
  11 + @ApiModelProperty("聚合间隔:单位毫秒,默认0")
  12 + private Long interval = 0L;
12 13
13   - @ApiModelProperty("查询的最大数量:该参数仅在'agg'参数设置为'NONE'的情况下使用")
14   - private Integer limit;
  14 + @ApiModelProperty("查询的最大数量:该参数仅在'agg'参数设置为'NONE'的情况下使用,默认100")
  15 + private Integer limit = 100;
15 16
16 17 @ApiModelProperty("聚合条件:AVG, COUNT, MAX, MIN, NONE, SUM")
17   - private String agg;
  18 + private Aggregation agg;
18 19
19   - @ApiModelProperty("排序:ASC, DESC")
20   - private String orderBy;
  20 + @ApiModelProperty("排序:ASC, DESC 默认DESC")
  21 + private String orderBy = "DESC";
21 22
22 23 @ApiModelProperty("启用/禁用遥测值到字符串的转换。默认情况下启用转换。设置参数为'true'以禁用转换")
23 24 private boolean useStrictDataTypes;
... ...
... ... @@ -8,8 +8,8 @@ public enum StatusEnum {
8 8 ONLINE("在线", 1),
9 9 ENABLE("启用", 1),
10 10 DISABLE("禁用", 0),
11   - NORMAL("正常", 0),
12   - PAUSE("暂停", 1),
  11 + NORMAL("正常", 1),
  12 + PAUSE("暂停", 0),
13 13 FAIL("失败", 0),
14 14 SUCCESS("成功", 1),
15 15 RUNNING("运行中",2),
... ...
1 1 package org.thingsboard.server.common.data.yunteng.utils;
2 2
3 3 import com.alibaba.excel.EasyExcel;
  4 +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
4 5 import org.apache.commons.lang3.StringUtils;
5 6 import org.springframework.beans.BeanUtils;
6 7
7 8 import javax.servlet.http.HttpServletResponse;
  9 +import java.io.ByteArrayOutputStream;
8 10 import java.io.IOException;
9 11 import java.net.URLEncoder;
10 12 import java.nio.charset.StandardCharsets;
... ... @@ -15,7 +17,6 @@ import java.util.List;
15 17
16 18 public class ExcelUtil {
17 19 private ExcelUtil() {}
18   -
19 20 public static void exportExcel(
20 21 HttpServletResponse response,
21 22 String fileName,
... ... @@ -52,4 +53,15 @@ public class ExcelUtil {
52 53
53 54 exportExcel(response, fileName, sheetName, targetList, targetClass);
54 55 }
  56 +
  57 + public static ByteArrayOutputStream noModelWrite(String fileName, List<List<String>> heads, List<?> data) {
  58 + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  59 + EasyExcel.write(byteArrayOutputStream)
  60 + .head(heads)
  61 + .sheet(fileName)
  62 + // 自适应列宽
  63 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
  64 + .doWrite(data);
  65 + return byteArrayOutputStream;
  66 + }
55 67 }
... ...
... ... @@ -64,7 +64,7 @@ public abstract class AbstractQuartzJob implements Job {
64 64 long runMs =
65 65 sysJobLog.getEndTime().toInstant(ZoneOffset.of("+8")).toEpochMilli()
66 66 - startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
67   - sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
  67 + sysJobLog.setJobMessage(" 总共耗时:" + runMs + "毫秒");
68 68 if (e != null) {
69 69 sysJobLog.setStatus(StatusEnum.FAIL.getIndex());
70 70 String errorMsg = StringUtils.substring(e.getMessage(), 0, 2000);
... ...
1 1 package org.thingsboard.server.dao.util.yunteng;
2 2
3 3 import org.apache.commons.lang3.StringUtils;
  4 +import org.thingsboard.server.common.data.yunteng.dto.ReportGenerateRecordDTO;
4 5 import org.thingsboard.server.common.data.yunteng.enums.JobGroupEnum;
5 6 import org.thingsboard.server.common.data.yunteng.utils.SpringBeanUtils;
6 7 import org.thingsboard.server.dao.yunteng.entities.SysJob;
... ... @@ -22,13 +23,16 @@ public class JobInvokeUtil {
22 23 String invokeTarget = sysJob.getInvokeTarget();
23 24 String beanName = getBeanName(invokeTarget);
24 25 String methodName = getMethodName(invokeTarget);
  26 + String reportGenerateRecordId = null;
25 27 if (StringUtils.isNotEmpty(sysJob.getSourceId())
26 28 && sysJob.getJobGroup().equals(JobGroupEnum.REPORT.toString())) {
27 29 // 写入报表生成记录
28   - SpringBeanUtils.getBean(YtReportGenerateRecordService.class)
29   - .generateReportRecord(sysJob.getSourceId(), sysJob.getTenantId(), sysJob.getId());
  30 + ReportGenerateRecordDTO dto =
  31 + SpringBeanUtils.getBean(YtReportGenerateRecordService.class)
  32 + .generateReportRecord(sysJob.getSourceId(), sysJob.getTenantId(), sysJob.getId());
  33 + reportGenerateRecordId = dto.getId();
30 34 }
31   - List<Object[]> methodParams = getMethodParams(invokeTarget);
  35 + List<Object[]> methodParams = getMethodParams(invokeTarget, reportGenerateRecordId);
32 36 Object bean;
33 37 if (!isValidClassName(beanName)) {
34 38 bean = SpringBeanUtils.getBean(beanName);
... ... @@ -96,11 +100,14 @@ public class JobInvokeUtil {
96 100 * @param invokeTarget 目标字符串
97 101 * @return method方法相关参数列表
98 102 */
99   - public static List<Object[]> getMethodParams(String invokeTarget) {
  103 + public static List<Object[]> getMethodParams(String invokeTarget, String reportGenerateRecordId) {
100 104 String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
101 105 if (StringUtils.isEmpty(methodStr)) {
102 106 return null;
103 107 }
  108 + if (reportGenerateRecordId != null) {
  109 + methodStr += ",\"" + reportGenerateRecordId + "\"";
  110 + }
104 111 String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
105 112 List<Object[]> classs = new LinkedList<>();
106 113 for (String methodParam : methodParams) {
... ...
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 +import lombok.RequiredArgsConstructor;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +import org.jetbrains.annotations.NotNull;
  9 +import org.springframework.http.HttpStatus;
  10 +import org.springframework.http.ResponseEntity;
3 11 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;
  18 +import org.thingsboard.server.common.data.yunteng.core.utils.FileStorageService;
  19 +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;
  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;
  28 +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.*;
  36 +import java.util.stream.Collectors;
4 37
5 38 @Component("reportTask")
  39 +@RequiredArgsConstructor
  40 +@Slf4j
6 41 public class ReportTask {
7   - public void multipleParams(String s, Boolean b, Long l, Double d, Integer i)
8   - {
9   - System.out.println("执行多参方法: s="+s+"b="+b+"l="+l+"d"+d+"i="+i);
10   - }
  42 + private static final String CONTENT_TYPE =
  43 + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  44 + private final YtReportFormConfigService ytReportFormConfigService;
  45 + private final TimeseriesService tsService;
  46 + private final FileStorageService fileStorageService;
  47 + private final YtReportGenerateRecordService ytReportGenerateRecordService;
11 48
12   - public void params(String params) throws InterruptedException {
13   - System.out.println("==========================执行有参方法:" + params+"=======================");
14   - Thread.sleep(100);
15   - }
  49 + public void multipleParams(String s, Boolean b, Long l, Double d, Integer i) {
  50 + System.out.println("执行多参方法: s=" + s + "b=" + b + "l=" + l + "d" + d + "i=" + i);
  51 + }
  52 +
  53 + public void params(String params) throws InterruptedException {
  54 + System.out.println("==========================执行有参方法:" + params + "=======================");
  55 + Thread.sleep(100);
  56 + }
16 57
17   - public void noParams() throws InterruptedException {
18   - System.out.println("=========================执行无参方法======================================");
  58 + public void noParams() throws InterruptedException {
  59 + System.out.println("=========================执行无参方法======================================");
  60 + }
  61 +
  62 + public void generateReport(String reportId, String reportGenerateRecordId) {
  63 + ReportFormConfigDTO formConfigDTO =
  64 + ytReportFormConfigService.findReportFormConfigById(reportId);
  65 + // 关联设备
  66 + List<ExecuteAttributesDTO> dtoList = formConfigDTO.getExecuteAttributes();
  67 + if (!dtoList.isEmpty()) {
  68 + if (dtoList.size() == FastIotConstants.MagicNumber.ONE) {
  69 + try {
  70 + ExecuteAttributesDTO attributesDTO = dtoList.get(0);
  71 + getTsKv(formConfigDTO, attributesDTO, reportGenerateRecordId);
  72 +
  73 + } catch (Exception e) {
  74 + log.error(e.getMessage());
  75 + }
  76 + } else {
  77 + for (ExecuteAttributesDTO dto : dtoList) {
  78 + getTsKv(formConfigDTO, dto, reportGenerateRecordId);
  79 + }
  80 + }
19 81 }
  82 + }
  83 +
  84 + private void getTsKv(
  85 + 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());
20 103
21   - public void generateReport(String reportId){
22   - System.out.println("===========================执行报表生成:"+ reportId +"====================================");
  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);
23 148 }
  149 + return entry.getValue();
  150 + }
24 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 + }
  213 + }
25 214 }
... ...
... ... @@ -33,11 +33,8 @@ public class ReportFormConfig extends TenantBaseEntity {
33 33 @TableField(typeHandler = JacksonTypeHandler.class)
34 34 private JsonNode executeAttributes;
35 35
36   - /** 属性性质:0共有属性 1全部属性 */
37   - private Integer attributeNature;
38   -
39   - /** 数据对比:0历史数据 1环比 2同比 */
40   - private Integer dataCompare;
  36 + /** 数据对比:0原始数据 1聚合数据 */
  37 + private Integer dataType;
41 38
42 39 /** 查询开始时间:单位毫秒 */
43 40 private Long startTs;
... ...
... ... @@ -31,7 +31,7 @@ public class SysJob extends TenantBaseEntity {
31 31 /** 是否并发执行(0允许 1禁止) */
32 32 private Integer concurrent;
33 33
34   - /** 任务状态(0正常 1暂停) */
  34 + /** 任务状态(0暂停 1正常) */
35 35 private Integer status;
36 36
37 37 /** 来源ID:即创建定时任务对应的业务ID */
... ...
... ... @@ -194,7 +194,6 @@ public class SysJobServiceImpl extends AbstractBaseService<SysJobMapper, SysJob>
194 194 @Transactional(rollbackFor = Exception.class)
195 195 public SysJobDTO saveOrUpdateJob(SysJobDTO jobDTO) throws SchedulerException {
196 196 if (StringUtils.isEmpty(jobDTO.getId())) {
197   - jobDTO.setStatus(StatusEnum.PAUSE.getIndex());
198 197 SysJob job = jobDTO.getEntity(SysJob.class);
199 198 baseMapper.insert(job);
200 199 ScheduleUtils.createScheduleJob(scheduler, job);
... ...
... ... @@ -13,28 +13,27 @@ import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
13 13 import org.thingsboard.server.common.data.yunteng.core.exception.YtDataValidationException;
14 14 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
15 15 import org.thingsboard.server.common.data.yunteng.dto.*;
  16 +import org.thingsboard.server.common.data.yunteng.dto.request.ExecuteAttributesDTO;
  17 +import org.thingsboard.server.common.data.yunteng.dto.request.QueryConditionDTO;
16 18 import org.thingsboard.server.common.data.yunteng.enums.JobGroupEnum;
17 19 import org.thingsboard.server.common.data.yunteng.enums.StatusEnum;
18 20 import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil;
19 21 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
20 22 import org.thingsboard.server.dao.yunteng.entities.ReportFormConfig;
21 23 import org.thingsboard.server.dao.yunteng.mapper.OrganizationMapper;
22   -import org.thingsboard.server.dao.yunteng.mapper.ReportFromConfigMapper;
  24 +import org.thingsboard.server.dao.yunteng.mapper.ReportFormConfigMapper;
23 25 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
24 26 import org.thingsboard.server.dao.yunteng.service.YtReportFormConfigService;
25 27 import org.thingsboard.server.dao.yunteng.service.YtSysJobService;
26 28
27   -import java.util.List;
28   -import java.util.Map;
29   -import java.util.Objects;
30   -import java.util.Optional;
  29 +import java.util.*;
31 30 import java.util.stream.Collectors;
32 31
33 32 @Slf4j
34 33 @Service
35 34 @RequiredArgsConstructor
36 35 public class YtReportFromConfigServiceImpl
37   - extends AbstractBaseService<ReportFromConfigMapper, ReportFormConfig>
  36 + extends AbstractBaseService<ReportFormConfigMapper, ReportFormConfig>
38 37 implements YtReportFormConfigService {
39 38 private final OrganizationMapper organizationMapper;
40 39 private final YtSysJobService ytSysJobService;
... ... @@ -43,11 +42,6 @@ public class YtReportFromConfigServiceImpl
43 42 public YtPageData<ReportFormConfigDTO> page(Map<String, Object> queryMap) {
44 43 String tenantId =
45 44 Optional.ofNullable(queryMap.get("tenantId")).map(Object::toString).orElse(null);
46   - String name = Optional.ofNullable(queryMap.get("name")).map(Object::toString).orElse(null);
47   - Integer status =
48   - Optional.ofNullable(queryMap.get("status"))
49   - .map(value -> Integer.valueOf(value.toString()))
50   - .orElse(null);
51 45 List<String> organizationIds =
52 46 Optional.ofNullable(queryMap.get("organizationId"))
53 47 .map(
... ... @@ -63,17 +57,10 @@ public class YtReportFromConfigServiceImpl
63 57 return null;
64 58 })
65 59 .orElse(null);
66   - IPage<ReportFormConfig> page =
67   - baseMapper.selectPage(
68   - getPage(queryMap, "create_time", false),
69   - new LambdaQueryWrapper<ReportFormConfig>()
70   - .eq(StringUtils.isNotEmpty(tenantId), ReportFormConfig::getTenantId, tenantId)
71   - .eq(null != status, ReportFormConfig::getStatus, status)
72   - .like(null != name, ReportFormConfig::getName, name)
73   - .in(
74   - null != organizationIds && organizationIds.size() > 0,
75   - ReportFormConfig::getOrganizationId,
76   - organizationIds));
  60 + queryMap.put("organizationIds", organizationIds);
  61 + IPage<ReportFormConfigDTO> page =
  62 + baseMapper.getReportFormConfigPage(
  63 + getPage(queryMap, FastIotConstants.DefaultOrder.CREATE_TIME, false), queryMap);
77 64 return getPageData(page, ReportFormConfigDTO.class);
78 65 }
79 66
... ... @@ -93,8 +80,7 @@ public class YtReportFromConfigServiceImpl
93 80 .getExecuteAttributes()
94 81 .forEach(
95 82 item -> {
96   - if (StringUtils.isEmpty(item.getDevice())
97   - || StringUtils.isEmpty(item.getAttribute())) {
  83 + if (StringUtils.isEmpty(item.getDevice()) || item.getAttributes().isEmpty()) {
98 84 throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
99 85 }
100 86 });
... ... @@ -123,7 +109,7 @@ public class YtReportFromConfigServiceImpl
123 109
124 110 @Override
125 111 @Transactional
126   - public boolean deleteReportFormConfig(DeleteDTO deleteDTO) {
  112 + public boolean deleteReportFormConfig(DeleteDTO deleteDTO) throws SchedulerException {
127 113 if (deleteDTO.getIds().isEmpty()) {
128 114 throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
129 115 }
... ... @@ -146,11 +132,13 @@ public class YtReportFromConfigServiceImpl
146 132 new LambdaQueryWrapper<ReportFormConfig>()
147 133 .eq(ReportFormConfig::getId, id)
148 134 .eq(ReportFormConfig::getTenantId, tenantId));
149   - updateCheckSysJob(queryEntity);
  135 + if (queryEntity != null) {
  136 + queryEntity.setStatus(status);
  137 + updateCheckSysJob(queryEntity);
  138 + }
150 139 return Optional.ofNullable(queryEntity)
151 140 .map(
152 141 entity -> {
153   - entity.setStatus(status);
154 142 baseMapper.updateById(entity);
155 143 return entity.getDTO(ReportFormConfigDTO.class);
156 144 })
... ... @@ -167,14 +155,26 @@ public class YtReportFromConfigServiceImpl
167 155 if (null == formConfig) {
168 156 throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
169 157 }
170   - return formConfig.getDTO(ReportFormConfigDTO.class);
  158 + return getReportFormConfigDTOByEntity(formConfig);
  159 + }
  160 +
  161 + @Override
  162 + public ReportFormConfigDTO findReportFormConfigById(String id) {
  163 + return Optional.ofNullable(baseMapper.selectById(id))
  164 + .map(obj -> getReportFormConfigDTOByEntity(obj))
  165 + .orElseThrow(
  166 + () -> {
  167 + throw new YtDataValidationException(ErrorMessage.INTERNAL_ERROR.getMessage());
  168 + });
171 169 }
172 170
173 171 private void addCheckSysJob(ReportFormConfig reportFormConfig) throws SchedulerException {
174 172 boolean isNowExecute =
175 173 Objects.equals(reportFormConfig.getExecuteWay(), FastIotConstants.MagicNumber.ZERO);
176 174 if (isNowExecute) {
177   - // 立即执行报表生成,并创建一条报表执行记录
  175 + if (Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex())) {
  176 + // 立即执行报表生成,并创建一条报表执行记录
  177 + }
178 178 } else {
179 179 createSysJob(reportFormConfig);
180 180 }
... ... @@ -186,30 +186,29 @@ public class YtReportFromConfigServiceImpl
186 186 String cronExpression = reportFormConfig.getExecuteContent();
187 187 // 查看之前是否创建了定时任务
188 188 SysJobDTO sysJobDTO = ytSysJobService.findSysJobBySourceId(reportFormConfig.getId());
189   -
190   - if (null != sysJobDTO
191   - && Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex())) {
  189 + boolean enableStatus =
  190 + Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex());
  191 + if (null != sysJobDTO) {
192 192 // 立即执行
193 193 if (isNowExecute) {
194 194 // 删除定时任务
195 195 ytSysJobService.deleteJob(sysJobDTO);
196   - // 立即执行报表生成,并创建一条报表执行记录
197   - // TODO hxp
  196 + if (enableStatus) {
  197 + // 立即执行报表生成,并创建一条报表执行记录
  198 + // TODO hxp
  199 + }
198 200 } else {
199 201 // 修改cron表达式
200 202 sysJobDTO.setCronExpression(cronExpression);
  203 + sysJobDTO.setStatus(reportFormConfig.getStatus());
201 204 ytSysJobService.saveOrUpdateJob(sysJobDTO);
202 205 }
203   - } else if (null != sysJobDTO
204   - && Objects.equals(reportFormConfig.getStatus(), StatusEnum.DISABLE.getIndex())) {
205   - // 修改定时任务状态
206   - sysJobDTO.setStatus(StatusEnum.DISABLE.getIndex());
207   - ytSysJobService.updateSysJobStatus(sysJobDTO);
208   - } else if (null == sysJobDTO
209   - && Objects.equals(reportFormConfig.getStatus(), StatusEnum.ENABLE.getIndex())) {
  206 + } else {
210 207 if (isNowExecute) {
211   - // 立即执行报表生成,并创建一条报表执行记录
212   - // TODO hxp
  208 + if (enableStatus) {
  209 + // 立即执行报表生成,并创建一条报表执行记录
  210 + // TODO hxp
  211 + }
213 212 } else {
214 213 createSysJob(reportFormConfig);
215 214 }
... ... @@ -240,7 +239,7 @@ public class YtReportFromConfigServiceImpl
240 239 sysJobDTO.setTenantId(reportFormConfig.getTenantId());
241 240 sysJobDTO.setCronExpression(cronExpression);
242 241 sysJobDTO.setConcurrent(StatusEnum.ENABLE.getIndex());
243   - sysJobDTO.setInvokeTarget("reportTask.generateReport(\""+reportId+"\")");
  242 + sysJobDTO.setInvokeTarget("reportTask.generateReport(\"" + reportId + "\")");
244 243 ytSysJobService.saveOrUpdateJob(sysJobDTO);
245 244 }
246 245 }
... ... @@ -250,24 +249,36 @@ public class YtReportFromConfigServiceImpl
250 249 *
251 250 * @param id 配置ID
252 251 */
253   - private void checkAndDeleteJob(String id) {
  252 + private void checkAndDeleteJob(String id) throws SchedulerException {
254 253 ReportFormConfig config = baseMapper.selectById(id);
255   - Optional.ofNullable(config)
256   - .map(
257   - obj -> {
258   - SysJobDTO sysJobDTO = ytSysJobService.findSysJobBySourceId(id);
259   - if (null != sysJobDTO) {
260   - try {
261   - ytSysJobService.deleteJob(sysJobDTO);
262   - } catch (SchedulerException e) {
263   - log.error(e.getMessage());
264   - }
265   - }
266   - return sysJobDTO;
267   - })
268   - .orElseThrow(
269   - () -> {
270   - throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
271   - });
  254 + if (null != config) {
  255 + SysJobDTO sysJobDTO = ytSysJobService.findSysJobBySourceId(id);
  256 + if (null != sysJobDTO) {
  257 + ytSysJobService.deleteJob(sysJobDTO);
  258 + }
  259 + }
  260 + }
  261 +
  262 + private ReportFormConfigDTO getReportFormConfigDTOByEntity(ReportFormConfig obj) {
  263 + ReportFormConfigDTO dto = new ReportFormConfigDTO();
  264 + obj.copyToDTO(dto, "executeAttributes");
  265 + JsonNode jsonNode = obj.getExecuteAttributes();
  266 + List<ExecuteAttributesDTO> list = new ArrayList<>();
  267 + for (JsonNode node : jsonNode) {
  268 + ExecuteAttributesDTO attributesDTO =
  269 + JacksonUtil.convertValue(node, ExecuteAttributesDTO.class);
  270 + List<String> attributes = new ArrayList<>();
  271 + JsonNode attribute = node.get("attributes");
  272 + for (JsonNode key : attribute) {
  273 + attributes.add(JacksonUtil.convertValue(key, String.class));
  274 + }
  275 + attributesDTO.setAttributes(attributes);
  276 + list.add(attributesDTO);
  277 + }
  278 + QueryConditionDTO queryCondition =
  279 + JacksonUtil.convertValue(obj.getQueryCondition(), QueryConditionDTO.class);
  280 + dto.setExecuteAttributes(list);
  281 + dto.setQueryCondition(queryCondition);
  282 + return dto;
272 283 }
273 284 }
... ...
... ... @@ -48,7 +48,7 @@ public class YtReportGenerateRecordServiceImpl
48 48 .orElse(null);
49 49 IPage<ReportGenerateRecord> iPage =
50 50 baseMapper.selectPage(
51   - getPage(queryMap, "", false),
  51 + getPage(queryMap, "execute_time", false),
52 52 new LambdaQueryWrapper<ReportGenerateRecord>()
53 53 .like(
54 54 StringUtils.isNotEmpty(reportConfigName),
... ... @@ -71,16 +71,15 @@ public class YtReportGenerateRecordServiceImpl
71 71 if (StringUtils.isEmpty(recordDTO.getId())) {
72 72 baseMapper.insert(reportGenerateRecord);
73 73 } else {
74   - Optional.ofNullable(recordDTO.getId())
75   - .map(id -> baseMapper.selectById(id))
76   - .filter(obj -> !obj.getTenantId().equals(recordDTO.getTenantId()))
77   - .map(record -> baseMapper.updateById(record))
  74 + ReportGenerateRecord record = baseMapper.selectById(recordDTO.getId());
  75 + Optional.ofNullable(record)
  76 + .map(obj -> baseMapper.updateById(reportGenerateRecord))
78 77 .orElseThrow(
79 78 () -> {
80 79 throw new YtDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage());
81 80 });
82 81 }
83   - return recordDTO;
  82 + return reportGenerateRecord.getDTO(ReportGenerateRecordDTO.class);
84 83 }
85 84
86 85 @Override
... ... @@ -111,17 +110,20 @@ public class YtReportGenerateRecordServiceImpl
111 110 }
112 111 OrganizationDTO organizationDTO =
113 112 ytOrganizationService.findOrganizationById(
114   - reportFormConfigDTO.getId(), reportFormConfigDTO.getTenantId());
  113 + reportFormConfigDTO.getOrganizationId(), reportFormConfigDTO.getTenantId());
115 114 // 创建报表生成记录
116 115 ReportGenerateRecordDTO dto = new ReportGenerateRecordDTO();
  116 + LocalDateTime time = LocalDateTime.now();
117 117 dto.setJobId(jobId);
118 118 dto.setReportConfigName(reportFormConfigDTO.getName());
119 119 dto.setOrganizationName(null != organizationDTO ? organizationDTO.getName() : null);
120   - dto.setDataCompare(reportFormConfigDTO.getDataCompare());
121   - dto.setExecuteTime(LocalDateTime.now());
  120 + dto.setDataCompare(reportFormConfigDTO.getDataType());
  121 + dto.setExecuteTime(time);
  122 + dto.setCreateTime(time);
  123 + dto.setCreator(reportFormConfigDTO.getCreator());
  124 + dto.setTenantId(reportFormConfigDTO.getTenantId());
122 125 dto.setExecuteStatus(StatusEnum.RUNNING.getIndex());
123 126 dto.setExecuteWay(reportFormConfigDTO.getExecuteWay());
124   - saveOrUpdateReportGenerateRecord(dto);
125   - return dto;
  127 + return saveOrUpdateReportGenerateRecord(dto);
126 128 }
127 129 }
... ...
dao/src/main/java/org/thingsboard/server/dao/yunteng/mapper/ReportFormConfigMapper.java renamed from dao/src/main/java/org/thingsboard/server/dao/yunteng/mapper/ReportFromConfigMapper.java
1 1 package org.thingsboard.server.dao.yunteng.mapper;
2 2
3 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4 +import com.baomidou.mybatisplus.core.metadata.IPage;
4 5 import org.apache.ibatis.annotations.Mapper;
5   -import org.thingsboard.server.dao.yunteng.entities.FrpInfo;
  6 +import org.apache.ibatis.annotations.Param;
  7 +import org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO;
6 8 import org.thingsboard.server.dao.yunteng.entities.ReportFormConfig;
7 9
8   -@Mapper
9   -public interface ReportFromConfigMapper extends BaseMapper<ReportFormConfig> {
  10 +import java.util.Map;
10 11
  12 +@Mapper
  13 +public interface ReportFormConfigMapper extends BaseMapper<ReportFormConfig> {
  14 + IPage<ReportFormConfigDTO> getReportFormConfigPage(
  15 + IPage<?> page, @Param("queryMap") Map<String, Object> queryMap);
11 16 }
... ...
... ... @@ -12,9 +12,11 @@ public interface YtReportFormConfigService {
12 12
13 13 ReportFormConfigDTO saveOrUpdateReportFormConfig(ReportFormConfigDTO report) throws SchedulerException;
14 14
15   - boolean deleteReportFormConfig(DeleteDTO deleteDTO);
  15 + boolean deleteReportFormConfig(DeleteDTO deleteDTO) throws SchedulerException;
16 16
17 17 ReportFormConfigDTO updateStatusById(String tenantId,Integer status,String id) throws SchedulerException;
18 18
19 19 ReportFormConfigDTO findReportFormConfigById(String id,String tenantId);
  20 +
  21 + ReportFormConfigDTO findReportFormConfigById(String id);
20 22 }
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +
  4 +<mapper namespace="org.thingsboard.server.dao.yunteng.mapper.ReportFormConfigMapper">
  5 + <resultMap id="formConfigDtoMap" type="org.thingsboard.server.common.data.yunteng.dto.ReportFormConfigDTO">
  6 + <result property="id" column="id"/>
  7 + <result property="name" column="name"/>
  8 + <result property="organizationId" column="organization_id"/>
  9 + <result property="executeWay" column="execute_way"/>
  10 + <result property="executeContent" column="execute_content"/>
  11 + <result property="dataType" column="data_type"/>
  12 + <result property="startTs" column="start_ts"/>
  13 + <result property="endTs" column="end_ts"/>
  14 + <result property="status" column="status"/>
  15 + <result property="remark" column="remark"/>
  16 + <result property="tenantId" column="tenant_id"/>
  17 + <result property="creator" column="creator"/>
  18 + <result property="createTime" column="create_time"/>
  19 + <result property="updater" column="updater"/>
  20 + <result property="updateTime" column="update_time"/>
  21 + <association property="organizationDTO"
  22 + javaType="org.thingsboard.server.common.data.yunteng.dto.OrganizationDTO">
  23 + <result property="name" column="organization_name"/>
  24 + </association>
  25 + </resultMap>
  26 +
  27 + <sql id="columns">
  28 + config.id,config.name,config.organization_id,config.execute_way,config.execute_content,
  29 + config.data_type,config.start_ts,config.end_ts,config.status,config.remark,config.create_time,config.creator,
  30 + config.updater,config.update_time,config.tenant_id
  31 + </sql>
  32 + <select id="getReportFormConfigPage" resultMap="formConfigDtoMap">
  33 + SELECT
  34 + <include refid="columns"/>,io.name as organization_name
  35 + FROM iotfs_report_form_config config
  36 + LEFT JOIN iotfs_organization io ON io.id = config.organization_id
  37 + <where>
  38 + config.tenant_id = #{queryMap.tenantId}
  39 + <if test="queryMap.name !=null and queryMap.name !=''">
  40 + AND config.name LIKE concat('%',#{queryMap.name}::TEXT,'%')
  41 + </if>
  42 + <if test="queryMap.status !=null">
  43 + AND config.status = #{queryMap.status}
  44 + </if>
  45 + <if test="queryMap.organizationIds !=null">
  46 + AND config.organization_id IN
  47 + <foreach collection="queryMap.organizationIds" item="organizationId" open="(" separator="," close=")">
  48 + #{organizationId}
  49 + </foreach>
  50 + </if>
  51 + </where>
  52 + </select>
  53 +</mapper>
... ...