update:迁移数据大屏模块

V0.5.x
tangfudong 2023-06-26 13:53:54 +08:00
parent 7d59858c53
commit 2779fc269a
26 changed files with 1388 additions and 0 deletions

View File

@ -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, "分组不存在"),

View File

@ -0,0 +1,47 @@
package cc.iotkit.model.screen;
import cc.iotkit.model.Owned;
import lombok.Data;
/**
* @Authortfd
* @Date2023/6/25 15:04
*/
@Data
public class Screen implements Owned<Long> {
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;
}

View File

@ -0,0 +1,49 @@
package cc.iotkit.model.screen;
import cc.iotkit.model.Owned;
import lombok.Data;
/**
* @Authortfd
* @Date2023/6/25 15:04
*/
@Data
public class ScreenApi implements Owned<Long> {
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;
}

View File

@ -0,0 +1,16 @@
package cc.iotkit.data.manager;
import cc.iotkit.data.IOwnedData;
import cc.iotkit.model.screen.ScreenApi;
import java.util.List;
/**
* @Authortfd
* @Date2023/6/25 15:34
*/
public interface IScreenApiData extends IOwnedData<ScreenApi, Long> {
List<ScreenApi> findByScreenId(Long id);
void deleteByScreenId(Long id);
}

View File

@ -0,0 +1,16 @@
package cc.iotkit.data.manager;
import cc.iotkit.data.IOwnedData;
import cc.iotkit.model.screen.Screen;
import java.util.List;
/**
* @Authortfd
* @Date2023/6/25 15:34
*/
public interface IScreenData extends IOwnedData<Screen, Long> {
Screen findByIsDefault(boolean isDefault);
List<Screen> findByState(String state);
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 15:23
*/
public interface ScreenApiRepository extends JpaRepository<TbScreenApi, Long>, QuerydslPredicateExecutor<TbScreenApi> {
List<TbScreenApi> findByScreenId(Long screenId);
void deleteByScreenId(Long screenId);
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 15:23
*/
public interface ScreenRepository extends JpaRepository<TbScreen, Long>, QuerydslPredicateExecutor<TbScreen> {
TbScreen findByIsDefault(boolean isDefault);
List<TbScreen> findByState(String state);
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/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;
}

View File

@ -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.*;
/**
* @Authortfd
* @Date2023/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;
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 15:21
*/
@Primary
@Service
public class ScreenApiDataImpl implements IScreenApiData,IJPACommData<ScreenApi,Long> {
@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<ScreenApi> findByScreenId(Long id) {
return MapstructUtils.convert(screenApiRepository.findByScreenId(id),ScreenApi.class);
}
@Override
public void deleteByScreenId(Long id) {
screenApiRepository.deleteByScreenId(id);
}
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 15:21
*/
@Primary
@Service
@RequiredArgsConstructor
public class ScreenDataImpl implements IScreenData,IJPACommData<Screen,Long> {
@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<Screen> findByState(String state) {
return MapstructUtils.convert(screenRepository.findByState(state),Screen.class);
}
}

View File

@ -59,6 +59,11 @@
<artifactId>iot-rule-engine</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-screen</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-component-oss</artifactId>

View File

@ -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;
/**
* @Authortfd
* @Date2023/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<Screen> getBigScreens(@Validated @RequestBody PageRequest<Screen> request) {
return screenService.getBigScreens(request);
}
@ApiOperation(value = "上传大屏资源包")
@PostMapping("/uploadResourceFile")
public Long uploadResourceFile(@RequestParam("file") MultipartFile file,
@RequestBody @Validated Request<Long> 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<ScreenApi> getScreenApis(@RequestBody @Validated Request<Long> 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<ScreenApi> syncResourceApis(@RequestBody @Validated Request<Long> 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<List<ScreenApi>> 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<List<ScreenApi>> 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<DebugChangeBo> debugChange) {
screenService.debugModeChange(debugChange.getData());
}
@ApiOperation(value = "添加大屏")
@PostMapping("/addScreen")
public void addScreen(@RequestBody @Validated Request<Screen> screen) {
screenService.addBigScreen(screen.getData());
}
@ApiOperation(value = "保存大屏")
@PostMapping("/saveScreen")
public void saveScreen(@RequestBody @Validated Request<Screen> screen) {
screenService.saveBigScreen(screen.getData());
}
@ApiOperation(value = "发布状态改变")
@PostMapping("/publishStatusChange")
public void publishStatusChange(@RequestBody @Validated Request<PublishChangeBo> req) {
screenService.publishStatusChange(req.getData());
}
@ApiOperation(value = "设置默认大屏")
@PostMapping("/setDefaultScreen")
public void setDefaultScreen(@RequestBody @Validated Request<Long> 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<Long> id) {
if (ObjectUtil.isEmpty(id.getData())) {
throw new BizException(ErrCode.ID_BLANK);
}
screenService.deleteScreen(id.getData());
}
}

View File

@ -0,0 +1,23 @@
package cc.iotkit.manager.dto.bo.screen;
import io.swagger.annotations.ApiModelProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* @Authortfd
* @Date2023/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;
}

View File

@ -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;
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 15:14
*/
public interface IScreenService {
Long uploadResourceFile(MultipartFile file, Long id);
List<ScreenApi> findByScreenId(Long id);
Screen getDefaultScreen();
List<ScreenApi> syncResourceApis(Long id);
void previewApis(List<ScreenApi> screenApis);
void saveScreenApis(List<ScreenApi> 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<Screen> getBigScreens(PageRequest<Screen> request);
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/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<ScreenApi> findByScreenId(Long id) {
return screenApiData.findByScreenId(id);
}
@Override
public Screen getDefaultScreen() {
return screenData.findByIsDefault(true);
}
@Override
public List<ScreenApi> syncResourceApis(Long id) {
Screen screen = getAndCheckBigScreen(id);
return screenManager.getScreenApis(screen);
}
@Override
public void previewApis(List<ScreenApi> screenApis) {
Screen screen = getAndCheckBigScreen(screenApis.get(0).getScreenId());
screenManager.previewApis(screen,screenApis);
}
@Override
public void saveScreenApis(List<ScreenApi> 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<ScreenApi> 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<Screen> getBigScreens(PageRequest<Screen> 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;
}
}

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>iot-module</artifactId>
<groupId>cc.iotkit</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>iot-screen</artifactId>
<description>
数据大屏模块
</description>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-proxy</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-common-core</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-script-engine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-data-service</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-component-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>utf8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 17:03
*/
@Slf4j
@Component
public class ScreenManager {
@Autowired
private IScreenApiData screenApiData;
@Autowired
private IScreenData screenData;
@Autowired
private ScreenConfig screenConfig;
private final Map<Long, ScreenComponent> screens = new HashMap<>();
private final Map<Long, Boolean> states = new HashMap<>();
@PostConstruct
public void init() {
List<Screen> 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<ScreenApi> screenApis) {
Long id = screen.getId();
ScreenComponent screenComponent = screens.get(id);
if (screenComponent == null) {
return;
}
screenComponent.previewApis(screenApis);
}
public List<ScreenApi> 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);
}
}

View File

@ -0,0 +1,18 @@
package cc.iotkit.screen.api;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Authortfd
* @Date2023/6/25 15:57
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class HttpContent {
private String content;
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/6/25 15:54
*/
@Slf4j
@Data
public class ScreenApiHandle {
private final Long screenId;
public List<ScreenApi> screenApis;
public boolean debugMode=false;
private final IScriptEngine scriptEngine = ScriptEngineFactory.getScriptEngine("js");
public ScreenApiHandle(Long screenId, List<ScreenApi> 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<String, Object> 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<String, Object> getData(MultiMap multiMap) {
Map<String, Object> data = new HashMap<>();
for (Map.Entry<String, String> entry : multiMap.entries()) {
data.put(entry.getKey(), entry.getValue());
}
return data;
}
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/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();
}
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/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<ScreenApi> 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<ScreenApi> screenApis) {
this.apiHandle.setScreenApis(screenApis);
}
public void publish() {
try {
screenVerticle.setApiHandler(apiHandle);
countDownLatch = new CountDownLatch(1);
Future<String> 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<Void> future = vertx.undeploy(deployedId);
future.onSuccess(unused -> log.info("unpublish screen success"));
}
}

View File

@ -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;
/**
* @Authortfd
* @Date2023/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..."));
}
}

View File

@ -18,6 +18,7 @@
<module>iot-rule-engine</module>
<module>iot-message-notify</module>
<module>iot-generator</module>
<module>iot-screen</module>
</modules>

View File

@ -527,6 +527,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-screen</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cc.iotkit</groupId>
<artifactId>iot-system</artifactId>