From 1a39d041a0394a25304882a9f43df2811b7fdeeb Mon Sep 17 00:00:00 2001 From: chenxg Date: Tue, 8 Aug 2023 16:30:23 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E5=8A=9F=E8=83=BD=E8=BF=AD=E4=BB=A3-?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0redis=E6=95=B0=E6=8D=AE=E6=BA=90-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- report-core/pom.xml | 6 + .../gaea/business/enums/SetTypeEnum.java | 1 + .../controller/dto/DataSourceDto.java | 3 + .../service/impl/DataSourceServiceImpl.java | 47 +++- .../service/impl/RedisServiceImpl.java | 227 ++++++++++++++++++ .../gaea/business/util/JdbcConstants.java | 1 + .../gaea/business/util/RedisConstants.java | 10 + .../resultset/components/EditDataSet.vue | 39 ++- report-ui/src/views/resultset/index.vue | 3 +- 9 files changed, 322 insertions(+), 15 deletions(-) create mode 100644 report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/RedisServiceImpl.java create mode 100644 report-core/src/main/java/com/anjiplus/template/gaea/business/util/RedisConstants.java diff --git a/report-core/pom.xml b/report-core/pom.xml index ad74fd59..ffaa94a3 100644 --- a/report-core/pom.xml +++ b/report-core/pom.xml @@ -178,6 +178,12 @@ 5.5.13 + + redis.clients + jedis + 3.1.0 + + diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/SetTypeEnum.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/SetTypeEnum.java index a0242f37..5d056aaa 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/SetTypeEnum.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/SetTypeEnum.java @@ -2,6 +2,7 @@ package com.anjiplus.template.gaea.business.enums; public enum SetTypeEnum { SQL("sql", "sql"), + NOSQL("redis","redis"), HTTP("http", "http"), ; diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/controller/dto/DataSourceDto.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/controller/dto/DataSourceDto.java index e22eea62..00987f8e 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/controller/dto/DataSourceDto.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/controller/dto/DataSourceDto.java @@ -41,6 +41,9 @@ public class DataSourceDto extends GaeaBaseDTO implements Serializable { /**关系型数据库jdbcUrl */ private String jdbcUrl; + /**RedisUrl */ + private String redisUrl; + /** 关系型数据库用户名 */ private String username; diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/DataSourceServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/DataSourceServiceImpl.java index 0485a294..c844c75a 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/DataSourceServiceImpl.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/DataSourceServiceImpl.java @@ -20,6 +20,7 @@ import com.anjiplus.template.gaea.business.modules.datasource.dao.entity.DataSou import com.anjiplus.template.gaea.business.modules.datasource.service.DataSourceService; import com.anjiplus.template.gaea.business.modules.datasource.service.JdbcService; import com.anjiplus.template.gaea.business.util.JdbcConstants; +import com.anjiplus.template.gaea.business.util.RedisConstants; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; @@ -31,6 +32,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; import javax.annotation.Resource; import java.sql.*; @@ -65,6 +68,8 @@ public class DataSourceServiceImpl implements DataSourceService { return dataSourceMapper; } + @Autowired + private RedisServiceImpl redisServiceImpl; /** * 获取所有数据源 @@ -73,7 +78,7 @@ public class DataSourceServiceImpl implements DataSourceService { @Override public List queryAllDataSource() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.select(DataSource::getSourceCode, DataSource::getSourceName) + wrapper.select(DataSource::getSourceCode, DataSource::getSourceName,DataSource::getSourceType) .eq(DataSource::getEnableFlag, Enabled.YES.getValue()); wrapper.orderByDesc(DataSource::getUpdateTime); return dataSourceMapper.selectList(wrapper); @@ -106,6 +111,9 @@ public class DataSourceServiceImpl implements DataSourceService { case JdbcConstants.HTTP: testHttp(dto); break; + case JdbcConstants.REDIS: + testRedis(dto); + break; default: throw BusinessExceptionBuilder.build(ResponseCode.DATA_SOURCE_TYPE_DOES_NOT_MATCH_TEMPORARILY); } @@ -129,6 +137,8 @@ public class DataSourceServiceImpl implements DataSourceService { return executeRelationalDb(dto); case JdbcConstants.HTTP: return executeHttp(dto); + case JdbcConstants.REDIS: + return executeRedis(dto); default: throw BusinessExceptionBuilder.build(ResponseCode.DATA_SOURCE_TYPE_DOES_NOT_MATCH_TEMPORARILY); } @@ -288,6 +298,10 @@ public class DataSourceServiceImpl implements DataSourceService { return result; } + private List executeRedis(DataSourceDto dto) { + return redisServiceImpl.queryRedisValue(dto); + } + /** * http 执行获取数据 * @@ -346,6 +360,37 @@ public class DataSourceServiceImpl implements DataSourceService { } } + /** + * redis缓存 测试连接 + * + * @param dto + */ + private void testRedis(DataSourceDto dto) { + analysisRedisConfig(dto); + try (JedisPool jedisPool = new JedisPool(dto.getRedisUrl().split(":")[0], Integer.parseInt(dto.getRedisUrl().split(":")[1])); + Jedis jedis = jedisPool.getResource()) { + log.info("redis连接成功: " + jedis.info()); + } catch (Exception e) { + log.error("error", e); + if (e.getCause() instanceof ClassNotFoundException) { + throw BusinessExceptionBuilder.build(ResponseCode.CLASS_NOT_FOUND, e.getCause().getMessage()); + } else { + throw BusinessExceptionBuilder.build(ResponseCode.DATA_SOURCE_CONNECTION_FAILED, e.getMessage()); + } + } + } + + public void analysisRedisConfig(DataSourceDto dto) { + JSONObject json = JSONObject.parseObject(dto.getSourceConfig()); + GaeaAssert.isFalse(json.containsKey("redisUrl"), ResponseCode.PARAM_IS_NULL,"redisUrl not empty"); + String redisUrl = json.getString("redisUrl"); + String username = json.getString("username"); + String password = json.getString("password"); + dto.setRedisUrl(redisUrl); + dto.setUsername(username); + dto.setPassword(password); + } + /** * http 测试连接 * diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/RedisServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/RedisServiceImpl.java new file mode 100644 index 00000000..ccbc1869 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/RedisServiceImpl.java @@ -0,0 +1,227 @@ +package com.anjiplus.template.gaea.business.modules.datasource.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.anjiplus.template.gaea.business.constant.BusinessConstant; +import com.anjiplus.template.gaea.business.modules.datasource.controller.dto.DataSourceDto; +import com.anjiplus.template.gaea.business.util.RedisConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Service +@Slf4j +public class RedisServiceImpl { + @Autowired + private DataSourceServiceImpl dataSourceServiceImpl; + + private Map jedisPoolmap = new ConcurrentHashMap<>(); + private Object lock = new Object(); + + + public List queryRedisValue(DataSourceDto dto) { + dataSourceServiceImpl.analysisRedisConfig(dto); + + // 获取键的数据类型 + String type; + try (Jedis jedis = getRedisConnection(dto)) { + type = jedis.type(dto.getDynSentence()); + } + + // 根据数据类型选择相应的查询方法和封装逻辑 + switch (type) { + case RedisConstants.STRING: + return executeString(dto); + + case RedisConstants.LIST: + return executeList(dto); + + case RedisConstants.HASH: + return executeHash(dto); + + case RedisConstants.SET: + return executeSet(dto); + + case RedisConstants.ZSET: + return executeZset(dto); + + default: + // 如果类型无法识别或者其他处理逻辑 + return null; + } + } + /*redis连接测试*/ + public JedisPool getRedisConnectionPool(DataSourceDto dataSource) { + if (jedisPoolmap.containsKey(dataSource.getId())) { + return jedisPoolmap.get(dataSource.getId()); + } else { + try { + synchronized (lock) { + if (!jedisPoolmap.containsKey(dataSource.getId())) { + JedisPool jedisPool=new JedisPool(dataSource.getRedisUrl().split(":")[0], Integer.parseInt(dataSource.getRedisUrl().split(":")[1])); + jedisPoolmap.put(dataSource.getId(), jedisPool); + log.info("创建连接池成功:{}", dataSource.getRedisUrl()); + } + } + return jedisPoolmap.get(dataSource.getId()); + } finally { + } + } + } + public Jedis getRedisConnection(DataSourceDto dto) { + return getRedisConnectionPool(dto).getResource(); + } + + public List executeString(DataSourceDto dto) { + dataSourceServiceImpl.analysisRedisConfig(dto); + Jedis jedis = null; + try { + jedis = getRedisConnection(dto); + String jsonValue = jedis.get(dto.getDynSentence()); + List result = new ArrayList<>(); + // + if (StringUtils.isEmpty(jsonValue)) { + return result; + } + log.info(jsonValue); + if (jsonValue.trim().startsWith(BusinessConstant.LEFT_BIG_BOAST) && jsonValue.trim().endsWith(BusinessConstant.RIGTH_BIG_BOAST)) { + //JSONObject + result.add(JSONObject.parseObject(jsonValue)); + } else if (jsonValue.trim().startsWith(BusinessConstant.LEFT_MIDDLE_BOAST) && jsonValue.trim().endsWith(BusinessConstant.RIGHT_MIDDLE_BOAST)) { + //List + result = JSONArray.parseArray(jsonValue, JSONObject.class); + } else { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(dto.getDynSentence(), jsonValue); + result.add(jsonObject); + } + return result; + } + finally { + if (jedis != null) { + jedis.close(); // 关闭连接 + } + } + } + + public List executeList(DataSourceDto dto) { + try (Jedis jedis = getRedisConnection(dto)) { + List jsonValueList = jedis.lrange(dto.getDynSentence(), 0, -1); + List result = new ArrayList<>(); + + if (CollectionUtils.isEmpty(jsonValueList)){ + return result; + } + + JSONArray jsonArray = new JSONArray(); + + for (String jsonValue : jsonValueList) { + if (jsonValue.trim().startsWith(BusinessConstant.LEFT_BIG_BOAST) && jsonValue.trim().endsWith(BusinessConstant.RIGTH_BIG_BOAST)) { + jsonArray.add(jsonValue); + } else if (jsonValue.trim().startsWith(BusinessConstant.LEFT_MIDDLE_BOAST) && jsonValue.trim().endsWith(BusinessConstant.RIGHT_MIDDLE_BOAST)) { + jsonArray.addAll(JSONArray.parseArray(jsonValue, JSONObject.class)); + } + } + + result.addAll(jsonArray.toJavaList(JSONObject.class)); + return result; + } + } + + public List executeHash(DataSourceDto dto) { + try (Jedis jedis = getRedisConnection(dto)) { + String dynSentence = dto.getDynSentence(); + String[] splitResult = dynSentence.split(" "); + if (splitResult.length != 2) { + throw new IllegalArgumentException("Invalid dynSentence format. Expected: 'key field'"); + } + + String jsonValue = jedis.hget(splitResult[0], splitResult[1]); + List result = new ArrayList<>(); + + if (StringUtils.isEmpty(jsonValue)) { + return result; + } + + log.info(jsonValue); + + if (jsonValue.trim().startsWith(BusinessConstant.LEFT_BIG_BOAST) && jsonValue.trim().endsWith(BusinessConstant.RIGTH_BIG_BOAST)) { + // JSONObject + result.add(JSONObject.parseObject(jsonValue)); + } else if (jsonValue.trim().startsWith(BusinessConstant.LEFT_MIDDLE_BOAST) && jsonValue.trim().endsWith(BusinessConstant.RIGHT_MIDDLE_BOAST)) { + // List + result.addAll(JSONArray.parseArray(jsonValue, JSONObject.class)); + } else { + result.add(new JSONObject()); + } + + return result; + } + } + public List executeSet(DataSourceDto dto) { + try (Jedis jedis = getRedisConnection(dto)) { + Set jsonValueSet = jedis.smembers(dto.getDynSentence()); + List result = new ArrayList<>(); + + if (CollectionUtils.isEmpty(jsonValueSet)) { + return result; + } + + log.info(JSON.toJSONString(jsonValueSet)); + + for (String jsonValue : jsonValueSet) { + String trimmedValue = jsonValue.trim(); + + if (trimmedValue.startsWith(BusinessConstant.LEFT_BIG_BOAST) && trimmedValue.endsWith(BusinessConstant.RIGTH_BIG_BOAST)) { + // JSONObject + result.add(JSONObject.parseObject(trimmedValue)); + } else if (trimmedValue.startsWith(BusinessConstant.LEFT_MIDDLE_BOAST) && trimmedValue.endsWith(BusinessConstant.RIGHT_MIDDLE_BOAST)) { + // List + result.addAll(JSONArray.parseArray(trimmedValue, JSONObject.class)); + } else { + result.add(new JSONObject()); + } + } + + return result; + } + } + + public List executeZset(DataSourceDto dto) { + try (Jedis jedis = getRedisConnection(dto)) { + Set jsonValueSet = jedis.zrange(dto.getDynSentence(), 0, -1); + List result = new ArrayList<>(); + + if (CollectionUtils.isEmpty(jsonValueSet)) { + return result; + } + + log.info(JSON.toJSONString(jsonValueSet)); + + for (String jsonValue : jsonValueSet) { + String trimmedValue = jsonValue.trim(); + + if (trimmedValue.startsWith(BusinessConstant.LEFT_BIG_BOAST) && trimmedValue.endsWith(BusinessConstant.RIGTH_BIG_BOAST)) { + // JSONObject + result.add(JSONObject.parseObject(trimmedValue)); + } else if (trimmedValue.startsWith(BusinessConstant.LEFT_MIDDLE_BOAST) && trimmedValue.endsWith(BusinessConstant.RIGHT_MIDDLE_BOAST)) { + // List + result.addAll(JSONArray.parseArray(trimmedValue, JSONObject.class)); + } else { + result.add(new JSONObject()); + } + } + + return result; + } + } +} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcConstants.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcConstants.java index 0d2febe5..96bafca5 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcConstants.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/JdbcConstants.java @@ -16,6 +16,7 @@ public class JdbcConstants { public final static String JDBC = "jdbc"; public final static String POSTGRESQL = "postgresql"; + public final static String REDIS="redis"; public final static String JTDS = "jtds"; public final static String MOCK = "mock"; diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/RedisConstants.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/RedisConstants.java new file mode 100644 index 00000000..3ac29a3f --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/RedisConstants.java @@ -0,0 +1,10 @@ + +package com.anjiplus.template.gaea.business.util; + +public class RedisConstants { + public final static String STRING = "string"; + public final static String LIST = "list"; + public final static String HASH = "hash"; + public final static String SET = "set"; + public final static String ZSET = "zset"; +} \ No newline at end of file diff --git a/report-ui/src/views/resultset/components/EditDataSet.vue b/report-ui/src/views/resultset/components/EditDataSet.vue index 49277b49..7f8ce722 100644 --- a/report-ui/src/views/resultset/components/EditDataSet.vue +++ b/report-ui/src/views/resultset/components/EditDataSet.vue @@ -16,7 +16,7 @@ > + + + + + - + - +
- + 添加 + >添加 必选 + >必选 删除 + >删除 追加 + >追加 @@ -340,17 +345,17 @@
新增 + >新增 取消取消 保存保存 @@ -406,7 +411,7 @@ 测试 保存保存 关闭 @@ -596,8 +601,16 @@ export default { this.httpForm = JSON.parse(row.dynSentence); } //获取数据源下拉 - const { code, data } = await queryAllDataSourceSet(); + let { code, data } = await queryAllDataSourceSet(); if (code != "200") return; + let newdata; + if (type === 'redis') { + newdata=data.filter(item=> item.sourceType === type) + }; + if (type==='sql') { + newdata=data.filter(item=> item.sourceType != 'redis') + }; + data=newdata; this.sourceList = data; this.dialogFormVisible = true; diff --git a/report-ui/src/views/resultset/index.vue b/report-ui/src/views/resultset/index.vue index 256852df..b8f35fa6 100644 --- a/report-ui/src/views/resultset/index.vue +++ b/report-ui/src/views/resultset/index.vue @@ -1,6 +1,6 @@ + + org.springframework.boot + spring-boot-starter-websocket + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + cn.hutool + hutool-all + 5.8.12 + + redis.clients jedis diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/ReportApplication.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/ReportApplication.java index c0b5a0d1..6c45b116 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/ReportApplication.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/ReportApplication.java @@ -2,10 +2,15 @@ package com.anjiplus.template.gaea.business; import com.anji.plus.gaea.annotation.enabled.EnabledGaeaConfiguration; import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; import springfox.documentation.swagger2.annotations.EnableSwagger2; +import java.util.Arrays; + /** * 业务模板 * @author lr @@ -14,7 +19,10 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnabledGaeaConfiguration @SpringBootApplication(scanBasePackages = { "com.anjiplus.template.gaea", - "com.anji.plus" + "com.anji.plus", + "com.anjiplus.template.gaea.business.config", + "com.anjiplus.template.gaea.business.util", + "com.anjiplus.template.gaea.business.websocket" }) @MapperScan(basePackages = { "com.anjiplus.template.gaea.business.modules.*.dao", @@ -22,8 +30,22 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; "com.anji.plus.gaea.*.module.*.dao" }) @EnableSwagger2 -public class ReportApplication { +public class ReportApplication implements CommandLineRunner { + + @Autowired + private ApplicationContext appContext; + public static void main( String[] args ) { SpringApplication.run(ReportApplication.class); } + + @Override + public void run(String... args) throws Exception { + String[] beans = appContext.getBeanDefinitionNames(); + Arrays.sort(beans); + for (String bean : beans) { + System.out.println("bean==>"+bean); + } + + } } diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/config/WebSocketConfig.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/config/WebSocketConfig.java new file mode 100644 index 00000000..ec85a0a5 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/config/WebSocketConfig.java @@ -0,0 +1,26 @@ +package com.anjiplus.template.gaea.business.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; +import org.springframework.web.socket.server.standard.ServerEndpointRegistration; + + +/*** + * 配置ServerEndpointExporter,配置后会自动注册所有“@ServerEndpoint”注解声明的Websocket Endpoint + */ +@Configuration +public class WebSocketConfig { + + /** + * 这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket ,如果你使用外置的tomcat就 + * 不需要该配置文件 + */ + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + +} \ No newline at end of file diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java index b9214f6f..7706c0e2 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java @@ -85,6 +85,11 @@ public class TokenFilter implements Filter { return; } + // websocket相关的直接放行 + if (uri.contains("websocket")) { + filterChain.doFilter(request, response); + return; + } if (SLASH.equals(uri) || SLASH.concat(BusinessConstant.SLASH).equals(uri)) { if (BusinessConstant.SLASH.equals(uri)) { diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/service/impl/DataSetServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/service/impl/DataSetServiceImpl.java index d669254b..ee232d2b 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/service/impl/DataSetServiceImpl.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataset/service/impl/DataSetServiceImpl.java @@ -372,7 +372,7 @@ public class DataSetServiceImpl implements DataSetService { @Override public List queryAllDataSet() { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.select(DataSet::getSetCode, DataSet::getSetName, DataSet::getSetDesc, DataSet::getId) + wrapper.select(DataSet::getSetCode,DataSet::getSetType, DataSet::getSetName, DataSet::getSetDesc, DataSet::getId) .eq(DataSet::getEnableFlag, Enabled.YES.getValue()); wrapper.orderByDesc(DataSet::getUpdateTime); return dataSetMapper.selectList(wrapper); diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/ApplicationContextUtil.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/ApplicationContextUtil.java new file mode 100644 index 00000000..1be80e41 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/ApplicationContextUtil.java @@ -0,0 +1,24 @@ +package com.anjiplus.template.gaea.business.util; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class ApplicationContextUtil implements ApplicationContextAware { + private static ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + context = applicationContext; + } + + public static ApplicationContext getApplicationContext() { + return context; + } + + public static Object getBean(String name) { + return getApplicationContext().getBean(name); + } +} \ No newline at end of file diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/IdLockUtil.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/IdLockUtil.java new file mode 100644 index 00000000..e9ad98f2 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/IdLockUtil.java @@ -0,0 +1,54 @@ +package com.anjiplus.template.gaea.business.util; + +import java.util.HashMap; + +public class IdLockUtil { + + private static HashMap mMapId = new HashMap<>(); + private static HashMap mMapIdCache = new HashMap<>(); + + /** + * 缓存切换的开始时间,等待{@link #M_CACHE_DELETE_TIME}时间后将清空切换数据 + */ + private static long mCacheCreatTime; + + /** + * 最大缓存数(当超出这一数值时,会自动清空),缓存切换等待时间 + */ + private static final int M_MAX_CACHE = 1000; + private static final int M_CACHE_DELETE_TIME = 10000; + + public static synchronized String getLock(String oldId) { + String returnSt; + if (mMapId.size() < M_MAX_CACHE) { + //数据比较少,普通的返回锁 + if (!mMapId.containsKey(oldId)) { + mMapId.put(oldId, oldId); + } + returnSt = mMapId.get(oldId); + } else { + //累加的残留数据太多,切换至缓存 + + //缓存开始时间 + long nowMills = System.currentTimeMillis(); + if (mMapIdCache.size() == 0) { + mCacheCreatTime = nowMills; + } + if (!mMapIdCache.containsKey(oldId)) { + mMapIdCache.put(oldId, mMapId.getOrDefault(oldId, oldId)); + } + returnSt = mMapIdCache.get(oldId); + + //等待mCacheChangeTime时间后清除原始数据 + if (nowMills - mCacheCreatTime > M_CACHE_DELETE_TIME) { + mMapId.clear(); + //原始和缓存对调即可实现切换 + HashMap change = mMapId; + mMapId = mMapIdCache; + mMapIdCache = change; + } + } + return returnSt; + } +} + diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/websocket/TestWebsocket.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/websocket/TestWebsocket.java new file mode 100644 index 00000000..311ff91a --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/websocket/TestWebsocket.java @@ -0,0 +1,143 @@ +package com.anjiplus.template.gaea.business.websocket; + +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.anjiplus.template.gaea.business.modules.dashboard.controller.dto.ChartDto; +import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.DataSetDto; +import com.anjiplus.template.gaea.business.modules.dataset.controller.dto.OriginalDataDto; +import com.anjiplus.template.gaea.business.modules.dataset.service.DataSetService; +import com.anjiplus.template.gaea.business.util.ApplicationContextUtil; +import com.anjiplus.template.gaea.business.util.IdLockUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.Trigger; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.data.redis.core.RedisTemplate; + +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +@ServerEndpoint(value = "/websocket/RealTimeWebsocket") +@Component +public class TestWebsocket { + private static Logger logger = LoggerFactory.getLogger(TestWebsocket.class); + /** + * 记录当前在线连接数 + */ + private static final AtomicInteger ONLINE_COUNT = new AtomicInteger(0); + + /** + * 存放所有在线的客户端 + */ + private static final Map CLIENTS = new ConcurrentHashMap<>(); + + + /** + * 存放最近信息 + */ + private static final Queue MESSAGES = new LinkedList<>(); + + /** + * 最近信息容量 + */ + private static final int ALARM_CAPACITY = 1; + + /** + * 定时任务集合 + */ + Map> stringScheduledFutureMap = new ConcurrentHashMap<>(); + + /** + * taskScheduler + */ + private TaskScheduler taskScheduler; + private RedisTemplate redisTemplate; + private static final Integer zero = 0; + + private DataSetService dataSetService; + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session) { + this.dataSetService = (DataSetService) ApplicationContextUtil.getBean("dataSetServiceImpl"); + + ONLINE_COUNT.incrementAndGet(); // 在线数加1 + CLIENTS.put(session.getId(), session); + logger.info("有新连接加入:{},当前在线人数为:{}", session.getId(), ONLINE_COUNT.get()); + } + + /** + * 连接关闭调用的方法 + */ + @OnClose + public void onClose(Session session) { + ONLINE_COUNT.decrementAndGet(); // 在线数减1 + CLIENTS.remove(session.getId()); + logger.info("有一连接关闭:{},当前在线人数为:{}", session.getId(), ONLINE_COUNT.get()); + } + + /** + * 收到客户端消息后调用的方法 + * + * @param message 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, Session session) { + //做自己的业务 + logger.info("messgae==>" + message + ",客户端id为: " + session.getId()); + this.sendMessage(session.getId(), message); + } + + @OnError + public void onError(Session session, Throwable error) { + logger.error("发生错误" + (null == session ? "" : ",客户端id为: " + session.getId())); + error.printStackTrace(); + } + + /** + * 发送消息给指定客户端 + */ + public void sendMessage(String sessionId, String message) { + if (sessionId == null || message == null) { + return; + } + ChartDto dto = JSON.parseObject(message, ChartDto.class); + DataSetDto setDto = new DataSetDto(); + setDto.setSetCode(dto.getSetCode()); + setDto.setContextData(dto.getContextData()); + OriginalDataDto result = dataSetService.getData(setDto); + List data = result.getData(); + synchronized (IdLockUtil.getLock("WebSocket:send" + sessionId)) { + logger.info("服务端给客户端[{}]发送消息", sessionId); + try { + Session session = CLIENTS.get(sessionId); + if (!ObjectUtils.isEmpty(session)) { + session.getBasicRemote().sendText(data.toString()); + } + } catch (Exception e) { + logger.error("服务端发送消息给客户端[{}]失败:{}", sessionId, e); + } + } + } + + + + + + +} + diff --git a/report-ui/src/api/socket.js b/report-ui/src/api/socket.js new file mode 100644 index 00000000..815f37e2 --- /dev/null +++ b/report-ui/src/api/socket.js @@ -0,0 +1,107 @@ +import {reportAdd, reportDeleteBatch, reportDetail, reportList, reportUpdate} from "./reportmanage"; + +var websock = null; + +var serverPort = "9095"; // webSocket连接端口 +var wsuri = "ws://" + window.location.hostname + ":" + serverPort + "/websocket/RealTimeWebsocket"; + +var global_callback = null; + + +export function createWebSocket() { + if (websock == null || typeof websock !== WebSocket) { + initWebSocket(); + } +} + +function initWebSocket() { + // console.log("开始了") + // 初始化websocket + websock = new WebSocket(wsuri); + websock.onmessage = function (e) { + websocketonmessage(e); + }; + websock.onclose = function (e) { + websocketclose(e); + }; + websock.onopen = function () { + websocketOpen(); + }; + + // 连接发生错误的回调方法 + websock.onerror = function () { + console.log("WebSocket连接发生错误"); + //createWebSocket();啊,发现这样写会创建多个连接,加延时也不行 + }; +} + +// 实际调用的方法 +export function sendSock(agentData,callback) { + global_callback = callback; + if (websock.readyState === websock.OPEN) { + // 若是ws开启状态 + websocketsend(agentData); + } else if (websock.readyState === websock.CONNECTING) { + // 若是 正在开启状态,则等待1s后重新调用 + setTimeout(function () { + sendSock(agentData); + }, 1000); + } else { + // 若未开启 ,则等待1s后重新调用 + setTimeout(function () { + sendSock(agentData); + }, 1000); + } +} + +export function closeSock() { + // console.log("关闭了") + websock.close(); +} + +// 数据接收 +function websocketonmessage(msg) { + // console.log("收到数据:"+JSON.parse(e.data)); + // console.log("收到数据:"+msg); + + global_callback(JSON.parse(msg.data)); + + // 收到信息为Blob类型时 + let result = null; + // debugger + if (msg.data instanceof Blob) { + const reader = new FileReader(); + reader.readAsText(msg.data, "UTF-8"); + reader.onload = (e) => { + result = JSON.parse(reader.result); + //console.log("websocket收到", result); + }; + } else { + result = JSON.parse(msg.data); + //console.log("websocket收到", result); + } +} + +// 数据发送 +function websocketsend(agentData) { + // console.log("发送数据:" + JSON.stringify(agentData)); + websock.send(JSON.stringify(agentData)); + + // 设置回调函数 + websock.onmessage = function(event) { + // console.log("收到数据:" + event.data); + const msg = JSON.parse(event.data); + global_callback(msg); + }; +} + +// 关闭 +function websocketclose(e) { + // console.log("connection closed (" + e.code + ")"); +} + +function websocketOpen(e) { + // console.log("连接打开"); +} + +export default { createWebSocket,closeSock,sendSock } diff --git a/report-ui/src/main.js b/report-ui/src/main.js index 2fc0e97d..8ca2c823 100644 --- a/report-ui/src/main.js +++ b/report-ui/src/main.js @@ -19,6 +19,7 @@ import echarts from 'echarts'; import ECharts from 'vue-echarts' import 'echarts/lib/chart/bar' import 'echarts/lib/component/tooltip' +import socket from "@/api/socket"; //import 'echarts-liquidfill' // import 'echarts-gl' Vue.component('v-chart', ECharts) @@ -58,6 +59,8 @@ Vue.mixin(mixins) // 分页的全局size配置; Vue.prototype.$pageSizeAll = [10, 50, 100, 200, 500] +Vue.prototype.socket = socket; + Vue.config.productionTip = false // create the app instance. diff --git a/report-ui/src/views/bigscreenDesigner/designer/components/dynamicComponents.vue b/report-ui/src/views/bigscreenDesigner/designer/components/dynamicComponents.vue index 684f53f6..f7317369 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/components/dynamicComponents.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/components/dynamicComponents.vue @@ -87,6 +87,15 @@ export default { } }); return code; + }, + setType() { + let type = ""; + this.dataSet.forEach(el => { + if (el.id == this.dataSetValue) { + type = el.setType; + } + }); + return type; } }, mounted() { @@ -115,6 +124,7 @@ export default { const params = { chartType: this.chartType, setCode: this.setCode, + setType: this.setType, chartProperties: this.chartProperties, contextData }; diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/texts/widgetText.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/texts/widgetText.vue index fa60696f..1a86ae26 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/texts/widgetText.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/texts/widgetText.vue @@ -9,6 +9,9 @@