Commit c495fa5bc6814d91b37a22a7ec8e0b90f1086310
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) | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/report/BasicData.java
0 → 100644
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 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/report/ChartReportDTO.java
0 → 100644
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 | +} | ... | ... |
... | ... | @@ -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; | ... | ... |
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; | ... | ... |
... | ... | @@ -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> | ... | ... |