Showing
41 changed files
with
568 additions
and
160 deletions
... | ... | @@ -64,10 +64,15 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
64 | 64 | import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; |
65 | 65 | import org.thingsboard.server.common.data.rule.RuleChainType; |
66 | 66 | import org.thingsboard.server.common.data.script.ScriptLanguage; |
67 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
68 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
69 | +import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | |
67 | 70 | import org.thingsboard.server.common.msg.TbMsg; |
68 | 71 | import org.thingsboard.server.common.msg.TbMsgDataType; |
69 | 72 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
70 | 73 | import org.thingsboard.server.dao.event.EventService; |
74 | +import org.thingsboard.server.dao.yunteng.service.ConvertConfigService; | |
75 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; | |
71 | 76 | import org.thingsboard.server.queue.util.TbCoreComponent; |
72 | 77 | import org.thingsboard.server.service.rule.TbRuleChainService; |
73 | 78 | import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
... | ... | @@ -152,6 +157,12 @@ public class RuleChainController extends BaseController { |
152 | 157 | @Autowired(required = false) |
153 | 158 | private ActorSystemContext actorContext; |
154 | 159 | |
160 | + @Autowired | |
161 | + private TkDeviceProfileService tkDeviceProfileService; | |
162 | + | |
163 | + @Autowired | |
164 | + private ConvertConfigService convertConfigService; | |
165 | + | |
155 | 166 | @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled}") |
156 | 167 | private boolean debugPerTenantEnabled; |
157 | 168 | |
... | ... | @@ -325,7 +336,15 @@ public class RuleChainController extends BaseController { |
325 | 336 | checkParameter(RULE_CHAIN_ID, strRuleChainId); |
326 | 337 | RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); |
327 | 338 | RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.DELETE); |
339 | + //判断规则链是否被产品在使用 thingskit | |
340 | + String tenantId = getCurrentUser().getCurrentTenantId(); | |
341 | + List<String> deviceProfileNames = tkDeviceProfileService.checkRuleChainIdUsedByDeviceProfiles(tenantId,ruleChainId.getId()); | |
342 | + if(null !=deviceProfileNames && !deviceProfileNames.isEmpty()){ | |
343 | + throw new TkDataValidationException(String.format(ErrorMessage.RULE_CHAIN_USED_BY_DEVICE_PROFILE.getMessage(),deviceProfileNames.get(0))); | |
344 | + } | |
328 | 345 | tbRuleChainService.delete(ruleChain, getCurrentUser()); |
346 | + //删除规则链转换脚本 | |
347 | + convertConfigService.deleteConvertConfigByRuleChainId(tenantId, ruleChainId.getId().toString(),0); | |
329 | 348 | } |
330 | 349 | |
331 | 350 | @ApiOperation(value = "Get latest input message (getLatestRuleNodeDebugInput)", | ... | ... |
... | ... | @@ -9,6 +9,8 @@ import io.swagger.annotations.ApiOperation; |
9 | 9 | import io.swagger.annotations.ApiParam; |
10 | 10 | import java.util.*; |
11 | 11 | import java.util.concurrent.ConcurrentMap; |
12 | +import java.util.stream.Collectors; | |
13 | + | |
12 | 14 | import lombok.RequiredArgsConstructor; |
13 | 15 | import org.springframework.beans.factory.annotation.Autowired; |
14 | 16 | import org.springframework.beans.factory.annotation.Value; |
... | ... | @@ -20,10 +22,9 @@ import org.thingsboard.common.util.JacksonUtil; |
20 | 22 | import org.thingsboard.server.actors.ActorSystemContext; |
21 | 23 | import org.thingsboard.server.actors.tenant.DebugTbRateLimits; |
22 | 24 | import org.thingsboard.server.common.data.StringUtils; |
23 | -import org.thingsboard.server.common.data.audit.ActionType; | |
24 | 25 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
26 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
25 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
26 | -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | |
27 | 28 | import org.thingsboard.server.common.data.rule.*; |
28 | 29 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
29 | 30 | import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; |
... | ... | @@ -34,12 +35,12 @@ import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; |
34 | 35 | import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertConfigDTO; |
35 | 36 | import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertConfigReqDTO; |
36 | 37 | import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertReqDTO; |
38 | +import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDTO; | |
37 | 39 | import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; |
38 | 40 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
39 | 41 | import org.thingsboard.server.controller.BaseController; |
40 | 42 | import org.thingsboard.server.dao.yunteng.service.ConvertConfigService; |
41 | 43 | import org.thingsboard.server.dao.yunteng.service.SceneLinkageService; |
42 | -import org.thingsboard.server.dao.yunteng.service.TkRuleChainService; | |
43 | 44 | import org.thingsboard.server.queue.util.TbCoreComponent; |
44 | 45 | import org.thingsboard.server.service.rule.TbRuleChainService; |
45 | 46 | import org.thingsboard.server.service.security.permission.Operation; |
... | ... | @@ -51,7 +52,6 @@ import org.thingsboard.server.service.security.permission.Operation; |
51 | 52 | @Api(tags = {"数据流转控制器"}) |
52 | 53 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
53 | 54 | public class TkConvertDataToController extends BaseController { |
54 | - private final TkRuleChainService tkRuleChainService; | |
55 | 55 | private final ConvertConfigService convertConfigService; |
56 | 56 | private final SceneLinkageService sceneLinkageService; |
57 | 57 | |
... | ... | @@ -75,7 +75,8 @@ public class TkConvertDataToController extends BaseController { |
75 | 75 | @RequestParam(value = ORDER_FILED, required = false) String orderBy, |
76 | 76 | @RequestParam(value = "startTime", required = false) Long startTime, |
77 | 77 | @RequestParam(value = "endTime", required = false) Long endTime, |
78 | - @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType) | |
78 | + @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType, | |
79 | + @RequestParam(value = "ruleChainId",required = false) String ruleChainId) | |
79 | 80 | throws ThingsboardException { |
80 | 81 | |
81 | 82 | HashMap<String, Object> queryMap = new HashMap<>(); |
... | ... | @@ -85,6 +86,7 @@ public class TkConvertDataToController extends BaseController { |
85 | 86 | queryMap.put("nodeType", nodeType); |
86 | 87 | queryMap.put("name", name); |
87 | 88 | queryMap.put("status", status); |
89 | + queryMap.put("ruleChainId",ruleChainId); | |
88 | 90 | checkTimeAndPut(queryMap, startTime, endTime); |
89 | 91 | if (orderType != null) { |
90 | 92 | queryMap.put(ORDER_TYPE, orderType.name()); |
... | ... | @@ -156,8 +158,8 @@ public class TkConvertDataToController extends BaseController { |
156 | 158 | } |
157 | 159 | |
158 | 160 | @PostMapping("update/config") |
159 | - @ApiOperation("启用或禁用配置") | |
160 | - public RuleChainMetaData updateConfig( | |
161 | + @ApiOperation("启用或禁用数据流转") | |
162 | + public ResponseEntity<Boolean> updateConfig( | |
161 | 163 | @Validated(UpdateGroup.class) @RequestBody ConvertConfigReqDTO convertConfigReqDTO) |
162 | 164 | throws Exception { |
163 | 165 | if (convertConfigService.checkConvertConfigStatusExist( |
... | ... | @@ -171,47 +173,53 @@ public class TkConvertDataToController extends BaseController { |
171 | 173 | List<RuleNode> nodes = |
172 | 174 | convertConfigService.getRuleNodesByConvertConfigIds( |
173 | 175 | ids, null, FastIotConstants.CONVERT_DATA); |
174 | - RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); | |
175 | 176 | if (null != nodes && nodes.size() > 0) { |
176 | - ruleChainMetaData = saveRuleChain(nodes, status, FastIotConstants.CONVERT_DATA,null); | |
177 | + classificationSaveRuleChain(nodes,status,FastIotConstants.CONVERT_DATA,null); | |
177 | 178 | } |
178 | - convertConfigService.updateConvertStatusByIds( | |
179 | - ids, status, getCurrentUser().getCurrentTenantId()); | |
180 | - return ruleChainMetaData; | |
179 | + return ResponseEntity.ok(convertConfigService.updateConvertStatusByIds( | |
180 | + ids, status, getCurrentUser().getCurrentTenantId())); | |
181 | 181 | } |
182 | 182 | |
183 | 183 | @PostMapping("update/js") |
184 | 184 | @ApiOperation("启用或禁用转换脚本") |
185 | - public RuleChainMetaData updateJs( | |
185 | + public ResponseEntity<Boolean> updateJs( | |
186 | 186 | @Validated(UpdateGroup.class) @RequestBody ConvertReqDTO convertJSReqDTO) |
187 | 187 | throws Exception { |
188 | 188 | int status = convertJSReqDTO.getStatus(); |
189 | + ConvertConfigDTO checkConfig = convertConfigService.findConvertConfigDTOById(getCurrentUser().getCurrentTenantId(), | |
190 | + convertJSReqDTO.getId()); | |
191 | + if(null == checkConfig){ | |
192 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_CUSTOMER.getMessage()); | |
193 | + } | |
189 | 194 | if (status == FastIotConstants.StateValue.ENABLE |
190 | - && convertConfigService.checkConvertJSStatusEnable(getCurrentUser().getCurrentTenantId())) { | |
195 | + && convertConfigService.checkConvertJSStatusEnable(getCurrentUser().getCurrentTenantId(),checkConfig.getRuleChainId())) { | |
191 | 196 | throw new TkDataValidationException(ErrorMessage.CONVERT_JS_IS_ALONE.getMessage()); |
192 | 197 | } |
193 | 198 | List<String> ids = new ArrayList<>(); |
194 | - ids.add(convertJSReqDTO.getId()); | |
199 | + ids.add(checkConfig.getId()); | |
195 | 200 | List<RuleNode> nodes = |
196 | 201 | convertConfigService.getRuleNodesByConvertConfigIds( |
197 | 202 | ids, null, FastIotConstants.JAVA_SCRIPT); |
198 | - | |
199 | - RuleChainMetaData ruleChainMetaData = | |
200 | - saveRuleChain(nodes, status, FastIotConstants.JAVA_SCRIPT,null); | |
201 | - convertConfigService.updateConvertStatusByIds( | |
202 | - ids, status, getCurrentUser().getCurrentTenantId()); | |
203 | - return ruleChainMetaData; | |
203 | + RuleChainId ruleChainId = StringUtils.isEmpty(checkConfig.getRuleChainId())?null:new RuleChainId(UUID.fromString(checkConfig.getRuleChainId())); | |
204 | + saveRuleChain(ruleChainId,nodes, status, FastIotConstants.JAVA_SCRIPT,null); | |
205 | + return ResponseEntity.ok( | |
206 | + convertConfigService.updateConvertStatusByIds( | |
207 | + ids, status, getCurrentUser().getCurrentTenantId())); | |
204 | 208 | } |
205 | 209 | |
206 | 210 | @PostMapping("update/scene") |
207 | 211 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
208 | 212 | @ApiOperation("启用或禁用场景联动") |
209 | - public RuleChainMetaData updateScene( | |
213 | + public ResponseEntity<Boolean> updateScene( | |
210 | 214 | @Validated(UpdateGroup.class) @RequestBody ConvertReqDTO convertReqDTO) |
211 | 215 | throws Exception { |
212 | - int status = -1; | |
213 | - String sceneId = convertReqDTO.getId(); | |
214 | 216 | String currentTenant = getCurrentUser().getCurrentTenantId(); |
217 | + SceneLinkageDTO querySceneLink = sceneLinkageService.selectById(currentTenant, convertReqDTO.getId()); | |
218 | + if(null == querySceneLink){ | |
219 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage()); | |
220 | + } | |
221 | + int status = -1; | |
222 | + String sceneId = querySceneLink.getId(); | |
215 | 223 | Integer sceneStatus = convertReqDTO.getStatus(); |
216 | 224 | JsonNode configuration = |
217 | 225 | sceneLinkageService.getRuleNodeConfig( |
... | ... | @@ -255,9 +263,10 @@ public class TkConvertDataToController extends BaseController { |
255 | 263 | objectNode.remove(otherKey); |
256 | 264 | scene.setConfiguration(objectNode); |
257 | 265 | } |
258 | - RuleChainMetaData result = saveRuleChain(ruleNodes, status, FastIotConstants.SCENE_REACT,nodeConnectionName); | |
266 | + RuleChainId ruleChainId = StringUtils.isEmpty(querySceneLink.getRuleChainId())?null:new RuleChainId(UUID.fromString(querySceneLink.getRuleChainId())); | |
267 | + saveRuleChain(ruleChainId,ruleNodes, status, FastIotConstants.SCENE_REACT,nodeConnectionName); | |
259 | 268 | sceneLinkageService.updateSceneStatus(sceneId, sceneStatus, currentTenant); |
260 | - return result; | |
269 | + return ResponseEntity.ok(true); | |
261 | 270 | } |
262 | 271 | |
263 | 272 | /** |
... | ... | @@ -265,33 +274,17 @@ public class TkConvertDataToController extends BaseController { |
265 | 274 | * |
266 | 275 | * @param nodes 规则节点 |
267 | 276 | * @param status 0禁用 1启用 |
277 | + * @param nodeType | |
278 | + * @param nodeConnectionName | |
268 | 279 | * @return 规则节点数据 |
269 | 280 | */ |
270 | - private RuleChainMetaData saveRuleChain(List<RuleNode> nodes, Integer status, Integer nodeType, | |
281 | + private void saveRuleChain(RuleChainId ruleChainId,List<RuleNode> nodes, Integer status, Integer nodeType, | |
271 | 282 | String nodeConnectionName) |
272 | 283 | throws Exception { |
273 | - boolean needSaveRuleNode; | |
274 | 284 | TenantId tenantId = getTenantId(); |
275 | - // 1. GET DEFAULT RULE CHAIN | |
276 | - RuleChain ruleChain = tkRuleChainService.getRootTenantRuleChain(getTenantId()); | |
277 | - // 2. GET RULE CHAIN METADATA | |
278 | - RuleChainMetaData ruleChainMetaData = | |
279 | - ruleChainService.loadRuleChainMetaData(getTenantId(), ruleChain.getId()); | |
280 | - // 3. SETUP CONNECTION AND ADD OR DELETE RULE NODE | |
281 | - if (status == FastIotConstants.MagicNumber.ZERO) { | |
282 | - needSaveRuleNode = | |
283 | - convertConfigService.deleteRuleNode( | |
284 | - nodes, ruleChainMetaData, nodeType, getCurrentUser().getCurrentTenantId()); | |
285 | - } else { | |
286 | - convertConfigService.addRuleNode( | |
287 | - nodes, ruleChainMetaData, nodeType, getCurrentUser().getCurrentTenantId()); | |
288 | - //添加节点连接信息 | |
289 | - addConnectionInfo(nodeConnectionName,nodeType,ruleChainMetaData); | |
290 | - needSaveRuleNode = true; | |
291 | - } | |
292 | - // 4. SAVE METADATA | |
293 | - RuleChainMetaData savedRuleChainMetaData = null; | |
294 | - if (needSaveRuleNode) { | |
285 | + RuleChain ruleChain; | |
286 | + RuleChainMetaData savedRuleChainMetaData = convertConfigService.getTenantRuleChainMetaData(tenantId,ruleChainId,nodes,status,nodeType,nodeConnectionName); | |
287 | + if (null !=savedRuleChainMetaData && savedRuleChainMetaData.isNeedSaveRuleNode()) { | |
295 | 288 | if (debugPerTenantEnabled) { |
296 | 289 | ConcurrentMap<TenantId, DebugTbRateLimits> debugPerTenantLimits = |
297 | 290 | actorContext.getDebugPerTenantLimits(); |
... | ... | @@ -300,42 +293,32 @@ public class TkConvertDataToController extends BaseController { |
300 | 293 | debugPerTenantLimits.remove(tenantId, debugTbRateLimits); |
301 | 294 | } |
302 | 295 | } |
303 | - ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); | |
304 | - savedRuleChainMetaData = tbRuleChainService.saveRuleChainMetaData(tenantId, ruleChain, ruleChainMetaData, true, getCurrentUser()); | |
296 | + ruleChain = checkRuleChain(savedRuleChainMetaData.getRuleChainId(), Operation.WRITE); | |
297 | + tbRuleChainService.saveRuleChainMetaData(tenantId, ruleChain, savedRuleChainMetaData, true, getCurrentUser()); | |
305 | 298 | } |
306 | - return savedRuleChainMetaData; | |
307 | 299 | } |
308 | 300 | |
309 | - private ResponseEntity<Boolean> delete(DeleteDTO deleteDTO, Integer nodeType) | |
310 | - throws Exception { | |
301 | + private ResponseEntity<Boolean> delete(DeleteDTO deleteDTO, Integer nodeType){ | |
311 | 302 | List<RuleNode> nodes = |
312 | 303 | convertConfigService.getRuleNodesByConvertConfigIds( |
313 | 304 | new ArrayList<>(deleteDTO.getIds()), FastIotConstants.StateValue.ENABLE, nodeType); |
314 | - if (nodes != null && nodes.size() > FastIotConstants.MagicNumber.ZERO) { | |
315 | - saveRuleChain(nodes, 0, nodeType,null); | |
316 | - } | |
305 | + classificationSaveRuleChain(nodes,0,nodeType,null); | |
317 | 306 | return ResponseEntity.ok(convertConfigService.deleteConvertConfig(deleteDTO, nodeType)); |
318 | 307 | } |
319 | - private void addConnectionInfo(String nodeConnectionName,Integer nodeType,RuleChainMetaData ruleChainMetaData){ | |
320 | - if(null != nodeConnectionName && nodeType == FastIotConstants.SCENE_REACT){ | |
321 | - int i = 0; | |
322 | - //查找事件节点下标 | |
323 | - int formIndex = 0; | |
324 | - int toIndex = 0; | |
325 | - for(RuleNode ruleNode : ruleChainMetaData.getNodes()){ | |
326 | - if(ruleNode.getType().equals(nodeConnectionName)){ | |
327 | - formIndex = i; | |
328 | - } | |
329 | - if(ruleNode.getType().equals(FastIotConstants.TkNodeClassName.GET_ORIGINATOR_FIELDS_NODE)){ | |
330 | - toIndex = i; | |
331 | - } | |
332 | - i++; | |
308 | + | |
309 | + private void classificationSaveRuleChain(List<RuleNode> nodes,Integer saveStatus,Integer nodeType,String nodeConnectionName){ | |
310 | + if (nodes != null && nodes.size() > FastIotConstants.MagicNumber.ZERO) { | |
311 | + //将规则节点按规则链分类 | |
312 | + Map<RuleChainId,List<RuleNode>> classification = nodes.stream().collect(Collectors.groupingBy(RuleNode::getRuleChainId)); | |
313 | + if(classification !=null && !classification.isEmpty()){ | |
314 | + classification.keySet().forEach(key->{ | |
315 | + try { | |
316 | + saveRuleChain(key,nodes, saveStatus, nodeType,nodeConnectionName); | |
317 | + } catch (Exception e) { | |
318 | + throw new RuntimeException(e); | |
319 | + } | |
320 | + }); | |
333 | 321 | } |
334 | - NodeConnectionInfo newConnectionInfo = new NodeConnectionInfo(); | |
335 | - newConnectionInfo.setFromIndex(formIndex); | |
336 | - newConnectionInfo.setToIndex(toIndex); | |
337 | - newConnectionInfo.setType("SUCCESS"); | |
338 | - ruleChainMetaData.getConnections().add(newConnectionInfo); | |
339 | 322 | } |
340 | 323 | } |
341 | 324 | } | ... | ... |
... | ... | @@ -106,16 +106,16 @@ public class TkDeviceProfileController extends BaseController { |
106 | 106 | } |
107 | 107 | |
108 | 108 | @GetMapping("/me/list") |
109 | - @ApiOperation("选项列表") | |
110 | - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | |
109 | + @ApiOperation("产品列表") | |
110 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
111 | 111 | public ResponseEntity listDeviceProfile( |
112 | 112 | @ApiParam(value = "设备类型") @RequestParam(value = "deviceType", required = false) |
113 | - DeviceTypeEnum deviceType,DeviceTransportType transportType,Boolean isSceneLinkage) | |
113 | + DeviceTypeEnum deviceType,DeviceTransportType transportType,Boolean isSceneLinkage,String ruleChainId) | |
114 | 114 | throws ThingsboardException { |
115 | 115 | List<DeviceProfileDTO> results; |
116 | 116 | TenantId tenantId = getCurrentUser().getTenantId(); |
117 | 117 | if (getCurrentUser().isTenantAdmin()) { |
118 | - results = tkDeviceProfileService.findDeviceProfile(tenantId, null,deviceType,transportType,isSceneLinkage); | |
118 | + results = tkDeviceProfileService.findDeviceProfile(tenantId, null,deviceType,transportType,isSceneLinkage,ruleChainId); | |
119 | 119 | } else { |
120 | 120 | List<TkDeviceProfileEntity> listEntity = |
121 | 121 | tkDeviceProfileService.findCustomerDeviceProfiles( | ... | ... |
... | ... | @@ -94,7 +94,8 @@ public class TkSceneLinkageController extends BaseController { |
94 | 94 | @RequestParam(value = "name", required = false) String name, |
95 | 95 | @RequestParam(value = "status", required = false) Integer status, |
96 | 96 | @RequestParam(value = "organizationId", required = false) String organizationId, |
97 | - @RequestParam(value = "triggers", required = false) List<TriggerDTO> triggers) | |
97 | + @RequestParam(value = "triggers", required = false) List<TriggerDTO> triggers, | |
98 | + @RequestParam(value = "ruleChainId",required = false) String ruleChainId) | |
98 | 99 | throws ThingsboardException { |
99 | 100 | HashMap<String, Object> queryMap = new HashMap<>(); |
100 | 101 | queryMap.put(PAGE_SIZE, pageSize); |
... | ... | @@ -103,6 +104,7 @@ public class TkSceneLinkageController extends BaseController { |
103 | 104 | queryMap.put("status", status); |
104 | 105 | queryMap.put("organizationId", organizationId); |
105 | 106 | queryMap.put("triggers", triggers); |
107 | + queryMap.put("ruleChainId",ruleChainId); | |
106 | 108 | queryMap.put("currentUser",getCurrentUser().getCurrentUserId()); |
107 | 109 | //如果是普通租户在最外层默认放入组织id |
108 | 110 | if(getCurrentUser().isPtCommonTenant()){ | ... | ... |
... | ... | @@ -238,7 +238,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { |
238 | 238 | if (TkEdgeProcessorFactory.isTkProcessorExist(type.getEntityType())) { |
239 | 239 | TkEdgeProcessorFactory.getTkProcessor(type.getEntityType()).processEntityNotification(tenantId, edgeNotificationMsg); |
240 | 240 | } else { |
241 | - log.error("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type); | |
241 | + log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type); | |
242 | 242 | } |
243 | 243 | } catch (Exception e) { |
244 | 244 | callBackFailure(tenantId, edgeNotificationMsg, callback, e); | ... | ... |
... | ... | @@ -69,6 +69,9 @@ public class RpcCommandTask { |
69 | 69 | if (null != tkTaskCenterDTO) { |
70 | 70 | TargetTypeEnum targetType = tkTaskCenterDTO.getTargetType(); |
71 | 71 | TargetContentDTO targetContent = tkTaskCenterDTO.getExecuteTarget(); |
72 | + if(null == targetContent){ | |
73 | + return; | |
74 | + } | |
72 | 75 | JsonNode cmdJsonNode = tkTaskCenterDTO.getExecuteContent().getPushContent(); |
73 | 76 | SecurityUser securityUser = new SecurityUser(); |
74 | 77 | String taskCenterId = tkTaskCenterDTO.getId(); | ... | ... |
... | ... | @@ -45,6 +45,9 @@ public class RuleChainMetaData { |
45 | 45 | @ApiModelProperty(position = 5, required = true, value = "List of JSON objects that represent connections between rule nodes and other rule chains.") |
46 | 46 | private List<RuleChainConnectionInfo> ruleChainConnections; |
47 | 47 | |
48 | + //thingskit | |
49 | + private boolean needSaveRuleNode; | |
50 | + | |
48 | 51 | public void addConnectionInfo(int fromIndex, int toIndex, String type) { |
49 | 52 | NodeConnectionInfo connectionInfo = new NodeConnectionInfo(); |
50 | 53 | connectionInfo.setFromIndex(fromIndex); | ... | ... |
... | ... | @@ -43,7 +43,7 @@ public enum ErrorMessage { |
43 | 43 | DEVICE_NOT_EXISTENCE_IN_TENANT(400021,"当前租户下不存在该设备"), |
44 | 44 | DEVICE_RELATION_IS_ABSENT(400022,"设备关联关系已不存在"), |
45 | 45 | DATA_IS_DELETED(400023,"数据已经删除"), |
46 | - CONVERT_JS_IS_ALONE(400024,"转换脚本只能启用一个"), | |
46 | + CONVERT_JS_IS_ALONE(400024,"同一个规则链转换脚本只能启用一个"), | |
47 | 47 | PHONE_OR_EMAIL_HAS_REGISTER(400025,"手机或邮箱已被使用"), |
48 | 48 | CONTACT_ALREADY_ASSOCIATED(400026,"当前联系人已与告警配置【%s】关联"), |
49 | 49 | MSG_CODE_NOT_MATCHED(400027,"验证码不正确"), |
... | ... | @@ -142,6 +142,10 @@ public enum ErrorMessage { |
142 | 142 | CUSTOMER_USER_UPDATE_ERROR(400117,"已分配设备给该客户,请取消分配后重试"), |
143 | 143 | SCRIPT_EVAL_FAILED(400118,"租户【%s】启动脚本【%s】失败,失败原因【%s】"), |
144 | 144 | SCENE_REACT_IS_EDGE(400119,"场景联动来自于边端请勿操作"), |
145 | + RULE_CHAIN_USED_BY_DEVICE_PROFILE(400120,"该规则链正在被【%s】产品使用,不能被删除!!"), | |
146 | + NODE_NAME_ALREADY_EXISTS(400121,"同类型的节点名称已被使用"), | |
147 | + CHOICE_DEVICE_PROFILE_RULE_CHAIN_IS_USED_FOR_CONVERT(400122,"选中的产品【%s】正在被数据流转【%s】使用,不能更换规则链!!"), | |
148 | + CHOICE_DEVICE_PROFILE_RULE_CHAIN_IS_USED_FOR_SCENE(400123,"选中的产品【%s】正在被场景联动【%s】使用,不能更换规则链!!"), | |
145 | 149 | HAVE_NO_PERMISSION(500002,"没有修改权限"), |
146 | 150 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); |
147 | 151 | ... | ... |
... | ... | @@ -25,6 +25,12 @@ public class ConvertConfigDTO extends TenantDTO { |
25 | 25 | @NotNull(message = "配置参数不能为空或者空字符串") |
26 | 26 | private JsonNode configuration; |
27 | 27 | |
28 | + @ApiModelProperty(value = "规则链ID") | |
29 | + private String ruleChainId; | |
30 | + | |
31 | + @ApiModelProperty(value = "规则链名称") | |
32 | + private String ruleChainName; | |
33 | + | |
28 | 34 | @ApiModelProperty(value = "附加信息") |
29 | 35 | private JsonNode additionalInfo; |
30 | 36 | ... | ... |
... | ... | @@ -49,6 +49,9 @@ public class DoActionDTO extends TenantDTO { |
49 | 49 | @ApiModelProperty(value = "场景联动id") |
50 | 50 | private String sceneLinkageId; |
51 | 51 | |
52 | + @ApiModelProperty(value = "场景联动名称") | |
53 | + private String sceneLinkageName; | |
54 | + | |
52 | 55 | @ApiModelProperty(value = "输出目标为告警才进行配置") |
53 | 56 | private String alarmProfileId; |
54 | 57 | } | ... | ... |
... | ... | @@ -44,4 +44,13 @@ public class SceneLinkageDTO extends TenantDTO implements HasName { |
44 | 44 | |
45 | 45 | @ApiModelProperty(value = "创建用户") |
46 | 46 | private String creatorName; |
47 | + | |
48 | + @NotEmpty( | |
49 | + message = "规则链ID不能为空或者空字符串", | |
50 | + groups = {AddGroup.class}) | |
51 | + @ApiModelProperty(value = "规则链ID", required = true) | |
52 | + private String ruleChainId; | |
53 | + | |
54 | + @ApiModelProperty(value = "规则链名称") | |
55 | + private String ruleChainName; | |
47 | 56 | } | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.dto.scene; | |
2 | + | |
3 | +import lombok.Data; | |
4 | + | |
5 | +import java.io.Serializable; | |
6 | +import java.util.List; | |
7 | + | |
8 | +@Data | |
9 | +public class SceneLinkageDeviceProfileDTO implements Serializable { | |
10 | + private String sceneId; | |
11 | + private String sceneName; | |
12 | + private List<String> deviceProfileIds; | |
13 | + | |
14 | + public SceneLinkageDeviceProfileDTO(String sceneId, String sceneName, List<String> deviceProfileIds) { | |
15 | + this.sceneId = sceneId; | |
16 | + this.sceneName = sceneName; | |
17 | + this.deviceProfileIds = deviceProfileIds; | |
18 | + } | |
19 | +} | ... | ... |
... | ... | @@ -43,6 +43,9 @@ public class TriggerDTO extends TenantDTO { |
43 | 43 | @ApiModelProperty(value = "场景联动id") |
44 | 44 | private String sceneLinkageId; |
45 | 45 | |
46 | + @ApiModelProperty(value = "场景联动名称") | |
47 | + private String sceneLinkageName; | |
48 | + | |
46 | 49 | public AlarmRule getTriggerCondition() { |
47 | 50 | return JacksonUtil.convertValue(triggerCondition,AlarmRule.class); |
48 | 51 | } | ... | ... |
... | ... | @@ -197,7 +197,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten |
197 | 197 | publishEvictEvent(new TenantEvictEvent(savedTenant.getId(), create)); |
198 | 198 | eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedTenant.getId()).entityId(savedTenant.getId()).created(create).build()); |
199 | 199 | if (tenant.getId() == null) { |
200 | - DeviceProfile deviceProfile = deviceProfileService.createDefaultDeviceProfile(savedTenant.getId()); | |
200 | + deviceProfileService.createDefaultDeviceProfile(savedTenant.getId()); | |
201 | 201 | assetProfileService.createDefaultAssetProfile(savedTenant.getId()); |
202 | 202 | //Thingskit function |
203 | 203 | deviceProfileService.createDeviceProfile(savedTenant.getId(),"默认MQTT网关设备", DeviceTypeEnum.GATEWAY); | ... | ... |
... | ... | @@ -11,6 +11,8 @@ import org.apache.commons.lang3.StringUtils; |
11 | 11 | import org.springframework.stereotype.Service; |
12 | 12 | import org.springframework.transaction.annotation.Transactional; |
13 | 13 | import org.thingsboard.common.util.JacksonUtil; |
14 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
15 | +import org.thingsboard.server.common.data.id.TenantId; | |
14 | 16 | import org.thingsboard.server.common.data.rule.NodeConnectionInfo; |
15 | 17 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
16 | 18 | import org.thingsboard.server.common.data.rule.RuleNode; |
... | ... | @@ -22,54 +24,36 @@ import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertConfigDTO; |
22 | 24 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; |
23 | 25 | import org.thingsboard.server.common.data.yunteng.enums.DatasourceTypeEnum; |
24 | 26 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
27 | +import org.thingsboard.server.dao.rule.RuleChainService; | |
25 | 28 | import org.thingsboard.server.dao.yunteng.ConvertConfig; |
26 | 29 | import org.thingsboard.server.dao.yunteng.mapper.ConvertConfigMapper; |
27 | 30 | import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; |
28 | 31 | import org.thingsboard.server.dao.yunteng.service.ConvertConfigService; |
29 | 32 | |
30 | -import java.time.LocalDateTime; | |
31 | 33 | import java.util.*; |
32 | 34 | import java.util.concurrent.ConcurrentHashMap; |
33 | 35 | import java.util.concurrent.atomic.AtomicInteger; |
36 | +import java.util.stream.Collectors; | |
34 | 37 | |
35 | 38 | @Slf4j |
36 | 39 | @Service |
37 | 40 | @RequiredArgsConstructor |
38 | 41 | public class TkConvertConfigServiceImpl |
39 | - extends AbstractBaseService<ConvertConfigMapper, ConvertConfig> | |
40 | - implements ConvertConfigService { | |
42 | + extends AbstractBaseService<ConvertConfigMapper, ConvertConfig> | |
43 | + implements ConvertConfigService { | |
41 | 44 | private final String connectionType = "Success"; |
42 | 45 | private final String originatorType = |
43 | - "org.thingsboard.rule.engine.metadata.TbGetOriginatorFieldsNode"; | |
46 | + "org.thingsboard.rule.engine.metadata.TbGetOriginatorFieldsNode"; | |
44 | 47 | private final String sceneOriginatorName = "scene@thingskit"; |
45 | 48 | private final String scriptType = "org.thingsboard.rule.engine.transform.TbTransformMsgNode"; |
46 | 49 | private final String filterScriptType = "org.thingsboard.rule.engine.filter.TbJsFilterNode"; |
47 | 50 | private final String convertNodeName = "dataConvert@thingskit"; |
51 | + private final RuleChainService ruleChainService; | |
48 | 52 | |
49 | 53 | @Override |
50 | 54 | public TkPageData<ConvertConfigDTO> page(String tenantId, Map<String, Object> queryMap) { |
51 | - LocalDateTime startTime = (LocalDateTime) queryMap.get("startTime"); | |
52 | - LocalDateTime endTime = (LocalDateTime) queryMap.get("endTime"); | |
53 | - IPage<ConvertConfig> iPage = | |
54 | - baseMapper.selectPage( | |
55 | - getPage(queryMap, FastIotConstants.DefaultOrder.CREATE_TIME, false), | |
56 | - new LambdaQueryWrapper<ConvertConfig>() | |
57 | - .eq(ConvertConfig::getTenantId, tenantId) | |
58 | - .eq( | |
59 | - null != queryMap.get("status"), | |
60 | - ConvertConfig::getStatus, | |
61 | - queryMap.get("status")) | |
62 | - .eq(ConvertConfig::getNodeType, queryMap.get("nodeType")) | |
63 | - .and( | |
64 | - null != startTime && null != endTime, | |
65 | - qr -> | |
66 | - qr.ge(ConvertConfig::getCreateTime, startTime) | |
67 | - .le(ConvertConfig::getCreateTime, endTime)) | |
68 | - .like( | |
69 | - null != queryMap.get("name"), | |
70 | - ConvertConfig::getName, | |
71 | - String.valueOf(queryMap.get("name"))) | |
72 | - ); | |
55 | + IPage<ConvertConfigDTO> iPage = baseMapper.getConvertPage( | |
56 | + getPage(queryMap, FastIotConstants.DefaultOrder.CREATE_TIME, false),tenantId,queryMap); | |
73 | 57 | return getPageData(iPage, ConvertConfigDTO.class); |
74 | 58 | } |
75 | 59 | |
... | ... | @@ -77,6 +61,8 @@ public class TkConvertConfigServiceImpl |
77 | 61 | @Transactional |
78 | 62 | public ConvertConfigDTO createOrUpdate(ConvertConfigDTO convertConfig) { |
79 | 63 | ConvertConfig config = convertConfig.getEntity(ConvertConfig.class); |
64 | + //设置规则链ID | |
65 | + config.setRuleChainId(getRuleChainId(convertConfig)); | |
80 | 66 | config.setDatasourceContent( |
81 | 67 | JacksonUtil.convertValue(convertConfig.getDatasourceContent(), JsonNode.class)); |
82 | 68 | if (StringUtils.isNotEmpty(convertConfig.getId())) { |
... | ... | @@ -92,10 +78,9 @@ public class TkConvertConfigServiceImpl |
92 | 78 | if (!checkConfig.getName().equals(config.getName())) { |
93 | 79 | // Check Name |
94 | 80 | boolean result = |
95 | - checkConvertConfigNameExist( | |
96 | - convertConfig.getId(), config.getName(), config.getType(), config.getTenantId()); | |
81 | + checkConvertConfigNameExist(null, config.getName(), config.getType(), config.getTenantId()); | |
97 | 82 | if (result) { |
98 | - throw new TkDataValidationException(ErrorMessage.NAME_ALREADY_EXISTS.getMessage()); | |
83 | + throw new TkDataValidationException(ErrorMessage.NODE_NAME_ALREADY_EXISTS.getMessage()); | |
99 | 84 | } |
100 | 85 | } |
101 | 86 | // 修改不改状态 |
... | ... | @@ -107,7 +92,7 @@ public class TkConvertConfigServiceImpl |
107 | 92 | checkConvertConfigNameExist( |
108 | 93 | null, config.getName(), config.getType(), config.getTenantId()); |
109 | 94 | if (result) { |
110 | - throw new TkDataValidationException(ErrorMessage.NAME_ALREADY_EXISTS.getMessage()); | |
95 | + throw new TkDataValidationException(ErrorMessage.NODE_NAME_ALREADY_EXISTS.getMessage()); | |
111 | 96 | } |
112 | 97 | // DEFAULT DISABLE |
113 | 98 | config.setStatus(FastIotConstants.StateValue.DISABLE); |
... | ... | @@ -143,12 +128,13 @@ public class TkConvertConfigServiceImpl |
143 | 128 | |
144 | 129 | @Override |
145 | 130 | public ConvertConfigDTO findConvertConfigDTOByName( |
146 | - String tenantId, String name, Integer nodeType) { | |
131 | + String tenantId, String name,String type, Integer nodeType) { | |
147 | 132 | ConvertConfig convertConfig = |
148 | 133 | baseMapper.selectOne( |
149 | 134 | new LambdaQueryWrapper<ConvertConfig>() |
150 | 135 | .eq(ConvertConfig::getTenantId, tenantId) |
151 | 136 | .eq(ConvertConfig::getName, name) |
137 | + .eq(ConvertConfig::getType,type) | |
152 | 138 | .eq(ConvertConfig::getNodeType, nodeType)); |
153 | 139 | return null != convertConfig ? convertConfig.getDTO(ConvertConfigDTO.class) : null; |
154 | 140 | } |
... | ... | @@ -173,7 +159,12 @@ public class TkConvertConfigServiceImpl |
173 | 159 | ruleNode.setType(convertConfig.getType()); |
174 | 160 | ruleNode.setConfiguration(convertConfig.getConfiguration()); |
175 | 161 | ruleNode.setAdditionalInfo(convertConfig.getAdditionalInfo()); |
176 | - | |
162 | + if (StringUtils.isNotEmpty(convertConfig.getRuleChainId())) { | |
163 | + ruleNode.setRuleChainId( | |
164 | + new RuleChainId(UUID.fromString(convertConfig.getRuleChainId()))); | |
165 | + } else { | |
166 | + ruleNode.setRuleChainId(ruleChainService.getRootTenantRuleChain(new TenantId(UUID.fromString(convertConfig.getTenantId()))).getId()); | |
167 | + } | |
177 | 168 | ruleNodes.add(ruleNode); |
178 | 169 | }); |
179 | 170 | return ruleNodes; |
... | ... | @@ -204,15 +195,16 @@ public class TkConvertConfigServiceImpl |
204 | 195 | } |
205 | 196 | |
206 | 197 | @Override |
207 | - public boolean checkConvertJSStatusEnable(String tenantId) { | |
198 | + public boolean checkConvertJSStatusEnable(String tenantId,String ruleChainId) { | |
208 | 199 | return baseMapper |
209 | 200 | .selectList( |
210 | - new LambdaQueryWrapper<ConvertConfig>() | |
211 | - .eq(ConvertConfig::getTenantId, tenantId) | |
212 | - .eq(ConvertConfig::getStatus, FastIotConstants.StateValue.ENABLE) | |
213 | - .eq(ConvertConfig::getNodeType, FastIotConstants.JAVA_SCRIPT)) | |
201 | + new LambdaQueryWrapper<ConvertConfig>() | |
202 | + .eq(ConvertConfig::getTenantId, tenantId) | |
203 | + .eq(ConvertConfig::getStatus, FastIotConstants.StateValue.ENABLE) | |
204 | + .eq(ConvertConfig::getNodeType, FastIotConstants.JAVA_SCRIPT) | |
205 | + .eq(ConvertConfig::getRuleChainId,ruleChainId)) | |
214 | 206 | .size() |
215 | - > FastIotConstants.MagicNumber.ZERO; | |
207 | + > FastIotConstants.MagicNumber.ZERO; | |
216 | 208 | } |
217 | 209 | /** |
218 | 210 | * 删除规则节点 |
... | ... | @@ -254,6 +246,41 @@ public class TkConvertConfigServiceImpl |
254 | 246 | } |
255 | 247 | } |
256 | 248 | |
249 | + @Override | |
250 | + public RuleChainMetaData getTenantRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId,List<RuleNode> nodes, | |
251 | + int status,int nodeType,String nodeConnectionName) { | |
252 | + boolean needSaveRuleNode = true; | |
253 | + RuleChainMetaData ruleChainMetaData = | |
254 | + ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId); | |
255 | + if (status == FastIotConstants.MagicNumber.ZERO) { | |
256 | + needSaveRuleNode = deleteRuleNode(nodes, ruleChainMetaData, nodeType, tenantId.getId().toString()); | |
257 | + } else { | |
258 | + addRuleNode(nodes, ruleChainMetaData, nodeType, tenantId.getId().toString()); | |
259 | + //添加节点连接信息 | |
260 | + addConnectionInfo(nodeConnectionName,nodeType,ruleChainMetaData); | |
261 | + } | |
262 | + ruleChainMetaData.setNeedSaveRuleNode(needSaveRuleNode); | |
263 | + return ruleChainMetaData; | |
264 | + } | |
265 | + | |
266 | + @Override | |
267 | + public boolean deleteConvertConfigByRuleChainId(String tenantId, String ruleChainId, Integer nodeType) { | |
268 | + nodeType = null == nodeType?0:nodeType; | |
269 | + return baseMapper.delete(new LambdaQueryWrapper<ConvertConfig>().eq(ConvertConfig::getTenantId,tenantId). | |
270 | + eq(ConvertConfig::getRuleChainId,ruleChainId).eq(ConvertConfig::getNodeType,nodeType)) > FastIotConstants.MagicNumber.ZERO; | |
271 | + } | |
272 | + | |
273 | + @Override | |
274 | + public List<ConvertConfigDTO> getConvertConfigUsedByRuleChainIds(String tenantId, List<String> ruleChainIds,Integer nodeType) { | |
275 | + List<ConvertConfig> convertConfigs = baseMapper.selectList(new LambdaQueryWrapper<ConvertConfig>(). | |
276 | + eq(ConvertConfig::getTenantId,tenantId).eq(null != nodeType,ConvertConfig::getNodeType,nodeType) | |
277 | + .in(ConvertConfig::getRuleChainId,ruleChainIds)); | |
278 | + if(null != convertConfigs && !convertConfigs.isEmpty()){ | |
279 | + return convertConfigs.stream().map(entity->entity.getDTO(ConvertConfigDTO.class)).collect(Collectors.toList()); | |
280 | + } | |
281 | + return null; | |
282 | + } | |
283 | + | |
257 | 284 | private boolean deleteConvertJS(List<RuleNode> nodes, RuleChainMetaData ruleChainMetaData) { |
258 | 285 | boolean needDelete = false; |
259 | 286 | // 1. Get firstNodeIndex |
... | ... | @@ -329,7 +356,7 @@ public class TkConvertConfigServiceImpl |
329 | 356 | // 判断数据源是产品、设备、全部 |
330 | 357 | ConvertConfigDTO convertConfigDTO = |
331 | 358 | findConvertConfigDTOByName( |
332 | - tenantId, deleteRuleNode.getName(), FastIotConstants.CONVERT_DATA.intValue()); | |
359 | + tenantId, deleteRuleNode.getName(),deleteRuleNode.getType(), FastIotConstants.CONVERT_DATA.intValue()); | |
333 | 360 | boolean haveFilterNode = false; |
334 | 361 | String filterNodeName = null; |
335 | 362 | String filterType = null; |
... | ... | @@ -522,7 +549,7 @@ public class TkConvertConfigServiceImpl |
522 | 549 | // estimate datasource type |
523 | 550 | ConvertConfigDTO convertConfigDTO = |
524 | 551 | findConvertConfigDTOByName( |
525 | - tenantId, ruleNode.getName(), FastIotConstants.CONVERT_DATA.intValue()); | |
552 | + tenantId, ruleNode.getName(),ruleNode.getType(), FastIotConstants.CONVERT_DATA.intValue()); | |
526 | 553 | if (null != convertConfigDTO |
527 | 554 | && null != convertConfigDTO.getDatasourceType() |
528 | 555 | && !Objects.equals(convertConfigDTO.getDatasourceType(), DatasourceTypeEnum.ALL)) { |
... | ... | @@ -849,4 +876,33 @@ public class TkConvertConfigServiceImpl |
849 | 876 | objectNode.put("layoutY", layoutY + randomNum); |
850 | 877 | return objectNode; |
851 | 878 | } |
879 | + | |
880 | + private String getRuleChainId(ConvertConfigDTO convertConfig){ | |
881 | + //通过nodeType区分是转换脚本(0)、数据流转(1) | |
882 | + return StringUtils.isNotEmpty(convertConfig.getRuleChainId()) ? convertConfig.getRuleChainId(): | |
883 | + ruleChainService.getRootTenantRuleChain(new TenantId(UUID.fromString(convertConfig.getTenantId()))).getId().toString(); | |
884 | + } | |
885 | + | |
886 | + private void addConnectionInfo(String nodeConnectionName,Integer nodeType,RuleChainMetaData ruleChainMetaData){ | |
887 | + if(null != nodeConnectionName && nodeType == FastIotConstants.SCENE_REACT){ | |
888 | + int i = 0; | |
889 | + //查找事件节点下标 | |
890 | + int formIndex = 0; | |
891 | + int toIndex = 0; | |
892 | + for(RuleNode ruleNode : ruleChainMetaData.getNodes()){ | |
893 | + if(ruleNode.getType().equals(nodeConnectionName)){ | |
894 | + formIndex = i; | |
895 | + } | |
896 | + if(ruleNode.getType().equals(FastIotConstants.TkNodeClassName.GET_ORIGINATOR_FIELDS_NODE)){ | |
897 | + toIndex = i; | |
898 | + } | |
899 | + i++; | |
900 | + } | |
901 | + NodeConnectionInfo newConnectionInfo = new NodeConnectionInfo(); | |
902 | + newConnectionInfo.setFromIndex(formIndex); | |
903 | + newConnectionInfo.setToIndex(toIndex); | |
904 | + newConnectionInfo.setType("SUCCESS"); | |
905 | + ruleChainMetaData.getConnections().add(newConnectionInfo); | |
906 | + } | |
907 | + } | |
852 | 908 | } | ... | ... |
... | ... | @@ -15,33 +15,30 @@ import org.thingsboard.server.common.data.device.profile.*; |
15 | 15 | import org.thingsboard.server.common.data.id.*; |
16 | 16 | import org.thingsboard.server.common.data.rule.RuleChain; |
17 | 17 | import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; |
18 | -import org.thingsboard.server.common.data.yunteng.constant.ModelConstants; | |
19 | 18 | import org.thingsboard.server.common.data.yunteng.constant.QueryConstant; |
20 | 19 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
21 | 20 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
22 | 21 | import org.thingsboard.server.common.data.yunteng.dto.*; |
22 | +import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertConfigDTO; | |
23 | +import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDeviceProfileDTO; | |
23 | 24 | import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; |
24 | -import org.thingsboard.server.common.data.yunteng.enums.FunctionTypeEnum; | |
25 | 25 | import org.thingsboard.server.common.data.yunteng.enums.TcpAuthTypeEnum; |
26 | 26 | import org.thingsboard.server.common.data.yunteng.enums.TransportTypeEnum; |
27 | -import org.thingsboard.server.common.data.yunteng.id.TkThingsModelId; | |
28 | 27 | import org.thingsboard.server.common.data.yunteng.utils.CopyUtils; |
29 | 28 | import org.thingsboard.server.common.data.yunteng.utils.DateTimeUtils; |
30 | 29 | import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; |
31 | 30 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
32 | 31 | import org.thingsboard.server.dao.rule.RuleChainService; |
33 | 32 | import org.thingsboard.server.dao.yunteng.entities.*; |
34 | -import org.thingsboard.server.dao.yunteng.event.TkEdgeSyncService; | |
35 | 33 | import org.thingsboard.server.dao.yunteng.mapper.DeviceMapper; |
36 | -import org.thingsboard.server.dao.yunteng.mapper.ThingsModelMapper; | |
37 | 34 | import org.thingsboard.server.dao.yunteng.mapper.TkDeviceProfileCategoryMapper; |
38 | 35 | import org.thingsboard.server.dao.yunteng.mapper.TkDeviceProfileMapper; |
39 | -import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; | |
40 | 36 | import org.thingsboard.server.dao.yunteng.service.AbstractTbBaseService; |
37 | +import org.thingsboard.server.dao.yunteng.service.ConvertConfigService; | |
38 | +import org.thingsboard.server.dao.yunteng.service.SceneLinkageService; | |
41 | 39 | import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; |
42 | 40 | |
43 | 41 | import java.time.LocalDateTime; |
44 | -import java.time.ZoneOffset; | |
45 | 42 | import java.util.*; |
46 | 43 | import java.util.stream.Collectors; |
47 | 44 | |
... | ... | @@ -58,6 +55,10 @@ public class TkDeviceProfileServiceImpl |
58 | 55 | |
59 | 56 | private final RuleChainService ruleChainService; |
60 | 57 | |
58 | + private final ConvertConfigService convertConfigService; | |
59 | + | |
60 | + private final SceneLinkageService sceneLinkageService; | |
61 | + | |
61 | 62 | |
62 | 63 | |
63 | 64 | @Override |
... | ... | @@ -184,6 +185,83 @@ public class TkDeviceProfileServiceImpl |
184 | 185 | return tbDeviceProfile; |
185 | 186 | } |
186 | 187 | |
188 | + @Override | |
189 | + public List<String> checkRuleChainIdUsedByDeviceProfiles(String tenantId, UUID ruleChainId) { | |
190 | + List<TkDeviceProfileEntity> entities=baseMapper.selectList(new LambdaQueryWrapper<TkDeviceProfileEntity>().eq(TkDeviceProfileEntity::getTenantId, | |
191 | + UUID.fromString(tenantId)).eq(TkDeviceProfileEntity::getDefaultRuleChainId,ruleChainId)); | |
192 | + if(null == entities || entities.isEmpty()){ | |
193 | + return null; | |
194 | + } | |
195 | + return entities.stream().map(entity->entity.getName()).collect(Collectors.toList()); | |
196 | + } | |
197 | + | |
198 | + @Override | |
199 | + public void checkDeviceProfilesCanChangeRuleChain(List<String> deviceProfileIds, TenantId tenantId) { | |
200 | + List<UUID> queryDeviceProfileIds = deviceProfileIds.stream().map(UUID::fromString).collect(Collectors.toList()); | |
201 | + //判断以上产品配置是否都属于该租户 | |
202 | + List<TkDeviceProfileEntity> entities = baseMapper.selectList(new LambdaQueryWrapper<TkDeviceProfileEntity>(). | |
203 | + eq(TkDeviceProfileEntity::getTenantId,tenantId.getId()).in(TkDeviceProfileEntity::getId,queryDeviceProfileIds)); | |
204 | + if(null == entities || entities.size() != deviceProfileIds.size()){ | |
205 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_TENANT.getMessage()); | |
206 | + } | |
207 | + Set<String> ruleChainIds = new HashSet<>(entities.stream().map(entity->entity.getDefaultRuleChainId().toString()).collect(Collectors.toList())); | |
208 | + //按规则链进行分类,找到具体的产品 | |
209 | + Map<UUID,List<TkDeviceProfileEntity>> classification = entities.stream().collect(Collectors.groupingBy(TkDeviceProfileEntity::getDefaultRuleChainId)); | |
210 | + //判断数据流转是否使用该规则链 | |
211 | + checkDataConvertUsedDeviceProfile(tenantId.getId().toString(),new ArrayList<>(ruleChainIds),classification); | |
212 | + // 判断选中的产品是否被场景联动使用 | |
213 | + checkSceneLinkageUsedDeviceProfile( | |
214 | + tenantId.getId().toString(), entities, deviceProfileIds, new ArrayList<>(ruleChainIds)); | |
215 | + } | |
216 | + | |
217 | + private void checkDataConvertUsedDeviceProfile(String tenantId,List<String>ruleChainIds,Map<UUID,List<TkDeviceProfileEntity>> classification){ | |
218 | + List<ConvertConfigDTO> convertConfigs = convertConfigService.getConvertConfigUsedByRuleChainIds(tenantId,ruleChainIds,1); | |
219 | + if(null !=convertConfigs && !convertConfigs.isEmpty()){ | |
220 | + convertConfigs.stream().forEach(convertEntity->{ | |
221 | + //找出这个规则链里面,有哪些产品被数据流转使用了 | |
222 | + JsonNode datasource = convertEntity.getDatasourceContent(); | |
223 | + if(null != datasource){ | |
224 | + JsonNode usedDeviceProfileIds = datasource.get("convertProducts"); | |
225 | + List<String> ids = null; | |
226 | + if(usedDeviceProfileIds.isArray()){ | |
227 | + Iterator<JsonNode> iteratorNodes = usedDeviceProfileIds.iterator(); | |
228 | + while (iteratorNodes.hasNext()){ | |
229 | + ids.add(iteratorNodes.next().asText()); | |
230 | + } | |
231 | + } | |
232 | + if(null != ids){ | |
233 | + classification.entrySet().forEach(key->{ | |
234 | + for (TkDeviceProfileEntity entity : classification.get(key)) { | |
235 | + if(ids.contains(entity.getId().toString())){ | |
236 | + throw new TkDataValidationException(String.format(ErrorMessage. | |
237 | + CHOICE_DEVICE_PROFILE_RULE_CHAIN_IS_USED_FOR_CONVERT.getMessage(),entity.getName(),convertEntity.getName())); | |
238 | + } | |
239 | + } | |
240 | + }); | |
241 | + | |
242 | + } | |
243 | + } | |
244 | + }); | |
245 | + } | |
246 | + } | |
247 | + private void checkSceneLinkageUsedDeviceProfile(String tenantId,List<TkDeviceProfileEntity> entityList,List<String> choiceDeviceProfileIds,List<String> ruleChainIds){ | |
248 | + //通过选中产品ID列表,判断是否有被场景联动使用 | |
249 | + SceneLinkageDeviceProfileDTO usedSceneLinkage = sceneLinkageService.checkDeviceProfileIdsUsedForSceneLinkage(tenantId,choiceDeviceProfileIds,ruleChainIds); | |
250 | + if(null != usedSceneLinkage){ | |
251 | + //被场景联动使用了,才进行逻辑处理 | |
252 | + String sceneName = usedSceneLinkage.getSceneName(); | |
253 | + List<String> deviceProfileIds = usedSceneLinkage.getDeviceProfileIds(); | |
254 | + if(null != deviceProfileIds && !deviceProfileIds.isEmpty()){ | |
255 | + entityList.forEach(deviceProfileEntity->{ | |
256 | + if(deviceProfileIds.contains(deviceProfileEntity.getId().toString())){ | |
257 | + throw new TkDataValidationException(String.format(ErrorMessage. | |
258 | + CHOICE_DEVICE_PROFILE_RULE_CHAIN_IS_USED_FOR_SCENE.getMessage(), | |
259 | + deviceProfileEntity.getName(),sceneName)); | |
260 | + } | |
261 | + }); | |
262 | + } | |
263 | + } | |
264 | + } | |
187 | 265 | private void setDeviceScriptId(DeviceProfile profile, DeviceProfileTransportConfiguration transportConfiguration) { |
188 | 266 | JsonNode jsonNode = JacksonUtil.convertValue(transportConfiguration, JsonNode.class); |
189 | 267 | if (null != jsonNode) { |
... | ... | @@ -266,9 +344,10 @@ public class TkDeviceProfileServiceImpl |
266 | 344 | |
267 | 345 | @Override |
268 | 346 | public List<DeviceProfileDTO> findDeviceProfile( |
269 | - TenantId tenantId, String scriptId, DeviceTypeEnum deviceType, DeviceTransportType transportType,Boolean isSceneLinkage) { | |
347 | + TenantId tenantId, String scriptId, DeviceTypeEnum deviceType, DeviceTransportType transportType, | |
348 | + Boolean isSceneLinkage,String ruleChainId) { | |
270 | 349 | List<TkDeviceProfileEntity> listEntity = |
271 | - baseMapper.profileByScriptId(tenantId.getId(), scriptId, deviceType, transportType, null,isSceneLinkage); | |
350 | + baseMapper.profileByScriptId(tenantId.getId(), scriptId, deviceType, transportType, null,isSceneLinkage,ruleChainId); | |
272 | 351 | return listEntity.stream().map(entity -> { |
273 | 352 | return CopyUtils.copyAndReturn(entity, new DeviceProfileDTO()); |
274 | 353 | } |
... | ... | @@ -291,7 +370,7 @@ public class TkDeviceProfileServiceImpl |
291 | 370 | if (null == deviceProfileIds || deviceProfileIds.isEmpty()) { |
292 | 371 | return null; |
293 | 372 | } |
294 | - return baseMapper.profileByScriptId(tenantId.getId(), null, deviceType, transportType, deviceProfileIds,false); | |
373 | + return baseMapper.profileByScriptId(tenantId.getId(), null, deviceType, transportType, deviceProfileIds,false,null); | |
295 | 374 | } |
296 | 375 | |
297 | 376 | @Override |
... | ... | @@ -299,7 +378,11 @@ public class TkDeviceProfileServiceImpl |
299 | 378 | if (tenantId == null || null == ids) { |
300 | 379 | throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); |
301 | 380 | } |
302 | - return baseMapper.profileByTransportAndIds(tenantId, ids, transportType); | |
381 | + List<TkDeviceProfileEntity> entities = baseMapper.profileByTransportAndIds(tenantId, ids, transportType); | |
382 | + if(null == entities || entities.isEmpty()){ | |
383 | + return null; | |
384 | + } | |
385 | + return entities.stream().map(entity->entity.getDTO(DeviceProfileDTO.class)).collect(Collectors.toList()); | |
303 | 386 | } |
304 | 387 | |
305 | 388 | @Override | ... | ... |
... | ... | @@ -42,4 +42,14 @@ public class TkDoActionServiceImpl extends AbstractBaseService<DoActionMapper, T |
42 | 42 | .map(doAction -> doAction.getDTO(DoActionDTO.class)) |
43 | 43 | .collect(Collectors.toList()); |
44 | 44 | } |
45 | + | |
46 | + @Override | |
47 | + public List<DoActionDTO> findDoActionsBySceneLinkageIds(String tenantId, List<String> sceneLinkageIds) { | |
48 | + List<TkDoActionEntity> entities = baseMapper.selectList(new LambdaQueryWrapper<TkDoActionEntity>().eq(TkDoActionEntity::getTenantId,tenantId) | |
49 | + .in(TkDoActionEntity::getSceneLinkageId,sceneLinkageIds)); | |
50 | + if(null !=entities && !entities.isEmpty()){ | |
51 | + return entities.stream().map(entity->entity.getDTO(DoActionDTO.class)).collect(Collectors.toList()); | |
52 | + } | |
53 | + return null; | |
54 | + } | |
45 | 55 | } | ... | ... |
... | ... | @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
4 | 4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
5 | 5 | import lombok.RequiredArgsConstructor; |
6 | 6 | import org.springframework.stereotype.Service; |
7 | +import org.thingsboard.server.common.data.yunteng.dto.scene.DoConditionDTO; | |
7 | 8 | import org.thingsboard.server.dao.yunteng.entities.TkDoConditionEntity; |
8 | 9 | import org.thingsboard.server.dao.yunteng.mapper.DoConditionMapper; |
9 | 10 | import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; |
... | ... | @@ -20,12 +21,16 @@ import java.util.List; |
20 | 21 | @RequiredArgsConstructor |
21 | 22 | public class TkDoConditionServiceImpl extends AbstractBaseService<DoConditionMapper, TkDoConditionEntity> |
22 | 23 | implements DoConditionService { |
23 | - private final DoConditionMapper triggerMapper; | |
24 | + private final DoConditionMapper baseMapper; | |
24 | 25 | @Override |
25 | 26 | public List<TkDoConditionEntity> getConditions(String sceneId) { |
26 | 27 | LambdaQueryWrapper filter = new QueryWrapper<TkDoConditionEntity>().lambda() |
27 | 28 | .eq(TkDoConditionEntity::getSceneLinkageId,sceneId); |
28 | - return triggerMapper.selectList(filter); | |
29 | + return baseMapper.selectList(filter); | |
29 | 30 | } |
30 | 31 | |
32 | + @Override | |
33 | + public List<DoConditionDTO> listByDeviceProfileIds(String tenantId, List<String> deviceProfileIds) { | |
34 | + return baseMapper.listByDeviceProfileIds(tenantId,deviceProfileIds); | |
35 | + } | |
31 | 36 | } | ... | ... |
... | ... | @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JsonNode; |
7 | 7 | import com.fasterxml.jackson.databind.node.ObjectNode; |
8 | 8 | import java.util.*; |
9 | 9 | import java.util.concurrent.atomic.AtomicBoolean; |
10 | +import java.util.concurrent.atomic.AtomicReference; | |
10 | 11 | import java.util.stream.Collectors; |
11 | 12 | import lombok.RequiredArgsConstructor; |
12 | 13 | import org.apache.commons.lang3.StringUtils; |
... | ... | @@ -635,7 +636,54 @@ public class TkSceneLinkageServiceImpl |
635 | 636 | } |
636 | 637 | |
637 | 638 | @Override |
638 | - public SceneLinkageDTO selectById( String id) { | |
639 | - return baseMapper.findById(id); | |
639 | + public SceneLinkageDTO selectById(String tenantId,String id) { | |
640 | + return baseMapper.findById(tenantId,id); | |
641 | + } | |
642 | + @Override | |
643 | + public SceneLinkageDeviceProfileDTO checkDeviceProfileIdsUsedForSceneLinkage(String tenantId, List<String> deviceProfileIds,List<String> ruleChainIds) { | |
644 | + //先判断触发器是否被场景联动使用 | |
645 | + List<TriggerDTO> triggers = triggerService.getTriggersByDeviceProfileIds(tenantId,deviceProfileIds); | |
646 | + if(null != triggers && !triggers.isEmpty()){ | |
647 | + TriggerDTO triggerDTO = triggers.get(0); | |
648 | + return new SceneLinkageDeviceProfileDTO(triggerDTO.getSceneLinkageId(),triggerDTO.getSceneLinkageName(),List.of(triggerDTO.getDeviceProfileId())); | |
649 | + } | |
650 | + //再判断执行条件是否被场景联动使用 | |
651 | + List<DoConditionDTO> doConditions = doConditionService.listByDeviceProfileIds(tenantId,deviceProfileIds); | |
652 | + if(null != doConditions && !doConditions.isEmpty()){ | |
653 | + DoConditionDTO doCondition = doConditions.get(0); | |
654 | + return new SceneLinkageDeviceProfileDTO(doCondition.getSceneLinkageId(),doCondition.getSceneLinkageName(),List.of(doCondition.getDeviceProfileId())); | |
655 | + } | |
656 | + //最后判断执行动作是否被场景联动使用 | |
657 | + List<TkSceneLinkageEntity>entities = baseMapper.selectList(new LambdaQueryWrapper<TkSceneLinkageEntity>().eq(TkSceneLinkageEntity::getTenantId,tenantId) | |
658 | + .in(TkSceneLinkageEntity::getRuleChainId,ruleChainIds)); | |
659 | + if(null !=entities && !entities.isEmpty()){ | |
660 | + List<String> sceneLinkageIds = entities.stream().map(entity->entity.getId()).collect(Collectors.toList()); | |
661 | + List<DoActionDTO> doActionList = doActionService.findDoActionsBySceneLinkageIds(tenantId,sceneLinkageIds); | |
662 | + if(null !=doActionList && !doActionList.isEmpty()){ | |
663 | + for (DoActionDTO entity :doActionList){ | |
664 | + if(entity.getOutTarget().equals(ActionTypeEnum.DEVICE_OUT)){ | |
665 | + if(deviceProfileIds.contains(entity.getDeviceProfileId())){ | |
666 | + return new SceneLinkageDeviceProfileDTO(entity.getSceneLinkageId(), entity.getSceneLinkageName(), List.of(entity.getDeviceProfileId())); | |
667 | + } | |
668 | + } | |
669 | + if(entity.getOutTarget().equals(ActionTypeEnum.MSG_NOTIFY)){ | |
670 | + JsonNode doContext = entity.getDoContext(); | |
671 | + if(null !=doContext){ | |
672 | + JsonNode clearRuleJsonNode = doContext.get("clearRule"); | |
673 | + if(null !=clearRuleJsonNode && clearRuleJsonNode.isArray()){ | |
674 | + Iterator<JsonNode> iteratorNodes = clearRuleJsonNode.iterator(); | |
675 | + while (iteratorNodes.hasNext()){ | |
676 | + String deviceProfileId = iteratorNodes.next().get("deviceProfileId").asText(); | |
677 | + if(deviceProfileIds.contains(deviceProfileId)){ | |
678 | + return new SceneLinkageDeviceProfileDTO(entity.getSceneLinkageId(), entity.getSceneLinkageName(), List.of(deviceProfileId)); | |
679 | + } | |
680 | + } | |
681 | + } | |
682 | + } | |
683 | + } | |
684 | + } | |
685 | + } | |
686 | + } | |
687 | + return null; | |
640 | 688 | } |
641 | 689 | } | ... | ... |
... | ... | @@ -39,4 +39,9 @@ public class TkTriggerServiceImpl extends AbstractBaseService<TriggerMapper, TkT |
39 | 39 | .map(item -> item.getDTO(TriggerDTO.class)) |
40 | 40 | .collect(Collectors.toList()); |
41 | 41 | } |
42 | + | |
43 | + @Override | |
44 | + public List<TriggerDTO> getTriggersByDeviceProfileIds(String tenantId, List<String> deviceProfileIds) { | |
45 | + return triggerMapper.listByDeviceProfileIds(tenantId,deviceProfileIds); | |
46 | + } | |
42 | 47 | } | ... | ... |
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 | 6 | import org.apache.ibatis.annotations.Param; |
7 | +import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertConfigDTO; | |
6 | 8 | import org.thingsboard.server.dao.yunteng.ConvertConfig; |
7 | 9 | |
8 | 10 | import java.util.List; |
11 | +import java.util.Map; | |
9 | 12 | |
10 | 13 | @Mapper |
11 | 14 | public interface ConvertConfigMapper extends BaseMapper<ConvertConfig> { |
15 | + IPage<ConvertConfigDTO> getConvertPage( | |
16 | + IPage<?> page,@Param("tenantId") String tenantId, @Param("queryMap") Map<String, Object> queryMap); | |
12 | 17 | boolean updateConvertStatusByIds( |
13 | 18 | @Param("tenantId") String tenantId, @Param("ids") List<String> ids, @Param("status") Integer status); |
14 | 19 | } | ... | ... |
... | ... | @@ -16,4 +16,6 @@ import java.util.List; |
16 | 16 | @Mapper |
17 | 17 | public interface DoActionMapper extends BaseMapper<TkDoActionEntity> { |
18 | 18 | List<DoActionDTO> listBySceneId(@Param("sceneId") String sceneId); |
19 | + | |
20 | + List<DoActionDTO> listBySceneLinkageIds(@Param("tenantId") String tenantId,@Param("sceneLinkageIds") List<String> sceneLinkageIds); | |
19 | 21 | } | ... | ... |
... | ... | @@ -16,4 +16,6 @@ import java.util.List; |
16 | 16 | @Mapper |
17 | 17 | public interface DoConditionMapper extends BaseMapper<TkDoConditionEntity> { |
18 | 18 | List<DoConditionDTO> listBySceneId(@Param("sceneId") String sceneId); |
19 | + | |
20 | + List<DoConditionDTO> listByDeviceProfileIds(@Param("tenantId")String tenantId,@Param("deviceProfileIds")List<String> deviceProfileIds); | |
19 | 21 | } | ... | ... |
... | ... | @@ -30,6 +30,6 @@ public interface SceneLinkageMapper extends BaseMapper<TkSceneLinkageEntity> { |
30 | 30 | * @param id |
31 | 31 | * @return |
32 | 32 | */ |
33 | - SceneLinkageDTO findById( @Param("id") String id); | |
33 | + SceneLinkageDTO findById(@Param("tenantId") String tenantId, @Param("id") String id); | |
34 | 34 | |
35 | 35 | } | ... | ... |
... | ... | @@ -5,9 +5,6 @@ import com.baomidou.mybatisplus.core.metadata.IPage; |
5 | 5 | import org.apache.ibatis.annotations.Mapper; |
6 | 6 | import org.apache.ibatis.annotations.Param; |
7 | 7 | import org.thingsboard.server.common.data.DeviceTransportType; |
8 | -import org.thingsboard.server.common.data.id.DeviceProfileId; | |
9 | -import org.thingsboard.server.common.data.id.TenantId; | |
10 | -import org.thingsboard.server.common.data.yunteng.dto.*; | |
11 | 8 | import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; |
12 | 9 | import org.thingsboard.server.common.data.yunteng.enums.TransportTypeEnum; |
13 | 10 | import org.thingsboard.server.dao.yunteng.entities.TkDeviceProfileEntity; |
... | ... | @@ -39,9 +36,10 @@ public interface TkDeviceProfileMapper extends BaseMapper<TkDeviceProfileEntity> |
39 | 36 | @Param("deviceType") DeviceTypeEnum deviceType, |
40 | 37 | @Param("transportType")DeviceTransportType transportType, |
41 | 38 | @Param("deviceProfileIds")List<UUID> deviceProfileIds, |
42 | - @Param("isSceneLinkage") Boolean isSceneLinkage); | |
39 | + @Param("isSceneLinkage") Boolean isSceneLinkage, | |
40 | + @Param("ruleChainId") String ruleChainId); | |
43 | 41 | |
44 | - List<DeviceProfileDTO> profileByTransportAndIds( | |
42 | + List<TkDeviceProfileEntity> profileByTransportAndIds( | |
45 | 43 | @Param("tenantId") UUID tenantId, |
46 | 44 | @Param("projectIds") List<String> projectIds, |
47 | 45 | @Param("deviceType") TransportTypeEnum transportType); | ... | ... |
... | ... | @@ -17,4 +17,6 @@ import java.util.List; |
17 | 17 | public interface TriggerMapper extends BaseMapper<TkTriggerEntity> { |
18 | 18 | |
19 | 19 | List<TriggerDTO> listBySceneId(@Param("sceneId") String sceneId); |
20 | + | |
21 | + List<TriggerDTO> listByDeviceProfileIds(@Param("tenantId")String tenantId,@Param("deviceProfileIds")List<String> deviceProfileIds); | |
20 | 22 | } | ... | ... |
1 | 1 | package org.thingsboard.server.dao.yunteng.service; |
2 | 2 | |
3 | +import org.thingsboard.server.common.data.id.RuleChainId; | |
4 | +import org.thingsboard.server.common.data.id.TenantId; | |
3 | 5 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
4 | 6 | import org.thingsboard.server.common.data.rule.RuleNode; |
5 | 7 | import org.thingsboard.server.common.data.yunteng.dto.convert.ConvertConfigDTO; |
... | ... | @@ -21,7 +23,7 @@ public interface ConvertConfigService extends BaseService<ConvertConfig> { |
21 | 23 | |
22 | 24 | boolean deleteConvertConfig(DeleteDTO deleteDTO, Integer nodeType); |
23 | 25 | |
24 | - ConvertConfigDTO findConvertConfigDTOByName(String tenantId,String name,Integer nodeType); | |
26 | + ConvertConfigDTO findConvertConfigDTOByName(String tenantId,String name,String type,Integer nodeType); | |
25 | 27 | /** |
26 | 28 | * 通过数据转换的IDS获取需要组装的节点 |
27 | 29 | * |
... | ... | @@ -57,12 +59,35 @@ public interface ConvertConfigService extends BaseService<ConvertConfig> { |
57 | 59 | * 检查该租户下是否已启用转换脚本状态 |
58 | 60 | * |
59 | 61 | * @param tenantId 租户ID |
62 | + * @param ruleChainId 规则链ID | |
60 | 63 | * @return true已启用 false未启用 |
61 | 64 | */ |
62 | - boolean checkConvertJSStatusEnable(String tenantId); | |
65 | + boolean checkConvertJSStatusEnable(String tenantId,String ruleChainId); | |
63 | 66 | |
64 | 67 | boolean deleteRuleNode( |
65 | 68 | List<RuleNode> nodes, RuleChainMetaData ruleChainMetaData, Integer nodeType,String tenantId); |
66 | 69 | |
67 | 70 | void addRuleNode(List<RuleNode> nodes, RuleChainMetaData ruleChainMetaData, Integer nodeType,String tenantId); |
71 | + | |
72 | + RuleChainMetaData getTenantRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId, List<RuleNode> nodes, | |
73 | + int status, int nodeType, String nodeConnectionName); | |
74 | + | |
75 | + /** | |
76 | + * 根据规则链ID删除对应的转换脚本 | |
77 | + * @param tenantId | |
78 | + * @param ruleChainId | |
79 | + * @param nodeType | |
80 | + * @return | |
81 | + */ | |
82 | + boolean deleteConvertConfigByRuleChainId(String tenantId,String ruleChainId,Integer nodeType); | |
83 | + | |
84 | + | |
85 | + /** | |
86 | + * 检查数据流转或规则链转换脚本使用了以下规则链 | |
87 | + * @param tenantId 租户ID | |
88 | + * @param ruleChainIds 规则链ID列表 | |
89 | + * @param nodeType 0 转换脚本 1数据流转 | |
90 | + * @return 返回使用以下规则链的数据流转或规则链转换脚本信息 | |
91 | + */ | |
92 | + List<ConvertConfigDTO> getConvertConfigUsedByRuleChainIds(String tenantId,List<String> ruleChainIds,Integer nodeType); | |
68 | 93 | } | ... | ... |
... | ... | @@ -16,4 +16,6 @@ public interface DoActionService extends BaseService<TkDoActionEntity>{ |
16 | 16 | List<TkDoActionEntity> getActionsByAll(String sceneId,String tenantId); |
17 | 17 | |
18 | 18 | List<DoActionDTO> findDoActionByAlarmProfileIds(String tenantId, Set<String> alarmProfileIds); |
19 | + | |
20 | + List<DoActionDTO> findDoActionsBySceneLinkageIds(String tenantId,List<String> sceneLinkageIds); | |
19 | 21 | } | ... | ... |
1 | 1 | package org.thingsboard.server.dao.yunteng.service; |
2 | 2 | |
3 | +import org.thingsboard.server.common.data.yunteng.dto.scene.DoConditionDTO; | |
3 | 4 | import org.thingsboard.server.dao.yunteng.entities.TkDoConditionEntity; |
4 | 5 | |
5 | 6 | import java.util.List; |
... | ... | @@ -11,4 +12,6 @@ import java.util.List; |
11 | 12 | */ |
12 | 13 | public interface DoConditionService extends BaseService<TkDoConditionEntity>{ |
13 | 14 | List<TkDoConditionEntity> getConditions(String sceneId); |
15 | + | |
16 | + List<DoConditionDTO> listByDeviceProfileIds(String tenantId,List<String> deviceProfileIds); | |
14 | 17 | } | ... | ... |
... | ... | @@ -2,6 +2,7 @@ package org.thingsboard.server.dao.yunteng.service; |
2 | 2 | import com.fasterxml.jackson.databind.JsonNode; |
3 | 3 | import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; |
4 | 4 | import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDTO; |
5 | +import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDeviceProfileDTO; | |
5 | 6 | import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; |
6 | 7 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
7 | 8 | import org.thingsboard.server.dao.yunteng.entities.TkSceneLinkageEntity; |
... | ... | @@ -92,6 +93,14 @@ public interface SceneLinkageService extends BaseService<TkSceneLinkageEntity>{ |
92 | 93 | * 详情 |
93 | 94 | * @return |
94 | 95 | */ |
95 | - SceneLinkageDTO selectById(String id); | |
96 | + SceneLinkageDTO selectById(String tenantId,String id); | |
96 | 97 | |
98 | + /** | |
99 | + * 判断哪些产品被产品联动使用了,并返回被使用的产品ID列表 | |
100 | + * @param tenantId 租户ID | |
101 | + * @param deviceProfileIds 检查的场景联动 | |
102 | + * @param ruleChainIds 规则链ID列表 | |
103 | + * @return | |
104 | + */ | |
105 | + SceneLinkageDeviceProfileDTO checkDeviceProfileIdsUsedForSceneLinkage(String tenantId, List<String> deviceProfileIds,List<String> ruleChainIds); | |
97 | 106 | } | ... | ... |
... | ... | @@ -23,8 +23,8 @@ public interface TkDeviceProfileService extends TbBaseService<TkDeviceProfileEnt |
23 | 23 | |
24 | 24 | TkPageData<DeviceProfileDTO> page(Map<String, Object> queryMap, boolean isTenantAdmin); |
25 | 25 | |
26 | - List<DeviceProfileDTO> findDeviceProfile( | |
27 | - TenantId tenantId, String scriptId, DeviceTypeEnum deviceType, DeviceTransportType transportType,Boolean isSceneLinkage); | |
26 | + List<DeviceProfileDTO> findDeviceProfile(TenantId tenantId, String scriptId, DeviceTypeEnum deviceType, | |
27 | + DeviceTransportType transportType,Boolean isSceneLinkage,String ruleChainId); | |
28 | 28 | |
29 | 29 | List<TkDeviceProfileEntity> findCustomerDeviceProfiles( |
30 | 30 | TenantId tenantId, CustomerId customerId, DeviceTypeEnum deviceType,DeviceTransportType transportType); |
... | ... | @@ -64,4 +64,19 @@ public interface TkDeviceProfileService extends TbBaseService<TkDeviceProfileEnt |
64 | 64 | |
65 | 65 | DeviceProfile buildTbDeviceProfileFromDeviceProfileDTO(DeviceProfileDTO deviceProfileDTO, User user); |
66 | 66 | |
67 | + /** | |
68 | + * 判断规则链ID被哪些产品使用了 | |
69 | + * @param tenantId 租户ID | |
70 | + * @param ruleChainId 规则链ID | |
71 | + * @return 产品名称列表 | |
72 | + */ | |
73 | + List<String> checkRuleChainIdUsedByDeviceProfiles(String tenantId,UUID ruleChainId); | |
74 | + | |
75 | + /** | |
76 | + * 检查产品(设备配置)是否允许修改规则链 | |
77 | + * @param deviceProfileIds 设备配置ID列表 | |
78 | + * @param tenantId 租户ID | |
79 | + */ | |
80 | + void checkDeviceProfilesCanChangeRuleChain(List<String> deviceProfileIds,TenantId tenantId); | |
81 | + | |
67 | 82 | } | ... | ... |
... | ... | @@ -12,4 +12,6 @@ import java.util.List; |
12 | 12 | */ |
13 | 13 | public interface TriggerService extends BaseService<TkTriggerEntity>{ |
14 | 14 | List<TriggerDTO> getTrigger(String sceneId); |
15 | + | |
16 | + List<TriggerDTO> getTriggersByDeviceProfileIds(String tenantId, List<String> deviceProfileIds); | |
15 | 17 | } | ... | ... |
... | ... | @@ -9,6 +9,11 @@ |
9 | 9 | <result property="configuration" column="configuration" |
10 | 10 | typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> |
11 | 11 | <result property="tenantId" column="tenant_id"/> |
12 | + <result property="ruleChainId" column="rule_chain_id"/> | |
13 | + <result property="ruleChainName" column="rule_chain_name"/> | |
14 | + <result property="datasourceType" column="datasource_type" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> | |
15 | + <result property="datasourceContent" column="datasource_content" | |
16 | + typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> | |
12 | 17 | <result property="status" column="status"/> |
13 | 18 | <result property="nodeType" column="node_type"/> |
14 | 19 | <result property="createTime" column="create_time"/> |
... | ... | @@ -17,6 +22,10 @@ |
17 | 22 | <result property="updater" column="updater"/> |
18 | 23 | <result property="remark" column="remark"/> |
19 | 24 | </resultMap> |
25 | + <sql id="columns"> | |
26 | + tcc.id,tcc.name,tcc.type,tcc.configuration,tcc.tenant_id,tcc.rule_chain_id,tcc.status,tcc.node_type, | |
27 | + tcc.datasource_type,tcc.datasource_content,tcc.create_time,tcc.update_time,tcc.creator,tcc.updater,tcc.remark | |
28 | + </sql> | |
20 | 29 | <update id="updateConvertStatusByIds"> |
21 | 30 | UPDATE tk_convert_config |
22 | 31 | SET status =#{status} |
... | ... | @@ -30,4 +39,27 @@ |
30 | 39 | </if> |
31 | 40 | </where> |
32 | 41 | </update> |
42 | + <select id="getConvertPage" resultMap="convertMap"> | |
43 | + SELECT | |
44 | + <include refid="columns"/>,rc.name as rule_chain_name | |
45 | + FROM tk_convert_config tcc LEFT JOIN rule_chain rc ON tcc.rule_chain_id = rc.id ::TEXT | |
46 | + <where> | |
47 | + tcc.tenant_id = #{tenantId} | |
48 | + <if test="queryMap.name != null and queryMap.name != ''"> | |
49 | + AND tcc.name LIKE concat('%',#{queryMap.name}::TEXT,'%') | |
50 | + </if> | |
51 | + <if test="queryMap.status != null"> | |
52 | + AND tcc.status = #{queryMap.status} | |
53 | + </if> | |
54 | + <if test="queryMap.nodeType != null"> | |
55 | + AND tcc.node_type = #{queryMap.nodeType} | |
56 | + </if> | |
57 | + <if test="queryMap.startTime != null and queryMap.endTime != null"> | |
58 | + AND tcc.create_time >= #{queryMap.startTime} AND tcc.create_time < #{queryMap.endTime} | |
59 | + </if> | |
60 | + <if test="queryMap.ruleChainId != null and queryMap.ruleChainId != ''"> | |
61 | + AND tcc.rule_chain_id = #{queryMap.ruleChainId} | |
62 | + </if> | |
63 | + </where> | |
64 | + </select> | |
33 | 65 | </mapper> | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | <result property="commandType" column="command_type"/> |
16 | 16 | <result property="thingsModelId" column="things_model_id"/> |
17 | 17 | <result property="sceneLinkageId" column="scene_linkage_id"/> |
18 | + <result property="sceneLinkageName" column="scene_linkage_name"/> | |
18 | 19 | <result property="deviceProfileId" column="device_profile_id"/> |
19 | 20 | <result property="alarmProfileId" column="alarm_profile_id"/> |
20 | 21 | <result property="tenantId" column="tenant_id"/> |
... | ... | @@ -24,12 +25,21 @@ |
24 | 25 | <result property="creator" column="creator"/> |
25 | 26 | </resultMap> |
26 | 27 | <sql id="columns"> |
27 | - id,device_id,entity_type,device_type,do_context,out_target,call_type,scene_linkage_id,alarm_profile_id, | |
28 | - tenant_id,updater,update_time,create_time,creator,command_type,things_model_id,device_profile_id | |
28 | + tda.id,tda.device_id,tda.entity_type,tda.device_type,tda.do_context,tda.out_target,tda.call_type,tda.scene_linkage_id,tda.alarm_profile_id, | |
29 | + tda.tenant_id,tda.updater,tda.update_time,tda.create_time,tda.creator,tda.command_type,tda.things_model_id,tda.device_profile_id | |
29 | 30 | </sql> |
30 | 31 | <select id="listBySceneId" resultMap="actionDTO"> |
31 | 32 | SELECT |
32 | 33 | <include refid="columns"/> |
33 | - FROM tk_do_action WHERE scene_linkage_id = #{sceneId} | |
34 | + FROM tk_do_action tda WHERE tda.scene_linkage_id = #{sceneId} | |
35 | + </select> | |
36 | + <select id="listBySceneLinkageIds" resultMap="actionDTO"> | |
37 | + SELECT | |
38 | + <include refid="columns"/>,tsl.name as scene_linkage_name | |
39 | + FROM tk_do_action tda LEFT JOIN tk_scene_linkage tsl ON tsl.id = tda.scene_linkage_id | |
40 | + WHERE tda.tenant_id = #{tenantId} AND tda.scene_linkage_id IN | |
41 | + <foreach collection="sceneLinkageIds" item="sceneLinkageId" open="(" separator="," close=")"> | |
42 | + #{sceneLinkageId} | |
43 | + </foreach> | |
34 | 44 | </select> |
35 | 45 | </mapper> | ... | ... |
... | ... | @@ -12,6 +12,7 @@ |
12 | 12 | typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> |
13 | 13 | <result property="triggerType" column="trigger_type" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> |
14 | 14 | <result property="sceneLinkageId" column="scene_linkage_id"/> |
15 | + <result property="sceneLinkageName" column="scene_linkage_name"/> | |
15 | 16 | <result property="tenantId" column="tenant_id"/> |
16 | 17 | <result property="updater" column="updater"/> |
17 | 18 | <result property="deviceProfileId" column="device_profile_id"/> |
... | ... | @@ -21,13 +22,24 @@ |
21 | 22 | </resultMap> |
22 | 23 | |
23 | 24 | <sql id="columns"> |
24 | - id,entity_id,entity_type,device_type,trigger_condition,trigger_type,scene_linkage_id,tenant_id | |
25 | - ,creator,updater,create_time,update_time,device_profile_id | |
25 | + tdc.id,tdc.entity_id,tdc.entity_type,tdc.device_type,tdc.trigger_condition,tdc.trigger_type,tdc.scene_linkage_id,tdc.tenant_id | |
26 | + ,tdc.creator,tdc.updater,tdc.create_time,tdc.update_time,tdc.device_profile_id | |
26 | 27 | </sql> |
27 | 28 | |
28 | 29 | <select id="listBySceneId" resultMap="conditionDTO"> |
29 | 30 | SELECT |
30 | 31 | <include refid="columns"/> |
31 | - FROM tk_do_condition WHERE scene_linkage_id = #{sceneId} | |
32 | + FROM tk_do_condition tdc WHERE tdc.scene_linkage_id = #{sceneId} | |
33 | + </select> | |
34 | + | |
35 | + <select id="listByDeviceProfileIds" resultMap="conditionDTO"> | |
36 | + SELECT | |
37 | + <include refid="columns"/>,tsl.name as scene_linkage_name | |
38 | + FROM tk_do_condition tdc | |
39 | + LEFT JOIN tk_scene_linkage tsl ON tdc.scene_linkage_id = tsl.id | |
40 | + WHERE tdc.tenant_id = #{tenantId} AND tdc.device_profile_id IN | |
41 | + <foreach collection="deviceProfileIds" item="deviceProfileId" open="(" separator="," close=")"> | |
42 | + #{deviceProfileId} | |
43 | + </foreach> | |
32 | 44 | </select> |
33 | 45 | </mapper> | ... | ... |
... | ... | @@ -6,6 +6,8 @@ |
6 | 6 | <result property="id" column="id"/> |
7 | 7 | <result property="name" column="name"/> |
8 | 8 | <result property="organizationId" column="organization_id"/> |
9 | + <result property="ruleChainId" column="rule_chain_id"/> | |
10 | + <result property="ruleChainName" column="rule_chain_name"/> | |
9 | 11 | <result property="status" column="status"/> |
10 | 12 | <result property="description" column="description"/> |
11 | 13 | <result property="tenantId" column="tenant_id"/> |
... | ... | @@ -20,14 +22,15 @@ |
20 | 22 | |
21 | 23 | </resultMap> |
22 | 24 | <sql id="columns"> |
23 | - s.id,s.name,s.organization_id,s.status,s.description,s.tenant_id,s.updater,s.update_time,s.create_time,s.creator | |
25 | + s.id,s.name,s.organization_id,s.rule_chain_id,s.status,s.description,s.tenant_id,s.updater,s.update_time,s.create_time,s.creator | |
24 | 26 | </sql> |
25 | 27 | <select id="getScenePage" resultMap="sceneLinkageMap"> |
26 | 28 | SELECT |
27 | - <include refid="columns"/>,su.real_name creator_name | |
29 | + <include refid="columns"/>,su.real_name creator_name,rc.name as rule_chain_name | |
28 | 30 | FROM tk_scene_linkage s |
29 | 31 | LEFT JOIN sys_user su ON su.id = s.creator |
30 | 32 | LEFT JOIN tk_organization io ON io.id = s.organization_id |
33 | + LEFT JOIN rule_chain rc ON rc.id::TEXT = s.rule_chain_id | |
31 | 34 | <where> |
32 | 35 | <if test="queryMap.tenantId !=null and queryMap.tenantId !=''"> |
33 | 36 | AND s.tenant_id = #{queryMap.tenantId} |
... | ... | @@ -44,6 +47,9 @@ |
44 | 47 | #{organizationId} |
45 | 48 | </foreach> |
46 | 49 | </if> |
50 | + <if test="queryMap.ruleChainId != null and queryMap.ruleChainId != ''"> | |
51 | + AND s.rule_chain_id = #{queryMap.ruleChainId} | |
52 | + </if> | |
47 | 53 | <if test="queryMap.currentUser !=null"> |
48 | 54 | AND s.creator = #{queryMap.currentUser} |
49 | 55 | </if> |
... | ... | @@ -58,6 +64,7 @@ |
58 | 64 | LEFT JOIN sys_user su ON su.id = s.creator |
59 | 65 | LEFT JOIN tk_organization io ON io.id = s.organization_id |
60 | 66 | <where> |
67 | + s.tenant_id = #{tenantId} | |
61 | 68 | <if test="id !=null"> |
62 | 69 | AND s.id = #{id} |
63 | 70 | </if> | ... | ... |
... | ... | @@ -107,6 +107,9 @@ |
107 | 107 | <if test="scriptId !=null and scriptId !=''"> |
108 | 108 | AND base.script_id = #{scriptId} |
109 | 109 | </if> |
110 | + <if test="ruleChainId !=null and ruleChainId !=''"> | |
111 | + AND base.default_rule_chain_id::TEXT = #{ruleChainId} | |
112 | + </if> | |
110 | 113 | <if test="deviceType !=null"> |
111 | 114 | AND base.device_type = #{deviceType} |
112 | 115 | </if> | ... | ... |
... | ... | @@ -13,6 +13,7 @@ |
13 | 13 | typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/> |
14 | 14 | <result property="triggerType" column="trigger_type" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> |
15 | 15 | <result property="sceneLinkageId" column="scene_linkage_id"/> |
16 | + <result property="sceneLinkageName" column="scene_linkage_name"/> | |
16 | 17 | <result property="tenantId" column="tenant_id"/> |
17 | 18 | <result property="updater" column="updater"/> |
18 | 19 | <result property="updateTime" column="update_time"/> |
... | ... | @@ -21,13 +22,24 @@ |
21 | 22 | </resultMap> |
22 | 23 | |
23 | 24 | <sql id="columns"> |
24 | - id,entity_id,entity_type,device_type,trigger_condition,trigger_type,scene_linkage_id,tenant_id,creator | |
25 | - ,updater,create_time,update_time,device_profile_id | |
25 | + tt.id,tt.entity_id,tt.entity_type,tt.device_type,tt.trigger_condition,tt.trigger_type,tt.scene_linkage_id,tt.tenant_id,tt.creator | |
26 | + ,tt.updater,tt.create_time,tt.update_time,tt.device_profile_id | |
26 | 27 | </sql> |
27 | 28 | |
28 | 29 | <select id="listBySceneId" resultMap="triggerDTO"> |
29 | 30 | SELECT |
30 | 31 | <include refid="columns"/> |
31 | - FROM tk_trigger WHERE scene_linkage_id = #{sceneId} | |
32 | + FROM tk_trigger tt WHERE tt.scene_linkage_id = #{sceneId} | |
33 | + </select> | |
34 | + | |
35 | + <select id="listByDeviceProfileIds" resultMap="triggerDTO"> | |
36 | + SELECT | |
37 | + <include refid="columns"/>,tsl.name as scene_linkage_name | |
38 | + FROM tk_trigger tt | |
39 | + LEFT JOIN tk_scene_linkage tsl ON tt.scene_linkage_id = tsl.id | |
40 | + WHERE tt.tenant_id = #{tenantId} AND tt.device_profile_id IN | |
41 | + <foreach collection="deviceProfileIds" item="deviceProfileId" open="(" separator="," close=")"> | |
42 | + #{deviceProfileId} | |
43 | + </foreach> | |
32 | 44 | </select> |
33 | 45 | </mapper> | ... | ... |