Просмотр исходного кода

Merge branch 'dev' of ssh://10.10.100.21/source/report-parent into dev

guq 7 лет назад
Родитель
Сommit
7b0e266d99
58 измененных файлов с 1436 добавлено и 707 удалено
  1. 2 2
      .gitignore
  2. 10 0
      crystal-replace-view/README.md
  3. BIN
      crystal-replace-view/lib/com/azalea/ufl/barcode/1.0.0/barcode-1.0.0.jar
  4. 7 0
      crystal-replace-view/lib/com/azalea/ufl/barcode/1.0.0/barcode-1.0.0.pom
  5. BIN
      crystal-replace-view/lib/com/businessobjects/foundation/logging/1.0.0/logging-1.0.0.jar
  6. 7 0
      crystal-replace-view/lib/com/businessobjects/foundation/logging/1.0.0/logging-1.0.0.pom
  7. BIN
      crystal-replace-view/lib/com/businessobjects/reports/JDBInterface/1.0.0/JDBInterface-1.0.0.jar
  8. 7 0
      crystal-replace-view/lib/com/businessobjects/reports/JDBInterface/1.0.0/JDBInterface-1.0.0.pom
  9. BIN
      crystal-replace-view/lib/com/crystaldecisions/CrystalCommon2/1.0.0/CrystalCommon2-1.0.0.jar
  10. 7 0
      crystal-replace-view/lib/com/crystaldecisions/CrystalCommon2/1.0.0/CrystalCommon2-1.0.0.pom
  11. BIN
      crystal-replace-view/lib/com/crystaldecisions/CrystalReportsRuntime/1.0.0/CrystalReportsRuntime-1.0.0.jar
  12. 7 0
      crystal-replace-view/lib/com/crystaldecisions/CrystalReportsRuntime/1.0.0/CrystalReportsRuntime-1.0.0.pom
  13. BIN
      crystal-replace-view/lib/com/crystaldecisions/DatabaseConnectors/1.0.0/DatabaseConnectors-1.0.0.jar
  14. 7 0
      crystal-replace-view/lib/com/crystaldecisions/DatabaseConnectors/1.0.0/DatabaseConnectors-1.0.0.pom
  15. BIN
      crystal-replace-view/lib/com/crystaldecisions/keycodeDecoder/1.0.0/keycodeDecoder-1.0.0.jar
  16. 7 0
      crystal-replace-view/lib/com/crystaldecisions/keycodeDecoder/1.0.0/keycodeDecoder-1.0.0.pom
  17. 106 0
      crystal-replace-view/pom.xml
  18. 159 0
      crystal-replace-view/src/main/java/com/uas/report/crystal/CrystalReplaceView.java
  19. 45 0
      crystal-replace-view/src/main/resources/logback.xml
  20. 21 0
      crystal-replace-view/src/test/java/com/uas/report/crystal/CrystalReplaceViewTest.java
  21. BIN
      crystal-replace-view/src/test/resources/out/bjd_price.rpt
  22. BIN
      crystal-replace-view/src/test/resources/rpts/BankLestMoney_E_Shine_DigitGroup.rpt
  23. BIN
      crystal-replace-view/src/test/resources/rpts/PROFITCUSTOMER_E_Shine_DigitGroup.rpt
  24. BIN
      crystal-replace-view/src/test/resources/rpts/PROFITChampion_Eshine_Group.rpt
  25. BIN
      crystal-replace-view/src/test/resources/rpts/PROFITTOTAL_E_Shine_DigitGroup.rpt
  26. BIN
      crystal-replace-view/src/test/resources/rpts/PURCLIST.rpt
  27. BIN
      crystal-replace-view/src/test/resources/rpts/ProductwhSystem_E_Shine_DigitGroup.rpt
  28. BIN
      crystal-replace-view/src/test/resources/rpts/bjd_price.rpt
  29. BIN
      crystal-replace-view/src/test/resources/rpts/overdate_arbill_customer_group.rpt
  30. BIN
      crystal-replace-view/src/test/resources/rpts/overdate_arbill_depart_group.rpt
  31. BIN
      crystal-replace-view/src/test/resources/rpts/profitdetail_eshinegroup.rpt
  32. BIN
      crystal-replace-view/src/test/resources/rpts/profitsyb_sum_e_shine_digitgroup.rpt
  33. 5 12
      crystal2jasper/src/main/java/com/uas/report/crystal2jasper/CrystalToJasper.java
  34. 1 0
      crystal2jasper/src/test/java/com/uas/report/crystal2jasper/CrystalToJasperTest.java
  35. 77 64
      pom.xml
  36. 5 0
      report-common/pom.xml
  37. 2 2
      report-common/src/main/java/com/uas/report/util/FileUtils.java
  38. 365 59
      report-common/src/main/java/com/uas/report/util/ObjectUtils.java
  39. 1 1
      report-common/src/main/java/com/uas/report/util/ResourceUtils.java
  40. 2 2
      report-common/src/main/java/com/uas/report/util/ZipUtils.java
  41. 0 13
      report/pom.xml
  42. 1 1
      report/src/main/java/com/uas/report/Application.java
  43. 336 0
      report/src/main/java/com/uas/report/DynamicProperties.java
  44. 0 127
      report/src/main/java/com/uas/report/SpecialProperties.java
  45. 38 69
      report/src/main/java/com/uas/report/SystemProperties.java
  46. 22 0
      report/src/main/java/com/uas/report/annotation/DynamicValue.java
  47. 5 5
      report/src/main/java/com/uas/report/axis/BasicResourceHandler.java
  48. 5 5
      report/src/main/java/com/uas/report/controller/PrintController.java
  49. 39 0
      report/src/main/java/com/uas/report/controller/PropertiesController.java
  50. 9 1
      report/src/main/java/com/uas/report/service/FileService.java
  51. 43 15
      report/src/main/java/com/uas/report/service/impl/FileServiceImpl.java
  52. 7 7
      report/src/main/java/com/uas/report/service/impl/PrintServiceImpl.java
  53. 43 6
      report/src/main/java/com/uas/report/util/MasterManager.java
  54. 0 302
      report/src/main/java/com/uas/report/util/ObjectUtils.java
  55. 9 1
      report/src/main/java/com/uas/report/util/ReportUtils.java
  56. 8 13
      report/src/main/resources/application.yml
  57. 16 0
      report/src/main/resources/report.properties
  58. 5 0
      report/src/main/webapp/WEB-INF/views/console.html

+ 2 - 2
.gitignore

@@ -24,7 +24,6 @@
 .mtj.tmp/
 
 # Package Files #
-*.jar
 *.war
 *.ear
 *.zip
@@ -52,4 +51,5 @@ buildNumber.properties
 # --------------------
 # Project
 data/
-report/src/main/resources/config/
+report/src/main/resources/config/
+report/report.properties

+ 10 - 0
crystal-replace-view/README.md

@@ -0,0 +1,10 @@
+## 运行方式
+`
+java -jar target/crystal-replace-view-0.0.1.jar src/test/resources/rpts HUASL_
+`
+## 参数说明
+| 选项 | 说明 |
+| - | - |
+| target/crystal-replace-view-0.0.1.jar | 程序主体 | 必填 |
+| src/test/resources/rpts | 水晶报表相对路径(文件或文件夹) | 是 |
+| HUASL_ | 前缀 | 是 |

BIN
crystal-replace-view/lib/com/azalea/ufl/barcode/1.0.0/barcode-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/azalea/ufl/barcode/1.0.0/barcode-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.azalea.ufl</groupId>
+    <artifactId>barcode</artifactId>
+    <version>1.0.0</version>
+</project>

BIN
crystal-replace-view/lib/com/businessobjects/foundation/logging/1.0.0/logging-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/businessobjects/foundation/logging/1.0.0/logging-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.businessobjects.foundation</groupId>
+    <artifactId>logging</artifactId>
+    <version>1.0.0</version>
+</project>

BIN
crystal-replace-view/lib/com/businessobjects/reports/JDBInterface/1.0.0/JDBInterface-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/businessobjects/reports/JDBInterface/1.0.0/JDBInterface-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.businessobjects.reports</groupId>
+    <artifactId>JDBInterface</artifactId>
+    <version>1.0.0</version>
+</project>

BIN
crystal-replace-view/lib/com/crystaldecisions/CrystalCommon2/1.0.0/CrystalCommon2-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/crystaldecisions/CrystalCommon2/1.0.0/CrystalCommon2-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.crystaldecisions</groupId>
+    <artifactId>CrystalCommon2</artifactId>
+    <version>1.0.0</version>
+</project>

BIN
crystal-replace-view/lib/com/crystaldecisions/CrystalReportsRuntime/1.0.0/CrystalReportsRuntime-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/crystaldecisions/CrystalReportsRuntime/1.0.0/CrystalReportsRuntime-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.crystaldecisions</groupId>
+    <artifactId>CrystalReportsRuntime</artifactId>
+    <version>1.0.0</version>
+</project>

BIN
crystal-replace-view/lib/com/crystaldecisions/DatabaseConnectors/1.0.0/DatabaseConnectors-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/crystaldecisions/DatabaseConnectors/1.0.0/DatabaseConnectors-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.crystaldecisions</groupId>
+    <artifactId>DatabaseConnectors</artifactId>
+    <version>1.0.0</version>
+</project>

BIN
crystal-replace-view/lib/com/crystaldecisions/keycodeDecoder/1.0.0/keycodeDecoder-1.0.0.jar


+ 7 - 0
crystal-replace-view/lib/com/crystaldecisions/keycodeDecoder/1.0.0/keycodeDecoder-1.0.0.pom

@@ -0,0 +1,7 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.crystaldecisions</groupId>
+    <artifactId>keycodeDecoder</artifactId>
+    <version>1.0.0</version>
+</project>

+ 106 - 0
crystal-replace-view/pom.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>report-parent</artifactId>
+        <groupId>com.uas.report</groupId>
+        <version>0.0.1</version>
+    </parent>
+    <artifactId>crystal-replace-view</artifactId>
+    <packaging>jar</packaging>
+
+    <repositories>
+        <repository>
+            <id>lib</id>
+            <url>file://${project.basedir}/lib</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.uas.report</groupId>
+            <artifactId>report-common</artifactId>
+        </dependency>
+
+        <!-- crystal reports dependencies -->
+        <dependency>
+            <groupId>com.crystaldecisions</groupId>
+            <artifactId>CrystalReportsRuntime</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.crystaldecisions</groupId>
+            <artifactId>CrystalCommon2</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.crystaldecisions</groupId>
+            <artifactId>DatabaseConnectors</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.businessobjects.foundation</groupId>
+            <artifactId>logging</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.crystaldecisions</groupId>
+            <artifactId>keycodeDecoder</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.businessobjects.reports</groupId>
+            <artifactId>JDBInterface</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>log4j-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+            <version>1.10</version>
+        </dependency>
+        <dependency>
+            <groupId>com.ibm.icu</groupId>
+            <artifactId>icu4j</artifactId>
+            <version>60.2</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.azalea.ufl</groupId>
+            <artifactId>barcode</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.lowagie</groupId>
+            <artifactId>itext</artifactId>
+            <version>2.1.7.js5</version>
+        </dependency>
+        <dependency>
+            <groupId>com.oracle</groupId>
+            <artifactId>ojdbc6</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 159 - 0
crystal-replace-view/src/main/java/com/uas/report/crystal/CrystalReplaceView.java

@@ -0,0 +1,159 @@
+package com.uas.report.crystal;
+
+import com.crystaldecisions.sdk.occa.report.application.DatabaseController;
+import com.crystaldecisions.sdk.occa.report.application.OpenReportOptions;
+import com.crystaldecisions.sdk.occa.report.application.ReportClientDocument;
+import com.crystaldecisions.sdk.occa.report.application.ReportSaveAsOptions;
+import com.crystaldecisions.sdk.occa.report.data.IDatabase;
+import com.crystaldecisions.sdk.occa.report.data.ITable;
+import com.crystaldecisions.sdk.occa.report.data.Tables;
+import com.crystaldecisions.sdk.occa.report.lib.ReportSDKException;
+import com.uas.report.util.ArrayUtils;
+import com.uas.report.util.FileUtils;
+import com.uas.report.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+
+/**
+ * 在报表的视图名称前添加前缀
+ *
+ * @author sunyj
+ * @since 2017/12/22 10:42
+ */
+public class CrystalReplaceView {
+    private static Logger logger = LoggerFactory.getLogger(CrystalReplaceView.class);
+
+    public static void main(String[] args) {
+        int length = args != null ? args.length : 0;
+        if (length < 2) {
+            logger.error("参数缺失\neg. java -jar target/crystal-replace-view-0.0.1.jar src/test/resources/rpts HUASL_");
+            return;
+        }
+        // 命令行接收两个参数:报表路径、需添加的前缀
+        File src = new File(args[0]);
+        String prefix = args[1];
+        try {
+            CrystalReplaceView.replaceReports(src, prefix);
+        } catch (IOException e) {
+            logger.error("替换失败", e);
+        }
+    }
+
+    /**
+     * 替换报表的视图名称
+     *
+     * @param src    报表文件(夹)
+     * @param prefix 需添加的视图前缀
+     * @throws IOException
+     */
+    public static void replaceReports(File src, String prefix) throws IOException {
+        logger.info("start...");
+        if (src == null || StringUtils.isEmpty(prefix)) {
+            throw new IllegalArgumentException("需指定参数 src, prefix");
+        }
+        FileUtils.checkFile(src);
+        if (src.isDirectory()) {
+            replaceReports(src, new File(src, "success"), prefix);
+        } else {
+            replaceReport(src, new File(src.getParentFile(), "success"), prefix);
+        }
+        logger.info("completed...");
+    }
+
+    /**
+     * 替换指定文件夹下报表的视图名称
+     *
+     * @param reportDir  报表文件夹
+     * @param successDir 报表替换成功后,新报表的路径
+     * @param prefix     需添加的视图前缀
+     * @throws IOException
+     */
+    private static void replaceReports(File reportDir, File successDir, String prefix) throws IOException {
+        FileUtils.checkDir(reportDir);
+        File[] files = reportDir.listFiles(new FileFilter() {
+            @Override
+            public boolean accept(File file) {
+                if (file == null || !file.exists()) {
+                    return false;
+                }
+                // success路径下的文件不进行转换
+                String filePath = file.getAbsolutePath();
+                if (filePath.endsWith("success") || filePath.contains("\\success\\")
+                        || filePath.contains("/success/")) {
+                    return false;
+                }
+                // 文件必须是 rpt 格式
+                return !file.isFile() || filePath.endsWith(".rpt");
+            }
+        });
+        if (ArrayUtils.isEmpty(files)) {
+            return;
+        }
+        for (File file : files) {
+            try {
+                String fileName = file.getName();
+                // 是文件夹的话,递归处理
+                if (file.isDirectory()) {
+                    // 不可直接修改 successDir,否则会造成其他文件的路径有问题
+                    File successDirCopy = new File(successDir, fileName);
+                    replaceReports(file, successDirCopy, prefix);
+                } else {
+                    replaceReport(file, successDir, prefix);
+                }
+            } catch (Throwable e) {
+                logger.error(file.getName(), e);
+            }
+        }
+    }
+
+    /**
+     * 替换单个报表的视图名称
+     *
+     * @param reportFile 报表文件
+     * @param successDir 报表替换成功后,新报表的路径
+     * @param prefix     需添加的视图前缀
+     * @throws IOException
+     */
+    private static void replaceReport(File reportFile, File successDir, String prefix) throws IOException {
+        logger.info("replacing... " + reportFile.getName());
+        FileUtils.initDir(successDir);
+        ReportClientDocument client = new ReportClientDocument();
+        try {
+            // 打开报表
+            client.setReportAppServer(ReportClientDocument.inprocConnectionString);
+            client.open(reportFile.getPath(), OpenReportOptions._retrieveNoReportDefinition);
+            DatabaseController databaseController = client.getDatabaseController();
+            IDatabase database = databaseController.getDatabase();
+            Tables tables = database.getTables();
+            for (ITable iTable : tables) {
+                String alias = iTable.getAlias();
+                // 只替换以 '_VIEW' 结尾的视图,不替换表
+                if (alias.toUpperCase().endsWith("_VIEW")) {
+                    logger.info("replacing table: name=" + iTable.getName() + ", alias=" + alias);
+                    // 修改视图名称
+                    databaseController.modifyTableAlias(iTable, prefix + alias);
+                }
+            }
+
+            client.saveAs(reportFile.getName(), successDir.getAbsolutePath(), ReportSaveAsOptions._overwriteExisting);
+            if (!reportFile.delete()) {
+                logger.info("文件删除失败:" + reportFile);
+            }
+            System.out.println();
+        } catch (ReportSDKException e) {
+            logger.error("报表读取失败", e);
+        } catch (IOException e) {
+            logger.error("报表写入失败", e);
+        } finally {
+            try {
+                client.close();
+            } catch (ReportSDKException e) {
+                throw new IllegalStateException("报表关闭失败:", e);
+            }
+        }
+    }
+}

+ 45 - 0
crystal-replace-view/src/main/resources/logback.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <appender name="FILE"
+              class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <File>logs/log.log</File>
+        <encoder>
+            <pattern>
+                %date{yyyy-MM-dd HH:mm:ss:SSS} [%relative ms] %-5level [%50.50(%logger{36}.%method:%line)] ---- %msg%n
+            </pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- daily rollover -->
+            <FileNamePattern>logs/log.%d{yyyy-MM-dd}.log</FileNamePattern>
+            <!-- keep 10 days' worth of history -->
+            <maxHistory>10</maxHistory>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+    </appender>
+
+    <!-- Console output -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
+        <encoder>
+            <pattern>
+                %date{yyyy-MM-dd HH:mm:ss:SSS} [%relative ms] %-5level [%50.50(%logger{36}.%method:%line)] ---- %msg%n
+            </pattern>
+            <charset>UTF-8</charset> <!-- 此处设置字符集 -->
+        </encoder>
+        <!-- Only log level WARN and above -->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+    </appender>
+
+    <!-- Enable FILE and STDOUT appenders for all log messages. By default,
+        only log at level INFO and above. -->
+    <root level="ERROR">
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="STDOUT"/>
+    </root>
+    <logger name="com.uas" level="INFO"/>
+</configuration>

+ 21 - 0
crystal-replace-view/src/test/java/com/uas/report/crystal/CrystalReplaceViewTest.java

@@ -0,0 +1,21 @@
+package com.uas.report.crystal;
+
+import com.uas.report.util.ResourceUtils;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author sunyj
+ * @since 2017/12/29 18:04
+ */
+public class CrystalReplaceViewTest {
+
+    @Test
+    public void test() throws IOException {
+        File src = ResourceUtils.getFile("rpts");
+        String prefix = "YITOA_";
+        CrystalReplaceView.replaceReports(src, prefix);
+    }
+}

BIN
crystal-replace-view/src/test/resources/out/bjd_price.rpt


BIN
crystal-replace-view/src/test/resources/rpts/BankLestMoney_E_Shine_DigitGroup.rpt


BIN
crystal-replace-view/src/test/resources/rpts/PROFITCUSTOMER_E_Shine_DigitGroup.rpt


BIN
crystal-replace-view/src/test/resources/rpts/PROFITChampion_Eshine_Group.rpt


BIN
crystal-replace-view/src/test/resources/rpts/PROFITTOTAL_E_Shine_DigitGroup.rpt


BIN
crystal-replace-view/src/test/resources/rpts/PURCLIST.rpt


BIN
crystal-replace-view/src/test/resources/rpts/ProductwhSystem_E_Shine_DigitGroup.rpt


BIN
crystal-replace-view/src/test/resources/rpts/bjd_price.rpt


BIN
crystal-replace-view/src/test/resources/rpts/overdate_arbill_customer_group.rpt


BIN
crystal-replace-view/src/test/resources/rpts/overdate_arbill_depart_group.rpt


BIN
crystal-replace-view/src/test/resources/rpts/profitdetail_eshinegroup.rpt


BIN
crystal-replace-view/src/test/resources/rpts/profitsyb_sum_e_shine_digitgroup.rpt


+ 5 - 12
crystal2jasper/src/main/java/com/uas/report/crystal2jasper/CrystalToJasper.java

@@ -3,10 +3,7 @@ package com.uas.report.crystal2jasper;
 import com.uas.report.crystal2jasper.Join.JoinType;
 import com.uas.report.crystal2jasper.Link.LinkType;
 import com.uas.report.crystal2jasper.Sort.SortType;
-import com.uas.report.util.CollectionUtils;
-import com.uas.report.util.FileUtils;
-import com.uas.report.util.StringUtils;
-import com.uas.report.util.ZipUtils;
+import com.uas.report.util.*;
 import org.apache.commons.io.output.FileWriterWithEncoding;
 import org.dom4j.*;
 import org.dom4j.io.SAXReader;
@@ -27,20 +24,16 @@ public class CrystalToJasper {
 
 	public static void main(String[] args) throws IOException, DocumentException, TransformerException {
 		int length = args != null ? args.length : 0;
-        File rptZipFile = null;
-        File outDir = null;
+        File rptZipFile;
+        File outDir;
         File styleFile = ResourceUtils.getFile("crystal2jasper.xsl");
         boolean remainHierarchy = false;
         if(length < 2){
             logger.error("参数缺失\neg. java -jar target/crystal2jasper-0.0.1.jar src/test/resources/formatted-rpts src/test/resources/out src/main/resources/crystal2jasper.xsl false");
             return;
         }
-		if (length >= 1) {
-			rptZipFile = new File(args[0]);
-		}
-		if (length >= 2) {
-			outDir = new File(args[1]);
-		}
+        rptZipFile = new File(args[0]);
+        outDir = new File(args[1]);
 		if (length >= 3) {
 			styleFile = new File(args[2]);
 		}

+ 1 - 0
crystal2jasper/src/test/java/com/uas/report/crystal2jasper/CrystalToJasperTest.java

@@ -1,5 +1,6 @@
 package com.uas.report.crystal2jasper;
 
+import com.uas.report.util.ResourceUtils;
 import org.dom4j.DocumentException;
 import org.junit.Test;
 import org.slf4j.Logger;

+ 77 - 64
pom.xml

@@ -1,64 +1,77 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.springframework.boot</groupId>
-        <artifactId>spring-boot-starter-parent</artifactId>
-        <version>1.4.1.RELEASE</version>
-    </parent>
-    <groupId>com.uas.report</groupId>
-    <artifactId>report-parent</artifactId>
-    <packaging>pom</packaging>
-    <version>0.0.1</version>
-
-    <modules>
-        <module>report</module>
-        <module>report-common</module>
-        <module>crystal2jasper</module>
-    </modules>
-
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <report.common.version>0.0.1</report.common.version>
-    </properties>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>com.uas.report</groupId>
-                <artifactId>report-common</artifactId>
-                <version>${report.common.version}</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-    <build>
-        <pluginManagement>
-            <plugins>
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-compiler-plugin</artifactId>
-                    <configuration>
-                        <source>1.7</source>
-                        <target>1.7</target>
-                    </configuration>
-                </plugin>
-            </plugins>
-        </pluginManagement>
-    </build>
-
-    <distributionManagement>
-        <!-- 发布release仓库 -->
-        <repository>
-            <id>platform-release</id>
-            <name>platform-release</name>
-            <url>http://113.105.74.141:8081/artifactory/libs-release-local</url>
-        </repository>
-        <!-- 发布快照版本 -->
-        <snapshotRepository>
-            <id>platform-snapshots</id>
-            <name>platform-snapshots</name>
-            <url>http://113.105.74.141:8081/artifactory/libs-snapshot-local</url>
-        </snapshotRepository>
-    </distributionManagement>
-</project>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>1.4.1.RELEASE</version>
+    </parent>
+    <groupId>com.uas.report</groupId>
+    <artifactId>report-parent</artifactId>
+    <packaging>pom</packaging>
+    <version>0.0.1</version>
+
+    <modules>
+        <module>report-common</module>
+        <module>report</module>
+        <module>crystal2jasper</module>
+        <module>crystal-replace-view</module>
+    </modules>
+
+    <properties>
+        <oracle.jdbc.version>11.2.0</oracle.jdbc.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <report.common.version>0.0.1</report.common.version>
+        <fastjson.version>1.2.15</fastjson.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>${fastjson.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.uas.report</groupId>
+                <artifactId>report-common</artifactId>
+                <version>${report.common.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.oracle</groupId>
+                <artifactId>ojdbc6</artifactId>
+                <version>${oracle.jdbc.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.7</source>
+                        <target>1.7</target>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <distributionManagement>
+        <!-- 发布release仓库 -->
+        <repository>
+            <id>platform-release</id>
+            <name>platform-release</name>
+            <url>http://113.105.74.141:8081/artifactory/libs-release-local</url>
+        </repository>
+        <!-- 发布快照版本 -->
+        <snapshotRepository>
+            <id>platform-snapshots</id>
+            <name>platform-snapshots</name>
+            <url>http://113.105.74.141:8081/artifactory/libs-snapshot-local</url>
+        </snapshotRepository>
+    </distributionManagement>
+</project>

+ 5 - 0
report-common/pom.xml

@@ -14,5 +14,10 @@
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <optional>true</optional>
+        </dependency>
     </dependencies>
 </project>

+ 2 - 2
report-common/src/main/java/com/uas/report/util/FileUtils.java

@@ -54,7 +54,7 @@ public class FileUtils {
 		}
 
 		File file = new File(filePath);
-		if (!file.getParentFile().exists()) {
+		if (file.getParentFile() != null && !file.getParentFile().exists()) {
 			file.getParentFile().mkdirs();
 		}
 		FileOutputStream fos = new FileOutputStream(file);
@@ -74,7 +74,7 @@ public class FileUtils {
 	 * @return 写入的文件
 	 */
 	public static File write(File file, String content) throws IOException {
-		if (!file.getParentFile().exists()) {
+		if (file.getParentFile() != null && !file.getParentFile().exists()) {
 			file.getParentFile().mkdirs();
 		}
 		file.createNewFile();

+ 365 - 59
report-common/src/main/java/com/uas/report/util/ObjectUtils.java

@@ -1,8 +1,13 @@
 package com.uas.report.util;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Map;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.Map.Entry;
 
 /**
  * 对象工具类
@@ -12,75 +17,376 @@ import java.util.Map;
  */
 public class ObjectUtils {
 
+	/**
+	 * 判断是否为 null、空数组、空串或者空集合
+	 *
+	 * @param obj
+	 *            对象
+	 * @return 是否为 null、空数组、空串或者空集合
+	 */
+	public static boolean isEmpty(Object obj) {
+		if (obj == null) {
+			return true;
+		}
+		if (obj.getClass().isArray()) {
+			if (obj instanceof long[]) {
+				return ArrayUtils.isEmpty((long[]) obj);
+			} else if (obj instanceof int[]) {
+				return ArrayUtils.isEmpty((int[]) obj);
+			} else if (obj instanceof short[]) {
+				return ArrayUtils.isEmpty((short[]) obj);
+			} else if (obj instanceof byte[]) {
+				return ArrayUtils.isEmpty((byte[]) obj);
+			} else if (obj instanceof char[]) {
+				return ArrayUtils.isEmpty((char[]) obj);
+			} else if (obj instanceof boolean[]) {
+				return ArrayUtils.isEmpty((boolean[]) obj);
+			} else if (obj instanceof float[]) {
+				return ArrayUtils.isEmpty((float[]) obj);
+			} else if (obj instanceof double[]) {
+				return ArrayUtils.isEmpty((double[]) obj);
+			}
+			return ArrayUtils.isEmpty((Object[]) obj);
+		}
+		return (obj instanceof String && StringUtils.isEmpty(obj))
+				|| (obj instanceof Collection && CollectionUtils.isEmpty((Collection<?>) obj))
+				|| (obj instanceof Map && CollectionUtils.isEmpty((Map<?, ?>) obj));
+	}
+
+	/**
+	 * 将对象转为 String
+	 *
+	 * @param obj
+	 *            对象
+	 * @return 转换的 String
+	 */
+	public static String toString(Object obj) {
+		if (obj == null) {
+			return "";
+		}
+		if (obj instanceof String) {
+			return (String) obj;
+		}
+		if (obj.getClass().isArray()) {
+			if (obj instanceof long[]) {
+				return Arrays.toString((long[]) obj);
+			} else if (obj instanceof int[]) {
+				return Arrays.toString((int[]) obj);
+			} else if (obj instanceof short[]) {
+				return Arrays.toString((short[]) obj);
+			} else if (obj instanceof byte[]) {
+				return Arrays.toString((byte[]) obj);
+			} else if (obj instanceof char[]) {
+				return Arrays.toString((char[]) obj);
+			} else if (obj instanceof boolean[]) {
+				return Arrays.toString((boolean[]) obj);
+			} else if (obj instanceof float[]) {
+				return Arrays.toString((float[]) obj);
+			} else if (obj instanceof double[]) {
+				return Arrays.toString((double[]) obj);
+			}
+			return Arrays.toString((Object[]) obj);
+		}
+		return obj.toString();
+	}
+
+	/**
+	 * 深克隆对象
+	 *
+	 * @param t
+	 *            要克隆的对象
+	 * @return 克隆得到的对象
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T extends Serializable> T clone(T t)
+			throws IOException, ClassNotFoundException, NotSerializableException {
+		if (t == null) {
+			return null;
+		}
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		new ObjectOutputStream(out).writeObject(t);
+		ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
+		return (T) in.readObject();
+	}
+
+	/**
+	 * 深克隆集合对象
+	 *
+	 * @param collection
+	 *            要克隆的对象
+	 * @return 克隆得到的对象
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> Collection<T> clone(Collection<T> collection) throws InstantiationException,
+			IllegalAccessException, NotSerializableException, ClassNotFoundException, IOException {
+		if (collection == null) {
+			return null;
+		}
+		Collection<T> result = collection.getClass().newInstance();
+		for (T t : collection) {
+			if (t instanceof Serializable) {
+				result.add((T) clone((Serializable) t));
+			} else if (t instanceof Collection) {
+				result.add((T) clone((Collection<?>) t));
+			} else if (t instanceof Map) {
+				result.add((T) clone((Map<String, ?>) t));
+			} else {
+				throw new IllegalArgumentException("未实现 Serializable 接口");
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * 深克隆 Map 对象
+	 *
+	 * @param map
+	 *            要克隆的对象
+	 * @return 克隆得到的对象
+	 */
+	@SuppressWarnings("unchecked")
+	public static <T> Map<String, T> clone(Map<String, T> map) throws InstantiationException, IllegalAccessException,
+			NotSerializableException, ClassNotFoundException, IOException {
+		if (map == null) {
+			return null;
+		}
+		Map<String, T> result = map.getClass().newInstance();
+		Set<Entry<String, T>> entrySet = map.entrySet();
+		for (Entry<String, T> entry : entrySet) {
+			T value = entry.getValue();
+			if (value instanceof Serializable) {
+				result.put(entry.getKey(), (T) clone((Serializable) value));
+			} else if (value instanceof Collection) {
+				result.put(entry.getKey(), (T) clone((Collection<?>) value));
+			} else if (value instanceof Map) {
+				result.put(entry.getKey(), (T) clone((Map<String, ?>) value));
+			} else {
+				throw new IllegalArgumentException("未实现 Serializable 接口");
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * JSONArray 转为指定类型的 List
+	 *
+	 * @param array
+	 *            JSONArray
+	 * @param clazz
+	 *            指定的类型
+	 * @param <T>
+	 *            指定的类型
+	 * @return 指定类型的 List
+	 */
+	public static <T> List<T> toList(JSONArray array, Class<T> clazz) {
+		if (array == null) {
+			return null;
+		}
+		List<T> list = new ArrayList<>();
+		for (int i = 0; i < array.size(); i++) {
+			list.add(array.getObject(i, clazz));
+		}
+		return list;
+	}
+
+	/**
+	 * 利用反射获取指定对象的指定字段的值
+	 *
+	 * @param field
+	 *            指定字段
+	 * @param k
+	 *            指定对象
+	 * @return 指定字段的值
+	 */
+	public static <K> Object getValue(Field field, K k) throws IllegalStateException {
+		Object value;
+		try {
+			if (!field.isAccessible()) {
+				field.setAccessible(true);
+				value = field.get(k);
+				field.setAccessible(false);
+			} else {
+				value = field.get(k);
+			}
+		} catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
+			throw new IllegalStateException("通过反射取值失败", e);
+		}
+		return value;
+	}
+
+	/**
+	 * 利用反射递归获取指定对象的指定字段的值
+	 *
+	 * @param field
+	 *            指定字段
+	 * @param k
+	 *            指定对象
+	 * @return 指定字段的值
+	 */
+	public static <K> Object recursivelyGetValue(String field, K k) {
+		return getValue(recursivelyGetField(field, k.getClass()), k);
+	}
+
+	/**
+	 * 递归获取指定类的指定字段(包括父类的私有字段)
+	 *
+	 * @param field
+	 *            指定字段
+	 * @param clazz
+	 *            指定类
+	 * @return 指定字段
+	 * @throws IllegalArgumentException
+	 */
+	public static Field recursivelyGetField(String field, Class<?> clazz) throws IllegalArgumentException {
+		try {
+			return clazz.getDeclaredField(field);
+		} catch (NoSuchFieldException e) {
+			Class<?> superclass = clazz.getSuperclass();
+			if (superclass != null) {
+				return recursivelyGetField(field, superclass);
+			}
+			throw new IllegalArgumentException(clazz + "中不存在字段:" + field);
+		}
+	}
+
+	/**
+	 * 获取指定类的指定字段
+	 *
+	 * @param field
+	 *            指定字段
+	 * @param clazz
+	 *            指定类
+	 * @return 指定字段
+	 * @throws IllegalArgumentException
+	 */
+	public static Field getField(String field, Class<?> clazz) throws IllegalArgumentException {
+		try {
+			return clazz.getDeclaredField(field);
+		} catch (NoSuchFieldException e) {
+			throw new IllegalArgumentException(clazz + "中不存在字段:" + field);
+		}
+	}
+
+	/**
+	 * 递归获取父类(包括私有字段)的所有字段
+	 *
+	 * @param clazz
+	 *            指定类
+	 * @return 所有字段
+	 */
+	public static List<Field> recursivelyGetParentField(Class<?> clazz) {
+		List<Field> fields = new ArrayList<>();
+		Class<?> superclass = clazz.getSuperclass();
+		if (superclass != null) {
+			fields.addAll(recursivelyGetField(superclass));
+		}
+		return fields;
+	}
+
+	/**
+	 * 递归获取所有字段(包括父类的私有字段)
+	 *
+	 * @param clazz
+	 *            指定类
+	 * @return 所有字段
+	 */
+	private static List<Field> recursivelyGetField(Class<?> clazz) {
+		List<Field> fields = new ArrayList<>();
+		Field[] declaredFields = clazz.getDeclaredFields();
+		if (!ArrayUtils.isEmpty(declaredFields)) {
+			fields.addAll(Arrays.asList(declaredFields));
+		}
+		Class<?> superclass = clazz.getSuperclass();
+		if (superclass != null) {
+			fields.addAll(recursivelyGetField(superclass));
+		}
+		return fields;
+	}
+
     /**
-     * 判断是否为 null、空数组、空串或者空集合
+     * 通过反射为指定的字段赋值
      *
-     * @param obj 对象
-     * @return 是否为 null、空数组、空串或者空集合
+     * @param object 对象
+     * @param field  字段
+     * @param value  值
+     * @throws IllegalAccessException
      */
-    public static boolean isEmpty(Object obj) {
-        if (obj == null) {
-            return true;
+    public static void setValue(Object object, Field field, Object value) throws IllegalAccessException {
+        if (object == null || field == null) {
+            throw new IllegalArgumentException("object 或 field 为空");
         }
-        if (obj.getClass().isArray()) {
-            if (obj instanceof long[]) {
-                return ArrayUtils.isEmpty((long[]) obj);
-            } else if (obj instanceof int[]) {
-                return ArrayUtils.isEmpty((int[]) obj);
-            } else if (obj instanceof short[]) {
-                return ArrayUtils.isEmpty((short[]) obj);
-            } else if (obj instanceof byte[]) {
-                return ArrayUtils.isEmpty((byte[]) obj);
-            } else if (obj instanceof char[]) {
-                return ArrayUtils.isEmpty((char[]) obj);
-            } else if (obj instanceof boolean[]) {
-                return ArrayUtils.isEmpty((boolean[]) obj);
-            } else if (obj instanceof float[]) {
-                return ArrayUtils.isEmpty((float[]) obj);
-            } else if (obj instanceof double[]) {
-                return ArrayUtils.isEmpty((double[]) obj);
+        if (value != null) {
+            Type type = field.getGenericType();
+            String typeString = type.toString();
+            if (typeString.equals("class java.lang.String")) {
+            } else if (typeString.equals("class java.lang.Long") || typeString.equals("long")) {
+                value = Long.valueOf(value.toString());
+            } else if (typeString.equals("class java.lang.Integer") || typeString.equals("int")) {
+                value = Integer.valueOf(value.toString());
+            } else if (typeString.equals("class java.lang.Short") || typeString.equals("short")) {
+                value = Short.valueOf(value.toString());
+            } else if (typeString.equals("class java.lang.Double") || typeString.equals("double")) {
+                value = Double.valueOf(value.toString());
+            } else if (typeString.equals("class java.lang.Float") || typeString.equals("float")) {
+                value = Float.valueOf(value.toString());
+            } else if (typeString.equals("class java.lang.Byte") || typeString.equals("byte")) {
+                value = Byte.valueOf(value.toString());
+            } else if (typeString.equals("class java.lang.Boolean") || typeString.equals("boolean")) {
+                value = Boolean.valueOf(value.toString());
+            } else if (typeString.equals("class com.alibaba.fastjson.JSONObject")) {
+                value = JSONObject.parseObject(value.toString());
+            } else if (typeString.startsWith("java.util.List")) {
+                Class<?> clazz = Object.class;
+                if (typeString.matches("^java.util.List<[\\s\\S]+?>$")) {
+                    try {
+                        clazz = Class.forName(typeString.substring(typeString.indexOf("<") + 1, typeString.length() - 1));
+                    } catch (ClassNotFoundException e) {
+                        throw new IllegalStateException("无法转换为 " + typeString, e);
+                    }
+                }
+                String[] strs = value.toString().split(",[ ]*");
+                value = castList(strs, clazz);
+            } else {
+                throw new IllegalArgumentException("不支持的类型:type=" + type + ", value=" + value);
             }
-            return ArrayUtils.isEmpty((Object[]) obj);
         }
-        return (obj instanceof String && StringUtils.isEmpty(obj))
-                || (obj instanceof Collection && CollectionUtils.isEmpty((Collection<?>) obj))
-                || (obj instanceof Map && CollectionUtils.isEmpty((Map<?, ?>) obj));
+        if (!field.isAccessible()) {
+            field.setAccessible(true);
+            field.set(object, value);
+            field.setAccessible(false);
+        } else {
+            field.set(object, value);
+        }
     }
 
     /**
-     * 将对象转为 String
+     * 转换 String 数组为指定对象列表
      *
-     * @param obj 对象
-     * @return 转换的 String
+     * @param strs  String 数组
+     * @param clazz 目标类型
+     * @param <T>   目标类型
+     * @return 对象列表
      */
-    public static String toString(Object obj) {
-        if (obj == null) {
-            return "";
-        }
-        if (obj instanceof String) {
-            return (String) obj;
+    private static <T> List<T> castList(String[] strs, Class<T> clazz) {
+        if (ArrayUtils.isEmpty(strs)) {
+            return null;
         }
-        if (obj.getClass().isArray()) {
-            if (obj instanceof long[]) {
-                return Arrays.toString((long[]) obj);
-            } else if (obj instanceof int[]) {
-                return Arrays.toString((int[]) obj);
-            } else if (obj instanceof short[]) {
-                return Arrays.toString((short[]) obj);
-            } else if (obj instanceof byte[]) {
-                return Arrays.toString((byte[]) obj);
-            } else if (obj instanceof char[]) {
-                return Arrays.toString((char[]) obj);
-            } else if (obj instanceof boolean[]) {
-                return Arrays.toString((boolean[]) obj);
-            } else if (obj instanceof float[]) {
-                return Arrays.toString((float[]) obj);
-            } else if (obj instanceof double[]) {
-                return Arrays.toString((double[]) obj);
-            }
-            return Arrays.toString((Object[]) obj);
+        List<T> list = new ArrayList<>();
+        for (String str : strs) {
+            list.add(cast(str, clazz));
         }
-        return obj.toString();
+        return list;
     }
 
+    /**
+     * 转换对象为指定类型
+     *
+     * @param object 对象
+     * @param clazz  目标类型
+     * @param <T>    目标类型
+     * @return 目标
+     */
+    private static <T> T cast(Object object, Class<T> clazz) {
+        return object == null ? null : (T) object;
+    }
 }

+ 1 - 1
crystal2jasper/src/main/java/com/uas/report/crystal2jasper/ResourceUtils.java → report-common/src/main/java/com/uas/report/util/ResourceUtils.java

@@ -1,4 +1,4 @@
-package com.uas.report.crystal2jasper;
+package com.uas.report.util;
 
 import java.io.File;
 import java.io.FileNotFoundException;

+ 2 - 2
report-common/src/main/java/com/uas/report/util/ZipUtils.java

@@ -146,7 +146,7 @@ public class ZipUtils {
 				}
 				continue;
 			}
-			if (!outFile.getParentFile().exists()) {
+			if (outFile.getParentFile() != null && !outFile.getParentFile().exists()) {
 				outFile.getParentFile().mkdirs();
 			}
 
@@ -199,7 +199,7 @@ public class ZipUtils {
 			}
 			InputStream inputStream = zipFile.getInputStream(zipEntry);
 			File outFile = new File(folder.getPath() + File.separator + zipEntry.getName());
-			if (!outFile.getParentFile().exists()) {
+			if (outFile.getParentFile() != null && !outFile.getParentFile().exists()) {
 				outFile.getParentFile().mkdirs();
 			}
 

+ 0 - 13
report/pom.xml

@@ -10,13 +10,11 @@
 	<packaging>war</packaging>
 
 	<properties>
-		<oracle.jdbc.version>11.2.0</oracle.jdbc.version>
 		<druid.version>1.0.24</druid.version>
 		<jasperreports.version>6.4.0</jasperreports.version>
 		<jasperreports-functions.version>6.3.0</jasperreports-functions.version>
 		<poi.version>3.10.1</poi.version>
 		<commons.fileupload.version>1.3.2</commons.fileupload.version>
-		<fastjson.version>1.2.15</fastjson.version>
 		<axis.version>1.4</axis.version>
 		<commons.discovery.version>0.2</commons.discovery.version>
 		<javax.activation.version>1.1.1</javax.activation.version>
@@ -80,12 +78,10 @@
 		<dependency>
 			<groupId>com.alibaba</groupId>
 			<artifactId>fastjson</artifactId>
-			<version>${fastjson.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>com.oracle</groupId>
 			<artifactId>ojdbc6</artifactId>
-			<version>${oracle.jdbc.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>mysql</groupId>
@@ -322,15 +318,6 @@
 					</items>
 				</configuration>
 			</plugin>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-compiler-plugin</artifactId>
-				<configuration>
-					<encoding>${project.build.sourceEncoding}</encoding>
-					<source>1.7</source>
-					<target>1.7</target>
-				</configuration>
-			</plugin>
 			<plugin>
 				<artifactId>maven-resources-plugin</artifactId>
 				<executions>

+ 1 - 1
report/src/main/java/com/uas/report/Application.java

@@ -41,7 +41,7 @@ public class Application extends SpringBootServletInitializer implements Applica
 	protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
 		try {
 			File logFile = new File("logs/log.log");
-			if (!logFile.getParentFile().exists()) {
+			if (logFile.getParentFile() != null && !logFile.getParentFile().exists()) {
 				logFile.getParentFile().mkdir();
 			}
 			System.setErr(new PrintStream(new FileOutputStream(logFile, true)));

+ 336 - 0
report/src/main/java/com/uas/report/DynamicProperties.java

@@ -0,0 +1,336 @@
+package com.uas.report;
+
+import com.alibaba.fastjson.JSONObject;
+import com.uas.report.annotation.DynamicValue;
+import com.uas.report.model.ExportType;
+import com.uas.report.util.FileUtils;
+import com.uas.report.util.ObjectUtils;
+import com.uas.report.util.ResourceUtils;
+import com.uas.report.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * 不同环境下的配置信息(来自自定义的配置文件)
+ *
+ * @author sunyj
+ * @since 2017年1月10日 下午3:32:30
+ */
+@Component
+public class DynamicProperties {
+
+    /**
+     * 配置文件名称
+     */
+    private static final String PROPERTY_FILE_NAME = "report.properties";
+
+    /**
+     * 记录配置文件最后修改时间
+     */
+    private long propertyFileLastModified;
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    /**
+	 * 本地资源根路径
+	 */
+	@DynamicValue("localBaseDir")
+	private String localBaseDir;
+
+	/**
+	 * 本地资源图片路径
+	 */
+	@DynamicValue("localImagesDir")
+	private String localImagesDir;
+
+	/**
+	 * 本地资源jrxml模板路径
+	 */
+	@DynamicValue("localJrxmlDir")
+	private String localJrxmlDir;
+
+	/**
+	 * 标准账套(存放标准模板)
+	 */
+	@DynamicValue("standardMaster")
+	private String standardMaster;
+
+	/**
+	 * 本地是否拥有标准模板
+	 */
+	@DynamicValue("hasStandardJrxmls")
+	private Boolean hasStandardJrxmls;
+
+	/**
+	 * 标准模板地址
+	 */
+	@DynamicValue("standardJrxmlsUrl")
+	private String standardJrxmlsUrl;
+
+	/**
+	 * 主账套与其子帐套是否共用模板
+	 */
+	@DynamicValue("shareJrxmlsWithSubMaster")
+	private Boolean shareJrxmlsWithSubMaster;
+
+	/**
+	 * 数据源配置信息
+	 */
+	@DynamicValue("datasource")
+	private JSONObject dataSourceInformation;
+
+    /**
+     * PC端支持打印的最大记录行数
+     */
+    @DynamicValue("max-record-size.pc")
+    private int maxRecordSizePc;
+
+    /**
+     * 手机端支持打印的最大记录行数
+     */
+    @DynamicValue("max-record-size.phone")
+    private int maxRecordSizePhone;
+
+    /**
+     * 是否使用 xlsx
+     */
+    @DynamicValue("use-xlsx")
+    private Boolean useXlsx;
+
+    /**
+     * 预览页面所显示的导出按钮
+     */
+    @DynamicValue("page.preview.show-export-buttons")
+    private List<ExportType> pagePreviewShowExportButtons;
+
+    public DynamicProperties() {
+        mayLoad();
+    }
+
+    /**
+     * 加载配置
+     */
+    private void mayLoad() {
+        try {
+            File propertyFile = new File(PROPERTY_FILE_NAME);
+            if (!propertyFile.exists()) {
+                // 配置文件不存在时,加载旧的配置文件(以兼容旧的配置方式)
+                // TODO temporary codes
+                File oldPropertyFile = new File("application.properties");
+                if (oldPropertyFile.exists() && oldPropertyFile.isFile()) {
+                    FileUtils.copy(oldPropertyFile, propertyFile);
+                } else {
+                    // 复制类路径下默认的配置文件
+                    File defaultPropertyFile = ResourceUtils.getFile(PROPERTY_FILE_NAME);
+                    if (defaultPropertyFile == null) {
+                        throw new FileNotFoundException("配置文件不存在:" + PROPERTY_FILE_NAME);
+                    }
+                    // 是否以 spring boot jar 形式运行
+                    URL location = getClass().getProtectionDomain().getCodeSource().getLocation();
+                    if (org.springframework.util.ResourceUtils.isJarURL(location)) {
+                        // 以 spring boot jar 形式运行时,不能直接复制默认配置文件,需以输入流的方式读取默认配置
+                        InputStream inputStream = getClass().getResourceAsStream("/" + PROPERTY_FILE_NAME);
+                        byte[] data = new byte[inputStream.available()];
+                        inputStream.read(data);
+                        FileUtils.write(propertyFile.getPath(), data);
+                    }else{
+                        FileUtils.copy(defaultPropertyFile, propertyFile);
+                    }
+                }
+            }
+            if(propertyFile.isDirectory()){
+                throw new IOException("并非文件:" + PROPERTY_FILE_NAME);
+            }
+            long lastModified = propertyFile.lastModified();
+            // 如果配置文件有修改,就重新加载
+            if (propertyFileLastModified >= lastModified) {
+                return;
+            }
+
+            logger.info("加载配置 " + PROPERTY_FILE_NAME + "...");
+            propertyFileLastModified = propertyFile.lastModified();
+            Properties properties = new Properties();
+            properties.load(new FileInputStream(propertyFile));
+
+            // TODO temporary codes
+            if(!properties.containsKey("max-record-size.pc")){
+                properties.put("max-record-size.pc", "100000");
+                properties.store(new FileOutputStream(propertyFile), "report properties updated on");
+            }
+            if(!properties.containsKey("max-record-size.phone")){
+                properties.put("max-record-size.phone", "10000");
+                properties.store(new FileOutputStream(propertyFile), "report properties updated on");
+            }
+            if(!properties.containsKey("use-xlsx")){
+                properties.put("use-xlsx", "false");
+                properties.store(new FileOutputStream(propertyFile), "report properties updated on");
+            }
+            if(!properties.containsKey("page.preview.show-export-buttons")){
+                properties.put("page.preview.show-export-buttons", "PDF, XLS, XLS_DATA");
+                properties.store(new FileOutputStream(propertyFile), "report properties updated on");
+            }
+
+            // 通过反射注入配置
+            Field[] declaredFields = getClass().getDeclaredFields();
+            for (Field declaredField : declaredFields) {
+                DynamicValue dynamicValue = declaredField.getAnnotation(DynamicValue.class);
+                if (dynamicValue == null) {
+                    continue;
+                }
+                String expression = dynamicValue.value();
+                if (StringUtils.isEmpty(expression)) {
+                    throw new IllegalStateException("配置项的表达式为空:" + declaredField.getName() + "=" + expression);
+                }
+                Object value = properties.get(expression);
+                if (ObjectUtils.isEmpty(value)) {
+                    throw new IllegalArgumentException("配置项的值为空:" + expression + "=" + value);
+                }
+                try {
+                    ObjectUtils.setValue(this, declaredField, value);
+                } catch (IllegalAccessException e) {
+                    throw new IllegalStateException("通过反射注入配置失败", e);
+                }
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("配置加载失败", e);
+        }
+    }
+
+    /**
+     * 更新配置
+     *
+     * @param json json 格式的配置
+     */
+    public void update(String json) {
+        if (StringUtils.isEmpty(json)) {
+            throw new IllegalArgumentException("json 为空");
+        }
+        JSONObject jsonObject = JSONObject.parseObject(json);
+        // 更新 json 中指定的配置项
+        Set<Map.Entry<String, Object>> entrySet = jsonObject.entrySet();
+        for (Map.Entry<String, Object> entry : entrySet) {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            if (ObjectUtils.isEmpty(value)) {
+                throw new IllegalArgumentException("参数值为空:" + entry);
+            }
+            Field field;
+            try {
+                field = getClass().getDeclaredField(key);
+                ObjectUtils.setValue(this, field, value);
+            } catch (NoSuchFieldException e) {
+                throw new IllegalArgumentException("不支持配置项:" + key, e);
+            } catch (IllegalAccessException e) {
+                throw new IllegalStateException("通过反射修改配置失败", e);
+            }
+        }
+        save();
+    }
+
+    /**
+     * 保存配置
+     */
+    private void save() {
+        try {
+            Properties properties = new Properties();
+            Field[] declaredFields = getClass().getDeclaredFields();
+            // 通过反射读取所有配置,写入本地文件
+            for (Field declaredField : declaredFields) {
+                DynamicValue dynamicValue = declaredField.getAnnotation(DynamicValue.class);
+                if (dynamicValue == null) {
+                    continue;
+                }
+                String expression = dynamicValue.value();
+                if (StringUtils.isEmpty(expression)) {
+                    throw new IllegalStateException("配置项的表达式为空:" + declaredField.getName() + "=" + expression);
+                }
+                Object value = ObjectUtils.getValue(declaredField, this);
+                if (ObjectUtils.isEmpty(value)) {
+                    throw new IllegalArgumentException("配置项的值为空:" + expression + "=" + value);
+                }
+                // List 直接 toString,结果中会带 '[]'
+                if (value instanceof List) {
+                    List list = (List) value;
+                    StringBuilder builder = new StringBuilder();
+                    for (int i = 0; i < list.size(); i++) {
+                        if (i != 0) {
+                            builder.append(", ");
+                        }
+                        builder.append(list.get(i));
+                    }
+                    properties.setProperty(expression, builder.toString());
+                } else {
+                    properties.setProperty(expression, value.toString());
+                }
+            }
+            properties.store(new FileOutputStream(new File(PROPERTY_FILE_NAME)), "report properties updated on");
+        } catch (IOException e) {
+            throw new IllegalStateException("配置保存失败", e);
+        }
+    }
+
+    public String getLocalBaseDir() {
+        mayLoad();
+		return localBaseDir;
+	}
+
+	public String getLocalImagesDir() {
+        mayLoad();
+		return localImagesDir;
+	}
+
+	public String getLocalJrxmlDir() {
+        mayLoad();
+		return localJrxmlDir;
+	}
+
+	public String getStandardMaster() {
+        mayLoad();
+		return standardMaster;
+	}
+
+	public Boolean getHasStandardJrxmls() {
+        mayLoad();
+		return hasStandardJrxmls;
+	}
+
+	public String getStandardJrxmlsUrl() {
+        mayLoad();
+		return standardJrxmlsUrl;
+	}
+
+	public Boolean getShareJrxmlsWithSubMaster() {
+        mayLoad();
+		return shareJrxmlsWithSubMaster;
+	}
+
+	public JSONObject getDataSourceInformation() {
+        mayLoad();
+		return dataSourceInformation;
+	}
+
+    public int getMaxRecordSizePc() {
+        return maxRecordSizePc;
+    }
+
+    public int getMaxRecordSizePhone() {
+        return maxRecordSizePhone;
+    }
+
+    public Boolean getUseXlsx() {
+        return useXlsx;
+    }
+
+    public List<ExportType> getPagePreviewShowExportButtons() {
+        return pagePreviewShowExportButtons;
+    }
+}

+ 0 - 127
report/src/main/java/com/uas/report/SpecialProperties.java

@@ -1,127 +0,0 @@
-package com.uas.report;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * 不同环境下的配置信息(来自spring cloud)
- * 
- * @author sunyj
- * @since 2017年1月10日 下午3:32:30
- */
-@Configuration
-public class SpecialProperties {
-
-	/**
-	 * 本地资源根路径
-	 */
-	@Value("${localBaseDir}")
-	private String localBaseDir;
-
-	/**
-	 * 本地资源图片路径
-	 */
-	@Value("${localImagesDir}")
-	private String localImagesDir;
-
-	/**
-	 * 本地资源jrxml模板路径
-	 */
-	@Value("${localJrxmlDir}")
-	private String localJrxmlDir;
-
-	/**
-	 * 标准账套(存放标准模板)
-	 */
-	@Value("${standardMaster}")
-	private String standardMaster;
-
-	/**
-	 * 本地是否拥有标准模板
-	 */
-	@Value("${hasStandardJrxmls}")
-	private boolean hasStandardJrxmls;
-
-	/**
-	 * 标准模板地址
-	 */
-	@Value("${standardJrxmlsUrl}")
-	private String standardJrxmlsUrl;
-
-	/**
-	 * 主账套与其子帐套是否共用模板
-	 */
-	@Value("${shareJrxmlsWithSubMaster}")
-	private boolean shareJrxmlsWithSubMaster;
-
-	/**
-	 * 数据源配置信息
-	 */
-	@Value("${datasource}")
-	private String dataSourceInformation;
-
-	public String getLocalBaseDir() {
-		return localBaseDir;
-	}
-
-	public void setLocalBaseDir(String localBaseDir) {
-		this.localBaseDir = localBaseDir;
-	}
-
-	public String getLocalImagesDir() {
-		return localImagesDir;
-	}
-
-	public void setLocalImagesDir(String localImagesDir) {
-		this.localImagesDir = localImagesDir;
-	}
-
-	public String getLocalJrxmlDir() {
-		return localJrxmlDir;
-	}
-
-	public void setLocalJrxmlDir(String localJrxmlDir) {
-		this.localJrxmlDir = localJrxmlDir;
-	}
-
-	public String getStandardMaster() {
-		return standardMaster;
-	}
-
-	public void setStandardMaster(String standardMaster) {
-		this.standardMaster = standardMaster;
-	}
-
-	public boolean hasStandardJrxmls() {
-		return hasStandardJrxmls;
-	}
-
-	public void setHasStandardJrxmls(boolean hasStandardJrxmls) {
-		this.hasStandardJrxmls = hasStandardJrxmls;
-	}
-
-	public String getStandardJrxmlsUrl() {
-		return standardJrxmlsUrl;
-	}
-
-	public void setStandardJrxmlsUrl(String standardJrxmlsUrl) {
-		this.standardJrxmlsUrl = standardJrxmlsUrl;
-	}
-
-	public boolean shareJrxmlsWithSubMaster() {
-		return shareJrxmlsWithSubMaster;
-	}
-
-	public void setShareJrxmlsWithSubMaster(boolean shareJrxmlsWithSubMaster) {
-		this.shareJrxmlsWithSubMaster = shareJrxmlsWithSubMaster;
-	}
-
-	public String getDataSourceInformation() {
-		return dataSourceInformation;
-	}
-
-	public void setDataSourceInformation(String dataSourceInformation) {
-		this.dataSourceInformation = dataSourceInformation;
-	}
-
-}

+ 38 - 69
report/src/main/java/com/uas/report/SystemProperties.java

@@ -1,11 +1,8 @@
 package com.uas.report;
 
-import com.uas.report.model.ExportType;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 
-import java.util.List;
-
 /**
  * 系统参数
  * 
@@ -16,10 +13,16 @@ import java.util.List;
 public class SystemProperties {
 
 	/**
-	 * 定时任务间隔时间(毫秒)
+	 * 定时删除临时文件的任务间隔时间(毫秒)
 	 */
-	@Value("${schedule.period}")
-	private int taskPeriod;
+	@Value("${schedule.period.delete-generated-files}")
+	private int deleteGeneratedFilesPeriod;
+
+    /**
+     * 定时备份报表模版的任务间隔时间(毫秒)
+     */
+    @Value("${schedule.period.backup}")
+    private int backupPeriod;
 
 	/**
 	 * 定时任务是否自动开启
@@ -27,82 +30,40 @@ public class SystemProperties {
 	@Value("${schedule.auto-start}")
 	private boolean taskAutoStart;
 
-	/**
-	 * PC端支持打印的最大记录行数
-	 */
-	@Value("${max-record-size.pc}")
-	private int maxRecordSizePc;
-
-	/**
-	 * 手机端支持打印的最大记录行数
-	 */
-	@Value("${max-record-size.phone}")
-	private int maxRecordSizePhone;
-
-	/**
-	 * 是否使用 xlsx
-	 */
-	@Value("${use-xlsx}")
-	private boolean useXlsx;
-
-	/**
-	 * 预览页面所显示的导出按钮
-	 */
-	@Value("${page.preview.show-export-buttons}")
-	private List<ExportType> pagePreviewShowExportButtons;
-
 	/**
 	 * 以 spring boot jar 方式启动时,需解压的 jar
 	 */
 	@Value("${extract-jars}")
 	private String[] extractJars;
 
-	public int getTaskPeriod() {
-		return taskPeriod;
-	}
+    /**
+     * 数据源的默认配置
+     */
+    @Value("${datasource-default-config}")
+	private String dataSourceDefaultConfig;
 
-	public void setTaskPeriod(int taskPeriod) {
-		this.taskPeriod = taskPeriod;
-	}
+    public int getDeleteGeneratedFilesPeriod() {
+        return deleteGeneratedFilesPeriod;
+    }
 
-	public boolean isTaskAutoStart() {
-		return taskAutoStart;
-	}
+    public void setDeleteGeneratedFilesPeriod(int deleteGeneratedFilesPeriod) {
+        this.deleteGeneratedFilesPeriod = deleteGeneratedFilesPeriod;
+    }
 
-	public void setTaskAutoStart(boolean taskAutoStart) {
-		this.taskAutoStart = taskAutoStart;
-	}
-
-	public int getMaxRecordSizePc() {
-		return maxRecordSizePc;
-	}
-
-	public void setMaxRecordSizePc(int maxRecordSizePc) {
-		this.maxRecordSizePc = maxRecordSizePc;
-	}
-
-	public int getMaxRecordSizePhone() {
-		return maxRecordSizePhone;
-	}
-
-	public void setMaxRecordSizePhone(int maxRecordSizePhone) {
-		this.maxRecordSizePhone = maxRecordSizePhone;
-	}
+    public int getBackupPeriod() {
+        return backupPeriod;
+    }
 
-	public boolean isUseXlsx() {
-		return useXlsx;
-	}
+    public void setBackupPeriod(int backupPeriod) {
+        this.backupPeriod = backupPeriod;
+    }
 
-	public void setUseXlsx(boolean useXlsx) {
-		this.useXlsx = useXlsx;
-	}
-
-	public List<ExportType> getPagePreviewShowExportButtons() {
-		return pagePreviewShowExportButtons;
+    public boolean isTaskAutoStart() {
+		return taskAutoStart;
 	}
 
-	public void setPagePreviewShowExportButtons(List<ExportType> pagePreviewShowExportButtons) {
-		this.pagePreviewShowExportButtons = pagePreviewShowExportButtons;
+	public void setTaskAutoStart(boolean taskAutoStart) {
+		this.taskAutoStart = taskAutoStart;
 	}
 
 	public String[] getExtractJars() {
@@ -112,4 +73,12 @@ public class SystemProperties {
 	public void setExtractJars(String[] extractJars) {
 		this.extractJars = extractJars;
 	}
+
+    public String getDataSourceDefaultConfig() {
+        return dataSourceDefaultConfig;
+    }
+
+    public void setDataSourceDefaultConfig(String dataSourceDefaultConfig) {
+        this.dataSourceDefaultConfig = dataSourceDefaultConfig;
+    }
 }

+ 22 - 0
report/src/main/java/com/uas/report/annotation/DynamicValue.java

@@ -0,0 +1,22 @@
+package com.uas.report.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation at the field level that indicates a default value expression for the affected argument.
+ *
+ * @author sunyj
+ * @since 2017/12/20 9:16
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DynamicValue {
+
+    /**
+     * The actual value expression: e.g. "#{systemProperties.myProp}".
+     */
+    String value() default "";
+}

+ 5 - 5
report/src/main/java/com/uas/report/axis/BasicResourceHandler.java

@@ -12,7 +12,7 @@ import java.util.Map;
 
 import javax.activation.DataSource;
 
-import com.uas.report.SpecialProperties;
+import com.uas.report.DynamicProperties;
 import com.uas.report.util.CollectionUtils;
 import com.uas.report.util.ContextUtils;
 import com.uas.report.util.FileUtils;
@@ -20,7 +20,7 @@ import com.uas.report.util.StringUtils;
 
 public class BasicResourceHandler implements ResourceHandler {
 
-	private SpecialProperties specialProperties = ContextUtils.getBean(SpecialProperties.class);
+	private DynamicProperties dynamicProperties = ContextUtils.getBean(DynamicProperties.class);
 
 	private FileFilter filter = new FileFilter() {
 		@Override
@@ -39,7 +39,7 @@ public class BasicResourceHandler implements ResourceHandler {
 	};
 
 	public List<Resource> listResource(String resourceURI) throws FileNotFoundException, IOException {
-		File dir = new File(specialProperties.getLocalBaseDir() + Folder.SEPARATOR + resourceURI);
+		File dir = new File(dynamicProperties.getLocalBaseDir() + Folder.SEPARATOR + resourceURI);
 		if (!dir.exists() || !dir.isDirectory() || !filter.accept(dir)) {
 			return null;
 		}
@@ -107,12 +107,12 @@ public class BasicResourceHandler implements ResourceHandler {
 	}
 
 	protected File getFile(String resourceURI) {
-		return new File(specialProperties.getLocalBaseDir() + "/" + resourceURI);
+		return new File(dynamicProperties.getLocalBaseDir() + "/" + resourceURI);
 	}
 
 	private String getResourceURI(File file) {
 		String resourceURI = file.getPath();
-		resourceURI = resourceURI.replace(new File(specialProperties.getLocalBaseDir()).getPath(), "");
+		resourceURI = resourceURI.replace(new File(dynamicProperties.getLocalBaseDir()).getPath(), "");
 		resourceURI = resourceURI.replace("\\", Folder.SEPARATOR);
 		if (!resourceURI.startsWith(Folder.SEPARATOR)) {
 			resourceURI = Folder.SEPARATOR + resourceURI;

+ 5 - 5
report/src/main/java/com/uas/report/controller/PrintController.java

@@ -1,6 +1,6 @@
 package com.uas.report.controller;
 
-import com.uas.report.SystemProperties;
+import com.uas.report.DynamicProperties;
 import com.uas.report.model.ExportType;
 import com.uas.report.model.Platform;
 import com.uas.report.model.PrintParameter;
@@ -46,8 +46,8 @@ public class PrintController {
 
     private Logger logger = LoggerFactory.getLogger(getClass());
 
-	@Autowired
-	private SystemProperties systemProperties;
+    @Autowired
+    private DynamicProperties dynamicProperties;
 
 	@Autowired
 	private PrintService printService;
@@ -120,7 +120,7 @@ public class PrintController {
                 break;
             // 该下载接口供 UAS 系统使用,应其要求,printType 为{@link PrintType.EXCEL}时,下载纯数据的 excel
             case EXCEL:
-                if (systemProperties.isUseXlsx()) {
+                if (dynamicProperties.getUseXlsx()) {
                     export(requestId, ExportType.XLSX_DATA.name(), true, request, response);
                 } else {
                     export(requestId, ExportType.XLS_DATA.name(), true, request, response);
@@ -311,6 +311,6 @@ public class PrintController {
 	@RequestMapping(value = "/exportButtons")
 	@ResponseBody
 	public List<ExportType> getPagePreviewShowExportButtons(HttpServletRequest request, HttpServletResponse response) {
-		return systemProperties.getPagePreviewShowExportButtons();
+		return dynamicProperties.getPagePreviewShowExportButtons();
 	}
 }

+ 39 - 0
report/src/main/java/com/uas/report/controller/PropertiesController.java

@@ -0,0 +1,39 @@
+package com.uas.report.controller;
+
+import com.uas.report.DynamicProperties;
+import com.uas.report.util.MasterManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 配置修改
+ *
+ * @author sunyj
+ * @since 2017/12/20 14:47
+ */
+@Controller
+@RequestMapping("/properties")
+public class PropertiesController {
+
+    @Autowired
+    private DynamicProperties dynamicProperties;
+
+    @RequestMapping("/get")
+    @ResponseBody
+    public DynamicProperties get(HttpServletRequest request) {
+        return dynamicProperties;
+    }
+
+    @RequestMapping("/update")
+    @ResponseBody
+    public boolean update(@RequestParam String json, HttpServletRequest request) {
+        dynamicProperties.update(json);
+        MasterManager.init();
+        return true;
+    }
+}

+ 9 - 1
report/src/main/java/com/uas/report/service/FileService.java

@@ -254,7 +254,15 @@ public interface FileService {
 	 * 
 	 * @param taskPeriod
 	 *            定时任务间隔时间(毫秒)
-	 * @return
+	 * @return 定时任务信息
 	 */
 	public TaskInformation newDeleteGeneratedFilesTask(Integer taskPeriod);
+
+    /**
+     * 开启备份模版文件的定时任务
+     *
+     * @param taskPeriod 定时任务间隔时间(毫秒)
+     * @return 定时任务信息
+     */
+    TaskInformation newBackupTask(Integer taskPeriod);
 }

+ 43 - 15
report/src/main/java/com/uas/report/service/impl/FileServiceImpl.java

@@ -2,7 +2,7 @@ package com.uas.report.service.impl;
 
 import com.alibaba.fastjson.JSONObject;
 import com.lowagie.text.pdf.PdfReader;
-import com.uas.report.SpecialProperties;
+import com.uas.report.DynamicProperties;
 import com.uas.report.schedule.model.TaskInformation;
 import com.uas.report.schedule.service.Executable;
 import com.uas.report.schedule.service.TaskService;
@@ -30,7 +30,7 @@ import java.util.*;
 public class FileServiceImpl implements FileService {
 
 	@Autowired
-	private SpecialProperties specialProperties;
+	private DynamicProperties dynamicProperties;
 
 	@Autowired
 	private TaskService taskService;
@@ -72,9 +72,9 @@ public class FileServiceImpl implements FileService {
 			throw new IllegalArgumentException("参数不能为空:sourceUserName,destinationUserNames");
 		}
 		byte[] data = null;
-		String stantardJrxmlsUrl = String.format(specialProperties.getStandardJrxmlsUrl(), sourceUserName);
+		String stantardJrxmlsUrl = String.format(dynamicProperties.getStandardJrxmlsUrl(), sourceUserName);
 		// 如果本机提供标准模板下载,直接从本地获取数据
-		if (specialProperties.hasStandardJrxmls()) {
+		if (dynamicProperties.getHasStandardJrxmls()) {
 			data = getStandardJrxmls(sourceUserName);
 		}
 		// 本机没有标准模板,则先下载标准模板数据
@@ -123,7 +123,7 @@ public class FileServiceImpl implements FileService {
 		}
 
 		String fileName = file.getOriginalFilename();
-		StringBuilder stringBuilder = new StringBuilder(specialProperties.getLocalBaseDir()).append("/");
+		StringBuilder stringBuilder = new StringBuilder(dynamicProperties.getLocalBaseDir()).append("/");
 		// jrxml模板和图片分别放在jrxml和Picture文件夹下,其他资源放在当前账套根路径下
 		if (fileType.equals("jrxml")) {
 			stringBuilder.append(userName).append("/").append("jrxml").append("/");
@@ -138,7 +138,7 @@ public class FileServiceImpl implements FileService {
 		stringBuilder.append(fileName);
 		String targetFilePath = stringBuilder.toString();
 		final File targetFile = new File(targetFilePath);
-		if (!targetFile.getParentFile().exists()) {
+		if (targetFile.getParentFile() != null && !targetFile.getParentFile().exists()) {
 			targetFile.getParentFile().mkdirs();
 		}
 		try {
@@ -159,7 +159,7 @@ public class FileServiceImpl implements FileService {
 		filePath = getAbsolutePath(filePath, isAbsolutePath);
 		File targetFile = new File(filePath + "/" + files[0].getOriginalFilename());
 		// 检查路径是否存在
-		if (!targetFile.getParentFile().exists()) {
+		if (targetFile.getParentFile() != null && !targetFile.getParentFile().exists()) {
 			targetFile.getParentFile().mkdirs();
 		}
 
@@ -182,7 +182,7 @@ public class FileServiceImpl implements FileService {
 	@Override
 	public String getJrxmlFilePath(String userName, String reportName) {
 		ReportUtils.checkParameters(userName, reportName);
-		return new StringBuilder(getMasterPath(userName)).append(specialProperties.getLocalJrxmlDir()).append("/")
+		return new StringBuilder(getMasterPath(userName)).append(dynamicProperties.getLocalJrxmlDir()).append("/")
 				.append(reportName).append(".jrxml").toString();
 	}
 
@@ -191,7 +191,7 @@ public class FileServiceImpl implements FileService {
 		if (StringUtils.isEmpty(userName)) {
 			throw new IllegalArgumentException("参数不能为空:userName");
 		}
-		return new StringBuilder(specialProperties.getLocalBaseDir()).append("/").append(userName).toString();
+		return new StringBuilder(dynamicProperties.getLocalBaseDir()).append("/").append(userName).toString();
 	}
 
 	@Override
@@ -199,11 +199,11 @@ public class FileServiceImpl implements FileService {
 		if (StringUtils.isEmpty(userName)) {
 			throw new IllegalArgumentException("未传入当前账套名称!");
 		}
-		if (!specialProperties.hasStandardJrxmls()) {
+		if (!dynamicProperties.getHasStandardJrxmls()) {
 			throw new IllegalStateException("没有" + userName + "标准模板!");
 		}
 		try {
-			return ZipUtils.zipFolder(getMasterPath(specialProperties.getStandardMaster()) + "/" + userName,
+			return ZipUtils.zipFolder(getMasterPath(dynamicProperties.getStandardMaster()) + "/" + userName,
 					FileServiceImpl.fileFilter);
 		} catch (Throwable e) {
 			throw new IOException("压缩失败", e);
@@ -215,10 +215,10 @@ public class FileServiceImpl implements FileService {
 		if (StringUtils.isEmpty(userName)) {
 			throw new IllegalArgumentException("未传入当前账套名称!");
 		}
-		if (!specialProperties.hasStandardJrxmls()) {
+		if (!dynamicProperties.getHasStandardJrxmls()) {
 			throw new IllegalStateException("没有" + userName + "标准模板!");
 		}
-		downloadZip(specialProperties.getStandardMaster() + "/" + userName, response);
+		downloadZip(dynamicProperties.getStandardMaster() + "/" + userName, response);
 	}
 
 	@Override
@@ -599,7 +599,7 @@ public class FileServiceImpl implements FileService {
 			return null;
 		}
 		// 获取相对路径,须将本地资源根路径替换掉,并且文件分隔符统一使用 '/'
-		return file.getPath().replace(new File(specialProperties.getLocalBaseDir()).getPath(), "").replace("\\", "/");
+		return file.getPath().replace(new File(dynamicProperties.getLocalBaseDir()).getPath(), "").replace("\\", "/");
 	}
 
 	@Override
@@ -674,7 +674,7 @@ public class FileServiceImpl implements FileService {
 		}
 		// 不是绝对路径的话,则相对于模板根路径
 		if (isAbsolutePath == null || !isAbsolutePath) {
-			filePath = specialProperties.getLocalBaseDir() + "/" + filePath;
+			filePath = dynamicProperties.getLocalBaseDir() + "/" + filePath;
 		}
 		return filePath;
 	}
@@ -709,4 +709,32 @@ public class FileServiceImpl implements FileService {
 		return taskInformation;
 	}
 
+    @Override
+    public TaskInformation newBackupTask(Integer taskPeriod) {
+        if (taskPeriod == null) {
+            throw new NullPointerException();
+        }
+        String title = "定时备份模版文件";
+        Executable command = new Executable() {
+            @Override
+            public String execute() {
+                try {
+                    File reportZipFile = new File(ReportUtils.getBackupDir(), "data.zip");
+                    if(reportZipFile.getParentFile() != null && !reportZipFile.getParentFile().exists()){
+                        reportZipFile.getParentFile().mkdirs();
+                    }
+                    // 先将整个目录压缩,再备份该压缩文件
+                    ZipUtils.zipFolder(dynamicProperties.getLocalBaseDir(), reportZipFile.getPath(), fileFilter);
+                    FileUtils.backup(reportZipFile, 10);
+                } catch (IOException e) {
+                    logger.error("备份失败", e);
+                }
+                return "success";
+            }
+        };
+        TaskInformation taskInformation = new TaskInformation(title, command, INITIAL_DELAY, taskPeriod);
+        taskService.newTask(taskInformation);
+        return taskInformation;
+    }
+
 }

+ 7 - 7
report/src/main/java/com/uas/report/service/impl/PrintServiceImpl.java

@@ -3,7 +3,7 @@ package com.uas.report.service.impl;
 import com.alibaba.druid.pool.DruidDataSource;
 import com.alibaba.druid.util.JdbcUtils;
 import com.alibaba.fastjson.JSONObject;
-import com.uas.report.SpecialProperties;
+import com.uas.report.DynamicProperties;
 import com.uas.report.SystemProperties;
 import com.uas.report.jasperreports.engine.export.CustomJRXlsExporter;
 import com.uas.report.jasperreports.engine.export.CustomJRXlsxExporter;
@@ -55,7 +55,7 @@ public class PrintServiceImpl implements PrintService {
 	private SystemProperties systemProperties;
 
 	@Autowired
-	private SpecialProperties specialProperties;
+	private DynamicProperties dynamicProperties;
 
 	private Logger logger = LoggerFactory.getLogger(getClass());
 
@@ -69,7 +69,7 @@ public class PrintServiceImpl implements PrintService {
 			throw new SQLException("获取数据源失败");
 		}
 
-		if(!exportFile.getParentFile().exists()){
+		if(exportFile.getParentFile() != null && !exportFile.getParentFile().exists()){
             exportFile.getParentFile().mkdirs();
         }
 
@@ -604,11 +604,11 @@ public class PrintServiceImpl implements PrintService {
 			File jrxmlFile = new File(jrxmlFilePath);
 			// 报表模板不存在,返回B2B标准模板账套
 			if (!jrxmlFile.exists()) {
-				return specialProperties.getStandardMaster() + "/B2B";
+				return dynamicProperties.getStandardMaster() + "/B2B";
 			}
 		} else {
 			// 如果主账套与子账套共用模板
-			if (specialProperties.shareJrxmlsWithSubMaster()) {
+			if (dynamicProperties.getShareJrxmlsWithSubMaster()) {
 				Master master = MasterManager.getMaster(userName);
 				if (master != null) {
 					// 获取账套的主账套
@@ -629,10 +629,10 @@ public class PrintServiceImpl implements PrintService {
 		int maxRecordSize = 0;
 		switch (platform) {
 		case PC:
-			maxRecordSize = systemProperties.getMaxRecordSizePc();
+			maxRecordSize = dynamicProperties.getMaxRecordSizePc();
 			break;
 		case PHONE:
-			maxRecordSize = systemProperties.getMaxRecordSizePhone();
+			maxRecordSize = dynamicProperties.getMaxRecordSizePhone();
 			break;
 		}
 

+ 43 - 6
report/src/main/java/com/uas/report/util/MasterManager.java

@@ -2,7 +2,8 @@ package com.uas.report.util;
 
 import com.alibaba.druid.pool.DruidDataSource;
 import com.alibaba.fastjson.JSONObject;
-import com.uas.report.SpecialProperties;
+import com.uas.report.DynamicProperties;
+import com.uas.report.SystemProperties;
 import com.uas.report.model.Master;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,24 +39,60 @@ public class MasterManager {
 	private static Logger logger = LoggerFactory.getLogger(MasterManager.class);
 
 	static {
-		initAllDataSources();
+		init();
 	}
 
 	/**
 	 * 初始化所有数据源
 	 */
-	private static void initAllDataSources() {
+	public static void init() {
+	    clear();
 		// 从配置中获取数据源信息
-		SpecialProperties specialProperties = ContextUtils.getBean(SpecialProperties.class);
-		JSONObject jsonObject = JSONObject.parseObject(specialProperties.getDataSourceInformation());
+		DynamicProperties dynamicProperties = ContextUtils.getBean(DynamicProperties.class);
+        JSONObject jsonObject = dynamicProperties.getDataSourceInformation();
 		Set<Entry<String, Object>> entrySet = jsonObject.entrySet();
 		for (Entry<String, Object> entry : entrySet) {
-			DruidDataSource dataSource = JSONObject.parseObject(entry.getValue().toString(), DruidDataSource.class);
+            JSONObject object = JSONObject.parseObject(entry.getValue().toString());
+            setDefaultConfig(object);
+            DruidDataSource dataSource = JSONObject.parseObject(entry.getValue().toString(), DruidDataSource.class);
 			dataSources.put(entry.getKey().toUpperCase(), dataSource);
 			masters.add(new Master(entry.getKey()));
 		}
 	}
 
+    /**
+     * 清除旧的数据源
+     */
+	private static void clear(){
+        Set<Entry<String, DruidDataSource>> entrySet = dataSources.entrySet();
+        for(Entry<String, DruidDataSource> entry : entrySet){
+            DruidDataSource dataSource = entry.getValue();
+            try {
+                dataSource.close();
+            } catch (Exception e) {
+                logger.error("数据源关闭失败", e);
+            }
+        }
+        dataSources = new ConcurrentHashMap<>();
+    }
+
+    /**
+     * 加载默认数据源配置
+     */
+    private static void setDefaultConfig(JSONObject dataSource){
+        SystemProperties systemProperties = ContextUtils.getBean(SystemProperties.class);
+        JSONObject dataSourceDefaultConfig = JSONObject.parseObject(systemProperties.getDataSourceDefaultConfig());
+        // 若数据源部分配置未显式指定,则使用默认值
+        Set<Entry<String, Object>> entrySet = dataSourceDefaultConfig.entrySet();
+        for(Entry<String, Object> entry : entrySet){
+            String key = entry.getKey();
+            if(StringUtils.isEmpty(dataSource.getString(key))){
+                dataSource.put(key, entry.getValue());
+            }
+        }
+    }
+
+
 	/**
 	 * 获取账套对应的数据源
 	 *

+ 0 - 302
report/src/main/java/com/uas/report/util/ObjectUtils.java

@@ -1,302 +0,0 @@
-package com.uas.report.util;
-
-import com.alibaba.fastjson.JSONArray;
-import java.io.*;
-import java.lang.reflect.Field;
-import java.util.*;
-import java.util.Map.Entry;
-
-/**
- * 对象工具类
- *
- * @author sunyj
- * @since 2017年9月1日 下午3:54:26
- */
-public class ObjectUtils {
-
-	/**
-	 * 判断是否为 null、空数组、空串或者空集合
-	 *
-	 * @param obj
-	 *            对象
-	 * @return 是否为 null、空数组、空串或者空集合
-	 */
-	public static boolean isEmpty(Object obj) {
-		if (obj == null) {
-			return true;
-		}
-		if (obj.getClass().isArray()) {
-			if (obj instanceof long[]) {
-				return ArrayUtils.isEmpty((long[]) obj);
-			} else if (obj instanceof int[]) {
-				return ArrayUtils.isEmpty((int[]) obj);
-			} else if (obj instanceof short[]) {
-				return ArrayUtils.isEmpty((short[]) obj);
-			} else if (obj instanceof byte[]) {
-				return ArrayUtils.isEmpty((byte[]) obj);
-			} else if (obj instanceof char[]) {
-				return ArrayUtils.isEmpty((char[]) obj);
-			} else if (obj instanceof boolean[]) {
-				return ArrayUtils.isEmpty((boolean[]) obj);
-			} else if (obj instanceof float[]) {
-				return ArrayUtils.isEmpty((float[]) obj);
-			} else if (obj instanceof double[]) {
-				return ArrayUtils.isEmpty((double[]) obj);
-			}
-			return ArrayUtils.isEmpty((Object[]) obj);
-		}
-		return (obj instanceof String && StringUtils.isEmpty(obj))
-				|| (obj instanceof Collection && CollectionUtils.isEmpty((Collection<?>) obj))
-				|| (obj instanceof Map && CollectionUtils.isEmpty((Map<?, ?>) obj));
-	}
-
-	/**
-	 * 将对象转为 String
-	 *
-	 * @param obj
-	 *            对象
-	 * @return 转换的 String
-	 */
-	public static String toString(Object obj) {
-		if (obj == null) {
-			return "";
-		}
-		if (obj instanceof String) {
-			return (String) obj;
-		}
-		if (obj.getClass().isArray()) {
-			if (obj instanceof long[]) {
-				return Arrays.toString((long[]) obj);
-			} else if (obj instanceof int[]) {
-				return Arrays.toString((int[]) obj);
-			} else if (obj instanceof short[]) {
-				return Arrays.toString((short[]) obj);
-			} else if (obj instanceof byte[]) {
-				return Arrays.toString((byte[]) obj);
-			} else if (obj instanceof char[]) {
-				return Arrays.toString((char[]) obj);
-			} else if (obj instanceof boolean[]) {
-				return Arrays.toString((boolean[]) obj);
-			} else if (obj instanceof float[]) {
-				return Arrays.toString((float[]) obj);
-			} else if (obj instanceof double[]) {
-				return Arrays.toString((double[]) obj);
-			}
-			return Arrays.toString((Object[]) obj);
-		}
-		return obj.toString();
-	}
-
-	/**
-	 * 深克隆对象
-	 *
-	 * @param t
-	 *            要克隆的对象
-	 * @return 克隆得到的对象
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T extends Serializable> T clone(T t)
-			throws IOException, ClassNotFoundException, NotSerializableException {
-		if (t == null) {
-			return null;
-		}
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		new ObjectOutputStream(out).writeObject(t);
-		ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
-		return (T) in.readObject();
-	}
-
-	/**
-	 * 深克隆集合对象
-	 *
-	 * @param collection
-	 *            要克隆的对象
-	 * @return 克隆得到的对象
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T> Collection<T> clone(Collection<T> collection) throws InstantiationException,
-			IllegalAccessException, NotSerializableException, ClassNotFoundException, IOException {
-		if (collection == null) {
-			return null;
-		}
-		Collection<T> result = collection.getClass().newInstance();
-		for (T t : collection) {
-			if (t instanceof Serializable) {
-				result.add((T) clone((Serializable) t));
-			} else if (t instanceof Collection) {
-				result.add((T) clone((Collection<?>) t));
-			} else if (t instanceof Map) {
-				result.add((T) clone((Map<String, ?>) t));
-			} else {
-				throw new IllegalArgumentException("未实现 Serializable 接口");
-			}
-		}
-		return result;
-	}
-
-	/**
-	 * 深克隆 Map 对象
-	 *
-	 * @param map
-	 *            要克隆的对象
-	 * @return 克隆得到的对象
-	 */
-	@SuppressWarnings("unchecked")
-	public static <T> Map<String, T> clone(Map<String, T> map) throws InstantiationException, IllegalAccessException,
-			NotSerializableException, ClassNotFoundException, IOException {
-		if (map == null) {
-			return null;
-		}
-		Map<String, T> result = map.getClass().newInstance();
-		Set<Entry<String, T>> entrySet = map.entrySet();
-		for (Entry<String, T> entry : entrySet) {
-			T value = entry.getValue();
-			if (value instanceof Serializable) {
-				result.put(entry.getKey(), (T) clone((Serializable) value));
-			} else if (value instanceof Collection) {
-				result.put(entry.getKey(), (T) clone((Collection<?>) value));
-			} else if (value instanceof Map) {
-				result.put(entry.getKey(), (T) clone((Map<String, ?>) value));
-			} else {
-				throw new IllegalArgumentException("未实现 Serializable 接口");
-			}
-		}
-		return result;
-	}
-
-	/**
-	 * JSONArray 转为指定类型的 List
-	 *
-	 * @param array
-	 *            JSONArray
-	 * @param clazz
-	 *            指定的类型
-	 * @param <T>
-	 *            指定的类型
-	 * @return 指定类型的 List
-	 */
-	public static <T> List<T> toList(JSONArray array, Class<T> clazz) {
-		if (array == null) {
-			return null;
-		}
-		List<T> list = new ArrayList<>();
-		for (int i = 0; i < array.size(); i++) {
-			list.add(array.getObject(i, clazz));
-		}
-		return list;
-	}
-
-	/**
-	 * 利用反射获取指定对象的指定字段的值
-	 *
-	 * @param field
-	 *            指定字段
-	 * @param k
-	 *            指定对象
-	 * @return 指定字段的值
-	 */
-	public static <K> Object getValue(Field field, K k) throws IllegalStateException {
-		Object value;
-		try {
-			if (!field.isAccessible()) {
-				field.setAccessible(true);
-				value = field.get(k);
-				field.setAccessible(false);
-			} else {
-				value = field.get(k);
-			}
-		} catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
-			throw new IllegalStateException("通过反射取值失败", e);
-		}
-		return value;
-	}
-
-	/**
-	 * 利用反射递归获取指定对象的指定字段的值
-	 *
-	 * @param field
-	 *            指定字段
-	 * @param k
-	 *            指定对象
-	 * @return 指定字段的值
-	 */
-	public static <K> Object recursivelyGetValue(String field, K k) {
-		return getValue(recursivelyGetField(field, k.getClass()), k);
-	}
-
-	/**
-	 * 递归获取指定类的指定字段(包括父类的私有字段)
-	 *
-	 * @param field
-	 *            指定字段
-	 * @param clazz
-	 *            指定类
-	 * @return 指定字段
-	 * @throws IllegalArgumentException
-	 */
-	public static Field recursivelyGetField(String field, Class<?> clazz) throws IllegalArgumentException {
-		try {
-			return clazz.getDeclaredField(field);
-		} catch (NoSuchFieldException e) {
-			Class<?> superclass = clazz.getSuperclass();
-			if (superclass != null) {
-				return recursivelyGetField(field, superclass);
-			}
-			throw new IllegalArgumentException(clazz + "中不存在字段:" + field);
-		}
-	}
-
-	/**
-	 * 获取指定类的指定字段
-	 *
-	 * @param field
-	 *            指定字段
-	 * @param clazz
-	 *            指定类
-	 * @return 指定字段
-	 * @throws IllegalArgumentException
-	 */
-	public static Field getField(String field, Class<?> clazz) throws IllegalArgumentException {
-		try {
-			return clazz.getDeclaredField(field);
-		} catch (NoSuchFieldException e) {
-			throw new IllegalArgumentException(clazz + "中不存在字段:" + field);
-		}
-	}
-
-	/**
-	 * 递归获取父类(包括私有字段)的所有字段
-	 *
-	 * @param clazz
-	 *            指定类
-	 * @return 所有字段
-	 */
-	public static List<Field> recursivelyGetParentField(Class<?> clazz) {
-		List<Field> fields = new ArrayList<>();
-		Class<?> superclass = clazz.getSuperclass();
-		if (superclass != null) {
-			fields.addAll(recursivelyGetField(superclass));
-		}
-		return fields;
-	}
-
-	/**
-	 * 递归获取所有字段(包括父类的私有字段)
-	 *
-	 * @param clazz
-	 *            指定类
-	 * @return 所有字段
-	 */
-	private static List<Field> recursivelyGetField(Class<?> clazz) {
-		List<Field> fields = new ArrayList<>();
-		Field[] declaredFields = clazz.getDeclaredFields();
-		if (!ArrayUtils.isEmpty(declaredFields)) {
-			fields.addAll(Arrays.asList(declaredFields));
-		}
-		Class<?> superclass = clazz.getSuperclass();
-		if (superclass != null) {
-			fields.addAll(recursivelyGetField(superclass));
-		}
-		return fields;
-	}
-}

+ 9 - 1
report/src/main/java/com/uas/report/util/ReportUtils.java

@@ -33,7 +33,8 @@ public class ReportUtils {
 		if (systemProperties.isTaskAutoStart()) {
 			FileService fileService = ContextUtils.getBean(FileService.class);
 			TaskService taskService = ContextUtils.getBean(TaskService.class);
-			fileService.newDeleteGeneratedFilesTask(systemProperties.getTaskPeriod());
+			fileService.newDeleteGeneratedFilesTask(systemProperties.getDeleteGeneratedFilesPeriod());
+			fileService.newBackupTask(systemProperties.getBackupPeriod());
 
 			// 开启定时任务
 			if (!taskService.isStopped()) {
@@ -70,4 +71,11 @@ public class ReportUtils {
 	public static String getLibDir() {
 		return getTmpDir() + "/lib";
 	}
+
+    /**
+     * @return 存放 lib 的临时路径
+     */
+    public static String getBackupDir() {
+        return getTmpDir() + "/backup";
+    }
 }

+ 8 - 13
report/src/main/resources/application.yml

@@ -18,7 +18,7 @@ spring:
 security:
  basic:
   enabled: true
-  path: /console, /file/delete, /schedule/**, /druid/*
+  path: /console, /file/delete, /schedule/**, /druid/*, /properties/**
  user:
   name: admin
   password: select111***
@@ -26,18 +26,13 @@ security:
  ignored: false
   
 schedule:
- period: 3600000
+ period:
+#  every 1 hour
+  delete-generated-files: 3600000
+#  every 100 hours
+  backup: 360000000
  auto-start: true
  
-max-record-size:
- pc: 100000
- phone: 10000
+extract-jars: report-common, jasperreports, fastjson
 
-use-xlsx: false
-
-page:
- preview:
-  # support PDF, XLS, XLS_DATA, XLSX, XLSX_DATA, DOC, TXT in com.uas.report.model.ExportType
-  show-export-buttons: PDF, XLS, XLS_DATA
-
-extract-jars: report-common, jasperreports, fastjson
+datasource-default-config: "{\"initialSize\":1,\"minIdle\":0,\"maxActive\":20,\"maxWait\":60000,\"timeBetweenEvictionRunsMillis\":60000,\"minEvictableIdleTimeMillis\":300000,\"validationQuery\":\"SELECT 1 FROM DUAL\",\"testWhileIdle\":true,\"testOnBorrow\":true,\"testOnReturn\":false,\"removeAbandoned\":true,\"removeAbandonedTimeout\":120,\"logAbandoned\":true,\"timeBetweenLogStatsMillis\":600000,\"poolPreparedStatements\":true,\"maxPoolPreparedStatementPerConnectionSize\":20,\"filters\":\"stat,slf4j\",\"connectionProperties\":\"druid.stat.mergeSql=false;druid.stat.slowSqlMillis=5000\"}"

+ 16 - 0
report/src/main/resources/report.properties

@@ -0,0 +1,16 @@
+localBaseDir=/home/uas/data/reports
+localImagesDir=/Picture
+localJrxmlDir=/jrxml
+standardMaster=STANDARD_MASTER
+hasStandardJrxmls=false
+standardJrxmlsUrl=http://print.ubtob.com/report/file/standardJrxmls?userName=%s&onlyData=1
+shareJrxmlsWithSubMaster=false
+
+max-record-size.pc=100000
+max-record-size.phone=10000
+
+use-xlsx=false
+# support PDF, XLS, XLS_DATA, XLSX, XLSX_DATA, DOC, TXT in com.uas.report.model.ExportType
+page.preview.show-export-buttons=PDF, XLS, XLS_DATA
+
+datasource={"B2B_dev":{"driverClassName":"oracle.jdbc.driver.OracleDriver","url":"jdbc:oracle:thin:@192.168.253.6:1521:orcl","username":"uuplatformdemo","password":"selectuuplatform"}, "UAS":{"driverClassName":"oracle.jdbc.driver.OracleDriver","url":"jdbc:oracle:thin:@192.168.253.6:1521:orcl","username":"UAS","password":"select!#%*("}}

+ 5 - 0
report/src/main/webapp/WEB-INF/views/console.html

@@ -96,6 +96,11 @@
 					<li><a target="_blank">print?userName=B2B/10005740&profile=test&reportName=PURCLIST&whereCondition=where rownum<30</a></li>
 					<li><a target="_blank">fileUpload?userName=B2B/1111</a></li>
 				</ol>
+                <strong><li class="title2">配置</li></strong>
+                <ol>
+                    <li><a target="_blank">properties/get</a></li>
+                    <li><a target="_blank">properties/update?json={"hasStandardJrxmls": false}</a></li>
+                </ol>
 			</ol>
 
 			<h2>5.定时任务</h2>