diff --git a/iot-common/iot-common-core/src/main/java/cc/iotkit/common/enums/ErrCode.java b/iot-common/iot-common-core/src/main/java/cc/iotkit/common/enums/ErrCode.java index 9671aedd..6475e3f3 100644 --- a/iot-common/iot-common-core/src/main/java/cc/iotkit/common/enums/ErrCode.java +++ b/iot-common/iot-common-core/src/main/java/cc/iotkit/common/enums/ErrCode.java @@ -46,10 +46,23 @@ public enum ErrCode implements IEnum { INIT_PRODUCER_ERROR(00000000, "初始化MQ生产者失败"), SEND_MSG_ERROR(00000000, "发送消息失败"), + /** + * 大屏通用异常段 + * */ + RESOURCE_FILE_NOT_FOUND(00000000, "资源包为空"), + BIG_SCREEN_NOT_FOUND(00000000, "大屏不存在"), + BIG_SCREEN_ALREADY(00000000, "大屏已存在"), + ADD_BIG_SCREEN_ERROR(00000000, "保存大屏失败"), + DELETE_BIG_SCREEN_ERROR(00000000, "删除大屏资源失败"), + SCREEN_API_NOT_FOUND(00000000, "大屏接口不存在"), + ADD_SCREEN_API_ERROR(00000000, "添加大屏接口失败"), + SCREEN_PUBLISH_ERROR(00000000, "大屏发布失败"), + API_LIST_BLANK(00000000, "接口列表为空"), /** * 业务通用异常段 */ + ID_BLANK(00000000, "ID为空"), TASK_NOT_SUPPORT_RENEW(00000000, "任务不支持续订"), GROUP_ALREADY(00000000, "分组已经存在"), GROUP_NOT_FOUND(00000000, "分组不存在"), diff --git a/iot-common/iot-common-dao/iot-common-model/src/main/java/cc/iotkit/model/screen/Screen.java b/iot-common/iot-common-dao/iot-common-model/src/main/java/cc/iotkit/model/screen/Screen.java new file mode 100644 index 00000000..229f0821 --- /dev/null +++ b/iot-common/iot-common-dao/iot-common-model/src/main/java/cc/iotkit/model/screen/Screen.java @@ -0,0 +1,47 @@ +package cc.iotkit.model.screen; + +import cc.iotkit.model.Owned; +import lombok.Data; + +/** + * @Author:tfd + * @Date:2023/6/25 15:04 + */ +@Data +public class Screen implements Owned { + + public static final String STATE_STOPPED = "stopped"; + public static final String STATE_RUNNING = "running"; + + private Long id; + private String uid; + /** + * 大屏名称 + */ + private String name; + + /** + * 资源文件 + */ + private String resourceFile; + + /** + * 端口 + */ + private Integer port; + + /** + * 发布状态 + */ + private String state; + + /** + * 创建时间 + */ + private Long createAt; + + /** + * 是否为默认大屏 + */ + private Boolean isDefault; +} diff --git a/iot-common/iot-common-dao/iot-common-model/src/main/java/cc/iotkit/model/screen/ScreenApi.java b/iot-common/iot-common-dao/iot-common-model/src/main/java/cc/iotkit/model/screen/ScreenApi.java new file mode 100644 index 00000000..228f36e7 --- /dev/null +++ b/iot-common/iot-common-dao/iot-common-model/src/main/java/cc/iotkit/model/screen/ScreenApi.java @@ -0,0 +1,49 @@ +package cc.iotkit.model.screen; + +import cc.iotkit.model.Owned; +import lombok.Data; + +/** + * @Author:tfd + * @Date:2023/6/25 15:04 + */ +@Data +public class ScreenApi implements Owned { + + private Long id; + private String uid; + /** + * 大屏id + */ + private Long screenId; + + /** + * 接口路径 + */ + private String apiPath; + + /** + * 接口参数 + */ + private String apiParams; + + /** + * 请求方法 + */ + private String httpMethod; + + /** + * 数据源 + */ + private String dataSource; + + /** + * 创建时间 + */ + private Long createAt; + + /** + * 转换脚本 + */ + private String script; +} diff --git a/iot-common/iot-common-dao/iot-data-service/src/main/java/cc/iotkit/data/manager/IScreenApiData.java b/iot-common/iot-common-dao/iot-data-service/src/main/java/cc/iotkit/data/manager/IScreenApiData.java new file mode 100644 index 00000000..e3df7a91 --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-service/src/main/java/cc/iotkit/data/manager/IScreenApiData.java @@ -0,0 +1,16 @@ +package cc.iotkit.data.manager; + +import cc.iotkit.data.IOwnedData; +import cc.iotkit.model.screen.ScreenApi; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:34 + */ +public interface IScreenApiData extends IOwnedData { + List findByScreenId(Long id); + + void deleteByScreenId(Long id); +} diff --git a/iot-common/iot-common-dao/iot-data-service/src/main/java/cc/iotkit/data/manager/IScreenData.java b/iot-common/iot-common-dao/iot-data-service/src/main/java/cc/iotkit/data/manager/IScreenData.java new file mode 100644 index 00000000..ae4578a3 --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-service/src/main/java/cc/iotkit/data/manager/IScreenData.java @@ -0,0 +1,16 @@ +package cc.iotkit.data.manager; + +import cc.iotkit.data.IOwnedData; +import cc.iotkit.model.screen.Screen; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:34 + */ +public interface IScreenData extends IOwnedData { + Screen findByIsDefault(boolean isDefault); + + List findByState(String state); +} diff --git a/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/dao/ScreenApiRepository.java b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/dao/ScreenApiRepository.java new file mode 100644 index 00000000..5cdf791b --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/dao/ScreenApiRepository.java @@ -0,0 +1,18 @@ +package cc.iotkit.data.dao; + +import cc.iotkit.data.model.TbScreenApi; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:23 + */ +public interface ScreenApiRepository extends JpaRepository, QuerydslPredicateExecutor { + + List findByScreenId(Long screenId); + + void deleteByScreenId(Long screenId); +} diff --git a/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/dao/ScreenRepository.java b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/dao/ScreenRepository.java new file mode 100644 index 00000000..4ae9b6bd --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/dao/ScreenRepository.java @@ -0,0 +1,18 @@ +package cc.iotkit.data.dao; + +import cc.iotkit.data.model.TbScreen; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:23 + */ +public interface ScreenRepository extends JpaRepository, QuerydslPredicateExecutor { + + TbScreen findByIsDefault(boolean isDefault); + + List findByState(String state); +} diff --git a/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/model/TbScreen.java b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/model/TbScreen.java new file mode 100644 index 00000000..c75cb275 --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/model/TbScreen.java @@ -0,0 +1,56 @@ +package cc.iotkit.data.model; + +import cc.iotkit.model.screen.Screen; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * @Author:tfd + * @Date:2023/6/25 15:02 + */ +@Data +@Entity +@Table(name = "screen") +@AutoMapper(target = Screen.class) +public class TbScreen { + + @Id + @GeneratedValue(generator = "SnowflakeIdGenerator") + @GenericGenerator(name = "SnowflakeIdGenerator", strategy = "cc.iotkit.data.config.id.SnowflakeIdGenerator") + private Long id; + /** + * 大屏名称 + */ + private String name; + + /** + * 资源文件 + */ + private String resourceFile; + + /** + * 端口 + */ + private Integer port; + + /** + * 发布状态 + */ + private String state; + + /** + * 创建时间 + */ + private Long createAt; + + /** + * 是否为默认大屏 + */ + private Boolean isDefault; +} diff --git a/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/model/TbScreenApi.java b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/model/TbScreenApi.java new file mode 100644 index 00000000..53807261 --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/model/TbScreenApi.java @@ -0,0 +1,59 @@ +package cc.iotkit.data.model; + +import cc.iotkit.model.screen.ScreenApi; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; + +/** + * @Author:tfd + * @Date:2023/6/25 15:02 + */ +@Data +@Entity +@Table(name = "screen_api") +@AutoMapper(target = ScreenApi.class) +public class TbScreenApi { + + @Id + @GeneratedValue(generator = "SnowflakeIdGenerator") + @GenericGenerator(name = "SnowflakeIdGenerator", strategy = "cc.iotkit.data.config.id.SnowflakeIdGenerator") + private Long id; + /** + * 大屏id + */ + private Long screenId; + + /** + * 接口路径 + */ + private String apiPath; + + /** + * 接口参数 + */ + private String apiParams; + + /** + * 请求方法 + */ + private String httpMethod; + + /** + * 数据源 + */ + private String dataSource; + + /** + * 创建时间 + */ + private Long createAt; + + /** + * 转换脚本 + */ + @Column(columnDefinition = "text") + private String script; +} diff --git a/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/service/ScreenApiDataImpl.java b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/service/ScreenApiDataImpl.java new file mode 100644 index 00000000..1ef37d7f --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/service/ScreenApiDataImpl.java @@ -0,0 +1,51 @@ +package cc.iotkit.data.service; + +import cc.iotkit.common.utils.MapstructUtils; +import cc.iotkit.data.dao.IJPACommData; +import cc.iotkit.data.dao.ScreenApiRepository; +import cc.iotkit.data.manager.IScreenApiData; +import cc.iotkit.data.model.TbScreenApi; +import cc.iotkit.model.screen.ScreenApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:21 + */ +@Primary +@Service +public class ScreenApiDataImpl implements IScreenApiData,IJPACommData { + + @Autowired + private ScreenApiRepository screenApiRepository; + + @Override + public JpaRepository getBaseRepository() { + return screenApiRepository; + } + + @Override + public Class getJpaRepositoryClass() { + return TbScreenApi.class; + } + + @Override + public Class getTClass() { + return ScreenApi.class; + } + + @Override + public List findByScreenId(Long id) { + return MapstructUtils.convert(screenApiRepository.findByScreenId(id),ScreenApi.class); + } + + @Override + public void deleteByScreenId(Long id) { + screenApiRepository.deleteByScreenId(id); + } +} diff --git a/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/service/ScreenDataImpl.java b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/service/ScreenDataImpl.java new file mode 100644 index 00000000..48290338 --- /dev/null +++ b/iot-common/iot-common-dao/iot-data-serviceImpl-rdb/src/main/java/cc/iotkit/data/service/ScreenDataImpl.java @@ -0,0 +1,56 @@ +package cc.iotkit.data.service; + +import cc.iotkit.common.utils.MapstructUtils; +import cc.iotkit.data.dao.IJPACommData; +import cc.iotkit.data.dao.ScreenRepository; +import cc.iotkit.data.manager.IScreenData; +import cc.iotkit.data.model.TbScreen; +import cc.iotkit.model.screen.Screen; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:21 + */ +@Primary +@Service +@RequiredArgsConstructor +public class ScreenDataImpl implements IScreenData,IJPACommData { + + @Autowired + private ScreenRepository screenRepository; + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public JpaRepository getBaseRepository() { + return screenRepository; + } + + @Override + public Class getJpaRepositoryClass() { + return TbScreen.class; + } + + @Override + public Class getTClass() { + return Screen.class; + } + + @Override + public Screen findByIsDefault(boolean isDefault) { + return MapstructUtils.convert(screenRepository.findByIsDefault(isDefault),Screen.class); + } + + @Override + public List findByState(String state) { + return MapstructUtils.convert(screenRepository.findByState(state),Screen.class); + } +} diff --git a/iot-module/iot-manager/pom.xml b/iot-module/iot-manager/pom.xml index 4927ecf0..75e13688 100644 --- a/iot-module/iot-manager/pom.xml +++ b/iot-module/iot-manager/pom.xml @@ -59,6 +59,11 @@ iot-rule-engine + + cc.iotkit + iot-screen + + cc.iotkit iot-component-oss diff --git a/iot-module/iot-manager/src/main/java/cc/iotkit/manager/controller/ScreenController.java b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/controller/ScreenController.java new file mode 100644 index 00000000..e7e86d64 --- /dev/null +++ b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/controller/ScreenController.java @@ -0,0 +1,137 @@ +package cc.iotkit.manager.controller; + +import cc.iotkit.common.api.PageRequest; +import cc.iotkit.common.api.Paging; +import cc.iotkit.common.api.Request; +import cc.iotkit.common.enums.ErrCode; +import cc.iotkit.common.exception.BizException; +import cc.iotkit.manager.dto.bo.screen.DebugChangeBo; +import cc.iotkit.manager.dto.bo.screen.PublishChangeBo; +import cc.iotkit.manager.service.IScreenService; +import cc.iotkit.model.screen.Screen; +import cc.iotkit.model.screen.ScreenApi; +import cn.hutool.core.util.ObjectUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 14:56 + */ +@Api(tags = {"大屏接口"}) +@Slf4j +@RestController +@RequestMapping("/screen") +public class ScreenController { + + @Autowired + private IScreenService screenService; + + @ApiOperation(value = "获取大屏列表", httpMethod = "POST") + @PostMapping("/getScreens") + public Paging getBigScreens(@Validated @RequestBody PageRequest request) { + return screenService.getBigScreens(request); + } + + @ApiOperation(value = "上传大屏资源包") + @PostMapping("/uploadResourceFile") + public Long uploadResourceFile(@RequestParam("file") MultipartFile file, + @RequestBody @Validated Request id){ + if (file == null) { + throw new BizException(ErrCode.PARAMS_EXCEPTION); + } + log.info("saving upload resource file:{}", file.getName()); + return screenService.uploadResourceFile(file,id.getData()); + } + + @ApiOperation(value = "获取大屏接口") + @PostMapping("/getScreenApis") + public List getScreenApis(@RequestBody @Validated Request id) { + if (ObjectUtil.isEmpty(id.getData())) { + throw new BizException(ErrCode.ID_BLANK); + } + return screenService.findByScreenId(id.getData()); + } + + @ApiOperation(value = "获取默认大屏") + @PostMapping("/getDefaultScreen") + public Screen getDefaultScreen() { + return screenService.getDefaultScreen(); + } + + @ApiOperation(value = "同步资源包接口") + @PostMapping("/syncResourceApis") + public List syncResourceApis(@RequestBody @Validated Request id) { + if (ObjectUtil.isEmpty(id.getData())) { + throw new BizException(ErrCode.ID_BLANK); + } + return screenService.syncResourceApis(id.getData()); + } + + @ApiOperation(value = "预览接口") + @PostMapping("/previewApis") + public void previewApis(@RequestBody @Validated Request> screenApis) { + if (ObjectUtil.isNull(screenApis.getData())||screenApis.getData().size()==0) { + throw new BizException(ErrCode.API_LIST_BLANK); + } + screenService.previewApis(screenApis.getData()); + } + + @ApiOperation(value = "保存大屏接口") + @PostMapping("/saveScreenApis") + public void saveScreenApis(@RequestBody @Validated Request> screenApis) { + if (ObjectUtil.isNull(screenApis.getData())||screenApis.getData().size()==0) { + throw new BizException(ErrCode.API_LIST_BLANK); + } + screenService.saveScreenApis(screenApis.getData()); + } + + @ApiOperation(value = "调试模式转换") + @PostMapping("/debugModeChange") + public void debugMode(@RequestBody @Validated Request debugChange) { + screenService.debugModeChange(debugChange.getData()); + } + + @ApiOperation(value = "添加大屏") + @PostMapping("/addScreen") + public void addScreen(@RequestBody @Validated Request screen) { + screenService.addBigScreen(screen.getData()); + } + + @ApiOperation(value = "保存大屏") + @PostMapping("/saveScreen") + public void saveScreen(@RequestBody @Validated Request screen) { + screenService.saveBigScreen(screen.getData()); + } + + @ApiOperation(value = "发布状态改变") + @PostMapping("/publishStatusChange") + public void publishStatusChange(@RequestBody @Validated Request req) { + screenService.publishStatusChange(req.getData()); + } + + @ApiOperation(value = "设置默认大屏") + @PostMapping("/setDefaultScreen") + public void setDefaultScreen(@RequestBody @Validated Request id) { + if (ObjectUtil.isEmpty(id.getData())) { + throw new BizException(ErrCode.ID_BLANK); + } + screenService.setDefaultScreen(id.getData()); + } + + @ApiOperation(value = "删除大屏", httpMethod = "POST") + @PostMapping("/deleteScreen") + public void deleteScreen(@RequestBody @Validated Request id) { + if (ObjectUtil.isEmpty(id.getData())) { + throw new BizException(ErrCode.ID_BLANK); + } + screenService.deleteScreen(id.getData()); + } +} diff --git a/iot-module/iot-manager/src/main/java/cc/iotkit/manager/dto/bo/screen/DebugChangeBo.java b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/dto/bo/screen/DebugChangeBo.java new file mode 100644 index 00000000..60f07809 --- /dev/null +++ b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/dto/bo/screen/DebugChangeBo.java @@ -0,0 +1,23 @@ +package cc.iotkit.manager.dto.bo.screen; + +import io.swagger.annotations.ApiModelProperty; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * @Author:tfd + * @Date:2023/6/26 11:23 + */ +@Data +public class DebugChangeBo { + + private static final long serialVersionUID = -1L; + + @NotBlank(message = "id不能为空") + @ApiModelProperty(value = "id") + private Long id; + + @NotBlank(message = "转换状态不能为空") + @ApiModelProperty(value = "转换状态") + private Boolean state; +} diff --git a/iot-module/iot-manager/src/main/java/cc/iotkit/manager/dto/bo/screen/PublishChangeBo.java b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/dto/bo/screen/PublishChangeBo.java new file mode 100644 index 00000000..6f4ceb11 --- /dev/null +++ b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/dto/bo/screen/PublishChangeBo.java @@ -0,0 +1,29 @@ +package cc.iotkit.manager.dto.bo.screen; + +import cc.iotkit.common.api.BaseDto; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + + +@ApiModel(value = "ChangeStateBo") +@Data +@EqualsAndHashCode(callSuper = true) +public class PublishChangeBo extends BaseDto { + private static final long serialVersionUID = -1L; + + @NotBlank(message = "id不能为空") + @ApiModelProperty(value = "") + private Long id; + + + @NotBlank(message = "state不能为空") + @ApiModelProperty(value = "运行状态") + @Size(max = 255, message = "运行状态长度不正确") + private String state; + + +} diff --git a/iot-module/iot-manager/src/main/java/cc/iotkit/manager/service/IScreenService.java b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/service/IScreenService.java new file mode 100644 index 00000000..5eafd271 --- /dev/null +++ b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/service/IScreenService.java @@ -0,0 +1,43 @@ +package cc.iotkit.manager.service; + +import cc.iotkit.common.api.PageRequest; +import cc.iotkit.common.api.Paging; +import cc.iotkit.manager.dto.bo.screen.DebugChangeBo; +import cc.iotkit.manager.dto.bo.screen.PublishChangeBo; +import cc.iotkit.model.screen.Screen; +import cc.iotkit.model.screen.ScreenApi; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * @Author:tfd + * @Date:2023/6/25 15:14 + */ +public interface IScreenService { + Long uploadResourceFile(MultipartFile file, Long id); + + List findByScreenId(Long id); + + Screen getDefaultScreen(); + + List syncResourceApis(Long id); + + void previewApis(List screenApis); + + void saveScreenApis(List screenApis); + + void debugModeChange(DebugChangeBo debugChange); + + void addBigScreen(Screen screen); + + void saveBigScreen(Screen screen); + + void publishStatusChange(PublishChangeBo data); + + void setDefaultScreen(Long id); + + void deleteScreen(Long id); + + Paging getBigScreens(PageRequest request); +} diff --git a/iot-module/iot-manager/src/main/java/cc/iotkit/manager/service/impl/ScreenServiceImpl.java b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/service/impl/ScreenServiceImpl.java new file mode 100644 index 00000000..9590a2c8 --- /dev/null +++ b/iot-module/iot-manager/src/main/java/cc/iotkit/manager/service/impl/ScreenServiceImpl.java @@ -0,0 +1,214 @@ +package cc.iotkit.manager.service.impl; + +import cc.iotkit.common.api.PageRequest; +import cc.iotkit.common.api.Paging; +import cc.iotkit.common.enums.ErrCode; +import cc.iotkit.common.exception.BizException; +import cc.iotkit.common.utils.ReflectUtil; +import cc.iotkit.data.manager.IScreenApiData; +import cc.iotkit.data.manager.IScreenData; +import cc.iotkit.manager.dto.bo.screen.DebugChangeBo; +import cc.iotkit.manager.dto.bo.screen.PublishChangeBo; +import cc.iotkit.manager.service.DataOwnerService; +import cc.iotkit.manager.service.IScreenService; +import cc.iotkit.model.screen.Screen; +import cc.iotkit.model.screen.ScreenApi; +import cc.iotkit.screen.ScreenManager; +import cc.iotkit.screen.config.ScreenConfig; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ZipUtil; +import com.github.yitter.idgen.YitIdHelper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.util.List; +import java.util.Objects; + +/** + * @Author:tfd + * @Date:2023/6/25 15:15 + */ +@Service +@Slf4j +public class ScreenServiceImpl implements IScreenService { + @Autowired + private IScreenData screenData; + @Autowired + private IScreenApiData screenApiData; + @Autowired + private DataOwnerService dataOwnerService; + @Autowired + private ScreenConfig screenConfig; + @Autowired + private ScreenManager screenManager; + + @Override + public Long uploadResourceFile(MultipartFile file, Long id) { + String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename())); + try { + if (ObjectUtil.isNotNull(id)) { + getAndCheckBigScreen(id); + } else { + id = YitIdHelper.nextId(); + } + Path filePath = screenConfig.getBigScreenFilePath(String.valueOf(id)); + Files.createDirectories(filePath); + Path targetLocation = filePath.resolve(fileName); + Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING); + ZipUtil.unzip(filePath.toString()+"/"+fileName); + return id; + } catch (IOException ex) { + throw new BizException(ErrCode.UPLOAD_FILE_ERROR, ex); + } + } + + @Override + public List findByScreenId(Long id) { + return screenApiData.findByScreenId(id); + } + + @Override + public Screen getDefaultScreen() { + return screenData.findByIsDefault(true); + } + + @Override + public List syncResourceApis(Long id) { + Screen screen = getAndCheckBigScreen(id); + return screenManager.getScreenApis(screen); + } + + @Override + public void previewApis(List screenApis) { + Screen screen = getAndCheckBigScreen(screenApis.get(0).getScreenId()); + screenManager.previewApis(screen,screenApis); + } + + @Override + public void saveScreenApis(List screenApis) { + Screen screen = getAndCheckBigScreen(screenApis.get(0).getScreenId()); + screenApiData.deleteByScreenId(screen.getId()); + screenApiData.batchSave(screenApis); + } + + @Override + public void debugModeChange(DebugChangeBo debugChange) { + Screen screen = getAndCheckBigScreen(debugChange.getId()); + screenManager.debugMode(screen,debugChange.getState()); + } + + @Override + public void addBigScreen(Screen screen) { + String id = String.valueOf(screen.getId()); + if (!StringUtils.hasLength(id)) { + throw new BizException(ErrCode.ID_BLANK); + } + Path resPath = screenConfig.getBigScreenFilePath(id); + if (!resPath.resolve(screen.getResourceFile()).toFile().exists()) { + throw new BizException(ErrCode.RESOURCE_FILE_NOT_FOUND); + } + Screen s = screenData.findById(screen.getId()); + if (s != null) { + throw new BizException(ErrCode.BIG_SCREEN_ALREADY); + } + try { + screen.setCreateAt(System.currentTimeMillis()); + screen.setIsDefault(false); + screenData.save(screen); + } catch (Throwable e) { + throw new BizException(ErrCode.ADD_BIG_SCREEN_ERROR, e); + } + } + + @Override + public void saveBigScreen(Screen screen) { + String id = String.valueOf(screen.getId()); + if (!StringUtils.hasLength(id)) { + throw new BizException(ErrCode.ID_BLANK); + } + Path jarPath = screenConfig.getBigScreenFilePath(id); + if (!jarPath.resolve(screen.getResourceFile()).toFile().exists()) { + throw new BizException(ErrCode.RESOURCE_FILE_NOT_FOUND); + } + Screen oldScreen = getAndCheckBigScreen(screen.getId()); + screen = ReflectUtil.copyNoNulls(screen, oldScreen); + try { + screenData.save(screen); + } catch (Throwable e) { + throw new BizException(ErrCode.ADD_BIG_SCREEN_ERROR, e); + } + } + + @Override + public void publishStatusChange(PublishChangeBo data) { + Screen screen = getAndCheckBigScreen(data.getId()); + if (screen.STATE_RUNNING.equals(data.getState())) {//发布状态 + List screenApis=screenApiData.findByScreenId(screen.getId()); + if(screenApis==null||screenApis.size()==0){ + throw new BizException(ErrCode.API_LIST_BLANK); + } + screen.setState(screen.STATE_RUNNING); + screenManager.register(screen); + screenManager.publish(screen); + } else {//取消发布 + screen.setState(screen.STATE_STOPPED); + screenManager.unpublish(screen); + } + screenData.save(screen); + } + + @Override + public void setDefaultScreen(Long id) { + Screen screen = getAndCheckBigScreen(id); + Screen oldScreen=screenData.findByIsDefault(true); + if(oldScreen!=null){ + oldScreen.setIsDefault(false); + } + screenData.save(oldScreen); + screen.setIsDefault(true); + screenData.save(screen); + } + + @Override + public void deleteScreen(Long id) { + Screen screen = getAndCheckBigScreen(id); + try { + Path path = Paths.get(String.format("%s/%s", screenConfig.getScreenDir(), id)) + .toAbsolutePath().normalize(); + File file = path.toFile(); + try { + if (file.isDirectory()) { + FileUtils.deleteDirectory(file); + } else { + FileUtils.delete(file); + } + } catch (NoSuchFileException e) { + log.warn("delete big screen resource error", e); + } + screenData.deleteById(screen.getId()); + } catch (Throwable e) { + throw new BizException(ErrCode.DELETE_BIG_SCREEN_ERROR, e); + } + } + + @Override + public Paging getBigScreens(PageRequest request) { + return screenData.findAll(request); + } + + private Screen getAndCheckBigScreen(Long id) { + Screen oldBigScreen = screenData.findById(id); + if (oldBigScreen == null) { + throw new BizException(ErrCode.BIG_SCREEN_NOT_FOUND); + } + dataOwnerService.checkOwner(oldBigScreen); + return oldBigScreen; + } +} diff --git a/iot-module/iot-screen/pom.xml b/iot-module/iot-screen/pom.xml new file mode 100644 index 00000000..746487bf --- /dev/null +++ b/iot-module/iot-screen/pom.xml @@ -0,0 +1,85 @@ + + + + iot-module + cc.iotkit + ${revision} + + 4.0.0 + + iot-screen + + 数据大屏模块 + + + + + + org.springframework + spring-context-support + + + + org.projectlombok + lombok + + + + io.vertx + vertx-core + + + + io.vertx + vertx-web-proxy + + + + cc.iotkit + iot-common-core + + + + cc.iotkit + iot-script-engine + + + + org.springframework + spring-context + + + + cc.iotkit + iot-data-service + + + + org.springframework.boot + spring-boot-starter-web + + + + cc.iotkit + iot-component-server + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + utf8 + + + + + + \ No newline at end of file diff --git a/iot-module/iot-screen/src/main/java/cc/iotkit/screen/ScreenManager.java b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/ScreenManager.java new file mode 100644 index 00000000..401bd648 --- /dev/null +++ b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/ScreenManager.java @@ -0,0 +1,119 @@ +package cc.iotkit.screen; + +import cc.iotkit.comps.ApiTool; +import cc.iotkit.data.manager.IScreenApiData; +import cc.iotkit.data.manager.IScreenData; +import cc.iotkit.model.screen.Screen; +import cc.iotkit.model.screen.ScreenApi; +import cc.iotkit.screen.api.ScreenApiHandle; +import cc.iotkit.screen.config.ScreenConfig; +import cc.iotkit.screen.staticres.ScreenComponent; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @Author:tfd + * @Date:2023/6/25 17:03 + */ +@Slf4j +@Component +public class ScreenManager { + + @Autowired + private IScreenApiData screenApiData; + + @Autowired + private IScreenData screenData; + + @Autowired + private ScreenConfig screenConfig; + + private final Map screens = new HashMap<>(); + private final Map states = new HashMap<>(); + + @PostConstruct + public void init() { + List screenList = screenData.findByState( + Screen.STATE_RUNNING); + for (Screen screen : screenList) { + try { + register(screen); + publish(screen); + } catch (Throwable e) { + log.error("init screen error", e); + } + } + } + + public void register(Screen screen) { + Long id = screen.getId(); + if (screens.containsKey(id)) { + return; + } + ScreenComponent screenComponent=new ScreenComponent(); + String[] pathNames=screen.getResourceFile().split("\\."); + screenComponent.create(screen.getPort(),pathNames.length>1?pathNames[0]:"",screenConfig); + screens.put(id,screenComponent); + } + + public void publish(Screen screen) { + Long id = screen.getId(); + ScreenComponent screenComponent = screens.get(id); + if (screenComponent == null) { + return; + } + ScreenApiHandle screenApiHandle=new ScreenApiHandle(id,screenApiData.findByScreenId(id)); + screenApiHandle.putScriptEnv("apiTool", new ApiTool()); + screenComponent.setApiHandle(screenApiHandle); + screenComponent.publish(); + states.put(id, true); + } + + public void unpublish(Screen screen) { + Long id = screen.getId(); + ScreenComponent screenComponent = screens.get(id); + if (screenComponent == null) { + return; + } + screens.remove(id); + states.remove(id); + screenComponent.unpublish(); + } + + public void previewApis(Screen screen,List screenApis) { + Long id = screen.getId(); + ScreenComponent screenComponent = screens.get(id); + if (screenComponent == null) { + return; + } + screenComponent.previewApis(screenApis); + } + + public List getScreenApis(Screen screen) { + Long id = screen.getId(); + ScreenComponent screenComponent = screens.get(id); + if (screenComponent == null) { + return null; + } + return screenComponent.getScreenApis(); + } + + public void debugMode(Screen screen,boolean state) { + Long id = screen.getId(); + ScreenComponent screenComponent = screens.get(id); + if (screenComponent == null) { + return; + } + screenComponent.debugMode(state); + } + + public boolean isRunning(String id) { + return states.containsKey(id) && states.get(id); + } +} diff --git a/iot-module/iot-screen/src/main/java/cc/iotkit/screen/api/HttpContent.java b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/api/HttpContent.java new file mode 100644 index 00000000..85795e35 --- /dev/null +++ b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/api/HttpContent.java @@ -0,0 +1,18 @@ +package cc.iotkit.screen.api; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @Author:tfd + * @Date:2023/6/25 15:57 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class HttpContent { + private String content; +} diff --git a/iot-module/iot-screen/src/main/java/cc/iotkit/screen/api/ScreenApiHandle.java b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/api/ScreenApiHandle.java new file mode 100644 index 00000000..7136bb98 --- /dev/null +++ b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/api/ScreenApiHandle.java @@ -0,0 +1,138 @@ +package cc.iotkit.screen.api; + +import cc.iotkit.common.utils.JsonUtils; +import cc.iotkit.common.utils.StringUtils; +import cc.iotkit.model.screen.ScreenApi; +import cc.iotkit.script.IScriptEngine; +import cc.iotkit.script.ScriptEngineFactory; +import com.fasterxml.jackson.core.type.TypeReference; +import io.vertx.core.MultiMap; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.JsonObject; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @Author:tfd + * @Date:2023/6/25 15:54 + */ +@Slf4j +@Data +public class ScreenApiHandle { + private final Long screenId; + + public List screenApis; + + public boolean debugMode=false; + + private final IScriptEngine scriptEngine = ScriptEngineFactory.getScriptEngine("js"); + + public ScreenApiHandle(Long screenId, List screenApis) { + this.screenId = screenId; + this.screenApis = screenApis; + } + + public void putScriptEnv(String key, Object value) { + this.scriptEngine.putScriptEnv(key, value); + } + + /** + * 每个请求的处理方法要执行对应的请求脚本然后将结果返回给http服务器 + * + */ + public String httpReq(HttpServerRequest req, HttpServerResponse res) { + String response=""; + try { + Map httpHeader = getData(req.headers()); + log.info("request header:{}", JsonUtils.toJsonString(httpHeader)); + String path=req.path(); + String params=""; + log.info("接收到请求:"+path); + if(HttpMethod.GET.name().equals(req.method().name())){ + String[] getParams=req.absoluteURI().split("\\?"); + if(getParams.length>1){ + params=getParams[1]; + } + }else if(HttpMethod.POST.name().equals(req.method().name())){ + MultiMap postParams=req.params(); + JsonObject jsonObject = new JsonObject(); + if(!postParams.isEmpty()){ + for (String paramName : postParams.names()) { + jsonObject.put(paramName, postParams.get(paramName)); + } + params=jsonObject.encode(); + } + } + log.info("参数:"+params); + if(debugMode){ + if(screenApis.stream().anyMatch(m -> m.getApiPath().equals(path))){ + String finalParams = params; + screenApis.stream().map(s -> { + s.setApiPath(path); + s.setApiParams(finalParams); + s.setHttpMethod(req.method().name()); + return s; + }).collect(Collectors.toList()); + }else{ + ScreenApi sai=new ScreenApi(); + sai.setApiPath(path); + sai.setApiParams(params); + sai.setHttpMethod(req.method().name()); + screenApis.add(sai); + } + response="PREVIEW API"; + }else{ + for (ScreenApi screenApi:screenApis) { + if(screenApi.getApiPath().equals(path)){ + String script=screenApi.getScript(); + if(StringUtils.isBlank(script)){ + response="转换脚本为空"; + return response; + } + scriptEngine.setScript(script); + try { + HttpContent content = + scriptEngine.invokeMethod( + new TypeReference<>() { + }, + "messageConver", + httpHeader, + params); + response = content.getContent(); + response = response == null ? "" : response; + } catch (Throwable e) { + log.error("invokeMethod messageConver error", e); + response = e.getMessage(); + return response; + } + log.info("response,content:{}", response); + }else if("/favicon.ico".equals(path)){ + response="NOT FOUND"; + }else{ + response="NOT FOUND"; + } + } + } + } catch (Throwable e) { + log.error("handle request error", e); + response="server error:" + e.getMessage(); + return response; + } + return response; + } + + private static Map getData(MultiMap multiMap) { + Map data = new HashMap<>(); + for (Map.Entry entry : multiMap.entries()) { + data.put(entry.getKey(), entry.getValue()); + } + return data; + } +} diff --git a/iot-module/iot-screen/src/main/java/cc/iotkit/screen/config/ScreenConfig.java b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/config/ScreenConfig.java new file mode 100644 index 00000000..c2829492 --- /dev/null +++ b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/config/ScreenConfig.java @@ -0,0 +1,27 @@ +package cc.iotkit.screen.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * @Author:tfd + * @Date:2023/6/25 16:04 + */ +@Configuration +@Data +public class ScreenConfig { + + @Value("${bigScreen.dir:./data/screens}") + private String screenDir; + @Value("${bigScreen.admin:/iotkit/screen}") + public String screenAdmin; + + public Path getBigScreenFilePath(String screenId) { + return Paths.get(screenDir, screenId) + .toAbsolutePath().normalize(); + } +} diff --git a/iot-module/iot-screen/src/main/java/cc/iotkit/screen/staticres/ScreenComponent.java b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/staticres/ScreenComponent.java new file mode 100644 index 00000000..4f37abb8 --- /dev/null +++ b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/staticres/ScreenComponent.java @@ -0,0 +1,76 @@ +package cc.iotkit.screen.staticres; + +import cc.iotkit.common.enums.ErrCode; +import cc.iotkit.common.exception.BizException; +import cc.iotkit.model.screen.ScreenApi; +import cc.iotkit.screen.api.ScreenApiHandle; +import cc.iotkit.screen.config.ScreenConfig; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.concurrent.CountDownLatch; + +/** + * @Author:tfd + * @Date:2023/6/25 16:01 + */ +@Slf4j +public class ScreenComponent { + + private Vertx vertx; + private CountDownLatch countDownLatch; + private String deployedId; + private ScreenApiHandle apiHandle; + private ScreenVerticle screenVerticle; + + public List getScreenApis() { + return apiHandle.screenApis; + } + + public void debugMode(boolean state) { + apiHandle.debugMode=state; + } + + public void create(int port, String packageName, ScreenConfig screenConfig) { + vertx = Vertx.vertx(); + screenVerticle = new ScreenVerticle(port,packageName,screenConfig); + } + + public void setApiHandle(ScreenApiHandle screenApiHandle) { + this.apiHandle=screenApiHandle; + } + + public void previewApis(List screenApis) { + this.apiHandle.setScreenApis(screenApis); + } + + public void publish() { + try { + screenVerticle.setApiHandler(apiHandle); + countDownLatch = new CountDownLatch(1); + Future future = vertx.deployVerticle(screenVerticle); + future.onSuccess((s -> { + deployedId = s; + countDownLatch.countDown(); + })); + future.onFailure((e) -> { + countDownLatch.countDown(); + log.error("publish screen failed", e); + }); + countDownLatch.await(); + future.succeeded(); + } catch (Throwable e) { + throw new BizException(ErrCode.SCREEN_PUBLISH_ERROR, e); + } + } + + @SneakyThrows + public void unpublish() { + screenVerticle.stop(); + Future future = vertx.undeploy(deployedId); + future.onSuccess(unused -> log.info("unpublish screen success")); + } +} diff --git a/iot-module/iot-screen/src/main/java/cc/iotkit/screen/staticres/ScreenVerticle.java b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/staticres/ScreenVerticle.java new file mode 100644 index 00000000..62dd0e8a --- /dev/null +++ b/iot-module/iot-screen/src/main/java/cc/iotkit/screen/staticres/ScreenVerticle.java @@ -0,0 +1,68 @@ +package cc.iotkit.screen.staticres; + +import cc.iotkit.screen.api.ScreenApiHandle; +import cc.iotkit.screen.config.ScreenConfig; +import io.vertx.core.AbstractVerticle; +import io.vertx.core.http.HttpServer; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.StaticHandler; +import lombok.extern.slf4j.Slf4j; + +/** + * @Author:tfd + * @Date:2023/6/25 16:03 + */ +@Slf4j +public class ScreenVerticle extends AbstractVerticle { + + private HttpServer httpServer; + + private int port; + + private String packageName; + + private ScreenApiHandle apiHandler; + + private ScreenConfig screenConfig; + + public ScreenVerticle(int port,String packageName,ScreenConfig screenConfig) { + this.port = port; + this.packageName = packageName; + this.screenConfig = screenConfig; + } + + public void setApiHandler(ScreenApiHandle apiHandler) { + this.apiHandler = apiHandler; + } + + @Override + public void start() throws Exception { + httpServer = vertx.createHttpServer(); + Router router = Router.router(vertx); + router.route(screenConfig.screenAdmin + "/*").handler(StaticHandler.create(screenConfig.getScreenDir()+"/"+apiHandler.getScreenId()+"/"+packageName)); + router.get(screenConfig.screenAdmin).handler(ctx -> { + ctx.response().sendFile(screenConfig.getScreenDir()+"/"+apiHandler.getScreenId() +"/"+packageName+ "/index.html"); + }); + router.get("/*").handler(ctx -> { + String res = apiHandler.httpReq(ctx.request(), ctx.response()); + ctx.response().end(res); + }); + router.post("/*").handler(BodyHandler.create()).handler(ctx -> { + String res = apiHandler.httpReq(ctx.request(), ctx.response()); + ctx.response().end(res); + }); + httpServer.requestHandler(router).listen(port, (http) -> { + if (http.succeeded()) { + log.info("screen server create succeed,port:{}", port); + } else { + log.error("screen server create failed", http.cause()); + } + }); + } + + @Override + public void stop() throws Exception { + httpServer.close(voidAsyncResult -> log.info("close screen server...")); + } +} diff --git a/iot-module/pom.xml b/iot-module/pom.xml index ca1d36eb..9883cac1 100644 --- a/iot-module/pom.xml +++ b/iot-module/pom.xml @@ -18,6 +18,7 @@ iot-rule-engine iot-message-notify iot-generator + iot-screen diff --git a/pom.xml b/pom.xml index d2988b55..af249656 100755 --- a/pom.xml +++ b/pom.xml @@ -527,6 +527,12 @@ ${project.version} + + cc.iotkit + iot-screen + ${project.version} + + cc.iotkit iot-system