Browse Source

Merge remote-tracking branch 'origin/master'

master
吴远 3 months ago
parent
commit
be7e56f753
  1. 7
      dk-api/api-system/src/main/java/org/dromara/system/api/RemoteConfigService.java
  2. 83
      dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/mqtt/MegaphoneTopicRequest.java
  3. 79
      dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/mqtt/MqttGatewayPublish.java
  4. 12
      dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/mqtt/property/PropertySetPublish.java
  5. 8
      dk-modules/business/src/main/java/org/dromara/business/controller/BusinessOperationController.java
  6. 12
      dk-modules/business/src/main/java/org/dromara/business/controller/BusinessTaskController.java
  7. 12
      dk-modules/resource/src/main/java/org/dromara/resource/dubbo/RemoteFileServiceImpl.java
  8. 6
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceQrtzController.java
  9. 172
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/MegaphoneController.java
  10. 77
      dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/PlayTextController.java
  11. 15
      dk-modules/sample/src/main/java/org/dromara/sample/manage/mapper/IPlayTextMapper.java
  12. 24
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/DisobeyDTO.java
  13. 22
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/FileDTO.java
  14. 18
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/PlayStartDTO.java
  15. 19
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/PlayVolumeDTO.java
  16. 19
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/RePlayDTO.java
  17. 20
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/TextDTO.java
  18. 48
      dk-modules/sample/src/main/java/org/dromara/sample/manage/model/entity/PlayTextEntity.java
  19. 37
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/IPlayTextService.java
  20. 110
      dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/PlayTextServiceImpl.java
  21. 2
      dk-modules/sample/src/main/java/org/dromara/sample/media/model/MediaFileDTO.java
  22. 1
      dk-modules/sample/src/main/java/org/dromara/sample/media/service/impl/FileServiceImpl.java
  23. 12
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/AiCompareController.java
  24. 2
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IAiCompareService.java
  25. 13
      dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/AiCompareServiceImpl.java
  26. 10
      dk-modules/system/src/main/java/org/dromara/system/dubbo/RemoteConfigServiceImpl.java
  27. 2
      dk-modules/system/src/main/java/org/dromara/system/service/ISysConfigService.java
  28. 7
      dk-modules/system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java

7
dk-api/api-system/src/main/java/org/dromara/system/api/RemoteConfigService.java

@ -14,4 +14,11 @@ public interface RemoteConfigService {
*/ */
boolean selectRegisterEnabled(String tenantId); boolean selectRegisterEnabled(String tenantId);
/**
* 获取流媒体ip
*
* @return true开启false关闭
*/
String selectStreamIp();
} }

83
dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/mqtt/MegaphoneTopicRequest.java

@ -0,0 +1,83 @@
package org.dromara.common.sdk.mqtt;
/**
* Unified topic request format.
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public class MegaphoneTopicRequest<T> {
/**
* The command is sent and the response is matched by the tid and bid fields in the message,
* and the reply should keep the tid and bid the same.
*/
protected String tid;
protected String bid;
protected Long timestamp;
protected String method;
protected T data;
public MegaphoneTopicRequest() {
}
@Override
public String toString() {
return "CommonTopicRequest{" +
"tid='" + tid + '\'' +
", bid='" + bid + '\'' +
", method='" + method + '\'' +
", timestamp=" + timestamp +
", data=" + data +
'}';
}
public String getTid() {
return tid;
}
public MegaphoneTopicRequest<T> setTid(String tid) {
this.tid = tid;
return this;
}
public String getBid() {
return bid;
}
public MegaphoneTopicRequest<T> setBid(String bid) {
this.bid = bid;
return this;
}
public String getMethod() {
return method;
}
public MegaphoneTopicRequest<T> setMethod(String method) {
this.method = method;
return this;
}
public Long getTimestamp() {
return timestamp;
}
public MegaphoneTopicRequest<T> setTimestamp(Long timestamp) {
this.timestamp = timestamp;
return this;
}
public T getData() {
return data;
}
public MegaphoneTopicRequest<T> setData(T data) {
this.data = data;
return this;
}
}

79
dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/mqtt/MqttGatewayPublish.java

@ -101,5 +101,84 @@ public class MqttGatewayPublish {
throw new CloudSDKException(CloudSDKErrorEnum.MQTT_PUBLISH_ABNORMAL, "No message reply received."); throw new CloudSDKException(CloudSDKErrorEnum.MQTT_PUBLISH_ABNORMAL, "No message reply received.");
} }
public void publishMegaphone(String topic, int qos, MegaphoneTopicRequest request) {
try {
log.debug("send topic: {}, payload: {}", topic, request.toString());
byte[] payload = Common.getObjectMapper().writeValueAsBytes(request);
messageGateway.publish(topic, payload, qos);
} catch (JsonProcessingException e) {
log.error("Failed to publish the message. {}", request.toString());
e.printStackTrace();
}
}
public <T> CommonTopicResponse<T> publishPlayTextWithReply(Class<T> clazz, String topic, MegaphoneTopicRequest request, int retryCount, long timeout) {
AtomicInteger time = new AtomicInteger(0);
boolean hasBid = StringUtils.hasText(request.getBid());
request.setBid(hasBid ? request.getBid() : UUID.randomUUID().toString());
// RetryServicesReplyReceiver
while (time.getAndIncrement() <= retryCount) {
this.publishMegaphone(topic, DEFAULT_RETRY_COUNT,request);
// If the message is not received in 3 seconds then resend it again.
CommonTopicResponse<T> receiver = Chan.getInstance(request.getTid(), true).get(request.getTid(), timeout);
// Need to match tid and bid.
if (Objects.nonNull(receiver)
&& receiver.getTid().equals(request.getTid())
&& receiver.getBid().equals(request.getBid())) {
if (clazz.isAssignableFrom(receiver.getData().getClass())) {
return receiver;
}
throw new TypeMismatchException(receiver.getData(), clazz);
}
// It must be guaranteed that the tid and bid of each message are different.
if (!hasBid) {
request.setBid(UUID.randomUUID().toString());
}
request.setTid(UUID.randomUUID().toString());
}
throw new CloudSDKException(CloudSDKErrorEnum.MQTT_PUBLISH_ABNORMAL, "No message reply received.");
}
public void publishPlayText(String topic, String reply, int qos, CommonTopicRequest request) {
try {
log.debug("send topic: {}, payload: {}", topic, request.toString());
byte[] payload = Common.getObjectMapper().writeValueAsBytes(request);
messageGateway.publish(topic, payload, qos);
} catch (JsonProcessingException e) {
log.error("Failed to publish the message. {}", request.toString());
e.printStackTrace();
}
}
public void publishPlayText(String topic, int qos, CommonTopicResponse response) {
try {
log.debug("send topic: {}, payload: {}", topic, response.toString());
byte[] payload = Common.getObjectMapper().writeValueAsBytes(response);
messageGateway.publish(topic, payload, qos);
} catch (JsonProcessingException e) {
log.error("Failed to publish the message. {}", response.toString());
e.printStackTrace();
}
}
public void publishPlayText(String topic, String reply, CommonTopicRequest request, int publishCount) {
AtomicInteger time = new AtomicInteger(0);
while (time.getAndIncrement() < publishCount) {
this.publishPlayText(topic, reply, DEFAULT_QOS, request);
}
}
public void publishPlayText(String topic, String reply, CommonTopicRequest request) {
this.publishPlayText(topic, reply, DEFAULT_QOS, request);
}
public void publishPlayTextReply(CommonTopicResponse response, String reply, MessageHeaders headers) {
this.publishPlayText(headers.get(MqttHeaders.RECEIVED_TOPIC) + reply, 2, response);
}
} }

12
dk-common/common-cloudsdk/src/main/java/org/dromara/common/sdk/mqtt/property/PropertySetPublish.java

@ -1,5 +1,6 @@
package org.dromara.common.sdk.mqtt.property; package org.dromara.common.sdk.mqtt.property;
import org.dromara.common.sdk.mqtt.MegaphoneTopicRequest;
import org.dromara.common.sdk.mqtt.MqttGatewayPublish; import org.dromara.common.sdk.mqtt.MqttGatewayPublish;
import org.dromara.common.sdk.mqtt.TopicConst; import org.dromara.common.sdk.mqtt.TopicConst;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@ -37,4 +38,15 @@ public class PropertySetPublish {
.setData(Objects.requireNonNull(data)), retryCount, timeout).getData(); .setData(Objects.requireNonNull(data)), retryCount, timeout).getData();
} }
public PropertySetReplyResultEnum publishPlayText(String topic, Object data, String method) {
return gatewayPublish.publishPlayTextWithReply(
PropertySetReplyResultEnum.class, topic, new MegaphoneTopicRequest<>()
.setTid(UUID.randomUUID().toString())
.setBid(null)
.setMethod(method)
.setTimestamp(System.currentTimeMillis())
.setData(Objects.requireNonNull(data)), MqttGatewayPublish.DEFAULT_RETRY_COUNT, MqttGatewayPublish.DEFAULT_RETRY_TIMEOUT).getData();
}
} }

8
dk-modules/business/src/main/java/org/dromara/business/controller/BusinessOperationController.java

@ -49,7 +49,7 @@ public class BusinessOperationController extends BaseController {
/** /**
* 查询运营中心-工单处理列表 * 查询运营中心-工单处理列表
*/ */
@SaCheckPermission("system:operation:list") @SaCheckPermission("business:operation:list")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo<BusinessOperationVo> list(BusinessOperationBo bo, PageQuery pageQuery) { public TableDataInfo<BusinessOperationVo> list(BusinessOperationBo bo, PageQuery pageQuery) {
return businessOperationService.queryPageList(bo, pageQuery); return businessOperationService.queryPageList(bo, pageQuery);
@ -58,7 +58,7 @@ public class BusinessOperationController extends BaseController {
/** /**
* 导出运营中心-工单处理列表 * 导出运营中心-工单处理列表
*/ */
@SaCheckPermission("system:operation:export") @SaCheckPermission("business:operation:export")
@Log(title = "运营中心-工单处理", businessType = BusinessType.EXPORT) @Log(title = "运营中心-工单处理", businessType = BusinessType.EXPORT)
@PostMapping("/export") @PostMapping("/export")
public void export(BusinessOperationBo bo, HttpServletResponse response) { public void export(BusinessOperationBo bo, HttpServletResponse response) {
@ -71,7 +71,7 @@ public class BusinessOperationController extends BaseController {
* *
* @param id 主键 * @param id 主键
*/ */
@SaCheckPermission("system:operation:query") @SaCheckPermission("business:operation:query")
@GetMapping("/{id}") @GetMapping("/{id}")
public R<BusinessOperationVo> getInfo(@NotNull(message = "主键不能为空") public R<BusinessOperationVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) { @PathVariable Long id) {
@ -81,7 +81,7 @@ public class BusinessOperationController extends BaseController {
/** /**
* 新增运营中心-工单处理 * 新增运营中心-工单处理
*/ */
@SaCheckPermission("system:operation:add") @SaCheckPermission("business:operation:add")
@Log(title = "运营中心-工单处理", businessType = BusinessType.INSERT) @Log(title = "运营中心-工单处理", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping() @PostMapping()

12
dk-modules/business/src/main/java/org/dromara/business/controller/BusinessTaskController.java

@ -43,7 +43,7 @@ public class BusinessTaskController extends BaseController {
/** /**
* 查询工单预约列表 * 查询工单预约列表
*/ */
@SaCheckPermission("system:task:list") @SaCheckPermission("business:task:list")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo<BusinessTaskVo> list(BusinessTaskBo bo, PageQuery pageQuery) { public TableDataInfo<BusinessTaskVo> list(BusinessTaskBo bo, PageQuery pageQuery) {
return businessTaskService.queryPageList(bo, pageQuery); return businessTaskService.queryPageList(bo, pageQuery);
@ -52,7 +52,7 @@ public class BusinessTaskController extends BaseController {
/** /**
* 导出工单预约列表 * 导出工单预约列表
*/ */
@SaCheckPermission("system:task:export") @SaCheckPermission("business:task:export")
@Log(title = "工单预约", businessType = BusinessType.EXPORT) @Log(title = "工单预约", businessType = BusinessType.EXPORT)
@PostMapping("/export") @PostMapping("/export")
public void export(BusinessTaskBo bo, HttpServletResponse response) { public void export(BusinessTaskBo bo, HttpServletResponse response) {
@ -65,7 +65,7 @@ public class BusinessTaskController extends BaseController {
* *
* @param id 主键 * @param id 主键
*/ */
@SaCheckPermission("system:task:query") @SaCheckPermission("business:task:query")
@GetMapping("/{id}") @GetMapping("/{id}")
public R<BusinessTaskVo> getInfo(@NotNull(message = "主键不能为空") public R<BusinessTaskVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) { @PathVariable Long id) {
@ -75,7 +75,7 @@ public class BusinessTaskController extends BaseController {
/** /**
* 新增工单预约 * 新增工单预约
*/ */
@SaCheckPermission("system:task:add") @SaCheckPermission("business:task:add")
@Log(title = "工单预约", businessType = BusinessType.INSERT) @Log(title = "工单预约", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping() @PostMapping()
@ -86,7 +86,7 @@ public class BusinessTaskController extends BaseController {
/** /**
* 修改工单预约 * 修改工单预约
*/ */
@SaCheckPermission("system:task:edit") @SaCheckPermission("business:task:edit")
@Log(title = "工单预约", businessType = BusinessType.UPDATE) @Log(title = "工单预约", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@PutMapping() @PutMapping()
@ -99,7 +99,7 @@ public class BusinessTaskController extends BaseController {
* *
* @param ids 主键串 * @param ids 主键串
*/ */
@SaCheckPermission("system:task:remove") @SaCheckPermission("business:task:remove")
@Log(title = "工单预约", businessType = BusinessType.DELETE) @Log(title = "工单预约", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}") @DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") public R<Void> remove(@NotEmpty(message = "主键不能为空")

12
dk-modules/resource/src/main/java/org/dromara/resource/dubbo/RemoteFileServiceImpl.java

@ -84,6 +84,16 @@ public class RemoteFileServiceImpl implements RemoteFileService {
@Override @Override
public List<RemoteFile> selectByIds(String ossIds){ public List<RemoteFile> selectByIds(String ossIds){
List<SysOssVo> sysOssVos = sysOssService.listByIds(StringUtils.splitTo(ossIds, Convert::toLong)); List<SysOssVo> sysOssVos = sysOssService.listByIds(StringUtils.splitTo(ossIds, Convert::toLong));
return MapstructUtils.convert(sysOssVos, RemoteFile.class); return sysOssVos.stream().map(sysOssVo -> {
RemoteFile remoteFile = new RemoteFile();
remoteFile.setFileName(sysOssVo.getFileName());
remoteFile.setOssId(sysOssVo.getOssId());
remoteFile.setUrl(sysOssVo.getUrl());
remoteFile.setOriginalName(sysOssVo.getOriginalName());
remoteFile.setFileSuffix(sysOssVo.getFileSuffix());
remoteFile.setOssId(sysOssVo.getOssId());
return remoteFile;
}).toList();
} }
} }

6
dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/DeviceQrtzController.java

@ -129,7 +129,7 @@ public class DeviceQrtzController extends BaseController {
} }
/** 自动飞行-新增航线*/ /** 自动飞行-新增航线*/
@SaCheckPermission("devict:qrtz:add") @SaCheckPermission("devict:qrtz:addFileList")
@Log(title = "自动飞行新增航线", businessType = BusinessType.INSERT) @Log(title = "自动飞行新增航线", businessType = BusinessType.INSERT)
@RepeatSubmit() @RepeatSubmit()
@PostMapping("/addFileList") @PostMapping("/addFileList")
@ -152,7 +152,7 @@ public class DeviceQrtzController extends BaseController {
* *
* @param id * @param id
*/ */
@SaCheckPermission("devict:qrtz:remove") @SaCheckPermission("devict:qrtz:fileRemove")
@Log(title = "自动飞行航线删除", businessType = BusinessType.DELETE) @Log(title = "自动飞行航线删除", businessType = BusinessType.DELETE)
@DeleteMapping("/fileRemove") @DeleteMapping("/fileRemove")
public R<Void> fileRemove(@NotNull(message = "主键不能为空")Long id) { public R<Void> fileRemove(@NotNull(message = "主键不能为空")Long id) {
@ -165,7 +165,7 @@ public class DeviceQrtzController extends BaseController {
* @param id 主键id * @param id 主键id
* @param sort 排序 * @param sort 排序
* */ * */
@SaCheckPermission("devict:qrtz:edit") @SaCheckPermission("devict:qrtz:fileSort")
@Log(title = "自动飞行编辑航线排序", businessType = BusinessType.UPDATE) @Log(title = "自动飞行编辑航线排序", businessType = BusinessType.UPDATE)
@RepeatSubmit() @RepeatSubmit()
@GetMapping("/fileSort") @GetMapping("/fileSort")

172
dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/MegaphoneController.java

@ -0,0 +1,172 @@
package org.dromara.sample.manage.controller;
import com.fasterxml.jackson.databind.JsonNode;
import io.seata.common.util.CollectionUtils;
import io.seata.common.util.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.business.api.RemoteBusinessTaskService;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sdk.common.HttpResultResponse;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.common.sdk.exception.CloudSDKErrorEnum;
import org.dromara.common.sdk.mqtt.CommonTopicRequest;
import org.dromara.common.sdk.mqtt.MegaphoneTopicRequest;
import org.dromara.common.sdk.mqtt.MqttGatewayPublish;
import org.dromara.common.sdk.mqtt.TopicConst;
import org.dromara.common.sdk.mqtt.property.PropertySetPublish;
import org.dromara.common.sdk.mqtt.property.PropertySetReplyResultEnum;
import org.dromara.common.sdk.mqtt.property.TopicPropertySetRequest;
import org.dromara.sample.manage.model.dto.*;
import org.dromara.sample.manage.service.IDeviceService;
import org.dromara.system.api.RemoteConfigService;
import org.dromara.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/15
*/
@RestController
@Slf4j
@RequestMapping("${url.manage.prefix}${url.manage.version}/megaphone")
@Tag(name = "无人机喊话模块")
public class MegaphoneController {
public static final String TOPIC = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT;
@Resource
private MqttGatewayPublish gatewayPublish;
@Resource
PropertySetPublish propertySetPublish;
@DubboReference
private RemoteConfigService remoteConfigService;
/**
* 喊话器-开始播放文档对应遥控器的录音广播模式
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/playStart")
@Operation(summary = "喊话器-开始播放文档(对应遥控器的录音广播模式)。", description = "喊话器-开始播放文档(对应遥控器的录音广播模式)")
public HttpResultResponse playStart(@PathVariable("workspace_id") String workspaceId,
@RequestParam("gatewaySn") String gatewaySn,
@RequestBody PlayStartDTO param
) {
String top = TOPIC+"gatewaySn"+"/services";
//发送mqtt
propertySetPublish.publishPlayText(top,param,"speaker_tts_play_start");
return HttpResultResponse.success();
}
/**
* 喊话器-重新播放文档
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/replay")
@Operation(summary = "喊话器-重新播放文档。", description = "喊话器-重新播放文档。")
public HttpResultResponse replay(@PathVariable("workspace_id") String workspaceId,
@RequestParam("gatewaySn") String gatewaySn,
@RequestBody RePlayDTO param
) {
String top = TOPIC+gatewaySn+"/services";
//发送mqtt
propertySetPublish.publishPlayText(top,param,"speaker_replay");
return HttpResultResponse.success();
}
/**
* 喊话器-暂停播放文档
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/playStop")
@Operation(summary = "喊话器-暂停播放文档。", description = "喊话器-暂停播放文档。")
public HttpResultResponse playStop(@PathVariable("workspace_id") String workspaceId,
@RequestParam("gatewaySn") String gatewaySn,
@RequestBody RePlayDTO param
) {
String top = TOPIC+gatewaySn+"/services";
//发送mqtt
propertySetPublish.publishPlayText(top,param,"speaker_play_stop");
return HttpResultResponse.success();
}
/**
* 喊话器-设置播放模式
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/playModeSet")
@Operation(summary = "喊话器-设置播放模式。", description = "喊话器-设置播放模式。")
public HttpResultResponse playModeSet(@PathVariable("workspace_id") String workspaceId,
@RequestParam("gatewaySn") String gatewaySn,
@RequestBody RePlayDTO param
) {
String top = TOPIC+gatewaySn+"/services";
//发送mqtt
propertySetPublish.publishPlayText(top,param,"speaker_play_mode_set");
return HttpResultResponse.success();
}
/**
* 喊话器-设置播放模式
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/widgetValueSet")
@Operation(summary = "喊话器-设置音量。", description = "喊话器-设置音量。")
public int widgetValueSet(@PathVariable("workspace_id") String workspaceId,
@RequestParam("gatewaySn") String gatewaySn,
@RequestBody PlayVolumeDTO param
) {
String top = TOPIC+gatewaySn+"/services";
//发送mqtt
PropertySetReplyResultEnum psdkWidgetValueSet = propertySetPublish.publishPlayText(top,param,"psdk_widget_value_set");
return psdkWidgetValueSet.getResult();
}
/**
* 获取媒体流ip
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/getStreamIp")
@Operation(summary = "获取媒体流ip。", description = "获取媒体流ip。")
public HttpResultResponse getStreamIp(@PathVariable("workspace_id") String workspaceId,
@RequestBody DisobeyDTO param
) {
String top = "task/image/disobey/smoke";
//发送mqtt
String s = remoteConfigService.selectStreamIp();
if (StringUtils.isNotEmpty(param.getModel())){
String[] split = param.getModel().split(",");
for (String type : split){
List<String> list = new ArrayList<>();
list.add(type);
DisobeyDTO disobeyDTO = new DisobeyDTO();
disobeyDTO.setUrl(s);
disobeyDTO.setOpen(param.getOpen());
disobeyDTO.setType(list);
gatewayPublish.publish(top,new CommonTopicRequest<>()
.setData(Objects.requireNonNull(disobeyDTO)),1);
}
}
return HttpResultResponse.success();
}
}

77
dk-modules/sample/src/main/java/org/dromara/sample/manage/controller/PlayTextController.java

@ -0,0 +1,77 @@
package org.dromara.sample.manage.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.sdk.common.HttpResultResponse;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.common.sdk.mqtt.MegaphoneTopicRequest;
import org.dromara.common.sdk.mqtt.MqttGatewayPublish;
import org.dromara.common.sdk.mqtt.TopicConst;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.model.dto.TextDTO;
import org.dromara.sample.manage.model.entity.PlayTextEntity;
import org.dromara.sample.manage.service.IDeviceService;
import org.dromara.sample.manage.service.IPlayTextService;
import org.dromara.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Objects;
import java.util.UUID;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/15
*/
@RestController
@Slf4j
@RequestMapping("${url.manage.prefix}${url.manage.version}/playText")
@Tag(name = "喊话text模版")
public class PlayTextController {
@Autowired
private IPlayTextService playTextService;
/**
* 获取一个工作区中所有在线设备的列表
* Get the topology list of all online devices in one workspace.
* @return
*/
@PostMapping("/{workspace_id}/playText/add")
@Operation(summary = "喊话text模版新增。", description = "喊话text模版新增")
public HttpResultResponse playTextAdd(@RequestBody PlayTextEntity param
) {
return playTextService.insertPlayText(param);
}
@PostMapping("/{workspace_id}/playText/update")
@Operation(summary = "喊话text模版修改。", description = "喊话text模版修改")
public HttpResultResponse playTextUpdate(@RequestBody PlayTextEntity param
) {
return playTextService.updatePlayText(param);
}
@PutMapping("/{workspace_id}/playText/deleted")
@Operation(summary = "喊话text模版删除", description = "喊话text模版删除")
public HttpResultResponse playTextDeleted(@RequestParam(name = "textSn") String textSn
) {
return playTextService.deletedPlayText(textSn);
}
@PostMapping("/{workspace_id}/playText/page")
@Operation(summary = "喊话text模版列表", description = "喊话text模版列表")
public HttpResultResponse<PaginationData<PlayTextEntity>> pageText(
@RequestParam(name = "pageNum", defaultValue = "1") Long page,
@RequestParam(name = "pageSize", defaultValue = "10") Long pageSize
) {
PaginationData<PlayTextEntity> pageText = playTextService.pageText(page, pageSize);
return HttpResultResponse.success(pageText);
}
}

15
dk-modules/sample/src/main/java/org/dromara/sample/manage/mapper/IPlayTextMapper.java

@ -0,0 +1,15 @@
package org.dromara.sample.manage.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.dromara.sample.manage.model.entity.DeviceEntity;
import org.dromara.sample.manage.model.entity.PlayTextEntity;
/**
*
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public interface IPlayTextMapper extends BaseMapper<PlayTextEntity> {
}

24
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/DisobeyDTO.java

@ -0,0 +1,24 @@
package org.dromara.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.List;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DisobeyDTO {
//1 开 2 关
private int open;
private String url;
private String model;
private List<String> type;
}

22
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/FileDTO.java

@ -0,0 +1,22 @@
package org.dromara.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class FileDTO {
private String format;
private String md5;
private String name;
private String url;
}

18
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/PlayStartDTO.java

@ -0,0 +1,18 @@
package org.dromara.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PlayStartDTO {
private int psdkIndex;
TextDTO tts;
}

19
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/PlayVolumeDTO.java

@ -0,0 +1,19 @@
package org.dromara.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PlayVolumeDTO {
private int psdk_index;
private int play_volume;
}

19
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/RePlayDTO.java

@ -0,0 +1,19 @@
package org.dromara.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class RePlayDTO {
private int psdk_index;
private int play_mode;
}

20
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/dto/TextDTO.java

@ -0,0 +1,20 @@
package org.dromara.sample.manage.model.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @author sean.zhou
* @version 0.1
* @date 2021/11/23
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TextDTO {
private String md5;
private String name;
private String text;
}

48
dk-modules/sample/src/main/java/org/dromara/sample/manage/model/entity/PlayTextEntity.java

@ -0,0 +1,48 @@
package org.dromara.sample.manage.model.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
/**
* The entity class of the device
*
* @author sean.zhou
* @version 0.1
* @date 2021/11/10
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "play_text")
public class PlayTextEntity implements Serializable {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField(value = "text_sn")
private String textSn;
@TableField(value = "name")
private String name;
@TableField(value = "text")
private String text;
@TableField(value = "md5")
private String md5;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}

37
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/IPlayTextService.java

@ -0,0 +1,37 @@
package org.dromara.sample.manage.service;
import com.fasterxml.jackson.databind.JsonNode;
import org.dromara.common.sdk.cloudapi.device.ControlSourceEnum;
import org.dromara.common.sdk.cloudapi.device.DeviceOsdHost;
import org.dromara.common.sdk.cloudapi.device.DockModeCodeEnum;
import org.dromara.common.sdk.cloudapi.device.DroneModeCodeEnum;
import org.dromara.common.sdk.common.HttpResultResponse;
import org.dromara.common.sdk.common.PaginationData;
import org.dromara.common.sdk.config.version.GatewayManager;
import org.dromara.common.websocket.dto.BizCodeEnum;
import org.dromara.sample.manage.model.dto.DeviceDTO;
import org.dromara.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
import org.dromara.sample.manage.model.dto.TopologyDeviceDTO;
import org.dromara.sample.manage.model.dto.WorkspaceDTO;
import org.dromara.sample.manage.model.entity.PlayTextEntity;
import org.dromara.sample.manage.model.param.DeviceQueryParam;
import java.util.List;
import java.util.Optional;
/**
* @author sean.zhou
* @date 2021/11/10
* @version 0.1
*/
public interface IPlayTextService {
HttpResultResponse insertPlayText(PlayTextEntity param);
HttpResultResponse updatePlayText(PlayTextEntity param);
HttpResultResponse deletedPlayText(String textSn);
PaginationData<PlayTextEntity> pageText(Long page, Long pageSize);
}

110
dk-modules/sample/src/main/java/org/dromara/sample/manage/service/impl/PlayTextServiceImpl.java

@ -0,0 +1,110 @@
package org.dromara.sample.manage.service.impl;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.sdk.cloudapi.device.*;
import org.dromara.common.sdk.cloudapi.firmware.*;
import org.dromara.common.sdk.cloudapi.firmware.api.AbstractFirmwareService;
import org.dromara.common.sdk.cloudapi.property.api.AbstractPropertyService;
import org.dromara.common.sdk.cloudapi.tsa.DeviceIconUrl;
import org.dromara.common.sdk.cloudapi.tsa.TopologyDeviceModel;
import org.dromara.common.sdk.common.*;
import org.dromara.common.sdk.config.version.GatewayManager;
import org.dromara.common.sdk.exception.CloudSDKException;
import org.dromara.common.sdk.mqtt.IMqttTopicService;
import org.dromara.common.sdk.mqtt.MqttGatewayPublish;
import org.dromara.common.sdk.mqtt.events.EventsSubscribe;
import org.dromara.common.sdk.mqtt.osd.OsdSubscribe;
import org.dromara.common.sdk.mqtt.property.PropertySetReplyResultEnum;
import org.dromara.common.sdk.mqtt.property.PropertySetSubscribe;
import org.dromara.common.sdk.mqtt.requests.RequestsSubscribe;
import org.dromara.common.sdk.mqtt.services.ServicesReplyData;
import org.dromara.common.sdk.mqtt.services.ServicesSubscribe;
import org.dromara.common.sdk.mqtt.services.TopicServicesResponse;
import org.dromara.common.sdk.mqtt.state.StateSubscribe;
import org.dromara.common.sdk.mqtt.status.StatusSubscribe;
import org.dromara.common.websocket.dto.BizCodeEnum;
import org.dromara.sample.common.error.CommonErrorEnum;
import org.dromara.sample.component.mqtt.model.EventsReceiver;
import org.dromara.sample.control.model.enums.DroneAuthorityEnum;
import org.dromara.sample.manage.mapper.IDeviceMapper;
import org.dromara.sample.manage.mapper.IPlayTextMapper;
import org.dromara.sample.manage.model.dto.*;
import org.dromara.sample.manage.model.entity.DeviceEntity;
import org.dromara.sample.manage.model.entity.PlayTextEntity;
import org.dromara.sample.manage.model.enums.DeviceFirmwareStatusEnum;
import org.dromara.sample.manage.model.enums.PropertySetFieldEnum;
import org.dromara.sample.manage.model.enums.UserTypeEnum;
import org.dromara.sample.manage.model.param.DeviceQueryParam;
import org.dromara.sample.manage.model.receiver.BasicDeviceProperty;
import org.dromara.sample.manage.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
/**
*
* @author sean.zhou
* @version 0.1
* @date 2021/11/10
*/
@Service
@Slf4j
@Transactional
public class PlayTextServiceImpl implements IPlayTextService {
@Autowired
private IPlayTextMapper playTextMapper;
@Override
public HttpResultResponse insertPlayText(PlayTextEntity param) {
param.setTextSn(String.valueOf(UUID.randomUUID()));
param.setMd5(SecureUtil.md5(param.getText()));
if(playTextMapper.insert(param) > 0){
return HttpResultResponse.success();
}
return HttpResultResponse.error("新增失败");
}
@Override
public HttpResultResponse updatePlayText(PlayTextEntity param) {
param.setMd5(SecureUtil.md5(param.getText()));
int update = playTextMapper.update(param,
new LambdaUpdateWrapper<PlayTextEntity>().eq(PlayTextEntity::getTextSn, param.getTextSn()));
if(update > 0){
return HttpResultResponse.success();
}
return HttpResultResponse.error("修改失败");
}
@Override
public HttpResultResponse deletedPlayText(String textSn) {
int delete = playTextMapper.delete(
new LambdaUpdateWrapper<PlayTextEntity>().eq(PlayTextEntity::getTextSn, textSn));
if(delete > 0){
return HttpResultResponse.success();
}
return HttpResultResponse.error("删除失败");
}
@Override
public PaginationData<PlayTextEntity> pageText(Long page, Long pageSize) {
Page<PlayTextEntity> pagination = playTextMapper.selectPage(new Page<>(page, pageSize),
new LambdaQueryWrapper<PlayTextEntity>()
.orderByDesc(true,PlayTextEntity::getId));
return new PaginationData<PlayTextEntity>(pagination.getRecords(), new Pagination(pagination.getCurrent(), pagination.getSize(), pagination.getTotal()));
}
}

2
dk-modules/sample/src/main/java/org/dromara/sample/media/model/MediaFileDTO.java

@ -64,6 +64,6 @@ public class MediaFileDTO {
private Double relativeAltitude; private Double relativeAltitude;
private String jobId; private String jobId;
private Integer fileIndex;
private String url; private String url;
} }

1
dk-modules/sample/src/main/java/org/dromara/sample/media/service/impl/FileServiceImpl.java

@ -221,6 +221,7 @@ public class FileServiceImpl implements IFileService {
.objectKey(entity.getObjectKey()) .objectKey(entity.getObjectKey())
.tinnyFingerprint(entity.getTinnyFingerprint()) .tinnyFingerprint(entity.getTinnyFingerprint())
.payload(entity.getPayload()) .payload(entity.getPayload())
.fileIndex(entity.getFileIndex())
.createTime(entity.getCreateTime()) .createTime(entity.getCreateTime())
.drone(entity.getDrone()) .drone(entity.getDrone())
.lat(entity.getLat()) .lat(entity.getLat())

12
dk-modules/sample/src/main/java/org/dromara/sample/wayline/controller/AiCompareController.java

@ -64,7 +64,7 @@ public class AiCompareController extends BaseController {
/** /**
* 提交对比列表 * 提交对比列表
*/ */
@SaCheckPermission("sample:compare:list") @SaCheckPermission("sample:compare:queueList")
@GetMapping("/queueList") @GetMapping("/queueList")
public TableDataInfo<AiCompareQueueEntity> queueList(AiCompareQueueDTO bo, PageQuery pageQuery) { public TableDataInfo<AiCompareQueueEntity> queueList(AiCompareQueueDTO bo, PageQuery pageQuery) {
return aiCompareQueueService.queueList(bo, pageQuery); return aiCompareQueueService.queueList(bo, pageQuery);
@ -74,7 +74,7 @@ public class AiCompareController extends BaseController {
* 模板列表 * 模板列表
* param createTime 当前记录的时间 * param createTime 当前记录的时间
*/ */
@SaCheckPermission("sample:compare:list") @SaCheckPermission("sample:compare:template")
@GetMapping("/templateList") @GetMapping("/templateList")
public List<WaylineJobEntity> templateList(String wayline_id, String create_time) { public List<WaylineJobEntity> templateList(String wayline_id, String create_time) {
return aiCompareQueueService.templateList(wayline_id,create_time); return aiCompareQueueService.templateList(wayline_id,create_time);
@ -153,6 +153,7 @@ public class AiCompareController extends BaseController {
/** /**
* 算法结果 算法使用 * 算法结果 算法使用
*/ */
@PostMapping("/expose") @PostMapping("/expose")
public R<Void> expose(@RequestBody List<Map<String,Object>> mapList) { public R<Void> expose(@RequestBody List<Map<String,Object>> mapList) {
return toAjax(aiCompareService.expose(mapList)); return toAjax(aiCompareService.expose(mapList));
@ -169,6 +170,7 @@ public class AiCompareController extends BaseController {
/** /**
* 验证预警列表 * 验证预警列表
*/ */
@SaCheckPermission("sample:compare:alertList")
@PostMapping("/alertList") @PostMapping("/alertList")
public R<List<RemoteBusinessAlertVo>> alertList(String jobId) { public R<List<RemoteBusinessAlertVo>> alertList(String jobId) {
return R.ok(aiCompareService.alertList(jobId)); return R.ok(aiCompareService.alertList(jobId));
@ -180,6 +182,7 @@ public class AiCompareController extends BaseController {
* 删除验证列表预警 * 删除验证列表预警
* @param ids 主键串 * @param ids 主键串
* * */ * * */
@SaCheckPermission("sample:compare:delAlertList")
@DeleteMapping("/delAlertList") @DeleteMapping("/delAlertList")
public R<Void> delAlertList(List<Long>ids) { public R<Void> delAlertList(List<Long>ids) {
return toAjax(aiCompareService.delAlertList(ids)); return toAjax(aiCompareService.delAlertList(ids));
@ -188,8 +191,9 @@ public class AiCompareController extends BaseController {
/** /**
* 提交 * 提交
*/ */
@SaCheckPermission("sample:compare:commit")
@PostMapping("/commit") @PostMapping("/commit")
public R<Boolean> commitAlert(@RequestBody List<Map<String,Object>> alertList) { public R<Boolean> commitAlert(@RequestBody Map<String,Object>compareMap) {
return R.ok(aiCompareService.commitAlert(alertList)); return R.ok(aiCompareService.commitAlert(compareMap));
} }
} }

2
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/IAiCompareService.java

@ -33,5 +33,5 @@ public interface IAiCompareService {
List<RemoteBusinessAlertVo> alertList(String jobId); List<RemoteBusinessAlertVo> alertList(String jobId);
Boolean delAlertList(List<Long>ids); Boolean delAlertList(List<Long>ids);
Boolean commitAlert(List<Map<String, Object>> alertList); Boolean commitAlert(Map<String, Object> compareMap);
} }

13
dk-modules/sample/src/main/java/org/dromara/sample/wayline/service/impl/AiCompareServiceImpl.java

@ -247,11 +247,14 @@ public class AiCompareServiceImpl implements IAiCompareService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public Boolean commitAlert(List<Map<String, Object>> alertList) { public Boolean commitAlert(Map<String,Object>compareMap) {
Long compareId = Convert.toLong(compareMap.get("compareId"));
List<Map<String, Object>>alertList = (List<Map<String, Object>>)compareMap.get("commitAlert");
if (ObjectUtil.isEmpty(alertList)) { if (ObjectUtil.isEmpty(alertList)) {
throw new ServiceException("预警信息为空!"); throw new ServiceException("预警信息为空!");
} }
List<RemoteStartProcess> startProcessList = alertList.stream().map(map -> { List<RemoteStartProcess> startProcessList = alertList.stream().map(map -> {
String flowCode = remoteWorkflowService.getFlowCode(map.get("labelEn").toString()); String flowCode = remoteWorkflowService.getFlowCode(map.get("labelEn").toString());
RemoteStartProcess remoteStartProcess = new RemoteStartProcess(); RemoteStartProcess remoteStartProcess = new RemoteStartProcess();
@ -259,10 +262,12 @@ public class AiCompareServiceImpl implements IAiCompareService {
remoteStartProcess.setFlowCode(flowCode); remoteStartProcess.setFlowCode(flowCode);
return remoteStartProcess; return remoteStartProcess;
}).toList(); }).toList();
//更新部门信息 //更新部门信息
businessAlertService.batchUpdateDept(alertList); businessAlertService.batchUpdateDept(alertList);
AiCompareEntity aiCompareEntity=new AiCompareEntity();
aiCompareEntity.setId(compareId);
aiCompareEntity.setStatus(AiCompareStatusConstants.COMPARE_STATUS_4);
aiCompareMapper.updateById(aiCompareEntity);
return remoteWorkflowService.startWorkFlowBatch(startProcessList); return remoteWorkflowService.startWorkFlowBatch(startProcessList);
} }

10
dk-modules/system/src/main/java/org/dromara/system/dubbo/RemoteConfigServiceImpl.java

@ -1,8 +1,10 @@
package org.dromara.system.dubbo; package org.dromara.system.dubbo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboService; import org.apache.dubbo.config.annotation.DubboService;
import org.dromara.system.api.RemoteConfigService; import org.dromara.system.api.RemoteConfigService;
import org.dromara.system.domain.SysConfig;
import org.dromara.system.service.ISysConfigService; import org.dromara.system.service.ISysConfigService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -18,6 +20,8 @@ public class RemoteConfigServiceImpl implements RemoteConfigService {
private final ISysConfigService configService; private final ISysConfigService configService;
private final ISysConfigService sysConfigService;
/** /**
* 获取注册开关 * 获取注册开关
*/ */
@ -26,4 +30,10 @@ public class RemoteConfigServiceImpl implements RemoteConfigService {
return configService.selectRegisterEnabled(tenantId); return configService.selectRegisterEnabled(tenantId);
} }
@Override
public String selectStreamIp() {
String ip =sysConfigService.selectStreamIp();
return ip;
}
} }

2
dk-modules/system/src/main/java/org/dromara/system/service/ISysConfigService.java

@ -84,4 +84,6 @@ public interface ISysConfigService {
*/ */
boolean checkConfigKeyUnique(SysConfigBo config); boolean checkConfigKeyUnique(SysConfigBo config);
String selectStreamIp();
} }

7
dk-modules/system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java

@ -203,4 +203,11 @@ public class SysConfigServiceImpl implements ISysConfigService {
return true; return true;
} }
@Override
public String selectStreamIp() {
SysConfig sysConfig = baseMapper.selectOne(new LambdaQueryWrapper<SysConfig>()
.eq(SysConfig::getConfigKey, "stream"));
return sysConfig.getConfigValue();
}
} }

Loading…
Cancel
Save