dygapp 4 anos atrás
commit
2e068d6eb1
60 arquivos alterados com 1823 adições e 0 exclusões
  1. 32 0
      .gitignore
  2. 13 0
      README.md
  3. 62 0
      apps/all-in-one/build.gradle.kts
  4. 6 0
      apps/all-in-one/script/init-mysql.bat
  5. 1 0
      apps/all-in-one/script/init-mysql.sh
  6. 6 0
      apps/all-in-one/script/init-oscar.bat
  7. 1 0
      apps/all-in-one/script/init-oscar.sh
  8. 5 0
      apps/all-in-one/script/init.bat
  9. 6 0
      apps/all-in-one/script/start-mysql.bat
  10. 1 0
      apps/all-in-one/script/start-mysql.sh
  11. 6 0
      apps/all-in-one/script/start-oscar.bat
  12. 1 0
      apps/all-in-one/script/start-oscar.sh
  13. 5 0
      apps/all-in-one/script/start.bat
  14. 65 0
      apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/AllInOneApplication.kt
  15. 25 0
      apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/StatusControllerAdvice.kt
  16. 12 0
      apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/api/TokenService.kt
  17. 19 0
      apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/bean/FactoryBean.kt
  18. 11 0
      apps/all-in-one/src/main/resources/application-feign.yml
  19. 25 0
      apps/all-in-one/src/main/resources/application-mysql.yml
  20. 39 0
      apps/all-in-one/src/main/resources/application-routes.yml
  21. 57 0
      apps/all-in-one/src/main/resources/application.yml
  22. 3 0
      apps/build.gradle.kts
  23. 121 0
      build.gradle.kts
  24. 0 0
      git
  25. 7 0
      gradle.properties
  26. BIN
      gradle/wrapper/gradle-wrapper.jar
  27. 5 0
      gradle/wrapper/gradle-wrapper.properties
  28. 185 0
      gradlew
  29. 104 0
      gradlew.bat
  30. 21 0
      platform/build.gradle.kts
  31. 1 0
      publish.sh
  32. 140 0
      services/build.gradle.kts
  33. 23 0
      services/service-all/src/main/kotlin/gaf3/demo/services/AllServicesApplication.kt
  34. 17 0
      services/service-all/src/main/kotlin/gaf3/demo/services/AllServicesConfiguration.kt
  35. 28 0
      services/service-all/src/main/resources/application.yml
  36. 3 0
      services/service-all/src/main/resources/data-mysql.sql
  37. 16 0
      services/service-api/src/main/kotlin/gaf3/demo/services/ServiceApiApplication.kt
  38. 14 0
      services/service-api/src/main/kotlin/gaf3/demo/services/api/LogService.kt
  39. 9 0
      services/service-api/src/main/kotlin/gaf3/demo/services/api/domain/LogData.kt
  40. 29 0
      services/service-api/src/main/kotlin/gaf3/demo/services/api/domain/LogForm.kt
  41. 21 0
      services/service-api/src/main/resources/application.yml
  42. 40 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/ServiceUserApplication.kt
  43. 45 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/UserServiceController.kt
  44. 8 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/dao/GafUserInfoDAO.kt
  45. 12 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/domain/CreateForm.kt
  46. 13 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/domain/UpdateForm.kt
  47. 49 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/entity/GafUserInfo.kt
  48. 86 0
      services/service-crud/src/main/kotlin/gaf3/demo/services/crud/service/UserService.kt
  49. 18 0
      services/service-crud/src/main/resources/application.yml
  50. 28 0
      services/service-nodb/build.gradle.kts
  51. 42 0
      services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/DemoVerifyConfiguration.kt
  52. 24 0
      services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/ServiceVerifyApplication.kt
  53. 25 0
      services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/VerifyCodeController.kt
  54. 47 0
      services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/service/VerifyCodeService.kt
  55. 127 0
      services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/util/VerifyCodeHelper.kt
  56. 22 0
      services/service-nodb/src/main/resources/application.yml
  57. 7 0
      settings.gradle.kts
  58. 68 0
      shared/build.gradle.kts
  59. 4 0
      shared/util/build.gradle.kts
  60. 13 0
      shared/util/src/main/java/gaf3/demo/util/AssertEx.java

+ 32 - 0
.gitignore

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

+ 13 - 0
README.md

@@ -0,0 +1,13 @@
+## backend-template
+后端服务工程模板
+### apps
+发布应用 
+### services
+服务库 
+### shared
+共享库
+### platform
+公共依赖
+### libs
+外部jar包
+

+ 62 - 0
apps/all-in-one/build.gradle.kts

@@ -0,0 +1,62 @@
+group = rootProject.group
+version = rootProject.version
+
+extra["jjwtVersion"] = "0.10.5"
+
+plugins {
+    id("java")
+    id("io.spring.dependency-management")
+    id("org.springframework.boot")
+    kotlin("jvm")
+    kotlin("plugin.spring")
+    kotlin("plugin.jpa")
+}
+
+dependencies {
+    api(platform(project(":platform")))
+    implementation(kotlin("reflect"))
+    implementation(kotlin("stdlib-jdk8"))
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive")
+    implementation("commons-io:commons-io")
+    implementation("org.apache.commons:commons-dbcp2")
+    implementation("cc-lotus.gaf3:gaf-core-shared")
+    implementation("cc-lotus.gaf3:gaf-core-gateway")
+    implementation("com.alibaba:fastjson")
+    implementation("io.jsonwebtoken:jjwt-api")
+    implementation("io.jsonwebtoken:jjwt-impl")
+    implementation("io.jsonwebtoken:jjwt-jackson")
+    implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
+    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
+    implementation("org.springframework.boot:spring-boot-starter-data-redis")
+    implementation("org.springframework.boot:spring-boot-starter-webflux")
+    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
+    implementation("org.springframework.boot:spring-boot-configuration-processor")
+    implementation("org.springframework.cloud:spring-cloud-starter-gateway")
+    implementation("org.springframework.cloud:spring-cloud-gateway-webflux")
+    implementation("org.springframework.cloud:spring-cloud-starter-netflix-ribbon")
+    implementation("io.github.openfeign:feign-jackson:10.9")
+    implementation(project(path = ":services:service-all", configuration = "lib"))
+    implementation(project(path = ":services:service-crud", configuration = "lib"))
+    implementation(project(path = ":services:service-nodb", configuration = "lib"))
+    implementation(project(path = ":shared"))
+    runtimeOnly("mysql:mysql-connector-java")
+    runtimeOnly(fileTree("$rootDir/libs") { include("*.jar") })
+}
+
+dependencyManagement {
+    imports {
+        mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}")
+    }
+}
+
+tasks {
+    processResources {
+        filesMatching("application-services.yml") {
+            // expand("db.user" to dbUser)
+            expand(project.properties)
+        }
+    }
+}
+
+

+ 6 - 0
apps/all-in-one/script/init-mysql.bat

@@ -0,0 +1,6 @@
+@ECHO OFF
+@CHCP 65001
+@ECHO STARTUP App
+FOR %%F IN (.\*.jar) DO SET MAIN_PACKAGE=%%F
+java.exe -Dfile.encoding="UTF-8" -jar %MAIN_PACKAGE% --spring.profiles.active=init,mysql
+pause

+ 1 - 0
apps/all-in-one/script/init-mysql.sh

@@ -0,0 +1 @@
+java -Dfile.encoding="UTF-8" -jar xms-all-in-one-1.0.325.jar --spring.profiles.active=init,mysql

+ 6 - 0
apps/all-in-one/script/init-oscar.bat

@@ -0,0 +1,6 @@
+@ECHO OFF
+@CHCP 65001
+@ECHO STARTUP App
+FOR %%F IN (.\*.jar) DO SET MAIN_PACKAGE=%%F
+java.exe -Dfile.encoding="UTF-8" -jar %MAIN_PACKAGE% --spring.profiles.active=init,oscar
+pause

+ 1 - 0
apps/all-in-one/script/init-oscar.sh

@@ -0,0 +1 @@
+java -Dfile.encoding="UTF-8" -jar xms-all-in-one-1.0.325.jar --spring.profiles.active=init,oscar

+ 5 - 0
apps/all-in-one/script/init.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+@ECHO STARTUP App
+FOR %%F IN (.\*.jar) DO SET MAIN_PACKAGE=%%F
+java.exe -Dfile.encoding="UTF-8" -jar %MAIN_PACKAGE% --spring.profiles.active=init --spring.jpa.hibernate.ddl-auto=update
+pause

+ 6 - 0
apps/all-in-one/script/start-mysql.bat

@@ -0,0 +1,6 @@
+@ECHO OFF
+@CHCP 65001
+@ECHO STARTUP App
+FOR %%F IN (.\*.jar) DO SET MAIN_PACKAGE=%%F
+java -cp %MAIN_PACKAGE% -Dloader.path=lib -Dfile.encoding="UTF-8" -Dspring.profiles.active=mysql org.springframework.boot.loader.PropertiesLauncher
+pause

+ 1 - 0
apps/all-in-one/script/start-mysql.sh

@@ -0,0 +1 @@
+java -cp xms-all-in-one-1.0.325.jar -Dloader.path=lib -Dfile.encoding="UTF-8" -Dspring.profiles.active=mysql org.springframework.boot.loader.PropertiesLauncher

+ 6 - 0
apps/all-in-one/script/start-oscar.bat

@@ -0,0 +1,6 @@
+@ECHO OFF
+@CHCP 65001
+@ECHO STARTUP App
+FOR %%F IN (.\*.jar) DO SET MAIN_PACKAGE=%%F
+java -cp %MAIN_PACKAGE% -Dloader.path=lib -Dfile.encoding="UTF-8" -Dspring.profiles.active=oscar org.springframework.boot.loader.PropertiesLauncher
+pause

+ 1 - 0
apps/all-in-one/script/start-oscar.sh

@@ -0,0 +1 @@
+java -cp xms-all-in-one-1.0.325.jar -Dloader.path=lib -Dfile.encoding="UTF-8" -Dspring.profiles.active=oscar org.springframework.boot.loader.PropertiesLauncher

+ 5 - 0
apps/all-in-one/script/start.bat

@@ -0,0 +1,5 @@
+@ECHO OFF
+@ECHO STARTUP App
+FOR %%F IN (.\*.jar) DO SET MAIN_PACKAGE=%%F
+java.exe -Dfile.encoding="UTF-8" -jar %MAIN_PACKAGE%
+pause

+ 65 - 0
apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/AllInOneApplication.kt

@@ -0,0 +1,65 @@
+package gaf3.demo.allinone
+
+import gaf3.core.cloud.GafCloudConfiguration
+import gaf3.core.gateway.GatewayController
+import gaf3.core.gateway.filter.factory.JwtParserGatewayFilterFactory
+import gaf3.core.jpa.GafJpaConfiguration
+import gaf3.core.services.GafCoreServicesConfiguration
+import gaf3.demo.services.AllServicesConfiguration
+import org.slf4j.LoggerFactory
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils
+import org.springframework.cloud.openfeign.EnableFeignClients
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.context.annotation.Import
+import org.springframework.core.annotation.Order
+import org.springframework.http.HttpStatus
+import org.springframework.stereotype.Controller
+import org.springframework.web.server.WebFilter
+
+
+@SpringBootApplication
+@Configuration
+@Controller
+@Import(GafCloudConfiguration::class, GafJpaConfiguration::class, AllServicesConfiguration::class)
+@EnableFeignClients
+class AllInOneApplication {
+
+    @Bean
+    fun statusControllerAdvice(): StatusControllerAdvice {
+        return StatusControllerAdvice()
+    }
+
+    @Bean
+    fun gatewayController(): GatewayController {
+        return GatewayController()
+    }
+
+    @Bean
+    @Order(-1)
+    fun apiAccessFilter(): WebFilter {
+        return WebFilter { exchange, chain ->
+            log.debug("[ApiAccess] start...")
+            val path = exchange.request.path.value()
+            log.debug("[ApiAccess] path : {}", path)
+            if(path.startsWith("/xms")
+                    || path.startsWith("/gaf")) {
+                ServerWebExchangeUtils.setResponseStatus(exchange, HttpStatus.FORBIDDEN)
+                exchange.response.setComplete()
+            } else {
+                chain.filter(exchange.mutate().build())
+            }
+        }
+    }
+
+    companion object {
+        internal val log = LoggerFactory.getLogger(AllInOneApplication::class.java)
+    }
+}
+
+fun main(args: Array<String>) {
+    runApplication<AllInOneApplication>(*args)
+    JwtParserGatewayFilterFactory.doInit()
+}

+ 25 - 0
apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/StatusControllerAdvice.kt

@@ -0,0 +1,25 @@
+package gaf3.demo.allinone
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.springframework.core.annotation.Order
+import org.springframework.http.server.reactive.ServerHttpRequest
+import org.springframework.web.bind.annotation.ExceptionHandler
+import org.springframework.web.bind.annotation.ResponseBody
+import org.springframework.web.bind.annotation.RestControllerAdvice
+import org.springframework.web.server.ResponseStatusException
+
+
+@RestControllerAdvice
+class StatusControllerAdvice {
+    val log: Logger = LoggerFactory.getLogger(StatusControllerAdvice::class.java)
+
+    @ExceptionHandler(ResponseStatusException::class)
+    @ResponseBody
+    @Order(-1)
+    fun handleError(ex: ResponseStatusException, req: ServerHttpRequest) {
+        log.warn("ResponseStatusException:{}-{}", ex.status, ex.reason)
+        log.debug("handleError", ex)
+        throw ex
+    }
+}

+ 12 - 0
apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/api/TokenService.kt

@@ -0,0 +1,12 @@
+package gaf3.demo.allinone.api
+
+import gaf3.core.data.JsonResult
+import org.springframework.cloud.openfeign.FeignClient
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PathVariable
+
+@FeignClient(name = "token-service", url = "http://localhost:19090" )
+interface TokenService {
+    @GetMapping(path = ["/api/agent/token/{state}"], consumes = ["application/json"])
+    fun fetch(@PathVariable("state") state: String): JsonResult<String>
+}

+ 19 - 0
apps/all-in-one/src/main/kotlin/gaf3/demo/allinone/bean/FactoryBean.kt

@@ -0,0 +1,19 @@
+package gaf3.demo.allinone.bean
+
+import gaf3.core.gateway.filter.factory.ForwardGatewayFilterFactory
+import gaf3.core.gateway.handler.predicate.JwtRoutePredicateFactory
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+
+@Configuration
+class FactoryBean {
+    @Bean
+    fun forwardGatewayFilterFactory(): ForwardGatewayFilterFactory {
+        return ForwardGatewayFilterFactory()
+    }
+
+    @Bean
+    fun jwtRoutePredicateFactory(): JwtRoutePredicateFactory {
+        return JwtRoutePredicateFactory()
+    }
+}

+ 11 - 0
apps/all-in-one/src/main/resources/application-feign.yml

@@ -0,0 +1,11 @@
+token-service:
+  ribbon:
+    listOfServers: http://localhost:19090
+
+feign:
+  client:
+    config:
+      default:
+        errorDecoder: gaf3.core.cloud.feign.GafErrorDecoder
+        encoder: feign.jackson.JacksonEncoder
+        decoder: feign.jackson.JacksonDecoder

+ 25 - 0
apps/all-in-one/src/main/resources/application-mysql.yml

@@ -0,0 +1,25 @@
+# 数据库配置
+---
+spring:
+  datasource:
+    username: root
+    password:
+    url: jdbc:mysql://127.0.0.1:13306/xms?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    platform: mysql
+#    schema:
+#      - classpath:/sql/drop.sql
+#      - classpath:/sql/schema-mysql.sql
+#      - classpath:/sql/trigger-mysql.sql
+#      - classpath:/sql/gaf-trigger-mysql.sql
+#    data:
+#      - classpath:/sql/data-mysql.sql
+#      - classpath:/sql/dict-mysql.sql
+  jpa:
+    database-platform: org.hibernate.dialect.MySQL8Dialect
+    show-sql: true
+    hibernate:
+      naming:
+        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+      ddl-auto: none

+ 39 - 0
apps/all-in-one/src/main/resources/application-routes.yml

@@ -0,0 +1,39 @@
+# 默认路由规则
+---
+
+spring:
+  profiles: routes
+  cloud:
+    gateway:
+      default-filters:
+        #- PrefixPath=/httpbin
+        #- JwtParser=.*/login
+        - AddResponseHeader=Cache-Control, no-cache
+        - AddResponseHeader=Pragma, no-cache
+        - AddResponseHeader=Expires, -1
+        # - RewritePath=/api/gaf/(?<segment>.*), /gaf/$\{segment}
+
+      routes:
+        # 验证码接口
+        - id: gaf_verify
+          uri: ${uri.gaf}
+          predicates:
+            - Path=/api/gaf/verify/**
+          filters:
+            - RewritePath=/api/gaf/(?<segment>.*), /gaf/$\{segment}
+            - Forward
+        # == 默认处理 ==
+        - id: index
+          uri: forward:///index # default for unauthorized
+          predicates:
+            - Path=/
+          filters:
+            - RedirectTo=302, /admin/index.html
+        - id: api_default
+          uri: forward:///401 # default for unauthorized
+          order: 1000
+          predicates:
+            - Path=/api/**
+            #- Method=POST
+          filters:
+            - SetStatus=401

+ 57 - 0
apps/all-in-one/src/main/resources/application.yml

@@ -0,0 +1,57 @@
+# 全局配置
+jwt.secret: &jwtSecret "GafJwtSecret!@#"
+api.host: 127.0.0.1
+uri:
+  gaf: http://${api.host}:8001
+  xms: http://${api.host}:8108
+
+gaf:
+  web:
+    spa:
+      index-paths:
+        - /admin/gaf
+        - /admin/xms
+        - /admin
+        - /register
+
+server:
+  port: 8080
+spring:
+  profiles:
+    include: routes, feign
+    active: local, mysql
+  http.encoding.charset: utf-8
+  resources:
+    static-locations: file:public/,file:dist/public/,classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
+  main:
+    allow-bean-definition-overriding: true
+  datasource:
+    type: org.apache.commons.dbcp2.BasicDataSource
+    dbcp2:
+      initial-size: 5
+      max-total: 20
+      max-idle: 10
+      min-idle: 5
+
+
+---
+spring:
+  profiles: local
+
+gaf:
+  menu:
+    items-file: classpath:/menu/items-lite.json #default: classpath:/menu/tree.json
+    menus-file: classpath:/menu/menus.json   #default: classpath:/menu/bar.json
+
+#logging.level.gaf3.core.*: DEBUG
+logging.level.jit.xms.*: DEBUG
+#logging.level.org.hibernate.type.descriptor.sql.BasicBinder: trace
+#debug: true
+
+---
+spring:
+  profiles: init
+  datasource:
+    initialization-mode: always
+    continue-on-error: false
+    separator: $$

+ 3 - 0
apps/build.gradle.kts

@@ -0,0 +1,3 @@
+tasks.forEach {
+    it.enabled = false
+}

+ 121 - 0
build.gradle.kts

@@ -0,0 +1,121 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.springframework.boot.gradle.tasks.bundling.BootJar
+
+group = "gaf3-demo"
+version = "2.3.0"
+extra["springCloudVersion"] = "Hoxton.SR3"
+extra["springBootVersion"] = "2.3.0.RELEASE"
+extra["gafVersion"] = "3.0.515"
+extra["fastjsonVersion"] = "1.2.66"
+extra["jjwtVersion"] = "0.10.5"
+
+private val repoConf: String = System.getProperty("repoPath") ?: "/var/repo"
+val repoPath: String = file("$rootDir").toPath().root.resolve(repoConf).toString()
+
+plugins {
+    id("java")
+    id("maven-publish")
+    id("io.spring.dependency-management") version "1.0.9.RELEASE" apply false
+    id("org.springframework.boot") version "2.2.6.RELEASE" apply false
+    id("org.jetbrains.dokka") version "0.10.1"
+    kotlin("jvm") version "1.3.72" apply false
+    kotlin("plugin.spring") version "1.3.72" apply false
+    kotlin("plugin.jpa") version "1.3.72" apply false
+}
+
+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("http://maven.cc-lotus.info/repository/maven-public/")
+    }
+    // mavenCentral()
+}
+
+configure(subprojects.filter { it.name != "platform" }) {
+    apply(plugin = "java")
+    apply(plugin = "maven-publish")
+    apply(plugin = "org.jetbrains.kotlin.jvm")
+    apply(plugin = "org.jetbrains.dokka")
+
+    java {
+        disableAutoTargetJvm()
+        withSourcesJar()
+        // withJavadocJar()
+    }
+
+    tasks.withType<JavaCompile> {
+        options.encoding = "UTF-8"
+        sourceCompatibility = "1.8"
+        targetCompatibility = "1.8"
+    }
+
+    tasks.withType<KotlinCompile> {
+        kotlinOptions {
+            freeCompilerArgs = listOf("-Xjsr305=strict")
+            jvmTarget = "1.8"
+        }
+    }
+
+    tasks.withType<Javadoc> {
+        options.encoding = "UTF-8"
+    }
+
+    repositories {
+        maven {
+            name = "localRepo"
+            url = uri("file://$repoPath")
+        }
+        maven {
+            name = "cc-lotus"
+            url = uri("http://maven.cc-lotus.info/repository/maven-public/")
+        }
+        // 阿里云镜像
+        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") }
+        // mavenCentral()
+    }
+    publishing {
+        repositories {
+            maven {
+                name = "localRepo"
+                url = uri("file://$repoPath")
+            }
+        }
+    }
+}
+
+task<Copy>("dist") {
+    into("$buildDir/dist")
+    project(":services:service-all") {
+        from(tasks.withType<Jar>())
+    }
+    project(":apps:all-in-one") {
+        from(tasks.withType<Jar>())
+    }
+}
+
+tasks {
+//    processResources {
+//         expand(project.properties)
+//    }
+    val dokka by getting(org.jetbrains.dokka.gradle.DokkaTask::class) {
+        outputFormat = "html"
+        outputDirectory = "$buildDir/dokka"
+    }
+}
+
+//task("printRepo") {
+//    println(org.jetbrains.kotlin.org.jline.utils.OSUtils.IS_WINDOWS)
+//    println(repoPath)
+//}

+ 0 - 0
git


+ 7 - 0
gradle.properties

@@ -0,0 +1,7 @@
+dbDialect = org.hibernate.dialect.MySQL5Dialect
+# dbUrl = jdbc:mysql://localhost:3306/gaf3?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
+dbUrl = jdbc:mysql://172.17.116.7:3308/xms?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
+dbUser = root
+dbPwd = 123456
+dbDriver = com.mysql.cj.jdbc.Driver
+systemProp.repoPath = /var/repo

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.4-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" "$@"

+ 104 - 0
gradlew.bat

@@ -0,0 +1,104 @@
+@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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+: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 %CMD_LINE_ARGS%
+
+: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

+ 21 - 0
platform/build.gradle.kts

@@ -0,0 +1,21 @@
+plugins {
+    `java-platform`
+}
+javaPlatform.allowDependencies()
+
+dependencies {
+    // The platform declares constraints on all components that
+    api("org.bouncycastle:bcprov-jdk15on:1.64")
+    api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5")
+    api("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.3.5")
+    api("org.apache.commons:commons-dbcp2:2.7.0")
+    api("commons-io:commons-io:2.6")
+    api("net.sourceforge.jexcelapi:jxl:2.6.12")
+    api("com.alibaba:fastjson:${property("fastjsonVersion")}")
+    api("io.jsonwebtoken:jjwt-api:${property("jjwtVersion")}")
+    api("io.jsonwebtoken:jjwt-impl:${property("jjwtVersion")}")
+    api("io.jsonwebtoken:jjwt-jackson:${property("jjwtVersion")}")
+    api("cc-lotus.gaf3:gaf-core-shared:${property("gafVersion")}")
+    api("cc-lotus.gaf3:gaf-core-services:${property("gafVersion")}")
+    api("cc-lotus.gaf3:gaf-core-gateway:${property("gafVersion")}")
+}

+ 1 - 0
publish.sh

@@ -0,0 +1 @@
+./gradlew publishAllPublicationsToLocalRepoRepository

+ 140 - 0
services/build.gradle.kts

@@ -0,0 +1,140 @@
+group = rootProject.group
+version = rootProject.version
+
+plugins {
+    id("java")
+    id("io.spring.dependency-management")
+    id("org.springframework.boot") apply false
+    kotlin("jvm")
+    kotlin("plugin.spring")
+    kotlin("plugin.jpa") apply false
+}
+
+java {
+    disableAutoTargetJvm()
+    withSourcesJar()
+    // withJavadocJar()
+}
+
+repositories {
+    maven {
+        name = "aliyun"
+        url = uri("http://maven.aliyun.com/nexus/content/groups/public/")
+    }
+    // mavenCentral()
+}
+
+allprojects {
+    apply(plugin = "java")
+
+    tasks {
+        processResources {
+            // expand("db.user" to dbUser)
+            // expand 在处理奇数个中文的时候会出现乱码情况
+            // 所以这里只处理application.yml,忽略其他资源文件,避免处理后产生乱码
+            filesMatching("application.yml") {
+                expand(project.properties)
+            }
+            // expand(project.properties)
+        }
+    }
+
+}
+
+subprojects {
+    apply(plugin = "org.springframework.boot")
+    apply(plugin = "io.spring.dependency-management")
+    apply(plugin = "org.jetbrains.kotlin.jvm")
+    apply(plugin = "org.jetbrains.kotlin.plugin.spring")
+    apply(plugin = "org.jetbrains.kotlin.plugin.jpa")
+
+    dependencies {
+        api(platform(project(":platform")))
+        api(kotlin("reflect"))
+        api(kotlin("stdlib-jdk8"))
+        implementation("org.springframework.boot:spring-boot-starter-webflux")
+        implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
+        implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
+        implementation("org.springframework.boot:spring-boot-configuration-processor")
+        implementation("com.alibaba:fastjson")
+        implementation("commons-io:commons-io")
+        implementation("io.jsonwebtoken:jjwt-api")
+        implementation("io.jsonwebtoken:jjwt-impl")
+        implementation("io.jsonwebtoken:jjwt-jackson")
+        runtimeOnly(fileTree("$rootDir/libs") { include("*.jar") })
+    }
+    dependencyManagement {
+        imports {
+            mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}")
+        }
+    }
+
+    tasks.register<Jar>(name = "libJar") {
+        archiveBaseName.set("${rootProject.name}-${project.name}-lib")
+        from(project.the<SourceSetContainer>()["main"].output)
+        include("**")
+        exclude {
+            it.name.endsWith("Application.class")
+        }
+        exclude {
+            it.name.endsWith("ApplicationKt.class")
+        }
+        exclude {
+            it.name.endsWith("Application${'$'}Companion.class")
+        }
+    }
+    tasks.build { dependsOn(tasks.named("libJar")) }
+
+    configurations {
+        create("lib")
+    }
+
+    artifacts {
+        add("lib", tasks["libJar"])
+    }
+}
+
+configure(subprojects.filter { it.name != "service-api" && it.name != "service-nodb" }) {
+    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")
+        runtimeOnly("mysql:mysql-connector-java")
+    }
+}
+
+project("service-all") {
+    dependencies {
+        implementation(project(path = ":services:service-crud", configuration = "lib"))
+        implementation(project(path = ":services:service-nodb", configuration = "lib"))
+    }
+}
+
+project(":services") {
+    tasks.jar {
+        archiveBaseName.set("${rootProject.name}-${project.name}")
+        subprojects {
+            from(tasks.withType<JavaCompile>())
+            from(tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>())
+            include("**")
+            exclude {
+                it.name.endsWith("Application.class")
+            }
+            exclude {
+                it.name.endsWith("ApplicationKt.class")
+            }
+            exclude {
+                it.name.endsWith("Application${'$'}Companion.class")
+            }
+        }
+    }
+    publishing {
+        publications {
+            create<MavenPublication>("maven") {
+                artifactId = "${rootProject.name}-${project.name}"
+                from(components["java"])
+            }
+        }
+    }
+}
+

+ 23 - 0
services/service-all/src/main/kotlin/gaf3/demo/services/AllServicesApplication.kt

@@ -0,0 +1,23 @@
+package gaf3.demo.services
+
+import gaf3.core.cloud.GafCloudConfiguration
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+import org.springframework.context.annotation.Import
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@SpringBootApplication
+@Import(GafCloudConfiguration::class)
+@RestController
+class AllServicesApplication {
+
+	@RequestMapping("/all/hello")
+	fun hello(name: String?) : String {
+		return """ hello, ${name ?: "all"}!"""
+	}
+}
+
+fun main(args: Array<String>) {
+	runApplication<AllServicesApplication>(*args)
+}

+ 17 - 0
services/service-all/src/main/kotlin/gaf3/demo/services/AllServicesConfiguration.kt

@@ -0,0 +1,17 @@
+package gaf3.demo.services
+
+import gaf3.core.cloud.GafCloudConfiguration
+import gaf3.core.jpa.GafJpaConfiguration
+import gaf3.core.services.code.GafCodeConfiguration
+import org.springframework.boot.autoconfigure.domain.EntityScan
+import org.springframework.context.annotation.ComponentScan
+import org.springframework.context.annotation.Configuration
+import org.springframework.context.annotation.Import
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories
+
+@Configuration
+@Import(GafCloudConfiguration::class, GafJpaConfiguration::class, GafCodeConfiguration::class)
+@EntityScan(basePackages = ["gaf3.demo.services"])
+@EnableJpaRepositories(basePackages = ["gaf3.demo.services"])
+@ComponentScan(basePackages = ["gaf3.demo.services"])
+class AllServicesConfiguration

+ 28 - 0
services/service-all/src/main/resources/application.yml

@@ -0,0 +1,28 @@
+gaf:
+  auth:
+    jwt-secret: "GafJwtSecret!@#"
+    jwt-issuer: xms-gaf
+    jwt-validity: 1h
+
+spring:
+  datasource:
+    username: ${dbUser}
+    password: ${dbPwd}
+    url: ${dbUrl}
+    driver-class-name: ${dbDriver}
+    initialization-mode: never
+    platform: mysql
+  jpa:
+    database-platform: ${dbDialect}
+    show-sql: true
+    hibernate:
+      naming:
+        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+      ddl-auto: update
+  main:
+    allow-bean-definition-overriding: true
+server:
+  port: 8080
+logging.level.gaf3.core.*: DEBUG
+#debug: true

+ 3 - 0
services/service-all/src/main/resources/data-mysql.sql

@@ -0,0 +1,3 @@
+#insert into gaf_code_item (id, item_type, code, alias, status, name, created_at) value ('0', '00', '01', 'demo', '0', '测试', now());
+insert into gaf_user_info (user_id, name, status, created_at, updated_at) values ('00000000-0000-0000-0000-000000000000', '系统管理员', '0', now(), now());
+insert into gaf_user_acct ( user_id, account, secret, status, expired, created_at, updated_at) values ('00000000-0000-0000-0000-000000000000', 'admin', '123456', '0', -1, now(), now());

+ 16 - 0
services/service-api/src/main/kotlin/gaf3/demo/services/ServiceApiApplication.kt

@@ -0,0 +1,16 @@
+package gaf3.demo.services
+
+import gaf3.core.cloud.GafCloudConfiguration
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+import org.springframework.cloud.openfeign.EnableFeignClients
+import org.springframework.context.annotation.Import
+
+@SpringBootApplication
+@EnableFeignClients
+@Import(GafCloudConfiguration::class)
+class ServiceApiApplication
+
+fun main(args: Array<String>) {
+	runApplication<ServiceApiApplication>(*args)
+}

+ 14 - 0
services/service-api/src/main/kotlin/gaf3/demo/services/api/LogService.kt

@@ -0,0 +1,14 @@
+package gaf3.demo.services.api
+
+import gaf3.core.cloud.feign.GafFeignConfiguration
+import gaf3.demo.services.api.domain.LogData
+import gaf3.demo.services.api.domain.LogForm
+import org.springframework.cloud.openfeign.FeignClient
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+
+@FeignClient(name = "\${feign.name}", url = "\${feign.url}", configuration = [GafFeignConfiguration::class] )
+interface LogService {
+    @PostMapping(path = ["/gaf/logs"])
+    fun create(@RequestBody data: LogForm): LogData
+}

+ 9 - 0
services/service-api/src/main/kotlin/gaf3/demo/services/api/domain/LogData.kt

@@ -0,0 +1,9 @@
+package gaf3.demo.services.api.domain
+
+import java.time.LocalDateTime
+
+class LogData: LogForm() {
+    var id: String? = null
+    var createdAt: LocalDateTime? = null
+    var updatedAt: LocalDateTime? = null
+}

+ 29 - 0
services/service-api/src/main/kotlin/gaf3/demo/services/api/domain/LogForm.kt

@@ -0,0 +1,29 @@
+package gaf3.demo.services.api.domain
+
+import com.fasterxml.jackson.annotation.JsonInclude
+import javax.validation.constraints.NotEmpty
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+open class LogForm{
+    @NotEmpty
+    var logType: String? = null
+    @NotEmpty
+    var event: String? = null
+    @NotEmpty
+    var level: String? = null
+    var module: String? = null
+    var action: String? = null
+    var target: String? = null
+    var param: String? = null
+    @NotEmpty
+    var result: String? = null
+    var detail: String? = null
+    var eventTime: String? = null
+    var timestamp: Long? = null
+    var extra: String? = null
+    @NotEmpty
+    var userName: String? = null
+    @NotEmpty
+    var userAcct: String? = null
+    var userIp: String? = null
+}

+ 21 - 0
services/service-api/src/main/resources/application.yml

@@ -0,0 +1,21 @@
+
+server.port: 8000
+
+feign:
+  name: users
+  url: http://localhost:8081/
+  client:
+    config:
+      users:
+        connectTimeout: 5000
+        readTimeout: 5000
+        loggerLevel: full
+#        errorDecoder: com.example.SimpleErrorDecoder
+#        retryer: com.example.SimpleRetryer
+#        requestInterceptors:
+#          - com.example.FooRequestInterceptor
+#          - com.example.BarRequestInterceptor
+#        decode404: false
+#        encoder: com.example.SimpleEncoder
+#        decoder: com.example.SimpleDecoder
+#        contract: com.example.SimpleContract

+ 40 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/ServiceUserApplication.kt

@@ -0,0 +1,40 @@
+package gaf3.demo.services.crud
+
+import gaf3.core.cloud.GafCloudConfiguration
+import gaf3.demo.services.crud.dao.GafUserInfoDAO
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+import org.springframework.context.annotation.Import
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories
+import org.springframework.http.MediaType
+import org.springframework.http.ResponseEntity
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@SpringBootApplication
+@RestController
+@EnableJpaRepositories(basePackageClasses=[GafUserInfoDAO::class])
+@Import(GafCloudConfiguration::class)
+class ServiceUserApplication {
+
+    @RequestMapping("/user/hello")
+    fun hello(name: String?) : String {
+        return """ hello, ${name ?: "user"}!"""
+    }
+
+    @GetMapping("/demo")
+    fun demo() : ResponseEntity<Any> {
+
+        return ResponseEntity.status(400)
+                .contentType(MediaType.APPLICATION_JSON)
+                .body(RestError(-1,"demo error"));
+        // return ResponseEntity.status(400).body("this is a error demo!")
+    }
+
+    data class RestError(val errcode: Int, val errmsg: String)
+}
+
+fun main(args: Array<String>) {
+    runApplication<ServiceUserApplication>(*args)
+}

+ 45 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/UserServiceController.kt

@@ -0,0 +1,45 @@
+package gaf3.demo.services.crud
+
+import gaf3.core.data.ErrorResult
+import gaf3.core.data.PagedData
+import gaf3.core.exception.BusinessError
+import gaf3.demo.services.crud.service.UserService
+import gaf3.demo.services.crud.domain.CreateForm
+import gaf3.demo.services.crud.domain.UpdateForm
+import gaf3.demo.services.crud.entity.GafUserInfo
+import org.springframework.web.bind.annotation.*
+import javax.validation.Valid
+
+@RestController
+class UserServiceController(val service: UserService) {
+//    companion object {
+//        val log: Logger = LoggerFactory.getLogger(UserServiceController::class.java)
+//    }
+
+    @PostMapping("/gaf/users")
+    fun create(@Valid @RequestBody data: CreateForm) : GafUserInfo {
+        return service.create(data)
+    }
+
+    @GetMapping("/gaf/users")
+    fun query(filter: GafUserInfo, skip: Int? = 0, limit: Int? = 100, exact: Boolean?) : PagedData<GafUserInfo> {
+        return service.find(filter, skip?:0, limit?:100, exact ?: true)
+    }
+
+    @GetMapping("/gaf/users/{userId}")
+    fun fetch(@PathVariable userId: String) : GafUserInfo {
+        return service.fetch(userId) ?: throw BusinessError(BusinessError.ERR_DATA_NOTEXIST)
+    }
+
+    @DeleteMapping("/gaf/users/{userId}")
+    fun delete(@PathVariable userId: String) : ErrorResult {
+        service.delete(userId)
+        return ErrorResult(0,"deleted")
+    }
+
+    @PostMapping("/gaf/users/{userId}")
+    fun update(@PathVariable userId: String, @Valid @RequestBody data: UpdateForm) : GafUserInfo {
+        return service.update(userId, data)
+    }
+
+}

+ 8 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/dao/GafUserInfoDAO.kt

@@ -0,0 +1,8 @@
+package gaf3.demo.services.crud.dao
+
+import gaf3.demo.services.crud.entity.GafUserInfo
+import org.springframework.data.jpa.repository.JpaRepository
+
+interface GafUserInfoDAO : JpaRepository<GafUserInfo, String?> {
+
+}

+ 12 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/domain/CreateForm.kt

@@ -0,0 +1,12 @@
+package gaf3.demo.services.crud.domain
+
+import com.fasterxml.jackson.annotation.JsonInclude
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+data class CreateForm @JvmOverloads constructor(var name: String? = null) {
+    var title: String? = null
+    var gender: String? = null
+    var mobile: String? = null
+    var email: String? = null
+    var remark: String? = null
+}

+ 13 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/domain/UpdateForm.kt

@@ -0,0 +1,13 @@
+package gaf3.demo.services.crud.domain
+
+import com.fasterxml.jackson.annotation.JsonInclude
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+data class UpdateForm @JvmOverloads constructor(var name: String? = null) {
+    var status: String? = null
+    var title: String? = null
+    var gender: String? = null
+    var mobile: String? = null
+    var email: String? = null
+    var remark: String? = null
+}

+ 49 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/entity/GafUserInfo.kt

@@ -0,0 +1,49 @@
+package gaf3.demo.services.crud.entity
+
+import gaf3.core.jpa.GafTimestamp
+import gaf3.core.jpa.annotation.WithStringMatcher
+import org.springframework.data.domain.ExampleMatcher
+import javax.persistence.Column
+import javax.persistence.Entity
+import javax.persistence.Id
+import javax.persistence.Table
+import javax.validation.constraints.NotEmpty
+
+@Entity
+@Table(name = "GAF_USER_INFO")
+@WithStringMatcher
+class GafUserInfo @JvmOverloads constructor(userId: String? = null) : GafTimestamp() {
+    @Id
+    @Column(name = "USER_ID", length = 48)
+    var userId: String? = null
+
+    @WithStringMatcher(ExampleMatcher.StringMatcher.STARTING)
+    @NotEmpty
+    @Column(name = "NAME", length = 48)
+    var name: String? = null
+
+    @Column(name = "TITLE")
+    var title: String? = null
+
+    @Column(name = "STATUS", length = 48)
+    var status: String? = null
+
+    @Column(name = "GENDER", length = 48)
+    var gender: String? = null
+
+    @Column(name = "MOBILE", length = 48)
+    var mobile: String? = null
+
+    @Column(name = "EMAIL")
+    var email: String? = null
+
+    @Column(name = "REMARK")
+    var remark: String? = null
+
+    @Column(name = "ORIGIN", length = 48)
+    var origin: String? = null
+
+    init {
+        this.userId = userId
+    }
+}

+ 86 - 0
services/service-crud/src/main/kotlin/gaf3/demo/services/crud/service/UserService.kt

@@ -0,0 +1,86 @@
+package gaf3.demo.services.crud.service
+
+import gaf3.core.data.PagedData
+import gaf3.core.exception.BusinessError
+import gaf3.core.jpa.extension.findAll
+import gaf3.core.util.DataBeanHelper
+import gaf3.demo.services.crud.dao.GafUserInfoDAO
+import gaf3.demo.services.crud.domain.CreateForm
+import gaf3.demo.services.crud.domain.UpdateForm
+import gaf3.demo.services.crud.entity.GafUserInfo
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.Sort
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.stereotype.Service
+import org.springframework.util.Assert
+import org.springframework.validation.annotation.Validated
+import java.util.*
+import javax.transaction.Transactional
+
+@Service("DemoUserService")
+@Validated
+class UserService(@Autowired private val userDao: GafUserInfoDAO) {
+//    companion object {
+//        val log: Logger = LoggerFactory.getLogger(UserService::class.java)
+//    }
+
+    @Transactional
+    fun create(data: CreateForm): GafUserInfo {
+        // TODO: 验证信息
+        Assert.hasText(data.name, "用户名不能为空")
+
+        // TODO: 设置默认属性
+        val userId = UUID.randomUUID().toString()
+        var user = GafUserInfo()
+        DataBeanHelper.Bean2Obj(data, user)
+        user.userId = userId
+        user.status = "0"
+
+        // TODO: 保存数据
+        user = userDao.save(user)
+        return fetch(user.userId!!)!!
+    }
+
+    fun delete(userId: String) {
+        // 验证信息
+        Assert.hasText(userId, "用户ID不能为空")
+        val entity: GafUserInfo = userDao.findById(userId).orElseThrow {
+            throw BusinessError(BusinessError.ERR_DATA_NOTEXIST, "数据不存在")
+        }
+        if(entity.origin == "default") {
+            throw BusinessError(BusinessError.ERR_SERVICE_FAULT, "默认用户不能删除")
+        }
+        // 删除数据
+        userDao.deleteById(userId)
+    }
+
+    fun update(userId: String, data: UpdateForm): GafUserInfo {
+        // TODO: 验证信息
+        Assert.hasText(userId, "用户ID不能为空")
+
+        // TODO: 设置修改的属性
+        val user = userDao.findByIdOrNull(userId) ?:
+            throw BusinessError(BusinessError.ERR_DATA_NOTEXIST, "用户数据不存在")
+        DataBeanHelper.Bean2Obj(data, user)
+
+        userDao.save(user)
+        return fetch(userId)!!
+    }
+
+    fun fetch(userId: String): GafUserInfo? {
+        // TODO: 验证信息
+        Assert.hasText(userId, "用户ID不能为空")
+
+        return userDao.findByIdOrNull(userId) ?: return null
+    }
+
+    @JvmOverloads
+    fun find(filter: GafUserInfo, skip: Int = 0, limit: Int = 100, exact: Boolean = true): PagedData<GafUserInfo> {
+        val page:Int = skip/limit
+        // val paged = userDao.findAll(example, PageRequest.of(page, limit, Sort.Direction.ASC, "createdAt"))
+        val paged = userDao.findAll(filter, PageRequest.of(page, limit, Sort.Direction.ASC, "createdAt"), exact)
+
+        return PagedData(paged.content, paged.totalElements.toInt())
+    }
+}

+ 18 - 0
services/service-crud/src/main/resources/application.yml

@@ -0,0 +1,18 @@
+
+spring:
+  datasource:
+    username: ${dbUser}
+    password: ${dbPwd}
+    url: ${dbUrl}
+    driver-class-name: ${dbDriver}
+  jpa:
+    database-platform: ${dbDialect}
+    show-sql: true
+    hibernate:
+      naming:
+        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+      ddl-auto: update
+server:
+  port: 8087
+logging.level.gaf3.core.*: DEBUG

+ 28 - 0
services/service-nodb/build.gradle.kts

@@ -0,0 +1,28 @@
+group = rootProject.group
+version = rootProject.version
+
+plugins {
+    id("java")
+    id("io.spring.dependency-management")
+    id("org.springframework.boot")
+    kotlin("jvm")
+    kotlin("plugin.spring")
+    kotlin("plugin.jpa") apply false
+}
+
+repositories {
+    maven {
+        name = "aliyun"
+        url = uri("http://maven.aliyun.com/nexus/content/groups/public/")
+    }
+    // mavenCentral()
+}
+
+dependencies {
+    implementation(kotlin("reflect"))
+    implementation(kotlin("stdlib-jdk8"))
+    implementation("org.springframework.boot:spring-boot-configuration-processor")
+    implementation("org.springframework.boot:spring-boot-starter-webflux")
+    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
+    implementation("org.springframework.boot:spring-boot-starter-data-redis")
+}

+ 42 - 0
services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/DemoVerifyConfiguration.kt

@@ -0,0 +1,42 @@
+package gaf3.demo.services.nodb
+
+import gaf3.core.cache.CacheService
+import gaf3.core.cache.GafCacheConfigure
+import gaf3.core.cache.MemCacheService
+import gaf3.core.cache.RedisCacheService
+import org.springframework.beans.factory.annotation.Qualifier
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
+import org.springframework.boot.context.properties.ConfigurationProperties
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.ComponentScan
+import org.springframework.context.annotation.Configuration
+import org.springframework.data.redis.core.RedisTemplate
+
+@Configuration
+@ComponentScan(basePackageClasses = [DemoVerifyConfiguration::class])
+class DemoVerifyConfiguration {
+
+    @Bean
+    @ConfigurationProperties(prefix = "gaf.cache.verify")
+    fun demoVerifyCacheConfigure(): GafCacheConfigure {
+        return GafCacheConfigure();
+    }
+
+    @ConditionalOnProperty(prefix = "gaf.cache.verify", name = ["useRedis"], havingValue = "true", matchIfMissing = false)
+    @Configuration
+    class RedisCache {
+        @Bean
+        fun demoVerifyCacheService(template: RedisTemplate<String,String>, @Qualifier("demoVerifyCacheConfigure") config: GafCacheConfigure): CacheService {
+            return RedisCacheService(template, config)
+        }
+    }
+
+    @ConditionalOnProperty(prefix = "gaf.cache.verify", name = ["useRedis"], havingValue = "false", matchIfMissing = true)
+    @Configuration
+    class MemCache {
+        @Bean
+        fun demoVerifyCacheService(@Qualifier("demoVerifyCacheConfigure") config: GafCacheConfigure): CacheService {
+            return MemCacheService(config)
+        }
+    }
+}

+ 24 - 0
services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/ServiceVerifyApplication.kt

@@ -0,0 +1,24 @@
+package gaf3.demo.services.nodb
+
+import gaf3.core.cloud.GafCloudConfiguration
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+import org.springframework.context.annotation.Import
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RestController
+
+@SpringBootApplication
+@RestController
+@Import(GafCloudConfiguration::class, DemoVerifyConfiguration::class)
+class ServiceVerifyApplication{
+
+    @RequestMapping("/verify/hello")
+    fun hello(): String {
+        return "hello"
+    }
+
+}
+
+fun main(args: Array<String>) {
+    runApplication<ServiceVerifyApplication>(*args)
+}

+ 25 - 0
services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/VerifyCodeController.kt

@@ -0,0 +1,25 @@
+package gaf3.demo.services.nodb
+
+import gaf3.core.data.ErrorResult
+import gaf3.core.exception.BusinessError
+import gaf3.demo.services.nodb.service.VerifyCodeService
+import org.springframework.http.MediaType
+import org.springframework.web.bind.annotation.*
+
+@RestController
+class VerifyCodeController(val service: VerifyCodeService) {
+
+    @GetMapping(path = ["/gaf/verify/{state}.jpg"], produces = [MediaType.IMAGE_JPEG_VALUE])
+    @ResponseBody
+    fun create(@PathVariable("state") state: String): ByteArray {
+        return service.create(state)
+    }
+
+    @PostMapping(path = ["/gaf/verify/image"])
+    fun verify(state: String, code: String): ErrorResult {
+        return if(service.verify(state, code))
+            ErrorResult(0,"success")
+        else
+            ErrorResult(BusinessError.ERR_VERIFYCODE_INVALID, "验证码无效")
+    }
+}

+ 47 - 0
services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/service/VerifyCodeService.kt

@@ -0,0 +1,47 @@
+package gaf3.demo.services.nodb.service
+
+import gaf3.core.cache.CacheService
+import gaf3.demo.services.nodb.util.VerifyCodeHelper
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.annotation.Qualifier
+import org.springframework.stereotype.Service
+import org.springframework.util.Assert
+import org.springframework.validation.annotation.Validated
+
+
+@Service("DemoVerifyCodeService")
+@Validated
+class VerifyCodeService (@Qualifier("demoVerifyCacheService") val cacheService : CacheService) {
+
+    fun create(state: String): ByteArray {
+        Assert.notNull(state, "必须提供状态码")
+        var code = cacheService.getCache(state)
+        if(code.isNullOrBlank()){
+            code = VerifyCodeHelper.generateCode()
+            cacheService.setCache(state, code)
+        }
+        return VerifyCodeHelper.generateImage(code)
+    }
+
+    fun verify(state: String, code: String): Boolean {
+        val value: String? = cacheService.getCache(state)
+        if(value == null) {
+            log.debug("验证码[{}]不存在", state)
+            return false
+        }
+
+        val res = code.compareTo(value, true) == 0
+        if(res) {
+            cacheService.delCache(state)
+            log.debug("验证码[{}]比对成功, code:{}", state, code)
+        } else {
+            log.debug("验证码[{}]比对失败, code:{}, cache: {}", state, code, value)
+        }
+        return res
+    }
+
+    companion object {
+        val log: Logger = LoggerFactory.getLogger(VerifyCodeService::class.java)
+    }
+}

+ 127 - 0
services/service-nodb/src/main/kotlin/gaf3/demo/services/nodb/util/VerifyCodeHelper.kt

@@ -0,0 +1,127 @@
+package gaf3.demo.services.nodb.util
+
+import java.awt.Color
+import java.awt.Font
+import java.awt.Graphics
+import java.awt.RenderingHints
+import java.awt.geom.AffineTransform
+import java.awt.image.BufferedImage
+import java.io.ByteArrayOutputStream
+import java.util.*
+import javax.imageio.ImageIO
+import kotlin.math.PI
+import kotlin.math.sin
+
+object VerifyCodeHelper {
+
+    private val VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
+
+    //生成验证码字符串,可以设置字符串长度
+    fun generateCode(size: Int = 4, source: String = VERIFY_CODES): String{
+        val result = StringBuilder()
+        for(i in 1..size)
+            result.append(source.random())
+        return result.toString()
+    }
+
+    //生成验证码图片
+    fun generateImage(code:String, width: Int = 200, height: Int = 80): ByteArray {
+        val image = BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
+        val g2d = image.createGraphics()
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
+
+        g2d.color = Color.GRAY
+        g2d.fillRect(0, 0, width, height)
+
+        val c = getRandomColor(200, 250)
+        g2d.color = c
+        g2d.fillRect(0,2,width, height-4)
+
+        g2d.color = getRandomColor(160, 200)
+        for(i in 0 until 20){
+            val x = (1..width).random()
+            val y = (1..height).random()
+            val xl = (1..6).random()
+            val yl = (1..12).random()
+            g2d.drawLine(x, y, x + xl + 40, y + yl + 20)
+        }
+
+        val yawpRate = 0.05f
+        val area = (yawpRate * width * height).toInt()
+        for(i in 0 until area){
+            val x = (0 until width).random()
+            val y = (0 until height).random()
+            val rgb = getRandomIntColor()
+            image.setRGB(x, y, rgb)
+        }
+
+        shear(g2d, width, height, c)
+
+        g2d.color = getRandomColor(100, 160)
+        val fontSize = height - 4
+        g2d.font = Font("Algerian", Font.ITALIC, fontSize)
+        val codeSize = code.length
+        for(i in (0 until codeSize)){
+            val affine = AffineTransform()
+            val random = Random()
+            affine.setToRotation(PI / 4 * random.nextDouble() * (if(random.nextBoolean()) 1 else -1), (width.toDouble() / codeSize) * i + fontSize/2, height.toDouble()/2)
+            g2d.transform = affine
+            g2d.drawChars(code.toCharArray(), i, 1,((width-10) / codeSize) * i + 5, height/2 + fontSize/2 - 10)
+        }
+        g2d.dispose()
+        val output = ByteArrayOutputStream()
+        ImageIO.write(image, "jpg", output);
+        val result = output.toByteArray()
+        output.close()
+        return result
+    }
+
+    private fun getRandomColor(fc: Int, bc: Int): Color {
+        val f = if(fc > 255) 255 else fc
+        val b = if(bc > 255) 255 else bc
+        return Color((f..b).random(), (f..b).random(), (f..b).random())
+    }
+
+    private fun getRandomIntColor(): Int{
+        var color = 0
+        val rgb = arrayOf((0 until 255).random(), (0 until 255).random(), (0 until 255).random())
+        for(c in rgb){
+            color = (color shl 8) and c
+        }
+        return color
+    }
+
+    private fun shear(g: Graphics, w: Int, h: Int, color: Color){
+        shearX(g, w, h, color)
+        shearY(g, w, h, color)
+    }
+
+    private fun shearX(g: Graphics, w: Int, h: Int, color: Color) {
+        val period = (0..1).random()
+        val frames = 1;
+        val phase = (0..1).random()
+
+        for (i in 0 until h) {
+            val d = (period ushr 1).toDouble() * sin(i.toDouble() / period.toDouble() + (6.2831853071795862 * phase.toDouble()) / frames.toDouble());
+            g.copyArea(0, i, w, 1, d.toInt(), 0);
+            g.setColor(color);
+            g.drawLine(d.toInt(), i, 0, i);
+            g.drawLine(d.toInt() + w, i, w, i);
+        }
+    }
+
+    private fun shearY(g: Graphics, w: Int, h: Int, color: Color) {
+        val period = (0 until 40).random()+ 10;
+        val frames = 20;
+        val phase = 7;
+        for (i in 0 until w) {
+            val d = (period ushr 1).toDouble() * Math.sin(i.toDouble() / period.toDouble() + (6.2831853071795862 * phase.toDouble()) / frames.toDouble());
+            g.copyArea(i, 0, 1, h, 0, d.toInt());
+            g.setColor(color);
+            g.drawLine(i, d.toInt(), i, 0);
+            g.drawLine(i, d.toInt() + h, i, h);
+        }
+
+    }
+
+}

+ 22 - 0
services/service-nodb/src/main/resources/application.yml

@@ -0,0 +1,22 @@
+
+spring:
+  redis:
+    database: 0
+    host: 172.17.116.101
+    port: 6379
+    password:
+    timeout: 1000
+    jedis:
+      pool:
+        max-active: 20
+        max-wait: -1
+        max-idle: 10
+        min-idle: 0
+server:
+  port: 8089
+gaf.cache.verify:
+  useRedis: false
+  validity: 60s
+  namespace: "gaf:cache:verify"
+logging.level.gaf3.core.*: DEBUG
+#debug: true

+ 7 - 0
settings.gradle.kts

@@ -0,0 +1,7 @@
+rootProject.name = "backend-template"
+include("apps", "apps:all-in-one")
+include("platform")
+include("services", "services:service-all", "services:service-api")
+include("services:service-crud")
+include("services:service-nodb")
+include("shared", "shared:util")

+ 68 - 0
shared/build.gradle.kts

@@ -0,0 +1,68 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+group = rootProject.group
+version = rootProject.version
+
+plugins {
+    id("java-library")
+    id("maven-publish")
+    id("io.spring.dependency-management")
+    kotlin("jvm")
+    kotlin("plugin.spring")
+    kotlin("plugin.jpa")
+}
+
+project(":shared") {
+    tasks.jar {
+        subprojects {
+            from(tasks.withType<JavaCompile>())
+            from(tasks.withType<KotlinCompile>())
+        }
+    }
+    tasks.sourcesJar {
+        subprojects {
+            from(sourceSets.main.get().allSource)
+        }
+    }
+}
+
+subprojects {
+    apply(plugin = "java")
+    apply(plugin = "maven-publish")
+    apply(plugin = "io.spring.dependency-management")
+    apply(plugin = "org.jetbrains.kotlin.jvm")
+    apply(plugin = "org.jetbrains.kotlin.plugin.spring")
+    apply(plugin = "org.jetbrains.kotlin.plugin.jpa")
+    dependencies {
+        api(platform(project(":platform")))
+        implementation(kotlin("reflect"))
+        implementation(kotlin("stdlib-jdk8"))
+        implementation("org.bouncycastle:bcprov-jdk15on")
+        implementation("com.alibaba:fastjson")
+        implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
+        implementation("org.springframework.boot:spring-boot-starter-webflux")
+        implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
+    }
+    dependencyManagement {
+        imports {
+            mavenBom("org.springframework.cloud:spring-cloud-dependencies:${property("springCloudVersion")}")
+        }
+    }
+    tasks.named<Jar>("jar") {
+        archiveBaseName.set("${rootProject.name}-${project.name}")
+    }
+}
+
+publishing {
+    publications {
+        create<MavenPublication>("maven") {
+            artifactId = "${rootProject.name}-${project.name}"
+            from(components["java"])
+            // artifact(tasks["sourcesJar"])
+        }
+    }
+}
+
+tasks.named<Jar>("jar") {
+    archiveBaseName.set("gaf3-core-${project.name}")
+}

+ 4 - 0
shared/util/build.gradle.kts

@@ -0,0 +1,4 @@
+dependencies {
+    implementation("org.springframework.boot:spring-boot-starter-data-jpa:${property("springBootVersion")}")
+    implementation("org.springframework.boot:spring-boot-starter-data-redis:${property("springBootVersion")}")
+}

+ 13 - 0
shared/util/src/main/java/gaf3/demo/util/AssertEx.java

@@ -0,0 +1,13 @@
+package gaf3.core.util;
+
+import org.springframework.util.Assert;
+
+final public class AssertEx {
+    final public static void isNullOrNotBlank(String value, String message) {
+        Assert.isTrue(value == null || !value.trim().isEmpty(), message);
+    }
+
+    final public static void notBlank(String value, String message) {
+        Assert.isTrue(value != null && !value.trim().isEmpty(), message);
+    }
+}