diff --git a/README.md b/README.md index 562e18e7..7fbc0fa3 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,8 @@ sql文件的目录在:report-core --> src --> main --> resources -- > db.migra - openJdk - Jdk 11 - Mysql 8.0(8.0.23/26版本没有问题,8.0.21版本存在问题) - **[常见问题](https://ajreport.beliefteam.cn/report-doc/guide/question.html)** + +**[常见问题](https://ajreport.beliefteam.cn/report-doc/guide/question.html)** ## 商业授权 diff --git a/build/index.js b/build/index.js new file mode 100644 index 00000000..af5695c5 --- /dev/null +++ b/build/index.js @@ -0,0 +1,86 @@ +'use strict' +// Template version: 1.2.6 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: true, + errorOverlay: true, + notifyOnErrors: false, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + // Use Eslint Loader? + // If true, your code will be linted during bundling and + // linting errors and warnings will be shown in the console. + useEslint: true, + // If true, eslint errors and warnings will also be shown in the error overlay + // in the browser. + showEslintErrorsInOverlay: false, + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-source-map', + + // CSS Sourcemaps off by default because relative paths are "buggy" + // with this option, according to the CSS-Loader README + // (https://github.com/webpack/css-loader#sourcemaps) + // In our experience, they generally work as expected, + // just be aware of this issue when enabling this option. + cssSourceMap: false + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + + /** + * You can set by youself according to actual condition + * You will need to set this if you plan to deploy your site under a sub path, + * for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/, + * then assetsPublicPath should be set to "/bar/". + * In most cases please use '/' !!! + */ + assetsPublicPath: '/report/', + + /** + * Source Maps + */ + + productionSourceMap: false, + // https://webpack.js.org/configuration/devtool/#production + devtool: 'source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report || false, + + // `npm run build:prod --generate_report` + generateAnalyzerReport: process.env.npm_config_generate_report || false + } +} diff --git a/build/pom.xml b/build/pom.xml new file mode 100644 index 00000000..5bcc2a97 --- /dev/null +++ b/build/pom.xml @@ -0,0 +1,217 @@ + + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.5.RELEASE + + + + 4.0.0 + + com.anji-plus.otwb + product-report-starter + 1.2.0-SNAPSHOT + + + UTF-8 + -Xdoclint:none + 1.8 + 1.8 + true + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.apache.logging.log4j + log4j-to-slf4j + + + + + + org.apache.logging.log4j + log4j-to-slf4j + 2.15.0 + + + + org.apache.logging.log4j + log4j-api + 2.15.0 + + + + + org.springframework.boot + spring-boot-starter-cache + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-context + 2.2.6.RELEASE + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + com.anji-plus + spring-boot-gaea + 2.0.3.RELEASE + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + + com.baomidou + mybatis-plus-boot-starter + 3.3.2 + + + + mysql + mysql-connector-java + + + com.alibaba + druid + 1.2.0 + + + org.flywaydb + flyway-core + 5.2.1 + + + + net.sf.ehcache + ehcache + 2.10.6 + + + + org.apache.httpcomponents + httpclient + 4.5.10 + + + + org.projectlombok + lombok + 1.18.10 + true + + + + com.itextpdf + itextpdf + 5.5.13.2 + + + com.itextpdf + itext-asian + 5.2.0 + + + + org.apache.poi + poi + 4.1.2 + + + org.apache.poi + poi-ooxml + 4.1.2 + + + org.apache.poi + poi-ooxml-schemas + 4.1.2 + + + + org.codehaus.groovy + groovy + 3.0.9 + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + UTF-8 + + + + org.apache.maven.plugins + maven-resources-plugin + 2.4 + + UTF-8 + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + + + + nexus-releases + http://10.108.10.53:8081/repository/maven-releases/ + + + nexus-snapshots + http://10.108.10.53:8081/repository/maven-snapshots/ + + + diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 00000000..9ed7aaed --- /dev/null +++ b/deploy.sh @@ -0,0 +1,34 @@ +#!/bin/bash +#该脚本为私有化脚本,打包成内含网页的product-report-starter放在内网私服 + +#判断node.js mvn是否存在 +command -v npm >/dev/null 2>&1 || { echo >&2 "I require node.js v14.16.0+ but it's not installed. Aborting."; sleep 5; exit 1; } +command -v mvn >/dev/null 2>&1 || { echo >&2 "I require maven 3.5 + but it's not installed. Aborting."; sleep 5; exit 1; } + +cd `dirname $0` +BuildDir=`pwd` #工程根目录 + +#重置pom和index.js +cat $BuildDir/build/index.js > $BuildDir/report-ui/config/index.js +cat $BuildDir/build/pom.xml > $BuildDir/report-core/pom.xml + +echo "build web" +cd $BuildDir/report-ui +rm -rf dist +npm install >/dev/null 2>&1 +npm run build:prod + +echo "publish web to springboot src/main/resources/static" +rm -rf $BuildDir/report-core/src/main/resources/db/migration +rm -rf $BuildDir/report-core/src/main/resources/static +mkdir -p $BuildDir/report-core/src/main/resources/static +mv $BuildDir/report-ui/dist/* $BuildDir/report-core/src/main/resources/static/ + + +echo "build springboot" +cd $BuildDir/report-core + +mvn clean deploy -DskipTests + +rm -rf $BuildDir/report-core/src/main/resources/static +git reset --hard \ No newline at end of file diff --git a/doc/docs/guide/briefSupport.md b/doc/docs/guide/briefSupport.md index 120041c7..82083132 100644 --- a/doc/docs/guide/briefSupport.md +++ b/doc/docs/guide/briefSupport.md @@ -1,6 +1,9 @@ **如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues)
** -**微信群:
** +个人企业微信:加微信进群备注AJ-Report或者Report
+如果不是为了进群,请直接说明来意,每天加群的都很多,企微也是我们自己的工作企微,会有很多消息会被刷下去,如果半天没有回复你,请发送多次
+ ![weixin.png](../picture/weixin.jpg) + #### 开源不易,劳烦各位star ☺ \ No newline at end of file diff --git a/doc/docs/guide/charts.md b/doc/docs/guide/charts.md index f4e7a0fa..859c847c 100644 --- a/doc/docs/guide/charts.md +++ b/doc/docs/guide/charts.md @@ -205,4 +205,18 @@ ### 数据格式 -和饼图、南丁格尔玫瑰图数据保持一致。
\ No newline at end of file +和饼图、南丁格尔玫瑰图数据保持一致。
+ +## 热力图 + +热力图是类似坐标轴一样的数据,当前的热力图数据集需要3个字段值,对应的字典是选择“X轴”,“Y轴”,“数值”,不明白可以看静态数据
+![img21](../picture/charts/img_21.png)
+ +注意:图设置功能中的最大最小值将会对热力图中的数值产生反应,主要根据设定的颜色来反应,数值越靠近最大值,颜色将更深
+![img22](../picture/charts/img_22.png)
+ +### 数据格式 + +![img23](../picture/charts/img_23.png)
+ +**如有问题,请提交 [Issue](https://gitee.com/anji-plus/report/issues)
** \ No newline at end of file diff --git a/doc/docs/guide/quicklySeparate.md b/doc/docs/guide/quicklySeparate.md index d31a3557..7917ce58 100644 --- a/doc/docs/guide/quicklySeparate.md +++ b/doc/docs/guide/quicklySeparate.md @@ -51,6 +51,7 @@ report-core --> src --> main --> resources --> bootstrap.yml
**打包之前如果系统用的不止mysql数据源,需要自己在pom文件中加入对应的数据库的驱动,登陆系统之后,数据源提示无驱动,则选择通用JDBC数据源,这里不做演示了**
使用 maven package
**注**:不要使用maven install
+**注**:此方式不会打包 lib目录下的驱动,详情可查看 **数据源 扩展**
![img10](../picture/quickly/img_10.png)
### linux启动jar包 diff --git a/doc/docs/guide/quicklySource.md b/doc/docs/guide/quicklySource.md index 867ff652..9c272a68 100644 --- a/doc/docs/guide/quicklySource.md +++ b/doc/docs/guide/quicklySource.md @@ -15,7 +15,9 @@ http://serverip:9095 ## 编译环境 -请在Linux上先准备好maven、node.js、jdk +请在Linux上先准备好maven、node.js、jdk
+如果在Win10上部署,还需要下载一个 Git 软件,软件名就是 Git
+以下内容需要特别注意的地方会有对应提示。
- [Apache Maven] 3.5
- [Node.js] v14.16.0
@@ -34,6 +36,9 @@ git clone https://gitee.com/anji-plus/report.git
![img_4.png](../picture/quickly/img_4.png)
编译完成后是放在当前目录下的build文件夹中:aj-report-xxxx.zip
+**注:** 如果Win10部署的话,如图用git执行sh build.sh就行了。Linux就直接去report目录下执行sh build.sh就行。
+**特别注意:** 如果是Win10编译,那么几个启动脚本的格式则是win的格式,放linux上执行会报错的,反之放linux编译在win10启动也会报错,需要转格式。
+ ## 修改mysql连接 解压aj-report-xxxx.zip,找到bootstrap.yml
@@ -56,11 +61,12 @@ linux启动:
aj-report-XXX --> bin --> start.sh
sh bin/start.sh
-注意啊,如果你在linux打包,然后在win上执行,要转化start.bat文件的格式,反之也是一样。 +win10启动:
+aj-report-XXX --> bin --> start.bat
+双击start.bat启动
## 日志位置 -看到控制台提示“The AJ-Report started!”,说明report正在启动,可以看看日志以确定程序启动到哪里了。
日志的位置是:report-xxx/logs/aj-report.log
## 访问 diff --git a/doc/docs/picture/charts/img_21.png b/doc/docs/picture/charts/img_21.png new file mode 100644 index 00000000..90908a80 Binary files /dev/null and b/doc/docs/picture/charts/img_21.png differ diff --git a/doc/docs/picture/charts/img_22.png b/doc/docs/picture/charts/img_22.png new file mode 100644 index 00000000..0c7b69e6 Binary files /dev/null and b/doc/docs/picture/charts/img_22.png differ diff --git a/doc/docs/picture/charts/img_23.png b/doc/docs/picture/charts/img_23.png new file mode 100644 index 00000000..1a68a1b2 Binary files /dev/null and b/doc/docs/picture/charts/img_23.png differ diff --git a/doc/docs/picture/qiwei.png b/doc/docs/picture/qiwei.png new file mode 100644 index 00000000..c3d636b7 Binary files /dev/null and b/doc/docs/picture/qiwei.png differ diff --git a/report-core/pom.xml b/report-core/pom.xml index 423b3b05..7118783c 100644 --- a/report-core/pom.xml +++ b/report-core/pom.xml @@ -151,6 +151,13 @@ poi-ooxml-schemas 4.1.2 + + + org.codehaus.groovy + groovy + 3.0.9 + + diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java index 94494b03..f0fe7039 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/code/ResponseCode.java @@ -131,6 +131,7 @@ public interface ResponseCode { String SET_CODE_ISEXIST = "4008"; String SOURCE_CODE_ISEXIST = "4009"; String CLASS_NOT_FOUND = "4010"; + String EXECUTE_GROOVY_ERROR = "4011"; String REPORT_SHARE_LINK_INVALID = "report.share.link.invalid"; diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/constant/BusinessConstant.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/constant/BusinessConstant.java index 08357c95..4e2aa8f5 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/constant/BusinessConstant.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/constant/BusinessConstant.java @@ -11,6 +11,11 @@ public interface BusinessConstant { String RIGTH_BIG_BOAST = "}"; String LEFT_MIDDLE_BOAST = "["; String RIGHT_MIDDLE_BOAST = "]"; + String SLASH = "/"; + + String USER_GUEST = "guest"; + String USER_ADMIN = "admin"; + /** * 字典项重复 diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ReportTypeEnum.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ReportTypeEnum.java new file mode 100644 index 00000000..f2d267a3 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/enums/ReportTypeEnum.java @@ -0,0 +1,33 @@ +package com.anjiplus.template.gaea.business.enums; + +/** + * Created by raodeming on 2022/5/8. + */ +public enum ReportTypeEnum { + + /**report_screen*/ + report_screen("report_screen", "大屏报表"), + /**report_excel*/ + report_excel("report_excel", "excel报表"), + ; + + private String codeValue; + private String codeDesc; + + ReportTypeEnum() { + } + + private ReportTypeEnum(String codeValue, String codeDesc) { + this.codeValue = codeValue; + this.codeDesc = codeDesc; + } + + public String getCodeValue() { + return this.codeValue; + } + + public String getCodeDesc() { + return this.codeDesc; + } + +} 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 e6595b68..642acddc 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 @@ -1,6 +1,5 @@ package com.anjiplus.template.gaea.business.filter; - import com.alibaba.fastjson.JSONObject; import com.anji.plus.gaea.bean.ResponseBean; import com.anji.plus.gaea.cache.CacheHelper; @@ -14,10 +13,8 @@ import org.apache.http.entity.ContentType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.annotation.Order; -import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; -import org.springframework.util.CollectionUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -25,9 +22,9 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; -import java.util.stream.Collectors; import static com.anji.plus.gaea.constant.GaeaConstant.URL_REPLACEMENT; @@ -40,8 +37,9 @@ import static com.anji.plus.gaea.constant.GaeaConstant.URL_REPLACEMENT; @Order(Integer.MIN_VALUE + 99) public class TokenFilter implements Filter { private static final Pattern PATTERN = Pattern.compile(".*().*"); - private static final String USER_GUEST = "guest"; - private static final String SLASH = "/"; + + @Value("${server.servlet.context-path:/}") + private String SLASH = "/"; private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Autowired @@ -49,12 +47,13 @@ public class TokenFilter implements Filter { @Autowired private JwtBean jwtBean; - /** 跳过token验证和权限验证的url清单*/ - @Value("#{'${customer.skip-authenticate-urls}'.split(',')}") + /** + * 跳过token验证和权限验证的url清单 + */ + @Value("#{'${customer.skip-authenticate-urls:}'.split(',')}") private List skipAuthenticateUrls; private Pattern skipAuthenticatePattern; - @Override public void init(FilterConfig filterConfig) throws ServletException { // 生成匹配正则,跳过token验证和权限验证的url @@ -68,14 +67,26 @@ public class TokenFilter implements Filter { HttpServletResponse response = (HttpServletResponse) servletResponse; String uri = request.getRequestURI(); + // TODO 暂时先不校验 直接放行 + /*if (true) { + filterChain.doFilter(request, response); + return; + }*/ + //OPTIONS直接放行 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { filterChain.doFilter(request, response); return; } - if (SLASH.equals(uri)) { - response.sendRedirect("/index.html"); + // swagger相关的直接放行 + if (uri.contains("swagger-ui") || uri.contains("swagger-resources")) { + filterChain.doFilter(request, response); + return; + } + + if (SLASH.equals(uri) || SLASH.concat(BusinessConstant.SLASH).equals(uri)) { + response.sendRedirect(SLASH + "/index.html"); return; } @@ -119,59 +130,23 @@ public class TokenFilter implements Filter { error(response); return; } - if (!cacheHelper.exist(userKey)) { - error(response); - return; - } - String gaeaUserJsonStr = cacheHelper.stringGet(userKey); - //判断接口权限 - //请求路径 - String requestUrl = request.getRequestURI(); - String methodValue = request.getMethod(); - //请求方法+#+请求路径 - String urlKey = methodValue + GaeaConstant.URL_SPLIT + requestUrl; + String gaeaUserJsonStr = cacheHelper.stringGet(userKey); - GaeaUserDto gaeaUserDto = JSONObject.parseObject(gaeaUserJsonStr, GaeaUserDto.class); - List authorities = gaeaUserDto.getAuthorities(); - Map applicationNameAllAuthorities = cacheHelper.hashGet(BusinessConstant.GAEA_SECURITY_AUTHORITIES); - AtomicBoolean authFlag = new AtomicBoolean(false); - //查询当前请求是否在对应的权限里。即:先精确匹配(保证当前路由是需要精确匹配还是模糊匹配,防止精确匹配的被模糊匹配) - // 比如:/user/info和/user/**同时存在,/user/info,被/user/**匹配掉 - if (applicationNameAllAuthorities.containsKey(urlKey)) { - String permissionCode = applicationNameAllAuthorities.get(urlKey); - if (authorities.contains(permissionCode)) { - authFlag.set(true); - } - } else { - List collect = applicationNameAllAuthorities.keySet().stream() - .filter(key -> StringUtils.isNotBlank(key) && key.contains(URL_REPLACEMENT)) - .filter(key -> antPathMatcher.match(key, urlKey)).collect(Collectors.toList()); - if (CollectionUtils.isEmpty(collect)) { - authFlag.set(true); - }else { - collect.forEach(key -> { - String permissionCode = applicationNameAllAuthorities.getOrDefault(key, ""); - if (authorities.contains(permissionCode)) { - authFlag.set(true); - } - }); + // 判断用户是否有该url的权限 + if (!BusinessConstant.USER_ADMIN.equals(loginName)) { + AtomicBoolean authorizeFlag = authorize(request, gaeaUserJsonStr); + if (!authorizeFlag.get()) { + authError(response);//无权限 + return; } } - if (!authFlag.get()) { - //无权限 - authError(response); - return; - } - - - - // 延长有效期 cacheHelper.stringSetExpire(tokenKey, token, 3600); cacheHelper.stringSetExpire(userKey, gaeaUserJsonStr, 3600); + //执行 filterChain.doFilter(request, response); } @@ -206,14 +181,58 @@ public class TokenFilter implements Filter { return Pattern.compile(patternString.toString()); } + /** 判断用户是否有该接口的权限 + * @return + */ + private AtomicBoolean authorize(HttpServletRequest request, String gaeaUserJsonStr){ + + //判断接口权限 + //请求路径 + String requestUrl = request.getRequestURI(); + if (!BusinessConstant.SLASH.equals(SLASH)) { + requestUrl = requestUrl.substring(SLASH.length()); + } + String methodValue = request.getMethod(); + //请求方法+#+请求路径 + String path = methodValue + GaeaConstant.URL_SPLIT + requestUrl; + + GaeaUserDto gaeaUserDto = JSONObject.parseObject(gaeaUserJsonStr, GaeaUserDto.class); + List userAuthorities = gaeaUserDto.getAuthorities(); + Map authoritiesAllMap = cacheHelper.hashGet(BusinessConstant.GAEA_SECURITY_AUTHORITIES); + + AtomicBoolean authFlag = new AtomicBoolean(false); + + // 接口GET#/gaeaDictItem/pageList + if(authoritiesAllMap.containsKey(path)){ + String permissionCode = authoritiesAllMap.get(path); + boolean flag = userAuthorities.contains(permissionCode); + authFlag.set(flag); + return authFlag; + } + + // 接口GET#/accessUser/roleTree/** + Optional optionalMatchKey = authoritiesAllMap.keySet().stream() + .filter(key -> StringUtils.isNotBlank(key) && key.contains(URL_REPLACEMENT)) + .filter(key -> antPathMatcher.match(key, path)).findFirst(); + if(optionalMatchKey.isPresent() == false){ + authFlag.set(true); + return authFlag; + } + String authoritieKey = optionalMatchKey.get(); + String needPermission = authoritiesAllMap.get(authoritieKey); + boolean flag = userAuthorities.contains(needPermission); + authFlag.set(flag); + return authFlag; + } + private void error(HttpServletResponse response) throws IOException { - ResponseBean responseBean = ResponseBean.builder().code("50014").message("The Token has expired").build(); + ResponseBean responseBean = ResponseBean.builder().code("50008").message("The Token has expired").build(); response.setContentType(ContentType.APPLICATION_JSON.getMimeType()); response.getWriter().print(JSONObject.toJSONString(responseBean)); } private void authError(HttpServletResponse response) throws IOException { - ResponseBean responseBean = ResponseBean.builder().code("User.no.authority").message("没有权限").build(); + ResponseBean responseBean = ResponseBean.builder().code("User.no.authority").message("no auth").build(); response.setContentType(ContentType.APPLICATION_JSON.getMimeType()); response.getWriter().print(JSONObject.toJSONString(responseBean)); } diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/IGroovyHandler.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/IGroovyHandler.java new file mode 100644 index 00000000..eb03cdaa --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/IGroovyHandler.java @@ -0,0 +1,14 @@ +package com.anjiplus.template.gaea.business.modules.datasettransform.service; + +import com.alibaba.fastjson.JSONObject; + +import java.util.List; + +/** + * @author: Raod + * @since: 2022-02-23 + */ +public interface IGroovyHandler { + + List transform(List data); +} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/GroovyTransformServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/GroovyTransformServiceImpl.java new file mode 100644 index 00000000..dc2de478 --- /dev/null +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/GroovyTransformServiceImpl.java @@ -0,0 +1,65 @@ +package com.anjiplus.template.gaea.business.modules.datasettransform.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.anji.plus.gaea.exception.BusinessExceptionBuilder; +import com.anjiplus.template.gaea.business.code.ResponseCode; +import com.anjiplus.template.gaea.business.modules.datasettransform.controller.dto.DataSetTransformDto; +import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler; +import com.anjiplus.template.gaea.business.modules.datasettransform.service.TransformStrategy; +import groovy.lang.GroovyClassLoader; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.script.Invocable; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.util.List; + +/** + * Created by raodeming on 2021/3/23. + */ +@Component +@Slf4j +public class GroovyTransformServiceImpl implements TransformStrategy { + + private GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); + + /** + * 数据清洗转换 类型 + * + * @return + */ + @Override + public String type() { + return "javaBean"; + } + + /*** + * 清洗转换算法接口 + * @param def + * @param data + * @return + */ + @Override + public List transform(DataSetTransformDto def, List data) { + String transformScript = def.getTransformScript(); + Class clazz = groovyClassLoader.parseClass(transformScript); + if (clazz != null) { + try { + Object instance = clazz.newInstance(); + if (instance!=null) { + if (instance instanceof IGroovyHandler) { + IGroovyHandler handler = (IGroovyHandler) instance; + return handler.transform(data); + } else { + System.err.println("转换失败!"); + } + } + } catch (Exception e) { + log.info("执行javaBean异常", e); + throw BusinessExceptionBuilder.build(ResponseCode.EXECUTE_GROOVY_ERROR, e.getMessage()); + } + } + return data; + } +} diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/JsTransformServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/JsTransformServiceImpl.java index 082a2796..a33a7dff 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/JsTransformServiceImpl.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasettransform/service/impl/JsTransformServiceImpl.java @@ -58,6 +58,7 @@ public class JsTransformServiceImpl implements TransformStrategy { } } catch (Exception ex) { + log.info("执行js异常", ex); throw BusinessExceptionBuilder.build(ResponseCode.EXECUTE_JS_ERROR, ex.getMessage()); } return null; diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/JdbcServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/JdbcServiceImpl.java index 694aa2fd..016bad3f 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/JdbcServiceImpl.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/datasource/service/impl/JdbcServiceImpl.java @@ -26,18 +26,21 @@ public class JdbcServiceImpl implements JdbcService { /** * 所有数据源的连接池存在map里 */ - static Map map = new ConcurrentHashMap<>(); + private Map map = new ConcurrentHashMap<>(); + private Object lock = new Object(); public DruidDataSource getJdbcConnectionPool(DataSourceDto dataSource) { if (map.containsKey(dataSource.getId())) { return map.get(dataSource.getId()); } else { try { - if (!map.containsKey(dataSource.getId())) { - DruidDataSource pool = druidProperties.dataSource(dataSource.getJdbcUrl(), - dataSource.getUsername(), dataSource.getPassword(), dataSource.getDriverName()); - map.put(dataSource.getId(), pool); - log.info("创建连接池成功:{}", dataSource.getJdbcUrl()); + synchronized (lock) { + if (!map.containsKey(dataSource.getId())) { + DruidDataSource pool = druidProperties.dataSource(dataSource.getJdbcUrl(), + dataSource.getUsername(), dataSource.getPassword(), dataSource.getDriverName()); + map.put(dataSource.getId(), pool); + log.info("创建连接池成功:{}", dataSource.getJdbcUrl()); + } } return map.get(dataSource.getId()); } finally { diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/controller/ReportController.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/controller/ReportController.java index 6269e7d0..65c06afe 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/controller/ReportController.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/controller/ReportController.java @@ -11,10 +11,7 @@ import com.anjiplus.template.gaea.business.modules.report.dao.entity.Report; import com.anjiplus.template.gaea.business.modules.report.service.ReportService; import io.swagger.annotations.Api; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; /** * TODO @@ -46,11 +43,11 @@ public class ReportController extends GaeaBaseController { - void delReport(ReportDto reportDto); /** * 下载次数+1 * @param reportCode */ void downloadStatistics(String reportCode); + + /** + * 复制大屏 + * @param reportId + */ + void copy(Long reportId); } diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java index e5b32704..4a36d9f0 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/modules/report/service/impl/ReportServiceImpl.java @@ -3,12 +3,26 @@ package com.anjiplus.template.gaea.business.modules.report.service.impl; import com.anji.plus.gaea.constant.BaseOperationEnum; import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper; import com.anji.plus.gaea.exception.BusinessException; +import com.anji.plus.gaea.utils.GaeaBeanUtils; +import com.anjiplus.template.gaea.business.enums.ReportTypeEnum; +import com.anjiplus.template.gaea.business.modules.dashboard.dao.entity.ReportDashboard; +import com.anjiplus.template.gaea.business.modules.dashboard.service.ReportDashboardService; +import com.anjiplus.template.gaea.business.modules.dashboardwidget.dao.entity.ReportDashboardWidget; +import com.anjiplus.template.gaea.business.modules.dashboardwidget.service.ReportDashboardWidgetService; import com.anjiplus.template.gaea.business.modules.report.controller.dto.ReportDto; import com.anjiplus.template.gaea.business.modules.report.dao.ReportMapper; import com.anjiplus.template.gaea.business.modules.report.dao.entity.Report; import com.anjiplus.template.gaea.business.modules.report.service.ReportService; +import com.anjiplus.template.gaea.business.modules.reportexcel.dao.entity.ReportExcel; +import com.anjiplus.template.gaea.business.modules.reportexcel.service.ReportExcelService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; /** * @@ -20,6 +34,12 @@ public class ReportServiceImpl implements ReportService { @Autowired private ReportMapper reportMapper; + @Autowired + private ReportDashboardService reportDashboardService; + @Autowired + private ReportDashboardWidgetService reportDashboardWidgetService; + @Autowired + private ReportExcelService reportExcelService; @Override public GaeaBaseMapper getMapper() { @@ -28,10 +48,41 @@ public class ReportServiceImpl implements ReportService { @Override - public void delReport(ReportDto reportDto) { - deleteById(reportDto.getId()); - //删除gaea_report_excel、gaea_report_dashboard、gaea_report_dashboard_widget - //... + public void processBatchBeforeOperation(List entities, BaseOperationEnum operationEnum) throws BusinessException { + ReportService.super.processBatchAfterOperation(entities, operationEnum); + switch (operationEnum) { + case DELETE_BATCH: + entities.forEach(report -> { + Long id = report.getId(); + Report delReport = selectOne(id); + if (null == delReport) { + return; + } + String reportCode = delReport.getReportCode(); + String reportType = delReport.getReportType(); + switch (ReportTypeEnum.valueOf(reportType)) { + case report_screen: + LambdaQueryWrapper reportDashboardLambdaQueryWrapper = Wrappers.lambdaQuery(); + reportDashboardLambdaQueryWrapper.eq(ReportDashboard::getReportCode, reportCode); + reportDashboardService.delete(reportDashboardLambdaQueryWrapper); + + LambdaQueryWrapper reportDashboardWidgetLambdaQueryWrapper = Wrappers.lambdaQuery(); + reportDashboardWidgetLambdaQueryWrapper.eq(ReportDashboardWidget::getReportCode, reportCode); + reportDashboardWidgetService.delete(reportDashboardWidgetLambdaQueryWrapper); + + break; + case report_excel: + LambdaQueryWrapper reportExcelLambdaQueryWrapper = Wrappers.lambdaQuery(); + reportExcelLambdaQueryWrapper.eq(ReportExcel::getReportCode, reportCode); + reportExcelService.delete(reportExcelLambdaQueryWrapper); + break; + default: + } + }); + break; + default: + + } } /** @@ -55,6 +106,64 @@ public class ReportServiceImpl implements ReportService { } + @Override + public void copy(Long reportId) { + Report report = selectOne(reportId); + String reportCode = report.getReportCode(); + Report copyReport = copyReport(report); + //复制主表数据 + insert(copyReport); + String copyReportCode = copyReport.getReportCode(); + String reportType = report.getReportType(); + switch (ReportTypeEnum.valueOf(reportType)) { + case report_screen: + //查询看板 + ReportDashboard reportDashboard = reportDashboardService.selectOne("report_code", reportCode); + if (null != reportDashboard) { + reportDashboard.setId(null); + reportDashboard.setReportCode(copyReportCode); + reportDashboardService.insert(reportDashboard); + } + + //查询组件 + List reportDashboardWidgetList = reportDashboardWidgetService.list("report_code", reportCode); + if (!CollectionUtils.isEmpty(reportDashboardWidgetList)) { + String finalCopyReportCode = copyReportCode; + reportDashboardWidgetList.forEach(reportDashboardWidget -> { + reportDashboardWidget.setId(null); + reportDashboardWidget.setReportCode(finalCopyReportCode); + }); + reportDashboardWidgetService.insertBatch(reportDashboardWidgetList); + } + + break; + case report_excel: + ReportExcel reportExcel = reportExcelService.selectOne("report_code", reportCode); + if (null != reportExcel) { + reportExcel.setId(null); + reportExcel.setReportCode(copyReportCode); + reportExcelService.insert(reportExcel); + } + + break; + default: + } + } + + private Report copyReport(Report report){ + //复制主表数据 + Report copyReport = new Report(); + GaeaBeanUtils.copyAndFormatter(report, copyReport); + copyReport.setId(null); + String copyReportCode = copyReport.getReportCode().concat("_").concat(String.valueOf(System.currentTimeMillis())); + if (copyReportCode.length() >= 100) { + copyReportCode = copyReportCode.substring(0, 100); + } + copyReport.setReportCode(copyReportCode); + copyReport.setReportName(copyReport.getReportName().concat("_copy")); + return copyReport; + } + @Override public void processBeforeOperation(Report entity, BaseOperationEnum operationEnum) throws BusinessException { diff --git a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java index 8a81f0c1..6fcdbacf 100644 --- a/report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java +++ b/report-core/src/main/java/com/anjiplus/template/gaea/business/util/FileUtil.java @@ -8,6 +8,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.net.URL; import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.Enumeration; @@ -116,9 +117,11 @@ public class FileUtil { */ public static String readFile(File file) { BufferedReader reader = null; + InputStreamReader isr = null; StringBuilder sbf = new StringBuilder(); try { - reader = new BufferedReader(new FileReader(file)); + isr = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8); + reader = new BufferedReader(isr); String tempStr; while ((tempStr = reader.readLine()) != null) { sbf.append(tempStr); @@ -129,6 +132,13 @@ public class FileUtil { log.error("读文件失败", e); throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage()); } finally { + if (null != isr) { + try { + isr.close(); + } catch (IOException e) { + throw BusinessExceptionBuilder.build(ResponseCode.FAIL_CODE, e.getMessage()); + } + } if (reader != null) { try { reader.close(); diff --git a/report-core/src/main/resources/bootstrap.yml b/report-core/src/main/resources/bootstrap.yml index b694ce45..2d3581a3 100644 --- a/report-core/src/main/resources/bootstrap.yml +++ b/report-core/src/main/resources/bootstrap.yml @@ -66,9 +66,10 @@ logging: customer: # 开发测试用本地文件,如果是生产,请考虑使用对象存储 file: - #上传对应本地全路径,路径必须是真实存在的 + #上传对应本地全路径,目录必须是真实存在的,注意 Win是 \ 且有盘符,linux是 / 无盘符 dist-path: /app/disk/upload/ - white-list: .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi + #dist-path: D:\Download + white-list: .png|.jpg|.jpeg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi excelSuffix: .xlsx|.xls|.csv #上传对应下载的下载链接路径 http://serverip:9095/file/download downloadPath: http://10.108.26.197:9095/file/download diff --git a/report-core/src/main/resources/db/migration/V1.0.15__update_javaBean.sql b/report-core/src/main/resources/db/migration/V1.0.15__update_javaBean.sql new file mode 100644 index 00000000..9d1b9738 --- /dev/null +++ b/report-core/src/main/resources/db/migration/V1.0.15__update_javaBean.sql @@ -0,0 +1 @@ +UPDATE `aj_report`.`gaea_dict_item` SET `dict_code` = 'TRANSFORM_TYPE', `item_name` = 'java脚本', `item_value` = 'javaBean', `item_extend` = NULL, `enabled` = 1, `locale` = 'zh', `remark` = NULL, `sort` = 2, `create_by` = 'admin', `create_time` = '2021-03-23 10:54:08', `update_by` = 'admin', `update_time` = '2021-03-23 10:54:08', `version` = 1 WHERE `id` = 151; diff --git a/report-core/src/main/resources/db/migration/V1.0.16__update_admin_authority.sql b/report-core/src/main/resources/db/migration/V1.0.16__update_admin_authority.sql new file mode 100644 index 00000000..85c9b928 --- /dev/null +++ b/report-core/src/main/resources/db/migration/V1.0.16__update_admin_authority.sql @@ -0,0 +1,5 @@ +-- 补充admin对于execl表格权限 + +INSERT INTO `aj_report`.`access_role_authority`(`role_code`,`target`,`action`) SELECT "root","excelManage","insert" FROM DUAL WHERE NOT EXISTS(SELECT `role_code`,`target`,`action` FROM `aj_report`.`access_role_authority` WHERE `role_code`="root" AND `target`="excelManage" AND `action`="insert"); + +INSERT INTO `aj_report`.`access_role_authority`(`role_code`,`target`,`action`) SELECT "root","excelManage","update" FROM DUAL WHERE NOT EXISTS(SELECT `role_code`,`target`,`action` FROM `aj_report`.`access_role_authority` WHERE `role_code`="root" AND `target`="excelManage" AND `action`="update"); diff --git a/report-core/src/main/resources/db/migration/V1.0.17__add_dict_coord.sql b/report-core/src/main/resources/db/migration/V1.0.17__add_dict_coord.sql new file mode 100644 index 00000000..7410e4f8 --- /dev/null +++ b/report-core/src/main/resources/db/migration/V1.0.17__add_dict_coord.sql @@ -0,0 +1,7 @@ +-- 新增坐标轴字典 + +INSERT INTO `aj_report`.`gaea_dict`(`dict_name`, `dict_code`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('XY坐标属性', 'COORD_PROPERTIES', 'XY坐标属性', 'admin', NOW(), 'admin', NOW(), 1); + +INSERT INTO `aj_report`.`gaea_dict_item`(`dict_code`, `item_name`, `item_value`, `item_extend`, `enabled`, `locale`, `remark`, `sort`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('COORD_PROPERTIES', '数据', 'series', NULL, 1, 'zh', NULL, NULL, 'admin', NOW(), 'admin', NOW(), 1); +INSERT INTO `aj_report`.`gaea_dict_item`(`dict_code`, `item_name`, `item_value`, `item_extend`, `enabled`, `locale`, `remark`, `sort`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('COORD_PROPERTIES', 'X轴', 'xAxis', NULL, 1, 'zh', NULL, NULL, 'admin', NOW(), 'admin', NOW(), 1); +INSERT INTO `aj_report`.`gaea_dict_item`(`dict_code`, `item_name`, `item_value`, `item_extend`, `enabled`, `locale`, `remark`, `sort`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('COORD_PROPERTIES', 'Y轴', 'yAxis', NULL, 1, 'zh', NULL, NULL, 'admin', NOW(), 'admin', NOW(), 1); diff --git a/report-core/src/main/resources/db/migration/V1.0.18__update_role_authority.sql b/report-core/src/main/resources/db/migration/V1.0.18__update_role_authority.sql new file mode 100644 index 00000000..78436220 --- /dev/null +++ b/report-core/src/main/resources/db/migration/V1.0.18__update_role_authority.sql @@ -0,0 +1,16 @@ +-- 角色权限调整 +-- access_authority +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('access', 'authorityManage', '权限管理', 'detail', '权限明细', 101, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('access', 'roleManage', '角色管理', 'detail', '角色明细', 105, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2021-07-17 20:41:46', 2); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('access', 'userManage', '用户管理', 'detail', '用户明细', 110, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'datasourceManage', '数据源管理', 'detail', '数据源明细', 200, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'resultsetManage', '数据集管理', 'detail', '数据集明细', 204, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'reportManage', '报表管理', 'detail', '报表明细', 221, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'bigScreenManage', '大屏报表', 'detail', '大屏明细', 231, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'excelManage', '表格报表', 'detail', 'excel明细', 234, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('system', 'fileManage', '文件管理', 'detail', '文件明细', 300, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('system', 'dictManage', '数据字典', 'detail', '数据字典明细', 300, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('system', 'dictItemManage', '数据字典项', 'detail', '数据字典项明细', 300, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); +REPLACE INTO `aj_report`.`access_authority`(`parent_target`, `target`, `target_name`, `action`, `action_name`, `sort`, `enable_flag`, `delete_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `version`) VALUES ('report', 'bigScreenManage', '大屏报表', 'copy', '复制大屏', 236, 1, 0, 'admin', '2019-07-23 15:59:40', 'admin', '2019-07-23 15:59:40', 1); + +UPDATE `aj_report`.`access_authority` SET `parent_target` = 'report', `target` = 'bigScreenManage', `target_name` = '大屏报表', `action` = 'view', `action_name` = '查看大屏', `sort` = 232, `enable_flag` = 1, `delete_flag` = 0, `create_by` = 'admin', `create_time` = '2019-07-23 15:59:40', `update_by` = 'admin', `update_time` = '2019-07-23 15:59:40', `version` = 1 WHERE `parent_target` = 'report' AND `target` = 'bigScreenManage' AND `action` = 'view'; diff --git a/report-core/src/main/resources/i18n/messages_en_US.properties b/report-core/src/main/resources/i18n/messages_en_US.properties index 4219c464..43cf12ef 100644 --- a/report-core/src/main/resources/i18n/messages_en_US.properties +++ b/report-core/src/main/resources/i18n/messages_en_US.properties @@ -47,5 +47,6 @@ Component.load.check.error={0} Component not load 4008=The set code does not allow duplication 4009=The source code does not allow duplication 4010=Can't auto find match driver class +4011=execute javaBean error report.share.link.invalid=report share link invalid diff --git a/report-core/src/main/resources/i18n/messages_zh_CN.properties b/report-core/src/main/resources/i18n/messages_zh_CN.properties index c25afa66..a8e54261 100644 --- a/report-core/src/main/resources/i18n/messages_zh_CN.properties +++ b/report-core/src/main/resources/i18n/messages_zh_CN.properties @@ -48,6 +48,7 @@ Component.load.check.error={0}\u7EC4\u4EF6\u672A\u52A0\u8F7D 4008=\u6570\u636E\u96C6\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D 4009=\u6570\u636E\u6E90\u7F16\u7801\u4E0D\u5141\u8BB8\u91CD\u590D 4010=\u9A71\u52A8\u5305\u4E0D\u5B58\u5728 +4011=\u6267\u884CjavaBean\u5931\u8D25 6001={0} 7001=\u89E3\u6790\u5931\u8D25 diff --git a/report-core/src/test/java/com/DemoGroovyHandler.java b/report-core/src/test/java/com/DemoGroovyHandler.java new file mode 100644 index 00000000..1ec6cb54 --- /dev/null +++ b/report-core/src/test/java/com/DemoGroovyHandler.java @@ -0,0 +1,18 @@ +package com; + +import com.alibaba.fastjson.JSONObject; +import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler; + +import java.util.List; + +/** + * 建议在idea写好复制整个类到此处,位置report-core/src/test/java/com/DemoGroovyHandler.java + */ +public class DemoGroovyHandler implements IGroovyHandler { + + @Override + public List transform(List data) { + + return data; + } +} diff --git a/report-core/src/test/java/com/GroovyTest.java b/report-core/src/test/java/com/GroovyTest.java new file mode 100644 index 00000000..b695c361 --- /dev/null +++ b/report-core/src/test/java/com/GroovyTest.java @@ -0,0 +1,58 @@ +package com; + +import com.alibaba.fastjson.JSONObject; +import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler; +import groovy.lang.GroovyClassLoader; + +import java.util.List; + +/** + * @author: Raod + * @since: 2022-02-23 + */ +public class GroovyTest { + + + public static void main(String[] args) throws InstantiationException, IllegalAccessException { + // codeSource来自DemoGroovyHandler + String codeSource = "package com;\n" + + "\n" + + "import com.alibaba.fastjson.JSONObject;\n" + + "import com.anjiplus.template.gaea.business.modules.datasettransform.service.IGroovyHandler;\n" + + "\n" + + "import java.util.ArrayList;\n" + + "import java.util.List;\n" + + "\n" + + "/**\n" + + " * @author: Raod\n" + + " * @since: 2022-02-23\n" + + " */\n" + + "public class DemoGroovyHandler implements IGroovyHandler {\n" + + "\n" + + " @Override\n" + + " public List transform(List data) {\n" + + " List result = new ArrayList<>();\n" + + " JSONObject jsonObject = new JSONObject();\n" + + " jsonObject.put(\"test\", \"demo\");\n" + + " result.add(jsonObject);\n" + + " return result;\n" + + " }\n" + + "}"; + GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); + Class clazz = groovyClassLoader.parseClass(codeSource); + if (clazz != null) { + Object instance = clazz.newInstance(); + if (instance!=null) { + if (instance instanceof IGroovyHandler) { + IGroovyHandler handler = (IGroovyHandler) instance; + List transform = handler.transform(null); + System.out.println(JSONObject.toJSONString(transform)); + + } else { + System.err.println("转换失败!"); + } + } + } + } + +} diff --git a/report-ui/src/api/dict-data.js b/report-ui/src/api/dict-data.js index 34c6d366..40ef8977 100644 --- a/report-ui/src/api/dict-data.js +++ b/report-ui/src/api/dict-data.js @@ -71,7 +71,7 @@ export function initDictToLocalstorage(callback) { } // 保存数据字典到localStorage - setStorageItem('gaeaDict', res.data) + setStorageItem('AJReportDict', res.data) if (callback != null) { callback() } diff --git a/report-ui/src/api/reportmanage.js b/report-ui/src/api/reportmanage.js index 351f62cc..3858fde0 100644 --- a/report-ui/src/api/reportmanage.js +++ b/report-ui/src/api/reportmanage.js @@ -39,4 +39,12 @@ export function reportDetail(data) { }) } +export function reportCopy(data) { + return request({ + url: '/report/copy', + method: 'get', + params: { reportId: data.id } + }) +} + export default { reportList, reportAdd, reportDeleteBatch, reportUpdate, reportDetail } diff --git a/report-ui/src/assets/iconfont/demo_index.html b/report-ui/src/assets/iconfont/demo_index.html index 648de746..be9a80fd 100644 --- a/report-ui/src/assets/iconfont/demo_index.html +++ b/report-ui/src/assets/iconfont/demo_index.html @@ -54,6 +54,24 @@
    +
  • + +
    热力图
    +
    &#xe683;
    +
  • + +
  • + +
    恢复备份
    +
    &#xe60d;
    +
  • + +
  • + +
    撤销
    +
    &#xe60e;
    +
  • +
  • 词云图
    @@ -792,9 +810,9 @@
    @font-face {
       font-family: 'iconfont';
    -  src: url('iconfont.woff2?t=1643094287456') format('woff2'),
    -       url('iconfont.woff?t=1643094287456') format('woff'),
    -       url('iconfont.ttf?t=1643094287456') format('truetype');
    +  src: url('iconfont.woff2?t=1650520683161') format('woff2'),
    +       url('iconfont.woff?t=1650520683161') format('woff'),
    +       url('iconfont.ttf?t=1650520683161') format('truetype');
     }
     

    第二步:定义使用 iconfont 的样式

    @@ -820,6 +838,33 @@
      +
    • + +
      + 热力图 +
      +
      .iconrelitu +
      +
    • + +
    • + +
      + 恢复备份 +
      +
      .iconhuifubeifen +
      +
    • + +
    • + +
      + 撤销 +
      +
      .iconundo +
      +
    • +
    • @@ -1927,6 +1972,30 @@
        +
      • + +
        热力图
        +
        #iconrelitu
        +
      • + +
      • + +
        恢复备份
        +
        #iconhuifubeifen
        +
      • + +
      • + +
        撤销
        +
        #iconundo
        +
      • +
      • - + 新增{{ handlegetLable(checkRecords, item.label) }} - 删除 -
      @@ -282,59 +265,58 @@ align="center" fixed="right" label="操作" - :width=" - option.buttons.customButton && - option.buttons.customButton.operationWidth - ? option.buttons.customButton.operationWidth - : 100 - " + :width="option.buttons.rowButtonsWidth || 100" > @@ -668,6 +650,21 @@ export default { }; this.$emit("handleCustomValue", obj); }, + handlegetLable(item, label) { + if (typeof label == "function") { + return label(item); + } else { + return label; + } + }, + // 是否disabled + isDisabledButton(item, row) { + if (typeof item.isDisable === "function") { + return item.isDisable(row); + } else { + return !!item.disabled; + } + }, // 弹框被关闭时的回调事件 editDialogClosedEvent(value) { // 把列表页中弹框打开标记改成已关闭 diff --git a/report-ui/src/components/AnjiPlus/anji-select.vue b/report-ui/src/components/AnjiPlus/anji-select.vue index f184126e..86d44cd5 100644 --- a/report-ui/src/components/AnjiPlus/anji-select.vue +++ b/report-ui/src/components/AnjiPlus/anji-select.vue @@ -244,9 +244,9 @@ export default { } return result; }, - // 从本地localStorage取 gaeaDict + // 从本地localStorage取 AJReportDict getOptionsFromLocalStorage() { - let dicts = JSON.parse(localStorage.getItem("gaeaDict")); + let dicts = JSON.parse(localStorage.getItem("AJReportDict")); let options = []; if (!dicts.hasOwnProperty(this.dictCode)) { return []; diff --git a/report-ui/src/components/AnjiPlus/anji-upload.vue b/report-ui/src/components/AnjiPlus/anji-upload.vue index 565d17df..f07e9a47 100644 --- a/report-ui/src/components/AnjiPlus/anji-upload.vue +++ b/report-ui/src/components/AnjiPlus/anji-upload.vue @@ -6,39 +6,16 @@ :action="requestUrl" list-type="picture-card" :file-list="fileList" - :on-preview="handlePictureCardPreview" :on-remove="handleRemove" + :on-exceed="handleExceed" :on-success="handleSuccess" :show-file-list="true" :before-upload="handleBeforeUpload" - :class="fileList && fileList.length >= limit ? 'hide_box' : ''" >
      - - - + - - -
      - - -
      + + diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineCompareChart.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineCompareChart.vue index e71d3b6c..729ee3b9 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineCompareChart.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineCompareChart.vue @@ -618,7 +618,7 @@ export default { xAxisList = this.setUnique(xAxisList); yAxisList = this.setUnique(yAxisList); for (const i in yAxisList) { - const data = new Array(yAxisList.length).fill(0); + const data = new Array(xAxisList.length).fill(0); for (const j in xAxisList) { for (const k in val) { if (val[k].name == yAxisList[i]) { diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineStackChart.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineStackChart.vue index c06ec3d4..6bfd591f 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineStackChart.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLineStackChart.vue @@ -332,7 +332,7 @@ export default { xAxisList = this.setUnique(xAxisList); yAxisList = this.setUnique(yAxisList); for (const i in yAxisList) { - const data = new Array(yAxisList.length).fill(0); + const data = new Array(xAxisList.length).fill(0); for (const j in xAxisList) { for (const k in val) { if (val[k].name == yAxisList[i]) { diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLinechart.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLinechart.vue index 15e931ba..8505293e 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLinechart.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/line/widgetLinechart.vue @@ -105,7 +105,6 @@ export default { this.setOptionsTooltip(); this.setOptionsData(); this.setOptionsMargin(); - this.setOptionsLegend(); this.setOptionsColor(); }, // 标题修改 @@ -244,7 +243,7 @@ export default { show: true, textStyle: { color: optionsSetup.lineColor, - fontSize: optionsSetup.fontSize + fontSize: optionsSetup.tipsFontSize } }; this.options.tooltip = tooltip; @@ -261,42 +260,6 @@ export default { }; this.options.grid = grid; }, - // 图例操作 legend - setOptionsLegend() { - const optionsSetup = this.optionsSetup; - const legend = this.options.legend; - legend.show = optionsSetup.isShowLegend; - legend.left = optionsSetup.lateralPosition; - legend.right = optionsSetup.lateralPosition; - legend.top = optionsSetup.longitudinalPosition; - legend.bottom = - optionsSetup.longitudinalPosition; - legend.orient = optionsSetup.layoutFront; - legend.textStyle = { - color: optionsSetup.lengedColor, - fontSize: optionsSetup.fontSize - }; - legend.itemWidth = optionsSetup.lengedWidth; - }, - // 图例名称设置 - setOptionsLegendName(name){ - const optionsSetup = this.optionsSetup; - const series = this.options.series; - const legendName = optionsSetup.legendName; - // 图例没有手动写则显示原值,写了则显示新值 - if (null == legendName || legendName == '') { - for (let i = 0; i < name.length; i++) { - series[i].name = name[i]; - } - this.options.legend['data'] = name; - }else { - const arr = legendName.split('|'); - for (let i = 0; i < arr.length; i++) { - series[i].name = arr[i]; - } - this.options.legend['data'] = arr - } - }, // 图例颜色修改 setOptionsColor() { const optionsSetup = this.optionsSetup; @@ -332,10 +295,6 @@ export default { series[i].data = data; } } - const legendName = []; - legendName.push('销售量') - this.options.legend['data'] = legendName; - this.setOptionsLegendName(legendName); }, dynamicDataFn(val, refreshTime) { if (!val) return; @@ -359,15 +318,11 @@ export default { this.options.xAxis.data = val.xAxis; // series const series = this.options.series; - const legendName = []; for (const i in series) { if (series[i].type == "line") { series[i].data = val.series[i].data; } - legendName.push(val.series[i].name); } - this.options.legend['data'] = legendName; - this.setOptionsLegendName(legendName); } } }; diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue index 90cfde5a..545c18ab 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/temp.vue @@ -38,6 +38,7 @@ import widgetLineCompareChart from "./line/widgetLineCompareChart"; import widgetDecoratePieChart from "./decorate/widgetDecoratePieChart"; import widgetMoreBarLineChart from "./bar/widgetMoreBarLineChart"; import widgetWordCloud from "./wordcloud/widgetWordCloud"; +import widgetHeatmap from "./heatmap/widgetHeatmap"; export default { name: "WidgetTemp", @@ -68,7 +69,8 @@ export default { widgetLineCompareChart, widgetDecoratePieChart, widgetMoreBarLineChart, - widgetWordCloud + widgetWordCloud, + widgetHeatmap }, model: { prop: "value", diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue index a3515311..da65cbb6 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/widget.vue @@ -17,7 +17,7 @@ @focus="handleFocus" @blur="handleBlur" > - + @@ -49,6 +49,7 @@ import widgetLineCompareChart from "./line/widgetLineCompareChart"; import widgetDecoratePieChart from "./decorate/widgetDecoratePieChart"; import widgetMoreBarLineChart from "./bar/widgetMoreBarLineChart"; import widgetWordCloud from "./wordcloud/widgetWordCloud"; +import widgetHeatmap from "./heatmap/widgetHeatmap"; export default { name: "Widget", @@ -79,7 +80,8 @@ export default { widgetLineCompareChart, widgetDecoratePieChart, widgetMoreBarLineChart, - widgetWordCloud + widgetWordCloud, + widgetHeatmap }, model: { prop: "value", @@ -95,8 +97,7 @@ export default { bigscreen: Object, value: { type: [Object], - default: () => { - } + default: () => {} }, step: Number }, @@ -126,13 +127,11 @@ export default { return this.value.position.zIndex || 1; } }, - mounted() { - }, + mounted() {}, methods: { - handleFocus({index, left, top, width, height}) { - }, - handleBlur({index, left, top, width, height}) { - this.$emit("onActivated", {index, left, top, width, height}); + handleFocus({ index, left, top, width, height }) {}, + handleBlur({ index, left, top, width, height }) { + this.$emit("onActivated", { index, left, top, width, height }); this.$refs.draggable.setActive(true); } } diff --git a/report-ui/src/views/bigscreenDesigner/designer/widget/widgetImage.vue b/report-ui/src/views/bigscreenDesigner/designer/widget/widgetImage.vue index afbd2eab..16794c43 100644 --- a/report-ui/src/views/bigscreenDesigner/designer/widget/widgetImage.vue +++ b/report-ui/src/views/bigscreenDesigner/designer/widget/widgetImage.vue @@ -41,7 +41,8 @@ export default { return { imageAdress: this.transStyle.imageAdress, "border-radius": this.transStyle.borderRadius + "px", - opacity: this.transStyle.transparency / 100 + opacity: this.transStyle.transparency / 100, + animation: this.transStyle.startRotate? "turn "+(101-this.transStyle.rotationSpeed)/10+"s linear infinite":"none" }; } }, @@ -74,21 +75,5 @@ export default { .startImg { animation: turn 1s linear infinite; } -@keyframes turn { - 0% { - -webkit-transform: rotate(0deg); - } - 25% { - -webkit-transform: rotate(90deg); - } - 50% { - -webkit-transform: rotate(180deg); - } - 75% { - -webkit-transform: rotate(270deg); - } - 100% { - -webkit-transform: rotate(360deg); - } -} + diff --git a/report-ui/src/views/datasource/index.vue b/report-ui/src/views/datasource/index.vue index 2cab7f9a..85cdb12b 100644 --- a/report-ui/src/views/datasource/index.vue +++ b/report-ui/src/views/datasource/index.vue @@ -4,28 +4,10 @@ * @Author: qianlishi * @Date: 2021-12-11 14:48:27 * @LastEditors: qianlishi - * @LastEditTime: 2021-12-24 14:00:47 + * @LastEditTime: 2022-03-09 09:43:31 -->