liyan hace 4 años
padre
commit
1bef7bf7b1
Se han modificado 28 ficheros con 1190 adiciones y 0 borrados
  1. 37 0
      .gitignore
  2. 67 0
      build.gradle.kts
  3. BIN
      gradle/wrapper/gradle-wrapper.jar
  4. 5 0
      gradle/wrapper/gradle-wrapper.properties
  5. 185 0
      gradlew
  6. 89 0
      gradlew.bat
  7. 1 0
      settings.gradle.kts
  8. 69 0
      src/main/java/com/hikari/regedist/support/BusinessError.java
  9. 21 0
      src/main/kotlin/com/hikari/regedist/PolicyConfiguration.kt
  10. 43 0
      src/main/kotlin/com/hikari/regedist/PolicyController.kt
  11. 43 0
      src/main/kotlin/com/hikari/regedist/PolicyItemController.kt
  12. 18 0
      src/main/kotlin/com/hikari/regedist/QueryByPathController.kt
  13. 15 0
      src/main/kotlin/com/hikari/regedist/RegedistApplication.kt
  14. 21 0
      src/main/kotlin/com/hikari/regedist/dao/PolicyDao.kt
  15. 13 0
      src/main/kotlin/com/hikari/regedist/dao/PolicyItemDao.kt
  16. 19 0
      src/main/kotlin/com/hikari/regedist/domain/PolicyForm.kt
  17. 26 0
      src/main/kotlin/com/hikari/regedist/domain/PolicyItemForm.kt
  18. 10 0
      src/main/kotlin/com/hikari/regedist/domain/PolicyResult.kt
  19. 35 0
      src/main/kotlin/com/hikari/regedist/entity/Policy.kt
  20. 41 0
      src/main/kotlin/com/hikari/regedist/entity/PolicyItem.kt
  21. 118 0
      src/main/kotlin/com/hikari/regedist/service/PolicyItemService.kt
  22. 148 0
      src/main/kotlin/com/hikari/regedist/service/PolicyService.kt
  23. 84 0
      src/main/kotlin/com/hikari/regedist/service/QueryByPathService.kt
  24. 10 0
      src/main/kotlin/com/hikari/regedist/support/ErrorResult.kt
  25. 3 0
      src/main/kotlin/com/hikari/regedist/support/PagedData.kt
  26. 38 0
      src/main/kotlin/com/hikari/regedist/support/WebControllerAdvice.kt
  27. 18 0
      src/main/resources/application.yml
  28. 13 0
      src/test/kotlin/com/hikari/regedist/RegedistApplicationTests.kt

+ 37 - 0
.gitignore

@@ -0,0 +1,37 @@
+HELP.md
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/

+ 67 - 0
build.gradle.kts

@@ -0,0 +1,67 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+    id("org.springframework.boot") version "2.3.4.RELEASE"
+    id("io.spring.dependency-management") version "1.0.10.RELEASE"
+    kotlin("jvm") version "1.3.72"
+    kotlin("plugin.spring") version "1.3.72"
+    kotlin("plugin.jpa") version "1.3.72"
+}
+
+group = "com.hikari"
+version = "0.0.1-SNAPSHOT"
+java.sourceCompatibility = JavaVersion.VERSION_11
+val repoConf: String = System.getProperty("repoPath") ?: "/var/repo"
+val repoPath: String = file("$rootDir").toPath().root.resolve(repoConf).toString()
+
+repositories {
+    // 阿里云镜像
+    maven { url = uri("https://maven.aliyun.com/repository/public") }
+    maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
+    maven { url = uri("https://maven.aliyun.com/repository/spring") }
+    maven { url = uri("https://maven.aliyun.com/repository/spring-plugin") }
+    maven {
+        name = "localRepo"
+        url = uri("file://$repoPath")
+    }
+    maven {
+        name = "cc-lotus"
+        url = uri("https://maven.cc-lotus.info/repository/maven-public/")
+    }
+}
+
+extra["springCloudVersion"] = "Hoxton.SR8"
+
+dependencies {
+    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+    implementation("org.springframework.boot:spring-boot-starter-webflux")
+    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
+    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
+    implementation("org.jetbrains.kotlin:kotlin-reflect")
+    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
+    implementation("org.springframework.cloud:spring-cloud-starter-gateway")
+    implementation("cc-lotus.gaf3:gaf-core-shared:3.0.928")
+    runtimeOnly("mysql:mysql-connector-java")
+    testImplementation("org.springframework.boot:spring-boot-starter-test") {
+        exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
+    }
+    testImplementation("io.projectreactor:reactor-test")
+}
+
+dependencyManagement {
+    imports {
+        mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}")
+    }
+}
+
+tasks.withType<Test> {
+    useJUnitPlatform()
+}
+
+tasks.withType<KotlinCompile> {
+    kotlinOptions {
+        freeCompilerArgs = listOf("-Xjsr305=strict")
+        jvmTarget = "11"
+    }
+}

BIN
gradle/wrapper/gradle-wrapper.jar


+ 5 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 185 - 0
gradlew

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"

+ 89 - 0
gradlew.bat

@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 0
settings.gradle.kts

@@ -0,0 +1 @@
+rootProject.name = "regedist"

+ 69 - 0
src/main/java/com/hikari/regedist/support/BusinessError.java

@@ -0,0 +1,69 @@
+package com.hikari.regedist.support;
+
+public class BusinessError extends RuntimeException {
+
+    public static final int SUCCESS = 0;
+    public static final int VALIDATE_FAILD = -1;
+    public static final int INSERT_DATA_ALLREADY_EXIST = -2;
+    public static final int DATA_NOT_EXIST = -3;
+    public static final int DATA_NOT_ILLEGAL = -4;
+    public static final int DEL_DATA_HASCHILDREN = -5;
+    public static final int UNKNOWN_ERROR = -10000;
+
+    protected int errCode;
+    protected String errMsg;
+    protected String details;
+
+    public BusinessError(int errCode) {
+        super(getErrDetails(errCode));
+        this.errCode = errCode;
+        this.errMsg = getErrDetails(errCode);
+        this.details = this.errMsg;
+    }
+
+    public BusinessError(int errCode, String errMsg) {
+        super(errMsg);
+        this.errMsg = errMsg;
+        this.errCode = errCode;
+        this.details = this.errMsg;
+    }
+
+    public BusinessError(int errCode, String errMsg, String details) {
+        super(errMsg);
+        this.errCode = errCode;
+        this.errMsg = errMsg;
+        this.details = details;
+    }
+
+    public static String getErrDetails(int errCode) {
+        switch (errCode) {
+            case SUCCESS:
+                return "操作成功";
+            case VALIDATE_FAILD:
+                return "数据校验失败";
+            case INSERT_DATA_ALLREADY_EXIST:
+                return "数据已存在";
+            case DATA_NOT_EXIST:
+                return "数据不存在";
+            case DATA_NOT_ILLEGAL:
+                return "参数不合法";
+            case DEL_DATA_HASCHILDREN:
+                return "被删除项含有子项,无法删除";
+            default:
+                return "未知错误";
+        }
+    }
+
+    public int getErrcode() {
+        return errCode;
+    }
+
+    public String getErrmsg() {
+        if (this.errMsg == null) return this.getMessage();
+        return this.errMsg;
+    }
+
+    public String getDetails() {
+        return details;
+    }
+}

+ 21 - 0
src/main/kotlin/com/hikari/regedist/PolicyConfiguration.kt

@@ -0,0 +1,21 @@
+package com.hikari.regedist
+
+import org.springframework.boot.context.properties.ConfigurationProperties
+
+@ConfigurationProperties(prefix = "hikari.policy")
+class PolicyConfiguration {
+
+    var separator: String = "/"
+
+    /**
+     * 策略删除策略
+     * soft:被删除的策略含有子项时,无法删除
+     * hard: 删除策略本身及所有子项
+     */
+    var deletePolicy: String? = "soft"
+
+    /**
+     * 是否级联删除策略下所有策略项,true:删除策略时删除策略所属的所有策略项,false:被删除策略
+     */
+    var cascadeDelPolicyItem: Boolean = false
+}

+ 43 - 0
src/main/kotlin/com/hikari/regedist/PolicyController.kt

@@ -0,0 +1,43 @@
+package com.hikari.regedist
+
+import com.hikari.regedist.domain.PolicyForm
+import com.hikari.regedist.domain.PolicyResult
+import com.hikari.regedist.entity.Policy
+import com.hikari.regedist.service.PolicyService
+import gaf3.core.data.PageParam
+import gaf3.core.data.PagedData
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.web.bind.annotation.*
+
+@RestController
+class PolicyController {
+
+    @Autowired
+    lateinit var policyService: PolicyService
+
+    @PostMapping(path = ["/policy"])
+    fun create(@RequestBody form: PolicyForm): Policy {
+        return policyService.create(form)
+    }
+
+    @PostMapping(path = ["/policy/{id}"])
+    fun update(@PathVariable id: String, @RequestBody form: PolicyForm): Policy {
+        return policyService.update(id, form)
+    }
+
+    @GetMapping(path = ["/policy"])
+    fun find(filter: Policy, pageParam: PageParam): PagedData<Policy> {
+        return policyService.find(filter, pageParam)
+    }
+
+    @GetMapping(path = ["/policy/{id}"])
+    fun findById(@PathVariable id: String): Policy {
+        return policyService.findById(id)
+    }
+
+    @DeleteMapping(path = ["/policy/{id}"])
+    fun delete(@PathVariable id: String) {
+        return policyService.delete(id)
+    }
+
+}

+ 43 - 0
src/main/kotlin/com/hikari/regedist/PolicyItemController.kt

@@ -0,0 +1,43 @@
+package com.hikari.regedist
+
+import com.hikari.regedist.domain.PolicyItemForm
+import com.hikari.regedist.entity.PolicyItem
+import com.hikari.regedist.service.PolicyItemService
+import gaf3.core.data.PageParam
+import gaf3.core.data.PagedData
+import kotlinx.coroutines.DisposableHandle
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.web.bind.annotation.*
+
+@RestController
+class PolicyItemController {
+
+    @Autowired
+    lateinit var policyItemService: PolicyItemService
+
+    @PostMapping(path = ["/policyItem"])
+    fun create(@RequestBody form: PolicyItemForm): PolicyItem {
+        return policyItemService.create(form)
+    }
+
+    @PostMapping(path = ["/policyItem/{id}"])
+    fun update(@PathVariable id: String, @RequestBody form: PolicyItemForm): PolicyItem {
+        return policyItemService.update(id, form)
+    }
+
+    @GetMapping(path = ["/policyItem"])
+    fun find(filter: PolicyItem, pageParam: PageParam): PagedData<PolicyItem> {
+        return policyItemService.find(filter, pageParam)
+    }
+
+    @GetMapping(path = ["/policyItem/{id}"])
+    fun findById(@PathVariable id: String): PolicyItem {
+        return policyItemService.findById(id)
+    }
+
+    @DeleteMapping(path = ["/policyItem/{id}"])
+    fun delete(@PathVariable id: String) {
+        policyItemService.delete(id)
+    }
+
+}

+ 18 - 0
src/main/kotlin/com/hikari/regedist/QueryByPathController.kt

@@ -0,0 +1,18 @@
+package com.hikari.regedist
+
+import com.hikari.regedist.service.QueryByPathService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+class QueryByPathController {
+
+    @Autowired
+    lateinit var queryByPathService: QueryByPathService
+
+    @GetMapping(path = ["/policy/query"])
+    fun find(path: String): Any {
+        return queryByPathService.findByKeyPath(path)
+    }
+}

+ 15 - 0
src/main/kotlin/com/hikari/regedist/RegedistApplication.kt

@@ -0,0 +1,15 @@
+package com.hikari.regedist
+
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.autoconfigure.domain.EntityScan
+import org.springframework.boot.runApplication
+import org.springframework.context.annotation.Import
+
+@SpringBootApplication
+@EntityScan(basePackages = ["com.hikari.regedist.entity"])
+@Import(PolicyConfiguration::class)
+class RegedistApplication
+
+fun main(args: Array<String>) {
+    runApplication<RegedistApplication>(*args)
+}

+ 21 - 0
src/main/kotlin/com/hikari/regedist/dao/PolicyDao.kt

@@ -0,0 +1,21 @@
+package com.hikari.regedist.dao
+
+import com.hikari.regedist.entity.Policy
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor
+
+interface PolicyDao : JpaRepository<Policy, String>, JpaSpecificationExecutor<Policy> {
+
+    fun findByName(name: String): Policy?
+
+    fun findByNameAndParentId(name: String, parentId: String): Policy?
+
+    fun findByPolicyKeyAndParentId(name: String, parentId: String): Policy?
+
+    fun findByNameAndParentIdIsNull(name: String): Policy?
+
+    fun findByPolicyKeyAndParentIdIsNull(name: String): Policy?
+
+    fun existsByParentId(pid: String): Boolean
+
+}

+ 13 - 0
src/main/kotlin/com/hikari/regedist/dao/PolicyItemDao.kt

@@ -0,0 +1,13 @@
+package com.hikari.regedist.dao
+
+import com.hikari.regedist.entity.PolicyItem
+import org.springframework.data.jpa.repository.JpaRepository
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor
+
+interface PolicyItemDao : JpaRepository<PolicyItem, String>, JpaSpecificationExecutor<PolicyItem> {
+
+    fun findByPolicyIdAndName(id: String, name: String): PolicyItem?
+
+    fun findByPolicyIdAndItemKey(id: String, key: String): PolicyItem?
+
+}

+ 19 - 0
src/main/kotlin/com/hikari/regedist/domain/PolicyForm.kt

@@ -0,0 +1,19 @@
+package com.hikari.regedist.domain
+
+import javax.validation.constraints.NotEmpty
+
+class PolicyForm {
+
+    @NotEmpty(message = "策略名不可为空")
+    var name: String? = null
+
+    var parentId: String? = null
+
+    @NotEmpty(message = "策略键不可为空")
+    var policyKey: String? = null
+
+    var description: String? = null
+
+    var orderNum: Int? = null
+
+}

+ 26 - 0
src/main/kotlin/com/hikari/regedist/domain/PolicyItemForm.kt

@@ -0,0 +1,26 @@
+package com.hikari.regedist.domain
+
+import javax.validation.constraints.NotEmpty
+
+class PolicyItemForm {
+
+    @NotEmpty(message = "策略项显示名称不可为空")
+    var name: String? = null
+
+    @NotEmpty(message = "策略id不可为空")
+    var policyId: String? = null
+
+    @NotEmpty(message = "策略项键不可为空")
+    var itemKey: String? = null
+
+    @NotEmpty(message = "策略项值不可为空")
+    var itemValue: String? = null
+
+    @NotEmpty(message = "策略项值类型不可为空")
+    var itemType: String? = null
+
+    var orderNum: Int? = null
+
+    var description: String? = null
+
+}

+ 10 - 0
src/main/kotlin/com/hikari/regedist/domain/PolicyResult.kt

@@ -0,0 +1,10 @@
+package com.hikari.regedist.domain
+
+class PolicyResult {
+
+    var data: Map<String, String?>? = null
+
+    var datatype: String? = null
+
+    var itemList: MutableList<Map<String, String?>> = mutableListOf()
+}

+ 35 - 0
src/main/kotlin/com/hikari/regedist/entity/Policy.kt

@@ -0,0 +1,35 @@
+package com.hikari.regedist.entity
+
+import gaf3.core.jpa.GafTimestamp
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Id
+import javax.persistence.Table
+
+@Entity
+@Table(name = "POLICY")
+class Policy : GafTimestamp() {
+
+    @Id
+    @Column(name = "ID")
+    var id: String? = null
+
+    @Column(name = "NAME")
+    var name: String? = null
+
+    @Column(name = "PARENT_ID")
+    var parentId: String? = null
+
+    @Column(name = "POLICY_KEY")
+    var policyKey: String? = null
+
+    @Column(name = "DESCRIPTION")
+    var description: String? = null
+
+    @Column(name = "ORDER_NUM")
+    var orderNum: Int? = null
+
+    @Column(name = "REMARK")
+    var remark: String? = null
+
+}

+ 41 - 0
src/main/kotlin/com/hikari/regedist/entity/PolicyItem.kt

@@ -0,0 +1,41 @@
+package com.hikari.regedist.entity
+
+import gaf3.core.jpa.GafTimestamp
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Id
+import javax.persistence.Table
+
+@Entity
+@Table(name = "POLICY_ITEM")
+class PolicyItem : GafTimestamp() {
+
+    @Id
+    @Column(name = "ID", length = 48)
+    var id: String? = null
+
+    @Column(name = "NAME", length = 255)
+    var name: String? = null
+
+    @Column(name = "POLICY_ID")
+    var policyId: String? = null
+
+    @Column(name = "ITEM_KEY", length = 48)
+    var itemKey: String? = null
+
+    @Column(name = "ITEM_VALUE", length = 255)
+    var itemValue: String? = null
+
+    @Column(name = "ITEM_TYPE", length = 48)
+    var itemType: String? = null
+
+    @Column(name = "ORDER_NUM", length = 10)
+    var orderNum: Int? = null
+
+    @Column(name = "DESCRIPTION", length = 255)
+    var description: String? = null
+
+    @Column(name = "REMARK", length = 255)
+    var remark: String? = null
+
+}

+ 118 - 0
src/main/kotlin/com/hikari/regedist/service/PolicyItemService.kt

@@ -0,0 +1,118 @@
+package com.hikari.regedist.service
+
+import com.hikari.regedist.dao.PolicyItemDao
+import com.hikari.regedist.domain.PolicyItemForm
+import com.hikari.regedist.entity.PolicyItem
+import com.hikari.regedist.support.BusinessError
+import com.hikari.regedist.support.BusinessError.DATA_NOT_EXIST
+import com.hikari.regedist.support.BusinessError.INSERT_DATA_ALLREADY_EXIST
+import gaf3.core.data.PageParam
+import gaf3.core.data.PagedData
+import gaf3.core.jpa.extension.findAll
+import gaf3.core.util.DataBeanHelper
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.stereotype.Service
+import org.springframework.validation.annotation.Validated
+import javax.validation.Valid
+
+@Service
+@Validated
+class PolicyItemService {
+
+    @Autowired
+    lateinit var policyItemDao: PolicyItemDao
+
+    /**
+     * 创建策略项信息
+     * @param form 策略项信息表单
+     * @return 添加到数据库的策略项信息
+     */
+    fun create(@Valid form: PolicyItemForm): PolicyItem {
+        return PolicyItem().apply {
+            check(form)
+            DataBeanHelper.Bean2Obj(form, this)
+        }.let {
+            policyItemDao.save(it)
+        }
+    }
+
+    /**
+     * 修改策略项信息
+     * @param id id
+     * @param form 策略项信息表单
+     * @return 修改后的策略项信息
+     */
+    fun update(id: String, @Valid form: PolicyItemForm): PolicyItem {
+        return policyItemDao.findByIdOrNull(id)?.apply {
+            check(form)
+            DataBeanHelper.Bean2Obj(form, this)
+        }?.also {
+            policyItemDao.save(it)
+        } ?: throw BusinessError(INSERT_DATA_ALLREADY_EXIST)
+    }
+
+    /**
+     * 根据条件查询策略项分页列表
+     * @param filter 查询条件
+     * @param pageParam 分页参数
+     * @return 策略项分页列表
+     */
+    fun find(filter: PolicyItem, pageParam: PageParam): PagedData<PolicyItem> {
+        val paged = policyItemDao.findAll(filter, pageParam)
+        return PagedData(data = paged.content, total = paged.totalElements.toInt())
+    }
+
+    /**
+     * 根据id查询策略项信息
+     */
+    fun findById(id: String): PolicyItem {
+        return policyItemDao.findByIdOrNull(id) ?: throw BusinessError(DATA_NOT_EXIST)
+    }
+
+    /**
+     * 根据id删除策略项
+     */
+    fun delete(id: String) {
+        policyItemDao.findByIdOrNull(id)?.let {
+            policyItemDao.delete(it)
+        } ?: throw BusinessError(DATA_NOT_EXIST)
+    }
+
+    /**
+     * 根据策略id和策略键查找策略项信息
+     */
+    fun findByPolicyIdAndKey(policyId: String, key: String): PolicyItem? {
+        return policyItemDao.findByPolicyIdAndItemKey(policyId, key)
+    }
+
+    /**
+     * 删除指定策略id下所有策略项
+     */
+    fun deleteByPolicyId(policyId: String) {
+        val filter = PolicyItem().apply {
+            this.policyId = policyId
+        }
+        var skip = 0
+        val limit = 100
+        var total: Int
+        do {
+            policyItemDao.findAll(filter, PageParam.of(skip, limit)).also {
+                skip += limit
+                total = it.totalElements.toInt()
+            }.content.forEach {
+                policyItemDao.delete(it)
+            }
+        } while (skip < total)
+    }
+
+    /**
+     * 检查添加或者修改的策略信息是否合理
+     * 同一策略id下,策略项名称,策略项键不可相同
+     */
+    private fun check(form: PolicyItemForm) {
+        if (policyItemDao.findByPolicyIdAndName(form.policyId!!, form.name!!) != null) throw BusinessError(INSERT_DATA_ALLREADY_EXIST)
+        if (policyItemDao.findByPolicyIdAndItemKey(form.policyId!!, form.itemKey!!) != null) throw BusinessError(INSERT_DATA_ALLREADY_EXIST)
+    }
+
+}

+ 148 - 0
src/main/kotlin/com/hikari/regedist/service/PolicyService.kt

@@ -0,0 +1,148 @@
+package com.hikari.regedist.service
+
+import com.hikari.regedist.PolicyConfiguration
+import com.hikari.regedist.dao.PolicyDao
+import com.hikari.regedist.domain.PolicyForm
+import com.hikari.regedist.domain.PolicyResult
+import com.hikari.regedist.entity.Policy
+import com.hikari.regedist.entity.PolicyItem
+import com.hikari.regedist.support.BusinessError
+import com.hikari.regedist.support.BusinessError.*
+import gaf3.core.data.PageParam
+import gaf3.core.data.PagedData
+import gaf3.core.jpa.extension.findAll
+import gaf3.core.util.DataBeanHelper
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.stereotype.Service
+import org.springframework.util.Assert
+import org.springframework.validation.annotation.Validated
+import javax.validation.Valid
+
+@Service
+@Validated
+class PolicyService(val config: PolicyConfiguration) {
+
+    @Autowired
+    lateinit var policyDao: PolicyDao
+
+    @Autowired
+    lateinit var policyItemService: PolicyItemService
+
+    /**
+     * 创建策略信息
+     * @param form 策略信息表单
+     * @return 添加的策略对象
+     */
+    fun create(@Valid form: PolicyForm): Policy {
+        return Policy().apply {
+            DataBeanHelper.Bean2Obj(form, this)
+        }.also {
+            check(it)
+            policyDao.save(it)
+        }
+    }
+
+    /**
+     * 更新策略
+     * @param id id
+     * @param form 策略信息表单
+     */
+    fun update(id: String, @Valid form: PolicyForm): Policy {
+        return policyDao.findByIdOrNull(id)?.also {
+            DataBeanHelper.Bean2Obj(form, it)
+            policyDao.save(it)
+        } ?: throw BusinessError(DATA_NOT_EXIST)
+    }
+
+    /**
+     * 查询策略列表
+     * @param filter 条件筛选
+     * @param pageParam 分页参数
+     * @return 分页策略列表信息
+     */
+    fun find(filter: Policy, pageParam: PageParam): PagedData<Policy> {
+        val paged = policyDao.findAll(filter, pageParam)
+        return PagedData(data = paged.content, total = paged.totalElements.toInt())
+    }
+
+
+    /**
+     * 根据id获取策略对象
+     */
+    fun findById(id: String): Policy {
+        return policyDao.findByIdOrNull(id) ?: throw BusinessError(DATA_NOT_EXIST)
+    }
+
+    /**
+     * 根据id删除策略对象
+     */
+    fun delete(id: String) {
+        policyDao.findByIdOrNull(id)?.let {
+            config.deletePolicy!!.run {
+                if (this.toLowerCase() == "hard")
+                    deleteAll(it.id)
+                else {
+                    if (policyDao.existsByParentId(it.id!!)) throw BusinessError(DEL_DATA_HASCHILDREN)
+                }
+            }
+            if (config.cascadeDelPolicyItem) {
+                policyItemService.deleteByPolicyId(it.id!!)
+            } else {
+                throw BusinessError(DEL_DATA_HASCHILDREN, "被删除策略含有策略项,无法删除")
+            }
+            policyDao.delete(it)
+        } ?: throw BusinessError(DATA_NOT_EXIST)
+    }
+
+    /**
+     * 使用递归删除策略项下所有子项
+     */
+    fun deleteAll(pid: String?) {
+        var skip = 0
+        val limit = 100
+        do {
+            val paged = Policy().apply {
+                this.parentId = pid
+            }.let {
+                find(it, pageParam = PageParam.of(skip, limit))
+            }
+            paged.data?.forEach {
+                deleteAll(it.id)
+                if (config.cascadeDelPolicyItem) {
+                    policyItemService.deleteByPolicyId(it.id!!)
+                } else {
+                    throw BusinessError(DEL_DATA_HASCHILDREN, "被删除策略含有策略项,无法删除")
+                }
+                policyDao.delete(it)
+            }
+            skip += limit
+        } while (skip < paged.total)
+    }
+
+    /**
+     * 根据策略键和parentId获取策略
+     */
+    fun findByKeyAndPid(key: String, pid: String?): Policy? {
+        Assert.hasText(key, "姓名不可为空")
+        return pid?.let {
+            policyDao.findByPolicyKeyAndParentId(key, it)
+        } ?: policyDao.findByPolicyKeyAndParentIdIsNull(key)
+    }
+
+    /**
+     * 对添加或修改的策略信息进行校验。
+     * 校验规则:同一级别名称,策略键不可相同
+     */
+    private fun check(policy: Policy) {
+        //判断同一级别策略名称、策略键不能相同
+        if (policy.parentId == null) {
+            if (policyDao.findByNameAndParentIdIsNull(policy.name!!) != null) throw BusinessError(INSERT_DATA_ALLREADY_EXIST, "策略同一级别名称不可相同")
+            if (policyDao.findByPolicyKeyAndParentIdIsNull(policy.name!!) != null) throw BusinessError(INSERT_DATA_ALLREADY_EXIST, "策略同一级别键不可相同")
+        } else {
+            if (policyDao.findByNameAndParentId(policy.name!!, policy.parentId!!) != null) throw BusinessError(INSERT_DATA_ALLREADY_EXIST, "策略同一级别名称不可相同")
+            if (policyDao.findByPolicyKeyAndParentId(policy.name!!, policy.parentId!!) != null) throw BusinessError(INSERT_DATA_ALLREADY_EXIST, "策略同一级别键不可相同")
+        }
+    }
+
+}

+ 84 - 0
src/main/kotlin/com/hikari/regedist/service/QueryByPathService.kt

@@ -0,0 +1,84 @@
+package com.hikari.regedist.service
+
+import com.hikari.regedist.PolicyConfiguration
+import com.hikari.regedist.entity.PolicyItem
+import com.hikari.regedist.support.BusinessError
+import gaf3.core.data.PageParam
+import gaf3.core.util.DataBeanHelper
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+@Service
+class QueryByPathService(val config: PolicyConfiguration) {
+
+    @Autowired
+    lateinit var policyService: PolicyService
+
+    @Autowired
+    lateinit var policyItemService: PolicyItemService
+
+    /**
+     * 根据路径参数获取策略下的所有策略项或策略项的值
+     * @param path 路径参数,默认以'/'分割,路径参数最后一项仍为策略键,返回该策略下所有策略项
+     *             若最后一项为策略项键,则返回该策略项的值
+     */
+    fun findByKeyPath(path: String): Any {
+        var keyPath = path
+        if (path.startsWith(config.separator)) keyPath = path.substring(1)
+        var pid: String? = null
+        var result: Any? = null
+        val keys = keyPath.split(config.separator).toTypedArray()
+        for (i in keys.indices) {
+            val policy = policyService.findByKeyAndPid(keys[i], pid)
+            policy?.run {
+                pid = this.id
+            }
+            if (i == keys.lastIndex && policy != null) {
+                result = mutableListOf<Map<String, String?>>()
+                var skip = 0
+                val limit = 100
+                var total: Int
+                do {
+                    val paged = PolicyItem().apply {
+                        this.policyId = policy.id
+                    }.run {
+                        policyItemService.find(this, pageParam = PageParam.of(skip, limit))
+                    }
+                    paged.data?.forEach {
+                        (result as MutableList<Map<String, String?>>).add(
+                                mapOf(
+                                        "id" to it.id,
+                                        "name" to it.name,
+                                        "key" to it.itemKey,
+                                        "value" to it.itemValue,
+                                        "type" to it.itemType,
+                                        "policyId" to it.policyId,
+                                        "orderNum" to it.orderNum?.toString(),
+                                        "description" to it.description
+                                ))
+                    }
+                    total = paged.total
+                    skip += limit
+                } while (skip < total)
+            }
+            if (i == keys.lastIndex && policy == null && i != 0) {
+                policyItemService.findByPolicyIdAndKey(pid!!, keys[keys.lastIndex])?.let {
+                    result = mapOf(
+                            "id" to it.id,
+                            "name" to it.name,
+                            "value" to it.itemValue,
+                            "key" to it.itemKey,
+                            "type" to it.itemType,
+                            "policyId" to it.policyId,
+                            "orderNum" to it.orderNum?.toString(),
+                            "description" to it.description
+                    )
+                }
+            }
+            if (i != keys.lastIndex && policy == null)
+                break
+        }
+        if (result == null) throw BusinessError(BusinessError.DATA_NOT_EXIST)
+        return result as Any
+    }
+}

+ 10 - 0
src/main/kotlin/com/hikari/regedist/support/ErrorResult.kt

@@ -0,0 +1,10 @@
+package com.hikari.regedist.support
+
+class ErrorResult {
+
+    var errCode: Int = 0
+
+    var errMsg: String? = null
+
+    var detils: String? = null
+}

+ 3 - 0
src/main/kotlin/com/hikari/regedist/support/PagedData.kt

@@ -0,0 +1,3 @@
+package com.hikari.regedist.support
+
+class PagedData<T>(var data: MutableList<T>, val total: Int)

+ 38 - 0
src/main/kotlin/com/hikari/regedist/support/WebControllerAdvice.kt

@@ -0,0 +1,38 @@
+package com.hikari.regedist.support
+
+import org.slf4j.LoggerFactory
+import org.springframework.http.HttpStatus
+import org.springframework.http.server.reactive.ServerHttpResponse
+import org.springframework.web.bind.annotation.ExceptionHandler
+import org.springframework.web.bind.annotation.RestControllerAdvice
+import javax.validation.ConstraintViolationException
+
+@RestControllerAdvice
+class WebControllerAdvice {
+
+    @ExceptionHandler(ConstraintViolationException::class)
+    fun validationExceptionHandler(ex: ConstraintViolationException,response:ServerHttpResponse): ErrorResult {
+        log.debug("[exception]校验异常:${ex.localizedMessage}")
+        response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
+        return ErrorResult().apply {
+            this.errCode = -1
+            this.errMsg = ex.message?.substring(ex.message!!.indexOf(":") + 2) ?: ""
+            this.detils = ex.localizedMessage
+        }
+    }
+
+    @ExceptionHandler(BusinessError::class)
+    fun businessErrorHandler(ex: BusinessError,response:ServerHttpResponse):ErrorResult{
+        log.debug("[exception]业务异常:${ex.errMsg}")
+        response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR
+        return ErrorResult().apply {
+            this.errCode = ex.errCode
+            this.errMsg = ex.errMsg
+            this.detils = ex.details
+        }
+    }
+
+    companion object {
+        val log = LoggerFactory.getLogger(WebControllerAdvice::class.java)
+    }
+}

+ 18 - 0
src/main/resources/application.yml

@@ -0,0 +1,18 @@
+server:
+  port: 8080
+spring:
+  datasource:
+    username: root
+    password: 123456
+    url: jdbc:mysql://127.0.0.1:3306/policy?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
+    driver-class-name: com.mysql.cj.jdbc.Driver
+  jpa:
+    hibernate:
+      ddl-auto: none
+    database-platform: org.hibernate.dialect.MySQL8Dialect
+    show-sql: true
+logging.level.com.hikari.regedist.*: DEBUG
+hikari:
+  policy:
+    separator: /
+    deletePolicy: soft

+ 13 - 0
src/test/kotlin/com/hikari/regedist/RegedistApplicationTests.kt

@@ -0,0 +1,13 @@
+package com.hikari.regedist
+
+import org.junit.jupiter.api.Test
+import org.springframework.boot.test.context.SpringBootTest
+
+@SpringBootTest
+class RegedistApplicationTests {
+
+    @Test
+    fun contextLoads() {
+    }
+
+}