Parcourir la source

新增starter。

zhouhao il y a 8 ans
Parent
commit
8afd7b2f39
17 fichiers modifiés avec 1526 ajouts et 144 suppressions
  1. 0 101
      hsweb-web-core/src/main/java/org/hsweb/web/core/Install.java
  2. 0 1
      hsweb-web-service/hsweb-web-service-simple/src/main/java/org/hsweb/web/service/impl/form/DefaultFormParser.java
  3. 0 1
      hsweb-web-service/hsweb-web-service-simple/src/main/java/org/hsweb/web/service/impl/form/DynamicFormServiceImpl.java
  4. 59 0
      hsweb-web-starter/pom.xml
  5. 66 0
      hsweb-web-starter/src/main/java/org/hsweb/web/starter/AppProperties.java
  6. 324 0
      hsweb-web-starter/src/main/java/org/hsweb/web/starter/SystemInitialize.java
  7. 73 0
      hsweb-web-starter/src/main/java/org/hsweb/web/starter/SystemInitializeAutoConfiguration.java
  8. 87 40
      hsweb-web-bean/src/main/java/org/hsweb/web/bean/po/system/SystemVersion.java
  9. 36 0
      hsweb-web-starter/src/main/resources/META-INF/spring-configuration-metadata.json
  10. 3 0
      hsweb-web-starter/src/main/resources/META-INF/spring.factories
  11. 35 0
      hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/initialize/initialize.groovy
  12. 196 0
      hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/install.groovy
  13. 252 0
      hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/sql/h2/install.sql
  14. 144 0
      hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/sql/mysql/install.sql
  15. 176 0
      hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/sql/oracle/install.sql
  16. 69 0
      hsweb-web-starter/src/test/java/org/hsweb/web/starter/TestInstall.java
  17. 6 1
      pom.xml

+ 0 - 101
hsweb-web-core/src/main/java/org/hsweb/web/core/Install.java

@@ -1,101 +0,0 @@
-package org.hsweb.web.core;
-
-import org.hsweb.commons.file.FileUtils;
-import org.hsweb.ezorm.rdb.executor.SqlExecutor;
-import org.hsweb.ezorm.rdb.render.SqlAppender;
-import org.hsweb.ezorm.rdb.render.support.simple.SimpleSQL;
-import org.hsweb.web.core.datasource.DataSourceHolder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
-import org.springframework.util.Assert;
-
-import javax.annotation.PostConstruct;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.sql.Connection;
-import java.util.ArrayList;
-import java.util.List;
-
-@Configuration
-public class Install {
-    /**
-     * 获取当前数据库类型
-     *
-     * @return
-     */
-    public static String getDatabaseType() {
-        return DataSourceHolder.getActiveDatabaseType().name();
-    }
-
-    @Autowired
-    private SqlExecutor sqlExecutor;
-
-    @PostConstruct
-    public void install() throws Exception {
-        String dbType = DataSourceHolder.getActiveDatabaseType().name();
-        Assert.notNull(dbType, "不支持的数据库类型");
-        try {
-            boolean firstInstall = false;
-            try {
-                sqlExecutor.exec(new SimpleSQL("select * from s_user where 1=2"));
-            } catch (Exception e) {
-                firstInstall = true;
-            }
-            if (firstInstall) {
-                //表结构
-                InputStream reader = FileUtils.getResourceAsStream("system/install/sql/" + dbType + "/install.sql");
-                execInstallSql(reader);
-                String installSqlName = "classpath*:/system/install/sql/" + dbType + "/*-data.sql";
-                Resource[] resources = new PathMatchingResourcePatternResolver().getResources(installSqlName);
-                for (Resource resource : resources) {
-                    if (resource.isReadable()) {
-                        execInstallSql(resource.getInputStream());
-                    }
-                }
-            }
-        } catch (IOException e) {
-            throw e;
-        }
-    }
-
-    protected void execInstallSql(InputStream sqlStream) throws Exception {
-        String username = "";
-        Connection connection = null;
-        try {
-            connection = DataSourceHolder.getActiveSource().getConnection();
-            username = connection.getMetaData().getUserName();
-        } finally {
-            if (null != connection) connection.close();
-        }
-        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(sqlStream, "utf-8"));
-        List<String> sqlList = new ArrayList<>();
-        SqlAppender tmp = new SqlAppender();
-        String uname = username;
-        bufferedReader.lines().forEach((line) -> {
-            if (line.startsWith("--")) return;
-            line = line.replace("${jdbc.username}", uname);
-            //去除sql中的;
-            if (line.endsWith(";"))
-                tmp.add(line.substring(0, line.length() - 1));
-            else
-                tmp.add(line);
-            tmp.add("\n");
-            if (line.endsWith(";")) {
-                sqlList.add(tmp.toString());
-                tmp.clear();
-            }
-        });
-        sqlList.forEach((sql) -> {
-            try {
-                sqlExecutor.exec(new SimpleSQL(sql));
-            } catch (Exception e) {
-                throw new RuntimeException("install sql fail", e);
-            }
-        });
-    }
-
-}

+ 0 - 1
hsweb-web-service/hsweb-web-service-simple/src/main/java/org/hsweb/web/service/impl/form/DefaultFormParser.java

@@ -15,7 +15,6 @@ import org.hsweb.ezorm.rdb.meta.converter.JSONValueConverter;
 import org.hsweb.ezorm.rdb.render.dialect.Dialect;
 import org.hsweb.ezorm.rdb.simple.trigger.ScriptTraggerSupport;
 import org.hsweb.web.bean.po.form.Form;
-import org.hsweb.web.core.Install;
 import org.hsweb.web.core.datasource.DataSourceHolder;
 import org.hsweb.web.service.form.FormParser;
 import org.jsoup.Jsoup;

+ 0 - 1
hsweb-web-service/hsweb-web-service-simple/src/main/java/org/hsweb/web/service/impl/form/DynamicFormServiceImpl.java

@@ -23,7 +23,6 @@ import org.hsweb.web.bean.common.UpdateParam;
 import org.hsweb.web.bean.po.GenericPo;
 import org.hsweb.web.bean.po.form.Form;
 import org.hsweb.web.bean.po.history.History;
-import org.hsweb.web.core.Install;
 import org.hsweb.web.core.authorize.ExpressionScopeBean;
 import org.hsweb.web.core.exception.BusinessException;
 import org.hsweb.web.core.exception.NotFoundException;

+ 59 - 0
hsweb-web-starter/pom.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>hsweb-framework</artifactId>
+        <groupId>org.hsweb</groupId>
+        <version>2.2-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>hsweb-web-starter</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.oracle</groupId>
+            <artifactId>ojdbc14</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hsweb</groupId>
+            <artifactId>hsweb-web-service-simple</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hsweb</groupId>
+            <artifactId>hsweb-web-controller</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hsweb</groupId>
+            <artifactId>hsweb-web-dao-mybatis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hsweb</groupId>
+            <artifactId>hsweb-web-websocket</artifactId>
+        </dependency>
+
+    </dependencies>
+</project>

+ 66 - 0
hsweb-web-starter/src/main/java/org/hsweb/web/starter/AppProperties.java

@@ -0,0 +1,66 @@
+package org.hsweb.web.starter;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author zhouhao
+ */
+@ConfigurationProperties(prefix = "hsweb.app")
+public class AppProperties {
+    private String name;
+    private String comment;
+    private String website;
+    private String version;
+
+    public SystemVersion build() {
+        SystemVersion systemVersion = new SystemVersion();
+        if (name == null) name = "default";
+        if (version == null) {
+            version = systemVersion.getFrameworkVersion().versionToString();
+        }
+        boolean snapshot = name.toLowerCase().contains("snapshot");
+        name = name.toLowerCase().replace(".snapshot", "").replace("-snapshot", "");
+
+        systemVersion.setName(name);
+        systemVersion.setComment(comment);
+        systemVersion.setWebsite(website);
+        String[] strVer = version.split("[.]");
+        systemVersion.setVersion(Integer.parseInt(strVer[0])
+                , strVer.length > 1 ? Integer.parseInt(strVer[1]) : 0
+                , strVer.length > 2 ? Integer.parseInt(strVer[2]) : 0
+                , snapshot);
+        return systemVersion;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getComment() {
+        return comment;
+    }
+
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    public String getWebsite() {
+        return website;
+    }
+
+    public void setWebsite(String website) {
+        this.website = website;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+}

+ 324 - 0
hsweb-web-starter/src/main/java/org/hsweb/web/starter/SystemInitialize.java

@@ -0,0 +1,324 @@
+package org.hsweb.web.starter;
+
+import org.hsweb.commons.StringUtils;
+import org.hsweb.expands.script.engine.DynamicScriptEngine;
+import org.hsweb.expands.script.engine.DynamicScriptEngineFactory;
+import org.hsweb.expands.script.engine.ExecuteResult;
+import org.hsweb.ezorm.rdb.RDBDatabase;
+import org.hsweb.ezorm.rdb.RDBTable;
+import org.hsweb.ezorm.rdb.executor.SqlExecutor;
+import org.hsweb.ezorm.rdb.meta.converter.ClobValueConverter;
+import org.hsweb.ezorm.rdb.meta.converter.JSONValueConverter;
+import org.hsweb.ezorm.rdb.render.SqlAppender;
+import org.hsweb.ezorm.rdb.simple.wrapper.BeanWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.util.Assert;
+import org.springframework.util.StreamUtils;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.function.Predicate;
+
+import static org.hsweb.web.starter.SystemVersion.Property.*;
+
+
+/**
+ * @author zhouhao
+ */
+public class SystemInitialize implements InitializingBean {
+    private static final Logger              logger       = LoggerFactory.getLogger(SystemInitialize.class);
+    static final         Predicate<Resource> allWayIsTrue = (resource) -> true;
+
+    private SqlExecutor   sqlExecutor;
+    private RDBDatabase   database;
+    private String        databaseUserName;
+    private SystemVersion targetVersion;
+    private SystemVersion installedVersion;
+    private String installGroovyScriptPackage = "classpath*:org/hsweb/start/scripts/install/install.groovy";
+    private String installSqlScriptPackage    = "classpath*:org/hsweb/start/scripts/install/sql/{db}/install.sql";
+
+    private String upgradeGroovyScriptPackage = "classpath*:org/hsweb/start/scripts/upgrade/*.groovy";
+    private String upgradeSqlScriptPackage    = "classpath*:org/hsweb/start/scripts/upgrade/sql/{db}/*.sql";
+
+    private String initializeScriptPackage = "classpath*:org/hsweb/start/scripts/initialize/initialize.groovy";
+    private String initializeSqlPackage    = "classpath*:org/hsweb/start/scripts/initialize/sql/{db}/initialize.sql";
+
+    private String customInitializeSqlScriptPackage = "classpath*:scripts/install/sql/{db}/*.sql";
+    private String customInitializeScriptPackage    = "classpath*:scripts/initialize/initialize.groovy";
+
+    private String customUpgradeSqlScriptPackage = "classpath*:scripts/upgrade/sql/{db}/*.sql";
+    private String customUpgradeScriptPackage    = "classpath*:scripts/upgrade/*.groovy";
+
+    public SystemInitialize(SystemVersion targetVersion,
+                            SqlExecutor sqlExecutor,
+                            RDBDatabase database,
+                            String databaseUserName, String dbType) {
+        Assert.notNull(sqlExecutor);
+        Assert.notNull(database);
+        Assert.notNull(databaseUserName);
+        Assert.notNull(targetVersion);
+        this.sqlExecutor = sqlExecutor;
+        this.database = database;
+        this.databaseUserName = databaseUserName;
+        this.targetVersion = targetVersion;
+        installSqlScriptPackage = installSqlScriptPackage.replace("{db}", dbType);
+        upgradeSqlScriptPackage = upgradeSqlScriptPackage.replace("{db}", dbType);
+        initializeSqlPackage = initializeSqlPackage.replace("{db}", dbType);
+        customInitializeSqlScriptPackage = customInitializeSqlScriptPackage.replace("{db}", dbType);
+        customUpgradeSqlScriptPackage = customUpgradeSqlScriptPackage.replace("{db}", dbType);
+    }
+
+
+    Predicate<Resource> systemVersionFilter = (resource) -> {
+        try {
+            String name = resource.getFilename();
+            name = name.substring(0, name.lastIndexOf("."));
+            boolean snapshot = name.toLowerCase().contains("snapshot");
+            name = name.toLowerCase().replace(".snapshot", "").replace("-snapshot", "");
+
+            String[] ver = name.split("[.]");
+
+
+            SystemVersion systemVersion = new SystemVersion();
+            systemVersion.setVersion(Integer.parseInt(ver[0])
+                    , Integer.parseInt(ver[1])
+                    , Integer.parseInt(ver[2])
+                    , snapshot);
+            boolean install = systemVersion.compareTo(targetVersion) <= 0;
+            if (installedVersion != null) {
+                install = systemVersion.compareTo(installedVersion) == 1;
+            }
+            return install;
+        } catch (Exception e) {
+            logger.warn("parse file {} version error", resource, e);
+            return false;
+        }
+    };
+
+    Predicate<Resource> frameworkVersionFilter = (resource) -> {
+        try {
+            String name = resource.getFilename();
+            name = name.substring(0, name.lastIndexOf("."));
+            boolean snapshot = name.toLowerCase().contains("snapshot");
+            name = name.toLowerCase().replace(".snapshot", "").replace("-snapshot", "");
+            String[] ver = name.split("[.]");
+            SystemVersion.FrameworkVersion systemVersion = new SystemVersion.FrameworkVersion();
+            systemVersion.setVersion(Integer.parseInt(ver[0]),
+                    Integer.parseInt(ver[1]),
+                    Integer.parseInt(ver[2]), snapshot);
+            boolean install = systemVersion.compareTo(targetVersion.getFrameworkVersion()) <= 0;
+            if (installedVersion != null) {
+                install = systemVersion.compareTo(installedVersion.getFrameworkVersion()) == 1;
+            }
+            return install;
+        } catch (Exception e) {
+            logger.warn("parse file {} version error", resource, e);
+            return false;
+        }
+    };
+
+
+    protected void install() throws Exception {
+        boolean sync = false;
+        if (installedVersion == null) {
+            tryInitDatabase();
+            tryUpgradeFramework();
+            tryCustomInit();
+            tryCustomUpgrade();
+            sync = true;
+        } else {
+            int frameworkCompare = installedVersion.getFrameworkVersion().compareTo(targetVersion.getFrameworkVersion());
+            int systemCompare = installedVersion.compareTo(targetVersion);
+
+            if (frameworkCompare > 0) {
+                logger.warn("The installation framework ({}) is newer than the new version ({}).", installedVersion.getFrameworkVersion(), targetVersion.getFrameworkVersion());
+            } else if (frameworkCompare < 0) {
+                tryUpgradeFramework();
+                sync = true;
+                syncSystemVersion();
+            } else {
+                if (logger.isInfoEnabled())
+                    logger.info("framework : {}", installedVersion.getFrameworkVersion());
+            }
+
+            if (systemCompare > 0) {
+                logger.warn("The installation ({}) is newer than the new version ({}).", installedVersion, targetVersion);
+            } else if (systemCompare < 0) {
+                tryCustomUpgrade();
+                sync = true;
+            } else {
+                if (logger.isInfoEnabled())
+                    logger.info("system : {}", installedVersion);
+            }
+        }
+        if (sync)
+            syncSystemVersion();
+    }
+
+    protected void tryInitDatabase() throws Exception {
+        executeGroovy(getScriptFileContext(installGroovyScriptPackage, allWayIsTrue));
+        executeSql(getScriptFileContext(installSqlScriptPackage, allWayIsTrue));
+        executeGroovy(getScriptFileContext(initializeScriptPackage, allWayIsTrue));
+        executeSql(getScriptFileContext(initializeSqlPackage, allWayIsTrue));
+    }
+
+    protected void executeGroovy(List<String> groovyScript) throws Exception {
+        DynamicScriptEngine engine = DynamicScriptEngineFactory.getEngine("groovy");
+        String id = "hsweb.install";
+        Map<String, Object> var = getScriptVar();
+        for (String s : groovyScript) {
+            if (StringUtils.isNullOrEmpty(s)) continue;
+            engine.compile(id, s);
+            ExecuteResult result = engine.execute(id, var);
+            engine.remove(id);
+            if (result.isSuccess()) {
+                //logger.debug("install success!");
+            } else {
+                if (result.getException() != null) {
+                    throw (Exception) result.getException();
+                } else {
+                    throw new RuntimeException("execute groovy script error!");
+                }
+            }
+        }
+    }
+
+    protected Map<String, Object> getScriptVar() {
+        Map<String, Object> var = new HashMap<>();
+        var.put("database", database);
+        var.put("sqlExecutor", sqlExecutor);
+        return var;
+    }
+
+    protected void tryUpgradeFramework() throws Exception {
+        executeGroovy(getScriptFileContext(upgradeGroovyScriptPackage, frameworkVersionFilter));
+        executeSql(getScriptFileContext(upgradeSqlScriptPackage, frameworkVersionFilter));
+    }
+
+
+    protected void tryCustomInit() throws Exception {
+        executeGroovy(getScriptFileContext(customInitializeScriptPackage, allWayIsTrue));
+        executeSql(getScriptFileContext(customInitializeSqlScriptPackage, allWayIsTrue));
+    }
+
+    protected void tryCustomUpgrade() throws Exception {
+        executeGroovy(getScriptFileContext(customUpgradeScriptPackage, systemVersionFilter));
+        executeSql(getScriptFileContext(customUpgradeSqlScriptPackage, systemVersionFilter));
+    }
+
+    protected void executeSql(List<String> sqlFile) throws SQLException {
+        for (String sql : sqlFile) {
+            if (StringUtils.isNullOrEmpty(sql)) continue;
+            sqlExecutor.exec(sql);
+        }
+    }
+
+    protected void syncSystemVersion() throws SQLException {
+        RDBTable<SystemVersion> rdbTable = database.getTable("s_system");
+        int total = rdbTable.createQuery().total();
+        if (total == 0) {
+            rdbTable.createInsert().value(targetVersion).exec();
+        } else {
+            rdbTable.createUpdate().set(targetVersion).where().sql("1=1").exec();
+        }
+    }
+
+    protected SystemVersion getSystemVersion() throws SQLException {
+        boolean tableInstall = sqlExecutor.tableExists("s_system");
+        database.createOrAlter("s_system")
+                .addColumn().name("name").varchar(128).notNull().comment("系统名称").commit()
+                .addColumn().name("major_version").alias(majorVersion).number(32).javaType(Integer.class).notNull().comment("主版本号").commit()
+                .addColumn().name("minor_version").alias(minorVersion).number(32).javaType(Integer.class).notNull().comment("次版本号").commit()
+                .addColumn().name("revision_version").alias(revisionVersion).number(32).javaType(Integer.class).notNull().comment("修订版").commit()
+                .addColumn().name("snapshot").number(1).javaType(Boolean.class).notNull().comment("是否快照版").commit()
+                .addColumn().name("comment").varchar(2000).comment("系统说明").commit()
+                .addColumn().name("website").varchar(2000).comment("系统网址").commit()
+                .addColumn().name("framework_version").notNull().alias(frameworkVersion).clob()
+                .custom(column -> column.setValueConverter(new JSONValueConverter(SystemVersion.FrameworkVersion.class, new ClobValueConverter()))).notNull().comment("框架版本").commit()
+                .comment("系统信息")
+                .custom(table -> table.setObjectWrapper(new BeanWrapper(() -> new SystemVersion(), table)))
+                .commit();
+
+        if (!tableInstall) {
+            if (!sqlExecutor.tableExists("s_user")) {
+                return null;
+            } else {
+                logger.warn("Database Already initialized,but table [s_system] not Exists!");
+                //直接同步数据库
+                syncSystemVersion();
+                return targetVersion;
+            }
+        }
+        RDBTable<SystemVersion> rdbTable = database.getTable("s_system");
+        return rdbTable.createQuery().single();
+    }
+
+    private int compareFileName(Resource resource, Resource target) {
+        String name = resource.getFilename().split("[.]", 1)[0];
+        String targetName = target.getFilename().split("[.]", 1)[0];
+
+        if (StringUtils.isDouble(name) && StringUtils.isDouble(targetName)) {
+            return ((Double) Double.parseDouble(name)).compareTo(Double.parseDouble(targetName));
+        }
+        return name.compareTo(targetName);
+    }
+
+    protected List<String> getScriptFileContext(String filePackage, Predicate<Resource> filter) throws IOException {
+        Resource[] resources = new PathMatchingResourcePatternResolver().getResources(filePackage);
+        List<String> scripts = new ArrayList<>();
+        Arrays.stream(resources)
+                .filter(filter)
+                .sorted(this::compareFileName)
+                .forEach(resource -> {
+                    String name = resource.getFilename();
+                    try {
+                        if (name.endsWith(".sql")) {
+                            scripts.addAll(stream2sqlString(resource.getInputStream()));
+                        } else
+                            scripts.add(stream2string(resource.getInputStream()));
+                    } catch (IOException e) {
+                        logger.error("read file ({}) error", name, e);
+                    }
+                });
+        return scripts;
+    }
+
+
+    protected List<String> stream2sqlString(InputStream stream) throws UnsupportedEncodingException {
+        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream, "utf-8"));
+        List<String> sqlList = new ArrayList<>();
+        SqlAppender tmp = new SqlAppender();
+        String uname = databaseUserName;
+        bufferedReader.lines().forEach((line) -> {
+            if (line.startsWith("--")) return;
+            line = line.replace("${jdbc.username}", uname);
+            //去除sql中的;
+            if (line.endsWith(";"))
+                tmp.add(line.substring(0, line.length() - 1));
+            else
+                tmp.add(line);
+            tmp.add("\n");
+            if (line.endsWith(";")) {
+                sqlList.add(tmp.toString());
+                tmp.clear();
+            }
+        });
+        return sqlList;
+    }
+
+    protected String stream2string(InputStream stream) throws IOException {
+        return StreamUtils.copyToString(stream, Charset.forName("utf-8"));
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.installedVersion = getSystemVersion();
+        install();
+    }
+}

+ 73 - 0
hsweb-web-starter/src/main/java/org/hsweb/web/starter/SystemInitializeAutoConfiguration.java

@@ -0,0 +1,73 @@
+package org.hsweb.web.starter;
+
+import org.hsweb.ezorm.rdb.RDBDatabase;
+import org.hsweb.ezorm.rdb.executor.SqlExecutor;
+import org.hsweb.ezorm.rdb.meta.RDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.meta.parser.H2TableMetaParser;
+import org.hsweb.ezorm.rdb.meta.parser.MysqlTableMetaParser;
+import org.hsweb.ezorm.rdb.meta.parser.OracleTableMetaParser;
+import org.hsweb.ezorm.rdb.render.dialect.H2RDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.render.dialect.MysqlRDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.render.dialect.OracleRDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.simple.SimpleDatabase;
+import org.hsweb.web.core.datasource.DataSourceHolder;
+import org.hsweb.web.core.datasource.DatabaseType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import javax.sql.DataSource;
+import java.sql.Connection;
+
+/**
+ * @author zhouhao
+ */
+@Configuration
+@EnableConfigurationProperties(AppProperties.class)
+public class SystemInitializeAutoConfiguration {
+
+    @Autowired
+    private AppProperties appProperties;
+
+    @Autowired
+    DataSource dataSource;
+
+    @Autowired
+    SqlExecutor sqlExecutor;
+
+    @PostConstruct
+    public void systemInitialize() throws Exception {
+        DatabaseType type = DataSourceHolder.getDefaultDatabaseType();
+        SystemVersion version = appProperties.build();
+        Connection connection = null;
+        String jdbcUserName;
+        try {
+            connection = DataSourceHolder.getActiveSource().getConnection();
+            jdbcUserName = connection.getMetaData().getUserName();
+        } finally {
+            if (null != connection) connection.close();
+        }
+        RDBDatabaseMetaData metaData;
+        switch (type) {
+            case oracle:
+                metaData = new OracleRDBDatabaseMetaData();
+                metaData.setParser(new OracleTableMetaParser(sqlExecutor));
+                break;
+            case mysql:
+                metaData = new MysqlRDBDatabaseMetaData();
+                metaData.setParser(new MysqlTableMetaParser(sqlExecutor));
+                break;
+            default:
+                h2:
+                metaData = new H2RDBDatabaseMetaData();
+                metaData.setParser(new H2TableMetaParser(sqlExecutor));
+                break;
+        }
+
+        RDBDatabase database = new SimpleDatabase(metaData, sqlExecutor);
+        SystemInitialize initialize = new SystemInitialize(version, sqlExecutor, database, jdbcUserName, type.name());
+        initialize.afterPropertiesSet();
+
+    }
+}

+ 87 - 40
hsweb-web-bean/src/main/java/org/hsweb/web/bean/po/system/SystemVersion.java

@@ -14,16 +14,80 @@
  * limitations under the License.
  */
 
-package org.hsweb.web.bean.po.system;
+package org.hsweb.web.starter;
+
+public class SystemVersion extends Version {
+
+    private FrameworkVersion frameworkVersion = new FrameworkVersion();
+
+    public FrameworkVersion getFrameworkVersion() {
+        return frameworkVersion;
+    }
+
+    public void setFrameworkVersion(FrameworkVersion frameworkVersion) {
+        this.frameworkVersion = frameworkVersion;
+    }
+
+    public static class FrameworkVersion extends Version {
+        public FrameworkVersion() {
+            setName("hsweb framework");
+            setComment("企业后台管理系统基础框架");
+            setWebsite("http://www.hsweb.me");
+            setComment("");
+            setVersion(2, 2, 0, true);
+        }
+    }
+
+    public interface Property {
+        /**
+         * @see SystemVersion#name
+         */
+        String name            = "name";
+        /**
+         * @see SystemVersion#comment
+         */
+        String comment         = "comment";
+        /**
+         * @see SystemVersion#website
+         */
+        String website         = "website";
+        /**
+         * @see SystemVersion#majorVersion
+         */
+        String majorVersion    = "majorVersion";
+        /**
+         * @see SystemVersion#minorVersion
+         */
+        String minorVersion    = "minorVersion";
+        /**
+         * @see SystemVersion#revisionVersion
+         */
+        String revisionVersion = "revisionVersion";
+        /**
+         * @see SystemVersion#snapshot
+         */
+        String snapshot        = "snapshot";
+
+        String frameworkVersion = "frameworkVersion";
+    }
+}
+
+class Version implements Comparable<Version> {
+    protected String name;
+    protected String comment;
+    protected String website;
+    protected int     majorVersion    = 1;
+    protected int     minorVersion    = 0;
+    protected int     revisionVersion = 0;
+    protected boolean snapshot        = false;
+
+    public void setVersion(int major, int minor, int revision, boolean snapshot) {
+        this.majorVersion = major;
+        this.minorVersion = minor;
+        this.revisionVersion = revision;
+        this.snapshot = snapshot;
+    }
 
-public class SystemVersion implements Comparable<SystemVersion> {
-    public String name;
-    public String comment;
-    public String website;
-    public int majorVersion    = 1;
-    public int minorVersion    = 0;
-    public int revisionVersion = 0;
-    public boolean snapshot;
 
     public String getName() {
         return name;
@@ -82,7 +146,7 @@ public class SystemVersion implements Comparable<SystemVersion> {
     }
 
     @Override
-    public int compareTo(SystemVersion o) {
+    public int compareTo(Version o) {
         if (null == o) return -1;
         if (o.getMajorVersion() > this.getMajorVersion()) return -1;
         if (o.getMajorVersion() == this.getMajorVersion()) {
@@ -99,35 +163,18 @@ public class SystemVersion implements Comparable<SystemVersion> {
         }
     }
 
+    public String versionToString() {
+        return new StringBuilder()
+                .append(majorVersion).append(".")
+                .append(minorVersion).append(".")
+                .append(revisionVersion).append(snapshot ? ".SNAPSHOT" : "").toString();
+    }
 
-    public interface Property {
-        /**
-         * @see SystemVersion#name
-         */
-        String name            = "name";
-        /**
-         * @see SystemVersion#comment
-         */
-        String comment         = "comment";
-        /**
-         * @see SystemVersion#website
-         */
-        String website         = "website";
-        /**
-         * @see SystemVersion#majorVersion
-         */
-        String majorVersion    = "majorVersion";
-        /**
-         * @see SystemVersion#minorVersion
-         */
-        String minorVersion    = "minorVersion";
-        /**
-         * @see SystemVersion#revisionVersion
-         */
-        String revisionVersion = "revisionVersion";
-        /**
-         * @see SystemVersion#snapshot
-         */
-        String snapshot        = "snapshot";
+    @Override
+    public String toString() {
+        return new StringBuilder(name).append(" version ")
+                .append(majorVersion).append(".")
+                .append(minorVersion).append(".")
+                .append(revisionVersion).append(snapshot ? ".SNAPSHOT" : "").toString();
     }
-}
+}

+ 36 - 0
hsweb-web-starter/src/main/resources/META-INF/spring-configuration-metadata.json

@@ -0,0 +1,36 @@
+{
+  "groups": [
+    {
+      "name": "hsweb.app",
+      "type": "org.hsweb.web.starter.AppProperties",
+      "sourceType": "org.hsweb.web.starter.AppProperties"
+    }
+  ],
+  "properties": [
+    {
+      "name": "hsweb.app.name",
+      "type": "java.lang.String",
+      "sourceType": "org.hsweb.web.starter.AppProperties",
+      "defaultValue": "default"
+    },
+    {
+      "name": "hsweb.app.comment",
+      "type": "java.lang.String",
+      "sourceType": "org.hsweb.web.starter.AppProperties",
+      "defaultValue": ""
+    },
+    {
+      "name": "hsweb.app.website",
+      "type": "java.lang.String",
+      "sourceType": "org.hsweb.web.starter.AppProperties",
+      "defaultValue": ""
+    },
+    {
+      "name": "hsweb.app.version",
+      "type": "java.lang.String",
+      "sourceType": "org.hsweb.web.starter.AppProperties",
+      "defaultValue": "default"
+    }
+  ],
+  "hints": []
+}

+ 3 - 0
hsweb-web-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.hsweb.web.starter.SystemInitializeAutoConfiguration

+ 35 - 0
hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/initialize/initialize.groovy

@@ -0,0 +1,35 @@
+import org.hsweb.commons.MD5
+
+
+database.getTable("s_user")
+        .createInsert()
+        .value(["u_id": "admin", "username": "admin", "password": MD5.encode("admin"), "name": "超级管理员", "status": 1, "create_date": new Date()])
+        .exec();
+
+def s_modules = database.getTable("s_modules");
+
+def modules = [
+        [u_id: 'sys', name: '系统配置', uri: '', icon: 'fa fa-cog', parent_id: '-1', remark: '系统权限', status: 1, optional: '[{"id":"M","checked":"true"}]', sort_index: 3]
+        , [u_id: 'module', name: '权限管理', uri: 'admin/module/list.html', icon: 'fa fa-list-alt', parent_id: 'sys', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"C","text":"新增","checked":false},{"id":"R","text":"查询","checked":false},{"id":"U","text":"修改","checked":false},{"id":"D","text":"删除","checked":false}]', sort_index: 301]
+        , [u_id: 'role', name: '角色管理', uri: 'admin/role/list.html', icon: 'fa fa-users', parent_id: 'sys', remark: '初始数据', status: 1, optional: '[{"id":"M", "text":"菜单可见", "uri":""},{"id":"C", "text":"新增", "uri":""},{"id":"R", "text":"查询", "uri":""},{"id":"U", "text":"修改", "uri":""},{"id":"D", "text":"删除", "uri":""}]', sort_index: 302]
+        , [u_id: 'user', name: '用户管理', uri: 'admin/user/list.html', icon: 'fa fa-user', parent_id: 'sys', remark: '初始数据', status: 1, optional: '[{"id":"M","text":"菜单可见"},{"id":"C","text":"新增"},{"id":"R","text":"查询"},{"id":"U","text":"修改"},{"id":"D","text":"删除"},{"id":"enable","text":"启用"},{"id":"disable","text":"禁用"}]', sort_index: 303]
+        , [u_id: 'sys-parent', name: '系统开发', uri: '', icon: 'icon-application', parent_id: '-1', remark: '', status: 1, optional: '[{"id":"M"}]', sort_index: 4]
+        , [u_id: 'form', name: '表单管理', uri: 'admin/form/list.html', icon: 'fa fa-wpforms', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"C","text":"新增","checked":false},{"id":"R","text":"查询","checked":false},{"id":"U","text":"修改","checked":false},{"id":"D","text":"删除","checked":false},{"id":"deploy","text":"发布","checked":false}]', sort_index: 401]
+        , [u_id: 'module-meta', name: '模块设置', uri: 'admin/system-dev/list.html', icon: 'icon-application', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 402]
+        , [u_id: 'config', name: '配置管理', uri: 'admin/config/list.html', icon: '', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 403]
+        , [u_id: 'classified', name: '分类管理', uri: '', icon: '', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 404]
+        , [u_id: 'script', name: '脚本管理', uri: 'admin/script/list.html', icon: 'icon-page-white', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"exec","text":"运行"},{"id":"compile","text":"编译"},{"id":"M","text":"菜单可见","checked":"false"},{"id":"R","text":"查询","checked":"false"},{"id":"C","text":"新增","checked":"false"},{"id":"U","text":"修改","checked":"false"},{"id":"D","text":"删除","checked":false}]', sort_index: 405]
+        , [u_id: 'database', name: '数据库维护', uri: 'admin/database/list.html', icon: '', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"drop"},{"id":"comment"},{"id":"create"},{"id":"alter"},{"id":"R"},{"id":"M","text":"菜单可见","checked":true},{"id":"select","text":"查询","checked":true},{"id":"insert","text":"新增","checked":true},{"id":"update","text":"修改","checked":true},{"id":"delete","text":"删除","checked":false}]', sort_index: 406]
+        , [u_id: 'generator', name: '代码生成器', uri: 'admin/system-dev/generator/code-generator.html', icon: 'icon-application-cascade', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true}]', sort_index: 407]
+        , [u_id: 'datasource', name: '数据源', uri: 'admin/datasource/list.html', icon: '', parent_id: 'sys-parent', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"import","text":"导入excel","checked":true},{"id":"export","text":"导出excel","checked":true},{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 409]
+        , [u_id: 'sys-monitor', name: '系统维护', uri: '', icon: 'icon-application-xp-terminal', parent_id: '-1', remark: '', status: 1, optional: '[{"id":"M"}]', sort_index: 5]
+        , [u_id: 'system-monitor', name: '系统监控', uri: 'admin/system-monitor/cpu-mem.html', icon: '', parent_id: 'sys-monitor', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 501]
+        , [u_id: 'others', name: '其他权限', uri: '', icon: 'icon-page-white-magnify', parent_id: '-1', remark: '', status: 1, optional: '[]', sort_index: 6]
+        , [u_id: 'resources', name: '文件管理', uri: '', icon: '', parent_id: 'others', remark: '', status: 1, optional: '[{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 601]
+        , [u_id: 'query-plan', name: '查询方案', uri: '', icon: 'icon-table-multiple', parent_id: 'others', remark: '', status: 1, optional: '[{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 602]
+        , [u_id: 'monitor-cache', name: '缓存监控', uri: 'admin/system-monitor/cache.html', icon: 'icon-monitor', parent_id: 'sys-monitor', remark: '', status: 1, optional: '[{"id":"M","text":"菜单可见","checked":true},{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 502]
+        , [u_id: 'quartz', name: '定时任务', uri: 'admin/quartz/list.html', icon: '', parent_id: 'sys-monitor', remark: '', status: 1, optional: '[{"id":"enable"},{"id":"disable"},{"id":"history","text":"历史记录"},{"id":"M","text":"菜单可见","checked":true},{"id":"R","text":"查询","checked":true},{"id":"C","text":"新增","checked":true},{"id":"U","text":"修改","checked":true},{"id":"D","text":"删除","checked":false}]', sort_index: 503]
+];
+for (module in modules) {
+    s_modules.createInsert().value(module).exec();
+}

+ 196 - 0
hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/install.groovy

@@ -0,0 +1,196 @@
+
+import java.sql.JDBCType;
+
+database.createOrAlter("s_user_role")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("ID").commit()
+        .addColumn().name("user_id").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("用户id").commit()
+        .addColumn().name("role_id").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("角色id").commit()
+        .comment("用户角色关联表").commit()
+
+database.createOrAlter("s_history")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("ID").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(64).notNull().comment("历史记录类型").commit()
+        .addColumn().name("describe").jdbcType(JDBCType.VARCHAR).length(512).comment("记录描述").commit()
+        .addColumn().name("primary_key_name").jdbcType(JDBCType.VARCHAR).length(32).comment("主键名").commit()
+        .addColumn().name("primary_key_value").jdbcType(JDBCType.VARCHAR).length(64).comment("主键值").commit()
+        .addColumn().name("change_before").jdbcType(JDBCType.CLOB).comment("修改前数据").commit()
+        .addColumn().name("change_after").jdbcType(JDBCType.CLOB).comment("修改后数据").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.TIMESTAMP).notNull().comment("修改时间").commit()
+        .addColumn().name("creator_id").jdbcType(JDBCType.VARCHAR).length(32).comment("修改人ID").commit()
+        .comment("历史记录表").commit()
+
+database.createOrAlter("s_user_profile")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("content").jdbcType(JDBCType.CLOB).notNull().comment("配置内容").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(512).comment("类型").commit()
+        .addColumn().name("user_id").jdbcType(JDBCType.VARCHAR).length(32).comment("用户id").commit()
+        .comment("用户配置表").commit()
+
+database.createOrAlter("s_form")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("classified_id").jdbcType(JDBCType.VARCHAR).length(32).comment("分类id").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("名称").commit()
+        .addColumn().name("html").jdbcType(JDBCType.CLOB).comment("html内容").commit()
+        .addColumn().name("meta").jdbcType(JDBCType.CLOB).comment("结构定义").commit()
+        .addColumn().name("config").jdbcType(JDBCType.CLOB).comment("配置").commit()
+        .addColumn().name("version").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("版本").commit()
+        .addColumn().name("revision").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("修订版").commit()
+        .addColumn().name("release").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("当前发布版本").commit()
+        .addColumn().name("using").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("是否使用中").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.TIMESTAMP).notNull().comment("创建日期").commit()
+        .addColumn().name("update_date").jdbcType(JDBCType.TIMESTAMP).comment("修改日期").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(200).comment("").commit()
+        .comment("动态表单").commit()
+
+database.createOrAlter("s_modules")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("模块名称").commit()
+        .addColumn().name("uri").jdbcType(JDBCType.VARCHAR).length(1024).comment("uri").commit()
+        .addColumn().name("icon").jdbcType(JDBCType.VARCHAR).length(256).comment("图标").commit()
+        .addColumn().name("parent_id").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("上级菜单").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(512).comment("备注").commit()
+        .addColumn().name("status").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("状态").commit()
+        .addColumn().name("optional").jdbcType(JDBCType.CLOB).notNull().comment("可选权限").commit()
+        .addColumn().name("sort_index").jdbcType(JDBCType.DECIMAL).length(32, 0).notNull().comment("排序").commit()
+        .comment("系统模块").commit()
+
+database.createOrAlter("s_module_meta")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("key").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("名称").commit()
+        .addColumn().name("module_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().comment("模块id").commit()
+        .addColumn().name("role_id").jdbcType(JDBCType.VARCHAR).length(32).comment("角色id").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(1024).comment("备注").commit()
+        .addColumn().name("meta").jdbcType(JDBCType.CLOB).comment("内容").commit()
+        .addColumn().name("status").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("状态").commit()
+        .comment("系统模块配置").commit()
+
+database.createOrAlter("s_role_modules")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("module_id").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("模块id").commit()
+        .addColumn().name("role_id").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("角色id").commit()
+        .addColumn().name("actions").jdbcType(JDBCType.CLOB).comment("可操作权限").commit()
+        .comment("角色模块绑定表").commit()
+
+database.createOrAlter("s_query_plan")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("主键").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("方案名称").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("方案分类").commit()
+        .addColumn().name("config").jdbcType(JDBCType.CLOB).comment("方案配置").commit()
+        .addColumn().name("sharing").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("是否共享方案").commit()
+        .addColumn().name("creator_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().comment("创建人id").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.TIMESTAMP).notNull().comment("创建日期").commit()
+        .comment("查询方案表").commit()
+
+database.createOrAlter("s_resources")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("资源名称").commit()
+        .addColumn().name("path").jdbcType(JDBCType.VARCHAR).length(1024).notNull().comment("路径").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("类型").commit()
+        .addColumn().name("md5").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("md5校验值").commit()
+        .addColumn().name("size").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("资源大小").commit()
+        .addColumn().name("status").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("状态").commit()
+        .addColumn().name("classified").jdbcType(JDBCType.VARCHAR).length(32).comment("分类").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.TIMESTAMP).notNull().comment("创建时间").commit()
+        .addColumn().name("creator_id").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("创建人").commit()
+        .comment("资源表").commit()
+
+database.createOrAlter("s_script")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("脚本名称").commit()
+        .addColumn().name("classified_id").jdbcType(JDBCType.VARCHAR).length(1024).notNull().comment("路径").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("类型").commit()
+        .addColumn().name("content").jdbcType(JDBCType.CLOB).notNull().comment("内容").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(512).comment("备注").commit()
+        .addColumn().name("status").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("状态").commit()
+        .comment("脚本").commit()
+
+database.createOrAlter("s_role")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("角色名称").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(50).comment("类型").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(512).comment("备注").commit()
+        .comment("角色表").commit()
+
+database.createOrAlter("s_data_source")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("id").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(64).notNull().comment("数据源名称").commit()
+        .addColumn().name("driver").jdbcType(JDBCType.VARCHAR).length(128).notNull().comment("驱动").commit()
+        .addColumn().name("url").jdbcType(JDBCType.VARCHAR).length(512).notNull().comment("url").commit()
+        .addColumn().name("username").jdbcType(JDBCType.VARCHAR).length(128).notNull().comment("用户名").commit()
+        .addColumn().name("password").jdbcType(JDBCType.VARCHAR).length(128).notNull().comment("密码").commit()
+        .addColumn().name("enabled").jdbcType(JDBCType.DECIMAL).length(4, 0).notNull().comment("是否启用").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.DATE).length(8, 0).notNull().comment("创建日期").commit()
+        .addColumn().name("properties").jdbcType(JDBCType.CLOB).comment("其他配置").commit()
+        .addColumn().name("comment").jdbcType(JDBCType.VARCHAR).length(512).comment("备注").commit()
+        .addColumn().name("test_sql").jdbcType(JDBCType.VARCHAR).length(512).comment("测试链接时使用的sql").commit()
+        .comment("数据源").commit()
+
+database.createOrAlter("s_classified")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("分类名称").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(1024).comment("备注").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(256).comment("类型").commit()
+        .addColumn().name("parent_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().comment("父级分类").commit()
+        .addColumn().name("icon").jdbcType(JDBCType.VARCHAR).length(256).comment("图标").commit()
+        .addColumn().name("config").jdbcType(JDBCType.CLOB).comment("分类配置").commit()
+        .addColumn().name("sort_index").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("排序").commit()
+        .comment("数据分类表").commit()
+
+database.createOrAlter("s_config")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("content").jdbcType(JDBCType.CLOB).notNull().comment("配置内容").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(512).comment("备注").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.TIMESTAMP).notNull().comment("创建日期").commit()
+        .addColumn().name("classified_id").jdbcType(JDBCType.VARCHAR).length(32).comment("分类id").commit()
+        .addColumn().name("update_date").jdbcType(JDBCType.TIMESTAMP).comment("修改日期").commit()
+        .comment("系统配置文件表").commit()
+
+database.createOrAlter("s_quartz_job")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("主键").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(128).notNull().comment("任务名称").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(512).comment("备注").commit()
+        .addColumn().name("cron").jdbcType(JDBCType.VARCHAR).length(512).notNull().comment("cron表达式").commit()
+        .addColumn().name("script").jdbcType(JDBCType.CLOB).notNull().comment("执行脚本").commit()
+        .addColumn().name("language").jdbcType(JDBCType.VARCHAR).length(32).notNull().comment("脚本语言").commit()
+        .addColumn().name("enabled").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("是否启用").commit()
+        .addColumn().name("parameters").jdbcType(JDBCType.CLOB).comment("启动参数").commit()
+        .addColumn().name("type").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("任务类型").commit()
+        .comment("定时任务").commit()
+
+database.createOrAlter("s_template")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("uid").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(256).notNull().comment("名称").commit()
+        .addColumn().name("template").jdbcType(JDBCType.CLOB).comment("模板").commit()
+        .addColumn().name("classified_id").jdbcType(JDBCType.VARCHAR).length(32).comment("分类").commit()
+        .addColumn().name("type").jdbcType(JDBCType.VARCHAR).length(64).comment("类型").commit()
+        .addColumn().name("script").jdbcType(JDBCType.CLOB).comment("脚本").commit()
+        .addColumn().name("css").jdbcType(JDBCType.CLOB).comment("样式").commit()
+        .addColumn().name("css_links").jdbcType(JDBCType.CLOB).comment("样式链接").commit()
+        .addColumn().name("script_links").jdbcType(JDBCType.CLOB).comment("脚本链接").commit()
+        .addColumn().name("version").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("版本").commit()
+        .addColumn().name("revision").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("修订版").commit()
+        .addColumn().name("release").jdbcType(JDBCType.DECIMAL).length(32, 0).comment("当前发布版本").commit()
+        .addColumn().name("using").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("是否使用中").commit()
+        .addColumn().name("remark").jdbcType(JDBCType.VARCHAR).length(200).comment("").commit()
+        .comment("模板").commit()
+
+database.createOrAlter("s_user")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("id").commit()
+        .addColumn().name("username").jdbcType(JDBCType.VARCHAR).length(64).notNull().comment("用户名").commit()
+        .addColumn().name("password").jdbcType(JDBCType.VARCHAR).length(64).notNull().comment("密码").commit()
+        .addColumn().name("name").jdbcType(JDBCType.VARCHAR).length(64).comment("姓名").commit()
+        .addColumn().name("email").jdbcType(JDBCType.VARCHAR).length(512).comment("邮箱").commit()
+        .addColumn().name("phone").jdbcType(JDBCType.VARCHAR).length(64).comment("联系电话").commit()
+        .addColumn().name("status").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("状态").commit()
+        .addColumn().name("create_date").jdbcType(JDBCType.TIMESTAMP).notNull().comment("创建日期").commit()
+        .addColumn().name("update_date").jdbcType(JDBCType.TIMESTAMP).comment("修改日期").commit()
+        .comment("用户表").commit()
+
+database.createOrAlter("s_quartz_job_his")
+        .addColumn().name("u_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().primaryKey().comment("主键").commit()
+        .addColumn().name("job_id").jdbcType(JDBCType.VARCHAR).length(32).notNull().comment("任务id").commit()
+        .addColumn().name("start_time").jdbcType(JDBCType.TIMESTAMP).notNull().comment("开始时间").commit()
+        .addColumn().name("end_time").jdbcType(JDBCType.TIMESTAMP).comment("结束时间").commit()
+        .addColumn().name("result").jdbcType(JDBCType.CLOB).comment("执行结果").commit()
+        .addColumn().name("status").jdbcType(JDBCType.DECIMAL).length(4, 0).comment("状态").commit()
+        .comment("定时任务执行记录").commit()

+ 252 - 0
hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/sql/h2/install.sql

@@ -0,0 +1,252 @@
+
+CREATE TABLE QRTZ_CALENDARS (
+  SCHED_NAME    VARCHAR(120) NOT NULL,
+  CALENDAR_NAME VARCHAR(200) NOT NULL,
+  CALENDAR      IMAGE        NOT NULL
+);
+
+CREATE TABLE QRTZ_CRON_TRIGGERS (
+  SCHED_NAME      VARCHAR(120) NOT NULL,
+  TRIGGER_NAME    VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP   VARCHAR(200) NOT NULL,
+  CRON_EXPRESSION VARCHAR(120) NOT NULL,
+  TIME_ZONE_ID    VARCHAR(80)
+);
+
+CREATE TABLE QRTZ_FIRED_TRIGGERS (
+  SCHED_NAME        VARCHAR(120) NOT NULL,
+  ENTRY_ID          VARCHAR(95)  NOT NULL,
+  TRIGGER_NAME      VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP     VARCHAR(200) NOT NULL,
+  INSTANCE_NAME     VARCHAR(200) NOT NULL,
+  FIRED_TIME        BIGINT       NOT NULL,
+  SCHED_TIME        BIGINT       NOT NULL,
+  PRIORITY          INTEGER      NOT NULL,
+  STATE             VARCHAR(16)  NOT NULL,
+  JOB_NAME          VARCHAR(200) NULL,
+  JOB_GROUP         VARCHAR(200) NULL,
+  IS_NONCONCURRENT  BOOLEAN      NULL,
+  REQUESTS_RECOVERY BOOLEAN      NULL
+);
+
+CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS (
+  SCHED_NAME    VARCHAR(120) NOT NULL,
+  TRIGGER_GROUP VARCHAR(200) NOT NULL
+);
+
+CREATE TABLE QRTZ_SCHEDULER_STATE (
+  SCHED_NAME        VARCHAR(120) NOT NULL,
+  INSTANCE_NAME     VARCHAR(200) NOT NULL,
+  LAST_CHECKIN_TIME BIGINT       NOT NULL,
+  CHECKIN_INTERVAL  BIGINT       NOT NULL
+);
+
+CREATE TABLE QRTZ_LOCKS (
+  SCHED_NAME VARCHAR(120) NOT NULL,
+  LOCK_NAME  VARCHAR(40)  NOT NULL
+);
+
+CREATE TABLE QRTZ_JOB_DETAILS (
+  SCHED_NAME        VARCHAR(120) NOT NULL,
+  JOB_NAME          VARCHAR(200) NOT NULL,
+  JOB_GROUP         VARCHAR(200) NOT NULL,
+  DESCRIPTION       VARCHAR(250) NULL,
+  JOB_CLASS_NAME    VARCHAR(250) NOT NULL,
+  IS_DURABLE        BOOLEAN      NOT NULL,
+  IS_NONCONCURRENT  BOOLEAN      NOT NULL,
+  IS_UPDATE_DATA    BOOLEAN      NOT NULL,
+  REQUESTS_RECOVERY BOOLEAN      NOT NULL,
+  JOB_DATA          IMAGE        NULL
+);
+
+CREATE TABLE QRTZ_SIMPLE_TRIGGERS (
+  SCHED_NAME      VARCHAR(120) NOT NULL,
+  TRIGGER_NAME    VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP   VARCHAR(200) NOT NULL,
+  REPEAT_COUNT    BIGINT       NOT NULL,
+  REPEAT_INTERVAL BIGINT       NOT NULL,
+  TIMES_TRIGGERED BIGINT       NOT NULL
+);
+
+CREATE TABLE QRTZ_SIMPROP_TRIGGERS
+(
+  SCHED_NAME    VARCHAR(120)   NOT NULL,
+  TRIGGER_NAME  VARCHAR(200)   NOT NULL,
+  TRIGGER_GROUP VARCHAR(200)   NOT NULL,
+  STR_PROP_1    VARCHAR(512)   NULL,
+  STR_PROP_2    VARCHAR(512)   NULL,
+  STR_PROP_3    VARCHAR(512)   NULL,
+  INT_PROP_1    INTEGER        NULL,
+  INT_PROP_2    INTEGER        NULL,
+  LONG_PROP_1   BIGINT         NULL,
+  LONG_PROP_2   BIGINT         NULL,
+  DEC_PROP_1    NUMERIC(13, 4) NULL,
+  DEC_PROP_2    NUMERIC(13, 4) NULL,
+  BOOL_PROP_1   BOOLEAN        NULL,
+  BOOL_PROP_2   BOOLEAN        NULL,
+);
+
+CREATE TABLE QRTZ_BLOB_TRIGGERS (
+  SCHED_NAME    VARCHAR(120) NOT NULL,
+  TRIGGER_NAME  VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR(200) NOT NULL,
+  BLOB_DATA     IMAGE        NULL
+);
+
+CREATE TABLE QRTZ_TRIGGERS (
+  SCHED_NAME     VARCHAR(120) NOT NULL,
+  TRIGGER_NAME   VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP  VARCHAR(200) NOT NULL,
+  JOB_NAME       VARCHAR(200) NOT NULL,
+  JOB_GROUP      VARCHAR(200) NOT NULL,
+  DESCRIPTION    VARCHAR(250) NULL,
+  NEXT_FIRE_TIME BIGINT       NULL,
+  PREV_FIRE_TIME BIGINT       NULL,
+  PRIORITY       INTEGER      NULL,
+  TRIGGER_STATE  VARCHAR(16)  NOT NULL,
+  TRIGGER_TYPE   VARCHAR(8)   NOT NULL,
+  START_TIME     BIGINT       NOT NULL,
+  END_TIME       BIGINT       NULL,
+  CALENDAR_NAME  VARCHAR(200) NULL,
+  MISFIRE_INSTR  SMALLINT     NULL,
+  JOB_DATA       IMAGE        NULL
+);
+
+ALTER TABLE QRTZ_CALENDARS
+  ADD
+  CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY
+    (
+      SCHED_NAME,
+      CALENDAR_NAME
+    );
+
+ALTER TABLE QRTZ_CRON_TRIGGERS
+  ADD
+  CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    );
+
+ALTER TABLE QRTZ_FIRED_TRIGGERS
+  ADD
+  CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY
+    (
+      SCHED_NAME,
+      ENTRY_ID
+    );
+
+ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS
+  ADD
+  CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY
+    (
+      SCHED_NAME,
+      TRIGGER_GROUP
+    );
+
+ALTER TABLE QRTZ_SCHEDULER_STATE
+  ADD
+  CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY
+    (
+      SCHED_NAME,
+      INSTANCE_NAME
+    );
+
+ALTER TABLE QRTZ_LOCKS
+  ADD
+  CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY
+    (
+      SCHED_NAME,
+      LOCK_NAME
+    );
+
+ALTER TABLE QRTZ_JOB_DETAILS
+  ADD
+  CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY
+    (
+      SCHED_NAME,
+      JOB_NAME,
+      JOB_GROUP
+    );
+
+ALTER TABLE QRTZ_SIMPLE_TRIGGERS
+  ADD
+  CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    );
+
+ALTER TABLE QRTZ_SIMPROP_TRIGGERS
+  ADD
+  CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    );
+
+ALTER TABLE QRTZ_TRIGGERS
+  ADD
+  CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    );
+
+ALTER TABLE QRTZ_CRON_TRIGGERS
+  ADD
+  CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    ) REFERENCES QRTZ_TRIGGERS (
+    SCHED_NAME,
+    TRIGGER_NAME,
+    TRIGGER_GROUP
+  ) ON DELETE CASCADE;
+
+
+ALTER TABLE QRTZ_SIMPLE_TRIGGERS
+  ADD
+  CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    ) REFERENCES QRTZ_TRIGGERS (
+    SCHED_NAME,
+    TRIGGER_NAME,
+    TRIGGER_GROUP
+  ) ON DELETE CASCADE;
+
+ALTER TABLE QRTZ_SIMPROP_TRIGGERS
+  ADD
+  CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS FOREIGN KEY
+    (
+      SCHED_NAME,
+      TRIGGER_NAME,
+      TRIGGER_GROUP
+    ) REFERENCES QRTZ_TRIGGERS (
+    SCHED_NAME,
+    TRIGGER_NAME,
+    TRIGGER_GROUP
+  ) ON DELETE CASCADE;
+
+
+ALTER TABLE QRTZ_TRIGGERS
+  ADD
+  CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS FOREIGN KEY
+    (
+      SCHED_NAME,
+      JOB_NAME,
+      JOB_GROUP
+    ) REFERENCES QRTZ_JOB_DETAILS (
+    SCHED_NAME,
+    JOB_NAME,
+    JOB_GROUP
+  );

+ 144 - 0
hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/sql/mysql/install.sql

@@ -0,0 +1,144 @@
+CREATE TABLE QRTZ_JOB_DETAILS
+(
+  SCHED_NAME        VARCHAR(120) NOT NULL,
+  JOB_NAME          VARCHAR(200) NOT NULL,
+  JOB_GROUP         VARCHAR(200) NOT NULL,
+  DESCRIPTION       VARCHAR(250) NULL,
+  JOB_CLASS_NAME    VARCHAR(250) NOT NULL,
+  IS_DURABLE        VARCHAR(1)   NOT NULL,
+  IS_NONCONCURRENT  VARCHAR(1)   NOT NULL,
+  IS_UPDATE_DATA    VARCHAR(1)   NOT NULL,
+  REQUESTS_RECOVERY VARCHAR(1)   NOT NULL,
+  JOB_DATA          BLOB         NULL,
+  PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
+);
+
+CREATE TABLE QRTZ_TRIGGERS
+(
+  SCHED_NAME     VARCHAR(120) NOT NULL,
+  TRIGGER_NAME   VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP  VARCHAR(200) NOT NULL,
+  JOB_NAME       VARCHAR(200) NOT NULL,
+  JOB_GROUP      VARCHAR(200) NOT NULL,
+  DESCRIPTION    VARCHAR(250) NULL,
+  NEXT_FIRE_TIME BIGINT(13)   NULL,
+  PREV_FIRE_TIME BIGINT(13)   NULL,
+  PRIORITY       INTEGER      NULL,
+  TRIGGER_STATE  VARCHAR(16)  NOT NULL,
+  TRIGGER_TYPE   VARCHAR(8)   NOT NULL,
+  START_TIME     BIGINT(13)   NOT NULL,
+  END_TIME       BIGINT(13)   NULL,
+  CALENDAR_NAME  VARCHAR(200) NULL,
+  MISFIRE_INSTR  SMALLINT(2)  NULL,
+  JOB_DATA       BLOB         NULL,
+  PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
+  REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)
+);
+
+CREATE TABLE QRTZ_SIMPLE_TRIGGERS
+(
+  SCHED_NAME      VARCHAR(120) NOT NULL,
+  TRIGGER_NAME    VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP   VARCHAR(200) NOT NULL,
+  REPEAT_COUNT    BIGINT(7)    NOT NULL,
+  REPEAT_INTERVAL BIGINT(12)   NOT NULL,
+  TIMES_TRIGGERED BIGINT(10)   NOT NULL,
+  PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+
+CREATE TABLE QRTZ_CRON_TRIGGERS
+(
+  SCHED_NAME      VARCHAR(120) NOT NULL,
+  TRIGGER_NAME    VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP   VARCHAR(200) NOT NULL,
+  CRON_EXPRESSION VARCHAR(200) NOT NULL,
+  TIME_ZONE_ID    VARCHAR(80),
+  PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+
+CREATE TABLE QRTZ_SIMPROP_TRIGGERS
+(
+  SCHED_NAME    VARCHAR(120)   NOT NULL,
+  TRIGGER_NAME  VARCHAR(200)   NOT NULL,
+  TRIGGER_GROUP VARCHAR(200)   NOT NULL,
+  STR_PROP_1    VARCHAR(512)   NULL,
+  STR_PROP_2    VARCHAR(512)   NULL,
+  STR_PROP_3    VARCHAR(512)   NULL,
+  INT_PROP_1    INT            NULL,
+  INT_PROP_2    INT            NULL,
+  LONG_PROP_1   BIGINT         NULL,
+  LONG_PROP_2   BIGINT         NULL,
+  DEC_PROP_1    NUMERIC(13, 4) NULL,
+  DEC_PROP_2    NUMERIC(13, 4) NULL,
+  BOOL_PROP_1   VARCHAR(1)     NULL,
+  BOOL_PROP_2   VARCHAR(1)     NULL,
+  PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+
+CREATE TABLE QRTZ_BLOB_TRIGGERS
+(
+  SCHED_NAME    VARCHAR(120) NOT NULL,
+  TRIGGER_NAME  VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR(200) NOT NULL,
+  BLOB_DATA     BLOB         NULL,
+  PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+
+CREATE TABLE QRTZ_CALENDARS
+(
+  SCHED_NAME    VARCHAR(120) NOT NULL,
+  CALENDAR_NAME VARCHAR(200) NOT NULL,
+  CALENDAR      BLOB         NOT NULL,
+  PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)
+);
+
+CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
+(
+  SCHED_NAME    VARCHAR(120) NOT NULL,
+  TRIGGER_GROUP VARCHAR(200) NOT NULL,
+  PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)
+);
+
+CREATE TABLE QRTZ_FIRED_TRIGGERS
+(
+  SCHED_NAME        VARCHAR(120) NOT NULL,
+  ENTRY_ID          VARCHAR(95)  NOT NULL,
+  TRIGGER_NAME      VARCHAR(200) NOT NULL,
+  TRIGGER_GROUP     VARCHAR(200) NOT NULL,
+  INSTANCE_NAME     VARCHAR(200) NOT NULL,
+  FIRED_TIME        BIGINT(13)   NOT NULL,
+  SCHED_TIME        BIGINT(13)   NOT NULL,
+  PRIORITY          INTEGER      NOT NULL,
+  STATE             VARCHAR(16)  NOT NULL,
+  JOB_NAME          VARCHAR(200) NULL,
+  JOB_GROUP         VARCHAR(200) NULL,
+  IS_NONCONCURRENT  VARCHAR(1)   NULL,
+  REQUESTS_RECOVERY VARCHAR(1)   NULL,
+  PRIMARY KEY (SCHED_NAME, ENTRY_ID)
+);
+
+CREATE TABLE QRTZ_SCHEDULER_STATE
+(
+  SCHED_NAME        VARCHAR(120) NOT NULL,
+  INSTANCE_NAME     VARCHAR(200) NOT NULL,
+  LAST_CHECKIN_TIME BIGINT(13)   NOT NULL,
+  CHECKIN_INTERVAL  BIGINT(13)   NOT NULL,
+  PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)
+);
+
+CREATE TABLE QRTZ_LOCKS
+(
+  SCHED_NAME VARCHAR(120) NOT NULL,
+  LOCK_NAME  VARCHAR(40)  NOT NULL,
+  PRIMARY KEY (SCHED_NAME, LOCK_NAME)
+);
+

+ 176 - 0
hsweb-web-starter/src/main/resources/org/hsweb/start/scripts/install/sql/oracle/install.sql

@@ -0,0 +1,176 @@
+CREATE TABLE ${jdbc.username}.qrtz_job_details
+(
+  SCHED_NAME        VARCHAR2(120) NOT NULL,
+  JOB_NAME          VARCHAR2(200) NOT NULL,
+  JOB_GROUP         VARCHAR2(200) NOT NULL,
+  DESCRIPTION       VARCHAR2(250) NULL,
+  JOB_CLASS_NAME    VARCHAR2(250) NOT NULL,
+  IS_DURABLE        VARCHAR2(1)   NOT NULL,
+  IS_NONCONCURRENT  VARCHAR2(1)   NOT NULL,
+  IS_UPDATE_DATA    VARCHAR2(1)   NOT NULL,
+  REQUESTS_RECOVERY VARCHAR2(1)   NOT NULL,
+  JOB_DATA          BLOB          NULL,
+  CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_triggers
+(
+  SCHED_NAME     VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME   VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP  VARCHAR2(200) NOT NULL,
+  JOB_NAME       VARCHAR2(200) NOT NULL,
+  JOB_GROUP      VARCHAR2(200) NOT NULL,
+  DESCRIPTION    VARCHAR2(250) NULL,
+  NEXT_FIRE_TIME NUMBER(13)    NULL,
+  PREV_FIRE_TIME NUMBER(13)    NULL,
+  PRIORITY       NUMBER(13)    NULL,
+  TRIGGER_STATE  VARCHAR2(16)  NOT NULL,
+  TRIGGER_TYPE   VARCHAR2(8)   NOT NULL,
+  START_TIME     NUMBER(13)    NOT NULL,
+  END_TIME       NUMBER(13)    NULL,
+  CALENDAR_NAME  VARCHAR2(200) NULL,
+  MISFIRE_INSTR  NUMBER(2)     NULL,
+  JOB_DATA       BLOB          NULL,
+  CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
+  REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_simple_triggers
+(
+  SCHED_NAME      VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME    VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP   VARCHAR2(200) NOT NULL,
+  REPEAT_COUNT    NUMBER(7)     NOT NULL,
+  REPEAT_INTERVAL NUMBER(12)    NOT NULL,
+  TIMES_TRIGGERED NUMBER(10)    NOT NULL,
+  CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_cron_triggers
+(
+  SCHED_NAME      VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME    VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP   VARCHAR2(200) NOT NULL,
+  CRON_EXPRESSION VARCHAR2(120) NOT NULL,
+  TIME_ZONE_ID    VARCHAR2(80),
+  CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_simprop_triggers
+(
+  SCHED_NAME    VARCHAR2(120)  NOT NULL,
+  TRIGGER_NAME  VARCHAR2(200)  NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200)  NOT NULL,
+  STR_PROP_1    VARCHAR2(512)  NULL,
+  STR_PROP_2    VARCHAR2(512)  NULL,
+  STR_PROP_3    VARCHAR2(512)  NULL,
+  INT_PROP_1    NUMBER(10)     NULL,
+  INT_PROP_2    NUMBER(10)     NULL,
+  LONG_PROP_1   NUMBER(13)     NULL,
+  LONG_PROP_2   NUMBER(13)     NULL,
+  DEC_PROP_1    NUMERIC(13, 4) NULL,
+  DEC_PROP_2    NUMERIC(13, 4) NULL,
+  BOOL_PROP_1   VARCHAR2(1)    NULL,
+  BOOL_PROP_2   VARCHAR2(1)    NULL,
+  CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_blob_triggers
+(
+  SCHED_NAME    VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME  VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  BLOB_DATA     BLOB          NULL,
+  CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
+  CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_calendars
+(
+  SCHED_NAME    VARCHAR2(120) NOT NULL,
+  CALENDAR_NAME VARCHAR2(200) NOT NULL,
+  CALENDAR      BLOB          NOT NULL,
+  CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)
+);
+CREATE TABLE ${jdbc.username}.qrtz_paused_trigger_grps
+(
+  SCHED_NAME    VARCHAR2(120) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)
+);
+CREATE TABLE ${jdbc.username}.qrtz_fired_triggers
+(
+  SCHED_NAME        VARCHAR2(120) NOT NULL,
+  ENTRY_ID          VARCHAR2(95)  NOT NULL,
+  TRIGGER_NAME      VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP     VARCHAR2(200) NOT NULL,
+  INSTANCE_NAME     VARCHAR2(200) NOT NULL,
+  FIRED_TIME        NUMBER(13)    NOT NULL,
+  SCHED_TIME        NUMBER(13)    NOT NULL,
+  PRIORITY          NUMBER(13)    NOT NULL,
+  STATE             VARCHAR2(16)  NOT NULL,
+  JOB_NAME          VARCHAR2(200) NULL,
+  JOB_GROUP         VARCHAR2(200) NULL,
+  IS_NONCONCURRENT  VARCHAR2(1)   NULL,
+  REQUESTS_RECOVERY VARCHAR2(1)   NULL,
+  CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME, ENTRY_ID)
+);
+CREATE TABLE ${jdbc.username}.qrtz_scheduler_state
+(
+  SCHED_NAME        VARCHAR2(120) NOT NULL,
+  INSTANCE_NAME     VARCHAR2(200) NOT NULL,
+  LAST_CHECKIN_TIME NUMBER(13)    NOT NULL,
+  CHECKIN_INTERVAL  NUMBER(13)    NOT NULL,
+  CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)
+);
+CREATE TABLE ${jdbc.username}.qrtz_locks
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  LOCK_NAME  VARCHAR2(40)  NOT NULL,
+  CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME, LOCK_NAME)
+);
+
+CREATE INDEX idx_qrtz_j_req_recovery
+  ON ${jdbc.username}.${jdbc.username}.qrtz_job_details (SCHED_NAME, REQUESTS_RECOVERY);
+CREATE INDEX idx_qrtz_j_grp
+  ON ${jdbc.username}.${jdbc.username}.qrtz_job_details (SCHED_NAME, JOB_GROUP);
+
+CREATE INDEX idx_qrtz_t_j
+  ON ${jdbc.username}.${jdbc.username}.qrtz_triggers (SCHED_NAME, JOB_NAME, JOB_GROUP);
+CREATE INDEX idx_qrtz_t_jg
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, JOB_GROUP);
+CREATE INDEX idx_qrtz_t_c
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, CALENDAR_NAME);
+CREATE INDEX idx_qrtz_t_g
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, TRIGGER_GROUP);
+CREATE INDEX idx_qrtz_t_state
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, TRIGGER_STATE);
+CREATE INDEX idx_qrtz_t_n_state
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);
+CREATE INDEX idx_qrtz_t_n_g_state
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);
+CREATE INDEX idx_qrtz_t_next_fire_time
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, NEXT_FIRE_TIME);
+CREATE INDEX idx_qrtz_t_nft_st
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);
+CREATE INDEX idx_qrtz_t_nft_misfire
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);
+CREATE INDEX idx_qrtz_t_nft_st_misfire
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);
+CREATE INDEX idx_qrtz_t_nft_st_misfire_grp
+  ON ${jdbc.username}.qrtz_triggers (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);
+
+CREATE INDEX idx_qrtz_ft_trig_inst_name
+  ON ${jdbc.username}.qrtz_fired_triggers (SCHED_NAME, INSTANCE_NAME);
+CREATE INDEX idx_qrtz_ft_inst_job_req_rcvry
+  ON ${jdbc.username}.qrtz_fired_triggers (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);
+CREATE INDEX idx_qrtz_ft_j_g
+  ON ${jdbc.username}.qrtz_fired_triggers (SCHED_NAME, JOB_NAME, JOB_GROUP);
+CREATE INDEX idx_qrtz_ft_jg
+  ON ${jdbc.username}.qrtz_fired_triggers (SCHED_NAME, JOB_GROUP);
+CREATE INDEX idx_qrtz_ft_t_g
+  ON ${jdbc.username}.qrtz_fired_triggers (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);
+CREATE INDEX idx_qrtz_ft_tg
+  ON ${jdbc.username}.qrtz_fired_triggers (SCHED_NAME, TRIGGER_GROUP);

+ 69 - 0
hsweb-web-starter/src/test/java/org/hsweb/web/starter/TestInstall.java

@@ -0,0 +1,69 @@
+package org.hsweb.web.starter;
+
+import org.hsweb.ezorm.rdb.RDBDatabase;
+import org.hsweb.ezorm.rdb.executor.AbstractJdbcSqlExecutor;
+import org.hsweb.ezorm.rdb.executor.SqlExecutor;
+import org.hsweb.ezorm.rdb.meta.RDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.render.dialect.H2RDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.render.dialect.MysqlRDBDatabaseMetaData;
+import org.hsweb.ezorm.rdb.simple.SimpleDatabase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+/**
+ * @author zhouhao
+ * @TODO
+ */
+public class TestInstall {
+
+    SqlExecutor sqlExecutor;
+    RDBDatabase database;
+    Connection  connection;
+
+    @Before
+    public void setup() throws Exception {
+//        Class.forName("org.h2.Driver");
+//        Connection connection = DriverManager.getConnection("jdbc:h2:file:./target/data/h2db;", "sa", "");
+
+        Class.forName("org.h2.Driver");
+        connection = DriverManager.getConnection("jdbc:h2:file:./target/data/h2db;", "sa", "");
+//        Class.forName("com.mysql.jdbc.Driver");
+//        connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_db?useUnicode=true&characterEncoding=utf-8&useSSL=false", "root", "19920622");
+
+        sqlExecutor = new AbstractJdbcSqlExecutor() {
+
+            @Override
+            public Connection getConnection() {
+                return connection;
+            }
+
+            @Override
+            public void releaseConnection(Connection connection) throws SQLException {
+                //connection.close();
+            }
+        };
+        RDBDatabaseMetaData databaseMetaData = new H2RDBDatabaseMetaData();
+        database = new SimpleDatabase(databaseMetaData, sqlExecutor);
+    }
+
+    @Test
+    public void testInstall() throws Exception {
+        SystemVersion version = new SystemVersion();
+        version.setVersion(2, 2, 4, true);
+        version.setName("hsweb");
+        version.setComment("测试");
+        SystemInitialize initialize = new SystemInitialize(version, sqlExecutor, database, "sa", "h2");
+        initialize.afterPropertiesSet();
+    }
+
+    @After
+    public void uninstall() throws SQLException {
+        connection.close();
+    }
+
+}

+ 6 - 1
pom.xml

@@ -49,6 +49,7 @@
         <module>hsweb-web-concurrent</module>
         <module>hsweb-web-oauth2</module>
         <module>hsweb-web-datasource</module>
+        <module>hsweb-web-starter</module>
     </modules>
 
     <properties>
@@ -163,7 +164,11 @@
                 <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
                 <version>1.0.2</version>
             </dependency>
-
+            <dependency>
+                <groupId>org.hsweb</groupId>
+                <artifactId>hsweb-web-starter</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.hsweb</groupId>
                 <artifactId>hsweb-web-datasource</artifactId>