Эх сурвалжийг харах

Merge branch 'master' of https://gitee.com/anji-plus/report

qianlishi 4 жил өмнө
parent
commit
b31e629c6b

+ 20 - 0
README.md

@@ -1,3 +1,7 @@
+## 简介
+&emsp;  &emsp;  AJ-Report是一个完全开源的BI平台,酷炫大屏展示,能随时随地掌控业务动态,让每个决策都有数据支撑。<br>
+&emsp;  &emsp;  多数据源支持,内置mysql、elasticsearch、kudu驱动,支持自定义数据集省去数据接口开发,支持17种大屏组件,不会开发,照着设计稿也可以制作大屏。<br>
+&emsp;  &emsp;  三步轻松完成大屏设计:配置数据源---->写SQL配置数据集---->拖拽配置大屏---->保存发布。欢迎体验。
 ## 在线体验
 #### &emsp;  电脑在线体验: [https://report.anji-plus.com/index.html](https://report.anji-plus.com/index.html "链接")  &emsp;体验账号:guest  密码:guest
 #### &emsp;  在线文档: [https://report.anji-plus.com/report-doc/](https://report.anji-plus.com/report-doc/ "doc")<br>
@@ -42,6 +46,22 @@
 ├── README.md
 ```
 
+## 核心技术
+### 后端
+- [Spring Boot2.3.5.RELEASE](https://spring.io/projects/spring-boot/): Spring Boot是一款开箱即用框架,让我们的Spring应用变的更轻量化、更快的入门。 在主程序执行main函数就可以运行。你也可以打包你的应用为jar并通过使用java -jar来运行你的Web应用;
+- [Mybatis-plus3.3.2](https://mp.baomidou.com/): MyBatis-plus(简称 MP)是一个 MyBatis (opens new window) 的增强工具。
+- [flyway5.2.1](https://flywaydb.org/): 主要用于在你的应用版本不断升级的同时,升级你的数据库结构和里面的数据
+### 前端
+- [npm](https://www.npmjs.com/):node.js的包管理工具,用于统一管理我们前端项目中需要用到的包、插件、工具、命令等,便于开发和维护。
+- [webpack](https://webpack.docschina.org/):用于现代 JavaScript 应用程序的_静态模块打包工具
+- [ES6](https://es6.ruanyifeng.com/):Javascript的新版本,ECMAScript6的简称。利用ES6我们可以简化我们的JS代码,同时利用其提供的强大功能来快速实现JS逻辑。
+- [vue-cli](https://cli.vuejs.org/):Vue的脚手架工具,用于自动生成Vue项目的目录及文件。
+- [vue-router](https://router.vuejs.org/): Vue提供的前端路由工具,利用其我们实现页面的路由控制,局部刷新及按需加载,构建单页应用,实现前后端分离。
+- [element-ui](https://element.eleme.cn/#/zh-CN):基于MVVM框架Vue开源出来的一套前端ui组件。
+- [avue](https://www.avuejs.com/): 用该组件包裹后可以变成拖拽组件,采用相对于父类绝对定位;用键盘的上下左右也可以控制移动
+- [vue-echarts](https://www.npmjs.com/package/vue-echarts/): vue-echarts是封装后的vue插件,基于 ECharts v4.0.1+ 开发
+- [vue-superslide](https://www.npmjs.com/package/vue-super-slider/): Vue-SuperSlide(Github) 是 SuperSlide 的 Vue 封装版本
+- [vuedraggable](https://github.com/SortableJS/Vue.Draggable/): 是一款基于Sortable.js实现的vue拖拽插件。
 
 ## 近期计划
 -   完善地图插件

BIN
doc/docs/picture/datasource/img.png


+ 1 - 1
pom.xml

@@ -21,7 +21,7 @@
     </modules>
 
     <properties>
-        <gaea.version>2.0.0.RELEASE</gaea.version>
+        <gaea.version>2.0.1.RELEASE</gaea.version>
         <gaea.security.version>1.0.0-SNAPSHOT</gaea.security.version>
         <gaea.export.version>1.0.0.RELEASE</gaea.export.version>
         <gaea.generator.version>1.0.0-SNAPSHOT</gaea.generator.version>

+ 6 - 0
report-core/pom.xml

@@ -93,6 +93,12 @@
             <version>4.4.5</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid</artifactId>
+            <version>1.2.6</version>
+        </dependency>
+
     </dependencies>
     <build>
         <resources>

+ 54 - 0
report-core/src/main/java/com/anjiplus/template/gaea/business/config/DatabaseInitializer.java

@@ -0,0 +1,54 @@
+package com.anjiplus.template.gaea.business.config;
+
+import com.zaxxer.hikari.HikariDataSource;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.flyway.FlywayProperties;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * Created by raodeming on 2021/7/5.
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class DatabaseInitializer {
+
+    private final FlywayProperties flywayProperties;
+    private final DataSourceProperties dataSourceProperties;
+
+    @PostConstruct
+    public void init() throws SQLException {
+        log.info("DatabaseInitializer uses flyway init-sqls to initiate database");
+        String url = dataSourceProperties.getUrl();
+        // jdbc url最后一个 '/' 用于分割具体 schema?参数
+        int lastSplitIndex = url.lastIndexOf('?');
+        // 获取spring.datasource.url具体数据库schema前的jdbc url
+        String addressUrl = url.substring(0, lastSplitIndex);
+        String addresslast = url.substring(lastSplitIndex);
+        addressUrl = addressUrl.substring(0, addressUrl.lastIndexOf("/"));
+        // 直连数据库地址:jdbc:mysql://yourIp:port
+        HikariDataSource dataSource = new HikariDataSource();
+        dataSource.setJdbcUrl(addressUrl.concat(addresslast));
+        dataSource.setUsername(dataSourceProperties.getUsername());
+        dataSource.setPassword(dataSourceProperties.getPassword());
+        Connection connection = dataSource.getConnection();
+        Statement statement = connection.createStatement();
+        for (String sql : flywayProperties.getInitSqls()) {
+            // 通过flyway的init-sqls配置进行建库与数据库配置
+            // executeUpdate:执行给定的SQL语句,该语句可以是INSERT,UPDATE或DELETE语句或不返回任何内容的SQL语句,例如SQL DDL语句。
+            statement.executeUpdate(sql);
+        }
+        statement.close();
+        connection.close();
+        dataSource.close();
+        log.info("DatabaseInitializer initialize completed");
+    }
+
+}

+ 0 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/filter/TokenFilter.java

@@ -37,7 +37,6 @@ public class TokenFilter implements Filter {
         String uri = request.getRequestURI();
 
         if (!uri.startsWith("/login")
-                && !uri.startsWith("/favicon.ico")
                 && !uri.startsWith("/static")
                 && !uri.startsWith("/file/download/")
                 && !uri.contains("index.html")) {

+ 19 - 19
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/pool/util/JdbcUtil.java

@@ -1,5 +1,6 @@
 package com.anjiplus.template.gaea.business.modules.dataSource.pool.util;
 
+import com.alibaba.druid.pool.DruidDataSource;
 import com.anjiplus.template.gaea.business.modules.dataSource.controller.dto.DataSourceDto;
 import com.anjiplus.template.gaea.business.modules.dataSource.pool.datasource.PooledDataSource;
 import com.anjiplus.template.gaea.business.modules.dataSource.pool.datasource.UnPooledDataSource;
@@ -9,6 +10,7 @@ import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -18,33 +20,34 @@ import java.util.concurrent.locks.ReentrantLock;
 @Slf4j
 public class JdbcUtil {
 
-    private static Lock lock = new ReentrantLock();
-
-    private static Lock deleteLock = new ReentrantLock();
-
     //所有数据源的连接池存在map里
-    static Map<Long, PooledDataSource> map = new HashMap<>();
+    static Map<Long, DruidDataSource> map = new ConcurrentHashMap<>();
 
-    public static PooledDataSource getJdbcConnectionPool(DataSourceDto dataSource) {
+    public static DruidDataSource getJdbcConnectionPool(DataSourceDto dataSource) {
         if (map.containsKey(dataSource.getId())) {
             return map.get(dataSource.getId());
         } else {
-            lock.lock();
             try {
-                log.debug(Thread.currentThread().getName() + "获取锁");
                 if (!map.containsKey(dataSource.getId())) {
-                    PooledDataSource pool = new PooledDataSource();
-                    pool.setJdbcUrl(dataSource.getJdbcUrl());
-                    pool.setUser(dataSource.getUsername());
+                    DruidDataSource pool = new DruidDataSource();
+                    pool.setUrl(dataSource.getJdbcUrl());
+                    pool.setUsername(dataSource.getUsername());
                     pool.setPassword(dataSource.getPassword());
-                    pool.setDriverClass(dataSource.getDriverName());
-                    pool.init();
+                    pool.setDriverClassName(dataSource.getDriverName());
+
+                    //下面都是可选的配置
+                    pool.setInitialSize(10);  //初始连接数,默认0
+                    pool.setMaxActive(30);  //最大连接数,默认8
+                    pool.setMinIdle(10);  //最小闲置数
+                    pool.setMaxWait(2000);  //获取连接的最大等待时间,单位毫秒
+                    pool.setPoolPreparedStatements(true); //缓存PreparedStatement,默认false
+                    pool.setMaxOpenPreparedStatements(20); //缓存PreparedStatement的最大数量,默认-1(不缓存)。大于0时会自动开启缓存PreparedStatement,所以可以省略上一句代码
+
                     map.put(dataSource.getId(), pool);
                     log.info("创建连接池成功:{}", dataSource.getJdbcUrl());
                 }
                 return map.get(dataSource.getId());
             }  finally {
-                lock.unlock();
             }
         }
     }
@@ -54,18 +57,15 @@ public class JdbcUtil {
      * @param id
      */
     public static void removeJdbcConnectionPool(Long id) {
-        deleteLock.lock();
         try {
-            PooledDataSource pool = map.get(id);
+            DruidDataSource pool = map.get(id);
             if (pool != null) {
                 map.remove(id);
             }
         } catch (Exception e) {
             log.error(e.toString());
         } finally {
-            deleteLock.unlock();
         }
-
     }
 
     /**
@@ -75,7 +75,7 @@ public class JdbcUtil {
      * @throws SQLException
      */
     public static Connection getPooledConnection(DataSourceDto dataSource) throws SQLException {
-        PooledDataSource pool = getJdbcConnectionPool(dataSource);
+        DruidDataSource pool = getJdbcConnectionPool(dataSource);
         return pool.getConnection();
     }
 

+ 17 - 1
report-core/src/main/java/com/anjiplus/template/gaea/business/modules/dataSource/service/impl/DataSourceServiceImpl.java

@@ -3,8 +3,10 @@ package com.anjiplus.template.gaea.business.modules.dataSource.service.impl;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.anji.plus.gaea.constant.BaseOperationEnum;
 import com.anji.plus.gaea.constant.Enabled;
 import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
+import com.anji.plus.gaea.exception.BusinessException;
 import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
 import com.anji.plus.gaea.utils.GaeaAssert;
 import com.anjiplus.template.gaea.business.code.ResponseCode;
@@ -379,5 +381,19 @@ public class DataSourceServiceImpl implements DataSourceService {
         dto.setBody(body);
     }
 
-
+    /**
+     * 操作后续处理
+     *
+     * @param entity
+     * @param operationEnum 操作类型
+     * @throws BusinessException 阻止程序继续执行或回滚事务
+     */
+    @Override
+    public void processAfterOperation(DataSource entity, BaseOperationEnum operationEnum) throws BusinessException {
+        switch (operationEnum){
+            case DELETE:
+                JdbcUtil.removeJdbcConnectionPool(entity.getId());
+                break;
+        }
+    }
 }

+ 23 - 23
report-core/src/main/resources/db/migration/V1.0.0__init_db.sql

@@ -1,25 +1,25 @@
 CREATE
 DATABASE IF NOT EXISTS `aj_report` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-
-USE
-`aj_report`;
-
-CREATE TABLE `flyway_schema_history`
-(
-    `installed_rank` int(11) NOT NULL,
-    `version`        varchar(50)            DEFAULT NULL,
-    `description`    varchar(200)  NOT NULL,
-    `type`           varchar(20)   NOT NULL,
-    `script`         varchar(1000) NOT NULL,
-    `checksum`       int(11) DEFAULT NULL,
-    `installed_by`   varchar(100)  NOT NULL,
-    `installed_on`   timestamp     NOT NULL DEFAULT current_timestamp(),
-    `execution_time` int(11) NOT NULL,
-    `success`        tinyint(1) NOT NULL,
-    PRIMARY KEY (`installed_rank`),
-    KEY              `flyway_schema_history_s_idx` (`success`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-insert into `flyway_schema_history`(`installed_rank`, `version`, `description`, `type`, `script`, `checksum`,
-                                    `installed_by`, `installed_on`, `execution_time`, `success`)
-values (1, '1', '<< Flyway Baseline >>', 'BASELINE', '<< Flyway Baseline >>', NULL, 'admin', now(), 0, 1);
+--
+-- USE
+-- `aj_report`;
+--
+-- CREATE TABLE `flyway_schema_history`
+-- (
+--     `installed_rank` int(11) NOT NULL,
+--     `version`        varchar(50)            DEFAULT NULL,
+--     `description`    varchar(200)  NOT NULL,
+--     `type`           varchar(20)   NOT NULL,
+--     `script`         varchar(1000) NOT NULL,
+--     `checksum`       int(11) DEFAULT NULL,
+--     `installed_by`   varchar(100)  NOT NULL,
+--     `installed_on`   timestamp     NOT NULL DEFAULT current_timestamp(),
+--     `execution_time` int(11) NOT NULL,
+--     `success`        tinyint(1) NOT NULL,
+--     PRIMARY KEY (`installed_rank`),
+--     KEY              `flyway_schema_history_s_idx` (`success`)
+-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+--
+-- insert into `flyway_schema_history`(`installed_rank`, `version`, `description`, `type`, `script`, `checksum`,
+--                                     `installed_by`, `installed_on`, `execution_time`, `success`)
+-- values (1, '1', '<< Flyway Baseline >>', 'BASELINE', '<< Flyway Baseline >>', NULL, 'admin', now(), 0, 1);

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 12 - 33
report-core/src/main/resources/db/migration/V1.0.1__create_tables.sql


+ 41 - 20
report-ui/src/mixins/queryform.js

@@ -1,5 +1,6 @@
 import miment from 'miment'
 import {getData} from '@/api/bigscreen'
+
 export default {
   data() {
     return {
@@ -10,7 +11,7 @@ export default {
       },
 
       //日期时间快捷选项
-      datetimeRangePickerOptions:{
+      datetimeRangePickerOptions: {
         shortcuts: [{
           text: '今天',
           onClick(picker) {
@@ -18,14 +19,14 @@ export default {
             const start = new Date(new Date(new Date().getTime()).setHours(0, 0, 0, 0));
             picker.$emit('pick', [start, end]);
           }
-        },{
+        }, {
           text: '昨天',
           onClick(picker) {
-            const start=new Date(new Date(new Date().getTime()-24*60*60*1000).setHours(0, 0, 0, 0));
-            const end=new Date(new Date(new Date().getTime()-24*60*60*1000).setHours(23, 59, 59, 999));
+            const start = new Date(new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0));
+            const end = new Date(new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(23, 59, 59, 999));
             picker.$emit('pick', [start, end]);
           }
-        },{
+        }, {
           text: '最近一周',
           onClick(picker) {
             const end = new Date();
@@ -51,13 +52,12 @@ export default {
           }
         }],
         // disabledDate(time){
-        //   return time.getTime() > Date.now() 
+        //   return time.getTime() > Date.now()
         // }
       },
     }
   },
-  computed: {
-  },
+  computed: {},
   created() {
   },
   mounted() {
@@ -66,8 +66,8 @@ export default {
   },
   methods: {
     // 搜索重置搜索页码
-    search(){
-      this.params.currentPage=1;
+    search() {
+      this.params.currentPage = 1;
       this.queryByPage();
     },
     // 把selectInput中的值赋到params查询对象中
@@ -80,7 +80,7 @@ export default {
       //   console.warn('params has no field:' + keyname)
       //   return
       // }
-      if (keyword !== undefined ) {
+      if (keyword !== undefined) {
         this.params[keyname] = keyword.trim()
       }
     },
@@ -90,22 +90,24 @@ export default {
         data[k] = null
       }
     },
-    handlerInputchange(val){
+    handlerInputchange(val) {
       this.parseParamsBySelectInput(this.selectInput.keyname, val)
     },
     // 查询echarts 数据
     queryEchartsData(params) {
-      return new Promise(async(resolve) => {
-        const { code, data } = await getData(params);
-        if(code != 200) return
-        const analysisData = this.analysisChartsData(params,  data);
+      return new Promise(async (resolve) => {
+        const {code, data} = await getData(params);
+        if (code != 200) return
+        const analysisData = this.analysisChartsData(params, data);
         resolve(analysisData)
       })
     },
     // 解析不同图标的数据
     analysisChartsData(params, data) {
-      // widget-barchart 柱线图、widget-linechart 折线图、 widget-barlinechart 柱线图、widget-piechart 饼图、widget-hollow-piechart 空心饼图
-      // widget-funnel 漏斗图  widget-gauge 仪表盘
+      // widget-barchart 柱线图、widget-linechart 折线图、 widget-barlinechart 柱线图
+      // widget-piechart 饼图、widget-hollow-piechart 空心饼图、widget-funnel 漏斗图
+      // widget-gauge 仪表盘
+      // widget-text 文本框
       const chartType = params.chartType
       if (
         chartType == "widget-barchart" ||
@@ -121,7 +123,10 @@ export default {
         return this.piechartFn(params.chartProperties, data);
       } else if (chartType == "widget-gauge") {
         return this.gaugeFn(params.chartProperties, data);
+      } else if (chartType == "widget-text") {
+        return this.widgettext(params.chartProperties, data)
       } else {
+
       }
     },
     // 柱状图、折线图、折柱图
@@ -190,7 +195,23 @@ export default {
       }
       // console.log(ananysicData, '结果数据')
       return ananysicData[0];
-    }
+    },
+    widgettext(chartProperties, data) {
+      const ananysicData = [];
+      for (let i = 0; i < data.length; i++) {
+        const obj = {};
+        for (const key in chartProperties) {
+          const value = chartProperties[key];
+          if (value === "name") {
+            //obj["name"] = data[i][key];
+          } else {
+            obj["value"] = data[i][key];
+          }
+        }
+        ananysicData.push(obj);
+      }
+      return ananysicData;
+    },
   },
   watch: {
     'selectInput.keyname'(newVal, oldVal) {
@@ -200,7 +221,7 @@ export default {
       this.parseParamsBySelectInput(newVal, this.selectInput.keyword)
     },
     'selectInput.keyword'(newVal, oldVal) {
-      if (!this.selectInput.keyname) return 
+      if (!this.selectInput.keyname) return
       this.parseParamsBySelectInput(this.selectInput.keyname, newVal)
     }
     // 'selectInput.keyword'(newVal, oldVal) {

+ 11 - 0
report-ui/src/views/report/bigscreen/designer/tools.js

@@ -184,6 +184,17 @@ const widgetTools = [
           relactiveDomValue: 'staticData',
           value: '文本框',
         },
+        {
+          type: 'dycustComponents',
+          label: '',
+          name: 'dynamicData',
+          required: false,
+          placeholder: 'px',
+          relactiveDom: 'dataType',
+          relactiveDomValue: 'dynamicData',
+          chartType: 'widget-text',
+          value: '',
+        },
       ],
 
       // 坐标

+ 0 - 3
report-ui/src/views/report/bigscreen/designer/widget/widgetBarchart.vue

@@ -179,9 +179,6 @@ export default {
             fontSize: optionsCollapse.fontSizeY
           }
         },
-        splitLine: {
-          show: false
-        },
         axisLine: {
           show: true,
           lineStyle: {

+ 38 - 2
report-ui/src/views/report/bigscreen/designer/widget/widgetText.vue

@@ -18,7 +18,8 @@ export default {
   },
   data() {
     return {
-      options: {}
+      options: {},
+      optionsData: {}
     };
   },
   computed: {
@@ -47,14 +48,49 @@ export default {
     value: {
       handler(val) {
         this.options = val;
+        this.optionsData = val.data
+        this.setOptionsData()
       },
       deep: true
     }
   },
   mounted() {
     this.options = this.value;
+    this.optionsData = this.value.data
+    this.setOptionsData()
   },
-  methods: {}
+  methods: {
+    // 数据解析
+    setOptionsData() {
+      const optionsData = this.optionsData; // 数据类型 静态 or 动态
+      optionsData.dataType == "staticData"
+        ? this.staticDataFn(optionsData.staticData)
+        : this.dynamicDataFn(
+        optionsData.dynamicData,
+        optionsData.refreshTime
+        );
+    },
+    staticDataFn(val) {
+    },
+    dynamicDataFn(val, refreshTime) {
+      if (!val) return;
+      if (this.ispreview) {
+        this.getEchartData(val);
+        this.flagInter = setInterval(() => {
+          this.getEchartData(val);
+        }, refreshTime);
+      } else {
+        this.getEchartData(val);
+      }
+    },
+    getEchartData(val) {
+      const data = this.queryEchartsData(val);
+      data.then(res => {
+        this.styleColor.text = res[0].value
+        this.$forceUpdate();
+      });
+    },
+  }
 };
 </script>
 

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно