Showing
19 changed files
with
986 additions
and
132 deletions
application/src/main/java/org/thingsboard/server/controller/yunteng/TkTaskCenterController.java
0 → 100644
1 | +package org.thingsboard.server.controller.yunteng; | |
2 | + | |
3 | +import io.swagger.annotations.Api; | |
4 | +import io.swagger.annotations.ApiOperation; | |
5 | +import lombok.RequiredArgsConstructor; | |
6 | +import org.apache.commons.lang3.StringUtils; | |
7 | +import org.quartz.SchedulerException; | |
8 | +import org.springframework.security.access.prepost.PreAuthorize; | |
9 | +import org.springframework.validation.annotation.Validated; | |
10 | +import org.springframework.web.bind.annotation.*; | |
11 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
12 | +import org.thingsboard.server.common.data.yunteng.common.AddGroup; | |
13 | +import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; | |
14 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
15 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
16 | +import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | |
17 | +import org.thingsboard.server.common.data.yunteng.dto.task.TkTaskCenterDTO; | |
18 | +import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | |
19 | +import org.thingsboard.server.common.data.yunteng.enums.TargetTypeEnum; | |
20 | +import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | |
21 | +import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | |
22 | +import org.thingsboard.server.controller.BaseController; | |
23 | +import org.thingsboard.server.dao.yunteng.service.TkTaskCenterService; | |
24 | + | |
25 | +import java.util.HashMap; | |
26 | + | |
27 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; | |
28 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; | |
29 | + | |
30 | +@RestController | |
31 | +@RequestMapping("api/yt/task_center") | |
32 | +@Api(tags = {"任务中心"}) | |
33 | +@RequiredArgsConstructor | |
34 | +public class TkTaskCenterController extends BaseController { | |
35 | + private final TkTaskCenterService tkTaskCenterService; | |
36 | + | |
37 | + @GetMapping( | |
38 | + name = "page", | |
39 | + params = {PAGE_SIZE, PAGE}) | |
40 | + public TkPageData<TkTaskCenterDTO> pageDevice( | |
41 | + @RequestParam(PAGE_SIZE) int pageSize, | |
42 | + @RequestParam(PAGE) int page, | |
43 | + @RequestParam(value = "state", required = false) Integer state, | |
44 | + @RequestParam(value = "targetType", required = false) TargetTypeEnum targetType, | |
45 | + @RequestParam(value = ORDER_FILED, required = false) String orderBy, | |
46 | + @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType) | |
47 | + throws ThingsboardException { | |
48 | + HashMap<String, Object> queryMap = new HashMap<>(); | |
49 | + queryMap.put(PAGE_SIZE, pageSize); | |
50 | + queryMap.put(PAGE, page); | |
51 | + queryMap.put(ORDER_FILED, orderBy); | |
52 | + queryMap.put(ORDER_TYPE, orderType); | |
53 | + queryMap.put("state", state); | |
54 | + queryMap.put("targetType", targetType); | |
55 | + return tkTaskCenterService.taskCenterPage(queryMap, getCurrentUser().getCurrentTenantId()); | |
56 | + } | |
57 | + | |
58 | + @PostMapping("/add") | |
59 | + @ApiOperation(value = "新增任务中心") | |
60 | +// @PreAuthorize( | |
61 | +// "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:task_center:add:post'})") | |
62 | + public ResponseResult<TkTaskCenterDTO> save( | |
63 | + @RequestBody @Validated(AddGroup.class) TkTaskCenterDTO taskCenter) | |
64 | + throws ThingsboardException, SchedulerException { | |
65 | + if (StringUtils.isNotEmpty(taskCenter.getId())) { | |
66 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
67 | + } | |
68 | + return ResponseResult.success(saveOrUpdate(taskCenter)); | |
69 | + } | |
70 | + | |
71 | + @PutMapping("/update") | |
72 | +// @PreAuthorize( | |
73 | +// "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:task_center:update:update'})") | |
74 | + @ApiOperation(value = "编辑任务中心") | |
75 | + public ResponseResult<TkTaskCenterDTO> update( | |
76 | + @RequestBody @Validated(UpdateGroup.class) TkTaskCenterDTO taskCenter) | |
77 | + throws ThingsboardException, SchedulerException { | |
78 | + if (StringUtils.isEmpty(taskCenter.getId())) { | |
79 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
80 | + } | |
81 | + return ResponseResult.success(saveOrUpdate(taskCenter)); | |
82 | + } | |
83 | + | |
84 | + @PutMapping("/{id}/update/{state}") | |
85 | +// @PreAuthorize( | |
86 | +// "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:task_center:update:update'})") | |
87 | + @ApiOperation(value = "更新状态") | |
88 | + public ResponseResult<Boolean> updateState( | |
89 | + @PathVariable("id") String id, @PathVariable("state") Integer state) | |
90 | + throws ThingsboardException, SchedulerException { | |
91 | + if (StringUtils.isEmpty(id) || null == state) { | |
92 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
93 | + } | |
94 | + return ResponseResult.success( | |
95 | + tkTaskCenterService.updateState(id, state, getCurrentUser().getCurrentTenantId())); | |
96 | + } | |
97 | + | |
98 | + @DeleteMapping | |
99 | +// @PreAuthorize( | |
100 | +// "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:task_center:delete'})") | |
101 | + @ApiOperation(value = "删除任务中心") | |
102 | + public ResponseResult<Boolean> deleteTaskCenter(@RequestBody DeleteDTO deleteDTO) | |
103 | + throws ThingsboardException, SchedulerException { | |
104 | + deleteDTO.setTenantId(getCurrentUser().getCurrentTenantId()); | |
105 | + return ResponseResult.success(tkTaskCenterService.deleteTaskCenter(deleteDTO)); | |
106 | + } | |
107 | + | |
108 | + @PutMapping("/{id}/cancel/{tbDeviceId}") | |
109 | +// @PreAuthorize( | |
110 | +// "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:task_center:cancel'})") | |
111 | + @ApiOperation(value = "设备取消任务执行") | |
112 | + public ResponseResult<Boolean> cancelExecute( | |
113 | + @PathVariable("id") String taskId, @PathVariable("tbDeviceId") String tbDeviceId)throws ThingsboardException { | |
114 | + if(StringUtils.isEmpty(taskId) || StringUtils.isEmpty(tbDeviceId)){ | |
115 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
116 | + } | |
117 | + return ResponseResult.success( | |
118 | + tkTaskCenterService.cancelExecute( | |
119 | + taskId, tbDeviceId, getCurrentUser().getCurrentTenantId())); | |
120 | + } | |
121 | + | |
122 | + private TkTaskCenterDTO saveOrUpdate(TkTaskCenterDTO tkTaskCenterDTO) | |
123 | + throws ThingsboardException, SchedulerException { | |
124 | + tkTaskCenterDTO.setTenantId(getCurrentUser().getCurrentTenantId()); | |
125 | + return tkTaskCenterService.saveOrUpdateTaskCenter(tkTaskCenterDTO); | |
126 | + } | |
127 | +} | ... | ... |
1 | +package org.thingsboard.server.utils.yunteng; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import com.google.common.util.concurrent.FutureCallback; | |
5 | +import com.google.common.util.concurrent.Futures; | |
6 | +import com.google.common.util.concurrent.ListenableFuture; | |
7 | +import com.google.common.util.concurrent.MoreExecutors; | |
8 | +import lombok.RequiredArgsConstructor; | |
9 | +import lombok.extern.slf4j.Slf4j; | |
10 | +import org.checkerframework.checker.nullness.qual.Nullable; | |
11 | +import org.jetbrains.annotations.NotNull; | |
12 | +import org.springframework.beans.factory.annotation.Value; | |
13 | +import org.springframework.stereotype.Component; | |
14 | +import org.thingsboard.common.util.JacksonUtil; | |
15 | +import org.thingsboard.server.common.data.id.DeviceId; | |
16 | +import org.thingsboard.server.common.data.id.TenantId; | |
17 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
18 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
19 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | |
20 | +import org.thingsboard.server.common.data.yunteng.dto.task.TargetContentDTO; | |
21 | +import org.thingsboard.server.common.data.yunteng.dto.task.TkTaskCenterDTO; | |
22 | +import org.thingsboard.server.common.data.yunteng.enums.TargetTypeEnum; | |
23 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
24 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | |
25 | +import org.thingsboard.server.dao.yunteng.service.TkTaskCenterService; | |
26 | +import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; | |
27 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
28 | +import java.util.*; | |
29 | + | |
30 | +@Component("rpcCommandTask") | |
31 | +@RequiredArgsConstructor | |
32 | +@Slf4j | |
33 | +public class RpcCommandTask { | |
34 | + @Value("${server.rest.server_side_rpc.default_timeout:10000}") | |
35 | + protected long defaultTimeout; | |
36 | + | |
37 | + @Value("${server.rest.server_side_rpc.min_timeout:5000}") | |
38 | + protected long minTimeout; | |
39 | + | |
40 | + private final TkTaskCenterService tkTaskCenterService; | |
41 | + private final TbCoreDeviceRpcService tbCoreDeviceRpcService; | |
42 | + private final TkDeviceService tkDeviceService; | |
43 | + public void process(String taskCenterId) { | |
44 | + // 通过任务中心ID查询执行命令及执行对象 | |
45 | + ListenableFuture<TkTaskCenterDTO> future = | |
46 | + tkTaskCenterService.getTaskCenterInfoById(taskCenterId); | |
47 | + Futures.addCallback( | |
48 | + future, | |
49 | + new FutureCallback<>() { | |
50 | + @Override | |
51 | + public void onSuccess(@Nullable TkTaskCenterDTO tkTaskCenterDTO) { | |
52 | + if (null != tkTaskCenterDTO) { | |
53 | + TargetTypeEnum targetType = tkTaskCenterDTO.getTargetType(); | |
54 | + TargetContentDTO targetContent = tkTaskCenterDTO.getExecuteTarget(); | |
55 | + JsonNode cmdJsonNode = tkTaskCenterDTO.getExecuteContent().getPushContent(); | |
56 | + SecurityUser securityUser = new SecurityUser(); | |
57 | + // send cmd | |
58 | + if (targetContent.getData() != null && null != cmdJsonNode) { | |
59 | + String tenantId = tkTaskCenterDTO.getTenantId(); | |
60 | + if (targetType.equals(TargetTypeEnum.DEVICES)) { | |
61 | + for (String deviceId : targetContent.getData()) { | |
62 | + sendRpcCommand(cmdJsonNode, tenantId, deviceId, securityUser); | |
63 | + } | |
64 | + } else { | |
65 | + sendRpcCommandByProducts(targetContent, cmdJsonNode, tenantId, securityUser); | |
66 | + } | |
67 | + } | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + @Override | |
72 | + public void onFailure(@NotNull Throwable throwable) {} | |
73 | + }, | |
74 | + MoreExecutors.directExecutor()); | |
75 | + } | |
76 | + | |
77 | + private void sendRpcCommandByProducts( | |
78 | + TargetContentDTO targetContent, | |
79 | + JsonNode cmdJsonNode, | |
80 | + String tenantId, | |
81 | + SecurityUser securityUser) { | |
82 | + for (String deviceProfileId : targetContent.getData()) { | |
83 | + Futures.addCallback( | |
84 | + tkDeviceService.findDeviceListByDeviceProfileId(deviceProfileId, tenantId), | |
85 | + new FutureCallback<List<DeviceDTO>>() { | |
86 | + @Override | |
87 | + public void onSuccess(@Nullable List<DeviceDTO> deviceDTOS) { | |
88 | + if (null != deviceDTOS && !deviceDTOS.isEmpty()) { | |
89 | + for (DeviceDTO dto : deviceDTOS) { | |
90 | + Map<String, List<String>> map = targetContent.getCancelExecuteDevices(); | |
91 | + boolean needSendCommand = true; | |
92 | + if (null != map && !map.isEmpty() && !map.get(deviceProfileId).isEmpty()) { | |
93 | + for (String deviceId : map.get(deviceProfileId)) { | |
94 | + if (Objects.equals(deviceId, dto.getTbDeviceId())) { | |
95 | + needSendCommand = false; | |
96 | + break; | |
97 | + } | |
98 | + } | |
99 | + } | |
100 | + if (needSendCommand) { | |
101 | + sendRpcCommand(cmdJsonNode, tenantId, dto.getTbDeviceId(), securityUser); | |
102 | + } | |
103 | + } | |
104 | + } | |
105 | + } | |
106 | + | |
107 | + @Override | |
108 | + public void onFailure(@NotNull Throwable throwable) {} | |
109 | + }, | |
110 | + MoreExecutors.directExecutor()); | |
111 | + } | |
112 | + } | |
113 | + | |
114 | + private void sendRpcCommand( | |
115 | + JsonNode cmdJsonNode, String tenantId, String originateId, SecurityUser securityUser) { | |
116 | + JsonNode rpcCommand = cmdJsonNode.get(FastIotConstants.RPC_COMMAND); | |
117 | + ToDeviceRpcRequestBody body = | |
118 | + new ToDeviceRpcRequestBody("methodThingskit", JacksonUtil.toString(rpcCommand)); | |
119 | + DeviceId deviceId = new DeviceId(UUID.fromString(originateId)); | |
120 | + ToDeviceRpcRequest request = | |
121 | + new ToDeviceRpcRequest( | |
122 | + UUID.randomUUID(), | |
123 | + new TenantId(UUID.fromString(tenantId)), | |
124 | + deviceId, | |
125 | + true, | |
126 | + System.currentTimeMillis() + Math.max(minTimeout, defaultTimeout), | |
127 | + body, | |
128 | + false, | |
129 | + null, | |
130 | + null); | |
131 | + tbCoreDeviceRpcService.processRestApiRpcRequest( | |
132 | + request, | |
133 | + fromDeviceRpcResponse -> { | |
134 | + log.trace("Device renamed RPC with id: [{}] ", request.getId()); | |
135 | + }, | |
136 | + securityUser); | |
137 | + } | |
138 | +} | ... | ... |
... | ... | @@ -431,18 +431,20 @@ caffeine: |
431 | 431 | timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}" |
432 | 432 | maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}" |
433 | 433 | yunTengIotCache: |
434 | - timeToLiveInMinutes: 1440 | |
435 | - maxSize: 10000 | |
434 | + timeToLiveInMinutes: "${CACHE_SPECS_YUN_TENG_IOT_CACHE_TTL:1440}" | |
435 | + maxSize: "${CACHE_SPECS_YUN_TENG_IOT_CACHE_MAX_SIZE:10000}" | |
436 | 436 | userPermissionFor: |
437 | - timeToLiveInMinutes: 1440 | |
438 | - maxSize: 10000 | |
437 | + timeToLiveInMinutes: "${CACHE_SPECS_USER_PERMISSION_FOR_TTL:1440}" | |
438 | + maxSize: "${CACHE_SPECS_USER_PERMISSION_FOR_MAX_SIZE:10000}" | |
439 | 439 | mobileLoginSmsCode: |
440 | - timeToLiveInMinutes: 2 | |
441 | - maxSize: 10000 | |
440 | + timeToLiveInMinutes: "${CACHE_SPECS_MOBILE_LOGIN_SMS_CODE_TTL:2}" | |
441 | + maxSize: "${CACHE_SPECS_MOBILE_LOGIN_SMS_CODE_MAX_SIZE:10000}" | |
442 | 442 | thingsArea: |
443 | - timeToLiveInMinutes: 1440 | |
444 | - maxSize: 500 | |
445 | - | |
443 | + timeToLiveInMinutes: "${CACHE_SPECS_THINGS_AREA_TTL:1440}" | |
444 | + maxSize: "${CACHE_SPECS_THINGS_AREA_MAX_SIZE:500}" | |
445 | + taskCenterInfos: | |
446 | + timeToLiveInMinutes: "${CACHE_SPECS_TASK_CENTER_TTL:1440}" | |
447 | + maxSize: "${CACHE_SPECS_TASK_CENTER_MAX_SIZE:10000}" | |
446 | 448 | redis: |
447 | 449 | # standalone or cluster |
448 | 450 | connection: |
... | ... | @@ -905,8 +907,8 @@ transport: |
905 | 907 | psm_activity_timer: "${LWM2M_PSM_ACTIVITY_TIMER:10000}" |
906 | 908 | paging_transmission_window: "${LWM2M_PAGING_TRANSMISSION_WINDOW:10000}" |
907 | 909 | network_config: # In this section you can specify custom parameters for LwM2M network configuration and expose the env variables to configure outside |
908 | -# - key: "PROTOCOL_STAGE_THREAD_COUNT" | |
909 | -# value: "${LWM2M_PROTOCOL_STAGE_THREAD_COUNT:4}" | |
910 | + # - key: "PROTOCOL_STAGE_THREAD_COUNT" | |
911 | + # value: "${LWM2M_PROTOCOL_STAGE_THREAD_COUNT:4}" | |
910 | 912 | snmp: |
911 | 913 | enabled: "${SNMP_ENABLED:true}" |
912 | 914 | response_processing: |
... | ... | @@ -989,9 +991,9 @@ queue: |
989 | 991 | tb_ota_package: |
990 | 992 | - key: max.poll.records |
991 | 993 | value: "${TB_QUEUE_KAFKA_OTA_MAX_POLL_RECORDS:10}" |
992 | -# tb_rule_engine.sq: | |
993 | -# - key: max.poll.records | |
994 | -# value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}" | |
994 | + # tb_rule_engine.sq: | |
995 | + # - key: max.poll.records | |
996 | + # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}" | |
995 | 997 | other: # In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside |
996 | 998 | - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms |
997 | 999 | value: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) |
... | ... | @@ -1193,14 +1195,14 @@ file: |
1193 | 1195 | staticUrl: /static/files/** #oss静态访问路径 只有type = local需要 |
1194 | 1196 | randomFileName: ${file.storage.randomFileName} |
1195 | 1197 | minio: |
1196 | - minioUrl: ${MINIO_URL:https://dev.thingskit.com:9000} #minio储存地址 | |
1197 | - minioName: ${MINIO_NAME:test} #minio账户 | |
1198 | - minioPass: ${MINIO_PWD:test} #minio访问密码 | |
1199 | - bucketName: yunteng #minio储存桶名称 | |
1198 | + minioUrl: ${MINIO_URL:http://localhost:9000} #minio储存地址 | |
1199 | + minioName: ${MINIO_NAME:thingskit} #minio账户 | |
1200 | + minioPass: ${MINIO_PWD:thingskit} #minio访问密码 | |
1201 | + bucketName: yunteng #minio储存桶名称 | |
1200 | 1202 | randomFileName: ${file.storage.randomFileName} |
1201 | 1203 | account: |
1202 | 1204 | info: |
1203 | - emailSuffix: ${ACCOUNT_EMAIL_SUFFIX:yunteng.com} | |
1205 | + emailSuffix: ${ACCOUNT_EMAIL_SUFFIX:thingskit.com} | |
1204 | 1206 | defaultPassword: 123456 |
1205 | 1207 | reset: ${ACCOUNT_PASSWORD_FORCE_RESET:true} |
1206 | 1208 | third: | ... | ... |
... | ... | @@ -118,6 +118,8 @@ public final class ModelConstants { |
118 | 118 | public static final String TK_CUSTOMER_DEVICE_NAME = "tk_customer_device"; |
119 | 119 | /** 设备上下线记录表 */ |
120 | 120 | public static final String TK_DEVICE_STATE_LOG = "tk_device_state_log"; |
121 | + /** 任务中心表 */ | |
122 | + public static final String TK_TASK_CENTER_NAME = "tk_task_center"; | |
121 | 123 | } |
122 | 124 | |
123 | 125 | public static class TableFields { | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/task/TargetContentDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.task; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import java.util.List; | |
6 | +import java.util.Map; | |
7 | + | |
8 | +@Data | |
9 | +public class TargetContentDTO { | |
10 | + @ApiModelProperty(value = "执行的数据:目标类型如果是产品则为deviceProfileId,如果是设备则为tbDeviceId") | |
11 | + List<String> data; | |
12 | + | |
13 | + @ApiModelProperty(value = "设备类型") | |
14 | + String deviceType; | |
15 | + | |
16 | + @ApiModelProperty(value = "组织ID") | |
17 | + String organizationId; | |
18 | + | |
19 | + @ApiModelProperty(value = "设备配置ID") | |
20 | + String deviceProfileId; | |
21 | + | |
22 | + @ApiModelProperty(value = "取消执行的数据:只有目标类型是产品时才会有值,且List<String>里面为tbDeviceId") | |
23 | + Map<String, List<String>> cancelExecuteDevices; | |
24 | +} | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.dto.task; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import org.thingsboard.server.common.data.yunteng.enums.ExecuteTypeEnum; | |
6 | + | |
7 | +@Data | |
8 | +public class TaskExecuteTimeDTO { | |
9 | + | |
10 | + @ApiModelProperty(value = "执行类型") | |
11 | + private ExecuteTypeEnum type = ExecuteTypeEnum.CUSTOM; | |
12 | + | |
13 | + @ApiModelProperty(value = "周期类型") | |
14 | + private String periodType; | |
15 | + | |
16 | + @ApiModelProperty(value = "周期") | |
17 | + private String period; | |
18 | + | |
19 | + @ApiModelProperty(value = "时间") | |
20 | + private String time; | |
21 | + | |
22 | + @ApiModelProperty(value = "cron表达式") | |
23 | + private String cron; | |
24 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/task/TaskTypeDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.task; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import io.swagger.annotations.ApiModelProperty; | |
5 | +import lombok.Data; | |
6 | +import org.thingsboard.server.common.data.yunteng.enums.TaskTypeEnum; | |
7 | + | |
8 | +@Data | |
9 | +public class TaskTypeDTO { | |
10 | + @ApiModelProperty(value = "类型") | |
11 | + private TaskTypeEnum type = TaskTypeEnum.CUSTOM; | |
12 | + | |
13 | + @ApiModelProperty(value = "推送方式:MQTT TCP") | |
14 | + private String pushWay; | |
15 | + | |
16 | + @ApiModelProperty(value = "推送内容:即下发的命令内容") | |
17 | + private JsonNode pushContent; | |
18 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/task/TkTaskCenterDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.task; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import lombok.EqualsAndHashCode; | |
6 | +import org.thingsboard.server.common.data.yunteng.common.AddGroup; | |
7 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
8 | +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO; | |
9 | +import org.thingsboard.server.common.data.yunteng.enums.TargetTypeEnum; | |
10 | + | |
11 | +import javax.validation.constraints.NotEmpty; | |
12 | +import javax.validation.constraints.NotNull; | |
13 | + | |
14 | +@Data | |
15 | +@EqualsAndHashCode(callSuper = true) | |
16 | +public class TkTaskCenterDTO extends TenantDTO { | |
17 | + @NotEmpty(message = "任务名称不能为空或空字符串", groups = AddGroup.class) | |
18 | + @ApiModelProperty(value = "任务名称") | |
19 | + private String name; | |
20 | + | |
21 | + @ApiModelProperty(value = "目标类型") | |
22 | + private TargetTypeEnum targetType = TargetTypeEnum.DEVICES; | |
23 | + | |
24 | + @NotNull(message = "执行目标不能为空", groups = AddGroup.class) | |
25 | + @ApiModelProperty(value = "执行目标") | |
26 | + private TargetContentDTO executeTarget; | |
27 | + | |
28 | + @NotNull(message = "执行内容不能为空", groups = AddGroup.class) | |
29 | + @ApiModelProperty(value = "执行内容") | |
30 | + private TaskTypeDTO executeContent; | |
31 | + | |
32 | + @ApiModelProperty(value = "执行时间") | |
33 | + @NotNull(message = "执行时间不能为空", groups = AddGroup.class) | |
34 | + private TaskExecuteTimeDTO executeTime; | |
35 | + | |
36 | + @ApiModelProperty(value = "0禁用 1启用") | |
37 | + private Integer state = FastIotConstants.StateValue.DISABLE; | |
38 | + | |
39 | + @ApiModelProperty(value = "备注") | |
40 | + private String remark; | |
41 | +} | ... | ... |
... | ... | @@ -36,7 +36,6 @@ import org.thingsboard.server.common.data.id.DeviceId; |
36 | 36 | import org.thingsboard.server.common.data.id.OtaPackageId; |
37 | 37 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
38 | 38 | import org.thingsboard.server.common.data.rpc.RpcStatus; |
39 | -import org.thingsboard.server.common.data.yunteng.enums.TcpDataTypeEnum; | |
40 | 39 | import org.thingsboard.server.common.msg.EncryptionUtil; |
41 | 40 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
42 | 41 | import org.thingsboard.server.common.transport.SessionMsgListener; |
... | ... | @@ -59,7 +58,6 @@ import org.thingsboard.server.transport.tcp.util.ByteUtils; |
59 | 58 | |
60 | 59 | import javax.net.ssl.SSLPeerUnverifiedException; |
61 | 60 | import java.io.IOException; |
62 | -import java.io.UnsupportedEncodingException; | |
63 | 61 | import java.net.InetSocketAddress; |
64 | 62 | import java.security.cert.Certificate; |
65 | 63 | import java.security.cert.X509Certificate; |
... | ... | @@ -717,25 +715,15 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements |
717 | 715 | * @return |
718 | 716 | */ |
719 | 717 | private ChannelFuture pushDeviceMsg(ChannelHandlerContext ctx,TCPMessage tcp) { |
720 | - try { | |
721 | - String message = tcp.getMessage(); | |
722 | - byte[] payloadInBytes ; | |
723 | - if(deviceSessionCtx.getPayloadType().equals(TcpDataTypeEnum.HEX)){ | |
724 | - payloadInBytes = ByteUtils.hexStr2Bytes(message); | |
725 | - }else{ | |
726 | - payloadInBytes = message.getBytes(ByteUtils.UTF_8); | |
727 | - } | |
728 | -// ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); | |
729 | -// ByteBuf payload = ALLOCATOR.buffer(); | |
730 | -// payload.writeBytes(payloadInBytes); | |
731 | - ByteBuf payload = Unpooled.copiedBuffer(payloadInBytes); | |
732 | - | |
733 | - return ctx.writeAndFlush(payload); | |
734 | - } catch (UnsupportedEncodingException e) { | |
735 | - log.error(e.getMessage(),e); | |
736 | - throw new RuntimeException(e); | |
718 | + String message = tcp.getMessage().replace(" ",""); | |
719 | + ByteBuf buff = Unpooled.buffer(); | |
720 | + if(!message.matches("-?[0-9a-fA-F]+")){ | |
721 | + //不满足16进制将字符串转为16进制 | |
722 | + message = ByteUtils.stringEncodeToHex(message); | |
723 | + } | |
724 | + buff.writeBytes(ByteUtils.hexToByteArray(message)); | |
725 | + return ctx.writeAndFlush(buff); | |
737 | 726 | } |
738 | - } | |
739 | 727 | |
740 | 728 | private boolean isAckExpected(MqttMessage message) { |
741 | 729 | return message.fixedHeader().qosLevel().value() > 0; |
... | ... | @@ -758,4 +746,9 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements |
758 | 746 | ctx.close(); |
759 | 747 | } |
760 | 748 | |
749 | + public static void main(String[] args){ | |
750 | + System.out.printf("结果1:" + ByteUtils.stringEncodeToHex("hex转字符串")); | |
751 | + System.out.printf("结果2:" + ByteUtils.hexDecodeToString("686578e8bdace5ad97e7aca6e4b8b2")); | |
752 | + System.out.printf("匹配结果:" + "414244535344".matches("-?[0-9a-fA-F]+")); | |
753 | + } | |
761 | 754 | } | ... | ... |
1 | +package org.thingsboard.server.dao.yunteng.entities; | |
2 | + | |
3 | +import com.baomidou.mybatisplus.annotation.TableField; | |
4 | +import com.baomidou.mybatisplus.annotation.TableName; | |
5 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
6 | +import com.fasterxml.jackson.databind.JsonNode; | |
7 | +import lombok.Data; | |
8 | +import lombok.EqualsAndHashCode; | |
9 | +import org.apache.ibatis.type.EnumTypeHandler; | |
10 | +import org.thingsboard.server.common.data.yunteng.constant.ModelConstants; | |
11 | +import org.thingsboard.server.common.data.yunteng.enums.TargetTypeEnum; | |
12 | + | |
13 | +@Data | |
14 | +@EqualsAndHashCode(callSuper = true) | |
15 | +@TableName(value = ModelConstants.Table.TK_TASK_CENTER_NAME, autoResultMap = true) | |
16 | +public class TkTaskCenterEntity extends TenantBaseEntity { | |
17 | + private String name; | |
18 | + | |
19 | + @TableField(typeHandler = EnumTypeHandler.class) | |
20 | + private TargetTypeEnum targetType; | |
21 | + | |
22 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
23 | + private JsonNode executeTarget; | |
24 | + | |
25 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
26 | + private JsonNode executeContent; | |
27 | + | |
28 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
29 | + private JsonNode executeTime; | |
30 | + | |
31 | + private Integer state; | |
32 | + | |
33 | + private String remark; | |
34 | +} | ... | ... |
... | ... | @@ -2,6 +2,8 @@ package org.thingsboard.server.dao.yunteng.impl; |
2 | 2 | |
3 | 3 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
4 | 4 | import com.baomidou.mybatisplus.core.metadata.IPage; |
5 | +import com.google.common.util.concurrent.Futures; | |
6 | +import com.google.common.util.concurrent.ListenableFuture; | |
5 | 7 | import lombok.RequiredArgsConstructor; |
6 | 8 | import org.apache.commons.lang3.StringUtils; |
7 | 9 | import org.quartz.*; |
... | ... | @@ -15,12 +17,14 @@ import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; |
15 | 17 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
16 | 18 | import org.thingsboard.server.dao.util.yunteng.CronUtils; |
17 | 19 | import org.thingsboard.server.dao.util.yunteng.ScheduleUtils; |
20 | +import org.thingsboard.server.dao.yunteng.entities.BaseEntity; | |
18 | 21 | import org.thingsboard.server.dao.yunteng.entities.SysJobEntity; |
19 | 22 | import org.thingsboard.server.dao.yunteng.mapper.SysJobMapper; |
20 | 23 | import org.thingsboard.server.dao.yunteng.service.*; |
21 | 24 | |
22 | 25 | import javax.annotation.PostConstruct; |
23 | 26 | import java.util.*; |
27 | +import java.util.stream.Collectors; | |
24 | 28 | |
25 | 29 | /** 定时任务调度信息 服务层 */ |
26 | 30 | @Service |
... | ... | @@ -35,10 +39,12 @@ public class SysJobServiceImpl extends AbstractBaseService<SysJobMapper, SysJobE |
35 | 39 | scheduler.clear(); |
36 | 40 | List<SysJobEntity> jobList = new ArrayList<>(); |
37 | 41 | try { |
38 | - jobList.addAll(baseMapper.selectList( | |
39 | - new LambdaQueryWrapper<SysJobEntity>().eq(SysJobEntity::getStatus, StatusEnum.ENABLE.getIndex()))); | |
40 | - }catch (Exception e){ | |
41 | - //TODO: 兼容ThingsboardInstallApplication。执行数据库脚本前,会跑表不存在异常。 | |
42 | + jobList.addAll( | |
43 | + baseMapper.selectList( | |
44 | + new LambdaQueryWrapper<SysJobEntity>() | |
45 | + .eq(SysJobEntity::getStatus, StatusEnum.ENABLE.getIndex()))); | |
46 | + } catch (Exception e) { | |
47 | + // TODO: 兼容ThingsboardInstallApplication。执行数据库脚本前,会跑表不存在异常。 | |
42 | 48 | } |
43 | 49 | |
44 | 50 | for (SysJobEntity job : jobList) { |
... | ... | @@ -72,15 +78,22 @@ public class SysJobServiceImpl extends AbstractBaseService<SysJobMapper, SysJobE |
72 | 78 | public SysJobDTO selectJobById(String id) { |
73 | 79 | |
74 | 80 | return Optional.ofNullable( |
75 | - baseMapper.selectOne(new LambdaQueryWrapper<SysJobEntity>().eq(SysJobEntity::getId, id))) | |
81 | + baseMapper.selectOne( | |
82 | + new LambdaQueryWrapper<SysJobEntity>().eq(SysJobEntity::getId, id))) | |
76 | 83 | .map(obj -> obj.getDTO(SysJobDTO.class)) |
77 | 84 | .orElseThrow( |
78 | - () -> { | |
79 | - throw new TkDataValidationException( | |
80 | - ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage()); | |
81 | - }); | |
85 | + () -> | |
86 | + new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage())); | |
82 | 87 | } |
83 | 88 | |
89 | + @Override | |
90 | + public ListenableFuture<SysJobDTO> selectJobInfoById(String id) { | |
91 | + return Optional.ofNullable( | |
92 | + baseMapper.selectOne( | |
93 | + new LambdaQueryWrapper<SysJobEntity>().eq(SysJobEntity::getId, id))) | |
94 | + .map(obj -> Futures.immediateFuture(obj.getDTO(SysJobDTO.class))) | |
95 | + .orElse(Futures.immediateFuture(null)); | |
96 | + } | |
84 | 97 | /** |
85 | 98 | * 暂停任务 |
86 | 99 | * |
... | ... | @@ -130,6 +143,40 @@ public class SysJobServiceImpl extends AbstractBaseService<SysJobMapper, SysJobE |
130 | 143 | } |
131 | 144 | return rows > 0; |
132 | 145 | } |
146 | + /** | |
147 | + * 批量删除任务后,所对应的trigger也将被删除 | |
148 | + * | |
149 | + * @param sourceIds 源ID | |
150 | + * @param jobGroup 分组 | |
151 | + * @param tenantId 租户ID | |
152 | + */ | |
153 | + @Override | |
154 | + @Transactional(rollbackFor = Exception.class) | |
155 | + public boolean deleteJobs(List<String> sourceIds, String jobGroup, String tenantId) | |
156 | + throws SchedulerException { | |
157 | + List<String> jobIds = | |
158 | + Optional.ofNullable( | |
159 | + baseMapper.selectList( | |
160 | + new LambdaQueryWrapper<SysJobEntity>() | |
161 | + .eq(SysJobEntity::getTenantId, tenantId) | |
162 | + .eq(SysJobEntity::getJobGroup, jobGroup) | |
163 | + .in(SysJobEntity::getSourceId, sourceIds))) | |
164 | + .map(list -> list.stream().map(BaseEntity::getId).collect(Collectors.toList())) | |
165 | + .orElse(null); | |
166 | + if (null != jobIds && !jobIds.isEmpty()) { | |
167 | + int rows = baseMapper.deleteBatchIds(jobIds); | |
168 | + if (rows > 0) { | |
169 | + List<JobKey> jobKeys = new ArrayList<>(); | |
170 | + for (String jobId : jobIds) { | |
171 | + jobKeys.add(ScheduleUtils.getJobKey(jobId, jobGroup)); | |
172 | + } | |
173 | + if (!jobKeys.isEmpty()) { | |
174 | + return scheduler.deleteJobs(jobKeys); | |
175 | + } | |
176 | + } | |
177 | + } | |
178 | + return false; | |
179 | + } | |
133 | 180 | |
134 | 181 | /** |
135 | 182 | * 批量删除调度信息 | ... | ... |
1 | +package org.thingsboard.server.dao.yunteng.impl; | |
2 | + | |
3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
4 | +import com.baomidou.mybatisplus.core.metadata.IPage; | |
5 | +import com.fasterxml.jackson.databind.JsonNode; | |
6 | +import com.google.common.util.concurrent.Futures; | |
7 | +import com.google.common.util.concurrent.ListenableFuture; | |
8 | +import lombok.RequiredArgsConstructor; | |
9 | +import lombok.extern.slf4j.Slf4j; | |
10 | +import org.apache.commons.lang3.StringUtils; | |
11 | +import org.quartz.SchedulerException; | |
12 | +import org.springframework.stereotype.Service; | |
13 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
14 | +import org.thingsboard.server.common.data.yunteng.core.cache.CacheUtils; | |
15 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
16 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
17 | +import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | |
18 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | |
19 | +import org.thingsboard.server.common.data.yunteng.dto.SysJobDTO; | |
20 | +import org.thingsboard.server.common.data.yunteng.dto.task.TargetContentDTO; | |
21 | +import org.thingsboard.server.common.data.yunteng.dto.task.TaskExecuteTimeDTO; | |
22 | +import org.thingsboard.server.common.data.yunteng.dto.task.TaskTypeDTO; | |
23 | +import org.thingsboard.server.common.data.yunteng.dto.task.TkTaskCenterDTO; | |
24 | +import org.thingsboard.server.common.data.yunteng.enums.JobGroupEnum; | |
25 | +import org.thingsboard.server.common.data.yunteng.enums.TargetTypeEnum; | |
26 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | |
27 | +import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | |
28 | +import org.thingsboard.server.dao.yunteng.entities.TkTaskCenterEntity; | |
29 | +import org.thingsboard.server.dao.yunteng.mapper.TaskCenterMapper; | |
30 | +import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; | |
31 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | |
32 | +import org.thingsboard.server.dao.yunteng.service.TkSysJobService; | |
33 | +import org.thingsboard.server.dao.yunteng.service.TkTaskCenterService; | |
34 | + | |
35 | +import javax.transaction.Transactional; | |
36 | +import java.util.*; | |
37 | +import java.util.stream.Collectors; | |
38 | + | |
39 | +@Service | |
40 | +@RequiredArgsConstructor | |
41 | +@Slf4j | |
42 | +public class TkTaskCenterServiceImpl | |
43 | + extends AbstractBaseService<TaskCenterMapper, TkTaskCenterEntity> | |
44 | + implements TkTaskCenterService { | |
45 | + private final TkSysJobService tkSysJobService; | |
46 | + private final TkDeviceService tkDeviceService; | |
47 | + private final CacheUtils cacheUtils; | |
48 | + | |
49 | + @Override | |
50 | + public TkPageData<TkTaskCenterDTO> taskCenterPage(Map<String, Object> queryMap, String tenantId) { | |
51 | + Integer state = (Integer) queryMap.get("state"); | |
52 | + TargetTypeEnum targetTypeEnum = (TargetTypeEnum) queryMap.get("targetType"); | |
53 | + IPage<TkTaskCenterEntity> page = | |
54 | + getPage(queryMap, FastIotConstants.DefaultOrder.CREATE_TIME, false); | |
55 | + IPage<TkTaskCenterEntity> taskCenterIPage = | |
56 | + baseMapper.selectPage( | |
57 | + page, | |
58 | + new LambdaQueryWrapper<TkTaskCenterEntity>() | |
59 | + .eq(TkTaskCenterEntity::getTenantId, tenantId) | |
60 | + .eq(null != state, TkTaskCenterEntity::getState, state) | |
61 | + .eq(null != targetTypeEnum, TkTaskCenterEntity::getTargetType, targetTypeEnum)); | |
62 | + if (!taskCenterIPage.getRecords().isEmpty()) { | |
63 | + List<TkTaskCenterDTO> listRecords = | |
64 | + taskCenterIPage.getRecords().stream() | |
65 | + .map( | |
66 | + entity -> { | |
67 | + TkTaskCenterDTO dto = entity.getDTO(TkTaskCenterDTO.class); | |
68 | + dto.setExecuteContent( | |
69 | + JacksonUtil.convertValue(entity.getExecuteContent(), TaskTypeDTO.class)); | |
70 | + dto.setExecuteTime( | |
71 | + JacksonUtil.convertValue( | |
72 | + entity.getExecuteTime(), TaskExecuteTimeDTO.class)); | |
73 | + dto.setExecuteTarget( | |
74 | + JacksonUtil.convertValue( | |
75 | + entity.getExecuteTarget(), TargetContentDTO.class)); | |
76 | + return dto; | |
77 | + }) | |
78 | + .collect(Collectors.toList()); | |
79 | + return new TkPageData<>(listRecords, taskCenterIPage.getTotal()); | |
80 | + } | |
81 | + return getPageData(taskCenterIPage, TkTaskCenterDTO.class); | |
82 | + } | |
83 | + | |
84 | + @Override | |
85 | + @Transactional | |
86 | + public TkTaskCenterDTO saveOrUpdateTaskCenter(TkTaskCenterDTO tkTaskCenterDTO) { | |
87 | + TkTaskCenterEntity entity = tkTaskCenterDTO.getEntity(TkTaskCenterEntity.class); | |
88 | + JsonNode jsonNode = tkTaskCenterDTO.getExecuteContent().getPushContent(); | |
89 | + if (null == jsonNode || null == jsonNode.get(FastIotConstants.RPC_COMMAND)) { | |
90 | + throw new TkDataValidationException(ErrorMessage.EXECUTE_COMMAND_IS_NULL.getMessage()); | |
91 | + } | |
92 | + TaskExecuteTimeDTO executeTime = tkTaskCenterDTO.getExecuteTime(); | |
93 | + if (null == executeTime || StringUtils.isEmpty(executeTime.getCron())) { | |
94 | + throw new TkDataValidationException(ErrorMessage.CRON_INVALID.getMessage()); | |
95 | + } | |
96 | + entity.setExecuteContent( | |
97 | + JacksonUtil.convertValue(tkTaskCenterDTO.getExecuteContent(), JsonNode.class)); | |
98 | + entity.setExecuteTarget( | |
99 | + JacksonUtil.convertValue(tkTaskCenterDTO.getExecuteTarget(), JsonNode.class)); | |
100 | + entity.setExecuteTime( | |
101 | + JacksonUtil.convertValue(tkTaskCenterDTO.getExecuteTime(), JsonNode.class)); | |
102 | + if (StringUtils.isEmpty(tkTaskCenterDTO.getId())) { | |
103 | + entity.setState(FastIotConstants.StateValue.DISABLE); | |
104 | + baseMapper.insert(entity); | |
105 | + } else { | |
106 | + TkTaskCenterDTO queryDTO = | |
107 | + findTaskCenterInfoById(tkTaskCenterDTO.getId(), tkTaskCenterDTO.getTenantId()); | |
108 | + if (null == queryDTO) { | |
109 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage()); | |
110 | + } | |
111 | + // 只有通过修改状态接口,才可以修改状态 | |
112 | + entity.setState(null); | |
113 | + baseMapper.updateById(entity); | |
114 | + updateTaskCenterCache(entity); | |
115 | + } | |
116 | + return tkTaskCenterDTO; | |
117 | + } | |
118 | + | |
119 | + @Override | |
120 | + @Transactional | |
121 | + public boolean deleteTaskCenter(DeleteDTO deleteDTO) throws SchedulerException { | |
122 | + List<TkTaskCenterEntity> list = | |
123 | + baseMapper.selectList( | |
124 | + new LambdaQueryWrapper<TkTaskCenterEntity>() | |
125 | + .eq(TkTaskCenterEntity::getTenantId, deleteDTO.getTenantId()) | |
126 | + .in(TkTaskCenterEntity::getId, deleteDTO.getIds())); | |
127 | + if (null == list || list.isEmpty()) { | |
128 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
129 | + } | |
130 | + List<String> sourceIds = new ArrayList<>(); | |
131 | + for (TkTaskCenterEntity entity : list) { | |
132 | + // 任务中心,是启用状态不能删除 | |
133 | + if (entity.getState() == FastIotConstants.StateValue.ENABLE) { | |
134 | + throw new TkDataValidationException( | |
135 | + String.format( | |
136 | + ErrorMessage.DATA_STATE_ENABLE_NOT_DELETE.getMessage(), entity.getName())); | |
137 | + } | |
138 | + sourceIds.add(entity.getId()); | |
139 | + } | |
140 | + int result = baseMapper.deleteBatchIds(sourceIds); | |
141 | + if (result > FastIotConstants.MagicNumber.ZERO) { | |
142 | + return tkSysJobService.deleteJobs( | |
143 | + sourceIds, JobGroupEnum.TASK_CENTER.name(), deleteDTO.getTenantId()); | |
144 | + } | |
145 | + // 移除任务中心缓存 | |
146 | + for (String taskCenterId : sourceIds) { | |
147 | + String key = FastIotConstants.CacheConfigKey.TASK_CENTER_INFOS + taskCenterId; | |
148 | + cacheUtils.invalidate(key); | |
149 | + } | |
150 | + return false; | |
151 | + } | |
152 | + | |
153 | + @Override | |
154 | + public TkTaskCenterDTO findTaskCenterInfoById(String id, String tenantId) { | |
155 | + if (StringUtils.isEmpty(id) || StringUtils.isEmpty(tenantId)) { | |
156 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
157 | + } | |
158 | + TkTaskCenterEntity entity = | |
159 | + baseMapper.selectOne( | |
160 | + new LambdaQueryWrapper<TkTaskCenterEntity>() | |
161 | + .eq(TkTaskCenterEntity::getTenantId, tenantId) | |
162 | + .eq(TkTaskCenterEntity::getId, id)); | |
163 | + return Optional.ofNullable(entity) | |
164 | + .map( | |
165 | + object -> { | |
166 | + TkTaskCenterDTO tkTaskCenterDTO = object.getDTO(TkTaskCenterDTO.class); | |
167 | + tkTaskCenterDTO.setExecuteTarget( | |
168 | + JacksonUtil.convertValue(object.getExecuteTarget(), TargetContentDTO.class)); | |
169 | + tkTaskCenterDTO.setExecuteContent( | |
170 | + JacksonUtil.convertValue(object.getExecuteContent(), TaskTypeDTO.class)); | |
171 | + tkTaskCenterDTO.setExecuteTime( | |
172 | + JacksonUtil.convertValue(object.getExecuteTime(), TaskExecuteTimeDTO.class)); | |
173 | + return tkTaskCenterDTO; | |
174 | + }) | |
175 | + .orElse(null); | |
176 | + } | |
177 | + | |
178 | + @Override | |
179 | + public ListenableFuture<TkTaskCenterDTO> getTaskCenterInfoById(String id) { | |
180 | + if (StringUtils.isEmpty(id)) { | |
181 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
182 | + } | |
183 | + String key = FastIotConstants.CacheConfigKey.TASK_CENTER_INFOS + id; | |
184 | + Optional<TkTaskCenterEntity> entityCache = cacheUtils.get(key); | |
185 | + TkTaskCenterEntity entity = | |
186 | + entityCache.orElseGet( | |
187 | + () -> | |
188 | + baseMapper.selectOne( | |
189 | + new LambdaQueryWrapper<TkTaskCenterEntity>() | |
190 | + .eq(TkTaskCenterEntity::getId, id))); | |
191 | + return Optional.ofNullable(entity) | |
192 | + .map( | |
193 | + object -> { | |
194 | + TkTaskCenterDTO tkTaskCenterDTO = object.getDTO(TkTaskCenterDTO.class); | |
195 | + tkTaskCenterDTO.setExecuteTarget( | |
196 | + JacksonUtil.convertValue(object.getExecuteTarget(), TargetContentDTO.class)); | |
197 | + tkTaskCenterDTO.setExecuteContent( | |
198 | + JacksonUtil.convertValue(object.getExecuteContent(), TaskTypeDTO.class)); | |
199 | + tkTaskCenterDTO.setExecuteTime( | |
200 | + JacksonUtil.convertValue(object.getExecuteTime(), TaskExecuteTimeDTO.class)); | |
201 | + return Futures.immediateFuture(tkTaskCenterDTO); | |
202 | + }) | |
203 | + .orElse(null); | |
204 | + } | |
205 | + | |
206 | + @Override | |
207 | + @Transactional | |
208 | + public boolean updateState(String id, Integer state, String tenantId) throws SchedulerException { | |
209 | + TkTaskCenterEntity entity = | |
210 | + baseMapper.selectOne( | |
211 | + new LambdaQueryWrapper<TkTaskCenterEntity>().eq(TkTaskCenterEntity::getId, id)); | |
212 | + boolean result = false; | |
213 | + if (null == entity) { | |
214 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
215 | + } | |
216 | + if (!Objects.equals(entity.getTenantId(), tenantId)) { | |
217 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage()); | |
218 | + } | |
219 | + entity.setState(state); | |
220 | + if (baseMapper.updateById(entity) > FastIotConstants.MagicNumber.ZERO) { | |
221 | + String sourceId = entity.getId(); | |
222 | + SysJobDTO queryJob = tkSysJobService.findSysJobBySourceId(sourceId); | |
223 | + if (null == queryJob) { | |
224 | + TaskExecuteTimeDTO executeTime = | |
225 | + JacksonUtil.convertValue(entity.getExecuteTime(), TaskExecuteTimeDTO.class); | |
226 | + if (null == executeTime || StringUtils.isEmpty(executeTime.getCron())) { | |
227 | + throw new TkDataValidationException(ErrorMessage.CRON_INVALID.getMessage()); | |
228 | + } | |
229 | + queryJob = new SysJobDTO(); | |
230 | + queryJob.setSourceId(sourceId); | |
231 | + queryJob.setJobName(entity.getName()); | |
232 | + queryJob.setInvokeTarget("rpcCommandTask.process('" + sourceId + "')"); | |
233 | + queryJob.setJobGroup(JobGroupEnum.TASK_CENTER.name()); | |
234 | + queryJob.setCronExpression(executeTime.getCron()); | |
235 | + queryJob.setTenantId(entity.getTenantId()); | |
236 | + } | |
237 | + queryJob.setStatus(state); | |
238 | + tkSysJobService.saveOrUpdateJob(queryJob); | |
239 | + result = true; | |
240 | + } | |
241 | + return result; | |
242 | + } | |
243 | + | |
244 | + @Override | |
245 | + @Transactional | |
246 | + public boolean cancelExecute(String id, String tbDeviceId, String tenantId) { | |
247 | + boolean result = false; | |
248 | + TkTaskCenterEntity entity = baseMapper.selectById(id); | |
249 | + if (entity != null) { | |
250 | + if (!Objects.equals(entity.getTenantId(), tenantId)) { | |
251 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage()); | |
252 | + } | |
253 | + TargetContentDTO targetContent = | |
254 | + JacksonUtil.convertValue(entity.getExecuteTarget(), TargetContentDTO.class); | |
255 | + if (null != targetContent) { | |
256 | + // 按设备执行 | |
257 | + if (Objects.equals(entity.getTargetType(), TargetTypeEnum.DEVICES)) { | |
258 | + List<String> data = targetContent.getData(); | |
259 | + List<String> removeResult = new ArrayList<>(); | |
260 | + if (null != data && !data.isEmpty()) { | |
261 | + for (String deviceId : data) { | |
262 | + if (!Objects.equals(deviceId, tbDeviceId)) { | |
263 | + removeResult.add(deviceId); | |
264 | + } | |
265 | + } | |
266 | + targetContent.setData(removeResult); | |
267 | + } | |
268 | + } else { | |
269 | + // 按产品执行 | |
270 | + Map<String, List<String>> map = targetContent.getCancelExecuteDevices(); | |
271 | + DeviceDTO deviceDTO = tkDeviceService.findDeviceInfoByTbDeviceId(tenantId, tbDeviceId); | |
272 | + for (String key : map.keySet()) { | |
273 | + if (Objects.equals(key, deviceDTO.getDeviceProfileId())) { | |
274 | + map.get(key).add(tbDeviceId); | |
275 | + break; | |
276 | + } | |
277 | + } | |
278 | + } | |
279 | + entity.setExecuteTarget(JacksonUtil.convertValue(targetContent, JsonNode.class)); | |
280 | + baseMapper.updateById(entity); | |
281 | + updateTaskCenterCache(entity); | |
282 | + } | |
283 | + } | |
284 | + return result; | |
285 | + } | |
286 | + | |
287 | + private void updateTaskCenterCache(TkTaskCenterEntity entity) { | |
288 | + String key = FastIotConstants.CacheConfigKey.TASK_CENTER_INFOS + entity.getId(); | |
289 | + Optional<TkTaskCenterEntity> entityCache = cacheUtils.get(key); | |
290 | + if (entityCache.isPresent()) { | |
291 | + cacheUtils.put(key, entity); | |
292 | + } | |
293 | + } | |
294 | +} | ... | ... |
1 | +package org.thingsboard.server.dao.yunteng.mapper; | |
2 | + | |
3 | +import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |
4 | +import org.apache.ibatis.annotations.Mapper; | |
5 | +import org.thingsboard.server.dao.yunteng.entities.TkTaskCenterEntity; | |
6 | + | |
7 | +@Mapper | |
8 | +public interface TaskCenterMapper extends BaseMapper<TkTaskCenterEntity> { | |
9 | +} | ... | ... |
1 | 1 | package org.thingsboard.server.dao.yunteng.service; |
2 | + | |
3 | +import com.google.common.util.concurrent.ListenableFuture; | |
2 | 4 | import org.quartz.SchedulerException; |
3 | 5 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; |
4 | 6 | import org.thingsboard.server.common.data.yunteng.dto.SysJobDTO; |
5 | 7 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
8 | +import java.util.List; | |
6 | 9 | import java.util.Map; |
7 | 10 | |
8 | -/** | |
9 | - * 定时任务调度信息信息 服务层 | |
10 | - */ | |
11 | -public interface TkSysJobService | |
12 | -{ | |
13 | - /** | |
14 | - * 获取quartz调度器的计划任务 | |
15 | - * | |
16 | - * @param queryMap 查询参数 | |
17 | - * @return 分页数据 | |
18 | - */ | |
19 | - TkPageData<SysJobDTO> sysJobPage(Map<String, Object> queryMap); | |
20 | - | |
21 | - /** | |
22 | - * 通过调度任务ID查询调度信息 | |
23 | - * | |
24 | - * @param id 调度任务ID | |
25 | - * @return 调度任务对象信息 | |
26 | - */ | |
27 | - SysJobDTO selectJobById(String id); | |
11 | +/** 定时任务调度信息信息 服务层 */ | |
12 | +public interface TkSysJobService { | |
13 | + /** | |
14 | + * 获取quartz调度器的计划任务 | |
15 | + * | |
16 | + * @param queryMap 查询参数 | |
17 | + * @return 分页数据 | |
18 | + */ | |
19 | + TkPageData<SysJobDTO> sysJobPage(Map<String, Object> queryMap); | |
28 | 20 | |
29 | - /** | |
30 | - * 暂停任务 | |
31 | - * | |
32 | - * @param job 调度信息 | |
33 | - * @return 结果 | |
34 | - */ | |
35 | - SysJobDTO pauseJob(SysJobDTO job) throws SchedulerException; | |
21 | + /** | |
22 | + * 通过调度任务ID查询调度信息 | |
23 | + * | |
24 | + * @param id 调度任务ID | |
25 | + * @return 调度任务对象信息 | |
26 | + */ | |
27 | + SysJobDTO selectJobById(String id); | |
28 | + /** | |
29 | + * 通过调度任务ID查询调度信息 | |
30 | + * | |
31 | + * @param id 调度任务ID | |
32 | + * @return 调度任务对象信息 | |
33 | + */ | |
34 | + ListenableFuture<SysJobDTO> selectJobInfoById(String id); | |
35 | + /** | |
36 | + * 暂停任务 | |
37 | + * | |
38 | + * @param job 调度信息 | |
39 | + * @return 结果 | |
40 | + */ | |
41 | + SysJobDTO pauseJob(SysJobDTO job) throws SchedulerException; | |
36 | 42 | |
37 | - /** | |
38 | - * 恢复任务 | |
39 | - * | |
40 | - * @param job 调度信息 | |
41 | - * @return 结果 | |
42 | - */ | |
43 | - SysJobDTO resumeJob(SysJobDTO job) throws SchedulerException; | |
43 | + /** | |
44 | + * 恢复任务 | |
45 | + * | |
46 | + * @param job 调度信息 | |
47 | + * @return 结果 | |
48 | + */ | |
49 | + SysJobDTO resumeJob(SysJobDTO job) throws SchedulerException; | |
44 | 50 | |
45 | - /** | |
46 | - * 删除任务后,所对应的trigger也将被删除 | |
47 | - * | |
48 | - * @param job 调度信息 | |
49 | - * @return 结果 | |
50 | - */ | |
51 | - boolean deleteJob(SysJobDTO job) throws SchedulerException; | |
51 | + /** | |
52 | + * 删除任务后,所对应的trigger也将被删除 | |
53 | + * | |
54 | + * @param job 调度信息 | |
55 | + * @return 结果 | |
56 | + */ | |
57 | + boolean deleteJob(SysJobDTO job) throws SchedulerException; | |
52 | 58 | |
53 | - /** | |
54 | - * 批量删除调度信息 | |
55 | - * | |
56 | - * @param deleteDTO 需要删除的数据ID | |
57 | - * @return 结果 true删除成功 false删除失败 | |
58 | - */ | |
59 | - boolean deleteJobByIds(DeleteDTO deleteDTO) throws SchedulerException; | |
59 | + /** | |
60 | + * 批量删除定时任务 | |
61 | + * | |
62 | + * @param sourceIds 来源ID即业务ID | |
63 | + * @param jobGroup 定时任务分组 | |
64 | + * @param tenantId 租户ID | |
65 | + * @return true成功 false失败 | |
66 | + */ | |
67 | + boolean deleteJobs(List<String> sourceIds, String jobGroup, String tenantId) | |
68 | + throws SchedulerException; | |
60 | 69 | |
61 | - /** | |
62 | - * 任务调度状态修改 | |
63 | - * | |
64 | - * @param job 调度信息 | |
65 | - * @return 结果 | |
66 | - */ | |
67 | - SysJobDTO updateSysJobStatus(SysJobDTO job) throws SchedulerException; | |
70 | + /** | |
71 | + * 批量删除调度信息 | |
72 | + * | |
73 | + * @param deleteDTO 需要删除的数据ID | |
74 | + * @return 结果 true删除成功 false删除失败 | |
75 | + */ | |
76 | + boolean deleteJobByIds(DeleteDTO deleteDTO) throws SchedulerException; | |
68 | 77 | |
69 | - /** | |
70 | - * 立即运行任务 | |
71 | - * | |
72 | - * @param job 调度信息 | |
73 | - * @return 结果 | |
74 | - */ | |
75 | - boolean run(SysJobDTO job) throws SchedulerException; | |
78 | + /** | |
79 | + * 任务调度状态修改 | |
80 | + * | |
81 | + * @param job 调度信息 | |
82 | + * @return 结果 | |
83 | + */ | |
84 | + SysJobDTO updateSysJobStatus(SysJobDTO job) throws SchedulerException; | |
76 | 85 | |
77 | - /** | |
78 | - * 新增或编辑任务 | |
79 | - * | |
80 | - * @param job 调度信息 | |
81 | - * @return 结果 | |
82 | - */ | |
83 | - SysJobDTO saveOrUpdateJob(SysJobDTO job) throws SchedulerException; | |
86 | + /** | |
87 | + * 立即运行任务 | |
88 | + * | |
89 | + * @param job 调度信息 | |
90 | + * @return 结果 | |
91 | + */ | |
92 | + boolean run(SysJobDTO job) throws SchedulerException; | |
84 | 93 | |
94 | + /** | |
95 | + * 新增或编辑任务 | |
96 | + * | |
97 | + * @param job 调度信息 | |
98 | + * @return 结果 | |
99 | + */ | |
100 | + SysJobDTO saveOrUpdateJob(SysJobDTO job) throws SchedulerException; | |
85 | 101 | |
86 | - /** | |
87 | - * 校验cron表达式是否有效 | |
88 | - * | |
89 | - * @param cronExpression 表达式 | |
90 | - * @return 结果 | |
91 | - */ | |
92 | - boolean checkCronExpressionIsValid(String cronExpression); | |
102 | + /** | |
103 | + * 校验cron表达式是否有效 | |
104 | + * | |
105 | + * @param cronExpression 表达式 | |
106 | + * @return 结果 | |
107 | + */ | |
108 | + boolean checkCronExpressionIsValid(String cronExpression); | |
93 | 109 | |
94 | - /** | |
95 | - * 通过源ID找到定时任务信息 | |
96 | - * @param sourceId 源ID | |
97 | - * @return 定时任务信息 | |
98 | - */ | |
99 | - SysJobDTO findSysJobBySourceId(String sourceId); | |
100 | -} | |
\ No newline at end of file | ||
110 | + /** | |
111 | + * 通过源ID找到定时任务信息 | |
112 | + * | |
113 | + * @param sourceId 源ID | |
114 | + * @return 定时任务信息 | |
115 | + */ | |
116 | + SysJobDTO findSysJobBySourceId(String sourceId); | |
117 | +} | ... | ... |
1 | +package org.thingsboard.server.dao.yunteng.service; | |
2 | + | |
3 | +import com.google.common.util.concurrent.ListenableFuture;import org.quartz.SchedulerException; | |
4 | +import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | |
5 | +import org.thingsboard.server.common.data.yunteng.dto.task.TkTaskCenterDTO; | |
6 | +import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | |
7 | +import java.util.Map; | |
8 | + | |
9 | +public interface TkTaskCenterService { | |
10 | + /** | |
11 | + * 任务中心分页 | |
12 | + * | |
13 | + * @param queryMap 分页查询条件 | |
14 | + * @param tenantId 租户ID | |
15 | + * @return 分页数据 | |
16 | + */ | |
17 | + TkPageData<TkTaskCenterDTO> taskCenterPage(Map<String, Object> queryMap, String tenantId); | |
18 | + | |
19 | + /** | |
20 | + * 修改或保存任务中心 | |
21 | + * | |
22 | + * @param tkTaskCenterDTO 任务中心信息 | |
23 | + */ | |
24 | + TkTaskCenterDTO saveOrUpdateTaskCenter(TkTaskCenterDTO tkTaskCenterDTO) throws SchedulerException; | |
25 | + | |
26 | + /** | |
27 | + * 删除任务中心 | |
28 | + * | |
29 | + * @param deleteDTO 删除IDS | |
30 | + */ | |
31 | + boolean deleteTaskCenter(DeleteDTO deleteDTO)throws SchedulerException; | |
32 | + | |
33 | + /** | |
34 | + * 根据任务中心ID,查询任务信息 | |
35 | + * @param id 任务中心ID | |
36 | + * @param tenantId 租户ID | |
37 | + * @return 任务信息 | |
38 | + */ | |
39 | + TkTaskCenterDTO findTaskCenterInfoById(String id,String tenantId); | |
40 | + | |
41 | + /** | |
42 | + * 根据任务中心ID,异步查询任务信息 | |
43 | + * @param id 任务中心ID | |
44 | + * @return 任务信息 | |
45 | + */ | |
46 | + ListenableFuture<TkTaskCenterDTO> getTaskCenterInfoById(String id); | |
47 | + | |
48 | + /** | |
49 | + * 启用禁用任务中心 | |
50 | + * @param id 任务中心ID | |
51 | + * @param state 1启用 0禁用 | |
52 | + * @param tenantId 租户ID | |
53 | + * @return true成功 false失败 | |
54 | + */ | |
55 | + boolean updateState(String id,Integer state,String tenantId)throws SchedulerException; | |
56 | + | |
57 | + /** | |
58 | + * 设备取消任务执行 | |
59 | + * @param id 任务ID | |
60 | + * @param tbDeviceId 设备ID | |
61 | + * @param tenantId 租户ID | |
62 | + * @return true成功 false失败 | |
63 | + */ | |
64 | + boolean cancelExecute(String id,String tbDeviceId,String tenantId); | |
65 | +} | ... | ... |