1811872455@163.com 1 місяць тому
коміт
8a280768ab
100 змінених файлів з 7756 додано та 0 видалено
  1. 42 0
      .gitignore
  2. 355 0
      Jenkinsfile
  3. 25 0
      java/storlead-framework/pom.xml
  4. 31 0
      java/storlead-framework/storlead-auth/pom.xml
  5. 133 0
      java/storlead-framework/storlead-auth/src/main/java/com/storlead/framework/auth/jwt/JwtUtil.java
  6. 181 0
      java/storlead-framework/storlead-auth/src/main/java/com/storlead/framework/auth/vo/LoginUser.java
  7. 48 0
      java/storlead-framework/storlead-auth/src/main/java/com/storlead/framework/util/LoginUserUtil.java
  8. BIN
      java/storlead-framework/storlead-auth/target/classes/com/storlead/framework/auth/jwt/JwtUtil.class
  9. BIN
      java/storlead-framework/storlead-auth/target/classes/com/storlead/framework/auth/vo/LoginUser.class
  10. BIN
      java/storlead-framework/storlead-auth/target/classes/com/storlead/framework/util/LoginUserUtil.class
  11. 169 0
      java/storlead-framework/storlead-common/pom.xml
  12. 32 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/CacheConstant.java
  13. 41 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/CodeGenerateInterface.java
  14. 82 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/CommonConstant.java
  15. 30 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DSConstants.java
  16. 60 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DataBaseConstant.java
  17. 12 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DefContants.java
  18. 14 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/LongToStringSerializer.java
  19. 25 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/RedisKeySaltConstant.java
  20. 11 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/SystemConstant.java
  21. 24 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/UserCacheKeyConstants.java
  22. 712 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/ecode/BCrypt.java
  23. 159 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/ecode/BCryptPasswordEncoder.java
  24. 19 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/ecode/PasswordEncoder.java
  25. 26 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/enums/DataTypeEnum.java
  26. 6 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/package-info.java
  27. 87 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/properties/UrlChainDefinitionPorperties.java
  28. 40 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/property/YamlPropertyResourceFactory.java
  29. 44 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/system/query/MatchTypeEnum.java
  30. 55 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/system/query/QueryCondition.java
  31. 71 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/system/query/QueryRuleEnum.java
  32. 88 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/thread/ThreadPoolUtil.java
  33. 619 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/ConvertUtils.java
  34. 905 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/DateUtil.java
  35. 895 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/DateUtils.java
  36. 150 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/HtmlUtils.java
  37. 529 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/LocalDateUtils.java
  38. 47 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/MD5Util.java
  39. 100 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/RandomCodeUtil.java
  40. 26 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/RandomGenerateHelper.java
  41. 202 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/RsaUtils.java
  42. 106 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SnowFlake.java
  43. 95 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SpringContextUtils.java
  44. 103 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SqlInjectionUtil.java
  45. 201 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/StringUtil.java
  46. 29 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SystemUtils.java
  47. 47 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/UrlChainBlackAndWhiteUtil.java
  48. 219 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/WordUtilsTest.java
  49. 211 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/ZipUtil.java
  50. 87 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/encryptor/AccessKeyEncryptor.java
  51. 78 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/CodeGenerate.java
  52. 29 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/DefaultIdentifierGenerator.java
  53. 36 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/IdWorker.java
  54. 10 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/IdentifierGenerator.java
  55. 126 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/Sequence.java
  56. 20 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NeContains.java
  57. 31 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NeContainsValidator.java
  58. 28 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NotContains.java
  59. 31 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NotContainsValidator.java
  60. 55 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/vo/FeignResult.java
  61. 119 0
      java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/vo/UserVo.java
  62. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/CacheConstant.class
  63. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/CodeGenerateInterface.class
  64. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/CommonConstant.class
  65. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/DSConstants.class
  66. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/DataBaseConstant.class
  67. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/DefContants.class
  68. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/LongToStringSerializer.class
  69. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/RedisKeySaltConstant.class
  70. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/SystemConstant.class
  71. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/UserCacheKeyConstants.class
  72. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/ecode/BCrypt.class
  73. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/ecode/BCryptPasswordEncoder.class
  74. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/ecode/PasswordEncoder.class
  75. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/enums/DataTypeEnum.class
  76. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/properties/UrlChainDefinitionPorperties.class
  77. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/property/YamlPropertyResourceFactory.class
  78. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/system/query/MatchTypeEnum.class
  79. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/system/query/QueryCondition.class
  80. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/system/query/QueryRuleEnum.class
  81. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/thread/ThreadPoolUtil.class
  82. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/ConvertUtils.class
  83. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/DateUtil.class
  84. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/DateUtils.class
  85. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/HtmlUtils.class
  86. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/LocalDateUtils.class
  87. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/MD5Util.class
  88. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/RandomCodeUtil.class
  89. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/RandomGenerateHelper.class
  90. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/RsaUtils.class
  91. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SnowFlake.class
  92. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SpringContextUtils.class
  93. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SqlInjectionUtil.class
  94. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/StringUtil.class
  95. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SystemUtils.class
  96. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/UrlChainBlackAndWhiteUtil.class
  97. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/WordUtilsTest.class
  98. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/ZipUtil.class
  99. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/encryptor/AccessKeyEncryptor.class
  100. BIN
      java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/generate/CodeGenerate.class

+ 42 - 0
.gitignore

@@ -0,0 +1,42 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+java/storlead-centre-api/target
+java/storlead-centre-service/target
+java/storlead-framework/*/target
+java/storlead-framework/storlead-web/target
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 355 - 0
Jenkinsfile

@@ -0,0 +1,355 @@
+properties([
+        parameters([
+                [$class: 'ChoiceParameter',
+                 choiceType: 'PT_SINGLE_SELECT',
+                 description: '选择要部署 API or UI',
+                 name: 'modulePrefix',
+                 randomName: 'choice-parameter-5631314439613978',
+                 script: [
+                         $class: 'GroovyScript',
+                         fallbackScript: [
+                                 classpath: [],
+                                 sandbox: true,
+                                 script:
+                                         'return[\'Could not get Env\']'
+                         ],
+                         script: [
+                                 classpath: [],
+                                 sandbox: true,
+                                 script:
+                                         'return["java","ui"]'
+                         ]
+                 ]
+                ],
+                [$class: 'CascadeChoiceParameter',
+                 choiceType: 'PT_SINGLE_SELECT',
+                 description: '选择要部署的项目',
+                 name: 'module',
+                 randomName: 'choice-parameter-5631314456178619',
+                 referencedParameters: 'modulePrefix',
+                 script: [
+                         $class: 'GroovyScript',
+                         fallbackScript: [
+                                 classpath: [],
+                                 sandbox: true,
+                                 script:
+                                         'return[\'Could not get Environment from Env Param\']'
+                         ],
+                         script: [
+                                 classpath: [],
+                                 sandbox: true,
+                                 script:
+                                         ''' if (modulePrefix.equals("java")){
+                                return["storlead-centre-api"]
+                            }
+                            else if(modulePrefix.equals("ui")){
+                                return["sp-user-center"]
+                            }
+                        '''
+                         ]
+                 ]
+                ]
+
+
+        ])
+])
+
+pipeline {
+    options {
+        timestamps()
+    }
+    environment {
+        GROUPID = readMavenPom().getGroupId()//com.storlead
+        ARTIFACTID = readMavenPom().getArtifactId()//sp-project
+        VERSION = readMavenPom().getVersion()//1.0
+    }
+    // 参数
+    parameters {
+        choice(
+                name: 'profile',
+                choices: ['test', 'prod','uat'],
+                description: '选择要部署的配置文件'
+        )
+    }
+    agent any
+    stages {
+        stage('处理参数') {
+            steps {
+                script {
+                    echo "处理参数"
+                    script {
+                        //服务器配置
+                        MODULE_PREFIX = "${params.modulePrefix}"
+                        SERVER_SSH_PORT = 53023
+                        SERVER_HOST = "node1.storlead.com"
+                        SWARM_INIT_REPLICAS_NUM = 1
+
+                        //服务配置
+                        API_PORT = 9200
+                        API_REMOTE_DEBUG_PORT = 9201
+                        API_NAMESPACE_RESTFUL = "${params.module}"
+
+                        //DOCKER配置
+                        DOCKER_REGISTRY = "reg-aliyun"
+                        DOCKER_HOST = "registry.cn-shenzhen.aliyuncs.com"
+                        //Docker私服登陆url https://registry-vpc.cn-shenzhen.aliyuncs.com
+                        DOCKER_LOGIN_REGISTRY = "https://${DOCKER_HOST}"
+
+                        BUILD_PREFIX = "storlead-huawei"
+                        //镜像完整名称 registry-vpc.cn-shenzhen.aliyuncs.com/storlead/sp-project-management-prod:1.0
+                        DOCKER_IMG_NAME = "${DOCKER_HOST}/${BUILD_PREFIX}/${params.module}-${params.profile}:${VERSION}"
+                        //镜像名 storlead/sp-project-management-prod:1.0
+                        IMG_NAME = "${BUILD_PREFIX}/${params.module}-${params.profile}:${VERSION}"
+
+                        PUSH_DOCK_IMG = true
+                        SERVER_HOST_NAME =  "test1"
+                        echo "为指定 module 设定对应 服务器及端口"
+                        echo "处理 ${params.module} 项目"
+                        if (params.modulePrefix == "java") {
+                            echo "---- storelad-centre-api"
+                            script {
+                                if (params.profile == "test") {
+                                    SWARM_INIT_REPLICAS_NUM = 1
+                                    SERVER_HOST = "test1.storlead.com"
+                                    API_PORT = 10060
+                                    API_REMOTE_DEBUG_PORT = 10061
+                                    SERVER_HOST_NAME =  "test1"
+                                } else if (params.profile == "prod") {
+                                    SERVER_HOST = "110.41.82.21"
+                                    API_PORT = 10060
+                                    API_REMOTE_DEBUG_PORT = 10061
+                                    SERVER_HOST_NAME =  "node1"
+                                }
+                            }
+                        }  else if (params.modulePrefix == "ui") {
+                            script {
+                                if (params.profile == "test") {
+                                    SERVER_HOST = "test1.storlead.com"
+                                } else if (params.profile == "prod") {
+                                    SERVER_HOST = "110.41.82.21"
+                                }
+                            }
+                        }
+
+                        echo "处理MODULE_NAMESPACE_RESTFUL"
+                        //把 module 名 从 sp-xxx 变成 xxx 也就是 项目 的 namespace
+                        if (API_NAMESPACE_RESTFUL == "storlead-centre-api") {
+                            API_NAMESPACE_RESTFUL = "api"
+                        }
+                        echo "处理镜像推送"
+                    }
+                }
+            }
+        }
+
+        stage('Node 编译') {
+
+            when {
+                environment name: 'modulePrefix', value: 'ui'
+            }
+            agent {
+                docker {
+//                      image "registry-vpc.cn-shenzhen.aliyuncs.com/storlead/node:19-alpine"
+                    image "node:20-alpine"
+                    args '-v /app:/app/:z -v /etc/localtime:/etc/localtime:ro -u root'
+                    reuseNode true
+                }
+            }
+            steps {
+                withCredentials([
+                        usernamePassword(credentialsId: 'storleadHW', usernameVariable: 'usernameHW', passwordVariable: 'passwordHW'),
+                        usernamePassword(credentialsId: 'storlead', usernameVariable: 'username', passwordVariable: 'password')
+                ]) {
+                    script {
+                        echo "发布 UI 代码"
+
+                        def remote = [:]
+                        remote.allowAnyHosts = true
+                        remote.name = "${SERVER_HOST}"
+                        remote.host = "${SERVER_HOST}"
+                        remote.port = 53023
+                        remote.user = username
+                        remote.password = password
+
+                        if (params.profile == "prod") {
+                            remote.port = 22
+                            remote.user = "root"
+                            remote.user = usernameHW
+                            remote.password = passwordHW
+                        }
+                        if(params.module == 'sp-user-mobile-center') {
+                           sh """cd ${params.modulePrefix}/${params.module} && npm install  && npm run build:${params.profile}"""
+                        } else {
+                           sh """cd ${params.modulePrefix}/${params.module} && npm install  && npm run ${params.profile}"""
+                        }
+
+//                          sh """cd ${params.modulePrefix}/${params.module}  && npm run ${params.profile}"""
+
+                        sshCommand remote: remote, command: "mkdir -p /app/sp/ui/app-centre/${params.profile}/${params.module}"
+
+                        sshCommand remote: remote, command: "rm -rf /app/sp/ui/app-centre/${params.profile}/${params.module}/dist/*"
+
+
+                        echo "/app/sp/ui/sales/${params.profile}/${params.module}/*"
+
+                        sshPut remote: remote, from: "${params.modulePrefix}/${params.module}/dist/", into: "/app/sp/ui/app-centre/${params.profile}/${params.module}/"
+
+                        echo "${params.modulePrefix}/${params.module}/dist/,/app/sp/ui/app-centre/${params.profile}/${params.module}/"
+
+                        sshCommand remote: remote, command: "cp -rf /app/sp/ui/app-centre/${params.profile}/${params.module}/dist/* /app/sp/ui/app-centre/${params.profile}/${params.module}/"
+                    }
+                }
+            }
+        }
+
+        stage('Maven 编译') {
+            when {
+                environment name: 'modulePrefix', value: 'java'
+            }
+            agent {
+                docker {
+                    image 'maven:3-amazoncorretto-11'
+                    //让docker 使用 host 宿主机的 m2仓库 使用root用户来运行 以让指定的~/.m2/config/setting.xml 阿里加速下载maven 依赖生效
+                    args '-v /app:/app:z -v $HOME/.m2:/root/.m2:z -u root'
+                    reuseNode true
+                }
+            }
+            steps {
+                echo "编译 API 代码"
+                sh "mvn clean package -U -am -pl ${params.module} -P${params.profile} -Dmaven.test.skip=true"
+            }
+        }
+
+        // 项目打包到镜像并推送到镜像仓库
+        stage('构建Docker镜像') {
+            when {
+                environment name: 'modulePrefix', value: 'java'
+            }
+            steps {
+                echo "构建Dcoker镜像-------------------"
+                script {
+                    docker.withRegistry(DOCKER_LOGIN_REGISTRY, DOCKER_REGISTRY) {
+
+                        BASE_IMAGE = 'registry.cn-shenzhen.aliyuncs.com/storlead/openjdk:11'
+                        sh "docker pull ${BASE_IMAGE}"
+
+                        sh """
+                          echo '
+                          FROM ${BASE_IMAGE}
+
+                          COPY ${params.module}/target/${params.module}.jar app.jar
+                          EXPOSE ${API_PORT}
+                          EXPOSE ${API_REMOTE_DEBUG_PORT}
+                          HEALTHCHECK --interval=1m --timeout=10s CMD curl -f http://localhost:${API_PORT}/${API_NAMESPACE_RESTFUL}/actuator
+                          ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-Djava.awt.headless=true","-Dspring.profiles.active=${params.profile}", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:${API_REMOTE_DEBUG_PORT}","-jar","/app.jar"]' > Dockerfile
+                          docker build -t ${DOCKER_IMG_NAME} .
+                         """
+                        sh "docker push ${DOCKER_IMG_NAME}"
+                    }
+                }
+            }
+        }
+
+        stage('启动服务') {
+            when {
+                environment name: 'modulePrefix', value: 'java'
+            }
+            steps {
+                withCredentials([
+                        usernamePassword(credentialsId: 'storleadHW', usernameVariable: 'usernameHW', passwordVariable: 'passwordHW'),
+                        usernamePassword(credentialsId: 'storlead', usernameVariable: 'username', passwordVariable: 'password'),
+                        usernamePassword(credentialsId: 'reg-aliyun', usernameVariable: 'aliUsername', passwordVariable: 'aliPassword')
+                ]) {
+                    script {
+
+                        echo "启动服务"
+                        echo "${aliPassword}"
+                        echo "docker login -u ${aliUsername} -p ${aliPassword} ${DOCKER_LOGIN_REGISTRY}"
+
+                        def remote = [:]
+                        remote.name = "${SERVER_HOST}"
+                        remote.host = "${SERVER_HOST}"
+                        remote.port = 53023
+                        remote.user = username
+                        remote.password = password
+                        remote.allowAnyHosts = true
+
+                        //因为使用 docker swarm 发布 只能在 manage node 执行部署
+                        if (params.profile == "prod") {
+                            remote.port = 22
+                            remote.user = "root"
+                            remote.user = usernameHW
+                            remote.password = passwordHW
+                        }
+                        if (PUSH_DOCK_IMG) {
+                            //docker 认证
+                            sshCommand remote: remote, command: "docker login -u ${aliUsername} -p ${aliPassword} ${DOCKER_LOGIN_REGISTRY}"
+                            //如果选择推送才使用远程的版本 如果不选择推送到远程则直接运行本机刚打包的镜像版本即可
+                            sshCommand remote: remote, command: "docker pull ${DOCKER_IMG_NAME}"
+                        }
+                        sh """
+                          echo '
+  version: "3.7"
+  services:
+    ${params.module}-${params.profile}:
+      image: ${DOCKER_IMG_NAME}
+      deploy:
+        replicas: ${SWARM_INIT_REPLICAS_NUM}
+        placement:
+          constraints:  
+            - 'node.hostname == ${SERVER_HOST_NAME}'
+        update_config:
+          parallelism: 1
+          delay: 1m
+        restart_policy:
+          condition: on-failure
+          # 最多连续重试3次如果仍然失败则放弃重试
+          max_attempts: 3
+      healthcheck:
+        test: ["CMD", "curl", "-f", "http://localhost:${API_PORT}/${API_NAMESPACE_RESTFUL}/actuator"]
+        interval: 1m
+        timeout: 5s
+        retries: 3
+        start_period: 30s
+      environment:
+        - "TZ=Asia/Shanghai"
+        - "SPRING_PROFILES_ACTIVE=${params.profile}"
+      volumes:
+        - /app:/app
+        - /mnt/vdb/storlead:/mnt/vdb/storlead
+      ports:
+        - "${API_PORT}:${API_PORT}"
+        - "${API_REMOTE_DEBUG_PORT}:${API_REMOTE_DEBUG_PORT}"
+      networks:
+        - vonedao_net
+      extra_hosts:
+        - "www.storlead.com:172.18.194.161"
+        - "node1:110.41.82.21"
+        - "node2:110.41.174.46"
+        - "node3:121.37.226.174"
+        - "test1.storlead.com:172.18.194.168"
+        - "test2.storlead.com:172.18.194.163"
+  networks:
+    vonedao_net:
+       external: true
+                          ' > ${params.module}-${params.profile}-stack.yml
+                         """
+                        //                        if (params.profile =="dev") {
+                        //                            sh "docker stack deploy -c ${ARTIFACTID}-${params.profile}-stack.yml ${ARTIFACTID}-${params.profile}"
+
+                        //                            sh "docker ps -f name=${ARTIFACTID} -q | xargs --no-run-if-empty docker container stop"
+                        //                            sh "docker container ls -a -fname=${ARTIFACTID} -q | xargs -r docker container rm"
+                        //                            sh "docker run -d --restart=always -p ${API_PORT}:${API_PORT} -p ${API_REMOTE_DEBUG_PORT}:${API_REMOTE_DEBUG_PORT} -v /app:/app --name ${ARTIFACTID} ${DOCKER_IMG_NAME}"
+                        //                        } else {
+                        // 全部都以swarm 模式启动
+                        sshPut remote: remote, from: "${params.module}-${params.profile}-stack.yml", into: "/docker/sp/${params.module}-${params.profile}-stack.yml"
+                        sshCommand remote: remote, command: "docker stack deploy --resolve-image=always --with-registry-auth -c /docker/sp/${params.module}-${params.profile}-stack.yml ${params.module}-${params.profile}"
+                        //                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+}

+ 25 - 0
java/storlead-framework/pom.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>storlead-smarttrade-platform</artifactId>
+        <groupId>com.storlead.boot</groupId>
+        <version>1.0</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>storlead-framework</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>storlead-common</module>
+        <module>storlead-redis</module>
+        <module>storlead-mybatis</module>
+        <module>storlead-web</module>
+        <module>storlead-core</module>
+        <module>storlead-auth</module>
+    </modules>
+
+</project>

+ 31 - 0
java/storlead-framework/storlead-auth/pom.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.storlead.boot</groupId>
+        <artifactId>storlead-framework</artifactId>
+        <version>1.0</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>storlead-auth</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.storlead.boot</groupId>
+            <artifactId>storlead-common</artifactId>
+            <version>1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.storlead.boot</groupId>
+            <artifactId>storlead-core</artifactId>
+            <version>1.0</version>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 133 - 0
java/storlead-framework/storlead-auth/src/main/java/com/storlead/framework/auth/jwt/JwtUtil.java

@@ -0,0 +1,133 @@
+package com.storlead.framework.auth.jwt;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONObject;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.algorithms.Algorithm;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import java.util.*;
+
+/**
+ * @program: storlead-storlead-mail-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2025-07-09 14:53
+ */
+public class JwtUtil {
+
+    /**
+     * 失效时间: 1 天 =24*3600*1000 ms
+     */
+    public static final long  EFFECTIVE_TIME = 1 * 24 * 3600 * 1000;
+    /**
+     * 私钥
+     */
+    public static final String APP_SECRET = "sp_user_2012";
+    /**
+     * token 前缀
+     */
+    public static final String APP_KEY = "sp_APP_KEY";
+    public static final String CLAIMS_KEY = "sp_CLAIMS_KEY";
+    /**
+     * 用户登录成功后生成Jwt
+     * 使用Hs256算法  私匙使用用户密码
+     *
+     * @param jsonInfo
+     * @param subjectKey
+     * @return
+     */
+    public static String createJWT(String jsonInfo,String subjectKey) {
+
+        //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
+        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+        long nowMillis = System.currentTimeMillis();
+        Date now = new Date(nowMillis);
+        //创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
+        //EntityUtils.entityToMap(user)
+
+        Map<String, Object> claims = new HashMap<>();
+        claims.put(CLAIMS_KEY, JSONObject.toJSON(jsonInfo));
+        //生成签发人
+        String subject = subjectKey;
+        //下面就是在为payload添加各种标准声明和私有声明了 JwtBuilder
+        //这里其实就是new一个JwtBuilder,设置jwt的body
+        JwtBuilder builder = Jwts.builder()
+                //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
+                .setClaims(claims)
+                //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
+                .setId(UUID.randomUUID().toString())
+                //iat: jwt的签发时间
+                .setIssuedAt(now)
+                //代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
+                .setSubject(subject)
+                //设置签名使用的签名算法和签名使用的秘钥Algorithm
+                .signWith(signatureAlgorithm, APP_KEY);
+
+        return builder.compact();
+    }
+
+
+    /**
+     * Token的解密
+     * @param jwtToken 加密后的token
+     * @return
+     */
+    public static String parseJWT(String jwtToken) {
+        //签名秘钥,和生成的签名的秘钥一模一样
+        Claims claims = Jwts.parser()
+                //设置签名的秘钥
+                .setSigningKey(APP_KEY)
+                //设置需要解析的jwt
+                .parseClaimsJws(jwtToken).getBody();
+        LinkedHashMap linkedHashMap = (LinkedHashMap)claims.get(CLAIMS_KEY);
+        String json =JSONObject.toJSONString(linkedHashMap);
+        return json;
+    }
+
+
+
+    //设置token的密钥
+//    private final static String SECRET = "token123";
+
+    public static String getGptToken(String apiKey) {
+        String[] apiKeySet = apiKey.split("\\.");
+        String apiKeyId = apiKeySet[0];
+
+        Date signDate = new Date();
+        Date date = new Date(System.currentTimeMillis() + EFFECTIVE_TIME);//过期时间
+        Algorithm algorithm = Algorithm.HMAC256(apiKeySet[1]);//进行加密算法
+
+        //token的Header信息
+        Map<String, Object> map = new HashMap<>();
+        map.put("alg", "HS256");
+        map.put("sign_type", "SIGN");
+
+        String token= JWT.create()
+                //(token的Header信息)
+                .withHeader(map)
+                //设置当前签发时间(token的Payload信息)
+                .withClaim("timestamp",signDate)
+
+                //设置token过期时间(token的Payload信息)
+                .withClaim("exp",date)
+                //自定义存放用户id在tokne中(token的自定义Payload信息)
+                .withClaim("api_key", apiKeyId)
+                //自定义存放用户名在token中(token的自定义Payload信息)
+//                .withClaim("username", user.getName())
+                //(token的Signature信息)
+                .sign(algorithm);
+        return token;
+    }
+
+
+    public static void main(String[] args) {
+        String apiKey = "89934b295eb37f5d5750643ba2b18dae.nnR0vfmj3rgMOgD6";
+
+        System.out.println(getGptToken(apiKey));
+    }
+}

+ 181 - 0
java/storlead-framework/storlead-auth/src/main/java/com/storlead/framework/auth/vo/LoginUser.java

@@ -0,0 +1,181 @@
+package com.storlead.framework.auth.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @program: storlead-storlead-mail-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2025-06-16 17:26
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class LoginUser implements Serializable {
+
+    /**
+     * 登录人id
+     */
+    private Long id;
+
+    /**
+     * 登录人账号
+     */
+    private String userName;
+
+    /**
+     * 公司ID
+     */
+    private Long companyId;
+
+    /**
+     * 分公司ID
+     */
+    private Long subCompanyId;
+
+    /**
+     * 部门ID
+     */
+    private Long deptId;
+
+    /**
+     * 部门ID
+     */
+    private String deptName;
+
+    /**
+     * 职位ID
+     */
+    private Long jobId;
+
+    /**
+     * 职位ID
+     */
+    private String jobName;
+
+    /**
+     * 测试
+     */
+    private Integer test;
+
+    /**
+     * 角色类型
+     */
+//    private UserRoleType roleType;
+
+    /**
+     * 登录人名字
+     */
+    private String realName;
+
+    /**
+     * 登录人密码
+     */
+    private String password;
+    /**
+     * 企业微信userid
+     */
+    private String xworkUserId;
+
+    /**
+     * 登录人名字
+     */
+    private String nickName;
+
+    /**
+     * 头像
+     */
+    private String avatar;
+
+    /**
+     * 生日
+     */
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    private LocalDate birthday;
+
+    /**
+     * 性别(1:男 2:女)
+     */
+    private String sex;
+
+    /**
+     * 电子邮件
+     */
+    private String email;
+
+    /**
+     * 电话
+     */
+    private String phone;
+
+    private String mobile;
+
+    /**
+     * 状态(1:正常 0:冻结 )
+     */
+    private Integer enabled;
+
+
+    /**
+     * 是否领导:0、否;1、分公司领导;2、部门领导
+     */
+    private Integer isLeader;
+
+    private String dataScope;
+
+    @ApiModelProperty(value = "直接上级ID")
+    private Long managerId;
+
+    @ApiModelProperty(value = "领导人ID集合")
+    private String managers;
+
+    /**
+     * 部门路由
+     */
+    private String deptCode;
+
+    @ApiModelProperty(value = "是否是超管")
+    private Boolean isAdmin;
+
+    /**
+     * 所属子公司路由
+     */
+    private String subCompanyCode;
+
+    /**
+     * 所属公司路由
+     */
+    private String companyCode;
+
+    @ApiModelProperty(value = "部门岗位职责")
+    private String deptJobDes;
+
+    @ApiModelProperty(value = "岗位职责")
+    private String jobDes;
+
+    private Set<Long> roleIds;
+
+    private String apiUrl;
+
+    private String scopeMenuId;
+
+    private Integer commonScope;
+
+    private String commonScopeType;
+    /**
+     * 按钮权限code
+     */
+    private Set<String> btnRes;
+
+}

+ 48 - 0
java/storlead-framework/storlead-auth/src/main/java/com/storlead/framework/util/LoginUserUtil.java

@@ -0,0 +1,48 @@
+package com.storlead.framework.util;
+
+import com.storlead.framework.auth.vo.LoginUser;
+import com.storlead.framework.common.constant.UserCacheKeyConstants;
+import com.storlead.framework.core.context.Context;
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * @author chenkq
+ */
+@Log4j2
+public class LoginUserUtil {
+    /**
+     * 获取当前用户信息
+     * @return
+     */
+    public static LoginUser getLoginUser(){
+        try {
+            LoginUser loginUser = Context.getContext().getAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_KEY, LoginUser.class);
+            return loginUser;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String getLoginToken(){
+        try {
+            String loginToken = Context.getContext().getAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_TOKEN_KEY, String.class);
+            return loginToken;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+    /**
+     * 获取当前用户信息
+     * @return
+     */
+    public static Long getCurrentUserId(){
+        try {
+            Long currentUserId = Context.getContext().getAttribute(UserCacheKeyConstants.LOGIN_USER_INFO_ID_KEY, Long.class);
+            return currentUserId;
+        } catch (Exception e) {
+            log.error("error - getCurrentUserId",e);
+            return null;
+        }
+    }
+
+}

BIN
java/storlead-framework/storlead-auth/target/classes/com/storlead/framework/auth/jwt/JwtUtil.class


BIN
java/storlead-framework/storlead-auth/target/classes/com/storlead/framework/auth/vo/LoginUser.class


BIN
java/storlead-framework/storlead-auth/target/classes/com/storlead/framework/util/LoginUserUtil.class


+ 169 - 0
java/storlead-framework/storlead-common/pom.xml

@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>com.storlead.boot</groupId>
+        <artifactId>storlead-framework</artifactId>
+        <version>1.0</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>storlead-common</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+        </dependency>
+
+        <!--JWT-->
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+        </dependency>
+
+
+        <!--  jwt 需要,移除会报错  -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.googlecode.aviator</groupId>
+            <artifactId>aviator</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <!-- excel导出-->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
+            <version>4.1.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.stylefeng.roses</groupId>
+            <artifactId>pinyin-sdk-pinyin4j</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>4.1.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml-schemas</artifactId>
+            <version>4.1.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springdoc</groupId>
+            <artifactId>springdoc-openapi-ui</artifactId>
+            <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
+        </dependency>
+
+        <dependency>
+            <groupId>ma.glasnost.orika</groupId>
+            <artifactId>orika-core</artifactId>
+            <version>1.5.4</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <!--   导出到word(循环图片)     -->
+        <!-- word导出  方式:easypoi-->
+        <dependency>
+            <groupId>cn.afterturn</groupId>
+            <artifactId>easypoi-base</artifactId>
+            <version>4.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.afterturn</groupId>
+            <artifactId>easypoi-web</artifactId>
+            <version>4.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.afterturn</groupId>
+            <artifactId>easypoi-annotation</artifactId>
+            <version>4.4.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+
+<!--        <dependency>-->
+<!--            <groupId>com.squareup.okhttp3</groupId>-->
+<!--            <artifactId>okhttp</artifactId>-->
+<!--        </dependency>-->
+
+<!--        <dependency>-->
+<!--            <groupId>com.squareup.okhttp3</groupId>-->
+<!--            <artifactId>okhttp-sse</artifactId>-->
+<!--        </dependency>-->
+        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+            <version>2.3.32</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+
+    </dependencies>
+</project>

+ 32 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/CacheConstant.java

@@ -0,0 +1,32 @@
+package com.storlead.framework.common.constant;
+
+/**
+ * @author: huangxutao
+ * @date: 2019-06-14
+ * @description: 缓存常量
+ */
+public interface CacheConstant {
+
+	/**
+	 * 缓存用户信息
+	 */
+	public static final String SYS_USERS_CACHE = "sys:cache:user";
+
+	/**
+	 * 全部部门信息缓存
+	 */
+	public static final String SYS_DEPARTS_CACHE = "sys:cache:depart:alldata";
+
+
+	/**
+	 * 全部部门ids缓存
+	 */
+	public static final String SYS_DEPART_IDS_CACHE = "sys:cache:depart:allids";
+
+
+	/**
+	 * 测试缓存key
+	 */
+	public static final String TEST_DEMO_CACHE = "test:demo";
+
+}

+ 41 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/CodeGenerateInterface.java

@@ -0,0 +1,41 @@
+package com.storlead.framework.common.constant;
+
+/**
+ * @program: sp-sales-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2024-08-21 16:27
+ */
+public interface CodeGenerateInterface {
+    // 统一编码
+    String CODE_KEY = "";
+    //订单费用
+    String ORDER_COST_CODE= "OC";
+    //订单票据
+    String ORDER_INVOICE_CODE= "OI";
+    //回款记录
+    String ORDER_RECEIVE_CODE= "OP";
+    //退款记录
+    String ORDER_REFUND_CODE= "ORF";
+    //报销单
+    String ORDER_REIMBURSE_BILL_CODE= "ORB";
+    //客户/线索编码
+    String LIAISON_CODE= "LC";
+    //客户/线索编码
+    String CUSTOMER_CODE= "CC";
+    //商机编码
+    String BUSINESS_CODE= "BC";
+    //订单编码
+    String ORDER_FORM_CODE= "OFC";
+    //工单编码
+    String WORK_ORDER_CODE= "WOC";
+    // 报价编码
+    String QUOTATION_CODE= "QC";
+    //任务
+    String TASK_CODE= "TC";
+    //邮件
+    String EMAIL_CODE= "EM";
+
+    //邮件
+    String BULLETIN_CODE= "BUC";
+}

+ 82 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/CommonConstant.java

@@ -0,0 +1,82 @@
+package com.storlead.framework.common.constant;
+
+public interface CommonConstant {
+
+	/**
+	 * 正常状态
+	 */
+	Integer STATUS_NORMAL = 1;
+
+	/**
+	 * 审批未通过状态
+	 */
+	Integer STATUS_NOT_ADOPT = -1;
+
+	/**
+	 * 禁用状态
+	 */
+	Integer STATUS_DISABLE = 0;
+
+	/**
+	 * 真
+	 */
+	Integer DEL_FLAG_0 = 0;
+
+    /**
+     * 假
+     */
+    Integer DEL_FLAG_1 = 1;
+
+
+	/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
+    Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
+    /** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
+    Integer SC_OK_200 = 200;
+
+    /**
+     * 业务通用异常
+     */
+    Integer BUSSINESS_COMM_ERROR = 19000;
+
+    /**访问权限认证未通过 510*/
+    Integer SC_JEECG_NO_AUTHZ=510;
+
+    /** 登录用户Shiro权限缓存KEY前缀 */
+    public static String PREFIX_USER_SHIRO_CACHE  = "shiro:cache:com.storlead.sales.modules.shiro.authc.ShiroRealm.authorizationCache:";
+    /** 登录用户Token令牌缓存KEY前缀 */
+    String PREFIX_USER_TOKEN  = "prefix_user_token_";
+
+    /**
+     * 是否用户已被冻结 1(解冻)正常 2冻结
+     */
+    Integer USER_FREEZE = 2;
+
+    /**字典翻译文本后缀*/
+    String DICT_TEXT_SUFFIX = "_dictText";
+
+    String USER_CACHE = "userCache";
+
+    String CAPCHA_CACHE = "capchaCache";
+
+    //长度超限
+    String LENGTH_EXCEEDS_THE_LIMIT = "length exceeds the limit";
+
+    /**
+     * 撤回状态
+     */
+    Integer STATUS_WITH_DRAW = 2;
+
+    /**
+     * 待发放状态
+     */
+    Integer STATUS_NOT_ISSUED = 3;
+
+    /**
+     * 已发放状态
+     */
+    Integer STATUS_ISSUED = 4;
+    /**
+     * 已完成状态
+     */
+    Integer STATUS_COMPLETE = 5;
+}

+ 30 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DSConstants.java

@@ -0,0 +1,30 @@
+package com.storlead.framework.common.constant;
+
+import lombok.Data;
+
+/**
+ * DBConstants
+ *
+ * @author blank
+ * @date 2021-6-16 上午 10:17
+ */
+@Data
+public class DSConstants {
+
+    /**
+     * 数据源分组 master库
+     */
+    public static final String DATASOURCE_MASTER = "master";
+
+    /**
+     * 数据源分组 oa库
+     */
+    public static final String DATASOURCE_OA = "oa";
+
+    /**
+     * 数据源分组 oa库
+     */
+    public static final String DATASOURCE_MANAGEMENT = "management";
+
+
+}

+ 60 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DataBaseConstant.java

@@ -0,0 +1,60 @@
+package com.storlead.framework.common.constant;
+/**
+ * 数据库上下文常量
+ */
+public interface DataBaseConstant {
+	//*********数据库类型****************************************
+	public static final String DB_TYPE_MYSQL = "MYSQL";
+	public static final String DB_TYPE_ORACLE = "ORACLE";
+	public static final String DB_TYPE_POSTGRESQL = "POSTGRESQL";
+	public static final String DB_TYPE_SQLSERVER = "SQLSERVER";
+
+	//*********系统上下文变量****************************************
+	/**
+	 * 数据-系统用户编码(对应登录用户账号)
+	 */
+	public static final String SYS_USER_CODE = "sysUserCode";
+	/**
+	 * 数据-系统用户编码(对应登录用户账号)
+	 */
+	public static final String SYS_USER_CODE_TABLE = "sys_user_code";
+
+	/**
+	 * 登录用户真实姓名
+	 */
+	public static final String SYS_USER_NAME = "sysUserName";
+	/**
+	 * 登录用户真实姓名
+	 */
+	public static final String SYS_USER_NAME_TABLE = "sys_user_name";
+	/**
+	 * 系统日期"yyyy-MM-dd"
+	 */
+	public static final String SYS_DATE = "sysDate";
+	/**
+	 * 系统日期"yyyy-MM-dd"
+	 */
+	public static final String SYS_DATE_TABLE = "sys_date";
+	/**
+	 * 系统时间"yyyy-MM-dd HH:mm"
+	 */
+	public static final String SYS_TIME = "sysTime";
+	/**
+	 * 系统时间"yyyy-MM-dd HH:mm"
+	 */
+	public static final String SYS_TIME_TABLE = "sys_time";
+	//*********系统上下文变量****************************************
+
+
+	//*********系统建表标准字段****************************************
+
+	/**
+	 * 业务流程状态
+	 */
+	public static final String BPM_STATUS = "bpmStatus";
+	/**
+	 * 业务流程状态
+	 */
+	public static final String BPM_STATUS_TABLE = "bpm_status";
+	//*********系统建表标准字段****************************************
+}

+ 12 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/DefContants.java

@@ -0,0 +1,12 @@
+package com.storlead.framework.common.constant;
+
+/**
+ * @program: sp-sales-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2024-03-29 17:37
+ */
+public class DefContants {
+    public static String ACCESS_TOKEN = "token";
+    public static String AUTHORIZATION = "Authorization";
+}

+ 14 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/LongToStringSerializer.java

@@ -0,0 +1,14 @@
+package com.storlead.framework.common.constant;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+
+public class LongToStringSerializer extends JsonSerializer<Long> {
+
+    @Override
+    public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        gen.writeNumber(value);
+    }
+}

+ 25 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/RedisKeySaltConstant.java

@@ -0,0 +1,25 @@
+package com.storlead.framework.common.constant;
+
+/**
+ * @program: sp-sales
+ * @description:
+ * @author: chenkq
+ * @create: 2022-07-05 09:20
+ */
+public class RedisKeySaltConstant {
+    /**
+     * 操作成功
+     */
+    public static final String API_CODE_REDIS = "api_code_redis_key_";
+
+    /**
+     * 操作成功
+     */
+    public static final String REDIS_API_CODE_SERVICE_CODE_KEY = "api_code_redis_key_user_service_code";
+
+
+    /**
+     * 操作成功
+     */
+    public static final String REDIS_LOGIN_VALID_CODE_KEY = "redis_login_valid_code_key_";
+}

+ 11 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/SystemConstant.java

@@ -0,0 +1,11 @@
+package com.storlead.framework.common.constant;
+
+/**
+ * @program: sp-sales
+ * @description: 系统产量
+ * @author: chenkq
+ * @create: 2022-07-25 20:22
+ */
+public class SystemConstant {
+
+}

+ 24 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/constant/UserCacheKeyConstants.java

@@ -0,0 +1,24 @@
+package com.storlead.framework.common.constant;
+
+/**
+ * @program: storlead-storlead-mail-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2025-06-17 15:39
+ */
+public class UserCacheKeyConstants {
+        /**
+         * 用户redisKEY
+         */
+        public static final String LOGIN_USER_INFO_KEY = "login_user_info";
+
+        /**
+         * 当前登录用户token key
+         */
+        public static final String LOGIN_USER_INFO_TOKEN_KEY = "login_user_token_key";
+
+        /**
+         * 登录用户id
+         */
+        public static final String LOGIN_USER_INFO_ID_KEY = "login_user_user_id";
+}

+ 712 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/ecode/BCrypt.java

@@ -0,0 +1,712 @@
+package com.storlead.framework.common.ecode;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+/**
+ * @program: sp-cloud
+ * @description:
+ * @author: chenkq
+ * @create: 2021-10-25 18:54
+ */
+public class BCrypt {
+
+    // BCrypt parameters
+    private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
+
+    private static final int BCRYPT_SALT_LEN = 16;
+
+    // Blowfish parameters
+    private static final int BLOWFISH_NUM_ROUNDS = 16;
+
+    // Initial contents of key schedule
+    private static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
+            0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,
+            0xb5470917, 0x9216d5d9, 0x8979fb1b };
+
+    private static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
+            0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3,
+            0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+            0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1,
+            0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
+            0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16,
+            0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+            0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81,
+            0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
+            0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050,
+            0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+            0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724,
+            0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
+            0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb,
+            0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+            0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe,
+            0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
+            0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6,
+            0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+            0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada,
+            0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
+            0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191,
+            0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+            0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065,
+            0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
+            0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2,
+            0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+            0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521,
+            0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, 0xb5b32944,
+            0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c,
+            0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+            0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,
+            0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c,
+            0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58,
+            0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+            0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38,
+            0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810,
+            0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc,
+            0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+            0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1,
+            0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1,
+            0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978,
+            0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+            0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef,
+            0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170,
+            0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b,
+            0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+            0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a,
+            0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263,
+            0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61,
+            0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+            0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2,
+            0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d,
+            0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1,
+            0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+            0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3,
+            0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a,
+            0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e,
+            0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+            0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3,
+            0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900,
+            0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399,
+            0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+            0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a,
+            0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9,
+            0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f,
+            0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+            0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0,
+            0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd,
+            0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab,
+            0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+            0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,
+            0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b,
+            0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8,
+            0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+            0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33,
+            0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2,
+            0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79,
+            0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+            0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d,
+            0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa,
+            0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa,
+            0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+            0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578,
+            0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4,
+            0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737,
+            0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+            0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee,
+            0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
+            0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550,
+            0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+            0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a,
+            0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
+            0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44,
+            0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+            0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711,
+            0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
+            0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd,
+            0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+            0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463,
+            0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
+            0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b,
+            0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+            0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a,
+            0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
+            0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065,
+            0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+            0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54,
+            0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
+            0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b,
+            0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+            0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,
+            0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
+            0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3,
+            0x3ac372e6 };
+
+    // bcrypt IV: "OrpheanBeholderScryDoubt"
+    static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944,
+            0x6f756274 };
+
+    // Table for Base64 encoding
+    static private final char base64_code[] = { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+            'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+            'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1',
+            '2', '3', '4', '5', '6', '7', '8', '9' };
+
+    // Table for Base64 decoding
+    static private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+            12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32,
+            33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 };
+    static final int MIN_LOG_ROUNDS = 4;
+    static final int MAX_LOG_ROUNDS = 31;
+
+    // Expanded Blowfish key
+    private int P[];
+
+    private int S[];
+
+    /**
+     * Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note
+     * that this is <strong>not</strong> compatible with the standard MIME-base64
+     * encoding.
+     * @param d the byte array to encode
+     * @param len the number of bytes to encode
+     * @param rs the destination buffer for the base64-encoded string
+     * @exception IllegalArgumentException if the length is invalid
+     */
+    static void encode_base64(byte d[], int len, StringBuilder rs) throws IllegalArgumentException {
+        int off = 0;
+        int c1, c2;
+
+        if (len <= 0 || len > d.length) {
+            throw new IllegalArgumentException("Invalid len");
+        }
+
+        while (off < len) {
+            c1 = d[off++] & 0xff;
+            rs.append(base64_code[(c1 >> 2) & 0x3f]);
+            c1 = (c1 & 0x03) << 4;
+            if (off >= len) {
+                rs.append(base64_code[c1 & 0x3f]);
+                break;
+            }
+            c2 = d[off++] & 0xff;
+            c1 |= (c2 >> 4) & 0x0f;
+            rs.append(base64_code[c1 & 0x3f]);
+            c1 = (c2 & 0x0f) << 2;
+            if (off >= len) {
+                rs.append(base64_code[c1 & 0x3f]);
+                break;
+            }
+            c2 = d[off++] & 0xff;
+            c1 |= (c2 >> 6) & 0x03;
+            rs.append(base64_code[c1 & 0x3f]);
+            rs.append(base64_code[c2 & 0x3f]);
+        }
+    }
+
+    /**
+     * Look up the 3 bits base64-encoded by the specified character, range-checking againt
+     * conversion table
+     * @param x the base64-encoded value
+     * @return the decoded value of x
+     */
+    private static byte char64(char x) {
+        if (x < 0 || x >= index_64.length) {
+            return -1;
+        }
+        return index_64[x];
+    }
+
+    /**
+     * Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that
+     * this is *not* compatible with the standard MIME-base64 encoding.
+     * @param s the string to decode
+     * @param maxolen the maximum number of bytes to decode
+     * @return an array containing the decoded bytes
+     * @throws IllegalArgumentException if maxolen is invalid
+     */
+    static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException {
+        StringBuilder rs = new StringBuilder();
+        int off = 0, slen = s.length(), olen = 0;
+        byte ret[];
+        byte c1, c2, c3, c4, o;
+
+        if (maxolen <= 0) {
+            throw new IllegalArgumentException("Invalid maxolen");
+        }
+
+        while (off < slen - 1 && olen < maxolen) {
+            c1 = char64(s.charAt(off++));
+            c2 = char64(s.charAt(off++));
+            if (c1 == -1 || c2 == -1) {
+                break;
+            }
+            o = (byte) (c1 << 2);
+            o |= (c2 & 0x30) >> 4;
+            rs.append((char) o);
+            if (++olen >= maxolen || off >= slen) {
+                break;
+            }
+            c3 = char64(s.charAt(off++));
+            if (c3 == -1) {
+                break;
+            }
+            o = (byte) ((c2 & 0x0f) << 4);
+            o |= (c3 & 0x3c) >> 2;
+            rs.append((char) o);
+            if (++olen >= maxolen || off >= slen) {
+                break;
+            }
+            c4 = char64(s.charAt(off++));
+            o = (byte) ((c3 & 0x03) << 6);
+            o |= c4;
+            rs.append((char) o);
+            ++olen;
+        }
+
+        ret = new byte[olen];
+        for (off = 0; off < olen; off++) {
+            ret[off] = (byte) rs.charAt(off);
+        }
+        return ret;
+    }
+
+    /**
+     * Blowfish encipher a single 64-bit block encoded as two 32-bit halves
+     * @param lr an array containing the two 32-bit half blocks
+     * @param off the position in the array of the blocks
+     */
+    private void encipher(int lr[], int off) {
+        int i, n, l = lr[off], r = lr[off + 1];
+
+        l ^= this.P[0];
+        for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
+            // Feistel substitution on left word
+            n = this.S[(l >> 24) & 0xff];
+            n += this.S[0x100 | ((l >> 16) & 0xff)];
+            n ^= this.S[0x200 | ((l >> 8) & 0xff)];
+            n += this.S[0x300 | (l & 0xff)];
+            r ^= n ^ this.P[++i];
+
+            // Feistel substitution on right word
+            n = this.S[(r >> 24) & 0xff];
+            n += this.S[0x100 | ((r >> 16) & 0xff)];
+            n ^= this.S[0x200 | ((r >> 8) & 0xff)];
+            n += this.S[0x300 | (r & 0xff)];
+            l ^= n ^ this.P[++i];
+        }
+        lr[off] = r ^ this.P[BLOWFISH_NUM_ROUNDS + 1];
+        lr[off + 1] = l;
+    }
+
+    /**
+     * Cycically extract a word of key material
+     * @param data the string to extract the data from
+     * @param offp a "pointer" (as a one-entry array) to the current offset into data
+     * @param signp a "pointer" (as a one-entry array) to the cumulative flag for
+     * non-benign sign extension
+     * @return correct and buggy next word of material from data as int[2]
+     */
+    private static int[] streamtowords(byte data[], int offp[], int signp[]) {
+        int i;
+        int words[] = { 0, 0 };
+        int off = offp[0];
+        int sign = signp[0];
+
+        for (i = 0; i < 4; i++) {
+            words[0] = (words[0] << 8) | (data[off] & 0xff);
+            words[1] = (words[1] << 8) | data[off]; // sign extension bug
+            if (i > 0) {
+                sign |= words[1] & 0x80;
+            }
+            off = (off + 1) % data.length;
+        }
+
+        offp[0] = off;
+        signp[0] = sign;
+        return words;
+    }
+
+    /**
+     * Cycically extract a word of key material
+     * @param data the string to extract the data from
+     * @param offp a "pointer" (as a one-entry array) to the current offset into data
+     * @return the next word of material from data
+     */
+    private static int streamtoword(byte data[], int offp[]) {
+        int signp[] = { 0 };
+        return streamtowords(data, offp, signp)[0];
+    }
+
+    /**
+     * Cycically extract a word of key material, with sign-extension bug
+     * @param data the string to extract the data from
+     * @param offp a "pointer" (as a one-entry array) to the current offset into data
+     * @return the next word of material from data
+     */
+    private static int streamtoword_bug(byte data[], int offp[]) {
+        int signp[] = { 0 };
+        return streamtowords(data, offp, signp)[1];
+    }
+
+    /**
+     * Initialise the Blowfish key schedule
+     */
+    private void init_key() {
+        this.P = P_orig.clone();
+        this.S = S_orig.clone();
+    }
+
+    /**
+     * Key the Blowfish cipher
+     * @param key an array containing the key
+     * @param sign_ext_bug true to implement the 2x bug
+     * @param safety bit 16 is set when the safety measure is requested
+     */
+    private void key(byte key[], boolean sign_ext_bug, int safety) {
+        int i;
+        int koffp[] = { 0 };
+        int lr[] = { 0, 0 };
+        int plen = this.P.length, slen = this.S.length;
+
+        for (i = 0; i < plen; i++) {
+            if (!sign_ext_bug) {
+                this.P[i] = this.P[i] ^ streamtoword(key, koffp);
+            }
+            else {
+                this.P[i] = this.P[i] ^ streamtoword_bug(key, koffp);
+            }
+        }
+
+        for (i = 0; i < plen; i += 2) {
+            encipher(lr, 0);
+            this.P[i] = lr[0];
+            this.P[i + 1] = lr[1];
+        }
+
+        for (i = 0; i < slen; i += 2) {
+            encipher(lr, 0);
+            this.S[i] = lr[0];
+            this.S[i + 1] = lr[1];
+        }
+    }
+
+    /**
+     * Perform the "enhanced key schedule" step described by Provos and Mazieres in "A
+     * Future-Adaptable Password Scheme" https://www.openbsd.org/papers/bcrypt-paper.ps
+     * @param data salt information
+     * @param key password information
+     * @param sign_ext_bug true to implement the 2x bug
+     * @param safety bit 16 is set when the safety measure is requested
+     */
+    private void ekskey(byte data[], byte key[], boolean sign_ext_bug, int safety) {
+        int i;
+        int koffp[] = { 0 }, doffp[] = { 0 };
+        int lr[] = { 0, 0 };
+        int plen = this.P.length, slen = this.S.length;
+        int signp[] = { 0 }; // non-benign sign-extension flag
+        int diff = 0; // zero iff correct and buggy are same
+
+        for (i = 0; i < plen; i++) {
+            int words[] = streamtowords(key, koffp, signp);
+            diff |= words[0] ^ words[1];
+            this.P[i] = this.P[i] ^ words[sign_ext_bug ? 1 : 0];
+        }
+
+        int sign = signp[0];
+
+        /*
+         * At this point, "diff" is zero iff the correct and buggy algorithms produced
+         * exactly the same result. If so and if "sign" is non-zero, which indicates that
+         * there was a non-benign sign extension, this means that we have a collision
+         * between the correctly computed hash for this password and a set of passwords
+         * that could be supplied to the buggy algorithm. Our safety measure is meant to
+         * protect from such many-buggy to one-correct collisions, by deviating from the
+         * correct algorithm in such cases. Let's check for this.
+         */
+        diff |= diff >> 16; /* still zero iff exact match */
+        diff &= 0xffff; /* ditto */
+        diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
+        sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
+        sign &= ~diff & safety; /* action needed? */
+
+        /*
+         * If we have determined that we need to deviate from the correct algorithm, flip
+         * bit 16 in initial expanded key. (The choice of 16 is arbitrary, but let's stick
+         * to it now. It came out of the approach we used above, and it's not any worse
+         * than any other choice we could make.)
+         *
+         * It is crucial that we don't do the same to the expanded key used in the main
+         * Eksblowfish loop. By doing it to only one of these two, we deviate from a state
+         * that could be directly specified by a password to the buggy algorithm (and to
+         * the fully correct one as well, but that's a side-effect).
+         */
+        this.P[0] ^= sign;
+
+        for (i = 0; i < plen; i += 2) {
+            lr[0] ^= streamtoword(data, doffp);
+            lr[1] ^= streamtoword(data, doffp);
+            encipher(lr, 0);
+            this.P[i] = lr[0];
+            this.P[i + 1] = lr[1];
+        }
+
+        for (i = 0; i < slen; i += 2) {
+            lr[0] ^= streamtoword(data, doffp);
+            lr[1] ^= streamtoword(data, doffp);
+            encipher(lr, 0);
+            this.S[i] = lr[0];
+            this.S[i + 1] = lr[1];
+        }
+    }
+
+    static long roundsForLogRounds(int log_rounds) {
+        if (log_rounds < 4 || log_rounds > 31) {
+            throw new IllegalArgumentException("Bad number of rounds");
+        }
+        return 1L << log_rounds;
+    }
+
+    /**
+     * Perform the central password hashing step in the bcrypt scheme
+     * @param password the password to hash
+     * @param salt the binary salt to hash with the password
+     * @param log_rounds the binary logarithm of the number of rounds of hashing to apply
+     * @param sign_ext_bug true to implement the 2x bug
+     * @param safety bit 16 is set when the safety measure is requested
+     * @return an array containing the binary hashed password
+     */
+    private byte[] crypt_raw(byte password[], byte salt[], int log_rounds, boolean sign_ext_bug, int safety) {
+        int rounds, i, j;
+        int cdata[] = bf_crypt_ciphertext.clone();
+        int clen = cdata.length;
+        byte ret[];
+
+        if (log_rounds < 4 || log_rounds > 31) {
+            throw new IllegalArgumentException("Bad number of rounds");
+        }
+        rounds = 1 << log_rounds;
+        if (salt.length != BCRYPT_SALT_LEN) {
+            throw new IllegalArgumentException("Bad salt length");
+        }
+
+        init_key();
+        ekskey(salt, password, sign_ext_bug, safety);
+        for (i = 0; i < rounds; i++) {
+            key(password, sign_ext_bug, safety);
+            key(salt, false, safety);
+        }
+
+        for (i = 0; i < 64; i++) {
+            for (j = 0; j < (clen >> 1); j++) {
+                encipher(cdata, j << 1);
+            }
+        }
+
+        ret = new byte[clen * 4];
+        for (i = 0, j = 0; i < clen; i++) {
+            ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
+            ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
+            ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
+            ret[j++] = (byte) (cdata[i] & 0xff);
+        }
+        return ret;
+    }
+
+    /**
+     * Hash a password using the OpenBSD bcrypt scheme
+     * @param password the password to hash
+     * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)
+     * @return the hashed password
+     */
+    public static String hashpw(String password, String salt) {
+        byte passwordb[];
+
+        passwordb = password.getBytes(StandardCharsets.UTF_8);
+
+        return hashpw(passwordb, salt);
+    }
+
+    /**
+     * Hash a password using the OpenBSD bcrypt scheme
+     * @param passwordb the password to hash, as a byte array
+     * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)
+     * @return the hashed password
+     */
+    public static String hashpw(byte passwordb[], String salt) {
+        BCrypt B;
+        String real_salt;
+        byte saltb[], hashed[];
+        char minor = (char) 0;
+        int rounds, off;
+        StringBuilder rs = new StringBuilder();
+
+        if (salt == null) {
+            throw new IllegalArgumentException("salt cannot be null");
+        }
+
+        int saltLength = salt.length();
+
+        if (saltLength < 28) {
+            throw new IllegalArgumentException("Invalid salt");
+        }
+
+        if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
+            throw new IllegalArgumentException("Invalid salt version");
+        }
+        if (salt.charAt(2) == '$') {
+            off = 3;
+        }
+        else {
+            minor = salt.charAt(2);
+            if ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b') || salt.charAt(3) != '$') {
+                throw new IllegalArgumentException("Invalid salt revision");
+            }
+            off = 4;
+        }
+
+        // Extract number of rounds
+        if (salt.charAt(off + 2) > '$') {
+            throw new IllegalArgumentException("Missing salt rounds");
+        }
+
+        if (off == 4 && saltLength < 29) {
+            throw new IllegalArgumentException("Invalid salt");
+        }
+        rounds = Integer.parseInt(salt.substring(off, off + 2));
+
+        real_salt = salt.substring(off + 3, off + 25);
+        saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
+
+        if (minor >= 'a') {
+            passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
+        }
+
+        B = new BCrypt();
+        hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0);
+
+        rs.append("$2");
+        if (minor >= 'a') {
+            rs.append(minor);
+        }
+        rs.append("$");
+        if (rounds < 10) {
+            rs.append("0");
+        }
+        rs.append(rounds);
+        rs.append("$");
+        encode_base64(saltb, saltb.length, rs);
+        encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
+        return rs.toString();
+    }
+
+    /**
+     * Generate a salt for use with the BCrypt.hashpw() method
+     * @param prefix the prefix value (default $2a)
+     * @param log_rounds the log2 of the number of rounds of hashing to apply - the work
+     * factor therefore increases as 2**log_rounds.
+     * @param random an instance of SecureRandom to use
+     * @return an encoded salt value
+     * @exception IllegalArgumentException if prefix or log_rounds is invalid
+     */
+    public static String gensalt(String prefix, int log_rounds, SecureRandom random) throws IllegalArgumentException {
+        StringBuilder rs = new StringBuilder();
+        byte rnd[] = new byte[BCRYPT_SALT_LEN];
+
+        if (!prefix.startsWith("$2")
+                || (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) {
+            throw new IllegalArgumentException("Invalid prefix");
+        }
+        if (log_rounds < 4 || log_rounds > 31) {
+            throw new IllegalArgumentException("Invalid log_rounds");
+        }
+
+        random.nextBytes(rnd);
+
+        rs.append("$2");
+        rs.append(prefix.charAt(2));
+        rs.append("$");
+        if (log_rounds < 10) {
+            rs.append("0");
+        }
+        rs.append(log_rounds);
+        rs.append("$");
+        encode_base64(rnd, rnd.length, rs);
+        return rs.toString();
+    }
+
+    /**
+     * Generate a salt for use with the BCrypt.hashpw() method
+     * @param prefix the prefix value (default $2a)
+     * @param log_rounds the log2 of the number of rounds of hashing to apply - the work
+     * factor therefore increases as 2**log_rounds.
+     * @return an encoded salt value
+     * @exception IllegalArgumentException if prefix or log_rounds is invalid
+     */
+    public static String gensalt(String prefix, int log_rounds) throws IllegalArgumentException {
+        return gensalt(prefix, log_rounds, new SecureRandom());
+    }
+
+    /**
+     * Generate a salt for use with the BCrypt.hashpw() method
+     * @param log_rounds the log2 of the number of rounds of hashing to apply - the work
+     * factor therefore increases as 2**log_rounds.
+     * @param random an instance of SecureRandom to use
+     * @return an encoded salt value
+     * @exception IllegalArgumentException if log_rounds is invalid
+     */
+    public static String gensalt(int log_rounds, SecureRandom random) throws IllegalArgumentException {
+        return gensalt("$2a", log_rounds, random);
+    }
+
+    /**
+     * Generate a salt for use with the BCrypt.hashpw() method
+     * @param log_rounds the log2 of the number of rounds of hashing to apply - the work
+     * factor therefore increases as 2**log_rounds.
+     * @return an encoded salt value
+     * @exception IllegalArgumentException if log_rounds is invalid
+     */
+    public static String gensalt(int log_rounds) throws IllegalArgumentException {
+        return gensalt(log_rounds, new SecureRandom());
+    }
+
+    public static String gensalt(String prefix) {
+        return gensalt(prefix, GENSALT_DEFAULT_LOG2_ROUNDS);
+    }
+
+    /**
+     * Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable
+     * default for the number of hashing rounds to apply
+     * @return an encoded salt value
+     */
+    public static String gensalt() {
+        return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
+    }
+
+    /**
+     * Check that a plaintext password matches a previously hashed one
+     * @param plaintext the plaintext password to verify
+     * @param hashed the previously-hashed password
+     * @return true if the passwords match, false otherwise
+     */
+    public static boolean checkpw(String plaintext, String hashed) {
+        return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
+    }
+
+    /**
+     * Check that a password (as a byte array) matches a previously hashed one
+     * @param passwordb the password to verify, as a byte array
+     * @param hashed the previously-hashed password
+     * @return true if the passwords match, false otherwise
+     * @since 5.3
+     */
+    public static boolean checkpw(byte[] passwordb, String hashed) {
+        return equalsNoEarlyReturn(hashed, hashpw(passwordb, hashed));
+    }
+
+    static boolean equalsNoEarlyReturn(String a, String b) {
+        return MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8), b.getBytes(StandardCharsets.UTF_8));
+    }
+}

+ 159 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/ecode/BCryptPasswordEncoder.java

@@ -0,0 +1,159 @@
+package com.storlead.framework.common.ecode;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Service;
+
+import java.security.SecureRandom;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @program: sp-cloud
+ * @description:
+ * @author: chenkq
+ * @create: 2021-10-25 18:50
+ */
+@Service
+@Primary
+public class BCryptPasswordEncoder implements PasswordEncoder {
+
+    private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2(a|y|b)?\\$(\\d\\d)\\$[./0-9A-Za-z]{53}");
+
+    private final Log logger = LogFactory.getLog(getClass());
+
+    private final int strength;
+
+    private final BCryptVersion version;
+
+    private final SecureRandom random;
+
+    public BCryptPasswordEncoder() {
+        this(-1);
+    }
+
+    /**
+     * @param strength the log rounds to use, between 4 and 31
+     */
+    public BCryptPasswordEncoder(int strength) {
+        this(strength, null);
+    }
+
+    /**
+     * @param version the version of bcrypt, can be 2a,2b,2y
+     */
+    public BCryptPasswordEncoder(BCryptVersion version) {
+        this(version, null);
+    }
+
+    /**
+     * @param version the version of bcrypt, can be 2a,2b,2y
+     * @param random the secure random instance to use
+     */
+    public BCryptPasswordEncoder(BCryptVersion version, SecureRandom random) {
+        this(version, -1, random);
+    }
+
+    /**
+     * @param strength the log rounds to use, between 4 and 31
+     * @param random the secure random instance to use
+     */
+    public BCryptPasswordEncoder(int strength, SecureRandom random) {
+        this(BCryptVersion.$2A, strength, random);
+    }
+
+    /**
+     * @param version the version of bcrypt, can be 2a,2b,2y
+     * @param strength the log rounds to use, between 4 and 31
+     */
+    public BCryptPasswordEncoder(BCryptVersion version, int strength) {
+        this(version, strength, null);
+    }
+
+    /**
+     * @param version the version of bcrypt, can be 2a,2b,2y
+     * @param strength the log rounds to use, between 4 and 31
+     * @param random the secure random instance to use
+     */
+    public BCryptPasswordEncoder(BCryptVersion version, int strength, SecureRandom random) {
+        if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) {
+            throw new IllegalArgumentException("Bad strength");
+        }
+        this.version = version;
+        this.strength = (strength == -1) ? 10 : strength;
+        this.random = random;
+    }
+
+    @Override
+    public String encode(CharSequence rawPassword) {
+        if (rawPassword == null) {
+            throw new IllegalArgumentException("rawPassword cannot be null");
+        }
+        String salt = getSalt();
+        return BCrypt.hashpw(rawPassword.toString(), salt);
+    }
+
+    private String getSalt() {
+        if (this.random != null) {
+            return BCrypt.gensalt(this.version.getVersion(), this.strength, this.random);
+        }
+        return BCrypt.gensalt(this.version.getVersion(), this.strength);
+    }
+
+    @Override
+    public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        if (rawPassword == null) {
+            throw new IllegalArgumentException("rawPassword cannot be null");
+        }
+        if (encodedPassword == null || encodedPassword.length() == 0) {
+            this.logger.warn("Empty encoded password");
+            return false;
+        }
+        if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
+            this.logger.warn("Encoded password does not look like BCrypt");
+            return false;
+        }
+        return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
+    }
+
+    @Override
+    public boolean upgradeEncoding(String encodedPassword) {
+        if (encodedPassword == null || encodedPassword.length() == 0) {
+            this.logger.warn("Empty encoded password");
+            return false;
+        }
+        Matcher matcher = this.BCRYPT_PATTERN.matcher(encodedPassword);
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Encoded password does not look like BCrypt: " + encodedPassword);
+        }
+        int strength = Integer.parseInt(matcher.group(2));
+        return strength < this.strength;
+    }
+
+    /**
+     * Stores the default bcrypt version for use in configuration.
+     *
+     * @author Lin Feng
+     */
+    public enum BCryptVersion {
+
+        $2A("$2a"),
+
+        $2Y("$2y"),
+
+        $2B("$2b");
+
+        private final String version;
+
+        BCryptVersion(String version) {
+            this.version = version;
+        }
+
+        public String getVersion() {
+            return this.version;
+        }
+
+    }
+}

+ 19 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/ecode/PasswordEncoder.java

@@ -0,0 +1,19 @@
+package com.storlead.framework.common.ecode;
+
+/**
+ * @program: sp-cloud
+ * @description:
+ * @author: chenkq
+ * @create: 2021-10-25 18:39
+ */
+
+public interface PasswordEncoder {
+
+    String encode(CharSequence var1);
+
+    boolean matches(CharSequence var1, String var2);
+
+    default boolean upgradeEncoding(String encodedPassword) {
+        return false;
+    }
+}

+ 26 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/enums/DataTypeEnum.java

@@ -0,0 +1,26 @@
+package com.storlead.framework.common.enums;
+
+/**
+ * @program: sp-sales-gotr
+ * @description:
+ * @author: chenkq
+ * @create: 2023-07-25 16:31
+ */
+public enum DataTypeEnum {
+
+    STRING(0, "字符串"),
+    INTEGER(1, "整数"),
+    DOUBLE(2, "小数"),
+    BIGDECIMAL(3, "金额"),
+    TIME(4, "时间"),
+    DATE(5, "日期"),
+    ;
+    private Integer code;
+
+    private String desc;
+
+    DataTypeEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+}

+ 6 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * 基础的通用类,和框架无关
+ *
+ * 例如说,CommonResult 为通用返回
+ */
+package com.storlead.framework.common;

+ 87 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/properties/UrlChainDefinitionPorperties.java

@@ -0,0 +1,87 @@
+package com.storlead.framework.common.properties;
+
+import lombok.Data;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @program: sp-sales
+ * @description:
+ * @author: chenkq
+ * @create: 2022-07-04 16:16
+ */
+@Data
+@Configuration
+public class UrlChainDefinitionPorperties {
+    private String contextPath = "/api" ;
+	private String [] authWhitelist = {
+			"/",
+			"/lingcun/common/view/**",
+			"/lingcun/common/download/**",
+			"/lingcun/common/pdf/**",
+			"/generic/**",
+			"/doc.html",
+			"/**/*.js",
+			"/**/*.css",
+			"/**/*.html",
+			"/**/*.svg",
+			"/**/*.pdf",
+			"/**/*.jpg",
+			"/**/*.png",
+			"/**/*.ico",
+			"/actuator",
+			"/actuator/health",
+			"/actuator/**",
+			"/app/**",
+			"/h5/**",
+			"/**/*.ttf",
+			"/**/*.woff",
+			"/druid/**",
+			"/swagger-ui.html",
+			"/**/swagger-ui",
+			"/swagger**/**",
+			"/webjars/**",
+			"/v2/**",
+			"/xwork/**",
+			"/lingcun/getPhoneCode",
+			"/lingcun/loginByMobile",
+			"/lingcun/login",
+			"/sys/auth/external_login",
+			"/lingcun/phone-login",
+			"/sys/auth/login",
+			"/sys/auth/redirect",
+			"/actuator/metrics/**",
+			"/actuator/httptrace/**",
+			"/actuator/redis/**",
+			"/sys/common/403",
+			"/sys/common/403",
+			"/reward/resource",
+			"/xwork/login",
+			"/xwork/code-login",
+			"/view/thumbnail",
+			"/sys/custom/settings/get",
+			"/tenant/license/getLicenseCode",
+			"/analysis-report/edit",
+			"/gpt/chatWebSocket/**",
+			"/sys/user/exchangeToken",
+			"/sys/menu/allMenulist",
+			"/sys/dict/pageDict",
+			"/mail/bind_mail_customer_all",
+			"/mail/bind_mail_customer",
+			"/sys/menu/update_menu_icon",
+			"/mail/file/images",
+			"/mail/file/cache_image_file",
+			"/mail/import_mail_eml",
+			"/mail/import_mail_eml_zip",
+			"/sys/test/receive_mail_test",
+			"/mail/list.json",
+			"/mail/listUrl",
+			"/mail/v1/file/content",
+			"/ram/oss/get_post_signature_for_oss_upload",
+			"/sys/permission/getUserAccessResourceTest",
+			"/mail/draft_download_url",
+			"/mail/download_url",
+			"/wxapi/wxclientmenu/**",
+			"/wxapi/wxclientmenu/*",
+			"/wxapi/getauth"
+	};
+}

+ 40 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/property/YamlPropertyResourceFactory.java

@@ -0,0 +1,40 @@
+package com.storlead.framework.common.property;
+
+import org.springframework.boot.env.YamlPropertySourceLoader;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.support.EncodedResource;
+import org.springframework.core.io.support.PropertySourceFactory;
+import org.springframework.lang.Nullable;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+
+/**
+ * 让 PropertySourceFactory 支持读取 yml/yaml格式配置
+ *
+ * @author blank
+ * @since 2021-8-13 下午 4:08
+ */
+public class YamlPropertyResourceFactory implements PropertySourceFactory {
+    /**
+     * Create a {@link PropertySource} that wraps the given resource.
+     *
+     * @param name     the name of the property source
+     * @param encodedResource the resource (potentially encoded) to wrap
+     * @return the new {@link PropertySource} (never {@code null})
+     * @throws IOException if resource resolution failed
+     */
+    @Override
+    public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource encodedResource) throws IOException {
+        String resourceName = Optional.ofNullable(name).orElse(encodedResource.getResource().getFilename());
+        if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {//yaml资源文件
+            List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, encodedResource.getResource());
+            return yamlSources.get(0);
+        } else {//返回空的Properties
+            return new PropertiesPropertySource(resourceName, new Properties());
+        }
+    }
+}

+ 44 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/system/query/MatchTypeEnum.java

@@ -0,0 +1,44 @@
+package com.storlead.framework.common.system.query;
+
+import com.storlead.framework.common.util.ConvertUtils;
+
+/**
+ * 查询链接规则
+ *
+ * @Author Sunjianlei
+ */
+public enum MatchTypeEnum {
+
+    WHERE(" WHERE"),
+    AND(" AND"),
+    OR(" OR");
+
+    private String value;
+
+    MatchTypeEnum(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public static MatchTypeEnum getByValue(Object value) {
+        if (ConvertUtils.isEmpty(value)) {
+            return null;
+        }
+        return getByValue(value.toString());
+    }
+
+    public static MatchTypeEnum getByValue(String value) {
+        if (ConvertUtils.isEmpty(value)) {
+            return null;
+        }
+        for (MatchTypeEnum val : values()) {
+            if (val.getValue().toLowerCase().equals(value.toLowerCase())) {
+                return val;
+            }
+        }
+        return null;
+    }
+}

+ 55 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/system/query/QueryCondition.java

@@ -0,0 +1,55 @@
+package com.storlead.framework.common.system.query;
+
+import java.io.Serializable;
+
+public class QueryCondition implements Serializable {
+
+	private static final long serialVersionUID = 4740166316629191651L;
+
+	private String field;
+	private String type;
+	private String rule;
+	private String val;
+
+	public String getField() {
+		return field;
+	}
+
+	public void setField(String field) {
+		this.field = field;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getRule() {
+		return rule;
+	}
+
+	public void setRule(String rule) {
+		this.rule = rule;
+	}
+
+	public String getVal() {
+		return val;
+	}
+
+	public void setVal(String val) {
+		this.val = val;
+	}
+
+	@Override
+	public String toString(){
+		StringBuffer sb =new StringBuffer();
+		if(field == null || "".equals(field)){
+			return "";
+		}
+		sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.val);
+		return sb.toString();
+	}
+}

+ 71 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/system/query/QueryRuleEnum.java

@@ -0,0 +1,71 @@
+package com.storlead.framework.common.system.query;
+
+import com.storlead.framework.common.util.ConvertUtils;
+
+/**
+ * Query 规则 常量
+ * @Author Scott
+ * @Date 2019年02月14日
+ */
+public enum QueryRuleEnum {
+
+    GT(">","gt","大于"),
+    GE(">=","ge","大于等于"),
+    LT("<","lt","小于"),
+    LE("<=","le","小于等于"),
+    EQ("=","eq","等于"),
+    NE("!=","ne","不等于"),
+    IN("IN","in","包含"),
+    LIKE("LIKE","like","全模糊"),
+    LEFT_LIKE("LEFT_LIKE","left_like","左模糊"),
+    RIGHT_LIKE("RIGHT_LIKE","right_like","右模糊"),
+    SQL_RULES("USE_SQL_RULES","ext","自定义SQL片段");
+
+    private String value;
+
+    private String condition;
+
+    private String msg;
+
+    QueryRuleEnum(String value, String condition, String msg){
+        this.value = value;
+        this.condition = condition;
+        this.msg = msg;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public String getCondition() {
+		return condition;
+	}
+
+	public void setCondition(String condition) {
+		this.condition = condition;
+	}
+
+	public static QueryRuleEnum getByValue(String value){
+    	if(ConvertUtils.isEmpty(value)) {
+    		return null;
+    	}
+        for(QueryRuleEnum val :values()){
+            if (val.getValue().equals(value) || val.getCondition().equals(value)){
+                return val;
+            }
+        }
+        return  null;
+    }
+}

+ 88 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/thread/ThreadPoolUtil.java

@@ -0,0 +1,88 @@
+package com.storlead.framework.common.thread;
+
+
+import java.util.List;
+import java.util.concurrent.*;
+
+/**
+ * @program: sp-project-system
+ * @description: 线程池工具类
+ * @author: chenkq
+ * @create: 2021-09-13 08:55
+ */
+public class ThreadPoolUtil {
+    /**
+     * 核心线程池的数量,同时能够执行的线程数量
+     */
+    private final static int corePoolSize  = Runtime.getRuntime().availableProcessors() * 2 + 1;
+    /**
+     * 最大线程池数量,表示当缓冲队列满的时候能继续容纳的等待任务的数量
+     */
+    private final static int maxPoolSize  = corePoolSize;
+    /**
+     * 存活时间
+     */
+    private final static long keepAliveTime = 60L;
+    private final static TimeUnit unit = TimeUnit.SECONDS;
+
+    /**
+     * 线程等待队列
+     */
+    private static LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(2000);
+
+
+
+    /**
+     * 线程池对象
+     */
+    private static ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
+            keepAliveTime, unit, queue, new ThreadPoolExecutor.AbortPolicy());
+
+    public static ThreadPoolExecutor getThreadExecutor() {
+        return threadExecutor;
+    }
+
+
+    /**
+     * 向线程池提交一个任务,返回线程结果
+     * @param r
+     * @return
+     */
+    public static Future<?> submit(Callable<?> r) {
+        return threadExecutor.submit(r);
+    }
+
+    /**
+     * 向线程池提交一个任务,返回线程结果
+     * @param rs
+     * @return
+     */
+    public static List<Future<?>> submits(List rs) {
+        try {
+            List list = threadExecutor.invokeAll(rs);
+            return list;
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 向线程池提交一个任务,不关心处理结果
+     * @param r
+     */
+    public static void execute(Runnable r) {
+        threadExecutor.execute(r);
+    }
+
+    /** 获取当前线程池线程数量 */
+    public static int getSize() {
+        return threadExecutor.getPoolSize();
+    }
+
+    /** 获取当前活动的线程数量 */
+    public static int getActiveCount() {
+        return threadExecutor.getActiveCount();
+    }
+}
+

+ 619 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/ConvertUtils.java

@@ -0,0 +1,619 @@
+package com.storlead.framework.common.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.sql.Date;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Slf4j
+public class ConvertUtils {
+	public static boolean isEmpty(Object object) {
+		if (object == null) {
+			return (true);
+		}
+		if ("".equals(object)) {
+			return (true);
+		}
+		if ("null".equals(object)) {
+			return (true);
+		}
+		return (false);
+	}
+
+	public static boolean isNotEmpty(Object object) {
+		if (object != null && !object.equals("") && !object.equals("null")) {
+			return (true);
+		}
+		return (false);
+	}
+
+	public static String decode(String strIn, String sourceCode, String targetCode) {
+		String temp = code2code(strIn, sourceCode, targetCode);
+		return temp;
+	}
+
+	public static String StrToUTF(String strIn, String sourceCode, String targetCode) {
+		strIn = "";
+		try {
+			strIn = new String(strIn.getBytes("ISO-8859-1"), "GBK");
+		} catch (UnsupportedEncodingException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return strIn;
+
+	}
+
+	private static String code2code(String strIn, String sourceCode, String targetCode) {
+		String strOut = null;
+		if (strIn == null || (strIn.trim()).equals("")) {
+			return strIn;
+		}
+		try {
+			byte[] b = strIn.getBytes(sourceCode);
+			for (int i = 0; i < b.length; i++) {
+				System.out.print(b[i] + "  ");
+			}
+			strOut = new String(b, targetCode);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+		return strOut;
+	}
+
+	public static int getInt(String s, int defval) {
+		if (s == null || s == "") {
+			return (defval);
+		}
+		try {
+			return (Integer.parseInt(s));
+		} catch (NumberFormatException e) {
+			return (defval);
+		}
+	}
+
+	public static int getInt(String s) {
+		if (s == null || s == "") {
+			return 0;
+		}
+		try {
+			return (Integer.parseInt(s));
+		} catch (NumberFormatException e) {
+			return 0;
+		}
+	}
+
+	public static int getInt(String s, Integer df) {
+		if (s == null || s == "") {
+			return df;
+		}
+		try {
+			return (Integer.parseInt(s));
+		} catch (NumberFormatException e) {
+			return 0;
+		}
+	}
+
+	public static Integer[] getInts(String[] s) {
+		Integer[] integer = new Integer[s.length];
+		if (s == null) {
+			return null;
+		}
+		for (int i = 0; i < s.length; i++) {
+			integer[i] = Integer.parseInt(s[i]);
+		}
+		return integer;
+
+	}
+
+	public static double getDouble(String s, double defval) {
+		if (s == null || s == "") {
+			return (defval);
+		}
+		try {
+			return (Double.parseDouble(s));
+		} catch (NumberFormatException e) {
+			return (defval);
+		}
+	}
+
+	public static double getDou(Double s, double defval) {
+		if (s == null) {
+			return (defval);
+		}
+		return s;
+	}
+
+	/*public static Short getShort(String s) {
+		if (StringUtil.isNotEmpty(s)) {
+			return (Short.parseShort(s));
+		} else {
+			return null;
+		}
+	}*/
+
+	public static int getInt(Object object, int defval) {
+		if (isEmpty(object)) {
+			return (defval);
+		}
+		try {
+			return (Integer.parseInt(object.toString()));
+		} catch (NumberFormatException e) {
+			return (defval);
+		}
+	}
+
+	public static Integer getInt(Object object) {
+		if (isEmpty(object)) {
+			return null;
+		}
+		try {
+			return (Integer.parseInt(object.toString()));
+		} catch (NumberFormatException e) {
+			return null;
+		}
+	}
+
+	public static int getInt(BigDecimal s, int defval) {
+		if (s == null) {
+			return (defval);
+		}
+		return s.intValue();
+	}
+
+	public static Integer[] getIntegerArry(String[] object) {
+		int len = object.length;
+		Integer[] result = new Integer[len];
+		try {
+			for (int i = 0; i < len; i++) {
+				result[i] = new Integer(object[i].trim());
+			}
+			return result;
+		} catch (NumberFormatException e) {
+			return null;
+		}
+	}
+
+	public static String getString(String s) {
+		return (getString(s, ""));
+	}
+
+	/**
+	 * 转义成Unicode编码
+	 * @param s
+	 * @return
+	 */
+	/*public static String escapeJava(Object s) {
+		return StringEscapeUtils.escapeJava(getString(s));
+	}*/
+
+	public static String getString(Object object) {
+		if (isEmpty(object)) {
+			return "";
+		}
+		return (object.toString().trim());
+	}
+
+	public static String getString(int i) {
+		return (String.valueOf(i));
+	}
+
+	public static String getString(float i) {
+		return (String.valueOf(i));
+	}
+
+	public static String getString(String s, String defval) {
+		if (isEmpty(s)) {
+			return (defval);
+		}
+		return (s.trim());
+	}
+
+	public static String getString(Object s, String defval) {
+		if (isEmpty(s)) {
+			return (defval);
+		}
+		return (s.toString().trim());
+	}
+
+	public static long stringToLong(String str) {
+		Long test = new Long(0);
+		try {
+			test = Long.valueOf(str);
+		} catch (Exception e) {
+		}
+		return test.longValue();
+	}
+
+	/**
+	 * 获取本机IP
+	 */
+	public static String getIp() {
+		String ip = null;
+		try {
+			InetAddress address = InetAddress.getLocalHost();
+			ip = address.getHostAddress();
+
+		} catch (UnknownHostException e) {
+			e.printStackTrace();
+		}
+		return ip;
+	}
+
+	/**
+	 * 判断一个类是否为基本数据类型。
+	 *
+	 * @param clazz
+	 *            要判断的类。
+	 * @return true 表示为基本数据类型。
+	 */
+	private static boolean isBaseDataType(Class clazz) throws Exception {
+		return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
+	}
+
+	/**
+	 * @param request
+	 *            IP
+	 * @return IP Address
+	 */
+//	public static String getIpAddrByRequest(HttpServletRequest request) {
+//		String ip = request.getHeader("x-forwarded-for");
+//		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+//			ip = request.getHeader("Proxy-Client-IP");
+//		}
+//		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+//			ip = request.getHeader("WL-Proxy-Client-IP");
+//		}
+//		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+//			ip = request.getRemoteAddr();
+//		}
+//		return ip;
+//	}
+
+	/**
+	 * @return 本机IP
+	 * @throws SocketException
+	 */
+	public static String getRealIp() throws SocketException {
+		String localip = null;// 本地IP,如果没有配置外网IP则返回它
+		String netip = null;// 外网IP
+
+		Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
+		InetAddress ip = null;
+		boolean finded = false;// 是否找到外网IP
+		while (netInterfaces.hasMoreElements() && !finded) {
+			NetworkInterface ni = netInterfaces.nextElement();
+			Enumeration<InetAddress> address = ni.getInetAddresses();
+			while (address.hasMoreElements()) {
+				ip = address.nextElement();
+				if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
+					netip = ip.getHostAddress();
+					finded = true;
+					break;
+				} else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
+					localip = ip.getHostAddress();
+				}
+			}
+		}
+
+		if (netip != null && !"".equals(netip)) {
+			return netip;
+		} else {
+			return localip;
+		}
+	}
+
+	/**
+	 * java去除字符串中的空格、回车、换行符、制表符
+	 *
+	 * @param str
+	 * @return
+	 */
+	public static String replaceBlank(String str) {
+		String dest = "";
+		if (str != null) {
+			Pattern p = Pattern.compile("\\s*|\t|\r|\n");
+			Matcher m = p.matcher(str);
+			dest = m.replaceAll("");
+		}
+		return dest;
+
+	}
+
+	/**
+	 * 判断元素是否在数组内
+	 *
+	 * @param substring
+	 * @param source
+	 * @return
+	 */
+	public static boolean isIn(String substring, String[] source) {
+		if (source == null || source.length == 0) {
+			return false;
+		}
+		for (int i = 0; i < source.length; i++) {
+			String aSource = source[i];
+			if (aSource.equals(substring)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * 获取Map对象
+	 */
+	public static Map<Object, Object> getHashMap() {
+		return new HashMap<Object, Object>();
+	}
+
+	/**
+	 * SET转换MAP
+	 *
+	 * @param str
+	 * @return
+	 */
+	public static Map<Object, Object> SetToMap(Set<Object> setobj) {
+		Map<Object, Object> map = getHashMap();
+		for (Iterator iterator = setobj.iterator(); iterator.hasNext();) {
+			Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) iterator.next();
+			map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim());
+		}
+		return map;
+
+	}
+
+	public static boolean isInnerIP(String ipAddress) {
+		boolean isInnerIp = false;
+		long ipNum = getIpNum(ipAddress);
+		/**
+		 * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址
+		 **/
+		long aBegin = getIpNum("10.0.0.0");
+		long aEnd = getIpNum("10.255.255.255");
+		long bBegin = getIpNum("172.16.0.0");
+		long bEnd = getIpNum("172.31.255.255");
+		long cBegin = getIpNum("192.168.0.0");
+		long cEnd = getIpNum("192.168.255.255");
+		isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || ipAddress.equals("127.0.0.1");
+		return isInnerIp;
+	}
+
+	private static long getIpNum(String ipAddress) {
+		String[] ip = ipAddress.split("\\.");
+		long a = Integer.parseInt(ip[0]);
+		long b = Integer.parseInt(ip[1]);
+		long c = Integer.parseInt(ip[2]);
+		long d = Integer.parseInt(ip[3]);
+
+		long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
+		return ipNum;
+	}
+
+	private static boolean isInner(long userIp, long begin, long end) {
+		return (userIp >= begin) && (userIp <= end);
+	}
+
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。
+	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+	 * 例如:hello_world->helloWorld
+	 *
+	 * @param name
+	 *            转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String camelName(String name) {
+		StringBuilder result = new StringBuilder();
+		// 快速检查
+		if (name == null || name.isEmpty()) {
+			// 没必要转换
+			return "";
+		} else if (!name.contains("_")) {
+			// 不含下划线,仅将首字母小写
+			//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+			//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+			return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
+			//update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+		}
+		// 用下划线将原始字符串分割
+		String camels[] = name.split("_");
+		for (String camel : camels) {
+			// 跳过原始字符串中开头、结尾的下换线或双重下划线
+			if (camel.isEmpty()) {
+				continue;
+			}
+			// 处理真正的驼峰片段
+			if (result.length() == 0) {
+				// 第一个驼峰片段,全部字母都小写
+				result.append(camel.toLowerCase());
+			} else {
+				// 其他的驼峰片段,首字母大写
+				result.append(camel.substring(0, 1).toUpperCase());
+				result.append(camel.substring(1).toLowerCase());
+			}
+		}
+		return result.toString();
+	}
+
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。
+	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+	 * 例如:hello_world,test_id->helloWorld,testId
+	 *
+	 * @param name
+	 *            转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String camelNames(String names) {
+		if(names==null||names.equals("")){
+			return null;
+		}
+		StringBuffer sf = new StringBuffer();
+		String[] fs = names.split(",");
+		for (String field : fs) {
+			field = camelName(field);
+			sf.append(field + ",");
+		}
+		String result = sf.toString();
+		return result.substring(0, result.length() - 1);
+	}
+
+	//update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+	/**
+	 * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
+	 * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+	 * 例如:hello_world->HelloWorld
+	 *
+	 * @param name
+	 *            转换前的下划线大写方式命名的字符串
+	 * @return 转换后的驼峰式命名的字符串
+	 */
+	public static String camelNameCapFirst(String name) {
+		StringBuilder result = new StringBuilder();
+		// 快速检查
+		if (name == null || name.isEmpty()) {
+			// 没必要转换
+			return "";
+		} else if (!name.contains("_")) {
+			// 不含下划线,仅将首字母小写
+			return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
+		}
+		// 用下划线将原始字符串分割
+		String camels[] = name.split("_");
+		for (String camel : camels) {
+			// 跳过原始字符串中开头、结尾的下换线或双重下划线
+			if (camel.isEmpty()) {
+				continue;
+			}
+			// 其他的驼峰片段,首字母大写
+			result.append(camel.substring(0, 1).toUpperCase());
+			result.append(camel.substring(1).toLowerCase());
+		}
+		return result.toString();
+	}
+	//update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+
+	/**
+	 * 将驼峰命名转化成下划线
+	 * @param para
+	 * @return
+	 */
+	public static String camelToUnderline(String para){
+        if(para.length()<3){
+        	return para.toLowerCase();
+        }
+        StringBuilder sb=new StringBuilder(para);
+        int temp=0;//定位
+        //从第三个字符开始 避免命名不规范
+        for(int i=2;i<para.length();i++){
+            if(Character.isUpperCase(para.charAt(i))){
+                sb.insert(i+temp, "_");
+                temp+=1;
+            }
+        }
+        return sb.toString().toLowerCase();
+	}
+
+	/**
+	 * 随机数
+	 * @param place 定义随机数的位数
+	 */
+	public static String randomGen(int place) {
+		String base = "qwertyuioplkjhgfdsazxcvbnmQAZWSXEDCRFVTGBYHNUJMIKLOP0123456789";
+		StringBuffer sb = new StringBuffer();
+		Random rd = new Random();
+		for(int i=0;i<place;i++) {
+			sb.append(base.charAt(rd.nextInt(base.length())));
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * 获取类的所有属性,包括父类
+	 *
+	 * @param object
+	 * @return
+	 */
+	public static Field[] getAllFields(Object object) {
+		Class<?> clazz = object.getClass();
+		List<Field> fieldList = new ArrayList<>();
+		while (clazz != null) {
+			fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
+			clazz = clazz.getSuperclass();
+		}
+		Field[] fields = new Field[fieldList.size()];
+		fieldList.toArray(fields);
+		return fields;
+	}
+
+	/**
+	  * 将map的key全部转成小写
+	 * @param list
+	 * @return
+	 */
+	public static List<Map<String, Object>> toLowerCasePageList(List<Map<String, Object>> list){
+		List<Map<String, Object>> select = new ArrayList<>();
+		for (Map<String, Object> row : list) {
+			 Map<String, Object> resultMap = new HashMap<>();
+			 Set<String> keySet = row.keySet();
+			 for (String key : keySet) {
+				 String newKey = key.toLowerCase();
+				 resultMap.put(newKey, row.get(key));
+			 }
+			 select.add(resultMap);
+		}
+		return select;
+	}
+
+	/**
+	 * 将entityList转换成modelList
+	 * @param fromList
+	 * @param tClass
+	 * @param <F>
+	 * @param <T>
+	 * @return
+	 */
+	public static<F,T> List<T> entityListToModelList(List<F> fromList, Class<T> tClass){
+		if(fromList.isEmpty() || fromList == null){
+			return null;
+		}
+		List<T> tList = new ArrayList<>();
+		for(F f : fromList){
+			T t = entityToModel(f, tClass);
+			tList.add(t);
+		}
+		return tList;
+	}
+
+	public static<F,T> T entityToModel(F entity, Class<T> modelClass) {
+		log.debug("entityToModel : Entity属性的值赋值到Model");
+		Object model = null;
+		if (entity == null || modelClass ==null) {
+			return null;
+		}
+
+		try {
+			model = modelClass.newInstance();
+		} catch (InstantiationException e) {
+			log.error("entityToModel : 实例化异常", e);
+		} catch (IllegalAccessException e) {
+			log.error("entityToModel : 安全权限异常", e);
+		}
+		BeanUtils.copyProperties(entity, model);
+		return (T)model;
+	}
+
+}

+ 905 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/DateUtil.java

@@ -0,0 +1,905 @@
+package com.storlead.framework.common.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.Assert;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * 时间/日期处理类
+ *
+ * @author blank
+ * @since 2018/5/17 上午10:16
+ */
+public class DateUtil {
+
+
+    /** 时间日期格式化到年月日时分秒. */
+    public static final String dateFormatYMDHMS = "yyyy-MM-dd HH:mm:ss";
+
+    /** 时间日期格式化到年月日时分秒无间隔 */
+    public static final String dateFormatYMDHMSSimple = "yyyyMMddHHmmss";
+
+    /** 时间日期格式化到年月日无间隔 */
+    public static final String dateFormatYMDSimple = "yyyyMMdd";
+
+    /** 时间日期格式化到年 */
+    public static final String dateFormatYear = "yyyy";
+
+    /** 时间日期格式化到月 */
+    public static final String dateFormatMonth = "MM";
+
+    /** 时间日期格式化到日 */
+    public static final String dateFormatDay = "dd";
+
+    /** 时间日期格式化到年月日. */
+    public static final String dateFormatYMD = "yyyy-MM-dd";
+
+    /** 时间日期格式化到年月日带中文. */
+    public static final String dateFormatYMDCN = "yyyy年MM月dd日";
+
+    /** 时间日期格式化到年月. */
+    public static final String dateFormatYM = "yyyy-MM";
+
+    /** 时间日期格式化到年月日时分. */
+    public static final String dateFormatYMDHM = "yyyy-MM-dd HH:mm";
+
+    /** 时间日期格式化到年月日时分. */
+    public static final String dateFormatYMDHMCN = "yyyy年MM月dd日 HH:mm";
+
+    /** 时间日期格式化到月日. */
+    public static final String dateFormatMD = "MM/dd";
+
+    /** 时分秒. */
+    public static final String dateFormatHMS = "HH:mm:ss";
+
+    /** 时分. */
+    public static final String dateFormatHM = "HH:mm";
+
+    /** 上午. */
+    public static final String AM = "AM";
+
+    /** 下午. */
+    public static final String PM = "PM";
+
+    /**
+     * 描述:String类型的日期时间转化为Date类型.
+     *
+     * @param strDate
+     *            String形式的日期时间
+     * @param format
+     *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
+     * @return Date Date类型日期时间
+     */
+    public static Date getDateByFormat(String strDate, String format) {
+        SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+        Date date = null;
+        try {
+            date = mSimpleDateFormat.parse(strDate);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return date;
+    }
+
+    /**
+     * 描述:获取偏移之后的Date.
+     *
+     * @param date
+     *            日期时间
+     * @param calendarField
+     *            Calendar属性,对应offset的值,
+     *            如(Calendar.DATE,表示+offset天,Calendar.HOUR_OF_DAY,表示+offset小时)
+     * @param offset
+     *            偏移(值大于0,表示+,值小于0,表示-)
+     * @return Date 偏移之后的日期时间
+     */
+    public static Date getDateByOffset(Date date, int calendarField, int offset) {
+        Calendar c = new GregorianCalendar();
+        try {
+            c.setTime(date);
+            c.add(calendarField, offset);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return c.getTime();
+    }
+
+    /**
+     * 描述:获取指定日期时间的字符串(可偏移).
+     *
+     * @param strDate
+     *            String形式的日期时间
+     * @param format
+     *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
+     * @param calendarField
+     *            Calendar属性,对应offset的值,
+     *            如(Calendar.DATE,表示+offset天,Calendar.HOUR_OF_DAY,表示+offset小时)
+     * @param offset
+     *            偏移(值大于0,表示+,值小于0,表示-)
+     * @return String String类型的日期时间
+     */
+    public static String getStringByOffset(String strDate, String format, int calendarField, int offset) {
+        String mDateTime = null;
+        try {
+            Calendar c = new GregorianCalendar();
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            c.setTime(mSimpleDateFormat.parse(strDate));
+            c.add(calendarField, offset);
+            mDateTime = mSimpleDateFormat.format(c.getTime());
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return mDateTime;
+    }
+
+    /**
+     * 描述:Date类型转化为String类型(可偏移).
+     *
+     * @param date
+     *            the date
+     * @param format
+     *            the format
+     * @param calendarField
+     *            the calendar field
+     * @param offset
+     *            the offset
+     * @return String String类型日期时间
+     */
+    public static String getStringByOffset(Date date, String format, int calendarField, int offset) {
+        String strDate = null;
+        try {
+            Calendar c = new GregorianCalendar();
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            c.setTime(date);
+            c.add(calendarField, offset);
+            strDate = mSimpleDateFormat.format(c.getTime());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return strDate;
+    }
+
+    /**
+     * 描述:Date类型转化为String类型.
+     *
+     * @param date
+     *            the date
+     * @param format
+     *            the format
+     * @return String String类型日期时间
+     */
+    public static String getStringByFormat(Date date, String format) {
+        SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+        String strDate = null;
+        try {
+            strDate = mSimpleDateFormat.format(date);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return strDate;
+    }
+
+    /**
+     * 描述:获取指定日期时间的字符串,用于导出想要的格式.
+     *
+     * @param strDate
+     *            String形式的日期时间,必须为yyyy-MM-dd HH:mm:ss格式
+     * @param format
+     *            输出格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
+     * @return String 转换后的String类型的日期时间
+     */
+    public static String getStringByFormat(String strDate, String format) {
+        String mDateTime = null;
+        try {
+            Calendar c = new GregorianCalendar();
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(dateFormatYMDHMS);
+            c.setTime(mSimpleDateFormat.parse(strDate));
+            SimpleDateFormat mSimpleDateFormat2 = new SimpleDateFormat(format);
+            mDateTime = mSimpleDateFormat2.format(c.getTime());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return mDateTime;
+    }
+
+    /**
+     * 描述:获取milliseconds表示的日期时间的字符串.
+     *
+     * @param milliseconds
+     *            the milliseconds
+     * @param format
+     *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
+     * @return String 日期时间字符串
+     */
+    public static String getStringByFormat(long milliseconds, String format) {
+        String thisDateTime = null;
+        try {
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            thisDateTime = mSimpleDateFormat.format(milliseconds);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return thisDateTime;
+    }
+
+    /**
+     * 描述:获取表示当前日期时间的字符串.
+     *
+     * @param format
+     *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
+     * @return String String类型的当前日期时间
+     */
+    public static String getCurrentDate(String format) {
+        String curDateTime = null;
+        try {
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            Calendar c = new GregorianCalendar();
+            curDateTime = mSimpleDateFormat.format(c.getTime());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return curDateTime;
+
+    }
+
+    /**
+     * 获取当前日期时间的字符串 格式为 yyyyMMdd
+     *
+     * @return java.lang.String
+     * @author blank
+     * @since 2018-12-19 下午 12:01
+     */
+    public static String getCurrentDateString() {
+        return DateUtil.getCurrentDate(dateFormatYMDSimple);
+
+    }
+
+
+    /**
+     * 获取当前日期时间的字符串 格式为 yyyyMMddHHmmss
+     *
+     * @return java.lang.String
+     * @author blank
+     * @since 2018-12-19 下午 12:01
+     */
+    public static String getCurrentDateYearMonthDayMinSecString() {
+        return DateUtil.getCurrentDate(dateFormatYMDHMSSimple);
+
+    }
+
+    /**
+     * 获取当前日期时间的字符串 格式为 yyyy年-MM月-dd日
+     *
+     * @return 格式为 yyyy年-MM月-dd日
+     */
+    public static String getCurrentDateCNString() {
+        return DateUtil.getCurrentDate(dateFormatYMDCN);
+
+    }
+
+    /**
+     * 获取当前日期 年 的字符串 格式为 yyyy
+     *
+     * @return java.lang.String
+     * @author blank
+     * @since 2019-1-10 下午 4:30
+     */
+    public static String getCurrentDateYear() {
+        return DateUtil.getCurrentDate(dateFormatYear);
+    }
+
+    /**
+     * 获取当前日期 月 的字符串 格式为 MM
+     *
+     * @return java.lang.String
+     * @author blank
+     * @since 2019-1-10 下午 4:31
+     */
+    public static String getCurrentDateMonth() {
+        return DateUtil.getCurrentDate(dateFormatMonth);
+    }
+
+    /**
+     * 获取当前日期 天 的字符串 格式为 dd
+     *
+     * @return java.lang.String
+     * @author blank
+     * @since 2019-1-10 下午 4:32
+     */
+    public static String getCurrentDateDay() {
+        return DateUtil.getCurrentDate(dateFormatDay);
+    }
+
+    /**
+     * 返回当前系统时间毫秒值
+     *
+     * @return java.lang.String
+     * @author blank
+     * @since 2018-12-19 下午 12:03
+     */
+    public static String getCurrentTimeMills(){
+        return System.currentTimeMillis() + "";
+    }
+
+
+    /**
+     * 描述:获取表示当前日期时间的字符串(可偏移).
+     *
+     * @param format
+     *            格式化字符串,如:"yyyy-MM-dd HH:mm:ss"
+     * @param calendarField
+     *            Calendar属性,对应offset的值,
+     *            如(Calendar.DATE,表示+offset天,Calendar.HOUR_OF_DAY,表示+offset小时)
+     * @param offset
+     *            偏移(值大于0,表示+,值小于0,表示-)
+     * @return String String类型的日期时间
+     */
+    public static String getCurrentDateByOffset(String format, int calendarField, int offset) {
+        String mDateTime = null;
+        try {
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            Calendar c = new GregorianCalendar();
+            c.add(calendarField, offset);
+            mDateTime = mSimpleDateFormat.format(c.getTime());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return mDateTime;
+
+    }
+
+    /**
+     * 描述:计算两个日期所差的天数.
+     *
+     * @param milliseconds1
+     *            the milliseconds1
+     * @param milliseconds2
+     *            the milliseconds2
+     * @return int 所差的天数
+     */
+    public static int getOffectDay(long milliseconds1, long milliseconds2) {
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTimeInMillis(milliseconds1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTimeInMillis(milliseconds2);
+        // 先判断是否同年
+        int y1 = calendar1.get(Calendar.YEAR);
+        int y2 = calendar2.get(Calendar.YEAR);
+        int d1 = calendar1.get(Calendar.DAY_OF_YEAR);
+        int d2 = calendar2.get(Calendar.DAY_OF_YEAR);
+        int maxDays = 0;
+        int day = 0;
+        if (y1 - y2 > 0) {
+            maxDays = calendar2.getActualMaximum(Calendar.DAY_OF_YEAR);
+            day = d1 - d2 + maxDays;
+        } else if (y1 - y2 < 0) {
+            maxDays = calendar1.getActualMaximum(Calendar.DAY_OF_YEAR);
+            day = d1 - d2 - maxDays;
+        } else {
+            day = d1 - d2;
+        }
+        return day;
+    }
+
+    /**
+     * 描述:计算两个日期所差的小时数.
+     *
+     * @param date1
+     *            第一个时间的毫秒表示
+     * @param date2
+     *            第二个时间的毫秒表示
+     * @return int 所差的小时数
+     */
+    public static int getOffsetHour(long date1, long date2) {
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTimeInMillis(date1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTimeInMillis(date2);
+        int h1 = calendar1.get(Calendar.HOUR_OF_DAY);
+        int h2 = calendar2.get(Calendar.HOUR_OF_DAY);
+        int h = 0;
+        int day = getOffectDay(date1, date2);
+        h = h1 - h2 + day * 24;
+        return h;
+    }
+
+    /**
+     * 描述:计算两个日期所差的分钟数.
+     *
+     * @param date1
+     *            第一个时间的毫秒表示
+     * @param date2
+     *            第二个时间的毫秒表示
+     * @return int 所差的分钟数
+     */
+    public static int getOffsetMinutes(long date1, long date2) {
+        Calendar calendar1 = Calendar.getInstance();
+        calendar1.setTimeInMillis(date1);
+        Calendar calendar2 = Calendar.getInstance();
+        calendar2.setTimeInMillis(date2);
+        int m1 = calendar1.get(Calendar.MINUTE);
+        int m2 = calendar2.get(Calendar.MINUTE);
+        int h = getOffsetHour(date1, date2);
+        int m = 0;
+        m = m1 - m2 + h * 60;
+        return m;
+    }
+
+    /**
+     * 描述:获取本周一.
+     *
+     * @param format
+     *            the format
+     * @return String String类型日期时间
+     */
+    public static String getFirstDayOfWeek(String format) {
+        return getDayOfWeek(format, Calendar.MONDAY);
+    }
+
+    /**
+     * 描述:获取本周日.
+     *
+     * @param format
+     *            the format
+     * @return String String类型日期时间
+     */
+    public static String getLastDayOfWeek(String format) {
+        return getDayOfWeek(format, Calendar.SUNDAY);
+    }
+
+    /**
+     * 描述:获取本周的某一天.
+     *
+     * @param format
+     *            the format
+     * @param calendarField
+     *            the calendar field
+     * @return String String类型日期时间
+     */
+    private static String getDayOfWeek(String format, int calendarField) {
+        String strDate = null;
+        try {
+            Calendar c = new GregorianCalendar();
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            int week = c.get(Calendar.DAY_OF_WEEK);
+            if (week == calendarField) {
+                strDate = mSimpleDateFormat.format(c.getTime());
+            } else {
+                int offectDay = calendarField - week;
+                if (calendarField == Calendar.SUNDAY) {
+                    offectDay = 7 - Math.abs(offectDay);
+                }
+                c.add(Calendar.DATE, offectDay);
+                strDate = mSimpleDateFormat.format(c.getTime());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return strDate;
+    }
+
+    /**
+     * 描述:获取本月第一天.
+     *
+     * @param format
+     *            the format
+     * @return String String类型日期时间
+     */
+    public static String getFirstDayOfMonth(String format) {
+        String strDate = null;
+        try {
+            Calendar c = new GregorianCalendar();
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            // 当前月的第一天
+            c.set(GregorianCalendar.DAY_OF_MONTH, 1);
+            strDate = mSimpleDateFormat.format(c.getTime());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return strDate;
+
+    }
+
+    /**
+     * 描述:获取本月最后一天.
+     *
+     * @param format
+     *            the format
+     * @return String String类型日期时间
+     */
+    public static String getLastDayOfMonth(String format) {
+        String strDate = null;
+        try {
+            Calendar c = new GregorianCalendar();
+            SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat(format);
+            // 当前月的最后一天
+            c.set(Calendar.DATE, 1);
+            c.roll(Calendar.DATE, -1);
+            strDate = mSimpleDateFormat.format(c.getTime());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return strDate;
+    }
+
+    /**
+     * 描述:获取表示当前日期的0点时间毫秒数.
+     *
+     * @return the first time of day
+     */
+    public static long getFirstTimeOfDay() {
+        Date date = null;
+        try {
+            String currentDate = getCurrentDate(dateFormatYMD);
+            date = getDateByFormat(currentDate + " 00:00:00", dateFormatYMDHMS);
+            return date.getTime();
+        } catch (Exception ignored) {
+        }
+        return -1;
+    }
+
+    /**
+     * 描述:获取表示当前日期24点时间毫秒数.
+     *
+     * @return the last time of day
+     */
+    public static long getLastTimeOfDay() {
+        Date date = null;
+        try {
+            String currentDate = getCurrentDate(dateFormatYMD);
+            date = getDateByFormat(currentDate + " 24:00:00", dateFormatYMDHMS);
+            return date.getTime();
+        } catch (Exception ignored) {
+        }
+        return -1;
+    }
+
+    /**
+     * 描述:判断是否是闰年()
+     * <p>
+     * (year能被4整除 并且 不能被100整除) 或者 year能被400整除,则该年为闰年.
+     *
+     * @param year
+     *            年代(如2012)
+     * @return boolean 是否为闰年
+     */
+    public static boolean isLeapYear(int year) {
+        return ((year % 4 == 0 && year % 400 != 0) || year % 400 == 0);
+    }
+
+    /**
+     * 描述:根据时间返回格式化后的时间的描述. 小于1小时显示多少分钟前 大于1小时显示今天+实际日期,大于今天全部显示实际时间
+     *
+     * @param strDate
+     *            the str date
+     * @param outFormat
+     *            the out format
+     * @return the string
+     */
+    public static String formatDateStr2Desc(String strDate, String outFormat) {
+
+        DateFormat df = new SimpleDateFormat(dateFormatYMDHMS);
+        Calendar c1 = Calendar.getInstance();
+        Calendar c2 = Calendar.getInstance();
+        try {
+            c2.setTime(df.parse(strDate));
+            c1.setTime(new Date());
+            int d = getOffectDay(c1.getTimeInMillis(), c2.getTimeInMillis());
+            if (d == 0) {
+                int h = getOffsetHour(c1.getTimeInMillis(), c2.getTimeInMillis());
+                if (h > 0) {
+                    return "今天" + getStringByFormat(strDate, dateFormatHM);
+                    // return h + "小时前";
+                } else if (h < 0) {
+                    // return Math.abs(h) + "小时后";
+                } else if (h == 0) {
+                    int m = getOffsetMinutes(c1.getTimeInMillis(), c2.getTimeInMillis());
+                    if (m > 0) {
+                        return m + "分钟前";
+                    } else if (m < 0) {
+                        // return Math.abs(m) + "分钟后";
+                    } else {
+                        return "刚刚";
+                    }
+                }
+
+            } else if (d > 0) {
+                if (d == 1) {
+                    // return "昨天"+getStringByFormat(strDate,outFormat);
+                } else if (d == 2) {
+                    // return "前天"+getStringByFormat(strDate,outFormat);
+                }
+            } else if (d < 0) {
+                if (d == -1) {
+                    // return "明天"+getStringByFormat(strDate,outFormat);
+                } else if (d == -2) {
+                    // return "后天"+getStringByFormat(strDate,outFormat);
+                } else {
+                    // return Math.abs(d) +
+                    // "天后"+getStringByFormat(strDate,outFormat);
+                }
+            }
+
+            String out = getStringByFormat(strDate, outFormat);
+            if (!StringUtils.isBlank(out)) {
+                return out;
+            }
+        } catch (Exception e) {
+        }
+
+        return strDate;
+    }
+
+    /**
+     * 取指定日期为星期几.
+     *
+     * @param strDate
+     *            指定日期
+     * @param inFormat
+     *            指定日期格式
+     * @return String 星期几
+     */
+    public static String getWeekNumber(String strDate, String inFormat) {
+        Assert.isTrue((StringUtils.isNoneBlank(strDate) && StringUtils.isNoneBlank(inFormat)), "strDate or inFormat can not be null!");
+
+        Date mDate = getDateByFormat(strDate, inFormat);
+        Calendar calendar = new GregorianCalendar();
+        calendar.setTime(mDate);
+
+        int intTemp = calendar.get(Calendar.DAY_OF_WEEK) - 1;
+        switch (intTemp) {
+            case 0:
+                return "星期日";
+            case 1:
+                return "星期一";
+            case 2:
+                return "星期二";
+            case 3:
+                return "星期三";
+            case 4:
+                return "星期四";
+            case 5:
+                return "星期五";
+            case 6:
+                return "星期六";
+
+            default:
+                return "";
+        }
+    }
+
+    /**
+     * 根据给定的日期判断是否为上下午.
+     *
+     * @param strDate
+     *            the str date
+     * @param format
+     *            the format
+     * @return the time quantum  Calendar.get(Calendar.HOUR_OF_DAY)
+     */
+    @SuppressWarnings("unchecked")
+    public static String getTimeQuantum(String strDate, String format) {
+        Date mDate = getDateByFormat(strDate, format);
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(mDate);
+        if (calendar.get(Calendar.AM_PM) == Calendar.AM) {
+            return "AM";
+        } else {
+            return "PM";
+        }
+    }
+
+    /**
+     * 判断当前日期是否上下午.
+     * 如果true 表示是上午 false 表示下午
+     *
+     * @return the time quantum  Calendar.get(Calendar.HOUR_OF_DAY)
+     */
+    @SuppressWarnings("unchecked")
+    public static Boolean getIfTimeNowIsAM() {
+
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        if (calendar.get(Calendar.AM_PM) == Calendar.AM) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 根据给定的毫秒数算得时间的描述.
+     *
+     * @param milliseconds
+     *            the milliseconds
+     * @return the time description
+     */
+    public static String getTimeDescription(long milliseconds) {
+        if (milliseconds > 1000) {
+            // 大于一分
+            if (milliseconds / 1000 / 60 > 1) {
+                long minute = milliseconds / 1000 / 60;
+                long second = milliseconds / 1000 % 60;
+                return minute + "分" + second + "秒";
+            } else {
+                // 显示秒
+                return milliseconds / 1000 + "秒";
+            }
+        } else {
+            return milliseconds + "毫秒";
+        }
+    }
+
+    /**
+     * 解析时间按照小时:分:秒格式输出
+     *
+     * @param iTimeSeconds 秒
+     * @return
+     */
+    public static String getTime(int iTimeSeconds) {
+        String strUserTime = iTimeSeconds / 60 + " : "
+                + ((iTimeSeconds % 60) < 10 ? ("0" + iTimeSeconds % 60) : iTimeSeconds % 60);
+        return strUserTime;
+    }
+
+    /**
+     *@Author: timo
+     *@Date: 2018/5/23 12:01
+     *@Description:
+     * 获取本周一日期
+     *
+     */
+    public static Date getMondayOFWeek(Date date){
+        int mondayPlus = getMondayPlus(date);
+        GregorianCalendar currentDate = new GregorianCalendar();
+        currentDate.setTime(date);
+//        System.out.println(" 当前时间 :"+currentDate.getTime());
+        currentDate.add(GregorianCalendar.DATE, mondayPlus);
+        return currentDate.getTime();
+    }
+
+    /**
+     *@Author: timo
+     *@Date: 2018/5/23 12:02
+     *@Description:
+     * 获取本周日日期
+     *
+     */
+    public static Date getCurrentWeekday(Date date){
+        int mondayPlus = getMondayPlus(date);
+        GregorianCalendar currentDate = new GregorianCalendar();
+        currentDate.setTime(date);
+        currentDate.add(GregorianCalendar.DATE, mondayPlus+ 6);
+        return currentDate.getTime();
+    }
+
+    /**
+     *@Author: timo
+     *@Date: 2018/5/23 12:03
+     *@Description:
+     * 获取下周一的日期
+     *
+     */
+    public static Date getNextMonday(Date date) {
+        int mondayPlus = getMondayPlus(date);
+        GregorianCalendar currentDate = new GregorianCalendar();
+        currentDate.setTime(date);
+        currentDate.add(GregorianCalendar.DATE, mondayPlus + 7);
+        return currentDate.getTime();
+    }
+
+    /**
+     *@Author: timo
+     *@Date: 2018/5/23 12:03
+     *@Description:
+     * 获取下周日的日期
+     *
+     */
+    public static Date getNextSunday(Date date) {
+        int mondayPlus = getMondayPlus(date);
+        GregorianCalendar currentDate = new GregorianCalendar();
+        currentDate.setTime(date);
+        currentDate.add(GregorianCalendar.DATE, mondayPlus + 7 + 6);
+        return currentDate.getTime();
+    }
+
+    private static int getMondayPlus(Date date) {
+        Calendar cd = Calendar.getInstance();
+        cd.setTime(date);
+        // 获得今天是一周的第几天,星期日是第一天,星期二是第二天......
+        // 因为按中国礼拜一作为第一天所以这里减1
+        int dayOfWeek = cd.get(Calendar.DAY_OF_WEEK) - 1;
+        if (dayOfWeek == 0) {
+            return -6;
+        } else {
+            return 1-dayOfWeek;
+        }
+    }
+
+    /**
+     *@Author: timo
+     *@Date: 2018/5/24 17:32
+     *@Description:
+     * 添加分钟
+     * @param date 从那个时间开始
+     * @param minute 延长的分钟
+     */
+    public static Date addMinute(Date date, Integer minute){
+        Calendar calendar = Calendar.getInstance();
+        calendar.add(Calendar.MINUTE,minute);
+        return calendar.getTime();
+    }
+
+    /**
+     * @Author: timo
+     * @Date: 2018/6/12 16:46
+     * @Description: 获取传入时间的月份的最后一天
+     * @param
+     * @return:
+     *
+     */
+    public static Date getLastDayByMonth(Date date){
+        Calendar ca = Calendar.getInstance();
+        ca.setTime(date);
+        ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
+        return ca.getTime();
+    }
+
+    public static Date getDateShort(Date date){
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+        String strDate=formatter.format(date);
+        ParsePosition pos = new ParsePosition(0);
+        Date strtodate = formatter.parse(strDate, pos);
+        return strtodate;
+    }
+    public static void main(String[] args) {
+        LocalDateTime dateTime1 = LocalDateTime.of(2023,  10, 1, 12, 0);
+        LocalDateTime dateTime2 = LocalDateTime.of(2023,  10, 5, 14, 0);
+
+        // 计算两个 LocalDateTime 之间的天数差
+        long daysDifference = ChronoUnit.DAYS.between(dateTime1,  dateTime2);
+
+        // 将结果转换为 Integer 类型
+        Integer daysDifferenceInteger = (int) daysDifference;
+
+        // 输出结果
+        System.out.println("Days  difference: " + daysDifferenceInteger);
+//        System.out.println("date : "+DateUtil.getStringByOffset(new Date(),"yyyy-MM-dd HH:mm:ss", Calendar.DATE,-5));
+//        // 本周一
+//        Date nowDate = DateUtil.getDateByFormat("2018-07-02",DateUtil.dateFormatYMD);
+//        Date firsDate = getMondayOFWeek(nowDate);
+//        String firsDateStr = DateUtil.getStringByFormat(firsDate,dateFormatYMD);
+//        System.out.println("firsDateStr : "+firsDateStr);
+//
+//        // 本周日
+//        Date lastCurDate = getCurrentWeekday(firsDate);
+//        String lastCurDateStr = DateUtil.getStringByFormat(lastCurDate,dateFormatYMD);
+//        System.out.println("lastCurDateStr : "+lastCurDateStr);
+//
+//        // 下周一
+//        Date nextMonday = getNextMonday(firsDate);
+//        String nextMondayStr = DateUtil.getStringByFormat(nextMonday,dateFormatYMD);
+//        System.out.println("nextMondayStr : "+nextMondayStr);
+//        // 下周日
+//        Date nextSunday = getNextSunday(firsDate);
+//        String nextSundayStr = DateUtil.getStringByFormat(nextSunday,dateFormatYMD);
+//        System.out.println("nextSundayStr : "+nextSundayStr);
+//
+//        Date toDay = new Date();
+//        String toDayStr = DateUtil.getStringByFormat(toDay,dateFormatYMD);
+//        System.out.println("toDayStr : "+toDayStr);
+
+
+
+    }
+
+}

+ 895 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/DateUtils.java

@@ -0,0 +1,895 @@
+package com.storlead.framework.common.util;
+
+import cn.hutool.core.util.RandomUtil;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.util.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.Month;
+import java.time.ZoneId;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ *
+ * 类描述:时间操作定义类
+ *
+ * @Author: 张代浩
+ * @Date:2012-12-8 12:15:03
+ * @Version 1.0
+ */
+@Log4j2
+public class DateUtils extends PropertyEditorSupport {
+	// 各种时间格式
+	public static final SimpleDateFormat date_sdf = new SimpleDateFormat("yyyy-MM-dd");
+
+	public static final SimpleDateFormat date_yyyy_mm = new SimpleDateFormat("yyyy-MM");
+	// 各种时间格式
+	public static final SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
+	// 各种时间格式
+	public static final SimpleDateFormat date_sdf_wz = new SimpleDateFormat("yyyy年MM月dd日");
+	public static final SimpleDateFormat time_sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+	public static final SimpleDateFormat yyyymmddhhmmss = new SimpleDateFormat("yyyyMMddHHmmss");
+	public static final SimpleDateFormat short_time_sdf = new SimpleDateFormat("HH:mm");
+	public static final SimpleDateFormat long_time_sdf = new SimpleDateFormat("HHmmss");
+	public static final SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+	// 以毫秒表示的时间
+	private static final long DAY_IN_MILLIS = 24 * 3600 * 1000;
+	private static final long HOUR_IN_MILLIS = 3600 * 1000;
+	private static final long MINUTE_IN_MILLIS = 60 * 1000;
+	private static final long SECOND_IN_MILLIS = 1000;
+
+	// 指定模式的时间格式
+	private static SimpleDateFormat getSDFormat(String pattern) {
+		return new SimpleDateFormat(pattern);
+	}
+
+	/**
+	 * 当前日历,这里用中国时间表示
+	 *
+	 * @return 以当地时区表示的系统当前日历
+	 */
+	public static Calendar getCalendar() {
+		return Calendar.getInstance();
+	}
+
+	/**
+	 * 指定毫秒数表示的日历
+	 *
+	 * @param millis 毫秒数
+	 * @return 指定毫秒数表示的日历
+	 */
+	public static Calendar getCalendar(long millis) {
+		Calendar cal = Calendar.getInstance();
+		// --------------------cal.setTimeInMillis(millis);
+		cal.setTime(new Date(millis));
+		return cal;
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// getDate
+	// 各种方式获取的Date
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 当前日期
+	 *
+	 * @return 系统当前时间
+	 */
+	public static Date getDate() {
+		return new Date();
+	}
+
+	/**
+	 * 指定毫秒数表示的日期
+	 *
+	 * @param millis 毫秒数
+	 * @return 指定毫秒数表示的日期
+	 */
+	public static Date getDate(long millis) {
+		return new Date(millis);
+	}
+
+	/**
+	 * 时间戳转换为字符串
+	 *
+	 * @param time
+	 * @return
+	 */
+	public static String timestamptoStr(Timestamp time) {
+		Date date = null;
+		if (null != time) {
+			date = new Date(time.getTime());
+		}
+		return date2Str(date_sdf);
+	}
+
+	/**
+	 * 字符串转换时间戳
+	 *
+	 * @param str
+	 * @return
+	 */
+	public static Timestamp str2Timestamp(String str) {
+		Date date = str2Date(str, date_sdf);
+		return new Timestamp(date.getTime());
+	}
+
+	/**
+	 * 字符串转换成日期
+	 *
+	 * @param str
+	 * @param sdf
+	 * @return
+	 */
+	public static Date str2Date(String str, SimpleDateFormat sdf) {
+		if (null == str || "".equals(str)) {
+			return null;
+		}
+		Date date = null;
+		try {
+			date = sdf.parse(str);
+			return date;
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/**
+	 * 日期转换为字符串
+	 *
+	 * @return 字符串
+	 */
+	public static String date2Str(SimpleDateFormat date_sdf) {
+		Date date = getDate();
+		if (null == date) {
+			return null;
+		}
+		return date_sdf.format(date);
+	}
+
+	/**
+	 * 格式化时间
+	 *
+	 * @param date
+	 * @param format
+	 * @return
+	 */
+	public static String dateformat(String date, String format) {
+		SimpleDateFormat sformat = new SimpleDateFormat(format);
+		Date _date = null;
+		try {
+			_date = sformat.parse(date);
+		} catch (ParseException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return sformat.format(_date);
+	}
+
+	/**
+	 * 日期转换为字符串
+	 *
+	 * @param date   日期
+	 * @return 字符串
+	 */
+	public static String date2Str(Date date, SimpleDateFormat date_sdf) {
+		if (null == date) {
+			return null;
+		}
+		return date_sdf.format(date);
+	}
+
+	/**
+	 * 日期转换为字符串
+	 *
+	 * @param format 日期格式
+	 * @return 字符串
+	 */
+	public static String getDate(String format) {
+		Date date = new Date();
+		SimpleDateFormat sdf = new SimpleDateFormat(format);
+		return sdf.format(date);
+	}
+
+	public static String getYestoday() {
+		DateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");
+		Calendar calendar=Calendar.getInstance();
+		calendar.set(Calendar.HOUR_OF_DAY,-24);
+		return dateFormat.format(calendar.getTime());
+	}
+
+	/**
+	 * 指定毫秒数的时间戳
+	 *
+	 * @param millis 毫秒数
+	 * @return 指定毫秒数的时间戳
+	 */
+	public static Timestamp getTimestamp(long millis) {
+		return new Timestamp(millis);
+	}
+
+	/**
+	 * 以字符形式表示的时间戳
+	 *
+	 * @param time 毫秒数
+	 * @return 以字符形式表示的时间戳
+	 */
+	public static Timestamp getTimestamp(String time) {
+		return new Timestamp(Long.parseLong(time));
+	}
+
+	/**
+	 * 系统当前的时间戳
+	 *
+	 * @return 系统当前的时间戳
+	 */
+	public static Timestamp getTimestamp() {
+		return new Timestamp(System.currentTimeMillis());
+	}
+
+	/**
+	 * 当前时间,格式 yyyy-MM-dd HH:mm:ss
+	 *
+	 * @return 当前时间的标准形式字符串
+	 */
+	public static String now() {
+		return datetimeFormat.format(getCalendar().getTime());
+	}
+
+	/**
+	 * 指定日期的时间戳
+	 *
+	 * @param date 指定日期
+	 * @return 指定日期的时间戳
+	 */
+	public static Timestamp getTimestamp(Date date) {
+		return new Timestamp(date.getTime());
+	}
+
+	/**
+	 * 指定日历的时间戳
+	 *
+	 * @param cal 指定日历
+	 * @return 指定日历的时间戳
+	 */
+	public static Timestamp getCalendarTimestamp(Calendar cal) {
+		// ---------------------return new Timestamp(cal.getTimeInMillis());
+		return new Timestamp(cal.getTime().getTime());
+	}
+
+	public static Timestamp gettimestamp() {
+		Date dt = new Date();
+		DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		String nowTime = df.format(dt);
+		Timestamp buydate = Timestamp.valueOf(nowTime);
+		return buydate;
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// getMillis
+	// 各种方式获取的Millis
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 系统时间的毫秒数
+	 *
+	 * @return 系统时间的毫秒数
+	 */
+	public static long getMillis() {
+		return System.currentTimeMillis();
+	}
+
+	/**
+	 * 指定日历的毫秒数
+	 *
+	 * @param cal 指定日历
+	 * @return 指定日历的毫秒数
+	 */
+	public static long getMillis(Calendar cal) {
+		// --------------------return cal.getTimeInMillis();
+		return cal.getTime().getTime();
+	}
+
+	/**
+	 * 指定日期的毫秒数
+	 *
+	 * @param date 指定日期
+	 * @return 指定日期的毫秒数
+	 */
+	public static long getMillis(Date date) {
+		return date.getTime();
+	}
+
+	/**
+	 * 指定时间戳的毫秒数
+	 *
+	 * @param ts 指定时间戳
+	 * @return 指定时间戳的毫秒数
+	 */
+	public static long getMillis(Timestamp ts) {
+		return ts.getTime();
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// formatDate
+	// 将日期按照一定的格式转化为字符串
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 默认方式表示的系统当前日期,具体格式:年-月-日
+	 *
+	 * @return 默认日期按“年-月-日“格式显示
+	 */
+	public static String formatDate() {
+		return date_sdf.format(getCalendar().getTime());
+	}
+
+	/**
+	 * 默认方式表示的系统当前日期,具体格式:yyyy-MM-dd HH:mm:ss
+	 *
+	 * @return 默认日期按“yyyy-MM-dd HH:mm:ss“格式显示
+	 */
+	public static String formatDateTime() {
+		return datetimeFormat.format(getCalendar().getTime());
+	}
+
+	/**
+	 * 获取时间字符串
+	 */
+	public static String getDataString(SimpleDateFormat formatstr) {
+		return formatstr.format(getCalendar().getTime());
+	}
+
+	/**
+	 * 指定日期的默认显示,具体格式:年-月-日
+	 *
+	 * @param cal 指定的日期
+	 * @return 指定日期按“年-月-日“格式显示
+	 */
+	public static String formatDate(Calendar cal) {
+		return date_sdf.format(cal.getTime());
+	}
+
+	/**
+	 * 指定日期的默认显示,具体格式:年-月-日
+	 *
+	 * @param date 指定的日期
+	 * @return 指定日期按“年-月-日“格式显示
+	 */
+	public static String formatDate(Date date) {
+		return date_sdf.format(date);
+	}
+
+	/**
+	 * 指定毫秒数表示日期的默认显示,具体格式:年-月-日
+	 *
+	 * @param millis 指定的毫秒数
+	 * @return 指定毫秒数表示日期按“年-月-日“格式显示
+	 */
+	public static String formatDate(long millis) {
+		return date_sdf.format(new Date(millis));
+	}
+
+	/**
+	 * 默认日期按指定格式显示
+	 *
+	 * @param pattern 指定的格式
+	 * @return 默认日期按指定格式显示
+	 */
+	public static String formatDate(String pattern) {
+		return getSDFormat(pattern).format(getCalendar().getTime());
+	}
+
+	/**
+	 * 指定日期按指定格式显示
+	 *
+	 * @param cal     指定的日期
+	 * @param pattern 指定的格式
+	 * @return 指定日期按指定格式显示
+	 */
+	public static String formatDate(Calendar cal, String pattern) {
+		return getSDFormat(pattern).format(cal.getTime());
+	}
+
+	/**
+	 * 指定日期按指定格式显示
+	 *
+	 * @param date    指定的日期
+	 * @param pattern 指定的格式
+	 * @return 指定日期按指定格式显示
+	 */
+	public static String formatDate(Date date, String pattern) {
+		return getSDFormat(pattern).format(date);
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// formatTime
+	// 将日期按照一定的格式转化为字符串
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 默认方式表示的系统当前日期,具体格式:年-月-日 时:分
+	 *
+	 * @return 默认日期按“年-月-日 时:分“格式显示
+	 */
+	public static String formatTime() {
+		return time_sdf.format(getCalendar().getTime());
+	}
+
+	/**
+	 * 指定毫秒数表示日期的默认显示,具体格式:年-月-日 时:分
+	 *
+	 * @param millis 指定的毫秒数
+	 * @return 指定毫秒数表示日期按“年-月-日 时:分“格式显示
+	 */
+	public static String formatTime(long millis) {
+		return time_sdf.format(new Date(millis));
+	}
+
+	/**
+	 * 指定日期的默认显示,具体格式:年-月-日 时:分
+	 *
+	 * @param cal 指定的日期
+	 * @return 指定日期按“年-月-日 时:分“格式显示
+	 */
+	public static String formatTime(Calendar cal) {
+		return time_sdf.format(cal.getTime());
+	}
+
+	/**
+	 * 指定日期的默认显示,具体格式:年-月-日 时:分
+	 *
+	 * @param date 指定的日期
+	 * @return 指定日期按“年-月-日 时:分“格式显示
+	 */
+	public static String formatTime(Date date) {
+		return time_sdf.format(date);
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// formatShortTime
+	// 将日期按照一定的格式转化为字符串
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 默认方式表示的系统当前日期,具体格式:时:分
+	 *
+	 * @return 默认日期按“时:分“格式显示
+	 */
+	public static String formatShortTime() {
+		return short_time_sdf.format(getCalendar().getTime());
+	}
+
+	/**
+	 * 指定毫秒数表示日期的默认显示,具体格式:时:分
+	 *
+	 * @param millis 指定的毫秒数
+	 * @return 指定毫秒数表示日期按“时:分“格式显示
+	 */
+	public static String formatShortTime(long millis) {
+		return short_time_sdf.format(new Date(millis));
+	}
+
+	/**
+	 * 指定日期的默认显示,具体格式:时:分
+	 *
+	 * @param cal 指定的日期
+	 * @return 指定日期按“时:分“格式显示
+	 */
+	public static String formatShortTime(Calendar cal) {
+		return short_time_sdf.format(cal.getTime());
+	}
+
+	/**
+	 * 指定日期的默认显示,具体格式:时:分
+	 *
+	 * @param date 指定的日期
+	 * @return 指定日期按“时:分“格式显示
+	 */
+	public static String formatShortTime(Date date) {
+		return short_time_sdf.format(date);
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// parseDate
+	// parseCalendar
+	// parseTimestamp
+	// 将字符串按照一定的格式转化为日期或时间
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间
+	 *
+	 * @param src     将要转换的原始字符窜
+	 * @param pattern 转换的匹配格式
+	 * @return 如果转换成功则返回转换后的日期
+	 * @throws ParseException
+	 */
+	public static Date parseDate(String src, String pattern) throws ParseException {
+		return getSDFormat(pattern).parse(src);
+
+	}
+
+	/**
+	 * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间
+	 *
+	 * @param src     将要转换的原始字符窜
+	 * @param pattern 转换的匹配格式
+	 * @return 如果转换成功则返回转换后的日期
+	 * @throws ParseException
+	 */
+	public static Calendar parseCalendar(String src, String pattern) throws ParseException {
+
+		Date date = parseDate(src, pattern);
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		return cal;
+	}
+
+	public static String formatAddDate(String src, String pattern, int amount) throws ParseException {
+		Calendar cal;
+		cal = parseCalendar(src, pattern);
+		cal.add(Calendar.DATE, amount);
+		return formatDate(cal);
+	}
+
+	/**
+	 * 根据指定的格式将字符串转换成Date 如输入:2003-11-19 11:20:20将按照这个转成时间
+	 *
+	 * @param src     将要转换的原始字符窜
+	 * @param pattern 转换的匹配格式
+	 * @return 如果转换成功则返回转换后的时间戳
+	 * @throws ParseException
+	 */
+	public static Timestamp parseTimestamp(String src, String pattern) throws ParseException {
+		Date date = parseDate(src, pattern);
+		return new Timestamp(date.getTime());
+	}
+
+	// ////////////////////////////////////////////////////////////////////////////
+	// dateDiff
+	// 计算两个日期之间的差值
+	// ////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * 计算两个时间之间的差值,根据标志的不同而不同
+	 *
+	 * @param flag   计算标志,表示按照年/月/日/时/分/秒等计算
+	 * @param calSrc 减数
+	 * @param calDes 被减数
+	 * @return 两个日期之间的差值
+	 */
+	public static int dateDiff(char flag, Calendar calSrc, Calendar calDes) {
+
+		long millisDiff = getMillis(calSrc) - getMillis(calDes);
+
+		if (flag == 'y') {
+			return (calSrc.get(calSrc.YEAR) - calDes.get(calDes.YEAR));
+		}
+
+		if (flag == 'd') {
+			return (int) (millisDiff / DAY_IN_MILLIS);
+		}
+
+		if (flag == 'h') {
+			return (int) (millisDiff / HOUR_IN_MILLIS);
+		}
+
+		if (flag == 'm') {
+			return (int) (millisDiff / MINUTE_IN_MILLIS);
+		}
+
+		if (flag == 's') {
+			return (int) (millisDiff / SECOND_IN_MILLIS);
+		}
+
+		return 0;
+	}
+	// 计算两个日期之间的差值
+	public static int daysBetween(Date one, Date two) {
+		long difference =  (one.getTime()-two.getTime())/86400000;
+		return (int)Math.abs(difference);
+	}
+
+
+	/**
+	 * String类型 转换为Date, 如果参数长度为10 转换格式”yyyy-MM-dd“ 如果参数长度为19 转换格式”yyyy-MM-dd
+	 * HH:mm:ss“ * @param text String类型的时间值
+	 */
+	@Override
+	public void setAsText(String text) throws IllegalArgumentException {
+		if (StringUtils.hasText(text)) {
+			try {
+				if (text.indexOf(":") == -1 && text.length() == 10) {
+					setValue(this.date_sdf.parse(text));
+				} else if (text.indexOf(":") > 0 && text.length() == 19) {
+					setValue(this.datetimeFormat.parse(text));
+				} else {
+					throw new IllegalArgumentException("Could not parse date, date format is error ");
+				}
+			} catch (ParseException ex) {
+				IllegalArgumentException iae = new IllegalArgumentException("Could not parse date: " + ex.getMessage());
+				iae.initCause(ex);
+				throw iae;
+			}
+		} else {
+			setValue(null);
+		}
+	}
+
+	public static int getYear() {
+		GregorianCalendar calendar = new GregorianCalendar();
+		calendar.setTime(getDate());
+		return calendar.get(Calendar.YEAR);
+	}
+	 /***
+	  * @Description: 获取日本月的第一天(本月)
+	  * @Param:
+	  * @return:
+	  * @Author: YPZ
+	  * @Date: 2023/5/19 14:48
+	  */
+	public static Date getMonthFirstDate() {
+		// 获取当前日期
+		Date currentDate = new Date();
+		// 创建Calendar实例
+		Calendar calendar = Calendar.getInstance();
+		// 设置日期为当前日期
+		calendar.setTime(currentDate);
+		// 将日期设置为该月的第一天
+		calendar.set(Calendar.DAY_OF_MONTH, 1);
+		String strNew = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
+		try {
+			Date date =new SimpleDateFormat("yyyy-MM-dd").parse(strNew);
+			return date;
+		}catch (Exception e){
+			log.error("日期转换错误");
+		}
+		// 获取本月的开始时间
+
+		return calendar.getTime();
+	}
+
+	/***
+	  * @Description: 获取本月的最后一天(本月)
+	  * @Param:
+	  * @return:
+	  * @Author: YPZ
+	  * @Date: 2023/5/19 14:48
+	  */
+	public static Date getMonthLastDate() {
+		// 获取当前日期
+		Date currentDate = new Date();
+		// 创建Calendar实例
+		Calendar calendar = Calendar.getInstance();
+		// 设置日期为当前日期
+		calendar.setTime(currentDate);
+		// 将日期设置为该月的第一天
+		calendar.set(Calendar.DAY_OF_MONTH, 1);
+
+		// 将日期设置为该月的最后一天
+		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+		// 获取本月的结束时间
+//		return calendar.getTime();
+		String strNew = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
+		try {
+			Date date =new SimpleDateFormat("yyyy-MM-dd").parse(strNew);
+			return date;
+		}catch (Exception e){
+			log.error("日期转换错误");
+		}
+		// 获取本月的开始时间
+
+		return calendar.getTime();
+	}
+
+	/**
+   * 获取当前日期所在季度的开始日期和结束日期
+   * 季度一年四季, 第一季度:1月-3月, 第二季度:4月-6月, 第三季度:7月-9月, 第四季度:10月-12月
+   * @param isFirst  true表示查询本季度开始日期  false表示查询本季度结束日期
+   * @return
+   */
+      public static Date getStartOrEndDayOfQuarter(Boolean isFirst){
+		         LocalDate today=LocalDate.now();
+		         LocalDate resDate = LocalDate.now();
+		         if (today == null) {
+			             today = resDate;
+			         }
+		         Month month = today.getMonth();
+		         Month firstMonthOfQuarter = month.firstMonthOfQuarter();
+		         Month endMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
+		         if (isFirst) {
+			             resDate = LocalDate.of(today.getYear(), firstMonthOfQuarter, 1);
+			         } else {
+			             resDate = LocalDate.of(today.getYear(), endMonthOfQuarter, endMonthOfQuarter.length(today.isLeapYear()));
+			         }
+		         return Date.from(resDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+		     }
+
+	/**
+	 * 通过时间秒毫秒数判断两个时间的间隔
+	 * @param date1
+	 * @param date2
+	 * @return
+	 */
+	public static int differentDaysByMillisecond(Date date1,Date date2)
+	{
+		int days = (int) ((date2.getTime() - date1.getTime()) / (1000*3600*24));
+		return days;
+	}
+
+	public static String getUploadFileName() {
+		return date2Str(long_time_sdf)+RandomUtil.randomNumbers(4);
+	}
+
+	public static String getUploadDir() {
+		return date2Str(yyyyMMdd);
+	}
+	/**
+	* @Description:  取日期的后一天
+	* @Param:
+	* @return:
+	* @Author: YPZ
+	* @Date: 2022/7/25
+	*/
+
+	public static Date nextDate(Date date) {
+		Calendar calendar = Calendar.getInstance(); //得到日历
+		calendar.setTime(date);
+		calendar.add(Calendar.DAY_OF_MONTH, 1);  //设置为后一天
+		Date tomorrow = calendar.getTime();   //得到后一天的时间
+		return tomorrow;
+
+	}
+
+	/**
+	* @Description:  取日期的前一天
+	* @Param:
+	* @return:
+	* @Author: YPZ
+	* @Date: 2022/7/25
+	*/
+
+	public static Date preDate(Date date) {
+		Calendar calendar = Calendar.getInstance(); //得到日历
+		calendar.setTime(date);
+		calendar.add(Calendar.DAY_OF_MONTH, -1);  //设置为前一天
+		Date tomorrow = calendar.getTime();   //得到前一天的时间
+		return tomorrow;
+
+	}
+
+	/**
+	* @Description:  取日期的最后一秒
+	* @Param:
+	* @return:
+	* @Author: YPZ
+	* @Date: 2022/7/25
+	*/
+
+	public static Date dateEndTime(Date date) {
+
+		try {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+			String time=sdf.format(date);//将前端的日期转成String字符串
+			time+=" 23:59:59";//字符串拼接
+			SimpleDateFormat end = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//将字符串格式化一下
+			return end.parse(time);
+		}catch (ParseException e){
+			return date;
+		}
+	}
+
+	/**
+	* @Description:  取日期的最后一秒
+	* @Param:
+	* @return:
+	* @Author: YPZ
+	* @Date: 2022/7/25
+	*/
+
+	public static Date dateEndTime(LocalDate localdate) {
+
+		ZoneId zone = ZoneId.systemDefault();
+		Instant instant = localdate.atStartOfDay().atZone(zone).toInstant();
+		Date date = Date.from(instant);
+		try {
+			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+			String time=sdf.format(date);//将前端的日期转成String字符串
+			time+=" 23:59:59";//字符串拼接
+			SimpleDateFormat end = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//将字符串格式化一下
+			return end.parse(time);
+		}catch (ParseException e){
+			return null;
+		}
+	}
+
+
+
+	/**
+	 * 判断一年的第几周
+	 * @param datetime
+	 * @return
+	 * @throws ParseException
+	 */
+	public static Integer whatWeek(String datetime) {
+		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+		Date date = null;
+		try {
+			date = format.parse(datetime);
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		Calendar calendar = Calendar.getInstance();
+		calendar.setFirstDayOfWeek(Calendar.MONDAY);
+		calendar.setTime(date);
+		Integer weekNumbe = calendar.get(Calendar.WEEK_OF_YEAR);
+		return weekNumbe;
+	}
+	public static Integer weekTest(String datetime){
+		Calendar calendar = Calendar.getInstance();
+		//设置星期一为一周开始的第一天
+		calendar.setFirstDayOfWeek(Calendar.MONDAY);
+		//获得当前的时间戳
+		calendar.setTimeInMillis(System.currentTimeMillis());
+		//获得当前日期属于今年的第几周
+		Integer weekOfYear = calendar.get(Calendar.WEEK_OF_YEAR);
+		return weekOfYear;
+	}
+	 /***
+	  * @Description: 获取日期几年前后的时间
+	  * @Param: years参照时间,年(之前为负数,之后为正数)
+	  * @return:
+	  * @Author: YPZ
+	  * @Date: 2022/11/22 14:38
+	  */
+	public static Date calYear(Date date,int years){
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.add(Calendar.YEAR,years);
+		return calendar.getTime();
+	}
+
+	public static void main(String[] args) {
+
+		System.out.println("本月第一天为:"+getMonthFirstDate());
+		System.out.println("本月最后一天为:"+getMonthLastDate());
+		System.out.println("本季度第一天为:"+getStartOrEndDayOfQuarter(true));
+		System.out.println("本季度最后一天为:"+getStartOrEndDayOfQuarter(false));
+//		System.out.println(getUploadDir());
+//		System.out.println(weekTest("2022-02-18"));
+//		LocalDateTime end = LocalDateTime.parse("2022-07-21 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+//
+//		long until = java.time.LocalDate.now().until(end, ChronoUnit.DAYS);
+//		System.out.println(until);
+//		try {
+//		LocalDate localDate1 = LocalDate.of(2022,8,15);
+//
+//		String beginTime = "2022-08-16 14:42:32";
+//		String endTime = "2022-08-17 14:42:32";
+//		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//		Date date1 = format.parse(beginTime);
+//		Date date2 = format.parse(endTime);
+//
+//		LocalDate localDate2 = date1.toInstant()
+//				.atZone(ZoneId.systemDefault())
+//				.toLocalDate();
+//		Boolean isBefore = localDate1.isBefore(localDate2);
+//
+//			System.out.println(isBefore);
+//		}catch (Exception e){
+//
+//		}
+
+	}
+
+}

+ 150 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/HtmlUtils.java

@@ -0,0 +1,150 @@
+package com.storlead.framework.common.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.beans.PropertyEditorSupport;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * 类描述:时间操作定义类
+ *
+ * @Author:
+ * @Date:2012-12-8 12:15:03
+ * @Version 1.0
+ */
+public class HtmlUtils extends PropertyEditorSupport {
+
+
+	/**
+	 * content
+	 *
+	 */
+//	public static String userContent(String content) {
+//		// <p>段落替换为换行
+//		content = content.replaceAll("<p .*?>", "\r\n");
+//// <br><br/>替换为换行
+//		content = content.replaceAll("<br\\s*/?>", "\r\n");
+//// 去掉其它的<>之间的东西
+//		content = content.replaceAll("\\<.*?>", "");
+//		content = content.replaceAll("&nbsp;", " ");
+//// 还原HTML
+//// content = HTMLDecoder.decode(content);
+//		return content;
+//	}
+
+
+	 /***
+	  * @Description: 不处理HTML标签
+	  * @Param:
+	  * @return:
+	  * @Author: YPZ
+	  * @Date: 2022/11/29 14:32
+	  */
+	 public static String userContent(Object var) {
+		 if (Objects.isNull(var)){
+			 return "";
+		 }
+	 	 String inputString = var.toString();
+		 if (StringUtils.isNumeric(inputString)) {
+			 return inputString;
+		 }
+		 if(StringUtils.isBlank(inputString)){
+			 return inputString;
+		 }
+		 if (inputString.indexOf("http") >= 0 || inputString.indexOf("<img") >= 0) {
+			 return inputString;
+		 }
+		 return clearContent(inputString);
+	 }
+	/**
+	 * 去除inputString中的HTML/CSS/JS标签
+	 */
+	public static String clearContent(String inputString) {
+		if(StringUtils.isBlank(inputString)){
+			return inputString;
+		}
+
+		String htmlStr = inputString;
+		String textStr = "";
+		Pattern p_script;
+		Matcher m_script;
+		Pattern p_style;
+		Matcher m_style;
+		Pattern p_html;
+		Matcher m_html;
+		Pattern p_html1;
+		Matcher m_html1;
+
+		try {
+			String regEx_script = "<[//s]*?script[^>]*?>[//s//S]*?<[//s]*?///[//s]*?script[//s]*?>";
+			String regEx_style = "<[//s]*?style[^>]*?>[//s//S]*?<[//s]*?///[//s]*?style[//s]*?>";
+			String regEx_html = "<[^>]+>";
+			String regEx_html1 = "<[^>]+";
+
+			p_script = Pattern.compile(regEx_script, Pattern.CASE_INSENSITIVE);
+
+			m_script = p_script.matcher(htmlStr);
+			htmlStr = m_script.replaceAll("");
+
+			p_style = Pattern.compile(regEx_style, Pattern.CASE_INSENSITIVE);
+
+			m_style = p_style.matcher(htmlStr);
+
+			htmlStr = m_style.replaceAll("");
+
+			p_html = Pattern.compile(regEx_html, Pattern.CASE_INSENSITIVE);
+
+			m_html = p_html.matcher(htmlStr);
+
+			htmlStr = m_html.replaceAll("");
+
+			p_html1 = Pattern.compile(regEx_html1, Pattern.CASE_INSENSITIVE);
+
+			m_html1 = p_html1.matcher(htmlStr);
+
+			htmlStr = m_html1.replaceAll("");
+
+			htmlStr = htmlStr.replaceAll("&nbsp;", " ");
+			textStr = htmlStr;
+
+		} catch (Exception e) {
+			System.err.println("Html2Text: " + e.getMessage());
+		}
+		return textStr;
+	}
+
+
+
+	/**
+	 * @Description:  统计有效字符长度
+	 * @Param:
+	 * @return:
+	 * @Author: YPZ
+	 * @Date: 2022-7-4
+	 */
+
+	public static int userContentSize(String content) {
+		// <p>段落替换为换行
+		content = content.replaceAll("<p .*?>", "");
+// <br><br/>替换为换行
+		content = content.replaceAll("<br\\s*/?>", "");
+// 去掉其它的<>之间的东西
+		content = content.replaceAll("\\<.*?>", "");
+		content = content.replaceAll("&nbsp;", "");
+// 还原HTML
+// content = HTMLDecoder.decode(content);
+		return content.length();
+	}
+
+	public static void main(String[] args) {
+//		String content = "<p>一、SP21014.01,&nbsp; 网表与客户光纤那部分代码合成编译后测试仍有问题,时钟域太多,得去掉一部分;</p><p>二、SP22001.00,DDR3多通道读功能还有问题;</p><p>三、SP19021.01,&nbsp; 30000个文件情况下列表性能测试;</p>";
+		String content = "<p><span style=\"color: rgb(96, 98, 102); background-color: rgb(245, 247, 250); font-size: 13px;\">年度目标3</span></p>";
+		System.out.println(userContent(content));
+		System.out.println(userContent(content).length());
+		System.out.println(userContentSize(content));
+	}
+
+}

+ 529 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/LocalDateUtils.java

@@ -0,0 +1,529 @@
+package com.storlead.framework.common.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.*;
+import java.time.temporal.ChronoField;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.util.*;
+
+import static java.time.ZoneId.SHORT_IDS;
+/**
+ * @program: sp-sales
+ * @description: jdk8日期判断工具类
+ * @author: xiaojiawei
+ * @create: 2022-01-18 09:57
+ **/
+@Slf4j
+public class LocalDateUtils {
+
+
+    public static Date LocalDate2Date(LocalDate date){
+        ZoneId zone = ZoneId.systemDefault();
+        Instant instant = date.atStartOfDay().atZone(zone).toInstant();
+        Date da = Date.from(instant);
+        return da;
+    }
+    public static LocalDate date2LocalDate(Date date){
+        Instant instant =date.toInstant();
+        ZoneId zone = ZoneId.systemDefault();
+        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
+        LocalDate localDate= localDateTime.toLocalDate();
+        return localDate;
+    }
+    public static LocalDateTime date2LocalDateTime(Date date){
+        Instant instant = date.toInstant();
+        ZoneId zoneId = ZoneId.systemDefault();
+
+        LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
+        return localDateTime;
+    }
+
+    public static Date localDateTime2date(LocalDateTime localDateTime){
+        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+    }
+    /**
+     * 从时间戳转换成 LocalDateTime
+     * @param timestamp
+     * @return
+     */
+    private static LocalDateTime timestamp(Long timestamp){
+        Date date = new Date(timestamp);
+        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.of(SHORT_IDS.get("CTT")));
+    }
+
+    public static boolean isBetween(LocalDate date ,LocalDate t1,LocalDate t2){
+        if(t1.isAfter(t2)){
+            LocalDate tmp = t1;
+            t1=t2;
+            t2=tmp;
+        }
+        if(date.isBefore(t1)){
+            return false;
+        }
+        if(date.isAfter(t2)){
+            return false;
+        }
+        return true;
+    }
+    /**
+     * @Description: 计算两个日期相隔的几周 ,e.g 本周日和下周一 相隔一周
+     * @Param: [t1, t2]
+     * @return: long  相隔几周
+     * @Author: xiaojiawei
+     * @Date: 2022/1/18
+     */
+    public static long interWeeks(LocalDate t1, LocalDate t2){
+        if(t1.isAfter(t2)){
+            LocalDate tmp = t1;
+            t1=t2;
+            t2=tmp;
+        }
+        t1 = t1.with(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue());
+        long weeks =  ChronoUnit.WEEKS.between(t1,t2);
+        return weeks;
+    }
+    /**
+     * 以second为主进行判断
+     * 以first为开始时间,second为结束时间
+     * 判断两个时间是否在通过一个周内。
+     *
+     * @param first
+     * @param second
+     */
+    private static Boolean checkWeekBetween(LocalDateTime first, LocalDateTime second){
+        //两个时间差不超过7天,
+        Period period = Period.between(first.toLocalDate(),second.toLocalDate());
+        int years = period.getYears();
+        log.info("years:{}",years);
+        if(years > 0){
+            return false;
+        }
+        int months = period.getMonths();
+        log.info("months:{}",months);
+        if(months > 0){
+            return false;
+        }
+        int days = period.getDays();
+        log.info("days:{}",days);
+        if(days == 0){
+//          表明是同一天
+            return true;
+        }
+        if(days > 7  || days < -7){
+//            两个时间差 超出了7天
+            return false;
+        }
+        int firstDayOfWeek = first.getDayOfWeek().getValue();
+        int secondDayOfWeek = second.getDayOfWeek().getValue();
+        if(secondDayOfWeek == 1){
+            if(oneDay(firstDayOfWeek,secondDayOfWeek,days)){
+                return true;
+            }else {
+                return false;
+            }
+        }
+        if(secondDayOfWeek == 7){
+            if(sevenDay(firstDayOfWeek,secondDayOfWeek,days)){
+                return true;
+            }else {
+                return false;
+            }
+        }
+        if(otherDay(firstDayOfWeek,secondDayOfWeek,days)){
+            return true;
+        }else{
+            return false;
+        }
+    }
+
+    /**
+     * secondDayOfWeek 是所在星期的第一天
+     * 星期的第一天 数据处理
+     * @return
+     */
+    private static Boolean oneDay(int firstDayOfWeek,int secondDayOfWeek,int days){
+        if(days > 0 ){
+            // 表明 first 比second 小 不在同一周
+            return false;
+        }else {
+            //  表明 first 比second 大
+            if(secondDayOfWeek - days == firstDayOfWeek){
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 星期的第7天的时候处理数据
+     * @return
+     */
+    private static Boolean sevenDay(int firstDayOfWeek,int secondDayOfWeek,int days){
+//        second 是周日
+        if(firstDayOfWeek + days == secondDayOfWeek){
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 其他天的数据处理
+     * @return
+     */
+    private static Boolean otherDay(int firstDayOfWeek,int secondDayOfWeek,int days){
+        if(days < 0){
+            //表明 first 比 second 大
+            if((secondDayOfWeek - days) == firstDayOfWeek){
+                //两者是 一周内
+                return true;
+            }
+        }else {
+            //表明 first 比 second 小
+            if(firstDayOfWeek + days == secondDayOfWeek){
+                //两者是 一周内
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取几月份的第几周 [X月份第X周]
+     *
+     * @param sourceTime 这个推荐取00:00:00时间
+     * @return
+     */
+    public static Map<String, Object> getMonthNoAndWeekNo(LocalDateTime sourceTime) {
+        Map<String, Object> map = new HashMap<>();
+        String monthNoAndWeekNo = null;
+        //获取当月的第一天
+        LocalDateTime firstDayOfMonth = getFirstLocalDayOfMonth(sourceTime);
+
+        //获取月第一天开始的周一,从当月第一天开始找
+        LocalDateTime firstMondayOfMonth = firstDayOfMonth;
+        for (int i = 0; i < 6; i++) {
+            DayOfWeek dayOfWeekTemp = firstMondayOfMonth.getDayOfWeek();
+            if (dayOfWeekTemp.equals(DayOfWeek.MONDAY)) {
+                break;
+            }
+            //往后推一天
+            firstMondayOfMonth = firstMondayOfMonth.plusDays(1);
+        }
+
+        //比较当月的第一个星期一 < = 参数时间
+        if (!firstMondayOfMonth.isBefore(sourceTime)) {
+            //如果当月的第一个周一大于参数时间,则要计算到上个月份去
+            //获取上一个月的第一个周一
+            LocalDateTime lastMontLocalDateTime = sourceTime.minusMonths(1);
+            //上个月的第一天
+            LocalDateTime firstDayOfMonth_last = getFirstLocalDayOfMonth(lastMontLocalDateTime);
+            //从上个月的第一天开始找周一
+            LocalDateTime firstMondayOfMonth_last = firstDayOfMonth_last;
+            for (int i = 0; i < 6; i++) {
+                DayOfWeek dayOfWeekTemp = firstMondayOfMonth_last.getDayOfWeek();
+                if (dayOfWeekTemp.equals(DayOfWeek.MONDAY)) {
+                    break;
+                }
+                //往后推一天
+                firstMondayOfMonth_last = firstMondayOfMonth_last.plusDays(1);
+            }
+
+            //  计算两个时间间隔天数 (上月第一个周一 减去 当前时间)
+            Duration duration = Duration.between(firstMondayOfMonth_last, sourceTime);
+            long diffDays = duration.toDays(); //相差的天数
+            //第几周weekNo
+            long weekNo = (diffDays / 7) + 1;
+            //月份
+            int monthNo = firstMondayOfMonth_last.getMonth().getValue();//汉字版月份
+            String monthChinese = getMonthChinese(monthNo);
+            monthNoAndWeekNo = monthChinese + "月份" + "第" + weekNo + "周";
+            map.put("yearNo",firstMondayOfMonth_last.getYear());
+            map.put("monthNo", monthNo);
+            map.put("weekNo", weekNo);
+            map.put("monthNoAndWeekNo", monthNoAndWeekNo);
+
+        } else {
+            //当月第一个周一在当前时间之前 firstMondayOfMonth<=sourceTime
+            //计算两个时间间隔天数
+            int dayOfMonthFirstMonday = firstMondayOfMonth.getDayOfMonth();
+            int dayOfMonthSourceTime = sourceTime.getDayOfMonth();
+
+            int diffDays = dayOfMonthSourceTime - dayOfMonthFirstMonday;
+            //第几周weekNo
+            int weekNo = (diffDays / 7) + 1;
+            //月份
+            int monthNo = sourceTime.getMonth().getValue();
+            map.put("yearNo",sourceTime.getYear());
+            map.put("monthNo", monthNo);
+            map.put("weekNo", weekNo);
+            //汉字版月份
+            String monthChinese = getMonthChinese(monthNo);
+            monthNoAndWeekNo = monthChinese + "月份" + "第" + weekNo + "周";
+            map.put("monthNoAndWeekNo", monthNoAndWeekNo);
+        }
+
+        return map;
+    }
+    /**
+     * 获取汉字版月份
+     *
+     * @param monthNo 第几月
+     * @return
+     */
+    private static String getMonthChinese(int monthNo) {
+        switch (monthNo) {
+            case 1:
+                return "一";
+            case 2:
+                return "二";
+            case 3:
+                return "三";
+            case 4:
+                return "四";
+            case 5:
+                return "五";
+            case 6:
+                return "六";
+            case 7:
+                return "七";
+            case 8:
+                return "八";
+            case 9:
+                return "九";
+            case 10:
+                return "十";
+            case 11:
+                return "十一";
+            case 12:
+                return "十二";
+            default:
+                break;
+        }
+        return null;
+    }
+    /**
+     * 获取某月第一天的00:00:00
+     *
+     * @return
+     */
+    public static LocalDateTime getFirstLocalDayOfMonth(LocalDateTime localDateTime) {
+        return localDateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+    }
+
+    public static Date LocalDateTimeTodate(LocalDateTime localDateTime) {
+        Date date = Date.from( localDateTime.atZone( ZoneId.systemDefault()).toInstant());
+        return date;
+    }
+    /**
+     * 获取当前日期所在季度的开始日期和结束日期
+     * 季度一年四季, 第一季度:1月-3月, 第二季度:4月-6月, 第三季度:7月-9月, 第四季度:10月-12月
+     * @param isFirst  true表示查询本季度开始日期  false表示查询本季度结束日期
+     * @return
+     */
+    public static LocalDate getStartOrEndDayOfQuarter(Boolean isFirst){
+        LocalDate today=LocalDate.now();
+        LocalDate resDate = LocalDate.now();
+        if (today == null) {
+            today = resDate;
+        }
+        Month month = today.getMonth();
+        Month firstMonthOfQuarter = month.firstMonthOfQuarter();
+        Month endMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
+        if (isFirst) {
+            resDate = LocalDate.of(today.getYear(), firstMonthOfQuarter, 1);
+        } else {
+            resDate = LocalDate.of(today.getYear(), endMonthOfQuarter, endMonthOfQuarter.length(today.isLeapYear()));
+        }
+        return resDate;
+    }
+    /**
+     * @Description: 获取当前季度,根据月转换到季度
+     * @Param:
+     * @return:
+     * @Author: YPZ
+     * @Date: 2022-6-30
+     */
+    public static int getQuarterOfYear() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(new Date());
+        return calendar.get(Calendar.MONTH) / 3 + 1;
+    }
+
+    /**
+     * @Description: 获取时间对应季度
+     * @Param:
+     * @return:
+     * @Author: YPZ
+     * @Date: 2022-6-30
+     */
+    public static int getQuarter(LocalDateTime localDateTime) {
+        return (localDateTime.getMonthValue()-1)/3 + 1;
+    }
+    /**
+     * @Description: 根据季度获取第一天/最后一天,从季度到第一个月,再到月的第一天
+     * @Param: year,quarter,isFirst(true表示查询本季度开始日期  false表示查询本季度结束日期)
+     * @return:
+     * @Author: YPZ
+     * @Date: 2022-6-30
+     */
+    public static LocalDate getStartDayOfQuarter(int year, int quarter,Boolean isFirst) {
+
+        int startMonth = (quarter - 1) * 3;
+
+        int lastMonth = quarter * 3 - 1;
+
+        // 根据月获取开始时间
+        Calendar cal = Calendar.getInstance();
+
+        cal.set(Calendar.YEAR, year);
+        if(isFirst) {
+            cal.set(Calendar.MONTH, startMonth);
+            cal.set(Calendar.DAY_OF_MONTH, 1);
+        }else {
+            cal.set(Calendar.MONTH, lastMonth);
+            cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
+        }
+
+        cal.set(Calendar.HOUR, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+
+        return cal.getTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+     /***
+      * @Description: 获取两个localDate间的所有日期(从小到大排序)
+      * @Param:
+      * @return:
+      * @Author: YPZ
+      * @Date: 2022/9/7 17:55
+      */
+    public static  List<LocalDate> getAscDateList(LocalDate startDate,LocalDate endDate) {
+        List<LocalDate> result = new ArrayList<>();
+        if(endDate.compareTo(startDate) < 0 ){
+            return  result;
+        }
+        while (true){
+            result.add(startDate);
+            if(startDate.compareTo(endDate) >= 0){
+                break;
+            }
+            startDate = startDate.plusDays(1);
+        }
+        return result;
+    }
+    /***
+      * @Description: java获取两个LocalDate时间段内,每隔固定天数的所有日期
+      * @Param:
+      * @return:
+      * @Author: YPZ
+      * @Date: 2023/9/19 11:26
+      */
+    public static List<LocalDate> getEveryOtherDayBetweenDates(LocalDate startDate, LocalDate endDate,Integer days) {
+        List<LocalDate> result = new ArrayList<>();
+        days = days +1;
+        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(days)) {
+            result.add(date);
+        }
+        return result;
+    }
+
+    // 静态方法,用于计算日期范围内的每隔两周的周一和周日
+    public static List<LocalDate> getMondaysAndSundays(LocalDate startDate, LocalDate endDate,int weeks) {
+        weeks = weeks+1;
+        List<LocalDate> result = new ArrayList<>();
+
+        // 初始日期调整到当前周的周一
+        startDate = startDate.with(DayOfWeek.MONDAY);
+
+        // 如果第一周的周一小于开始日期,将开始日期添加到结果列表
+        if (!startDate.isBefore(startDate)) {
+
+            result.add(startDate);
+        }
+
+        // 计算两周的时间间隔
+        long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
+
+        // 计算每隔两周的日期
+        for (long i = weeks*7; i <= daysBetween; i += weeks*7) {
+            LocalDate monday = startDate.plusDays(i);
+            result.add(monday);
+        }
+
+        return result;
+    }
+
+
+    public static void main(String[] args) {
+//        System.out.println(getStartOrEndDayOfQuarter(false));
+//        //获取当前年份
+//        Calendar calendar = Calendar.getInstance();
+//        int year = calendar.get(Calendar.YEAR);
+//        // 获取当前季度
+//        int month = calendar.get(Calendar.MONTH);
+//        int quarter = month%3==0?month/3:month/3+1;
+//        System.out.println(year);
+//        System.out.println(quarter);
+//        System.out.println(getStartDayOfQuarter(2021, 3, true));
+//        System.out.println(getStartDayOfQuarter(2021, 3, false));
+//
+//        LocalDateTime start = LocalDateTime.now();
+//        int year = start.getYear();
+//        int quarter = getQuarter(start);
+//
+//
+//        System.out.println("年度为:"+year);
+//        System.out.println("季度为:"+quarter);
+//        String dateStr = "2022-08-29";
+//
+//        LocalDate localDate = LocalDate.parse(dateStr , DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+//
+//        if(localDate.equals(localDate.with(TemporalAdjusters.lastDayOfMonth()))){
+//            System.out.println("是月底");
+//        }
+//        if(localDate.equals(localDate.with(DayOfWeek.SUNDAY))){
+//            System.out.println("是周日");
+//        }
+//
+//        String beginDateStr = "2022-08-29";
+//        LocalDate beginDate = LocalDate.parse(beginDateStr , DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+//        String endDateStr = "2022-09-09";
+//        LocalDate endDate = LocalDate.parse(endDateStr , DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+//        List<LocalDate> localDateList = getAscDateList(beginDate,endDate);
+//        localDateList.forEach(d->System.out.println(d));
+
+//        LocalDate startDate = LocalDate.of(2022, 1, 1);
+//        LocalDate endDate = LocalDate.of(2022, 1, 10);
+//        List<LocalDate> result = getEveryOtherDayBetweenDates(startDate, endDate,1);
+//        for (LocalDate date : result) {
+//            System.out.println(date);
+//        }
+
+//        LocalDate startDate = LocalDate.of(2023, 9, 16);
+//        LocalDate endDate = LocalDate.of(2023, 12, 30);
+//
+//        List<LocalDate> dateRanges = getMondaysAndSundays(startDate, endDate,2);
+//
+//        for (LocalDate vo : dateRanges) {
+//            System.out.println("Start Date: " + vo);
+//        }
+
+        LocalDate startDate = LocalDate.of(2023, 1, 2);
+        LocalDate endDate = LocalDate.of(2028, 11, 26);
+
+        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusYears(2)) {
+            LocalDate firstDayOfMonth = date.withDayOfYear(1);
+            LocalDate lastDayOfMonth = date.withDayOfYear(date.lengthOfYear());
+            System.out.println("月份开始时间:" + firstDayOfMonth);
+            System.out.println("月份结束时间:" + lastDayOfMonth);
+            System.out.println();
+        }
+
+
+
+
+    }
+}

+ 47 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/MD5Util.java

@@ -0,0 +1,47 @@
+package com.storlead.framework.common.util;
+
+import java.security.MessageDigest;
+
+public class MD5Util {
+
+	public static String byteArrayToHexString(byte b[]) {
+		StringBuffer resultSb = new StringBuffer();
+		for (int i = 0; i < b.length; i++){
+			resultSb.append(byteToHexString(b[i]));
+		}
+		return resultSb.toString();
+	}
+
+	private static String byteToHexString(byte b) {
+		int n = b;
+		if (n < 0) {
+			n += 256;
+		}
+		int d1 = n / 16;
+		int d2 = n % 16;
+		return hexDigits[d1] + hexDigits[d2];
+	}
+
+	public static String MD5Encode(String origin, String charsetname) {
+		String resultString = null;
+		try {
+			resultString = new String(origin);
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			if (charsetname == null || "".equals(charsetname)) {
+				resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
+			} else {
+				resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
+			}
+		} catch (Exception exception) {
+		}
+		return resultString;
+	}
+
+	private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
+			"6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };
+
+	public static void main(String[] args) {
+		System.out.println(MD5Encode("otnt3isebpmz", "utf-8"));
+	}
+
+}

+ 100 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/RandomCodeUtil.java

@@ -0,0 +1,100 @@
+package com.storlead.framework.common.util;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.util.Assert;
+
+import java.util.Random;
+
+/**
+ * 随机数工具生成类
+ *
+ * @author blank
+ * @since 2018/5/17 上午10:36
+ */
+public class RandomCodeUtil {
+
+
+    /**
+     * 生成4位数随机验证码
+     */
+    public static int getCheckCode() {
+        Random random = new Random(System.currentTimeMillis());
+        int num = random.nextInt(9999) % 10000;
+        return num < 1000 ? 1000 + num : num;
+    }
+
+    /**
+     * 返回 1000-9999之间的4位随机数
+     *
+     * @return 1000-9999之间的4位随机数
+     * @author blank
+     * @since 2018/5/17 上午10:42
+     */
+    public static int RandomCheckCode() {
+        int num = RandomUtils.nextInt(0, 9999);
+        return num < 1000 ? 1000 + num : num;
+    }
+
+    /**
+     * 返回指定[1~9]位数的随机数
+     *
+     * @param numLength 位数
+     * @return 指定位数的随机数
+     * @author blank
+     * @since 2018/5/17 上午10:49
+     */
+    public static int randomCode(int numLength) {
+        Assert.isTrue(numLength >= 1 && numLength <= 9, " Due the Integer Number limit the numLength can neither smaller than 1 nor larger than 9!");
+
+        int startNum;
+        int endNum;
+        switch (numLength) {
+
+            case 1:
+                startNum = 0;
+                endNum = 9;
+                break;
+            case 2:
+                startNum = 10;
+                endNum = 99;
+                break;
+            case 3:
+                startNum = 100;
+                endNum = 999;
+                break;
+            case 5:
+                startNum = 10000;
+                endNum = 99999;
+                break;
+            case 6:
+                startNum = 100000;
+                endNum = 999999;
+                break;
+            case 7:
+                startNum = 1000000;
+                endNum = 9999999;
+                break;
+            case 8:
+                startNum = 10000000;
+                endNum = 99999999;
+                break;
+            case 9:
+                startNum = 100000000;
+                endNum = 999999999;
+                break;
+
+            default:
+                startNum = 1000;
+                endNum = 9999;
+                break;
+        }
+
+        return RandomUtils.nextInt(startNum, endNum);
+    }
+
+
+    public static void main(String[] args) {
+        System.out.println(randomCode(19));
+
+    }
+}

+ 26 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/RandomGenerateHelper.java

@@ -0,0 +1,26 @@
+package com.storlead.framework.common.util;
+
+import java.util.Random;
+
+/**
+ * @program: sp-sales-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2024-08-09 10:33
+ */
+public class RandomGenerateHelper {
+
+    public static String generateRandomString(int length) {
+        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+        Random random = new Random();
+        StringBuilder sb = new StringBuilder(length);
+
+        for (int i = 0; i < length; i++) {
+            int index = random.nextInt(characters.length());
+            sb.append(characters.charAt(index));
+        }
+
+        return sb.toString();
+    }
+
+}

+ 202 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/RsaUtils.java

@@ -0,0 +1,202 @@
+package com.storlead.framework.common.util;
+
+import org.apache.commons.codec.binary.Base64;
+
+import javax.crypto.Cipher;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * @from fhadmin.cn
+ * @description Rsa 工具类,公钥私钥生成,加解密
+ * @date 2020-05-18
+ **/
+public class RsaUtils {
+
+
+//    public static final String PUBLIC_KEY = "RSAPublicKey";
+//
+//    public static final String PRIVATE_KEY = "RSAPrivateKey";
+
+    private static final String SRC = "123456";
+    /**
+     * 公钥
+     */
+    public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCOlrKBglhJkU/bfgEEv7zuo4y5RiScg3XeDTBrHHePoixDOvyvBsiAc8b33bjin9j77EzRusJT2jjJV1hnHIbu5HkMd3T5JK00gMviCiSbfCkS4/tf3CnFMWLn31YcBgdCOFoiKh/JpGEvTx192LWSKNupDkR6bgpcRvWr5Yw2swIDAQAB";
+    /**
+     * 私钥  私钥不需要在特殊处理在java中直接调用即可
+     */
+    public static final String PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAI6WsoGCWEmRT9t+AQS/vO6jjLlGJJyDdd4NMGscd4+iLEM6/K8GyIBzxvfduOKf2PvsTNG6wlPaOMlXWGcchu7keQx3dPkkrTSAy+IKJJt8KRLj+1/cKcUxYuffVhwGB0I4WiIqH8mkYS9PHX3YtZIo26kORHpuClxG9avljDazAgMBAAECgYA3mZWjoILytOHx0bFt+6IdX9LYz/wot643VudBbQlGDdO6p36udTOruvmj32ZfoDsJCPxvb6ak8dvgztle46XNW4tXRtjKdS9skyA4yDRHXWDZjeKpBeNc3vVl1xpf6zWoVgSsD75JMeNLPWb8+nayInDaU8kM7YZmK/MdC2reCQJBAOi1qDH7eiRgLPjR201GscRRLsFupNrc7s+eX+huKhVJi7DiSQ2ukKry6twvZI9b1O3rVVDQBaFvSJyXTl4ojMUCQQCc3ARKcVGU4h0nVr1+qCmsipNvjY2Hl6QuMcFPbfNOlIuRIANNFZUDrxHsVMkUvcdv0mWVxXi12j2qdLb19F0XAkEAw5tuthTcppbxNnWwEVTLOGnFE3Mdv5rWYk6N76IqXZpkgVq0bXu+vvNR16M+tAJNCXA3VqaFFR2lu3qztRIAwQJAer5D8Ui5MQq7C3R5tem7KpQJiOo4jJjh1XADt7bvBFeC2x401xYDVC2jlc5Gxx82N89oxIkQyySqyn6oSNBeIwJBAIyLnhPkRSSS8zQl6OuRzzdIulZFzmGqb+v9ocwoJzQz4dv795G7/61rT2pdp/1bcsXfu/vWQaiHQcuaBULKSmU=";
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("\n");
+        RsaKeyPair keyPair = generateKeyPair();
+        System.out.println("公钥:" + keyPair.getPublicKey());
+        System.out.println("私钥:" + keyPair.getPrivateKey());
+        System.out.println("\n");
+        test1(keyPair);
+        System.out.println("\n");
+        test2(keyPair);
+        System.out.println("\n");
+    }
+
+    /**
+     * 公钥加密私钥解密
+     */
+    private static void test1(RsaKeyPair keyPair) throws Exception {
+        System.out.println("***************** 公钥加密私钥解密开始 *****************");
+//        String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC);
+//        String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
+        String text1 = encryptByPublicKey(RsaUtils.PUBLIC_KEY, RsaUtils.SRC);
+        String text2 = decryptByPrivateKey(RsaUtils.PRIVATE_KEY, text1);
+        System.out.println("加密前:" + RsaUtils.SRC);
+        System.out.println("加密后:" + text1);
+        System.out.println("解密后:" + text2);
+        if (RsaUtils.SRC.equals(text2)) {
+            System.out.println("解密字符串和原始字符串一致,解密成功");
+        } else {
+            System.out.println("解密字符串和原始字符串不一致,解密失败");
+        }
+        System.out.println("***************** 公钥加密私钥解密结束 *****************");
+    }
+
+    /**
+     * 私钥加密公钥解密
+     * @throws Exception /
+     */
+    private static void test2(RsaKeyPair keyPair) throws Exception {
+        System.out.println("***************** 私钥加密公钥解密开始 *****************");
+        String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC);
+        String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
+        System.out.println("加密前:" + RsaUtils.SRC);
+        System.out.println("加密后:" + text1);
+        System.out.println("解密后:" + text2);
+        if (RsaUtils.SRC.equals(text2)) {
+            System.out.println("解密字符串和原始字符串一致,解密成功");
+        } else {
+            System.out.println("解密字符串和原始字符串不一致,解密失败");
+        }
+        System.out.println("***************** 私钥加密公钥解密结束 *****************");
+    }
+
+    /**
+     * 公钥解密
+     * @from fhadmin.cn
+     * @param publicKeyText 公钥
+     * @param text 待解密的信息
+     * @return /
+     * @throws Exception /
+     */
+    public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
+        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, publicKey);
+        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
+        return new String(result);
+    }
+
+    /**
+     * 私钥加密
+     * @from fhadmin.cn
+     * @param privateKeyText 私钥
+     * @param text 待加密的信息
+     * @return /
+     * @throws Exception /
+     */
+    public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
+        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
+        byte[] result = cipher.doFinal(text.getBytes());
+        return Base64.encodeBase64String(result);
+    }
+
+    /**
+     * 私钥解密
+     * @from fhadmin.cn
+     * @param privateKeyText 私钥
+     * @param text 待解密的文本
+     * @return /
+     * @throws Exception /
+     */
+    public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
+        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, privateKey);
+        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
+        return new String(result);
+    }
+
+    /**
+     * 公钥加密
+     * @from fhadmin.cn
+     * @param publicKeyText 公钥
+     * @param text 待加密的文本
+     * @return /
+     */
+    public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
+        X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+        byte[] result = cipher.doFinal(text.getBytes());
+        return Base64.encodeBase64String(result);
+    }
+
+    /**
+     * 构建RSA密钥对
+     * @from fhadmin.cn
+     * @return /
+     * @throws NoSuchAlgorithmException /
+     */
+    public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+        keyPairGenerator.initialize(1024);
+        KeyPair keyPair = keyPairGenerator.generateKeyPair();
+        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
+        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
+        String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
+        String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
+        RsaKeyPair pair = new RsaKeyPair(publicKeyString, privateKeyString);
+        return pair;
+    }
+
+
+    /**
+     * RSA密钥对对象
+     */
+    public static class RsaKeyPair {
+
+        private final String publicKey;
+        private final String privateKey;
+
+        public RsaKeyPair(String publicKey, String privateKey) {
+            this.publicKey = publicKey;
+            this.privateKey = privateKey;
+        }
+
+        public String getPublicKey() {
+            return publicKey;
+        }
+
+        public String getPrivateKey() {
+            return privateKey;
+        }
+
+    }
+}

+ 106 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SnowFlake.java

@@ -0,0 +1,106 @@
+package com.storlead.framework.common.util;
+
+/**
+ * Twitter的分布式自增ID雪花算法snowflake
+ * @author MENG
+ * @create 2018-08-23 10:21
+ **/
+public class SnowFlake {
+
+    /**
+     * 起始的时间戳
+     */
+    private final static long START_STMP = 1480166465631L;
+
+    /**
+     * 每一部分占用的位数
+     */
+    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
+    private final static long MACHINE_BIT = 5;   //机器标识占用的位数
+    private final static long DATACENTER_BIT = 5;//数据中心占用的位数
+
+    /**
+     * 每一部分的最大值
+     */
+    private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
+    private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
+    private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
+
+    /**
+     * 每一部分向左的位移
+     */
+    private final static long MACHINE_LEFT = SEQUENCE_BIT;
+    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
+    private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
+
+    private long datacenterId;  //数据中心
+    private long machineId;     //机器标识
+    private long sequence = 0L; //序列号
+    private long lastStmp = -1L;//上一次时间戳
+
+    public SnowFlake(long datacenterId, long machineId) {
+        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
+            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
+        }
+        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
+            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
+        }
+        this.datacenterId = datacenterId;
+        this.machineId = machineId;
+    }
+
+    /**
+     * 产生下一个ID
+     *
+     * @return
+     */
+    public synchronized long nextId() {
+        long currStmp = getNewstmp();
+        if (currStmp < lastStmp) {
+            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
+        }
+
+        if (currStmp == lastStmp) {
+            //相同毫秒内,序列号自增
+            sequence = (sequence + 1) & MAX_SEQUENCE;
+            //同一毫秒的序列数已经达到最大
+            if (sequence == 0L) {
+                currStmp = getNextMill();
+            }
+        } else {
+            //不同毫秒内,序列号置为0
+            sequence = 0L;
+        }
+
+        lastStmp = currStmp;
+
+        return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
+                | datacenterId << DATACENTER_LEFT       //数据中心部分
+                | machineId << MACHINE_LEFT             //机器标识部分
+                | sequence;                             //序列号部分
+    }
+
+    private long getNextMill() {
+        long mill = getNewstmp();
+        while (mill <= lastStmp) {
+            mill = getNewstmp();
+        }
+        return mill;
+    }
+
+    private long getNewstmp() {
+        return System.currentTimeMillis();
+    }
+
+    public static void main(String[] args) {
+        SnowFlake snowFlake = new SnowFlake(1, 1);
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < 1000; i++) {
+            System.out.println(snowFlake.nextId());
+        }
+
+        System.out.println(System.currentTimeMillis() - start);
+
+
+    }
+}

+ 95 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SpringContextUtils.java

@@ -0,0 +1,95 @@
+package com.storlead.framework.common.util;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+
+	/**
+	 * 上下文对象实例
+	 */
+	private static ApplicationContext applicationContext;
+
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		SpringContextUtils.applicationContext = applicationContext;
+	}
+
+	/**
+	 * 获取applicationContext
+	 *
+	 * @return
+	 */
+	public static ApplicationContext getApplicationContext() {
+		return applicationContext;
+	}
+
+	/**
+	  * 获取HttpServletRequest
+	 */
+	public static HttpServletRequest getHttpServletRequest() {
+		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+	}
+
+	public static String getDomain(){
+		HttpServletRequest request = getHttpServletRequest();
+		StringBuffer url = request.getRequestURL();
+		return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+	}
+
+	public static String getOrigin(){
+		HttpServletRequest request = getHttpServletRequest();
+		return request.getHeader("Origin");
+	}
+
+	/**
+	 * 通过name获取 Bean.
+	 *
+	 * @param name
+	 * @return
+	 */
+	public static Object getBean(String name) {
+		return getApplicationContext().getBean(name);
+	}
+
+	/**
+	 * 通过class获取Bean.
+	 *
+	 * @param clazz
+	 * @param       <T>
+	 * @return
+	 */
+	public static <T> T getBean(Class<T> clazz) {
+		return getApplicationContext().getBean(clazz);
+	}
+
+	/**
+	 * 通过name,以及Clazz返回指定的Bean
+	 *
+	 * @param name
+	 * @param clazz
+	 * @param       <T>
+	 * @return
+	 */
+	public static <T> T getBean(String name, Class<T> clazz) {
+		return getApplicationContext().getBean(name, clazz);
+	}
+
+	/**
+	 * 获取当前环境
+	 *
+	 * @return 对应 profile name [dev,test,prod]
+	 * @author blank
+	 * @since 2018-10-10 上午11:24
+	 */
+	public static String getActiveProfile() {
+		return applicationContext.getEnvironment().getActiveProfiles()[0];
+	}
+}

+ 103 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SqlInjectionUtil.java

@@ -0,0 +1,103 @@
+package com.storlead.framework.common.util;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * sql注入处理工具类
+ *
+ * @author zhoujf
+ */
+@Slf4j
+public class SqlInjectionUtil {
+	final static String xssStr = "'|and |exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|,";
+
+	/**
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 *
+	 * @param value
+	 * @return
+	 */
+	public static void filterContent(String value) {
+		if (value == null || "".equals(value)) {
+			return;
+		}
+		value = value.toLowerCase();// 统一转为小写
+		String[] xssArr = xssStr.split("\\|");
+		for (int i = 0; i < xssArr.length; i++) {
+			if (value.indexOf(xssArr[i]) > -1) {
+				log.error("请注意,值可能存在SQL注入风险!---> {}", value);
+				throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
+			}
+		}
+		return;
+	}
+
+	/**
+	 * sql注入过滤处理,遇到注入关键字抛异常
+	 *
+	 * @param values
+	 * @return
+	 */
+	public static void filterContent(String[] values) {
+		String[] xssArr = xssStr.split("\\|");
+		for (String value : values) {
+			if (value == null || "".equals(value)) {
+				return;
+			}
+			value = value.toLowerCase();// 统一转为小写
+			for (int i = 0; i < xssArr.length; i++) {
+				if (value.indexOf(xssArr[i]) > -1) {
+					log.error("请注意,值可能存在SQL注入风险!---> {}", value);
+					throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
+				}
+			}
+		}
+		return;
+	}
+
+	/**
+	 * @特殊方法(不通用) 仅用于字典条件SQL参数,注入过滤
+	 * @param value
+	 * @return
+	 */
+	@Deprecated
+	public static void specialFilterContent(String value) {
+		String specialXssStr = "exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|";
+		String[] xssArr = specialXssStr.split("\\|");
+		if (value == null || "".equals(value)) {
+			return;
+		}
+		value = value.toLowerCase();// 统一转为小写
+		for (int i = 0; i < xssArr.length; i++) {
+			if (value.indexOf(xssArr[i]) > -1) {
+				log.error("请注意,值可能存在SQL注入风险!---> {}", value);
+				throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
+			}
+		}
+		return;
+	}
+
+
+	/**
+	 * @特殊方法(不通用) 仅用于Online报表SQL解析,注入过滤
+	 * @param value
+	 * @return
+	 */
+	@Deprecated
+	public static void specialFilterContentForOnlineReport(String value) {
+		String specialXssStr = "exec |insert |delete |update |drop |chr |mid |master |truncate |char |declare |";
+		String[] xssArr = specialXssStr.split("\\|");
+		if (value == null || "".equals(value)) {
+			return;
+		}
+		value = value.toLowerCase();// 统一转为小写
+		for (int i = 0; i < xssArr.length; i++) {
+			if (value.indexOf(xssArr[i]) > -1) {
+				log.error("请注意,值可能存在SQL注入风险!---> {}", value);
+				throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value);
+			}
+		}
+		return;
+	}
+
+}

+ 201 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/StringUtil.java

@@ -0,0 +1,201 @@
+package com.storlead.framework.common.util;
+
+import cn.hutool.core.util.StrUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @program: storelad
+ * @description: String工具类
+ * @author: chenkq
+ * @create: 2022-09-29 11:24
+ **/
+public class StringUtil extends StringUtils {
+
+    public static String trimSpace(String str, String space) {
+        if (str.startsWith(space)) {
+            str = str.substring(space.length());
+        }
+        if (str.endsWith(space)) {
+            str = str.substring(0, str.length() - space.length());
+        }
+        return str;
+    }
+
+    public static String removePrefixAndSuffix(String str, String prefix, String suffix) {
+        if (str.startsWith(prefix)) {
+            str = str.substring(prefix.length());
+        }
+        if (str.endsWith(suffix)) {
+            str = str.substring(0, str.length() - suffix.length());
+        }
+        return str;
+    }
+
+    /**
+     * 判断两个字符串是否相等,除去html标签 两端空格和换行
+     * @param s1 s1
+     * @param s2 s2
+     * @return: @return boolean
+     * @author: xiajiawei
+     * @date: 2022-09-29
+     */
+    public static  boolean ignoreHtmlSpaceEnterEquals(String s1,String s2){
+        if(s1==null&&s2==null){
+            return true;
+        }
+        if(s1 == null||s2==null){
+            return false;
+        }
+        s1 = HtmlUtils.userContent(s1)
+                .replace("/r", "")
+                .replace("/n", "")
+                .trim();
+        s2 = HtmlUtils.userContent(s2)
+                .replace("/r", "")
+                .replace("/n", "")
+                .trim();
+        return Objects.equals(s1,s2);
+    }
+
+    public static List<Long> stringToLongList(String commaSeparatedString) {
+        List<Long> longList = new ArrayList<>();
+        if (commaSeparatedString != null && !commaSeparatedString.isEmpty()) {
+            String[] strings = commaSeparatedString.split(",");
+            for (String s : strings) {
+                // 去除字符串两侧的空白字符,并尝试转换为Long
+                Long number = Long.parseLong(s.trim());
+                longList.add(number);
+            }
+        }
+        return longList;
+    }
+
+    //List<Long>转逗号分割的字符串
+    public static String longListToString(List<Long> longList) {
+        if (CollectionUtils.isEmpty(longList)){
+            return null;
+        }
+        String listString = String.join(",", longList.stream().map(Object::toString).toArray(String[]::new));
+
+        return listString;
+    }
+    //List<String>转逗号分割的字符串
+    public static String stringListToString(List<String> stringList) {
+        if (CollectionUtils.isEmpty(stringList)){
+            return null;
+        }
+        String listString = String.join(",", stringList);
+
+        return listString;
+    }
+    //逗号分割的字符串转List<Long>
+    public static List<Long> stringToStringList(String commaSeparatedString) {
+        if (ObjectUtils.isEmpty(commaSeparatedString)){
+            return new ArrayList<>();
+        }
+        List<Long> longList = Arrays.stream(commaSeparatedString.split(","))
+                .map(Long::parseLong)
+                .collect(Collectors.toList());
+        return longList;
+    }
+    //逗号分割的字符串转List<String>
+    public static List<String> stringToList(String commaSeparatedString) {
+        if (ObjectUtils.isEmpty(commaSeparatedString)){
+            return new ArrayList<>();
+        }
+        List<String> stringList = Arrays.stream(commaSeparatedString.split(","))
+                .collect(Collectors.toList());
+        return stringList;
+    }
+
+    public static boolean containsAllIds(List<Long> subScopeIdList, String subScopeId) {
+        // Split the comma-separated string into a list of strings
+        if (ObjectUtils.isEmpty(subScopeId)){
+            return false;
+        }
+        List<String> subScopeIdStrList = Arrays.asList(subScopeId.split(","));
+
+        // Convert the list of strings to a list of longs
+        List<Long> subScopeIdLongList = subScopeIdStrList.stream()
+                .map(Long::parseLong)
+                .collect(Collectors.toList());
+
+        // Check if subScopeIdList contains all elements of subScopeIdLongList
+        return subScopeIdList.containsAll(subScopeIdLongList);
+    }
+
+
+
+    public static String extractUsernames(String recipients) {
+        try {
+            if (StrUtil.isBlank(recipients)) {
+                return  "";
+            }
+            String regex = "(\\S+)@";
+
+            // 创建一个匹配模式
+            Pattern pattern = Pattern.compile(regex);
+            Matcher matcher = pattern.matcher(recipients);
+            List<String> usernames = new ArrayList<>();
+            // 查找所有匹配的部分
+            while (matcher.find()) {
+                usernames.add(matcher.group(1)); // 获取 @ 前的部分
+            }
+            // 将结果列表用逗号连接
+            return String.join(",", usernames);
+        }catch (Exception e) {
+            return "";
+        }
+    }
+
+
+    public static String updateStringList(String ids, List<Long> idListAll, List<Long> idList) {
+        // Step 1: Parse string A into a List<Long>
+        if (ObjectUtils.isEmpty(ids)){
+            return idList.stream()
+                    .map(Object::toString)
+                    .collect((Collectors.joining(",")));
+        }
+        List<Long> parsedA = Arrays.stream(ids.split(","))
+                .map(Long::parseLong)
+                .collect(Collectors.toList());
+
+        // Step 2: Remove elements from B from parsedA
+        Set<Long> setB = new HashSet<>(idListAll);
+        List<Long> updatedA = parsedA.stream()
+                .filter(element -> !setB.contains(element))
+                .collect(Collectors.toList());
+
+        // Step 3: Add elements from C to updatedA
+        updatedA.addAll(idList);
+
+        // Step 4: Convert updatedA back to a comma-separated string
+        String result = updatedA.stream()
+                .map(Object::toString)
+                .collect((Collectors.joining(",")));
+        return result;
+    }
+
+    public static void main(String[] args) {
+//        System.out.println(stringListToString(null));
+        String A = "1,3,5,6,7";
+        List<Long> B = Arrays.asList(1L, 2L, 3L, 4L);
+        List<Long> C = Arrays.asList(2L, 3L, 4L);
+        System.out.println(updateStringList(A, B, C));
+    }
+
+    public static String concatRegexp(List<Long> ids){
+        if(ids.size()==0){
+            return "-";
+        }else {
+            return ids.stream().map(String::valueOf).collect(Collectors.joining("|"));
+        }
+    }
+}

+ 29 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/SystemUtils.java

@@ -0,0 +1,29 @@
+package com.storlead.framework.common.util;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @program: storlead-storlead-mail-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2025-06-17 15:47
+ */
+public class SystemUtils {
+    /**
+     * 在filter里输出json格式
+     * @param json  json的字符串
+     * */
+    public static void printJson(HttpServletResponse response, String json){
+        try {
+            response.setContentType("application/json");
+            response.setCharacterEncoding("UTF-8");
+            response.getWriter().print(json);
+            response.getWriter().flush();
+            response.getWriter().close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+    }
+}

+ 47 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/UrlChainBlackAndWhiteUtil.java

@@ -0,0 +1,47 @@
+package com.storlead.framework.common.util;
+
+import com.storlead.framework.common.properties.UrlChainDefinitionPorperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+
+/**
+ * @program: sp-sales
+ * @description:
+ * @author: chenkq
+ * @create: 2022-07-04 16:24
+ */
+@Component
+public class UrlChainBlackAndWhiteUtil {
+    @Autowired
+    private UrlChainDefinitionPorperties properties;
+
+    public String getContextPath() {
+        return properties.getContextPath();
+    }
+
+    /**
+     * 白名单URL
+     * @param uri
+     * @return
+     */
+    public boolean IsWhiteUri(String uri)
+    {
+        String [] AUTH_WHITELIST = properties.getAuthWhitelist();
+        uri = uri.replace(properties.getContextPath()+"/","/");
+        for (int i = 0; i < AUTH_WHITELIST.length; i++)
+        {
+            if (AUTH_WHITELIST[i].length() > 0)
+            {
+                String whiteuri = AUTH_WHITELIST[i];
+                whiteuri = whiteuri.trim();
+                AntPathMatcher antPathMatcher = new AntPathMatcher();
+                boolean match = antPathMatcher.match(whiteuri,uri);
+                if (match) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}

+ 219 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/WordUtilsTest.java

@@ -0,0 +1,219 @@
+package com.storlead.framework.common.util;
+
+import freemarker.template.Configuration;
+import freemarker.template.DefaultObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.Version;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.util.List;
+import java.util.Map;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * @author running
+ * @description
+ * @date 2020/9/22
+ */
+
+
+public class WordUtilsTest {
+
+    private static Configuration configuration = null;
+
+    static {
+        configuration = new Configuration(new Version("2.3.30"));
+        configuration.setDefaultEncoding("utf-8");
+        //以下配置只能在本地使用,linux环境下无法获取地址,注掉
+//        try {
+//            ResourceLoader resourceLoader = new DefaultResourceLoader();
+        //指定模板目录在类路径:WEB-INF/classes
+//            Resource resource = resourceLoader.getResource("/");
+//            File file = resource.getFile();
+        //设置要解析的模板所在的目录,并加载模板文件
+//            configuration.setDirectoryForTemplateLoading(file);
+        ///设置包装器,并将对象包装为数据模型
+        configuration.setObjectWrapper(new DefaultObjectWrapper(new Version("2.3.30")));
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//        }
+    }
+
+    private WordUtilsTest() {
+        throw new AssertionError();
+    }
+
+    /**
+     * 导出单个word
+     *
+     * @param map      数据
+     * @param title    文件名
+     * @param ftlFile  模板文件
+     * @param response 响应
+     */
+    public static void exportWord(Map map, String title, String ftlFile, HttpServletResponse response) {
+
+        File file = null;
+        InputStream fin = null;
+        ServletOutputStream out = null;
+        try {
+            //Template freemarkerTemplate = configuration.getTemplate(ftlFile, "UTF-8");
+            ClassPathResource classPathResource = new ClassPathResource(ftlFile);
+            InputStream inputStream = classPathResource.getInputStream();
+            InputStreamReader reader = new InputStreamReader(inputStream);
+            Template freemarkerTemplate = new Template(ftlFile, reader, configuration);
+            // 调用工具类的createDoc方法生成Word文档
+            String fileName = title + ".doc";
+            file = createDoc(map, freemarkerTemplate, fileName);
+            fin = new FileInputStream(file);
+
+            response.setCharacterEncoding("utf-8");
+            response.setContentType("application/msword");
+            // 设置浏览器以下载的方式处理该文件名
+
+            response.setHeader("Content-Disposition", "attachment;filename="
+                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
+
+            out = response.getOutputStream();
+            // 缓冲区
+            byte[] buffer = new byte[1024];
+            int bytesToRead = -1;
+            // 通过循环将读入的Word文件的内容输出到浏览器中
+            while ((bytesToRead = fin.read(buffer)) != -1) {
+                out.write(buffer, 0, bytesToRead);
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (fin != null) {
+                    fin.close();
+                }
+                if (out != null) {
+                    out.close();
+                }
+                // 删除临时文件
+                if (file != null) {
+                    file.delete();
+                }
+            } catch (Exception e2) {
+                e2.printStackTrace();
+            }
+        }
+    }
+
+
+    /**
+     * 压缩包方式导出多个word
+     * 由于一次请求浏览器只能响应一次,想导出多个必须打包,亲测for循环导出只能导一个
+     * 如果想做到分别单独下载,那就得用插件啦,这里不提供插件的做法
+     * 思路:生成临时目录-在临时目录生成word-将临时目录打zip包-zip文件下载-删除临时目录和zip包,
+     * 回收系统资源
+     *
+     * @param mapList
+     * @param titleList
+     * @param ftlFile
+     */
+    public static void exportWordBatch(List<Map<String, Object>> mapList, List<String> titleList, String ftlFile,
+                                       HttpServletResponse response, HttpServletRequest request) {
+        File file = null;
+        File zipfile = null;
+        File directory = null;
+        InputStream fin = null;
+        ServletOutputStream out = null;
+        response.setCharacterEncoding("utf-8");
+        response.setContentType("application/octet-stream");
+        response.addHeader("Content-Disposition", "attachment;filename=" + System.currentTimeMillis() + ".zip");
+
+        try {
+            //以此方式,在Linux环境下也可获取到模板文件
+            ClassPathResource classPathResource = new ClassPathResource(ftlFile);
+            InputStream inputStream = classPathResource.getInputStream();
+            InputStreamReader reader = new InputStreamReader(inputStream);
+            Template freemarkerTemplate = new Template(ftlFile, reader, configuration);
+            out = response.getOutputStream();
+            //根据当前时间创建临时目录
+            String path = request.getRealPath("/resources/word/" + System.currentTimeMillis());
+            directory = new File(path);
+            directory.mkdirs();
+            for (int i = 0; i < mapList.size(); i++) {
+                Map<String, Object> map = mapList.get(i);
+                String title = titleList.get(i);
+                // 调用工具类的createDoc方法在临时目录下生成Word文档
+                file = createDoc(map, freemarkerTemplate, directory.getPath() + "/" + title + ".doc");
+            }
+            //压缩目录
+            ZipUtil.createZip(path, path + "zip.zip");
+            //根据路径获取刚生成的zip包文件
+            zipfile = new File(path + "zip.zip");
+            fin = new FileInputStream(zipfile);
+            // 缓冲区
+            byte[] buffer = new byte[1024];
+            int bytesToRead = -1;
+            // 通过循环将读入的Word文件的内容输出到浏览器中
+            while ((bytesToRead = fin.read(buffer)) != -1) {
+                out.write(buffer, 0, bytesToRead);
+            }
+            response.flushBuffer();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (fin != null) {
+                    fin.close();
+                }
+                if (out != null) {
+                    out.close();
+                }
+                if (zipfile != null) {
+                    zipfile.delete();
+                }
+                if (directory != null) {
+                    //递归删除目录及目录下文件
+                    ZipUtil.deleteFile(directory);
+                }
+            } catch (Exception e2) {
+                e2.printStackTrace();
+            }
+
+        }
+    }
+
+    //生成word文档方法
+    private static File createDoc(Map<?, ?> dataMap, Template template, String filename) {
+
+        File f = new File(filename);
+        Template t = template;
+        Writer w = null;
+        FileOutputStream fos = null;
+        try {
+            // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
+            fos = new FileOutputStream(f);
+            w = new OutputStreamWriter(fos, UTF_8);
+            //不要偷懒写成下面酱紫: 否则无法关闭fos流,打zip包时存取被拒抛异常
+            //w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
+            t.process(dataMap, w);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            throw new RuntimeException(ex);
+        } finally {
+            try {
+                fos.close();
+                w.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        }
+        return f;
+    }
+
+}
+

+ 211 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/ZipUtil.java

@@ -0,0 +1,211 @@
+package com.storlead.framework.common.util;
+
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @author running
+ * @description
+ * @date 2020/9/22
+ */
+public class ZipUtil {
+
+    public static void zip(String inputFileName, String zipFileName)
+            throws Exception {
+        zip(zipFileName, new File(inputFileName));
+    }
+
+    private static void zip(String zipFileName, File inputFile)
+            throws Exception {
+        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
+                zipFileName));
+        zip(out, inputFile, "");
+        System.out.println("zip done");
+        out.close();
+    }
+
+    private static void zip(ZipOutputStream out, File f, String base)
+            throws Exception {
+        if (f.isDirectory()) {
+            File[] fl = f.listFiles();
+            out.putNextEntry(new ZipEntry(base + "/"));
+            base = base.length() == 0 ? "" : base + "/";
+            for (int i = 0; i < fl.length; i++) {
+                zip(out, fl[i], base + fl[i].getName());
+            }
+        } else {
+            out.putNextEntry(new ZipEntry(base));
+            FileInputStream in = new FileInputStream(f);
+            int b;
+            //System.out.println(base);
+            while ((b = in.read()) != -1) {
+                out.write(b);
+            }
+            in.close();
+        }
+    }
+
+    /**
+     * 创建ZIP文件
+     *
+     * @param sourcePath 文件或文件夹路径
+     * @param zipPath    生成的zip文件存在路径(包括文件名)
+     */
+    public static void createZip(String sourcePath, String zipPath) {
+        FileOutputStream fos = null;
+        ZipOutputStream zos = null;
+        try {
+            fos = new FileOutputStream(zipPath);
+            zos = new ZipOutputStream(fos);
+            writeZip(new File(sourcePath), "", zos);
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (zos != null) {
+                    zos.close();
+                    //压缩成功后,删除打包前的文件
+                    deleteFile(new File(sourcePath));
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private static void writeZip(File file, String parentPath,
+                                 ZipOutputStream zos) {
+        if (file.exists()) {
+            // 处理文件夹
+            if (file.isDirectory()) {
+                parentPath += file.getName() + File.separator;
+                File[] files = file.listFiles();
+                for (File f : files) {
+                    writeZip(f, parentPath, zos);
+                }
+            } else {
+                FileInputStream fis = null;
+                try {
+                    fis = new FileInputStream(file);
+                    ZipEntry ze = new ZipEntry(parentPath + file.getName());
+                    zos.putNextEntry(ze);
+                    byte[] content = new byte[1024];
+                    int len;
+                    while ((len = fis.read(content)) != -1) {
+                        zos.write(content, 0, len);
+                        zos.flush();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    try {
+                        if (fis != null) {
+                            fis.close();
+                        }
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+
+    public static void copyResource(List<String> oldResPath, String newResPath) {
+        for (int m = 0; m < oldResPath.size(); m++) {
+            try {
+                // 如果文件夹不存在 则建立新文件夹
+                (new File(newResPath)).mkdirs();
+                File a = new File(oldResPath.get(m));
+                // 如果已经是具体文件,读取
+                if (a.isFile()) {
+                    FileInputStream input = new FileInputStream(a);
+                    FileOutputStream output = new FileOutputStream(newResPath + "/" + (a.getName()).toString());
+                    byte[] b = new byte[1024 * 4];
+                    int len;
+                    while ((len = input.read(b)) != -1) {
+                        output.write(b, 0, len);
+                    }
+                    output.flush();
+                    output.close();
+                    input.close();
+                    // 如果文件夹下还存在文件,遍历,直到得到具体的文件
+                } else {
+                    String[] file = a.list();
+                    File temp = null;
+                    for (int i = 0; i < file.length; i++) {
+                        if (oldResPath.get(m).endsWith(File.separator)) {
+                            temp = new File(oldResPath.get(m) + file[i]);
+                        } else {
+                            temp = new File(oldResPath.get(m) + File.separator + file[i]);
+                        }
+
+                        if (temp.isFile()) {
+                            FileInputStream input = new FileInputStream(temp);
+                            FileOutputStream output = new FileOutputStream(newResPath + "/" + (temp.getName()).toString());
+                            byte[] b = new byte[1024 * 4];
+                            int len;
+                            while ((len = input.read(b)) != -1) {
+                                output.write(b, 0, len);
+                            }
+                            output.flush();
+                            output.close();
+                            input.close();
+                        }
+                        if (temp.isDirectory()) {
+                            List<String> oldChildPath = new ArrayList<String>();
+                            oldChildPath.add(oldResPath.get(m) + "/" + file[i]);
+                            newResPath = newResPath + "/" + file[i];
+                            // 如果是子文件夹 递归循环
+                            copyResource(oldChildPath, newResPath);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 删除文件夹
+     *
+     * @param file
+     */
+    public static void deleteFile(File file) {
+        // 判断文件是否存在
+        if (file.exists()) {
+            // 判断是否是文件
+            if (file.isFile()) {
+                file.delete();
+            } else if (file.isDirectory()) {
+                // 否则如果它是一个目录
+                // 声明目录下所有的文件 files[];
+                File files[] = file.listFiles();
+                // 遍历目录下所有的文件
+                for (int i = 0; i < files.length; i++) {
+                    // 把每个文件 用这个方法进行迭代
+                    deleteFile(files[i]);
+                }
+            }
+            file.delete();
+        }
+    }
+
+    /**
+     * 时间格式化
+     *
+     * @return
+     */
+    public static String dateToString() {
+        Date d = new Date();
+        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
+        String time = formatter.format(d);
+        return time;
+    }
+}
+

+ 87 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/encryptor/AccessKeyEncryptor.java

@@ -0,0 +1,87 @@
+package com.storlead.framework.common.util.encryptor;
+
+/**
+ * @program: sp-salary-system
+ * @description:
+ * @author: chenkq
+ * @create: 2024-05-27 18:41
+ */
+import cn.hutool.core.util.StrUtil;
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Base64;
+
+public class AccessKeyEncryptor {
+
+    private static final String ALGORITHM = "AES";
+    private static final int KEY_SIZE = 128;
+
+    private SecretKey secretKey;
+
+    public AccessKeyEncryptor(String secret) {
+        if (StrUtil.isBlank(secret)) {
+            secret = "-storlead$209912";
+        }
+        this.secretKey = new SecretKeySpec(secret.getBytes(), ALGORITHM);
+    }
+
+    public static AccessKeyEncryptor getAccessKeyEncryptor(String secret) {
+        return new AccessKeyEncryptor(secret);
+    }
+
+    /**
+     * 加密
+     * @param accessKey
+     * @return
+     * @throws Exception
+     */
+    public String encrypt(String accessKey) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+            byte[] encryptedBytes = cipher.doFinal(accessKey.getBytes());
+            return Base64.getEncoder().encodeToString(encryptedBytes);
+        }catch (Exception e) {
+            return "";
+        }
+    }
+
+    /**
+     * 解密
+     * @param encryptedAccessKey
+     * @return
+     * @throws Exception
+     */
+    public String decrypt(String encryptedAccessKey) {
+        try {
+            Cipher cipher = Cipher.getInstance(ALGORITHM);
+            cipher.init(Cipher.DECRYPT_MODE, secretKey);
+            byte[] decodedBytes = Base64.getDecoder().decode(encryptedAccessKey);
+            byte[] decryptedBytes = cipher.doFinal(decodedBytes);
+            return new String(decryptedBytes);
+        }catch (Exception e) {
+            return encryptedAccessKey;
+        }
+    }
+
+
+    public static void main(String[] args) {
+        try {
+            String secret = "";
+            AccessKeyEncryptor encryptor = new AccessKeyEncryptor(secret);
+
+            String accessKey = "-Chenkq1992";
+            System.out.println("Original AccessKey: " + accessKey);
+
+            String encryptedAccessKey = encryptor.encrypt(accessKey);
+            System.out.println("Encrypted AccessKey: " + encryptedAccessKey);
+
+            String decryptedAccessKey = encryptor.decrypt(encryptedAccessKey);
+            System.out.println("Decrypted AccessKey: " + decryptedAccessKey);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 78 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/CodeGenerate.java

@@ -0,0 +1,78 @@
+package com.storlead.framework.common.util.generate;
+
+import cn.hutool.core.util.StrUtil;
+import com.storlead.framework.common.constant.CodeGenerateInterface;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @program: sp-project-system
+ * @description:
+ * @author: chenkq
+ * @create: 2021-09-11 11:13
+ */
+public class CodeGenerate {
+
+
+    public static void main(String[] args) {
+        for (int i = 0; i < 200; i++) {
+            System.out.println(CodeGenerate.generate10Code(CodeGenerateInterface.ORDER_COST_CODE));
+        }
+    }
+
+//    public static String Generate8Code(String moduleKey) {
+//        String id = IdWorker.nextId().toString();
+//        System.out.println(id);
+//        String code = StrUtil.sub(id, 8, id.length());
+//        System.out.println(code);
+//        return moduleKey + code;
+//    }
+//
+//    public static String Generate12Code(String moduleKey) {
+//        String id = IdWorker.nextId().toString();
+//        System.out.println(id);
+//        String code = StrUtil.sub(id, id.length()-12, id.length());
+//        System.out.println(code);
+//        return moduleKey + code;
+//    }
+
+    public static String GenerateFullCode() {
+        String code = IdWorker.nextId().toString();
+        return code;
+    }
+
+    /**
+     * 生成模块对于的Code
+     *
+     * @param moduleKey  模块Key
+     * @return encodeStr 生成的唯一的Code
+     * @throws Exception
+     */
+    public static String generate10Code(String moduleKey) {
+        String id = IdWorker.nextId().toString();
+        String code = StrUtil.sub(id, id.length() - 6, id.length());
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyMM");
+        String time = sdf1.format(new Date(System.currentTimeMillis()));
+        String encodeStr = CodeGenerateInterface.CODE_KEY + moduleKey + time + code;
+        return encodeStr;
+    }
+
+    public static String generate14Code(String moduleKey) {
+        String id = IdWorker.nextId().toString();
+        String code = StrUtil.sub(id, id.length() - 10, id.length());
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyMM");
+        String time = sdf1.format(new Date(System.currentTimeMillis()));
+        String encodeStr = CodeGenerateInterface.CODE_KEY + moduleKey + time + code;
+        return encodeStr;
+    }
+
+    public static String generate12Code(String moduleKey) {
+        String id = IdWorker.nextId().toString();
+        String code = StrUtil.sub(id, id.length() - 8, id.length());
+        SimpleDateFormat sdf1 = new SimpleDateFormat("yyMM");
+        String time = sdf1.format(new Date(System.currentTimeMillis()));
+        String encodeStr = CodeGenerateInterface.CODE_KEY + moduleKey + time + code;
+        return encodeStr;
+    }
+}

+ 29 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/DefaultIdentifierGenerator.java

@@ -0,0 +1,29 @@
+package com.storlead.framework.common.util.generate;
+
+/**
+ * @program: sp-project-system
+ * @description:
+ * @author: chenkq
+ * @create: 2021-09-11 14:18
+ */
+public class DefaultIdentifierGenerator implements IdentifierGenerator {
+
+    private final Sequence sequence;
+
+    public DefaultIdentifierGenerator() {
+        this.sequence = new Sequence();
+    }
+
+    public DefaultIdentifierGenerator(long workerId, long dataCenterId) {
+        this.sequence = new Sequence(workerId, dataCenterId);
+    }
+
+    public DefaultIdentifierGenerator(Sequence sequence) {
+        this.sequence = sequence;
+    }
+
+    @Override
+    public Long nextId() {
+        return this.sequence.nextId();
+    }
+}

+ 36 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/IdWorker.java

@@ -0,0 +1,36 @@
+package com.storlead.framework.common.util.generate;
+
+import java.time.format.DateTimeFormatter;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * @program: sp-project-system
+ * @description: 编码生成
+ * @author: chenkq
+ * @create: 2021-09-11 09:30
+ */
+public class IdWorker {
+    private static IdentifierGenerator IDENTIFIER_GENERATOR = new DefaultIdentifierGenerator();
+    public static final DateTimeFormatter MILLISECOND = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
+
+    public IdWorker() {
+    }
+
+    public static void initSequence(long workerId, long dataCenterId) {
+        IDENTIFIER_GENERATOR = new DefaultIdentifierGenerator(workerId, dataCenterId);
+    }
+
+    public static void setIdentifierGenerator(IdentifierGenerator identifierGenerator) {
+        IDENTIFIER_GENERATOR = identifierGenerator;
+    }
+
+    public static String get32UUID() {
+        ThreadLocalRandom random = ThreadLocalRandom.current();
+        return (new UUID(random.nextLong(), random.nextLong())).toString().replace("-", "");
+    }
+
+    public static Long nextId() {
+        return IDENTIFIER_GENERATOR.nextId();
+    }
+}

+ 10 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/IdentifierGenerator.java

@@ -0,0 +1,10 @@
+package com.storlead.framework.common.util.generate;
+
+public interface IdentifierGenerator {
+
+    Long nextId();
+
+    default String nextUUID() {
+        return IdWorker.get32UUID();
+    }
+}

+ 126 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/util/generate/Sequence.java

@@ -0,0 +1,126 @@
+package com.storlead.framework.common.util.generate;
+
+import cn.hutool.core.date.SystemClock;
+import cn.hutool.core.lang.Assert;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * @program: sp-project-system
+ * @description: 雪花算法工具类
+ * @author: chenkq
+ * @create: 2021-09-11 14:13
+ */
+public class Sequence {
+
+    private static final Logger logger = LoggerFactory.getLogger(Sequence.class);
+    private final long twepoch = 1288834974657L;
+    private final long workerIdBits = 5L;
+    private final long datacenterIdBits = 5L;
+    private final long maxWorkerId = 31L;
+    private final long maxDatacenterId = 31L;
+    private final long sequenceBits = 12L;
+    private final long workerIdShift = 12L;
+    private final long datacenterIdShift = 17L;
+    private final long timestampLeftShift = 22L;
+    private final long sequenceMask = 4095L;
+    private final long workerId;
+    private final long datacenterId;
+    private long sequence = 0L;
+    private long lastTimestamp = -1L;
+
+    public Sequence() {
+        this.datacenterId = getDatacenterId(31L);
+        this.workerId = getMaxWorkerId(this.datacenterId, 31L);
+    }
+
+    public Sequence(long workerId, long datacenterId) {
+        Assert.isFalse(workerId > 31L || workerId < 0L, String.format("worker Id can't be greater than %d or less than 0", 31L), new Object[0]);
+        Assert.isFalse(datacenterId > 31L || datacenterId < 0L, String.format("datacenter Id can't be greater than %d or less than 0", 31L), new Object[0]);
+        this.workerId = workerId;
+        this.datacenterId = datacenterId;
+    }
+
+    protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
+        StringBuilder mpid = new StringBuilder();
+        mpid.append(datacenterId);
+        String name = ManagementFactory.getRuntimeMXBean().getName();
+        if (StringUtils.isNotBlank(name)) {
+            mpid.append(name.split("@")[0]);
+        }
+
+        return (long)(mpid.toString().hashCode() & '\uffff') % (maxWorkerId + 1L);
+    }
+
+    protected static long getDatacenterId(long maxDatacenterId) {
+        long id = 0L;
+
+        try {
+            InetAddress ip = InetAddress.getLocalHost();
+            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
+            if (network == null) {
+                id = 1L;
+            } else {
+                byte[] mac = network.getHardwareAddress();
+                if (null != mac) {
+                    id = (255L & (long)mac[mac.length - 2] | 65280L & (long)mac[mac.length - 1] << 8) >> 6;
+                    id %= maxDatacenterId + 1L;
+                }
+            }
+        } catch (Exception var7) {
+            logger.warn(" getDatacenterId: " + var7.getMessage());
+        }
+
+        return id;
+    }
+
+    public synchronized long nextId() {
+        long timestamp = this.timeGen();
+        if (timestamp < this.lastTimestamp) {
+            long offset = this.lastTimestamp - timestamp;
+            if (offset > 5L) {
+                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
+            }
+
+            try {
+                this.wait(offset << 1);
+                timestamp = this.timeGen();
+                if (timestamp < this.lastTimestamp) {
+                    throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));
+                }
+            } catch (Exception var6) {
+                throw new RuntimeException(var6);
+            }
+        }
+
+        if (this.lastTimestamp == timestamp) {
+            this.sequence = this.sequence + 1L & 4095L;
+            if (this.sequence == 0L) {
+                timestamp = this.tilNextMillis(this.lastTimestamp);
+            }
+        } else {
+            this.sequence = ThreadLocalRandom.current().nextLong(1L, 3L);
+        }
+
+        this.lastTimestamp = timestamp;
+        return timestamp - 1288834974657L << 22 | this.datacenterId << 17 | this.workerId << 12 | this.sequence;
+    }
+
+    protected long tilNextMillis(long lastTimestamp) {
+        long timestamp;
+        for(timestamp = this.timeGen(); timestamp <= lastTimestamp; timestamp = this.timeGen()) {
+        }
+
+        return timestamp;
+    }
+
+    protected long timeGen() {
+        return SystemClock.now();
+    }
+}

+ 20 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NeContains.java

@@ -0,0 +1,20 @@
+package com.storlead.framework.common.validation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Constraint(validatedBy = NotContainsValidator.class) // 绑定校验器
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface  NeContains {
+    String message() default "邮箱格式不正确";
+
+    // 用来指定不能包含的字符串
+    String value();
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}

+ 31 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NeContainsValidator.java

@@ -0,0 +1,31 @@
+package com.storlead.framework.common.validation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * @program: sp-sales-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2024-12-11 11:04
+ */
+public class NeContainsValidator implements ConstraintValidator<NeContains, String> {
+
+    private String forbiddenString;
+
+    @Override
+    public void initialize(NeContains constraintAnnotation) {
+        // 获取注解中定义的不能包含的字符串
+        this.forbiddenString = constraintAnnotation.value();
+    }
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        // 空值直接返回 true,不参与校验
+        if (value == null) {
+            return false;
+        }
+        // 判断字符串是否包含指定的字符串
+        return value.indexOf(forbiddenString) > 0;
+    }
+}

+ 28 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NotContains.java

@@ -0,0 +1,28 @@
+package com.storlead.framework.common.validation;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+import java.lang.annotation.*;
+
+/**
+ * @program: sp-sales-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2024-10-17 10:49
+ */
+
+@Constraint(validatedBy = NotContainsValidator.class) // 绑定校验器
+@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface NotContains {
+    String message() default "字符串不能包含指定的子字符串";
+
+    // 用来指定不能包含的字符串
+    String value();
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+}

+ 31 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/validation/NotContainsValidator.java

@@ -0,0 +1,31 @@
+package com.storlead.framework.common.validation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * @program: sp-sales-platform
+ * @description:
+ * @author: chenkq
+ * @create: 2024-10-17 10:52
+ */
+public class NotContainsValidator implements ConstraintValidator<NotContains, String> {
+
+    private String forbiddenString;
+
+    @Override
+    public void initialize(NotContains constraintAnnotation) {
+        // 获取注解中定义的不能包含的字符串
+        this.forbiddenString = constraintAnnotation.value();
+    }
+
+    @Override
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        // 空值直接返回 true,不参与校验
+        if (value == null) {
+            return true;
+        }
+        // 判断字符串是否包含指定的字符串
+        return !value.contains(forbiddenString);
+    }
+}

+ 55 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/vo/FeignResult.java

@@ -0,0 +1,55 @@
+package com.storlead.framework.common.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @program: sp-sales
+ * @description:
+ * @author: chenkq
+ * @create: 2024-03-17 11:26
+ */
+
+@Data
+public class FeignResult<T> implements Serializable{
+    private static final long serialVersionUID = 8225645185075835261L;
+
+    /**
+     * 是否处理成功
+     */
+    private boolean isSuccess;
+    /**
+     * 状态码, 默认1000是成功
+     */
+    protected int code;
+    /**
+     * 响应信息, 来说明响应情况
+     */
+    protected String msg;
+    /**
+     * 响应的具体数据
+     */
+    private T data;
+
+    public FeignResult() {
+        this.isSuccess = true;
+        this.code = 200;
+        this.msg = "成功";
+        this.data = null;
+    }
+
+    public FeignResult(int code,String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public static <T> FeignResult<T> failure(String msg) {
+        return new FeignResult<>(0,msg, null);
+    }
+
+    public static <T> FeignResult<T> success(String msg) {
+        return new FeignResult<>(0,msg, null);
+    }
+}

+ 119 - 0
java/storlead-framework/storlead-common/src/main/java/com/storlead/framework/common/vo/UserVo.java

@@ -0,0 +1,119 @@
+package com.storlead.framework.common.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.Date;
+
+/**
+ * @program: sp-sales
+ * @description:
+ * @author: chenkq
+ * @create: 2022-07-06 18:45
+ */
+@Data
+public class UserVo {
+
+    private Long id;
+
+    @ApiModelProperty(value = "用户名")
+    private String username;
+
+    private String password;
+
+    private String xworkUserId;
+
+    @ApiModelProperty(value = "头像")
+    private String avatar;
+
+    @ApiModelProperty(value = "性别")
+    private String sex;
+
+    @ApiModelProperty(value = "昵称")
+    private String nickName;
+
+    @ApiModelProperty(value = "姓名")
+    private String realName;
+
+    @ApiModelProperty(value = "姓名")
+    private String mobile;
+
+    @ApiModelProperty(value = "email")
+    private String email;
+
+    @ApiModelProperty(value = "岗位ID")
+    private Long jobId;
+
+    @ApiModelProperty(value = "部门ID")
+    private Long deptId;
+
+    @ApiModelProperty(value = "分公司ID")
+    private Long subCompanyId;
+
+    @ApiModelProperty(value = "公司ID")
+    private Long companyId;
+
+    @ApiModelProperty(value = "直接上级ID")
+    private Long managerId;
+
+    @ApiModelProperty(value = "领导路由")
+    private String managers;
+
+    private String idNum;
+    private String address;
+
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    private LocalDate birthday;
+
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    private LocalDate entryDate;
+
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    private LocalDate permanentDate;
+
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern="yyyy-MM-dd")
+    private LocalDate resignDate;
+    private String bankName;
+    private String bankCardNum;
+
+    @ApiModelProperty(value = "状态 0:试用期 1:正式期 2:临时 3:试用延期 5:离职流程")
+    private String status;
+    private Integer isShenzhenCensus;
+    private String census;
+    private String ethnic;
+    private String marriage;
+    private String education;
+    private Integer enabled;
+    private Long createBy;
+    /**创建日期*/
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+    private Long updateBy;
+    /**创建日期*/
+    @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    @ApiModelProperty(value = "是否负责人:0 否,1分公司负责人,2部门负责人")
+    private Integer isLeader;
+
+    @ApiModelProperty(value = "是否是超管")
+    private Boolean isAdmin;
+
+    @ApiModelProperty(value = "数据权限")
+    private String dataScope;
+
+    @ApiModelProperty(value = "测试")
+    private Integer test;
+
+    @ApiModelProperty(value = "组织路由")
+    private String orgRouteCode;
+}

BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/CacheConstant.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/CodeGenerateInterface.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/CommonConstant.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/DSConstants.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/DataBaseConstant.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/DefContants.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/LongToStringSerializer.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/RedisKeySaltConstant.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/SystemConstant.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/constant/UserCacheKeyConstants.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/ecode/BCrypt.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/ecode/BCryptPasswordEncoder.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/ecode/PasswordEncoder.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/enums/DataTypeEnum.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/properties/UrlChainDefinitionPorperties.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/property/YamlPropertyResourceFactory.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/system/query/MatchTypeEnum.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/system/query/QueryCondition.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/system/query/QueryRuleEnum.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/thread/ThreadPoolUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/ConvertUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/DateUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/DateUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/HtmlUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/LocalDateUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/MD5Util.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/RandomCodeUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/RandomGenerateHelper.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/RsaUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SnowFlake.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SpringContextUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SqlInjectionUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/StringUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/SystemUtils.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/UrlChainBlackAndWhiteUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/WordUtilsTest.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/ZipUtil.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/encryptor/AccessKeyEncryptor.class


BIN
java/storlead-framework/storlead-common/target/classes/com/storlead/framework/common/util/generate/CodeGenerate.class


Деякі файли не було показано, через те що забагато файлів було змінено